diff --git a/system/rest.yaml b/system/rest.yaml index 72f99a7bf..c7bf918ce 100644 --- a/system/rest.yaml +++ b/system/rest.yaml @@ -694,6 +694,16 @@ endpoints: type: string title: Script to execute required: true + - name: sessionsRemove + method: DELETE + title: Remove all auth sessions of user + path: "/{userID}/sessions" + parameters: + path: + - type: uint64 + name: userID + required: true + title: ID - title: Applications path: "/application" entrypoint: application diff --git a/system/rest/handlers/user.go b/system/rest/handlers/user.go index 6f373ce88..aef5ab27a 100644 --- a/system/rest/handlers/user.go +++ b/system/rest/handlers/user.go @@ -33,6 +33,7 @@ type ( MembershipAdd(context.Context, *request.UserMembershipAdd) (interface{}, error) MembershipRemove(context.Context, *request.UserMembershipRemove) (interface{}, error) TriggerScript(context.Context, *request.UserTriggerScript) (interface{}, error) + SessionsRemove(context.Context, *request.UserSessionsRemove) (interface{}, error) } // HTTP API interface @@ -51,6 +52,7 @@ type ( MembershipAdd func(http.ResponseWriter, *http.Request) MembershipRemove func(http.ResponseWriter, *http.Request) TriggerScript func(http.ResponseWriter, *http.Request) + SessionsRemove func(http.ResponseWriter, *http.Request) } ) @@ -278,6 +280,22 @@ func NewUser(h UserAPI) *User { return } + api.Send(w, r, value) + }, + SessionsRemove: func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + params := request.NewUserSessionsRemove() + if err := params.Fill(r); err != nil { + api.Send(w, r, err) + return + } + + value, err := h.SessionsRemove(r.Context(), params) + if err != nil { + api.Send(w, r, err) + return + } + api.Send(w, r, value) }, } @@ -300,5 +318,6 @@ func (h User) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.H r.Post("/users/{userID}/membership/{roleID}", h.MembershipAdd) r.Delete("/users/{userID}/membership/{roleID}", h.MembershipRemove) r.Post("/users/{userID}/trigger", h.TriggerScript) + r.Delete("/users/{userID}/sessions", h.SessionsRemove) }) } diff --git a/system/rest/request/user.go b/system/rest/request/user.go index 5f711b809..b1e93d52e 100644 --- a/system/rest/request/user.go +++ b/system/rest/request/user.go @@ -267,6 +267,13 @@ type ( // Script to execute Script string } + + UserSessionsRemove struct { + // UserID PATH parameter + // + // ID + UserID uint64 `json:",string"` + } ) // NewUserList request @@ -1188,3 +1195,38 @@ func (r *UserTriggerScript) Fill(req *http.Request) (err error) { return err } + +// NewUserSessionsRemove request +func NewUserSessionsRemove() *UserSessionsRemove { + return &UserSessionsRemove{} +} + +// Auditable returns all auditable/loggable parameters +func (r UserSessionsRemove) Auditable() map[string]interface{} { + return map[string]interface{}{ + "userID": r.UserID, + } +} + +// Auditable returns all auditable/loggable parameters +func (r UserSessionsRemove) GetUserID() uint64 { + return r.UserID +} + +// Fill processes request and fills internal variables +func (r *UserSessionsRemove) Fill(req *http.Request) (err error) { + + { + var val string + // path params + + val = chi.URLParam(req, "userID") + r.UserID, err = payload.ParseUint64(val), nil + if err != nil { + return err + } + + } + + return err +} diff --git a/system/rest/user.go b/system/rest/user.go index a5ceefd59..c1f53cf31 100644 --- a/system/rest/user.go +++ b/system/rest/user.go @@ -232,6 +232,26 @@ func (ctrl *User) TriggerScript(ctx context.Context, r *request.UserTriggerScrip } +func (ctrl *User) SessionsRemove(ctx context.Context, r *request.UserSessionsRemove) (rsp interface{}, err error) { + var ( + user *types.User + ) + + if user, err = ctrl.user.FindByID(ctx, r.UserID); err != nil { + return + } + + if err = ctrl.user.DeleteAuthSessionsByUserID(ctx, user.ID); err != nil { + return + } + + if err = ctrl.user.DeleteAuthTokensByUserID(ctx, user.ID); err != nil { + return + } + + return +} + func (ctrl User) makeFilterPayload(ctx context.Context, uu types.UserSet, f types.UserFilter, err error) (*userSetPayload, error) { if err != nil { return nil, err diff --git a/system/service/user.go b/system/service/user.go index 7920d9fa8..0d6514bcf 100644 --- a/system/service/user.go +++ b/system/service/user.go @@ -82,6 +82,9 @@ type ( SetPassword(ctx context.Context, userID uint64, password string) error Preloader(context.Context, userIdGetter, types.UserFilter, userSetter) error + + DeleteAuthTokensByUserID(ctx context.Context, userID uint64) (err error) + DeleteAuthSessionsByUserID(ctx context.Context, userID uint64) (err error) } ) @@ -750,6 +753,48 @@ rangeLoop: return uu.Walk(s) } +// DeleteAuthTokensByUserID will delete all auth tokens of user which will un-authorize all auth clients of user +func (svc user) DeleteAuthTokensByUserID(ctx context.Context, userID uint64) (err error) { + var ( + uaProps = &userActionProps{user: &types.User{ID: userID}} + ) + + err = func() (err error) { + if userID == 0 { + return UserErrInvalidID() + } + + if err = svc.store.DeleteAuthOA2TokenByUserID(ctx, userID); err != nil { + return + } + + return nil + }() + + return svc.recordAction(ctx, uaProps, UserActionDeleteAuthTokens, err) +} + +// DeleteAuthSessionsByUserID will delete all auth session of user +func (svc user) DeleteAuthSessionsByUserID(ctx context.Context, userID uint64) (err error) { + var ( + uaProps = &userActionProps{user: &types.User{ID: userID}} + ) + + err = func() (err error) { + if userID == 0 { + return UserErrInvalidID() + } + + if err = svc.store.DeleteAuthSessionsByUserID(ctx, userID); err != nil { + return + } + + return nil + }() + + return svc.recordAction(ctx, uaProps, UserActionDeleteAuthSessions, err) +} + // UniqueCheck verifies user's email, username and handle func uniqueUserCheck(ctx context.Context, s store.Storer, u *types.User) (err error) { isUnique := func(field string) bool { diff --git a/system/service/user_actions.gen.go b/system/service/user_actions.gen.go index 802b80aef..9969c9a72 100644 --- a/system/service/user_actions.gen.go +++ b/system/service/user_actions.gen.go @@ -506,6 +506,46 @@ func UserActionSetPassword(props ...*userActionProps) *userAction { return a } +// UserActionDeleteAuthTokens returns "system:user.deleteAuthTokens" action +// +// This function is auto-generated. +// +func UserActionDeleteAuthTokens(props ...*userActionProps) *userAction { + a := &userAction{ + timestamp: time.Now(), + resource: "system:user", + action: "deleteAuthTokens", + log: "deleted auth tokens of {user}", + severity: actionlog.Notice, + } + + if len(props) > 0 { + a.props = props[0] + } + + return a +} + +// UserActionDeleteAuthSessions returns "system:user.deleteAuthSessions" action +// +// This function is auto-generated. +// +func UserActionDeleteAuthSessions(props ...*userActionProps) *userAction { + a := &userAction{ + timestamp: time.Now(), + resource: "system:user", + action: "deleteAuthSessions", + log: "deleted auth sessions of {user}", + severity: actionlog.Notice, + } + + if len(props) > 0 { + a.props = props[0] + } + + return a +} + // ********************************************************************************************************************* // ********************************************************************************************************************* // Error constructors diff --git a/system/service/user_actions.yaml b/system/service/user_actions.yaml index 097a4c9a3..500b81a5b 100644 --- a/system/service/user_actions.yaml +++ b/system/service/user_actions.yaml @@ -59,6 +59,12 @@ actions: - action: setPassword log: "password changed for {user}" + - action: deleteAuthTokens + log: "deleted auth tokens of {user}" + + - action: deleteAuthSessions + log: "deleted auth sessions of {user}" + errors: - error: notFound message: "user not found"