Refactor caches

Signed-off-by: Christian Richter <crichter@owncloud.com>
This commit is contained in:
Christian Richter
2023-04-19 17:32:25 +02:00
parent bc15b8a396
commit a900d0ed8d
4 changed files with 47 additions and 27 deletions
+30 -20
View File
@@ -43,13 +43,13 @@ import (
) )
type StaticRouteHandler struct { type StaticRouteHandler struct {
prefix string prefix string
proxy http.Handler proxy http.Handler
sidCache microstore.Store userInfoCache microstore.Store
accessTokenCache microstore.Store sessionLookupCache microstore.Store
logger log.Logger logger log.Logger
config config.Config config config.Config
oidcClient oidc.OIDCProvider oidcClient oidc.OIDCProvider
} }
// Server is the entrypoint for the server command. // Server is the entrypoint for the server command.
@@ -62,7 +62,7 @@ func Server(cfg *config.Config) *cli.Command {
return configlog.ReturnFatal(parser.ParseConfig(cfg)) return configlog.ReturnFatal(parser.ParseConfig(cfg))
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
cache := store.Create( userInfoCache := store.Create(
store.Store(cfg.OIDC.UserinfoCache.Store), store.Store(cfg.OIDC.UserinfoCache.Store),
store.TTL(cfg.OIDC.UserinfoCache.TTL), store.TTL(cfg.OIDC.UserinfoCache.TTL),
store.Size(cfg.OIDC.UserinfoCache.Size), store.Size(cfg.OIDC.UserinfoCache.Size),
@@ -71,6 +71,15 @@ func Server(cfg *config.Config) *cli.Command {
microstore.Table(cfg.OIDC.UserinfoCache.Table), microstore.Table(cfg.OIDC.UserinfoCache.Table),
) )
sessionLookupCache := store.Create(
store.Store(cfg.OIDC.SessionLookupCache.Store),
store.TTL(cfg.OIDC.SessionLookupCache.TTL),
store.Size(cfg.OIDC.SessionLookupCache.Size),
microstore.Nodes(cfg.OIDC.SessionLookupCache.Nodes...),
microstore.Database(cfg.OIDC.SessionLookupCache.Database),
microstore.Table(cfg.OIDC.SessionLookupCache.Table),
)
logger := logging.Configure(cfg.Service.Name, cfg.Log) logger := logging.Configure(cfg.Service.Name, cfg.Log)
err := tracing.Configure(cfg) err := tracing.Configure(cfg)
if err != nil { if err != nil {
@@ -122,20 +131,20 @@ func Server(cfg *config.Config) *cli.Command {
) )
lh := StaticRouteHandler{ lh := StaticRouteHandler{
prefix: cfg.HTTP.Root, prefix: cfg.HTTP.Root,
sidCache: cache, // FIXME use correct cache userInfoCache: userInfoCache,
accessTokenCache: cache, // FIXME use correct cache sessionLookupCache: sessionLookupCache,
logger: logger, logger: logger,
config: *cfg, config: *cfg,
oidcClient: oidcClient, oidcClient: oidcClient,
proxy: rp, proxy: rp,
} }
if err != nil { if err != nil {
return fmt.Errorf("failed to initialize reverse proxy: %w", err) return fmt.Errorf("failed to initialize reverse proxy: %w", err)
} }
{ {
middlewares := loadMiddlewares(ctx, logger, cfg, cache) middlewares := loadMiddlewares(ctx, logger, cfg, userInfoCache, sessionLookupCache)
server, err := proxyHTTP.Server( server, err := proxyHTTP.Server(
proxyHTTP.Handler(lh.handler()), proxyHTTP.Handler(lh.handler()),
proxyHTTP.Logger(logger), proxyHTTP.Logger(logger),
@@ -222,14 +231,14 @@ func (h *StaticRouteHandler) backchannelLogout(w http.ResponseWriter, r *http.Re
return return
} }
records, err := h.sidCache.Read(logoutToken.SessionId) records, err := h.userInfoCache.Read(logoutToken.SessionId)
if errors.Is(err, microstore.ErrNotFound) || len(records) == 0 { if errors.Is(err, microstore.ErrNotFound) || len(records) == 0 {
render.Status(r, http.StatusOK) render.Status(r, http.StatusOK)
return return
} }
for _, record := range records { for _, record := range records {
err = h.accessTokenCache.Delete(string(record.Value)) err = h.sessionLookupCache.Delete(string(record.Value))
if errors.Is(err, microstore.ErrNotFound) { if errors.Is(err, microstore.ErrNotFound) {
render.Status(r, http.StatusOK) render.Status(r, http.StatusOK)
return return
@@ -239,7 +248,7 @@ func (h *StaticRouteHandler) backchannelLogout(w http.ResponseWriter, r *http.Re
render.Status(r, http.StatusOK) render.Status(r, http.StatusOK)
} }
func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config, cache microstore.Store) alice.Chain { func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config, userInfoCache microstore.Store, sessionLookupCache microstore.Store) alice.Chain {
rolesClient := settingssvc.NewRoleService("com.owncloud.api.settings", grpc.DefaultClient()) rolesClient := settingssvc.NewRoleService("com.owncloud.api.settings", grpc.DefaultClient())
revaClient, err := pool.GetGatewayServiceClient(cfg.Reva.Address, cfg.Reva.GetRevaOptions()...) revaClient, err := pool.GetGatewayServiceClient(cfg.Reva.Address, cfg.Reva.GetRevaOptions()...)
if err != nil { if err != nil {
@@ -316,7 +325,8 @@ func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config,
authenticators = append(authenticators, middleware.NewOIDCAuthenticator( authenticators = append(authenticators, middleware.NewOIDCAuthenticator(
middleware.Logger(logger), middleware.Logger(logger),
middleware.Cache(cache), middleware.UserInfoCache(userInfoCache),
middleware.SessionLookupCache(sessionLookupCache),
middleware.DefaultAccessTokenTTL(cfg.OIDC.UserinfoCache.TTL), middleware.DefaultAccessTokenTTL(cfg.OIDC.UserinfoCache.TTL),
middleware.HTTPClient(oidcHTTPClient), middleware.HTTPClient(oidcHTTPClient),
middleware.OIDCIss(cfg.OIDC.Issuer), middleware.OIDCIss(cfg.OIDC.Issuer),
+1
View File
@@ -106,6 +106,7 @@ type OIDC struct {
Insecure bool `yaml:"insecure" env:"OCIS_INSECURE;PROXY_OIDC_INSECURE" desc:"Disable TLS certificate validation for connections to the IDP. Note that this is not recommended for production environments."` Insecure bool `yaml:"insecure" env:"OCIS_INSECURE;PROXY_OIDC_INSECURE" desc:"Disable TLS certificate validation for connections to the IDP. Note that this is not recommended for production environments."`
AccessTokenVerifyMethod string `yaml:"access_token_verify_method" env:"PROXY_OIDC_ACCESS_TOKEN_VERIFY_METHOD" desc:"Sets how OIDC access tokens should be verified. Possible values are 'none' and 'jwt'. When using 'none', no special validation apart from using it for accessing the IPD's userinfo endpoint will be done. When using 'jwt', it tries to parse the access token as a jwt token and verifies the signature using the keys published on the IDP's 'jwks_uri'."` AccessTokenVerifyMethod string `yaml:"access_token_verify_method" env:"PROXY_OIDC_ACCESS_TOKEN_VERIFY_METHOD" desc:"Sets how OIDC access tokens should be verified. Possible values are 'none' and 'jwt'. When using 'none', no special validation apart from using it for accessing the IPD's userinfo endpoint will be done. When using 'jwt', it tries to parse the access token as a jwt token and verifies the signature using the keys published on the IDP's 'jwks_uri'."`
UserinfoCache *Cache `yaml:"user_info_cache"` UserinfoCache *Cache `yaml:"user_info_cache"`
SessionLookupCache *Cache `yaml:"session_lookup_cache"`
JWKS JWKS `yaml:"jwks"` JWKS JWKS `yaml:"jwks"`
RewriteWellKnown bool `yaml:"rewrite_well_known" env:"PROXY_OIDC_REWRITE_WELLKNOWN" desc:"Enables rewriting the /.well-known/openid-configuration to the configured OIDC issuer. Needed by the Desktop Client, Android Client and iOS Client to discover the OIDC provider."` RewriteWellKnown bool `yaml:"rewrite_well_known" env:"PROXY_OIDC_REWRITE_WELLKNOWN" desc:"Enables rewriting the /.well-known/openid-configuration to the configured OIDC issuer. Needed by the Desktop Client, Android Client and iOS Client to discover the OIDC provider."`
} }
+2 -2
View File
@@ -30,8 +30,8 @@ func NewOIDCAuthenticator(opts ...Option) *OIDCAuthenticator {
return &OIDCAuthenticator{ return &OIDCAuthenticator{
Logger: options.Logger, Logger: options.Logger,
userInfoCache: options.Cache, userInfoCache: options.UserInfoCache,
sessionLookupCache: options.Cache, sessionLookupCache: options.SessionLookupCache,
DefaultTokenCacheTTL: options.DefaultAccessTokenTTL, DefaultTokenCacheTTL: options.DefaultAccessTokenTTL,
HTTPClient: options.HTTPClient, HTTPClient: options.HTTPClient,
OIDCIss: options.OIDCIss, OIDCIss: options.OIDCIss,
+14 -5
View File
@@ -54,8 +54,10 @@ type Options struct {
EnableBasicAuth bool EnableBasicAuth bool
// DefaultAccessTokenTTL is used to calculate the expiration when an access token has no expiration set // DefaultAccessTokenTTL is used to calculate the expiration when an access token has no expiration set
DefaultAccessTokenTTL time.Duration DefaultAccessTokenTTL time.Duration
// Cache sets the access token cache store // UserInfoCache sets the access token cache store
Cache store.Store UserInfoCache store.Store
// SessionLookupCache maps the session to a hashed jwt token
SessionLookupCache store.Store
// CredentialsByUserAgent sets the auth challenges on a per user-agent basis // CredentialsByUserAgent sets the auth challenges on a per user-agent basis
CredentialsByUserAgent map[string]string CredentialsByUserAgent map[string]string
// AccessTokenVerifyMethod configures how access_tokens should be verified but the oidc_auth middleware. // AccessTokenVerifyMethod configures how access_tokens should be verified but the oidc_auth middleware.
@@ -191,10 +193,17 @@ func DefaultAccessTokenTTL(ttl time.Duration) Option {
} }
} }
// Cache provides a function to set the Cache // UserInfoCache provides a function to set the UserInfoCache
func Cache(val store.Store) Option { func UserInfoCache(val store.Store) Option {
return func(o *Options) { return func(o *Options) {
o.Cache = val o.UserInfoCache = val
}
}
// SessionLookupCache provides a function to set the SessionLookupCache
func SessionLookupCache(val store.Store) Option {
return func(o *Options) {
o.SessionLookupCache = val
} }
} }