From 3fc276852bf8c3c4c789dd4a27054c04f4e56d94 Mon Sep 17 00:00:00 2001 From: Werner Dweight Date: Mon, 9 Sep 2024 19:40:02 +0200 Subject: [PATCH] feat: allow one-off token handler targeting --- README.md | 6 ++++++ auth/config/main.go | 9 +++++++++ auth/config/main_test.go | 10 ++++++++++ auth/contract/config.go | 6 ++++++ auth/contract/errors.go | 2 ++ auth/security/main.go | 19 +++++++++++++++++++ 6 files changed, 52 insertions(+) diff --git a/README.md b/README.md index 84ccc08..326046b 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,12 @@ Full configuration structure (only client configuration is mandatory, default va // TTL: cache TTL in seconds - defaults to 3600 (1 hour) TTL *time.Duration } + + // TargetOneOffTokenHandlers: list of handlers to target for one-off token authentication (optional; if you omit target handlers, all handlers will be targeted) + TargetOneOffTokenHandlers *[]string + // '.*' # all handlers + // '/v1/*' # all handlers starting with '/v1/' + // '/v1/some/path' # only '/v1/some/path' handler } ``` diff --git a/auth/config/main.go b/auth/config/main.go index 19c7060..d17c1cc 100644 --- a/auth/config/main.go +++ b/auth/config/main.go @@ -26,6 +26,10 @@ func (p *Provider) GetExcludeHandlers() *[]string { return p.config.ExcludeHandlers } +func (p *Provider) GetTargetOneOffTokenHandlers() *[]string { + return p.config.TargetOneOffTokenHandlers +} + func (p *Provider) GetClientProvider() contract.ApiClientProviderInterface[contract.ApiClientInterface] { return p.config.Client.Provider } @@ -201,6 +205,10 @@ func (p *Provider) Init(config contract.Config) { if nil != config.Cache { p.initCache(config) } + + if nil != config.TargetOneOffTokenHandlers { + p.config.TargetOneOffTokenHandlers = config.TargetOneOffTokenHandlers + } } var ( @@ -250,5 +258,6 @@ var ProviderInstance = &Provider{ Prefix: &defaultCachePrefix, TTL: &defaultCacheTTL, }, + TargetOneOffTokenHandlers: nil, }, } diff --git a/auth/config/main_test.go b/auth/config/main_test.go index dfc0413..7f96d21 100644 --- a/auth/config/main_test.go +++ b/auth/config/main_test.go @@ -97,6 +97,7 @@ func (s *TestSuite) SetupTest() { Prefix: &defaultCachePrefix, TTL: &defaultCacheTTL, }, + TargetOneOffTokenHandlers: nil, }, } } @@ -156,6 +157,15 @@ func (s *TestSuite) TestProvider_GetExcludeHandlers() { s.Equal(&handlers, s.provider.GetExcludeHandlers()) } +func (s *TestSuite) TestProvider_GetTargetOneOffTokenHandlers() { + s.Nil(s.provider.GetTargetOneOffTokenHandlers()) + handlers := []string{".*"} + s.provider.Init(contract.Config{ + TargetOneOffTokenHandlers: &handlers, + }) + s.Equal(&handlers, s.provider.GetTargetOneOffTokenHandlers()) +} + func (s *TestSuite) TestProvider_GetClientProvider() { s.Nil(s.provider.GetClientProvider()) s.provider.Init(contract.Config{ diff --git a/auth/contract/config.go b/auth/contract/config.go index 8aa1833..0cb6608 100644 --- a/auth/contract/config.go +++ b/auth/contract/config.go @@ -78,4 +78,10 @@ type Config struct { // Cache: cache configuration (optional) Cache *CacheConfig + + // TargetOneOffTokenHandlers: list of handlers to target for one-off token authentication (optional; if you omit target handlers, all handlers will be targeted) + TargetOneOffTokenHandlers *[]string + // '.*' # all handlers + // '/v1/*' # all handlers starting with '/v1/' + // '/v1/some/path' # only '/v1/some/path' handler } diff --git a/auth/contract/errors.go b/auth/contract/errors.go index 2223d6b..e5d75b1 100644 --- a/auth/contract/errors.go +++ b/auth/contract/errors.go @@ -43,6 +43,7 @@ const ( CacheDisabled InvalidOneOffToken InvalidFUPCookie + OneOffTokenNotAllowed ) var AuthErrorCodes = map[AuthErrorCode]string{ @@ -74,6 +75,7 @@ var AuthErrorCodes = map[AuthErrorCode]string{ CacheDisabled: "cache driver needs to be configured for this functionality to work", InvalidOneOffToken: "one-off token is invalid, already used or expired", InvalidFUPCookie: "FUP cookie present, but invalid", + OneOffTokenNotAllowed: "one-off token authentication is not allowed for this endpoint", } func NewAuthError(code AuthErrorCode, payload interface{}) *AuthError { diff --git a/auth/security/main.go b/auth/security/main.go index e58c7f2..5001c91 100644 --- a/auth/security/main.go +++ b/auth/security/main.go @@ -52,6 +52,25 @@ func shouldAuthenticateByApiKey(c *gin.Context) bool { } func authenticateApiClientByOneOffToken(c *gin.Context) (contract.ApiClientInterface, *contract.AuthError) { + // check if one-off token is allowed for the current request + targetHandlers := config.ProviderInstance.GetTargetOneOffTokenHandlers() + if nil != targetHandlers && len(*targetHandlers) > 0 { + inScope := false + for _, targetHandler := range *targetHandlers { + matched, err := regexp.MatchString(targetHandler, c.Request.URL.String()) + if nil != err { + log.Printf("can't match one-off token target handler pattern '%s': %v", targetHandler, err) + } + if matched { + inScope = true + break + } + } + if !inScope { + return nil, contract.NewAuthError(contract.OneOffTokenNotAllowed, nil) + } + } + token := c.Request.Header.Get(constants.OneOffTokenHeader) if !config.ProviderInstance.IsCacheEnabled() { return nil, contract.NewInternalError(contract.CacheDisabled, nil)