diff --git a/authorization.go b/authorization.go index 4288f8d..88ad01d 100644 --- a/authorization.go +++ b/authorization.go @@ -19,7 +19,7 @@ package auth import ( "fmt" "github.com/pkg/errors" - "strings" + "regexp" ) const ( @@ -32,6 +32,8 @@ var ( BasicAuthAuthorizerType AuthorizerType = "basic" BearerAuthAuthorizerType AuthorizerType = "bearer" + + bearerTokenMatch = regexp.MustCompile("(?i)bearer (.*)") ) type ( @@ -166,7 +168,11 @@ func (authorizer *Authorizer) authorizeBearerAuth(authHeader string, action stri var allowed bool var wwwAuthenticateHeader string - signedString := strings.TrimPrefix(authHeader, "Bearer ") + if namespace == "" { + namespace = authorizer.DefaultNamespace + } + + signedString := bearerTokenMatch.ReplaceAllString(authHeader, "$1") // TODO log error token, err := authorizer.TokenDecoder.DecodeToken(signedString) @@ -194,9 +200,6 @@ func (authorizer *Authorizer) authorizeBearerAuth(authHeader string, action stri } if !allowed { - if namespace == "" { - namespace = authorizer.DefaultNamespace - } wwwAuthenticateHeader = fmt.Sprintf("Bearer realm=\"%s\",service=\"%s\",scope=\"%s:%s:%s\"", authorizer.Realm, authorizer.Service, authorizer.AccessEntryType, namespace, action) } diff --git a/authorization_test.go b/authorization_test.go index 7fbd87d..bc9233a 100644 --- a/authorization_test.go +++ b/authorization_test.go @@ -42,6 +42,9 @@ type AuthorizationTestSuite struct { CustomAccessEntryTypeAuthorizer *Authorizer + CustomDefaultNamespaceAuthorizer *Authorizer + EmptyDefaultNamespaceAuthorizer *Authorizer + BasicBadAuthorizationHeader string BasicGoodAuthorizationHeader string BasicExpectedWWWAuthHeader string @@ -113,6 +116,22 @@ func (suite *AuthorizationTestSuite) SetupSuite() { }) suite.Nil(err) + suite.CustomDefaultNamespaceAuthorizer, err = NewAuthorizer(&AuthorizerOptions{ + Realm: "https://my.site.io/oauth2/token", + Service: "my.site.io", + PublicKeyPath: testPublicKey, + DefaultNamespace: "woo", + }) + suite.Nil(err) + + suite.EmptyDefaultNamespaceAuthorizer, err = NewAuthorizer(&AuthorizerOptions{ + Realm: "https://my.site.io/oauth2/token", + Service: "my.site.io", + PublicKeyPath: testPublicKey, + EmptyDefaultNamespace: true, + }) + suite.Nil(err) + suite.BasicBadAuthorizationHeader = generateBasicAuthHeader("cm-test-baduser", "cm-test-badpass") suite.BasicGoodAuthorizationHeader = generateBasicAuthHeader("cm-test-user", "cm-test-pass") suite.BasicExpectedWWWAuthHeader = "Basic realm=\"https://my.site.io/oauth2/token\"" @@ -215,7 +234,7 @@ func (suite *AuthorizationTestSuite) TestAuthorizeBearerRequest() { signedString, err := suite.TokenGenerator.GenerateToken(access, 0) suite.Nil(err) authHeader := fmt.Sprintf("Bearer %s", signedString) - permission, err = suite.BearerAuthAuthorizer.Authorize(authHeader, PullAction, DefaultNamespace) + permission, err = suite.BearerAuthAuthorizer.Authorize(authHeader, PullAction, "") suite.True(permission.Allowed) suite.Equal("", permission.WWWAuthenticateHeader) suite.Nil(err) @@ -242,7 +261,7 @@ func (suite *AuthorizationTestSuite) TestAuthorizeBearerRequest() { suite.Nil(err) authHeader = fmt.Sprintf("Bearer %s", signedString) - permission, err = suite.BearerAuthAuthorizer.Authorize(authHeader, PullAction, DefaultNamespace) + permission, err = suite.BearerAuthAuthorizer.Authorize(authHeader, PullAction, "") suite.True(permission.Allowed) suite.Equal("", permission.WWWAuthenticateHeader) suite.Nil(err) @@ -257,7 +276,7 @@ func (suite *AuthorizationTestSuite) TestAuthorizeBearerRequest() { suite.Equal("", permission.WWWAuthenticateHeader) suite.Nil(err) - permission, err = suite.BearerAuthAuthorizer.Authorize(authHeader, PushAction, DefaultNamespace) + permission, err = suite.BearerAuthAuthorizer.Authorize(authHeader, PushAction, "") suite.False(permission.Allowed) suite.Equal(suite.BearerPushScopeExpectedWWWAuthHeader, permission.WWWAuthenticateHeader) suite.Nil(err) @@ -286,7 +305,7 @@ func (suite *AuthorizationTestSuite) TestAuthorizeBearerRequest() { fmt.Println("Sleeping for 2 seconds to test token expiration...") time.Sleep(time.Second * 2) authHeader = fmt.Sprintf("Bearer %s", signedString) - permission, err = suite.BearerAuthAuthorizer.Authorize(authHeader, PullAction, DefaultNamespace) + permission, err = suite.BearerAuthAuthorizer.Authorize(authHeader, PullAction, "") suite.False(permission.Allowed) suite.Equal(suite.BearerPullScopeExpectedWWWAuthHeader, permission.WWWAuthenticateHeader) suite.Nil(err) @@ -302,7 +321,7 @@ func (suite *AuthorizationTestSuite) TestAuthorizeBearerRequest() { signedString, err = suite.TokenGenerator.GenerateToken(access, 0) suite.Nil(err) authHeader = fmt.Sprintf("Bearer %s", signedString) - permission, err = suite.BearerAuthAuthorizer.Authorize(authHeader, PullAction, DefaultNamespace) + permission, err = suite.BearerAuthAuthorizer.Authorize(authHeader, PullAction, "") suite.False(permission.Allowed) suite.Equal(suite.BearerPullScopeExpectedWWWAuthHeader, permission.WWWAuthenticateHeader) suite.Nil(err) @@ -318,7 +337,7 @@ func (suite *AuthorizationTestSuite) TestAuthorizeBearerRequest() { signedString, err = suite.TokenGenerator.GenerateToken(access, 0) suite.Nil(err) authHeader = fmt.Sprintf("Bearer %s", signedString) - permission, err = suite.BearerAuthAuthorizer.Authorize(authHeader, PullAction, DefaultNamespace) + permission, err = suite.BearerAuthAuthorizer.Authorize(authHeader, PullAction, "") suite.False(permission.Allowed) suite.Equal(suite.BearerPullScopeExpectedWWWAuthHeader, permission.WWWAuthenticateHeader) suite.Nil(err) @@ -336,7 +355,7 @@ func (suite *AuthorizationTestSuite) TestCustomAccessEntryTypeAuthorizer() { signedString, err := suite.TokenGenerator.GenerateToken(access, 0) suite.Nil(err) authHeader := fmt.Sprintf("Bearer %s", signedString) - permission, err := suite.CustomAccessEntryTypeAuthorizer.Authorize(authHeader, PullAction, DefaultNamespace) + permission, err := suite.CustomAccessEntryTypeAuthorizer.Authorize(authHeader, PullAction, "") suite.False(permission.Allowed) suite.NotEmpty(permission.WWWAuthenticateHeader) suite.Nil(err) @@ -352,12 +371,103 @@ func (suite *AuthorizationTestSuite) TestCustomAccessEntryTypeAuthorizer() { signedString, err = suite.TokenGenerator.GenerateToken(access, 0) suite.Nil(err) authHeader = fmt.Sprintf("Bearer %s", signedString) - permission, err = suite.CustomAccessEntryTypeAuthorizer.Authorize(authHeader, PullAction, DefaultNamespace) + permission, err = suite.CustomAccessEntryTypeAuthorizer.Authorize(authHeader, PullAction, "") + suite.True(permission.Allowed) + suite.Empty(permission.WWWAuthenticateHeader) + suite.Nil(err) +} + +func (suite *AuthorizationTestSuite) TestCustomDefaultNamespaceAuthorizer() { + // Token using default namespace does not work when authorizer configured with custom default namespace + access := []AccessEntry{ + { + Name: DefaultNamespace, + Type: AccessEntryType, + Actions: []string{PullAction}, + }, + } + signedString, err := suite.TokenGenerator.GenerateToken(access, 0) + suite.Nil(err) + authHeader := fmt.Sprintf("Bearer %s", signedString) + permission, err := suite.CustomDefaultNamespaceAuthorizer.Authorize(authHeader, PullAction, "") + suite.False(permission.Allowed) + suite.NotEmpty(permission.WWWAuthenticateHeader) + suite.Nil(err) + + // Using custom custom default namespace provides access + access = []AccessEntry{ + { + Name: "woo", + Type: AccessEntryType, + Actions: []string{PullAction}, + }, + } + signedString, err = suite.TokenGenerator.GenerateToken(access, 0) + suite.Nil(err) + + authHeader = fmt.Sprintf("Bearer %s", signedString) + permission, err = suite.CustomDefaultNamespaceAuthorizer.Authorize(authHeader, PullAction, "") + suite.True(permission.Allowed) + suite.Empty(permission.WWWAuthenticateHeader) + suite.Nil(err) +} + +func (suite *AuthorizationTestSuite) TestEmptyDefaultNamespaceAuthorizer() { + // Token using default namespace does not work when authorizer configured with empty default namespace + access := []AccessEntry{ + { + Name: DefaultNamespace, + Type: AccessEntryType, + Actions: []string{PullAction}, + }, + } + signedString, err := suite.TokenGenerator.GenerateToken(access, 0) + suite.Nil(err) + authHeader := fmt.Sprintf("Bearer %s", signedString) + permission, err := suite.EmptyDefaultNamespaceAuthorizer.Authorize(authHeader, PullAction, "") + suite.False(permission.Allowed) + suite.NotEmpty(permission.WWWAuthenticateHeader) + suite.Nil(err) + + // Using empty string namespace provides access + access = []AccessEntry{ + { + Name: "", + Type: AccessEntryType, + Actions: []string{PullAction}, + }, + } + signedString, err = suite.TokenGenerator.GenerateToken(access, 0) + suite.Nil(err) + + authHeader = fmt.Sprintf("Bearer %s", signedString) + permission, err = suite.EmptyDefaultNamespaceAuthorizer.Authorize(authHeader, PullAction, "") suite.True(permission.Allowed) suite.Empty(permission.WWWAuthenticateHeader) suite.Nil(err) } +func (suite *AuthorizationTestSuite) TestTokenBearerCapitalization() { + // check that the prefixing "Bearer" used in token auth can be of any capitalization + for _, h := range []string{"Bearer", "bearer", "BEARER", "BeArEr"} { + access := []AccessEntry{ + { + Name: "", + Type: AccessEntryType, + Actions: []string{PullAction}, + }, + } + signedString, err := suite.TokenGenerator.GenerateToken(access, 0) + suite.Nil(err) + authHeader := fmt.Sprintf("%s %s", h, signedString) + permission, err := suite.EmptyDefaultNamespaceAuthorizer.Authorize(authHeader, PullAction, "") + suite.True(permission.Allowed) + suite.Empty(permission.WWWAuthenticateHeader) + suite.Nil(err) + } + +} + func TestAuthorizationTestSuite(t *testing.T) { suite.Run(t, new(AuthorizationTestSuite)) }