diff --git a/pkg/auth/middleware.go b/pkg/auth/middleware.go index 752032ed3..c8212e06e 100644 --- a/pkg/auth/middleware.go +++ b/pkg/auth/middleware.go @@ -1,11 +1,10 @@ package auth import ( - "github.com/cortezaproject/corteza-server/system/types" - "github.com/go-chi/jwtauth" "net/http" "github.com/cortezaproject/corteza-server/pkg/errors" + "github.com/go-chi/jwtauth" ) func MiddlewareValidOnly(next http.Handler) http.Handler { @@ -15,24 +14,15 @@ func MiddlewareValidOnly(next http.Handler) http.Handler { func AccessTokenCheck(scope ...string) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var ( - ctx = r.Context() - roles []uint64 - ) + var ctx = r.Context() // retrieve token and claims from context - tkn, claims, err := jwtauth.FromContext(ctx) + tkn, _, err := jwtauth.FromContext(ctx) if err != nil || !tkn.Valid { errors.ProperlyServeHTTP(w, r, ErrUnauthorized(), false) return } - i := ClaimsToIdentity(claims) - if !i.Valid() { - errors.ProperlyServeHTTP(w, r, ErrUnauthorized(), false) - return - } - // check valid scope for _, s := range scope { if !CheckScope(ctx.Value(scopeCtxKey{}), s) { @@ -48,51 +38,7 @@ func AccessTokenCheck(scope ...string) func(http.Handler) http.Handler { return } - u, err := DefaultJwtStore.LookupUserByID(ctx, i.Identity()) - if err != nil { - errors.ProperlyServeHTTP(w, r, ErrUnauthorized(), false) - return - } - - deleteTokens := func() { - _ = DefaultJwtStore.DeleteAuthOA2TokenByUserID(ctx, u.ID) - } - - // check if user is not suspended or deleted otherwise remove their all tokens - if u.SuspendedAt != nil || u.DeletedAt != nil { - deleteTokens() - errors.ProperlyServeHTTP(w, r, ErrUnauthorized(), false) - return - } - - // check if user's role haven't changed otherwise remove their all tokens - set, _, _ := DefaultJwtStore.SearchRoleMembers(ctx, types.RoleMemberFilter{UserID: u.ID}) - _ = set.Walk(func(member *types.RoleMember) error { - roles = append(roles, member.RoleID) - return nil - }) - if !equal(roles, i.memberOf) { - deleteTokens() - errors.ProperlyServeHTTP(w, r, ErrUnauthorized(), false) - return - } - next.ServeHTTP(w, r) }) } } - -// Equal tells whether a and b contain the same elements. -// A nil argument is equivalent to an empty slice. -// fixme maybe move to utils -func equal(a, b []uint64) bool { - if len(a) != len(b) { - return false - } - for i, v := range a { - if v != b[i] { - return false - } - } - return true -} diff --git a/system/service/auth.go b/system/service/auth.go index ed6f87ad0..eb3fe574c 100644 --- a/system/service/auth.go +++ b/system/service/auth.go @@ -1458,3 +1458,14 @@ func (svc auth) checkLimits(ctx context.Context) error { return nil } + +// RemoveAccessTokens removes all user's access tokens when suspended, +// deleted or security context changes +func (svc auth) RemoveAccessTokens(ctx context.Context, user *types.User) error { + return svc.recordAction( + ctx, + &authActionProps{user: user}, + AuthActionAccessTokensRemoved, + svc.store.DeleteAuthOA2TokenByUserID(ctx, user.ID), + ) +} diff --git a/system/service/auth_actions.gen.go b/system/service/auth_actions.gen.go index 487994170..c0dbf1162 100644 --- a/system/service/auth_actions.gen.go +++ b/system/service/auth_actions.gen.go @@ -11,12 +11,13 @@ package service import ( "context" "fmt" + "strings" + "time" + "github.com/cortezaproject/corteza-server/pkg/actionlog" "github.com/cortezaproject/corteza-server/pkg/errors" "github.com/cortezaproject/corteza-server/pkg/locale" "github.com/cortezaproject/corteza-server/system/types" - "strings" - "time" ) type ( @@ -636,6 +637,26 @@ func AuthActionEmailOtpVerify(props ...*authActionProps) *authAction { return a } +// AuthActionAccessTokensRemoved returns "system:auth.accessTokensRemoved" action +// +// This function is auto-generated. +// +func AuthActionAccessTokensRemoved(props ...*authActionProps) *authAction { + a := &authAction{ + timestamp: time.Now(), + resource: "system:auth", + action: "accessTokensRemoved", + log: "access tokens for {{user}} removed", + severity: actionlog.Notice, + } + + if len(props) > 0 { + a.props = props[0] + } + + return a +} + // ********************************************************************************************************************* // ********************************************************************************************************************* // Error constructors diff --git a/system/service/auth_actions.yaml b/system/service/auth_actions.yaml index 69f8fe2d9..0796eb963 100644 --- a/system/service/auth_actions.yaml +++ b/system/service/auth_actions.yaml @@ -83,6 +83,8 @@ actions: - action: emailOtpVerify log: "email one-time-password for {{user}} verified" + - action: accessTokensRemoved + log: "access tokens for {{user}} removed" errors: - error: invalidCredentials message: "invalid username and password combination" diff --git a/system/service/role.go b/system/service/role.go index aa279d460..ce4f70ffe 100644 --- a/system/service/role.go +++ b/system/service/role.go @@ -30,6 +30,7 @@ type ( eventbus eventDispatcher user UserService + auth roleAuth store store.Storer @@ -80,6 +81,10 @@ type ( rbacRoleUpdater interface { UpdateRoles(rr ...*rbac.Role) } + + roleAuth interface { + RemoveAccessTokens(context.Context, *types.User) error + } ) func Role() *role { @@ -90,6 +95,7 @@ func Role() *role { actionlog: DefaultActionlog, user: DefaultUser, + auth: DefaultAuth, store: DefaultStore, system: make(map[string]bool), @@ -716,6 +722,10 @@ func (svc role) MemberRemove(ctx context.Context, roleID, memberID uint64) (err return } + if err = svc.auth.RemoveAccessTokens(ctx, m); err != nil { + return + } + _ = svc.eventbus.WaitFor(ctx, event.RoleMemberAfterRemove(m, r)) return nil }() diff --git a/system/service/user.go b/system/service/user.go index b9ac03508..ca42482fc 100644 --- a/system/service/user.go +++ b/system/service/user.go @@ -54,6 +54,7 @@ type ( CheckPasswordStrength(string) bool SetPasswordCredentials(context.Context, uint64, string) error RemovePasswordCredentials(context.Context, uint64) error + RemoveAccessTokens(context.Context, *types.User) error } userAccessController interface { @@ -557,6 +558,10 @@ func (svc user) Delete(ctx context.Context, userID uint64) (err error) { return } + if err = svc.auth.RemoveAccessTokens(ctx, u); err != nil { + return + } + _ = svc.eventbus.WaitFor(ctx, event.UserAfterDelete(nil, u)) return nil }() @@ -646,6 +651,10 @@ func (svc user) Suspend(ctx context.Context, userID uint64) (err error) { return } + if err = svc.auth.RemoveAccessTokens(ctx, u); err != nil { + return + } + _ = svc.eventbus.WaitFor(ctx, event.UserAfterSuspend(u, &oldUser)) return nil }()