diff --git a/api/http/common.go b/api/http/common.go index 36210d4b5e..802c872e63 100644 --- a/api/http/common.go +++ b/api/http/common.go @@ -127,6 +127,7 @@ func EncodeError(_ context.Context, err error, w http.ResponseWriter) { switch { case errors.Contains(err, svcerr.ErrAuthorization), errors.Contains(err, svcerr.ErrDomainAuthorization), + errors.Contains(err, svcerr.ErrUnauthorizedPAT), errors.Contains(err, bootstrap.ErrExternalKey), errors.Contains(err, bootstrap.ErrExternalKeySecure): err = unwrap(err) diff --git a/auth/api/grpc/utils.go b/auth/api/grpc/utils.go index aef3da53c2..5823d853d0 100644 --- a/auth/api/grpc/utils.go +++ b/auth/api/grpc/utils.go @@ -25,7 +25,9 @@ func EncodeError(err error) error { err == apiutil.ErrMissingMemberType, err == apiutil.ErrMissingPolicySub, err == apiutil.ErrMissingPolicyObj, - err == apiutil.ErrMalformedPolicyAct: + err == apiutil.ErrMalformedPolicyAct, + err == apiutil.ErrMissingUserID, + err == apiutil.ErrMissingPATID: return status.Error(codes.InvalidArgument, err.Error()) case errors.Contains(err, svcerr.ErrAuthentication), errors.Contains(err, auth.ErrKeyExpired), diff --git a/auth/hasher/doc.go b/auth/hasher/doc.go index 98be992262..e762b1335e 100644 --- a/auth/hasher/doc.go +++ b/auth/hasher/doc.go @@ -2,5 +2,5 @@ // SPDX-License-Identifier: Apache-2.0 // Package hasher contains the domain concept definitions needed to -// support Magistrala users password hasher sub-service functionality. +// support Supermq users password hasher sub-service functionality. package hasher diff --git a/auth/tracing/tracing.go b/auth/tracing/tracing.go index 0321eaf3bd..945bd8f4e8 100644 --- a/auth/tracing/tracing.go +++ b/auth/tracing/tracing.go @@ -203,7 +203,7 @@ func (tm *tracingMiddleware) AuthorizePAT(ctx context.Context, userID, patID str func (tm *tracingMiddleware) CheckPAT(ctx context.Context, userID, patID string, platformEntityType auth.PlatformEntityType, optionalDomainID string, optionalDomainEntityType auth.DomainEntityType, operation auth.OperationType, entityIDs ...string) error { ctx, span := tm.tracer.Start(ctx, "check_pat", trace.WithAttributes( attribute.String("user_id", userID), - attribute.String("patID", patID), + attribute.String("pat_id", patID), attribute.String("platform_entity", platformEntityType.String()), attribute.String("optional_domain_id", optionalDomainID), attribute.String("optional_domain_entity", optionalDomainEntityType.String()), diff --git a/clients/middleware/authorization.go b/clients/middleware/authorization.go index 6a386b1bfc..3729d463c9 100644 --- a/clients/middleware/authorization.go +++ b/clients/middleware/authorization.go @@ -6,9 +6,10 @@ package middleware import ( "context" + "github.com/absmach/supermq/auth" "github.com/absmach/supermq/clients" "github.com/absmach/supermq/pkg/authn" - "github.com/absmach/supermq/pkg/authz" + smqauthz "github.com/absmach/supermq/pkg/authz" "github.com/absmach/supermq/pkg/errors" svcerr "github.com/absmach/supermq/pkg/errors/service" "github.com/absmach/supermq/pkg/policies" @@ -18,18 +19,18 @@ import ( ) var ( - errView = errors.New("not authorized to view client") - errUpdate = errors.New("not authorized to update client") - errUpdateTags = errors.New("not authorized to update client tags") - errUpdateSecret = errors.New("not authorized to update client secret") - errEnable = errors.New("not authorized to enable client") - errDisable = errors.New("not authorized to disable client") - errDelete = errors.New("not authorized to delete client") - errSetParentGroup = errors.New("not authorized to set parent group to client") - errRemoveParentGroup = errors.New("not authorized to remove parent group from client") - errDomainCreateClients = errors.New("not authorized to create client in domain") - errGroupSetChildClients = errors.New("not authorized to set child client for group") - errGroupRemoveChildClients = errors.New("not authorized to remove child client for group") + errView = errors.New("not authorized to view thing") + errUpdate = errors.New("not authorized to update thing") + errUpdateTags = errors.New("not authorized to update thing tags") + errUpdateSecret = errors.New("not authorized to update thing secret") + errEnable = errors.New("not authorized to enable thing") + errDisable = errors.New("not authorized to disable thing") + errDelete = errors.New("not authorized to delete thing") + errSetParentGroup = errors.New("not authorized to set parent group to thing") + errRemoveParentGroup = errors.New("not authorized to remove parent group from thing") + errDomainCreateClients = errors.New("not authorized to create thing in domain") + errGroupSetChildClients = errors.New("not authorized to set child thing for group") + errGroupRemoveChildClients = errors.New("not authorized to remove child thing for group") ) var _ clients.Service = (*authorizationMiddleware)(nil) @@ -37,16 +38,16 @@ var _ clients.Service = (*authorizationMiddleware)(nil) type authorizationMiddleware struct { svc clients.Service repo clients.Repository - authz authz.Authorization + authz smqauthz.Authorization opp svcutil.OperationPerm extOpp svcutil.ExternalOperationPerm rmMW.RoleManagerAuthorizationMiddleware } // AuthorizationMiddleware adds authorization to the clients service. -func AuthorizationMiddleware(entityType string, svc clients.Service, authz authz.Authorization, repo clients.Repository, clientsOpPerm, rolesOpPerm map[svcutil.Operation]svcutil.Permission, extOpPerm map[svcutil.ExternalOperation]svcutil.Permission) (clients.Service, error) { +func AuthorizationMiddleware(entityType string, svc clients.Service, authz smqauthz.Authorization, repo clients.Repository, thingsOpPerm, rolesOpPerm map[svcutil.Operation]svcutil.Permission, extOpPerm map[svcutil.ExternalOperation]svcutil.Permission) (clients.Service, error) { opp := clients.NewOperationPerm() - if err := opp.AddOperationPermissionMap(clientsOpPerm); err != nil { + if err := opp.AddOperationPermissionMap(thingsOpPerm); err != nil { return nil, err } if err := opp.Validate(); err != nil { @@ -74,7 +75,21 @@ func AuthorizationMiddleware(entityType string, svc clients.Service, authz authz } func (am *authorizationMiddleware) CreateClients(ctx context.Context, session authn.Session, client ...clients.Client) ([]clients.Client, []roles.RoleProvision, error) { - if err := am.extAuthorize(ctx, clients.DomainOpCreateClient, authz.PolicyReq{ + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainClientsScope, + Operation: auth.CreateOp, + EntityIDs: auth.AnyIDs{}.Values(), + }); err != nil { + return []clients.Client{}, []roles.RoleProvision{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + + if err := am.extAuthorize(ctx, clients.DomainOpCreateClient, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -88,7 +103,21 @@ func (am *authorizationMiddleware) CreateClients(ctx context.Context, session au } func (am *authorizationMiddleware) View(ctx context.Context, session authn.Session, id string) (clients.Client, error) { - if err := am.authorize(ctx, clients.OpViewClient, authz.PolicyReq{ + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainClientsScope, + Operation: auth.ReadOp, + EntityIDs: []string{id}, + }); err != nil { + return clients.Client{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + + if err := am.authorize(ctx, clients.OpViewClient, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -101,6 +130,20 @@ func (am *authorizationMiddleware) View(ctx context.Context, session authn.Sessi } func (am *authorizationMiddleware) ListClients(ctx context.Context, session authn.Session, reqUserID string, pm clients.Page) (clients.ClientsPage, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainClientsScope, + Operation: auth.ListOp, + EntityIDs: auth.AnyIDs{}.Values(), + }); err != nil { + return clients.ClientsPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + if err := am.checkSuperAdmin(ctx, session.UserID); err != nil { session.SuperAdmin = true } @@ -109,7 +152,21 @@ func (am *authorizationMiddleware) ListClients(ctx context.Context, session auth } func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Session, client clients.Client) (clients.Client, error) { - if err := am.authorize(ctx, clients.OpUpdateClient, authz.PolicyReq{ + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainClientsScope, + Operation: auth.UpdateOp, + EntityIDs: []string{client.ID}, + }); err != nil { + return clients.Client{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + + if err := am.authorize(ctx, clients.OpUpdateClient, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -123,7 +180,21 @@ func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Ses } func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn.Session, client clients.Client) (clients.Client, error) { - if err := am.authorize(ctx, clients.OpUpdateClientTags, authz.PolicyReq{ + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainClientsScope, + Operation: auth.UpdateOp, + EntityIDs: []string{client.ID}, + }); err != nil { + return clients.Client{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + + if err := am.authorize(ctx, clients.OpUpdateClientTags, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -137,7 +208,21 @@ func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn } func (am *authorizationMiddleware) UpdateSecret(ctx context.Context, session authn.Session, id, key string) (clients.Client, error) { - if err := am.authorize(ctx, clients.OpUpdateClientSecret, authz.PolicyReq{ + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainClientsScope, + Operation: auth.UpdateOp, + EntityIDs: []string{id}, + }); err != nil { + return clients.Client{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + + if err := am.authorize(ctx, clients.OpUpdateClientSecret, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -150,7 +235,21 @@ func (am *authorizationMiddleware) UpdateSecret(ctx context.Context, session aut } func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Session, id string) (clients.Client, error) { - if err := am.authorize(ctx, clients.OpEnableClient, authz.PolicyReq{ + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainClientsScope, + Operation: auth.UpdateOp, + EntityIDs: []string{id}, + }); err != nil { + return clients.Client{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + + if err := am.authorize(ctx, clients.OpEnableClient, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -164,7 +263,21 @@ func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Ses } func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Session, id string) (clients.Client, error) { - if err := am.authorize(ctx, clients.OpDisableClient, authz.PolicyReq{ + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainClientsScope, + Operation: auth.UpdateOp, + EntityIDs: []string{id}, + }); err != nil { + return clients.Client{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + + if err := am.authorize(ctx, clients.OpDisableClient, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -177,7 +290,20 @@ func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Se } func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Session, id string) error { - if err := am.authorize(ctx, clients.OpDeleteClient, authz.PolicyReq{ + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainClientsScope, + Operation: auth.DeleteOp, + EntityIDs: []string{id}, + }); err != nil { + return errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + if err := am.authorize(ctx, clients.OpDeleteClient, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -191,7 +317,21 @@ func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Ses } func (am *authorizationMiddleware) SetParentGroup(ctx context.Context, session authn.Session, parentGroupID string, id string) error { - if err := am.authorize(ctx, clients.OpSetParentGroup, authz.PolicyReq{ + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainGroupsScope, + Operation: auth.UpdateOp, + EntityIDs: []string{id}, + }); err != nil { + return errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + + if err := am.authorize(ctx, clients.OpSetParentGroup, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -201,7 +341,7 @@ func (am *authorizationMiddleware) SetParentGroup(ctx context.Context, session a return errors.Wrap(err, errSetParentGroup) } - if err := am.extAuthorize(ctx, clients.GroupOpSetChildClient, authz.PolicyReq{ + if err := am.extAuthorize(ctx, clients.GroupOpSetChildClient, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -214,7 +354,21 @@ func (am *authorizationMiddleware) SetParentGroup(ctx context.Context, session a } func (am *authorizationMiddleware) RemoveParentGroup(ctx context.Context, session authn.Session, id string) error { - if err := am.authorize(ctx, clients.OpRemoveParentGroup, authz.PolicyReq{ + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: auth.PlatformDomainsScope, + OptionalDomainID: session.DomainID, + OptionalDomainEntityType: auth.DomainGroupsScope, + Operation: auth.DeleteOp, + EntityIDs: []string{id}, + }); err != nil { + return errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + + if err := am.authorize(ctx, clients.OpRemoveParentGroup, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, @@ -224,18 +378,18 @@ func (am *authorizationMiddleware) RemoveParentGroup(ctx context.Context, sessio return errors.Wrap(err, errRemoveParentGroup) } - cli, err := am.repo.RetrieveByID(ctx, id) + th, err := am.repo.RetrieveByID(ctx, id) if err != nil { return errors.Wrap(svcerr.ErrRemoveEntity, err) } - if cli.ParentGroup != "" { - if err := am.extAuthorize(ctx, clients.GroupOpSetChildClient, authz.PolicyReq{ + if th.ParentGroup != "" { + if err := am.extAuthorize(ctx, clients.GroupOpSetChildClient, smqauthz.PolicyReq{ Domain: session.DomainID, SubjectType: policies.UserType, Subject: session.DomainUserID, ObjectType: policies.GroupType, - Object: cli.ParentGroup, + Object: th.ParentGroup, }); err != nil { return errors.Wrap(err, errGroupRemoveChildClients) } @@ -244,7 +398,7 @@ func (am *authorizationMiddleware) RemoveParentGroup(ctx context.Context, sessio return nil } -func (am *authorizationMiddleware) authorize(ctx context.Context, op svcutil.Operation, req authz.PolicyReq) error { +func (am *authorizationMiddleware) authorize(ctx context.Context, op svcutil.Operation, req smqauthz.PolicyReq) error { perm, err := am.opp.GetPermission(op) if err != nil { return err @@ -259,11 +413,12 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, op svcutil.Ope return nil } -func (am *authorizationMiddleware) extAuthorize(ctx context.Context, extOp svcutil.ExternalOperation, req authz.PolicyReq) error { +func (am *authorizationMiddleware) extAuthorize(ctx context.Context, extOp svcutil.ExternalOperation, req smqauthz.PolicyReq) error { perm, err := am.extOpp.GetPermission(extOp) if err != nil { return err } + req.Permission = perm.String() if err := am.authz.Authorize(ctx, req); err != nil { @@ -274,7 +429,7 @@ func (am *authorizationMiddleware) extAuthorize(ctx context.Context, extOp svcut } func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, userID string) error { - if err := am.authz.Authorize(ctx, authz.PolicyReq{ + if err := am.authz.Authorize(ctx, smqauthz.PolicyReq{ SubjectType: policies.UserType, Subject: userID, Permission: policies.AdminPermission, diff --git a/pkg/authz/authsvc/authz.go b/pkg/authz/authsvc/authz.go index 1c2801c04a..f0d1de592b 100644 --- a/pkg/authz/authsvc/authz.go +++ b/pkg/authz/authsvc/authz.go @@ -121,3 +121,23 @@ func (a authorization) checkDomain(ctx context.Context, subjectType, subject, do return svcerr.ErrInvalidStatus } } + +func (a authorization) AuthorizePAT(ctx context.Context, pr authz.PatReq) error { + req := grpcAuthV1.AuthZPatReq{ + UserId: pr.UserID, + PatId: pr.PatID, + PlatformEntityType: uint32(pr.PlatformEntityType), + OptionalDomainId: pr.OptionalDomainID, + OptionalDomainEntityType: uint32(pr.OptionalDomainEntityType), + Operation: uint32(pr.Operation), + EntityIds: pr.EntityIDs, + } + res, err := a.authSvcClient.AuthorizePAT(ctx, &req) + if err != nil { + return errors.Wrap(errors.ErrAuthorization, err) + } + if !res.Authorized { + return errors.ErrAuthorization + } + return nil +} diff --git a/pkg/authz/authz.go b/pkg/authz/authz.go index f6f27e0f2a..93d807ac5e 100644 --- a/pkg/authz/authz.go +++ b/pkg/authz/authz.go @@ -3,7 +3,11 @@ package authz -import "context" +import ( + "context" + + "github.com/absmach/supermq/auth" +) type PolicyReq struct { // Domain contains the domain ID. @@ -42,9 +46,20 @@ type PolicyReq struct { Permission string `json:"permission,omitempty"` } +type PatReq struct { + UserID string `json:"user_id,omitempty"` // UserID + PatID string `json:"pat_id,omitempty"` // UserID + PlatformEntityType auth.PlatformEntityType `json:"platform_entity_type,omitempty"` // Platform entity type + OptionalDomainID string `json:"optional_domainID,omitempty"` // Optional domain id + OptionalDomainEntityType auth.DomainEntityType `json:"optional_domain_entity_type,omitempty"` // Optional domain entity type + Operation auth.OperationType `json:"operation,omitempty"` // Operation + EntityIDs []string `json:"entityIDs,omitempty"` // EntityIDs +} + // Authz is supermq authorization library. // //go:generate mockery --name Authorization --output=./mocks --filename authz.go --quiet --note "Copyright (c) Abstract Machines" type Authorization interface { Authorize(ctx context.Context, pr PolicyReq) error + AuthorizePAT(ctx context.Context, pr PatReq) error } diff --git a/pkg/authz/mocks/authz.go b/pkg/authz/mocks/authz.go index f3153680c9..69a75517bd 100644 --- a/pkg/authz/mocks/authz.go +++ b/pkg/authz/mocks/authz.go @@ -35,6 +35,24 @@ func (_m *Authorization) Authorize(ctx context.Context, pr authz.PolicyReq) erro return r0 } +// AuthorizePAT provides a mock function with given fields: ctx, pr +func (_m *Authorization) AuthorizePAT(ctx context.Context, pr authz.PatReq) error { + ret := _m.Called(ctx, pr) + + if len(ret) == 0 { + panic("no return value specified for AuthorizePAT") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, authz.PatReq) error); ok { + r0 = rf(ctx, pr) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // NewAuthorization creates a new instance of Authorization. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewAuthorization(t interface { diff --git a/pkg/errors/service/types.go b/pkg/errors/service/types.go index 7fe49f6134..928a2268b4 100644 --- a/pkg/errors/service/types.go +++ b/pkg/errors/service/types.go @@ -84,4 +84,7 @@ var ( // ErrRollbackRepo indicates a failure to rollback repository. ErrRollbackRepo = errors.New("failed to rollback repo") + + // ErrUnauthorizedPAT indicates failure occurred while authorizing PAT. + ErrUnauthorizedPAT = errors.New("failed to authorize PAT") ) diff --git a/users/middleware/authorization.go b/users/middleware/authorization.go index e5774f8c79..b38e622f03 100644 --- a/users/middleware/authorization.go +++ b/users/middleware/authorization.go @@ -9,8 +9,8 @@ import ( grpcTokenV1 "github.com/absmach/supermq/api/grpc/token/v1" smqauth "github.com/absmach/supermq/auth" "github.com/absmach/supermq/pkg/authn" - "github.com/absmach/supermq/pkg/authz" smqauthz "github.com/absmach/supermq/pkg/authz" + "github.com/absmach/supermq/pkg/errors" svcerr "github.com/absmach/supermq/pkg/errors/service" "github.com/absmach/supermq/pkg/policies" "github.com/absmach/supermq/users" @@ -44,6 +44,19 @@ func (am *authorizationMiddleware) Register(ctx context.Context, session authn.S } func (am *authorizationMiddleware) View(ctx context.Context, session authn.Session, id string) (users.User, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.ReadOp, + EntityIDs: []string{id}, + }); err != nil { + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { session.SuperAdmin = true } @@ -52,10 +65,34 @@ func (am *authorizationMiddleware) View(ctx context.Context, session authn.Sessi } func (am *authorizationMiddleware) ViewProfile(ctx context.Context, session authn.Session) (users.User, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.ReadOp, + EntityIDs: []string{session.UserID}, + }); err != nil { + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } return am.svc.ViewProfile(ctx, session) } func (am *authorizationMiddleware) ListUsers(ctx context.Context, session authn.Session, pm users.Page) (users.UsersPage, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.ListOp, + EntityIDs: smqauth.AnyIDs{}.Values(), + }); err != nil { + return users.UsersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { session.SuperAdmin = true } @@ -64,6 +101,49 @@ func (am *authorizationMiddleware) ListUsers(ctx context.Context, session authn. } func (am *authorizationMiddleware) ListMembers(ctx context.Context, session authn.Session, objectKind, objectID string, pm users.Page) (users.MembersPage, error) { + if session.Type == authn.PersonalAccessToken { + switch objectKind { + case policies.GroupsKind: + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + OptionalDomainID: session.DomainID, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainGroupsScope, + Operation: smqauth.ListOp, + EntityIDs: smqauth.AnyIDs{}.Values(), + }); err != nil { + return users.MembersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + case policies.DomainsKind: + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + OptionalDomainID: session.DomainID, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainManagementScope, + Operation: smqauth.ListOp, + EntityIDs: smqauth.AnyIDs{}.Values(), + }); err != nil { + return users.MembersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + case policies.ClientsKind: + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + OptionalDomainID: session.DomainID, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainClientsScope, + Operation: smqauth.ListOp, + EntityIDs: smqauth.AnyIDs{}.Values(), + }); err != nil { + return users.MembersPage{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + default: + return users.MembersPage{}, svcerr.ErrAuthorization + } + } + if session.DomainUserID == "" { return users.MembersPage{}, svcerr.ErrDomainAuthorization } @@ -92,6 +172,19 @@ func (am *authorizationMiddleware) SearchUsers(ctx context.Context, pm users.Pag } func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Session, user users.User) (users.User, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.UpdateOp, + EntityIDs: []string{user.ID}, + }); err != nil { + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { session.SuperAdmin = true } @@ -100,6 +193,19 @@ func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Ses } func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn.Session, user users.User) (users.User, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.UpdateOp, + EntityIDs: []string{user.ID}, + }); err != nil { + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { session.SuperAdmin = true } @@ -108,6 +214,18 @@ func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn } func (am *authorizationMiddleware) UpdateEmail(ctx context.Context, session authn.Session, id, email string) (users.User, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.UpdateOp, + EntityIDs: []string{id}, + }); err != nil { + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { session.SuperAdmin = true } @@ -116,6 +234,19 @@ func (am *authorizationMiddleware) UpdateEmail(ctx context.Context, session auth } func (am *authorizationMiddleware) UpdateUsername(ctx context.Context, session authn.Session, id, username string) (users.User, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.UpdateOp, + EntityIDs: []string{id}, + }); err != nil { + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { session.SuperAdmin = true } @@ -124,9 +255,23 @@ func (am *authorizationMiddleware) UpdateUsername(ctx context.Context, session a } func (am *authorizationMiddleware) UpdateProfilePicture(ctx context.Context, session authn.Session, user users.User) (users.User, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.UpdateOp, + EntityIDs: []string{user.ID}, + }); err != nil { + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { session.SuperAdmin = true } + return am.svc.UpdateProfilePicture(ctx, session, user) } @@ -135,6 +280,19 @@ func (am *authorizationMiddleware) GenerateResetToken(ctx context.Context, email } func (am *authorizationMiddleware) UpdateSecret(ctx context.Context, session authn.Session, oldSecret, newSecret string) (users.User, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.UpdateOp, + EntityIDs: []string{session.UserID}, + }); err != nil { + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + return am.svc.UpdateSecret(ctx, session, oldSecret, newSecret) } @@ -147,6 +305,19 @@ func (am *authorizationMiddleware) SendPasswordReset(ctx context.Context, host, } func (am *authorizationMiddleware) UpdateRole(ctx context.Context, session authn.Session, user users.User) (users.User, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.UpdateOp, + EntityIDs: []string{user.ID}, + }); err != nil { + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + if err := am.checkSuperAdmin(ctx, session.UserID); err != nil { return users.User{}, err } @@ -159,6 +330,19 @@ func (am *authorizationMiddleware) UpdateRole(ctx context.Context, session authn } func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Session, id string) (users.User, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.UpdateOp, + EntityIDs: []string{id}, + }); err != nil { + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { session.SuperAdmin = true } @@ -167,6 +351,19 @@ func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Ses } func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Session, id string) (users.User, error) { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.UpdateOp, + EntityIDs: []string{id}, + }); err != nil { + return users.User{}, errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { session.SuperAdmin = true } @@ -175,6 +372,19 @@ func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Se } func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Session, id string) error { + if session.Type == authn.PersonalAccessToken { + if err := am.authz.AuthorizePAT(ctx, smqauthz.PatReq{ + UserID: session.UserID, + PatID: session.ID, + PlatformEntityType: smqauth.PlatformUsersScope, + OptionalDomainEntityType: smqauth.DomainNullScope, + Operation: smqauth.DeleteOp, + EntityIDs: []string{id}, + }); err != nil { + return errors.Wrap(svcerr.ErrUnauthorizedPAT, err) + } + } + if err := am.checkSuperAdmin(ctx, session.UserID); err == nil { session.SuperAdmin = true } @@ -206,7 +416,7 @@ func (am *authorizationMiddleware) OAuthAddUserPolicy(ctx context.Context, user } func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, adminID string) error { - if err := am.authz.Authorize(ctx, authz.PolicyReq{ + if err := am.authz.Authorize(ctx, smqauthz.PolicyReq{ SubjectType: policies.UserType, Subject: adminID, Permission: policies.AdminPermission, @@ -219,7 +429,7 @@ func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, adminID } func (am *authorizationMiddleware) authorize(ctx context.Context, domain, subjType, subjKind, subj, perm, objType, obj string) error { - req := authz.PolicyReq{ + req := smqauthz.PolicyReq{ Domain: domain, SubjectType: subjType, SubjectKind: subjKind,