Refactored access token removal logic
This commit is contained in:
+3
-57
@@ -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
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
}
|
||||
|
||||
Generated
+23
-2
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}()
|
||||
|
||||
@@ -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
|
||||
}()
|
||||
|
||||
Reference in New Issue
Block a user