From 539a004f65e23308fdb2b1e6a2e5dfbe44f8e746 Mon Sep 17 00:00:00 2001 From: johnabass Date: Thu, 11 Jul 2024 12:50:06 -0700 Subject: [PATCH] refactored the Authorizer API to be consistent with Validator --- authorizer.go | 28 +++++++++++++--------------- authorizer_test.go | 16 ++++++++-------- basculehttp/middleware.go | 4 ++-- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/authorizer.go b/authorizer.go index 9818416..79e03a2 100644 --- a/authorizer.go +++ b/authorizer.go @@ -18,26 +18,24 @@ type Authorizer[R any] interface { // cancelation semantics. // // If this method doesn't support the given token, it should return nil. - Authorize(ctx context.Context, token Token, resource R) error + Authorize(ctx context.Context, resource R, token Token) error } // AuthorizerFunc is a closure type that implements Authorizer. -type AuthorizerFunc[R any] func(context.Context, Token, R) error +type AuthorizerFunc[R any] func(context.Context, R, Token) error -func (af AuthorizerFunc[R]) Authorize(ctx context.Context, token Token, resource R) error { - return af(ctx, token, resource) +func (af AuthorizerFunc[R]) Authorize(ctx context.Context, resource R, token Token) error { + return af(ctx, resource, token) } // Authorizers is a collection of Authorizers. type Authorizers[R any] []Authorizer[R] -// Add appends authorizers to this aggregate Authorizers. -func (as *Authorizers[R]) Add(a ...Authorizer[R]) { - if *as == nil { - *as = make(Authorizers[R], 0, len(a)) - } - - *as = append(*as, a...) +// Append tacks on one or more authorizers to this collection. The possibly +// new Authorizers instance is returned. The semantics of this method are +// the same as the built-in append. +func (as Authorizers[R]) Append(a ...Authorizer[R]) Authorizers[R] { + return append(as, a...) } // Authorize requires all authorizers in this sequence to allow access. This @@ -45,9 +43,9 @@ func (as *Authorizers[R]) Add(a ...Authorizer[R]) { // // Because authorization can be arbitrarily expensive, execution halts at the first failed // authorization attempt. -func (as Authorizers[R]) Authorize(ctx context.Context, token Token, resource R) error { +func (as Authorizers[R]) Authorize(ctx context.Context, resource R, token Token) error { for _, a := range as { - if err := a.Authorize(ctx, token, resource); err != nil { + if err := a.Authorize(ctx, resource, token); err != nil { return err } } @@ -61,10 +59,10 @@ type requireAny[R any] struct { // Authorize returns nil at the first authorizer that returns nil, i.e. accepts the access. // Otherwise, this method returns an aggregate error of all the authorization errors. -func (ra requireAny[R]) Authorize(ctx context.Context, token Token, resource R) error { +func (ra requireAny[R]) Authorize(ctx context.Context, resource R, token Token) error { var err error for _, a := range ra.a { - authErr := a.Authorize(ctx, token, resource) + authErr := a.Authorize(ctx, resource, token) if authErr == nil { return nil } diff --git a/authorizer_test.go b/authorizer_test.go index a77ba48..3f5c564 100644 --- a/authorizer_test.go +++ b/authorizer_test.go @@ -63,8 +63,8 @@ func (suite *AuthorizersTestSuite) TestAuthorize() { for _, err := range testCase.results { err := err - as.Add( - AuthorizerFunc[string](func(ctx context.Context, token Token, resource string) error { + as = as.Append( + AuthorizerFunc[string](func(ctx context.Context, resource string, token Token) error { suite.Same(testCtx, ctx) suite.Same(testToken, token) suite.Equal(placeholderResource, resource) @@ -75,7 +75,7 @@ func (suite *AuthorizersTestSuite) TestAuthorize() { suite.Equal( testCase.expectedErr, - as.Authorize(testCtx, testToken, placeholderResource), + as.Authorize(testCtx, placeholderResource, testToken), ) }) } @@ -123,8 +123,8 @@ func (suite *AuthorizersTestSuite) TestAny() { for _, err := range testCase.results { err := err - as.Add( - AuthorizerFunc[string](func(ctx context.Context, token Token, resource string) error { + as = as.Append( + AuthorizerFunc[string](func(ctx context.Context, resource string, token Token) error { suite.Same(testCtx, ctx) suite.Same(testToken, token) suite.Equal(placeholderResource, resource) @@ -136,13 +136,13 @@ func (suite *AuthorizersTestSuite) TestAny() { anyAs := as.Any() suite.Equal( testCase.expectedErr, - anyAs.Authorize(testCtx, testToken, placeholderResource), + anyAs.Authorize(testCtx, placeholderResource, testToken), ) if len(as) > 0 { // the any instance should be distinct as[0] = AuthorizerFunc[string]( - func(context.Context, Token, string) error { + func(context.Context, string, Token) error { suite.Fail("should not have been called") return nil }, @@ -150,7 +150,7 @@ func (suite *AuthorizersTestSuite) TestAny() { suite.Equal( testCase.expectedErr, - anyAs.Authorize(testCtx, testToken, placeholderResource), + anyAs.Authorize(testCtx, placeholderResource, testToken), ) } }) diff --git a/basculehttp/middleware.go b/basculehttp/middleware.go index a94c392..9868f6e 100644 --- a/basculehttp/middleware.go +++ b/basculehttp/middleware.go @@ -77,7 +77,7 @@ func WithChallenges(ch ...Challenge) MiddlewareOption { // This is useful for use cases like admin access or alternate capabilities. func WithAuthorization(a ...bascule.Authorizer[*http.Request]) MiddlewareOption { return middlewareOptionFunc(func(m *Middleware) error { - m.authorization.Add(a...) + m.authorization = m.authorization.Append(a...) return nil }) } @@ -199,7 +199,7 @@ func (m *Middleware) authenticate(ctx context.Context, request *http.Request, to } func (m *Middleware) authorize(ctx context.Context, token bascule.Token, request *http.Request) error { - return m.authorization.Authorize(ctx, token, request) + return m.authorization.Authorize(ctx, request, token) } // frontDoor is the internal handler implementation that protects a handler