From 8880876bcd00733ddae631e408d591c480f79646 Mon Sep 17 00:00:00 2001 From: Prabhat Khera <91852476+prabhat-org@users.noreply.github.com> Date: Thu, 14 Mar 2024 09:02:25 +1300 Subject: [PATCH] fix(auth): make createAccessToken api backward compatible [EE-6818] (#11327) * fix(auth): make createAccessToken api backward compatible [EE-6818] * fix(api): api error message [EE-6818] * fix messages --- api/http/handler/users/user_create_access_token.go | 9 +++++++-- .../handler/users/user_create_access_token_test.go | 2 +- api/http/security/bouncer.go | 12 ++++++++---- api/internal/testhelpers/request_bouncer.go | 4 ++++ 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/api/http/handler/users/user_create_access_token.go b/api/http/handler/users/user_create_access_token.go index d27242ca8..373a5ecac 100644 --- a/api/http/handler/users/user_create_access_token.go +++ b/api/http/handler/users/user_create_access_token.go @@ -60,8 +60,13 @@ type accessTokenResponse struct { // @router /users/{id}/tokens [post] func (handler *Handler) userCreateAccessToken(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { // specifically require Cookie auth for this endpoint since API-Key based auth is not supported - if jwt, _ := handler.bouncer.CookieAuthLookup(r); jwt == nil { - return httperror.Unauthorized("Auth not supported", errors.New("Cookie Authentication required")) + jwt, _ := handler.bouncer.CookieAuthLookup(r) + if jwt == nil { + jwt, _ = handler.bouncer.JWTAuthLookup(r) + } + + if jwt == nil { + return httperror.Unauthorized("Auth not supported", errors.New("Authentication required")) } var payload userAccessTokenCreatePayload diff --git a/api/http/handler/users/user_create_access_token_test.go b/api/http/handler/users/user_create_access_token_test.go index 335f69bd9..8e79a9555 100644 --- a/api/http/handler/users/user_create_access_token_test.go +++ b/api/http/handler/users/user_create_access_token_test.go @@ -107,7 +107,7 @@ func Test_userCreateAccessToken(t *testing.T) { body, err := io.ReadAll(rr.Body) is.NoError(err, "ReadAll should not return error") - is.Equal(`{"message":"Auth not supported","details":"Cookie Authentication required"}`, string(body)) + is.Equal(`{"message":"Auth not supported","details":"Authentication required"}`, string(body)) }) } diff --git a/api/http/security/bouncer.go b/api/http/security/bouncer.go index c7dd164ff..4b7095373 100644 --- a/api/http/security/bouncer.go +++ b/api/http/security/bouncer.go @@ -1,6 +1,7 @@ package security import ( + "fmt" "net/http" "strings" "time" @@ -10,6 +11,7 @@ import ( "github.com/portainer/portainer/api/dataservices" httperrors "github.com/portainer/portainer/api/http/errors" httperror "github.com/portainer/portainer/pkg/libhttp/error" + "github.com/rs/zerolog/log" "github.com/pkg/errors" ) @@ -27,6 +29,7 @@ type ( AuthorizedEdgeEndpointOperation(*http.Request, *portainer.Endpoint) error TrustedEdgeEnvironmentAccess(dataservices.DataStoreTx, *portainer.Endpoint) error CookieAuthLookup(*http.Request) (*portainer.TokenData, error) + JWTAuthLookup(*http.Request) (*portainer.TokenData, error) } // RequestBouncer represents an entity that manages API request accesses @@ -280,7 +283,7 @@ func (bouncer *RequestBouncer) mwAuthenticateFirst(tokenLookups []tokenLookup, n for _, lookup := range tokenLookups { resultToken, err := lookup(r) if err != nil { - httperror.WriteError(w, http.StatusUnauthorized, "Invalid API key", httperrors.ErrUnauthorized) + httperror.WriteError(w, http.StatusUnauthorized, "Invalid JWT token", httperrors.ErrUnauthorized) return } @@ -316,7 +319,7 @@ func (bouncer *RequestBouncer) CookieAuthLookup(r *http.Request) (*portainer.Tok tokenData, err := bouncer.jwtService.ParseAndVerifyToken(token) if err != nil { - return nil, ErrInvalidKey + return nil, err } return tokenData, nil @@ -332,7 +335,7 @@ func (bouncer *RequestBouncer) JWTAuthLookup(r *http.Request) (*portainer.TokenD tokenData, err := bouncer.jwtService.ParseAndVerifyToken(token) if err != nil { - return nil, ErrInvalidKey + return nil, err } return tokenData, nil @@ -366,7 +369,8 @@ func (bouncer *RequestBouncer) apiKeyLookup(r *http.Request) (*portainer.TokenDa Role: user.Role, } if _, _, err := bouncer.jwtService.GenerateToken(tokenData); err != nil { - return nil, ErrInvalidKey + log.Debug().Err(err).Msg("Failed to generate token") + return nil, fmt.Errorf("failed to generate token") } if now := time.Now().UTC().Unix(); now-apiKey.LastUsed > 60 { // [seconds] diff --git a/api/internal/testhelpers/request_bouncer.go b/api/internal/testhelpers/request_bouncer.go index 7f62d106e..692f23eb4 100644 --- a/api/internal/testhelpers/request_bouncer.go +++ b/api/internal/testhelpers/request_bouncer.go @@ -54,6 +54,10 @@ func (testRequestBouncer) CookieAuthLookup(r *http.Request) (*portainer.TokenDat return nil, nil } +func (testRequestBouncer) JWTAuthLookup(r *http.Request) (*portainer.TokenData, error) { + return nil, nil +} + // AddTestSecurityCookie adds a security cookie to the request func AddTestSecurityCookie(r *http.Request, jwt string) { r.AddCookie(&http.Cookie{