From 8ccaa76d87d2fb9ddd37c60fe7b986a5f3f92782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=C5=BE=20Jerman?= Date: Mon, 26 Jul 2021 13:31:32 +0200 Subject: [PATCH] Improve default auth client handling Prevent default auth clients from changing their handle, being deleted, or de-activated. --- system/rest/auth_client.go | 8 ++ system/service/auth_client.go | 53 ++++++++++++- system/service/auth_client_actions.gen.go | 96 +++++++++++++++++++++++ system/service/auth_client_actions.yaml | 12 +++ system/service/service.go | 2 +- 5 files changed, 167 insertions(+), 4 deletions(-) diff --git a/system/rest/auth_client.go b/system/rest/auth_client.go index e6cbc1cfc..8c5b9ef8e 100644 --- a/system/rest/auth_client.go +++ b/system/rest/auth_client.go @@ -2,8 +2,10 @@ package rest import ( "context" + "github.com/cortezaproject/corteza-server/pkg/api" "github.com/cortezaproject/corteza-server/pkg/filter" + "github.com/cortezaproject/corteza-server/pkg/options" "github.com/cortezaproject/corteza-server/system/rest/request" "github.com/cortezaproject/corteza-server/system/service" "github.com/cortezaproject/corteza-server/system/types" @@ -16,6 +18,7 @@ type ( AuthClient struct { authClient authClientService ac authClientAccessController + opt options.AuthOpt } authClientService interface { @@ -27,6 +30,7 @@ type ( Undelete(ctx context.Context, ID uint64) (err error) ExposeSecret(ctx context.Context, ID uint64) (secret string, err error) RegenerateSecret(ctx context.Context, ID uint64) (secret string, err error) + IsDefaultClient(c *types.AuthClient) bool } authClientAccessController interface { @@ -39,6 +43,8 @@ type ( authClientPayload struct { *types.AuthClient + IsDefault bool `json:"isDefault"` + CanGrant bool `json:"canGrant"` CanUpdateAuthClient bool `json:"canUpdateAuthClient"` CanDeleteAuthClient bool `json:"canDeleteAuthClient"` @@ -153,6 +159,8 @@ func (ctrl AuthClient) makePayload(ctx context.Context, m *types.AuthClient, err return &authClientPayload{ AuthClient: m, + IsDefault: ctrl.authClient.IsDefaultClient(m), + CanGrant: ctrl.ac.CanGrant(ctx), CanUpdateAuthClient: ctrl.ac.CanUpdateAuthClient(ctx, m), diff --git a/system/service/auth_client.go b/system/service/auth_client.go index 9a4b9a444..c49136499 100644 --- a/system/service/auth_client.go +++ b/system/service/auth_client.go @@ -7,6 +7,7 @@ import ( "github.com/cortezaproject/corteza-server/pkg/errors" "github.com/cortezaproject/corteza-server/pkg/filter" "github.com/cortezaproject/corteza-server/pkg/label" + "github.com/cortezaproject/corteza-server/pkg/options" "github.com/cortezaproject/corteza-server/pkg/rand" "github.com/cortezaproject/corteza-server/store" "github.com/cortezaproject/corteza-server/system/service/event" @@ -20,6 +21,7 @@ type ( eventbus eventDispatcher actionlog actionlog.Recorder store store.Storer + opt options.AuthOpt } authClientAccessController interface { @@ -32,8 +34,14 @@ type ( ) // AuthClient is a default authClient service initializer -func AuthClient(s store.Storer, ac authClientAccessController, al actionlog.Recorder, eb eventDispatcher) *authClient { - return &authClient{store: s, ac: ac, actionlog: al, eventbus: eb} +func AuthClient(s store.Storer, ac authClientAccessController, al actionlog.Recorder, eb eventDispatcher, opt options.AuthOpt) *authClient { + return &authClient{ + store: s, + ac: ac, + actionlog: al, + eventbus: eb, + opt: opt, + } } func (svc *authClient) LookupByID(ctx context.Context, ID uint64) (client *types.AuthClient, err error) { @@ -80,6 +88,14 @@ func (svc *authClient) RegenerateSecret(ctx context.Context, ID uint64) (secret return secret, svc.recordAction(ctx, aaProps, AuthClientActionRegenerateSecret, err) } +func (svc *authClient) IsDefaultClient(c *types.AuthClient) bool { + if c == nil { + return false + } + + return c.Handle == svc.opt.DefaultClient +} + func (svc *authClient) lookupByID(ctx context.Context, ID uint64) (client *types.AuthClient, err error) { err = func() error { if ID == 0 { @@ -222,7 +238,24 @@ func (svc *authClient) Create(ctx context.Context, new *types.AuthClient) (app * func (svc *authClient) Update(ctx context.Context, upd *types.AuthClient) (app *types.AuthClient, err error) { var ( - aaProps = &authClientActionProps{update: upd} + aaProps = &authClientActionProps{update: upd} + defaultClientValidator = func(old, upd *types.AuthClient) error { + if old.Handle != svc.opt.DefaultClient { + return nil + } + + // The handle may not change + if old.Handle != upd.Handle { + return AuthClientErrUnableToChangeDefaultClientHandle() + } + + // The client may not get disabled + if !upd.Enabled { + return AuthClientErrUnableToDisableDefaultClient() + } + + return nil + } ) err = func() (err error) { @@ -247,10 +280,20 @@ func (svc *authClient) Update(ctx context.Context, upd *types.AuthClient) (app * return AuthClientErrNotAllowedToUpdate() } + // Firstly validate before the automation occurs + if err = defaultClientValidator(app, upd); err != nil { + return err + } + if err = svc.eventbus.WaitFor(ctx, event.AuthClientBeforeUpdate(upd, app)); err != nil { return } + // Next validate after the automation occurs + if err = defaultClientValidator(app, upd); err != nil { + return err + } + // Assign changed values after afterUpdate events are emitted app.Handle = upd.Handle app.ValidGrant = upd.ValidGrant @@ -309,6 +352,10 @@ func (svc *authClient) Delete(ctx context.Context, ID uint64) (err error) { return AuthClientErrNotAllowedToDelete() } + if app.Handle == svc.opt.DefaultClient { + return AuthClientErrUnableToDeleteDefaultClient() + } + if err = svc.eventbus.WaitFor(ctx, event.AuthClientBeforeDelete(nil, app)); err != nil { return } diff --git a/system/service/auth_client_actions.gen.go b/system/service/auth_client_actions.gen.go index bd3819d98..1817a86e6 100644 --- a/system/service/auth_client_actions.gen.go +++ b/system/service/auth_client_actions.gen.go @@ -498,6 +498,102 @@ func AuthClientErrInvalidID(mm ...*authClientActionProps) *errors.Error { return e } +// AuthClientErrUnableToChangeDefaultClientHandle returns "system:auth-client.unableToChangeDefaultClientHandle" as *errors.Error +// +// +// This function is auto-generated. +// +func AuthClientErrUnableToChangeDefaultClientHandle(mm ...*authClientActionProps) *errors.Error { + var p = &authClientActionProps{} + if len(mm) > 0 { + p = mm[0] + } + + var e = errors.New( + errors.KindInternal, + + p.Format("unable to change the handle of the default auth client", nil), + + errors.Meta("type", "unableToChangeDefaultClientHandle"), + errors.Meta("resource", "system:auth-client"), + + // action log entry; no formatting, it will be applied inside recordAction fn. + errors.Meta(authClientLogMetaKey{}, "failed to update {authClient}; unable to change the default auth client handle"), + errors.Meta(authClientPropsMetaKey{}, p), + + errors.StackSkip(1), + ) + + if len(mm) > 0 { + } + + return e +} + +// AuthClientErrUnableToDisableDefaultClient returns "system:auth-client.unableToDisableDefaultClient" as *errors.Error +// +// +// This function is auto-generated. +// +func AuthClientErrUnableToDisableDefaultClient(mm ...*authClientActionProps) *errors.Error { + var p = &authClientActionProps{} + if len(mm) > 0 { + p = mm[0] + } + + var e = errors.New( + errors.KindInternal, + + p.Format("unable to disable the default auth client", nil), + + errors.Meta("type", "unableToDisableDefaultClient"), + errors.Meta("resource", "system:auth-client"), + + // action log entry; no formatting, it will be applied inside recordAction fn. + errors.Meta(authClientLogMetaKey{}, "failed to update {authClient}; unable to disable the default auth client"), + errors.Meta(authClientPropsMetaKey{}, p), + + errors.StackSkip(1), + ) + + if len(mm) > 0 { + } + + return e +} + +// AuthClientErrUnableToDeleteDefaultClient returns "system:auth-client.unableToDeleteDefaultClient" as *errors.Error +// +// +// This function is auto-generated. +// +func AuthClientErrUnableToDeleteDefaultClient(mm ...*authClientActionProps) *errors.Error { + var p = &authClientActionProps{} + if len(mm) > 0 { + p = mm[0] + } + + var e = errors.New( + errors.KindInternal, + + p.Format("unable to delete the default auth client", nil), + + errors.Meta("type", "unableToDeleteDefaultClient"), + errors.Meta("resource", "system:auth-client"), + + // action log entry; no formatting, it will be applied inside recordAction fn. + errors.Meta(authClientLogMetaKey{}, "failed to update {authClient}; unable to delete the default auth client"), + errors.Meta(authClientPropsMetaKey{}, p), + + errors.StackSkip(1), + ) + + if len(mm) > 0 { + } + + return e +} + // AuthClientErrNotAllowedToRead returns "system:auth-client.notAllowedToRead" as *errors.Error // // diff --git a/system/service/auth_client_actions.yaml b/system/service/auth_client_actions.yaml index 1c6e0c209..f84755b2e 100644 --- a/system/service/auth_client_actions.yaml +++ b/system/service/auth_client_actions.yaml @@ -61,6 +61,18 @@ errors: message: "invalid ID" severity: warning + - error: unableToChangeDefaultClientHandle + message: "unable to change the handle of the default auth client" + log: "failed to update {authClient}; unable to change the default auth client handle" + + - error: unableToDisableDefaultClient + message: "unable to disable the default auth client" + log: "failed to update {authClient}; unable to disable the default auth client" + + - error: unableToDeleteDefaultClient + message: "unable to delete the default auth client" + log: "failed to update {authClient}; unable to delete the default auth client" + - error: notAllowedToRead message: "not allowed to read this auth client" log: "failed to read {authClient}; insufficient permissions" diff --git a/system/service/service.go b/system/service/service.go index d1ddc9810..71c6403c4 100644 --- a/system/service/service.go +++ b/system/service/service.go @@ -159,7 +159,7 @@ func Initialize(ctx context.Context, log *zap.Logger, s store.Storer, ws websock DefaultRenderer = Renderer(c.Template) DefaultAuthNotification = AuthNotification(CurrentSettings, DefaultRenderer, c.Auth) DefaultAuth = Auth() - DefaultAuthClient = AuthClient(DefaultStore, DefaultAccessControl, DefaultActionlog, eventbus.Service()) + DefaultAuthClient = AuthClient(DefaultStore, DefaultAccessControl, DefaultActionlog, eventbus.Service(), c.Auth) DefaultUser = User() DefaultRole = Role() DefaultApplication = Application(DefaultStore, DefaultAccessControl, DefaultActionlog, eventbus.Service())