diff --git a/channels/middleware/authorization.go b/channels/middleware/authorization.go index 8de28bb9b7..41f5dfe51b 100644 --- a/channels/middleware/authorization.go +++ b/channels/middleware/authorization.go @@ -20,7 +20,6 @@ import ( "github.com/absmach/supermq/pkg/policies" "github.com/absmach/supermq/pkg/roles" rmMW "github.com/absmach/supermq/pkg/roles/rolemanager/middleware" - "github.com/absmach/supermq/pkg/svcutil" ) var ( @@ -48,8 +47,8 @@ type authorizationMiddleware struct { svc channels.Service repo channels.Repository authz smqauthz.Authorization - opp svcutil.OperationPerm - extOpp svcutil.ExternalOperationPerm + opp channels.OperationPerm + extOpp channels.ExternalOperationPerm callout callout.Callout rmMW.RoleManagerAuthorizationMiddleware } @@ -59,8 +58,9 @@ func AuthorizationMiddleware( svc channels.Service, repo channels.Repository, authz smqauthz.Authorization, - channelsOpPerm, rolesOpPerm map[svcutil.Operation]svcutil.Permission, - extOpPerm map[svcutil.ExternalOperation]svcutil.Permission, + channelsOpPerm map[channels.Operation]channels.Permission, + rolesOpPerm map[roles.Operation]roles.Permission, + extOpPerm map[channels.ExternalOperation]channels.Permission, callout callout.Callout, ) (channels.Service, error) { opp := channels.NewOperationPerm() @@ -78,6 +78,7 @@ func AuthorizationMiddleware( if err := extOpp.Validate(); err != nil { return nil, err } + ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(policies.ChannelType, svc, authz, rolesOpPerm, callout) if err != nil { return nil, err @@ -134,7 +135,7 @@ func (am *authorizationMiddleware) CreateChannels(ctx context.Context, session a "entities": chs, "count": len(chs), } - if err := am.callOut(ctx, session, channels.OpCreateChannel.String(channels.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, channels.OpCreateChannel.String(), params); err != nil { return []channels.Channel{}, []roles.RoleProvision{}, err } @@ -167,7 +168,7 @@ func (am *authorizationMiddleware) ViewChannel(ctx context.Context, session auth params := map[string]any{ "entity_id": id, } - if err := am.callOut(ctx, session, channels.OpViewChannel.String(channels.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, channels.OpViewChannel.String(), params); err != nil { return channels.Channel{}, err } return am.svc.ViewChannel(ctx, session, id, withRoles) @@ -193,7 +194,7 @@ func (am *authorizationMiddleware) ListChannels(ctx context.Context, session aut params := map[string]any{ "pagemeta": pm, } - if err := am.callOut(ctx, session, channels.OpListChannels.String(channels.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, channels.OpListChannels.String(), params); err != nil { return channels.ChannelsPage{}, err } return am.svc.ListChannels(ctx, session, pm) @@ -219,7 +220,7 @@ func (am *authorizationMiddleware) ListUserChannels(ctx context.Context, session "user_id": userID, "pagemeta": pm, } - if err := am.callOut(ctx, session, channels.OpListUserChannels.String(channels.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, channels.OpListUserChannels.String(), params); err != nil { return channels.ChannelsPage{}, err } return am.svc.ListUserChannels(ctx, session, userID, pm) @@ -251,7 +252,7 @@ func (am *authorizationMiddleware) UpdateChannel(ctx context.Context, session au params := map[string]any{ "entity_id": channel.ID, } - if err := am.callOut(ctx, session, channels.OpUpdateChannel.String(channels.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, channels.OpUpdateChannel.String(), params); err != nil { return channels.Channel{}, err } return am.svc.UpdateChannel(ctx, session, channel) @@ -283,7 +284,7 @@ func (am *authorizationMiddleware) UpdateChannelTags(ctx context.Context, sessio params := map[string]any{ "entity_id": channel.ID, } - if err := am.callOut(ctx, session, channels.OpUpdateChannelTags.String(channels.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, channels.OpUpdateChannelTags.String(), params); err != nil { return channels.Channel{}, err } return am.svc.UpdateChannelTags(ctx, session, channel) @@ -315,7 +316,7 @@ func (am *authorizationMiddleware) EnableChannel(ctx context.Context, session au params := map[string]any{ "entity_id": id, } - if err := am.callOut(ctx, session, channels.OpEnableChannel.String(channels.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, channels.OpEnableChannel.String(), params); err != nil { return channels.Channel{}, err } return am.svc.EnableChannel(ctx, session, id) @@ -347,7 +348,7 @@ func (am *authorizationMiddleware) DisableChannel(ctx context.Context, session a params := map[string]any{ "entity_id": id, } - if err := am.callOut(ctx, session, channels.OpDisableChannel.String(channels.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, channels.OpDisableChannel.String(), params); err != nil { return channels.Channel{}, err } return am.svc.DisableChannel(ctx, session, id) @@ -378,7 +379,7 @@ func (am *authorizationMiddleware) RemoveChannel(ctx context.Context, session au params := map[string]any{ "entity_id": id, } - if err := am.callOut(ctx, session, channels.OpDeleteChannel.String(channels.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, channels.OpDeleteChannel.String(), params); err != nil { return err } @@ -440,7 +441,7 @@ func (am *authorizationMiddleware) Connect(ctx context.Context, session authn.Se "client_ids": thIDs, "connection_types": connTypes, } - if err := am.callOut(ctx, session, channels.OpConnectClient.String(channels.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, channels.OpConnectClient.String(), params); err != nil { return err } return am.svc.Connect(ctx, session, chIDs, thIDs, connTypes) @@ -502,7 +503,7 @@ func (am *authorizationMiddleware) Disconnect(ctx context.Context, session authn "client_ids": thIDs, "connection_types": connTypes, } - if err := am.callOut(ctx, session, channels.OpDisconnectClient.String(channels.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, channels.OpDisconnectClient.String(), params); err != nil { return err } return am.svc.Disconnect(ctx, session, chIDs, thIDs, connTypes) @@ -545,7 +546,7 @@ func (am *authorizationMiddleware) SetParentGroup(ctx context.Context, session a "entity_id": id, "parent_group_id": parentGroupID, } - if err := am.callOut(ctx, session, channels.OpSetParentGroup.String(channels.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, channels.OpSetParentGroup.String(), params); err != nil { return err } return am.svc.SetParentGroup(ctx, session, parentGroupID, id) @@ -593,7 +594,7 @@ func (am *authorizationMiddleware) RemoveParentGroup(ctx context.Context, sessio "entity_id": id, "parent_group_id": ch.ParentGroup, } - if err := am.callOut(ctx, session, channels.OpRemoveParentGroup.String(channels.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, channels.OpRemoveParentGroup.String(), params); err != nil { return err } return am.svc.RemoveParentGroup(ctx, session, id) @@ -601,7 +602,7 @@ func (am *authorizationMiddleware) RemoveParentGroup(ctx context.Context, sessio return nil } -func (am *authorizationMiddleware) authorize(ctx context.Context, op svcutil.Operation, req smqauthz.PolicyReq) error { +func (am *authorizationMiddleware) authorize(ctx context.Context, op channels.Operation, req smqauthz.PolicyReq) error { perm, err := am.opp.GetPermission(op) if err != nil { return err @@ -616,7 +617,7 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, op svcutil.Ope return nil } -func (am *authorizationMiddleware) extAuthorize(ctx context.Context, extOp svcutil.ExternalOperation, req smqauthz.PolicyReq) error { +func (am *authorizationMiddleware) extAuthorize(ctx context.Context, extOp channels.ExternalOperation, req smqauthz.PolicyReq) error { perm, err := am.extOpp.GetPermission(extOp) if err != nil { return err diff --git a/channels/operationperm.go b/channels/operationperm.go new file mode 100644 index 0000000000..37a52c5705 --- /dev/null +++ b/channels/operationperm.go @@ -0,0 +1,186 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package channels + +import "fmt" + +type Operation int + +func (op Operation) String() string { + switch op { + case OpViewChannel: + return OpViewChannelStr + case OpUpdateChannel: + return OpUpdateChannelStr + case OpUpdateChannelTags: + return OpUpdateChannelTagsStr + case OpEnableChannel: + return OpEnableChannelStr + case OpDisableChannel: + return OpDisableChannelStr + case OpDeleteChannel: + return OpDeleteChannelStr + case OpSetParentGroup: + return OpSetParentGroupStr + case OpRemoveParentGroup: + return OpRemoveParentGroupStr + case OpConnectClient: + return OpConnectClientStr + case OpDisconnectClient: + return OpDisconnectClientStr + case OpCreateChannel: + return OpCreateChannelStr + case OpListChannels: + return OpListChannelsStr + case OpListUserChannels: + return OpListUserChannelsStr + default: + return fmt.Sprintf("unknown operation: %d", op) + } +} + +type OperationPerm struct { + opPerm map[Operation]Permission + expectedOps []Operation +} + +func newOperationPerm(expectedOps []Operation) OperationPerm { + return OperationPerm{ + opPerm: make(map[Operation]Permission), + expectedOps: expectedOps, + } +} + +func (opp OperationPerm) AddOperationPermissionMap(opMap map[Operation]Permission) error { + // First iteration check all the keys are valid, If any one key is invalid then no key should be added. + for op := range opMap { + if !opp.isKeyRequired(op) { + return fmt.Errorf("%v is not a valid operation", op.String()) + } + } + for op, perm := range opMap { + opp.opPerm[op] = perm + } + return nil +} + +func (opp OperationPerm) isKeyRequired(op Operation) bool { + for _, key := range opp.expectedOps { + if key == op { + return true + } + } + return false +} + +func (opp OperationPerm) AddOperationPermission(op Operation, perm Permission) error { + if !opp.isKeyRequired(op) { + return fmt.Errorf("%v is not a valid operation", op.String()) + } + opp.opPerm[op] = perm + return nil +} + +func (opp OperationPerm) Validate() error { + for op := range opp.opPerm { + if !opp.isKeyRequired(op) { + return fmt.Errorf("OperationPerm: \"%s\" is not a valid operation", op.String()) + } + } + for _, eeo := range opp.expectedOps { + if _, ok := opp.opPerm[eeo]; !ok { + return fmt.Errorf("OperationPerm: \"%s\" operation is missing", eeo.String()) + } + } + return nil +} + +func (opp OperationPerm) GetPermission(op Operation) (Permission, error) { + if perm, ok := opp.opPerm[op]; ok { + return perm, nil + } + return "", fmt.Errorf("operation \"%s\" doesn't have any permissions", op.String()) +} + +type Permission string + +func (p Permission) String() string { + return string(p) +} + +type ExternalOperation int + +func (ep ExternalOperation) String() string { + switch ep { + case DomainOpCreateChannel: + return DomainOpCreateChannelStr + case DomainOpListChannel: + return DomainOpListChannelStr + case GroupOpSetChildChannel: + return GroupOpSetChildChannelStr + case GroupsOpRemoveChildChannel: + return GroupsOpRemoveChildChannelStr + case ClientsOpConnectChannel: + return ClientsOpConnectChannelStr + case ClientsOpDisconnectChannel: + return ClientsOpDisconnectChannelStr + default: + return fmt.Sprintf("unknown external operation: %d", ep) + } +} + +type ExternalOperationPerm struct { + opPerm map[ExternalOperation]Permission + expectedOps []ExternalOperation +} + +func newExternalOperationPerm(expectedOps []ExternalOperation) ExternalOperationPerm { + return ExternalOperationPerm{ + opPerm: make(map[ExternalOperation]Permission), + expectedOps: expectedOps, + } +} + +func (eopp ExternalOperationPerm) isKeyRequired(eop ExternalOperation) bool { + for _, key := range eopp.expectedOps { + if key == eop { + return true + } + } + return false +} + +func (eopp ExternalOperationPerm) AddOperationPermissionMap(eopMap map[ExternalOperation]Permission) error { + // First iteration check all the keys are valid, If any one key is invalid then no key should be added. + for eop := range eopMap { + if !eopp.isKeyRequired(eop) { + return fmt.Errorf("%v is not a valid external operation", eop.String()) + } + } + for eop, perm := range eopMap { + eopp.opPerm[eop] = perm + } + return nil +} + +func (eopp ExternalOperationPerm) Validate() error { + for eop := range eopp.opPerm { + if !eopp.isKeyRequired(eop) { + return fmt.Errorf("ExternalOperationPerm: \"%s\" is not a valid external operation", eop.String()) + } + } + for _, eeo := range eopp.expectedOps { + if _, ok := eopp.opPerm[eeo]; !ok { + return fmt.Errorf("ExternalOperationPerm: \"%s\" external operation is missing", eeo.String()) + } + } + return nil +} + +func (eopp ExternalOperationPerm) GetPermission(eop ExternalOperation) (Permission, error) { + if perm, ok := eopp.opPerm[eop]; ok { + return perm, nil + } + return "", fmt.Errorf("external operation \"%s\" doesn't have any permissions", eop.String()) +} diff --git a/channels/roleoperations.go b/channels/roleoperations.go index d7f23a8882..a50bed9bf2 100644 --- a/channels/roleoperations.go +++ b/channels/roleoperations.go @@ -3,15 +3,11 @@ package channels -import ( - "github.com/absmach/supermq/pkg/roles" - "github.com/absmach/supermq/pkg/svcutil" -) - -// Internal Operations +import "github.com/absmach/supermq/pkg/roles" +// Internal Operations. const ( - OpViewChannel svcutil.Operation = iota + OpViewChannel Operation = iota OpUpdateChannel OpUpdateChannelTags OpEnableChannel @@ -26,7 +22,23 @@ const ( OpListUserChannels ) -var expectedOperations = []svcutil.Operation{ +const ( + OpViewChannelStr = "OpViewChannel" + OpUpdateChannelStr = "OpUpdateChannel" + OpUpdateChannelTagsStr = "OpUpdateChannelTags" + OpEnableChannelStr = "OpEnableChannel" + OpDisableChannelStr = "OpDisableChannel" + OpDeleteChannelStr = "OpDeleteChannel" + OpSetParentGroupStr = "OpSetParentGroup" + OpRemoveParentGroupStr = "OpRemoveParentGroup" + OpConnectClientStr = "OpConnectClient" + OpDisconnectClientStr = "OpDisconnectClient" + OpCreateChannelStr = "OpCreateChannel" + OpListChannelsStr = "OpListChannels" + OpListUserChannelsStr = "OpListUserChannels" +) + +var expectedOperations = []Operation{ OpViewChannel, OpUpdateChannel, OpUpdateChannelTags, @@ -39,29 +51,13 @@ var expectedOperations = []svcutil.Operation{ OpDisconnectClient, } -var OperationNames = []string{ - "OpViewChannel", - "OpUpdateChannel", - "OpUpdateChannelTags", - "OpEnableChannel", - "OpDisableChannel", - "OpDeleteChannel", - "OpSetParentGroup", - "OpRemoveParentGroup", - "OpConnectClient", - "OpDisconnectClient", - "OpCreateChannel", - "OpListChannels", - "OpListUserChannels", -} - -func NewOperationPerm() svcutil.OperationPerm { - return svcutil.NewOperationPerm(expectedOperations, OperationNames) +func NewOperationPerm() OperationPerm { + return newOperationPerm(expectedOperations) } // External Operations. const ( - DomainOpCreateChannel svcutil.ExternalOperation = iota + DomainOpCreateChannel ExternalOperation = iota DomainOpListChannel GroupOpSetChildChannel GroupsOpRemoveChildChannel @@ -69,7 +65,16 @@ const ( ClientsOpDisconnectChannel ) -var expectedExternalOperations = []svcutil.ExternalOperation{ +const ( + DomainOpCreateChannelStr = "DomainOpCreateChannel" + DomainOpListChannelStr = "DomainOpListChannel" + GroupOpSetChildChannelStr = "GroupOpSetChildChannel" + GroupsOpRemoveChildChannelStr = "GroupsOpRemoveChildChannel" + ClientsOpConnectChannelStr = "ClientsOpConnectChannel" + ClientsOpDisconnectChannelStr = "ClientsOpDisconnectChannel" +) + +var expectedExternalOperations = []ExternalOperation{ DomainOpCreateChannel, DomainOpListChannel, GroupOpSetChildChannel, @@ -78,17 +83,8 @@ var expectedExternalOperations = []svcutil.ExternalOperation{ ClientsOpDisconnectChannel, } -var externalOperationNames = []string{ - "DomainOpCreateChannel", - "DomainOpListChannel", - "GroupOpSetChildChannel", - "GroupsOpRemoveChildChannel", - "ClientsOpConnectChannel", - "ClientsOpDisconnectChannel", -} - -func NewExternalOperationPerm() svcutil.ExternalOperationPerm { - return svcutil.NewExternalOperationPerm(expectedExternalOperations, externalOperationNames) +func NewExternalOperationPerm() ExternalOperationPerm { + return newExternalOperationPerm(expectedExternalOperations) } // Below codes should moved out of service, may be can be kept in `cmd//main.go` @@ -106,8 +102,8 @@ const ( viewRoleUsersPermission = "view_role_users_permission" ) -func NewOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { - opPerm := map[svcutil.Operation]svcutil.Permission{ +func NewOperationPermissionMap() map[Operation]Permission { + opPerm := map[Operation]Permission{ OpViewChannel: readPermission, OpUpdateChannel: updatePermission, OpUpdateChannelTags: updatePermission, @@ -122,8 +118,8 @@ func NewOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { return opPerm } -func NewRolesOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { - opPerm := map[svcutil.Operation]svcutil.Permission{ +func NewRolesOperationPermissionMap() map[roles.Operation]roles.Permission { + opPerm := map[roles.Operation]roles.Permission{ roles.OpAddRole: manageRolePermission, roles.OpRemoveRole: manageRolePermission, roles.OpUpdateRoleName: manageRolePermission, @@ -156,8 +152,8 @@ const ( clientsDisconnectChannelPermission = "connect_to_channel_permission" ) -func NewExternalOperationPermissionMap() map[svcutil.ExternalOperation]svcutil.Permission { - extOpPerm := map[svcutil.ExternalOperation]svcutil.Permission{ +func NewExternalOperationPermissionMap() map[ExternalOperation]Permission { + extOpPerm := map[ExternalOperation]Permission{ DomainOpCreateChannel: domainCreateChannelPermission, DomainOpListChannel: domainListChanelPermission, GroupOpSetChildChannel: groupSetChildChannelPermission, diff --git a/clients/middleware/authorization.go b/clients/middleware/authorization.go index 45dd1d255f..a364782342 100644 --- a/clients/middleware/authorization.go +++ b/clients/middleware/authorization.go @@ -18,7 +18,6 @@ import ( "github.com/absmach/supermq/pkg/policies" "github.com/absmach/supermq/pkg/roles" rmMW "github.com/absmach/supermq/pkg/roles/rolemanager/middleware" - "github.com/absmach/supermq/pkg/svcutil" ) var ( @@ -42,8 +41,8 @@ type authorizationMiddleware struct { svc clients.Service repo clients.Repository authz smqauthz.Authorization - opp svcutil.OperationPerm - extOpp svcutil.ExternalOperationPerm + opp clients.OperationPerm + extOpp clients.ExternalOperationPerm callout callout.Callout rmMW.RoleManagerAuthorizationMiddleware } @@ -54,8 +53,9 @@ func AuthorizationMiddleware( svc clients.Service, authz smqauthz.Authorization, repo clients.Repository, - thingsOpPerm, rolesOpPerm map[svcutil.Operation]svcutil.Permission, - extOpPerm map[svcutil.ExternalOperation]svcutil.Permission, + thingsOpPerm map[clients.Operation]clients.Permission, + rolesOpPerm map[roles.Operation]roles.Permission, + extOpPerm map[clients.ExternalOperation]clients.Permission, callout callout.Callout, ) (clients.Service, error) { opp := clients.NewOperationPerm() @@ -65,6 +65,7 @@ func AuthorizationMiddleware( if err := opp.Validate(); err != nil { return nil, err } + ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(policies.ClientType, svc, authz, rolesOpPerm, callout) if err != nil { return nil, err @@ -116,7 +117,7 @@ func (am *authorizationMiddleware) CreateClients(ctx context.Context, session au "count": len(client), } - if err := am.callOut(ctx, session, clients.OpCreateClient.String(clients.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, clients.OpCreateClient.String(), params); err != nil { return []clients.Client{}, []roles.RoleProvision{}, err } @@ -151,7 +152,7 @@ func (am *authorizationMiddleware) View(ctx context.Context, session authn.Sessi "entity_id": id, } - if err := am.callOut(ctx, session, clients.OpViewClient.String(clients.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, clients.OpViewClient.String(), params); err != nil { return clients.Client{}, err } @@ -179,7 +180,7 @@ func (am *authorizationMiddleware) ListClients(ctx context.Context, session auth params := map[string]any{ "pagemeta": pm, } - if err := am.callOut(ctx, session, clients.OpListClients.String(clients.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, clients.OpListClients.String(), params); err != nil { return clients.ClientsPage{}, err } @@ -207,7 +208,7 @@ func (am *authorizationMiddleware) ListUserClients(ctx context.Context, session "user_id": userID, "pagemeta": pm, } - if err := am.callOut(ctx, session, clients.OpListUserClients.String(clients.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, clients.OpListUserClients.String(), params); err != nil { return clients.ClientsPage{}, err } @@ -242,7 +243,7 @@ func (am *authorizationMiddleware) Update(ctx context.Context, session authn.Ses "entity_id": client.ID, } - if err := am.callOut(ctx, session, clients.OpUpdateClient.String(clients.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, clients.OpUpdateClient.String(), params); err != nil { return clients.Client{}, err } @@ -277,7 +278,7 @@ func (am *authorizationMiddleware) UpdateTags(ctx context.Context, session authn "entity_id": client.ID, } - if err := am.callOut(ctx, session, clients.OpUpdateClientTags.String(clients.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, clients.OpUpdateClientTags.String(), params); err != nil { return clients.Client{}, err } @@ -312,7 +313,7 @@ func (am *authorizationMiddleware) UpdateSecret(ctx context.Context, session aut "entity_id": id, } - if err := am.callOut(ctx, session, clients.OpUpdateClientSecret.String(clients.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, clients.OpUpdateClientSecret.String(), params); err != nil { return clients.Client{}, err } return am.svc.UpdateSecret(ctx, session, id, key) @@ -346,7 +347,7 @@ func (am *authorizationMiddleware) Enable(ctx context.Context, session authn.Ses "entity_id": id, } - if err := am.callOut(ctx, session, clients.OpEnableClient.String(clients.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, clients.OpEnableClient.String(), params); err != nil { return clients.Client{}, err } @@ -381,7 +382,7 @@ func (am *authorizationMiddleware) Disable(ctx context.Context, session authn.Se "entity_id": id, } - if err := am.callOut(ctx, session, clients.OpDisableClient.String(clients.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, clients.OpDisableClient.String(), params); err != nil { return clients.Client{}, err } @@ -415,7 +416,7 @@ func (am *authorizationMiddleware) Delete(ctx context.Context, session authn.Ses "entity_id": id, } - if err := am.callOut(ctx, session, clients.OpDeleteClient.String(clients.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, clients.OpDeleteClient.String(), params); err != nil { return err } @@ -461,7 +462,7 @@ func (am *authorizationMiddleware) SetParentGroup(ctx context.Context, session a "parent_id": parentGroupID, } - if err := am.callOut(ctx, session, clients.OpSetParentGroup.String(clients.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, clients.OpSetParentGroup.String(), params); err != nil { return err } return am.svc.SetParentGroup(ctx, session, parentGroupID, id) @@ -512,7 +513,7 @@ func (am *authorizationMiddleware) RemoveParentGroup(ctx context.Context, sessio "parent_id": th.ParentGroup, } - if err := am.callOut(ctx, session, clients.OpRemoveParentGroup.String(clients.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, clients.OpRemoveParentGroup.String(), params); err != nil { return err } @@ -521,7 +522,7 @@ func (am *authorizationMiddleware) RemoveParentGroup(ctx context.Context, sessio return nil } -func (am *authorizationMiddleware) authorize(ctx context.Context, op svcutil.Operation, req smqauthz.PolicyReq) error { +func (am *authorizationMiddleware) authorize(ctx context.Context, op clients.Operation, req smqauthz.PolicyReq) error { perm, err := am.opp.GetPermission(op) if err != nil { return err @@ -536,7 +537,7 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, op svcutil.Ope return nil } -func (am *authorizationMiddleware) extAuthorize(ctx context.Context, extOp svcutil.ExternalOperation, req smqauthz.PolicyReq) error { +func (am *authorizationMiddleware) extAuthorize(ctx context.Context, extOp clients.ExternalOperation, req smqauthz.PolicyReq) error { perm, err := am.extOpp.GetPermission(extOp) if err != nil { return err diff --git a/clients/operationperm.go b/clients/operationperm.go new file mode 100644 index 0000000000..d127a05717 --- /dev/null +++ b/clients/operationperm.go @@ -0,0 +1,188 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package clients + +import "fmt" + +type Operation int + +func (op Operation) String() string { + switch op { + case OpViewClient: + return OpViewClientStr + case OpUpdateClient: + return OpUpdateClientStr + case OpUpdateClientTags: + return OpUpdateClientTagsStr + case OpUpdateClientSecret: + return OpUpdateClientSecretStr + case OpEnableClient: + return OpEnableClientStr + case OpDisableClient: + return OpDisableClientStr + case OpDeleteClient: + return OpDeleteClientStr + case OpSetParentGroup: + return OpSetParentGroupStr + case OpRemoveParentGroup: + return OpRemoveParentGroupStr + case OpConnectToChannel: + return OpConnectToChannelStr + case OpDisconnectFromChannel: + return OpDisconnectFromChannelStr + case OpCreateClient: + return OpCreateClientStr + case OpListClients: + return OpListClientsStr + case OpListUserClients: + return OpListUserClientsStr + default: + return fmt.Sprintf("unknown operation: %d", op) + } +} + +type OperationPerm struct { + opPerm map[Operation]Permission + expectedOps []Operation +} + +func newOperationPerm(expectedOps []Operation) OperationPerm { + return OperationPerm{ + opPerm: make(map[Operation]Permission), + expectedOps: expectedOps, + } +} + +func (opp OperationPerm) isKeyRequired(op Operation) bool { + for _, key := range opp.expectedOps { + if key == op { + return true + } + } + return false +} + +func (opp OperationPerm) AddOperationPermissionMap(opMap map[Operation]Permission) error { + // First iteration check all the keys are valid, If any one key is invalid then no key should be added. + for op := range opMap { + if !opp.isKeyRequired(op) { + return fmt.Errorf("%v is not a valid operation", op.String()) + } + } + for op, perm := range opMap { + opp.opPerm[op] = perm + } + return nil +} + +func (opp OperationPerm) AddOperationPermission(op Operation, perm Permission) error { + if !opp.isKeyRequired(op) { + return fmt.Errorf("%v is not a valid operation", op.String()) + } + opp.opPerm[op] = perm + return nil +} + +func (opp OperationPerm) Validate() error { + for op := range opp.opPerm { + if !opp.isKeyRequired(op) { + return fmt.Errorf("OperationPerm: \"%s\" is not a valid operation", op.String()) + } + } + for _, eeo := range opp.expectedOps { + if _, ok := opp.opPerm[eeo]; !ok { + return fmt.Errorf("OperationPerm: \"%s\" operation is missing", eeo.String()) + } + } + return nil +} + +func (opp OperationPerm) GetPermission(op Operation) (Permission, error) { + if perm, ok := opp.opPerm[op]; ok { + return perm, nil + } + return "", fmt.Errorf("operation \"%s\" doesn't have any permissions", op.String()) +} + +type Permission string + +func (p Permission) String() string { + return string(p) +} + +type ExternalOperation int + +func (op ExternalOperation) String() string { + switch op { + case DomainOpCreateClient: + return DomainOpCreateClientStr + case DomainOpListClients: + return DomainOpListClientsStr + case GroupOpSetChildClient: + return GroupOpSetChildClientStr + case GroupsOpRemoveChildClient: + return GroupsOpRemoveChildClientStr + case ChannelsOpConnectChannel: + return ChannelsOpConnectChannelStr + case ChannelsOpDisconnectChannel: + return ChannelsOpDisconnectChannelStr + default: + return fmt.Sprintf("unknown external operation: %d", op) + } +} + +type ExternalOperationPerm struct { + opPerm map[ExternalOperation]Permission + expectedOps []ExternalOperation +} + +func newExternalOperationPerm(expectedOps []ExternalOperation) ExternalOperationPerm { + return ExternalOperationPerm{ + opPerm: make(map[ExternalOperation]Permission), + expectedOps: expectedOps, + } +} + +func (eopp ExternalOperationPerm) isKeyRequired(eop ExternalOperation) bool { + for _, key := range eopp.expectedOps { + if key == eop { + return true + } + } + return false +} + +func (eopp ExternalOperationPerm) AddOperationPermissionMap(eopMap map[ExternalOperation]Permission) error { + // First iteration check all the keys are valid, If any one key is invalid then no key should be added. + for eop := range eopMap { + if !eopp.isKeyRequired(eop) { + return fmt.Errorf("%v is not a valid external operation", eop.String()) + } + } + for eop, perm := range eopMap { + eopp.opPerm[eop] = perm + } + return nil +} + +func (eopp ExternalOperationPerm) Validate() error { + for eop := range eopp.opPerm { + if !eopp.isKeyRequired(eop) { + return fmt.Errorf("ExternalOperationPerm: \"%s\" is not a valid external operation", eop.String()) + } + } + for _, eeo := range eopp.expectedOps { + if _, ok := eopp.opPerm[eeo]; !ok { + return fmt.Errorf("ExternalOperationPerm: \"%s\" external operation is missing", eeo.String()) + } + } + return nil +} + +func (eopp ExternalOperationPerm) GetPermission(eop ExternalOperation) (Permission, error) { + if perm, ok := eopp.opPerm[eop]; ok { + return perm, nil + } + return "", fmt.Errorf("external operation \"%s\" doesn't have any permissions", eop.String()) +} diff --git a/clients/roleoperations.go b/clients/roleoperations.go index 8c99825ff5..99cf066057 100644 --- a/clients/roleoperations.go +++ b/clients/roleoperations.go @@ -3,15 +3,11 @@ package clients -import ( - "github.com/absmach/supermq/pkg/roles" - "github.com/absmach/supermq/pkg/svcutil" -) - -// Internal Operations +import "github.com/absmach/supermq/pkg/roles" +// Internal Operations. const ( - OpViewClient svcutil.Operation = iota + OpViewClient Operation = iota OpUpdateClient OpUpdateClientTags OpUpdateClientSecret @@ -27,7 +23,24 @@ const ( OpListUserClients ) -var expectedOperations = []svcutil.Operation{ +const ( + OpViewClientStr = "OpViewClient" + OpUpdateClientStr = "OpUpdateClient" + OpUpdateClientTagsStr = "OpUpdateClientTags" + OpUpdateClientSecretStr = "OpUpdateClientSecret" + OpEnableClientStr = "OpEnableClient" + OpDisableClientStr = "OpDisableClient" + OpDeleteClientStr = "OpDeleteClient" + OpSetParentGroupStr = "OpSetParentGroup" + OpRemoveParentGroupStr = "OpRemoveParentGroup" + OpConnectToChannelStr = "OpConnectToChannel" + OpDisconnectFromChannelStr = "OpDisconnectFromChannel" + OpCreateClientStr = "OpCreateClient" + OpListClientsStr = "OpListClients" + OpListUserClientsStr = "OpListUserClients" +) + +var expectedOperations = []Operation{ OpViewClient, OpUpdateClient, OpUpdateClientTags, @@ -41,30 +54,13 @@ var expectedOperations = []svcutil.Operation{ OpDisconnectFromChannel, } -var OperationNames = []string{ - "OpViewClient", - "OpUpdateClient", - "OpUpdateClientTags", - "OpUpdateClientSecret", - "OpEnableClient", - "OpDisableClient", - "OpDeleteClient", - "OpSetParentGroup", - "OpRemoveParentGroup", - "OpConnectToChannel", - "OpDisconnectFromChannel", - "OpCreateClient", - "OpListClients", - "OpListUserClients", -} - -func NewOperationPerm() svcutil.OperationPerm { - return svcutil.NewOperationPerm(expectedOperations, OperationNames) +func NewOperationPerm() OperationPerm { + return newOperationPerm(expectedOperations) } // External Operations. const ( - DomainOpCreateClient svcutil.ExternalOperation = iota + DomainOpCreateClient ExternalOperation = iota DomainOpListClients GroupOpSetChildClient GroupsOpRemoveChildClient @@ -72,7 +68,16 @@ const ( ChannelsOpDisconnectChannel ) -var expectedExternalOperations = []svcutil.ExternalOperation{ +const ( + DomainOpCreateClientStr = "DomainOpCreateClient" + DomainOpListClientsStr = "DomainOpListClients" + GroupOpSetChildClientStr = "GroupOpSetChildClient" + GroupsOpRemoveChildClientStr = "GroupsOpRemoveChildClient" + ChannelsOpConnectChannelStr = "ChannelsOpConnectChannel" + ChannelsOpDisconnectChannelStr = "ChannelsOpDisconnectChannel" +) + +var expectedExternalOperations = []ExternalOperation{ DomainOpCreateClient, DomainOpListClients, GroupOpSetChildClient, @@ -81,17 +86,8 @@ var expectedExternalOperations = []svcutil.ExternalOperation{ ChannelsOpDisconnectChannel, } -var externalOperationNames = []string{ - "DomainOpCreateClient", - "DomainOpListClients", - "GroupOpSetChildClient", - "GroupsOpRemoveChildClient", - "ChannelsOpConnectChannel", - "ChannelsOpDisconnectChannel", -} - -func NewExternalOperationPerm() svcutil.ExternalOperationPerm { - return svcutil.NewExternalOperationPerm(expectedExternalOperations, externalOperationNames) +func NewExternalOperationPerm() ExternalOperationPerm { + return newExternalOperationPerm(expectedExternalOperations) } // Below codes should moved out of service, may be can be kept in `cmd//main.go` @@ -109,8 +105,8 @@ const ( viewRoleUsersPermission = "view_role_users_permission" ) -func NewOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { - opPerm := map[svcutil.Operation]svcutil.Permission{ +func NewOperationPermissionMap() map[Operation]Permission { + opPerm := map[Operation]Permission{ OpViewClient: readPermission, OpUpdateClient: updatePermission, OpUpdateClientTags: updatePermission, @@ -126,8 +122,8 @@ func NewOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { return opPerm } -func NewRolesOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { - opPerm := map[svcutil.Operation]svcutil.Permission{ +func NewRolesOperationPermissionMap() map[roles.Operation]roles.Permission { + opPerm := map[roles.Operation]roles.Permission{ roles.OpAddRole: manageRolePermission, roles.OpRemoveRole: manageRolePermission, roles.OpUpdateRoleName: manageRolePermission, @@ -159,8 +155,8 @@ const ( channelsDisconnectClientPermission = "connect_to_client_permission" ) -func NewExternalOperationPermissionMap() map[svcutil.ExternalOperation]svcutil.Permission { - extOpPerm := map[svcutil.ExternalOperation]svcutil.Permission{ +func NewExternalOperationPermissionMap() map[ExternalOperation]Permission { + extOpPerm := map[ExternalOperation]Permission{ DomainOpCreateClient: domainCreateClientPermission, DomainOpListClients: domainListClientsPermission, GroupOpSetChildClient: groupSetChildClientPermission, diff --git a/domains/middleware/authorization.go b/domains/middleware/authorization.go index 38efae1220..3a165872f0 100644 --- a/domains/middleware/authorization.go +++ b/domains/middleware/authorization.go @@ -19,7 +19,6 @@ import ( "github.com/absmach/supermq/pkg/policies" "github.com/absmach/supermq/pkg/roles" rmMW "github.com/absmach/supermq/pkg/roles/rolemanager/middleware" - "github.com/absmach/supermq/pkg/svcutil" ) var _ domains.Service = (*authorizationMiddleware)(nil) @@ -30,13 +29,13 @@ var ErrMemberExist = errors.New("user is already a member of the domain") type authorizationMiddleware struct { svc domains.Service authz smqauthz.Authorization - opp svcutil.OperationPerm + opp domains.OperationPerm callout callout.Callout rmMW.RoleManagerAuthorizationMiddleware } // AuthorizationMiddleware adds authorization to the clients service. -func AuthorizationMiddleware(entityType string, svc domains.Service, authz smqauthz.Authorization, domainsOpPerm, rolesOpPerm map[svcutil.Operation]svcutil.Permission, callout callout.Callout) (domains.Service, error) { +func AuthorizationMiddleware(entityType string, svc domains.Service, authz smqauthz.Authorization, domainsOpPerm map[domains.Operation]domains.Permission, rolesOpPerm map[roles.Operation]roles.Permission, callout callout.Callout) (domains.Service, error) { opp := domains.NewOperationPerm() if err := opp.AddOperationPermissionMap(domainsOpPerm); err != nil { return nil, err @@ -62,7 +61,7 @@ func (am *authorizationMiddleware) CreateDomain(ctx context.Context, session aut params := map[string]any{ "domain": d, } - if err := am.callOut(ctx, session, domains.OpCreateDomain.String(domains.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, domains.OpCreateDomain.String(), params); err != nil { return domains.Domain{}, nil, err } return am.svc.CreateDomain(ctx, session, d) @@ -87,7 +86,7 @@ func (am *authorizationMiddleware) RetrieveDomain(ctx context.Context, session a "domain": id, "with_roles": withRoles, } - if err := am.callOut(ctx, session, domains.OpRetrieveDomain.String(domains.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, domains.OpRetrieveDomain.String(), params); err != nil { return domains.Domain{}, err } return am.svc.RetrieveDomain(ctx, session, id, withRoles) @@ -107,7 +106,7 @@ func (am *authorizationMiddleware) UpdateDomain(ctx context.Context, session aut "domain": id, "domain_req": d, } - if err := am.callOut(ctx, session, domains.OpUpdateDomain.String(domains.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, domains.OpUpdateDomain.String(), params); err != nil { return domains.Domain{}, err } return am.svc.UpdateDomain(ctx, session, id, d) @@ -126,7 +125,7 @@ func (am *authorizationMiddleware) EnableDomain(ctx context.Context, session aut params := map[string]any{ "domain": id, } - if err := am.callOut(ctx, session, domains.OpEnableDomain.String(domains.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, domains.OpEnableDomain.String(), params); err != nil { return domains.Domain{}, err } return am.svc.EnableDomain(ctx, session, id) @@ -145,7 +144,7 @@ func (am *authorizationMiddleware) DisableDomain(ctx context.Context, session au params := map[string]any{ "domain": id, } - if err := am.callOut(ctx, session, domains.OpDisableDomain.String(domains.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, domains.OpDisableDomain.String(), params); err != nil { return domains.Domain{}, err } return am.svc.DisableDomain(ctx, session, id) @@ -166,7 +165,7 @@ func (am *authorizationMiddleware) FreezeDomain(ctx context.Context, session aut params := map[string]any{ "domain": id, } - if err := am.callOut(ctx, session, domains.OpFreezeDomain.String(domains.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, domains.OpFreezeDomain.String(), params); err != nil { return domains.Domain{}, err } return am.svc.FreezeDomain(ctx, session, id) @@ -179,7 +178,7 @@ func (am *authorizationMiddleware) ListDomains(ctx context.Context, session auth params := map[string]any{ "page": page, } - if err := am.callOut(ctx, session, domains.OpListDomains.String(domains.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, domains.OpListDomains.String(), params); err != nil { return domains.DomainsPage{}, err } return am.svc.ListDomains(ctx, session, page) @@ -199,7 +198,7 @@ func (am *authorizationMiddleware) SendInvitation(ctx context.Context, session a "invitation": invitation, "domain": invitation.DomainID, } - if err := am.callOut(ctx, session, domains.OpSendInvitation.String(domains.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, domains.OpSendInvitation.String(), params); err != nil { return err } @@ -218,7 +217,7 @@ func (am *authorizationMiddleware) ViewInvitation(ctx context.Context, session a "invitee_user_id": inviteeUserID, "domain": domain, } - if err := am.callOut(ctx, session, domains.OpViewInvitation.String(domains.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, domains.OpViewInvitation.String(), params); err != nil { return domains.Invitation{}, err } @@ -246,7 +245,7 @@ func (am *authorizationMiddleware) ListInvitations(ctx context.Context, session params := map[string]any{ "page": page, } - if err := am.callOut(ctx, session, domains.OpListInvitations.String(domains.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, domains.OpListInvitations.String(), params); err != nil { return domains.InvitationPage{}, err } @@ -257,7 +256,7 @@ func (am *authorizationMiddleware) AcceptInvitation(ctx context.Context, session params := map[string]any{ "domain": domainID, } - if err := am.callOut(ctx, session, domains.OpAcceptInvitation.String(domains.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, domains.OpAcceptInvitation.String(), params); err != nil { return domains.Invitation{}, err } return am.svc.AcceptInvitation(ctx, session, domainID) @@ -267,7 +266,7 @@ func (am *authorizationMiddleware) RejectInvitation(ctx context.Context, session params := map[string]any{ "domain": domainID, } - if err := am.callOut(ctx, session, domains.OpRejectInvitation.String(domains.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, domains.OpRejectInvitation.String(), params); err != nil { return err } return am.svc.RejectInvitation(ctx, session, domainID) @@ -283,14 +282,14 @@ func (am *authorizationMiddleware) DeleteInvitation(ctx context.Context, session "invitee_user_id": inviteeUserID, "domain": domainID, } - if err := am.callOut(ctx, session, domains.OpDeleteInvitation.String(domains.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, domains.OpDeleteInvitation.String(), params); err != nil { return err } return am.svc.DeleteInvitation(ctx, session, inviteeUserID, domainID) } -func (am *authorizationMiddleware) authorize(ctx context.Context, op svcutil.Operation, authReq authz.PolicyReq) error { +func (am *authorizationMiddleware) authorize(ctx context.Context, op domains.Operation, authReq authz.PolicyReq) error { perm, err := am.opp.GetPermission(op) if err != nil { return err diff --git a/pkg/svcutil/operationperm.go b/domains/operationperm.go similarity index 59% rename from pkg/svcutil/operationperm.go rename to domains/operationperm.go index 40e909c4ec..812f936830 100644 --- a/pkg/svcutil/operationperm.go +++ b/domains/operationperm.go @@ -1,53 +1,62 @@ // Copyright (c) Abstract Machines // SPDX-License-Identifier: Apache-2.0 -package svcutil +package domains import "fmt" -type Permission string - -func (p Permission) String() string { - return string(p) -} - type Operation int -func (op Operation) String(operations []string) string { - if (int(op) < 0) || (int(op) == len(operations)) { - return fmt.Sprintf("UnknownOperation(%d)", op) +func (op Operation) String() string { + switch op { + case OpUpdateDomain: + return OpUpdateDomainStr + case OpRetrieveDomain: + return OpRetrieveDomainStr + case OpEnableDomain: + return OpEnableDomainStr + case OpDisableDomain: + return OpDisableDomainStr + case OpSendInvitation: + return OpSendInvitationStr + case OpAcceptInvitation: + return OpAcceptInvitationStr + case OpCreateDomain: + return OpCreateDomainStr + case OpFreezeDomain: + return OpFreezeDomainStr + case OpListDomains: + return OpListDomainsStr + case OpViewInvitation: + return OpViewInvitationStr + case OpListInvitations: + return OpListInvitationsStr + case OpRejectInvitation: + return OpRejectInvitationStr + case OpDeleteInvitation: + return OpDeleteInvitationStr + default: + return fmt.Sprintf("unknown operation: %d", op) } - return operations[op] } type OperationPerm struct { opPerm map[Operation]Permission expectedOps []Operation - opNames []string } -func NewOperationPerm(expectedOps []Operation, opNames []string) OperationPerm { +func newOperationPerm(expectedOps []Operation) OperationPerm { return OperationPerm{ opPerm: make(map[Operation]Permission), expectedOps: expectedOps, - opNames: opNames, } } -func (opp OperationPerm) isKeyRequired(op Operation) bool { - for _, key := range opp.expectedOps { - if key == op { - return true - } - } - return false -} - func (opp OperationPerm) AddOperationPermissionMap(opMap map[Operation]Permission) error { // First iteration check all the keys are valid, If any one key is invalid then no key should be added. for op := range opMap { if !opp.isKeyRequired(op) { - return fmt.Errorf("%v is not a valid operation", op.String(opp.opNames)) + return fmt.Errorf("%v is not a valid operation", op.String()) } } for op, perm := range opMap { @@ -56,9 +65,18 @@ func (opp OperationPerm) AddOperationPermissionMap(opMap map[Operation]Permissio return nil } +func (opp OperationPerm) isKeyRequired(op Operation) bool { + for _, key := range opp.expectedOps { + if key == op { + return true + } + } + return false +} + func (opp OperationPerm) AddOperationPermission(op Operation, perm Permission) error { if !opp.isKeyRequired(op) { - return fmt.Errorf("%v is not a valid operation", op.String(opp.opNames)) + return fmt.Errorf("%v is not a valid operation", op.String()) } opp.opPerm[op] = perm return nil @@ -67,12 +85,12 @@ func (opp OperationPerm) AddOperationPermission(op Operation, perm Permission) e func (opp OperationPerm) Validate() error { for op := range opp.opPerm { if !opp.isKeyRequired(op) { - return fmt.Errorf("OperationPerm: \"%s\" is not a valid operation", op.String(opp.opNames)) + return fmt.Errorf("OperationPerm: \"%s\" is not a valid operation", op.String()) } } for _, eeo := range opp.expectedOps { if _, ok := opp.opPerm[eeo]; !ok { - return fmt.Errorf("OperationPerm: \"%s\" operation is missing", eeo.String(opp.opNames)) + return fmt.Errorf("OperationPerm: \"%s\" operation is missing", eeo.String()) } } return nil @@ -82,5 +100,11 @@ func (opp OperationPerm) GetPermission(op Operation) (Permission, error) { if perm, ok := opp.opPerm[op]; ok { return perm, nil } - return "", fmt.Errorf("operation \"%s\" doesn't have any permissions", op.String(opp.opNames)) + return "", fmt.Errorf("operation \"%s\" doesn't have any permissions", op.String()) +} + +type Permission string + +func (p Permission) String() string { + return string(p) } diff --git a/domains/roleoperations.go b/domains/roleoperations.go index 518532d936..17ee4417db 100644 --- a/domains/roleoperations.go +++ b/domains/roleoperations.go @@ -3,13 +3,10 @@ package domains -import ( - "github.com/absmach/supermq/pkg/roles" - "github.com/absmach/supermq/pkg/svcutil" -) +import "github.com/absmach/supermq/pkg/roles" const ( - OpUpdateDomain svcutil.Operation = iota + OpUpdateDomain Operation = iota OpRetrieveDomain OpEnableDomain OpDisableDomain @@ -24,31 +21,31 @@ const ( OpDeleteInvitation ) -var expectedOperations = []svcutil.Operation{ +var expectedOperations = []Operation{ OpRetrieveDomain, OpUpdateDomain, OpEnableDomain, OpDisableDomain, } -var OperationNames = []string{ - "OpRetrieveDomain", - "OpUpdateDomain", - "OpEnableDomain", - "OpDisableDomain", - "OpSendInvitation", - "OpAcceptInvitation", - "OpCreateDomain", - "OpFreezeDomain", - "OpListDomains", - "OpViewInvitation", - "OpListInvitations", - "OpRejectInvitation", - "OpDeleteInvitation", -} +const ( + OpUpdateDomainStr = "OpRetrieveDomain" + OpRetrieveDomainStr = "OpUpdateDomain" + OpEnableDomainStr = "OpEnableDomain" + OpDisableDomainStr = "OpDisableDomain" + OpSendInvitationStr = "OpSendInvitation" + OpAcceptInvitationStr = "OpAcceptInvitation" + OpCreateDomainStr = "OpCreateDomain" + OpFreezeDomainStr = "OpFreezeDomain" + OpListDomainsStr = "OpListDomains" + OpViewInvitationStr = "OpViewInvitation" + OpListInvitationsStr = "OpListInvitations" + OpRejectInvitationStr = "OpRejectInvitation" + OpDeleteInvitationStr = "OpDeleteInvitation" +) -func NewOperationPerm() svcutil.OperationPerm { - return svcutil.NewOperationPerm(expectedOperations, OperationNames) +func NewOperationPerm() OperationPerm { + return newOperationPerm(expectedOperations) } // Below codes should moved out of service, may be can be kept in `cmd//main.go` @@ -71,8 +68,8 @@ const ( GroupCreatePermission = "group_create_permission" ) -func NewOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { - opPerm := map[svcutil.Operation]svcutil.Permission{ +func NewOperationPermissionMap() map[Operation]Permission { + opPerm := map[Operation]Permission{ OpRetrieveDomain: readPermission, OpUpdateDomain: updatePermission, OpEnableDomain: enablePermission, @@ -81,8 +78,8 @@ func NewOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { return opPerm } -func NewRolesOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { - opPerm := map[svcutil.Operation]svcutil.Permission{ +func NewRolesOperationPermissionMap() map[roles.Operation]roles.Permission { + opPerm := map[roles.Operation]roles.Permission{ roles.OpAddRole: manageRolePermission, roles.OpRemoveRole: manageRolePermission, roles.OpUpdateRoleName: manageRolePermission, diff --git a/groups/middleware/authorization.go b/groups/middleware/authorization.go index 7ed6ce8a0c..10b3966d6f 100644 --- a/groups/middleware/authorization.go +++ b/groups/middleware/authorization.go @@ -19,7 +19,6 @@ import ( "github.com/absmach/supermq/pkg/policies" "github.com/absmach/supermq/pkg/roles" rmMW "github.com/absmach/supermq/pkg/roles/rolemanager/middleware" - "github.com/absmach/supermq/pkg/svcutil" ) var ( @@ -48,8 +47,8 @@ type authorizationMiddleware struct { svc groups.Service repo groups.Repository authz smqauthz.Authorization - opp svcutil.OperationPerm - extOpp svcutil.ExternalOperationPerm + opp groups.OperationPerm + extOpp groups.ExternalOperationPerm callout callout.Callout rmMW.RoleManagerAuthorizationMiddleware } @@ -59,8 +58,9 @@ func AuthorizationMiddleware(entityType string, svc groups.Service, repo groups.Repository, authz smqauthz.Authorization, - groupsOpPerm, rolesOpPerm map[svcutil.Operation]svcutil.Permission, - extOpPerm map[svcutil.ExternalOperation]svcutil.Permission, + groupsOpPerm map[groups.Operation]groups.Permission, + rolesOpPerm map[roles.Operation]roles.Permission, + extOpPerm map[groups.ExternalOperation]groups.Permission, callout callout.Callout, ) (groups.Service, error) { opp := groups.NewOperationPerm() @@ -136,7 +136,7 @@ func (am *authorizationMiddleware) CreateGroup(ctx context.Context, session auth "entities": []groups.Group{g}, "count": 1, } - if err := am.callOut(ctx, session, groups.OpCreateGroup.String(groups.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, groups.OpCreateGroup.String(), params); err != nil { return groups.Group{}, []roles.RoleProvision{}, err } @@ -170,7 +170,7 @@ func (am *authorizationMiddleware) UpdateGroup(ctx context.Context, session auth params := map[string]any{ "entity_id": g.ID, } - if err := am.callOut(ctx, session, groups.OpUpdateGroup.String(groups.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, groups.OpUpdateGroup.String(), params); err != nil { return groups.Group{}, err } @@ -203,7 +203,7 @@ func (am *authorizationMiddleware) UpdateGroupTags(ctx context.Context, session params := map[string]any{ "entity_id": group.ID, } - if err := am.callOut(ctx, session, groups.OpUpdateGroupTags.String(groups.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, groups.OpUpdateGroupTags.String(), params); err != nil { return groups.Group{}, err } return am.svc.UpdateGroupTags(ctx, session, group) @@ -236,7 +236,7 @@ func (am *authorizationMiddleware) ViewGroup(ctx context.Context, session authn. params := map[string]any{ "entity_id": id, } - if err := am.callOut(ctx, session, groups.OpViewGroup.String(groups.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, groups.OpViewGroup.String(), params); err != nil { return groups.Group{}, err } @@ -276,7 +276,7 @@ func (am *authorizationMiddleware) ListGroups(ctx context.Context, session authn params := map[string]any{ "pagemeta": gm, } - if err := am.callOut(ctx, session, groups.OpListGroups.String(groups.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, groups.OpListGroups.String(), params); err != nil { return groups.Page{}, err } @@ -303,7 +303,7 @@ func (am *authorizationMiddleware) ListUserGroups(ctx context.Context, session a "user_id": userID, "pagemeta": pm, } - if err := am.callOut(ctx, session, groups.OpListUserGroups.String(groups.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, groups.OpListUserGroups.String(), params); err != nil { return groups.Page{}, err } return am.svc.ListUserGroups(ctx, session, userID, pm) @@ -335,7 +335,7 @@ func (am *authorizationMiddleware) EnableGroup(ctx context.Context, session auth params := map[string]any{ "entity_id": id, } - if err := am.callOut(ctx, session, groups.OpEnableGroup.String(groups.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, groups.OpEnableGroup.String(), params); err != nil { return groups.Group{}, err } @@ -368,7 +368,7 @@ func (am *authorizationMiddleware) DisableGroup(ctx context.Context, session aut params := map[string]any{ "entity_id": id, } - if err := am.callOut(ctx, session, groups.OpDisableGroup.String(groups.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, groups.OpDisableGroup.String(), params); err != nil { return groups.Group{}, err } @@ -400,7 +400,7 @@ func (am *authorizationMiddleware) DeleteGroup(ctx context.Context, session auth params := map[string]any{ "entity_id": id, } - if err := am.callOut(ctx, session, groups.OpDeleteGroup.String(groups.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, groups.OpDeleteGroup.String(), params); err != nil { return err } @@ -434,7 +434,7 @@ func (am *authorizationMiddleware) RetrieveGroupHierarchy(ctx context.Context, s "entity_id": id, "hierarchy_pagemeta": hm, } - if err := am.callOut(ctx, session, groups.OpRetrieveGroupHierarchy.String(groups.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, groups.OpRetrieveGroupHierarchy.String(), params); err != nil { return groups.HierarchyPage{}, err } return am.svc.RetrieveGroupHierarchy(ctx, session, id, hm) @@ -477,7 +477,7 @@ func (am *authorizationMiddleware) AddParentGroup(ctx context.Context, session a "entity_id": id, "parent_id": parentID, } - if err := am.callOut(ctx, session, groups.OpAddParentGroup.String(groups.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, groups.OpAddParentGroup.String(), params); err != nil { return err } return am.svc.AddParentGroup(ctx, session, id, parentID) @@ -527,7 +527,7 @@ func (am *authorizationMiddleware) RemoveParentGroup(ctx context.Context, sessio "entity_id": id, "parent_id": group.Parent, } - if err := am.callOut(ctx, session, groups.OpRemoveParentGroup.String(groups.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, groups.OpRemoveParentGroup.String(), params); err != nil { return err } return am.svc.RemoveParentGroup(ctx, session, id) @@ -572,7 +572,7 @@ func (am *authorizationMiddleware) AddChildrenGroups(ctx context.Context, sessio "entity_id": id, "children_group_ids": childrenGroupIDs, } - if err := am.callOut(ctx, session, groups.OpAddChildrenGroups.String(groups.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, groups.OpAddChildrenGroups.String(), params); err != nil { return err } @@ -606,7 +606,7 @@ func (am *authorizationMiddleware) RemoveChildrenGroups(ctx context.Context, ses "entity_id": id, "children_group_ids": childrenGroupIDs, } - if err := am.callOut(ctx, session, groups.OpRemoveChildrenGroups.String(groups.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, groups.OpRemoveChildrenGroups.String(), params); err != nil { return err } @@ -639,7 +639,7 @@ func (am *authorizationMiddleware) RemoveAllChildrenGroups(ctx context.Context, params := map[string]any{ "entity_id": id, } - if err := am.callOut(ctx, session, groups.OpRemoveAllChildrenGroups.String(groups.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, groups.OpRemoveAllChildrenGroups.String(), params); err != nil { return err } @@ -675,7 +675,7 @@ func (am *authorizationMiddleware) ListChildrenGroups(ctx context.Context, sessi "end_level": endLevel, "pagemeta": pm, } - if err := am.callOut(ctx, session, groups.OpListChildrenGroups.String(groups.OperationNames), params); err != nil { + if err := am.callOut(ctx, session, groups.OpListChildrenGroups.String(), params); err != nil { return groups.Page{}, err } @@ -695,7 +695,7 @@ func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, adminID return nil } -func (am *authorizationMiddleware) authorize(ctx context.Context, op svcutil.Operation, pr smqauthz.PolicyReq) error { +func (am *authorizationMiddleware) authorize(ctx context.Context, op groups.Operation, pr smqauthz.PolicyReq) error { perm, err := am.opp.GetPermission(op) if err != nil { return err @@ -708,7 +708,7 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, op svcutil.Ope return nil } -func (am *authorizationMiddleware) extAuthorize(ctx context.Context, extOp svcutil.ExternalOperation, req smqauthz.PolicyReq) error { +func (am *authorizationMiddleware) extAuthorize(ctx context.Context, extOp groups.ExternalOperation, req smqauthz.PolicyReq) error { perm, err := am.extOpp.GetPermission(extOp) if err != nil { return err diff --git a/groups/operationperm.go b/groups/operationperm.go new file mode 100644 index 0000000000..517cb6a01c --- /dev/null +++ b/groups/operationperm.go @@ -0,0 +1,190 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package groups + +import "fmt" + +type Operation int + +func (op Operation) String() string { + switch op { + case OpViewGroup: + return OpViewGroupStr + case OpUpdateGroup: + return OpUpdateGroupStr + case OpUpdateGroupTags: + return OpUpdateGroupTagsStr + case OpEnableGroup: + return OpEnableGroupStr + case OpDisableGroup: + return OpDisableGroupStr + case OpRetrieveGroupHierarchy: + return OpRetrieveGroupHierarchyStr + case OpAddParentGroup: + return OpAddParentGroupStr + case OpRemoveParentGroup: + return OpRemoveParentGroupStr + case OpAddChildrenGroups: + return OpAddChildrenGroupsStr + case OpRemoveChildrenGroups: + return OpRemoveChildrenGroupsStr + case OpRemoveAllChildrenGroups: + return OpRemoveAllChildrenGroupsStr + case OpListChildrenGroups: + return OpListChildrenGroupsStr + case OpDeleteGroup: + return OpDeleteGroupStr + case OpCreateGroup: + return OpCreateGroupStr + case OpListGroups: + return OpListGroupsStr + case OpListUserGroups: + return OpListUserGroupsStr + default: + return fmt.Sprintf("unknown operation: %d", op) + } +} + +type OperationPerm struct { + opPerm map[Operation]Permission + expectedOps []Operation +} + +func newOperationPerm(expectedOps []Operation) OperationPerm { + return OperationPerm{ + opPerm: make(map[Operation]Permission), + expectedOps: expectedOps, + } +} + +func (opp OperationPerm) isKeyRequired(op Operation) bool { + for _, key := range opp.expectedOps { + if key == op { + return true + } + } + return false +} + +func (opp OperationPerm) AddOperationPermissionMap(opMap map[Operation]Permission) error { + // First iteration check all the keys are valid, If any one key is invalid then no key should be added. + for op := range opMap { + if !opp.isKeyRequired(op) { + return fmt.Errorf("%v is not a valid operation", op.String()) + } + } + for op, perm := range opMap { + opp.opPerm[op] = perm + } + return nil +} + +func (opp OperationPerm) AddOperationPermission(op Operation, perm Permission) error { + if !opp.isKeyRequired(op) { + return fmt.Errorf("%v is not a valid operation", op.String()) + } + opp.opPerm[op] = perm + return nil +} + +func (opp OperationPerm) Validate() error { + for op := range opp.opPerm { + if !opp.isKeyRequired(op) { + return fmt.Errorf("OperationPerm: \"%s\" is not a valid operation", op.String()) + } + } + for _, eeo := range opp.expectedOps { + if _, ok := opp.opPerm[eeo]; !ok { + return fmt.Errorf("OperationPerm: \"%s\" operation is missing", eeo.String()) + } + } + return nil +} + +func (opp OperationPerm) GetPermission(op Operation) (Permission, error) { + if perm, ok := opp.opPerm[op]; ok { + return perm, nil + } + return "", fmt.Errorf("operation \"%s\" doesn't have any permissions", op.String()) +} + +type Permission string + +func (p Permission) String() string { + return string(p) +} + +type ExternalOperation int + +func (op ExternalOperation) String() string { + switch op { + case DomainOpCreateGroup: + return DomainOpCreateGroupStr + case DomainOpListGroups: + return DomainOpListGroupsStr + case UserOpListGroups: + return UserOpListGroupsStr + case ClientsOpListGroups: + return ClientsOpListGroupsStr + case ChannelsOpListGroups: + return ChannelsOpListGroupsStr + default: + return fmt.Sprintf("unknown external operation: %d", op) + } +} + +type ExternalOperationPerm struct { + opPerm map[ExternalOperation]Permission + expectedOps []ExternalOperation +} + +func newExternalOperationPerm(expectedOps []ExternalOperation) ExternalOperationPerm { + return ExternalOperationPerm{ + opPerm: make(map[ExternalOperation]Permission), + expectedOps: expectedOps, + } +} + +func (eopp ExternalOperationPerm) isKeyRequired(eop ExternalOperation) bool { + for _, key := range eopp.expectedOps { + if key == eop { + return true + } + } + return false +} + +func (eopp ExternalOperationPerm) AddOperationPermissionMap(eopMap map[ExternalOperation]Permission) error { + // First iteration check all the keys are valid, If any one key is invalid then no key should be added. + for eop := range eopMap { + if !eopp.isKeyRequired(eop) { + return fmt.Errorf("%v is not a valid external operation", eop.String()) + } + } + for eop, perm := range eopMap { + eopp.opPerm[eop] = perm + } + return nil +} + +func (eopp ExternalOperationPerm) Validate() error { + for eop := range eopp.opPerm { + if !eopp.isKeyRequired(eop) { + return fmt.Errorf("ExternalOperationPerm: \"%s\" is not a valid external operation", eop.String()) + } + } + for _, eeo := range eopp.expectedOps { + if _, ok := eopp.opPerm[eeo]; !ok { + return fmt.Errorf("ExternalOperationPerm: \"%s\" external operation is missing", eeo.String()) + } + } + return nil +} + +func (eopp ExternalOperationPerm) GetPermission(eop ExternalOperation) (Permission, error) { + if perm, ok := eopp.opPerm[eop]; ok { + return perm, nil + } + return "", fmt.Errorf("external operation \"%s\" doesn't have any permissions", eop.String()) +} diff --git a/groups/roleoperations.go b/groups/roleoperations.go index 28451410b4..4ce74cc24c 100644 --- a/groups/roleoperations.go +++ b/groups/roleoperations.go @@ -3,15 +3,11 @@ package groups -import ( - "github.com/absmach/supermq/pkg/roles" - "github.com/absmach/supermq/pkg/svcutil" -) - -// Internal Operations +import "github.com/absmach/supermq/pkg/roles" +// Internal Operations. const ( - OpViewGroup svcutil.Operation = iota + OpViewGroup Operation = iota OpUpdateGroup OpUpdateGroupTags OpEnableGroup @@ -29,7 +25,7 @@ const ( OpListUserGroups ) -var expectedOperations = []svcutil.Operation{ +var expectedOperations = []Operation{ OpViewGroup, OpUpdateGroup, OpUpdateGroupTags, @@ -45,39 +41,39 @@ var expectedOperations = []svcutil.Operation{ OpDeleteGroup, } -var OperationNames = []string{ - "OpViewGroup", - "OpUpdateGroup", - "OpUpdateGroupTags", - "OpEnableGroup", - "OpDisableGroup", - "OpRetrieveGroupHierarchy", - "OpAddParentGroup", - "OpRemoveParentGroup", - "OpAddChildrenGroups", - "OpRemoveChildrenGroups", - "OpRemoveAllChildrenGroups", - "OpListChildrenGroups", - "OpDeleteGroup", - "OpCreateGroup", - "OpListGroups", - "OpListUserGroups", -} +const ( + OpViewGroupStr = "OpViewGroup" + OpUpdateGroupStr = "OpUpdateGroup" + OpUpdateGroupTagsStr = "OpUpdateGroupTags" + OpEnableGroupStr = "OpEnableGroup" + OpDisableGroupStr = "OpDisableGroup" + OpRetrieveGroupHierarchyStr = "OpRetrieveGroupHierarchy" + OpAddParentGroupStr = "OpAddParentGroup" + OpRemoveParentGroupStr = "OpRemoveParentGroup" + OpAddChildrenGroupsStr = "OpAddChildrenGroups" + OpRemoveChildrenGroupsStr = "OpRemoveChildrenGroups" + OpRemoveAllChildrenGroupsStr = "OpRemoveAllChildrenGroups" + OpListChildrenGroupsStr = "OpListChildrenGroups" + OpDeleteGroupStr = "OpDeleteGroup" + OpCreateGroupStr = "OpCreateGroup" + OpListGroupsStr = "OpListGroups" + OpListUserGroupsStr = "OpListUserGroups" +) -func NewOperationPerm() svcutil.OperationPerm { - return svcutil.NewOperationPerm(expectedOperations, OperationNames) +func NewOperationPerm() OperationPerm { + return newOperationPerm(expectedOperations) } // External Operations. const ( - DomainOpCreateGroup svcutil.ExternalOperation = iota + DomainOpCreateGroup ExternalOperation = iota DomainOpListGroups UserOpListGroups ClientsOpListGroups ChannelsOpListGroups ) -var expectedExternalOperations = []svcutil.ExternalOperation{ +var expectedExternalOperations = []ExternalOperation{ DomainOpCreateGroup, DomainOpListGroups, UserOpListGroups, @@ -85,16 +81,16 @@ var expectedExternalOperations = []svcutil.ExternalOperation{ ChannelsOpListGroups, } -var externalOperationNames = []string{ - "DomainOpCreateGroup", - "DomainOpListGroups", - "UserOpListGroups", - "ClientsOpListGroups", - "ChannelsOpListGroups", -} +const ( + DomainOpCreateGroupStr = "DomainOpCreateGroup" + DomainOpListGroupsStr = "DomainOpListGroups" + UserOpListGroupsStr = "UserOpListGroups" + ClientsOpListGroupsStr = "ClientsOpListGroups" + ChannelsOpListGroupsStr = "ChannelsOpListGroups" +) -func NewExternalOperationPerm() svcutil.ExternalOperationPerm { - return svcutil.NewExternalOperationPerm(expectedExternalOperations, externalOperationNames) +func NewExternalOperationPerm() ExternalOperationPerm { + return newExternalOperationPerm(expectedExternalOperations) } // Below codes should moved out of service, may be can be kept in `cmd//main.go` @@ -112,8 +108,8 @@ const ( viewRoleUsersPermission = "view_role_users_permission" ) -func NewOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { - opPerm := map[svcutil.Operation]svcutil.Permission{ +func NewOperationPermissionMap() map[Operation]Permission { + opPerm := map[Operation]Permission{ OpViewGroup: readPermission, OpUpdateGroup: updatePermission, OpUpdateGroupTags: updatePermission, @@ -131,8 +127,8 @@ func NewOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { return opPerm } -func NewRolesOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { - opPerm := map[svcutil.Operation]svcutil.Permission{ +func NewRolesOperationPermissionMap() map[roles.Operation]roles.Permission { + opPerm := map[roles.Operation]roles.Permission{ roles.OpAddRole: manageRolePermission, roles.OpRemoveRole: manageRolePermission, roles.OpUpdateRoleName: manageRolePermission, @@ -161,8 +157,8 @@ const ( chanelListGroupPermission = "read_permission" ) -func NewExternalOperationPermissionMap() map[svcutil.ExternalOperation]svcutil.Permission { - extOpPerm := map[svcutil.ExternalOperation]svcutil.Permission{ +func NewExternalOperationPermissionMap() map[ExternalOperation]Permission { + extOpPerm := map[ExternalOperation]Permission{ DomainOpCreateGroup: domainCreateGroupPermission, DomainOpListGroups: domainListGroupPermission, UserOpListGroups: userListGroupsPermission, diff --git a/pkg/roles/operationperm.go b/pkg/roles/operationperm.go new file mode 100644 index 0000000000..23f47ae1bd --- /dev/null +++ b/pkg/roles/operationperm.go @@ -0,0 +1,168 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package roles + +import "fmt" + +type Operation int + +const ( + OpAddRole Operation = iota + OpRemoveRole + OpUpdateRoleName + OpRetrieveRole + OpRetrieveAllRoles + OpRoleAddActions + OpRoleListActions + OpRoleCheckActionsExists + OpRoleRemoveActions + OpRoleRemoveAllActions + OpRoleAddMembers + OpRoleListMembers + OpRoleCheckMembersExists + OpRoleRemoveMembers + OpRoleRemoveAllMembers + OpListAvailableActions +) + +var expectedOperations = []Operation{ + OpAddRole, + OpRemoveRole, + OpUpdateRoleName, + OpRetrieveRole, + OpRetrieveAllRoles, + OpRoleAddActions, + OpRoleListActions, + OpRoleCheckActionsExists, + OpRoleRemoveActions, + OpRoleRemoveAllActions, + OpRoleAddMembers, + OpRoleListMembers, + OpRoleCheckMembersExists, + OpRoleRemoveMembers, + OpRoleRemoveAllMembers, +} + +const ( + OpAddRoleStr = "OpAddRole" + OpRemoveRoleStr = "OpRemoveRole" + OpUpdateRoleNameStr = "OpUpdateRoleName" + OpRetrieveRoleStr = "OpRetrieveRole" + OpRetrieveAllRolesStr = "OpRetrieveAllRoles" + OpRoleAddActionsStr = "OpRoleAddActions" + OpRoleListActionsStr = "OpRoleListActions" + OpRoleCheckActionsExistsStr = "OpRoleCheckActionsExists" + OpRoleRemoveActionsStr = "OpRoleRemoveActions" + OpRoleRemoveAllActionsStr = "OpRoleRemoveAllActions" + OpRoleAddMembersStr = "OpRoleAddMembers" + OpRoleListMembersStr = "OpRoleListMembers" + OpRoleCheckMembersExistsStr = "OpRoleCheckMembersExists" + OpRoleRemoveMembersStr = "OpRoleRemoveMembers" + OpRoleRemoveAllMembersStr = "OpRoleRemoveAllMembers" + OpListAvailableActionsStr = "OpListAvailableActions" +) + +func (op Operation) String() string { + switch op { + case OpAddRole: + return OpAddRoleStr + case OpRemoveRole: + return OpRemoveRoleStr + case OpUpdateRoleName: + return OpUpdateRoleNameStr + case OpRetrieveRole: + return OpRetrieveRoleStr + case OpRetrieveAllRoles: + return OpRetrieveAllRolesStr + case OpRoleAddActions: + return OpRoleAddActionsStr + case OpRoleListActions: + return OpRoleListActionsStr + case OpRoleCheckActionsExists: + return OpRoleCheckActionsExistsStr + case OpRoleRemoveActions: + return OpRoleRemoveActionsStr + case OpRoleRemoveAllActions: + return OpRoleRemoveAllActionsStr + case OpRoleAddMembers: + return OpRoleAddMembersStr + case OpRoleListMembers: + return OpRoleListMembersStr + case OpRoleCheckMembersExists: + return OpRoleCheckMembersExistsStr + case OpRoleRemoveMembers: + return OpRoleRemoveMembersStr + case OpRoleRemoveAllMembers: + return OpRoleRemoveAllMembersStr + case OpListAvailableActions: + return OpListAvailableActionsStr + default: + return fmt.Sprintf("unknown operation: %d", op) + } +} + +type Permission string + +func (p Permission) String() string { + return string(p) +} + +type OperationPerm struct { + opPerm map[Operation]Permission + expectedOps []Operation +} + +func NewOperationPerm() OperationPerm { + return newOperationPerm(expectedOperations) +} + +func newOperationPerm(expectedOps []Operation) OperationPerm { + return OperationPerm{ + opPerm: make(map[Operation]Permission), + expectedOps: expectedOps, + } +} + +func (opp OperationPerm) AddOperationPermissionMap(opMap map[Operation]Permission) error { + // First iteration check all the keys are valid, If any one key is invalid then no key should be added. + for op := range opMap { + if !opp.isKeyRequired(op) { + return fmt.Errorf("%v is not a valid operation", op.String()) + } + } + for op, perm := range opMap { + opp.opPerm[op] = perm + } + return nil +} + +func (opp OperationPerm) isKeyRequired(op Operation) bool { + for _, key := range opp.expectedOps { + if key == op { + return true + } + } + return false +} + +func (opp OperationPerm) Validate() error { + for op := range opp.opPerm { + if !opp.isKeyRequired(op) { + return fmt.Errorf("OperationPerm: \"%s\" is not a valid operation", op.String()) + } + } + for _, eeo := range opp.expectedOps { + if _, ok := opp.opPerm[eeo]; !ok { + return fmt.Errorf("OperationPerm: \"%s\" operation is missing", eeo.String()) + } + } + return nil +} + +func (opp OperationPerm) GetPermission(op Operation) (Permission, error) { + if perm, ok := opp.opPerm[op]; ok { + return perm, nil + } + return "", fmt.Errorf("operation \"%s\" doesn't have any permissions", op.String()) +} diff --git a/pkg/roles/rolemanager/middleware/authorization.go b/pkg/roles/rolemanager/middleware/authorization.go index 361a4619bd..f9f87aad03 100644 --- a/pkg/roles/rolemanager/middleware/authorization.go +++ b/pkg/roles/rolemanager/middleware/authorization.go @@ -14,7 +14,6 @@ import ( "github.com/absmach/supermq/pkg/errors" "github.com/absmach/supermq/pkg/policies" "github.com/absmach/supermq/pkg/roles" - "github.com/absmach/supermq/pkg/svcutil" ) var _ roles.RoleManager = (*RoleManagerAuthorizationMiddleware)(nil) @@ -24,11 +23,10 @@ type RoleManagerAuthorizationMiddleware struct { svc roles.RoleManager authz smqauthz.Authorization callout callout.Callout - opp svcutil.OperationPerm + opp roles.OperationPerm } -// AuthorizationMiddleware adds authorization to the clients service. -func NewRoleManagerAuthorizationMiddleware(entityType string, svc roles.RoleManager, authz smqauthz.Authorization, opPerm map[svcutil.Operation]svcutil.Permission, callout callout.Callout) (RoleManagerAuthorizationMiddleware, error) { +func NewRoleManagerAuthorizationMiddleware(entityType string, svc roles.RoleManager, authz smqauthz.Authorization, opPerm map[roles.Operation]roles.Permission, callout callout.Callout) (RoleManagerAuthorizationMiddleware, error) { opp := roles.NewOperationPerm() if err := opp.AddOperationPermissionMap(opPerm); err != nil { return RoleManagerAuthorizationMiddleware{}, err @@ -44,19 +42,9 @@ func NewRoleManagerAuthorizationMiddleware(entityType string, svc roles.RoleMana opp: opp, callout: callout, } - if err := ram.validate(); err != nil { - return RoleManagerAuthorizationMiddleware{}, err - } return ram, nil } -func (ram RoleManagerAuthorizationMiddleware) validate() error { - if err := ram.opp.Validate(); err != nil { - return err - } - return nil -} - func (ram RoleManagerAuthorizationMiddleware) AddRole(ctx context.Context, session authn.Session, entityID, roleName string, optionalActions []string, optionalMembers []string) (roles.RoleProvision, error) { if err := ram.authorize(ctx, roles.OpAddRole, smqauthz.PolicyReq{ Domain: session.DomainID, @@ -78,9 +66,10 @@ func (ram RoleManagerAuthorizationMiddleware) AddRole(ctx context.Context, sessi "optional_members": optionalMembers, "count": 1, } - if err := ram.callOut(ctx, session, roles.OpAddRole.String(roles.OperationNames), params); err != nil { + if err := ram.callOut(ctx, session, roles.OpAddRole.String(), params); err != nil { return roles.RoleProvision{}, err } + return ram.svc.AddRole(ctx, session, entityID, roleName, optionalActions, optionalMembers) } @@ -99,14 +88,14 @@ func (ram RoleManagerAuthorizationMiddleware) RemoveRole(ctx context.Context, se "entity_id": entityID, "role_id": roleID, } - if err := ram.callOut(ctx, session, roles.OpRemoveRole.String(roles.OperationNames), params); err != nil { + if err := ram.callOut(ctx, session, roles.OpRemoveRole.String(), params); err != nil { return err } return ram.svc.RemoveRole(ctx, session, entityID, roleID) } func (ram RoleManagerAuthorizationMiddleware) UpdateRoleName(ctx context.Context, session authn.Session, entityID, roleID, newRoleName string) (roles.Role, error) { - if err := ram.authorize(ctx, roles.OpUpdateRoleName, smqauthz.PolicyReq{ + if err := ram.authorize(ctx, roles.OpAddRole, smqauthz.PolicyReq{ Domain: session.DomainID, Subject: session.DomainUserID, SubjectType: policies.UserType, @@ -121,7 +110,7 @@ func (ram RoleManagerAuthorizationMiddleware) UpdateRoleName(ctx context.Context "role_id": roleID, "new_role_name": newRoleName, } - if err := ram.callOut(ctx, session, roles.OpUpdateRoleName.String(roles.OperationNames), params); err != nil { + if err := ram.callOut(ctx, session, roles.OpUpdateRoleName.String(), params); err != nil { return roles.Role{}, err } return ram.svc.UpdateRoleName(ctx, session, entityID, roleID, newRoleName) @@ -142,7 +131,7 @@ func (ram RoleManagerAuthorizationMiddleware) RetrieveRole(ctx context.Context, "entity_id": entityID, "role_id": roleID, } - if err := ram.callOut(ctx, session, roles.OpRetrieveRole.String(roles.OperationNames), params); err != nil { + if err := ram.callOut(ctx, session, roles.OpRetrieveRole.String(), params); err != nil { return roles.Role{}, err } return ram.svc.RetrieveRole(ctx, session, entityID, roleID) @@ -164,7 +153,7 @@ func (ram RoleManagerAuthorizationMiddleware) RetrieveAllRoles(ctx context.Conte "limit": limit, "offset": offset, } - if err := ram.callOut(ctx, session, roles.OpRetrieveAllRoles.String(roles.OperationNames), params); err != nil { + if err := ram.callOut(ctx, session, roles.OpRetrieveAllRoles.String(), params); err != nil { return roles.RolePage{}, err } return ram.svc.RetrieveAllRoles(ctx, session, entityID, limit, offset) @@ -172,7 +161,7 @@ func (ram RoleManagerAuthorizationMiddleware) RetrieveAllRoles(ctx context.Conte func (ram RoleManagerAuthorizationMiddleware) ListAvailableActions(ctx context.Context, session authn.Session) ([]string, error) { params := map[string]any{} - if err := ram.callOut(ctx, session, roles.OpListAvailableActions.String(roles.OperationNames), params); err != nil { + if err := ram.callOut(ctx, session, roles.OpListAvailableActions.String(), params); err != nil { return []string{}, err } return ram.svc.ListAvailableActions(ctx, session) @@ -195,7 +184,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleAddActions(ctx context.Context "role_id": roleID, "actions": actions, } - if err := ram.callOut(ctx, session, roles.OpRoleAddActions.String(roles.OperationNames), params); err != nil { + if err := ram.callOut(ctx, session, roles.OpRoleAddActions.String(), params); err != nil { return []string{}, err } @@ -218,7 +207,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleListActions(ctx context.Contex "entity_id": entityID, "role_id": roleID, } - if err := ram.callOut(ctx, session, roles.OpRoleListActions.String(roles.OperationNames), params); err != nil { + if err := ram.callOut(ctx, session, roles.OpRoleListActions.String(), params); err != nil { return []string{}, err } @@ -241,7 +230,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleCheckActionsExists(ctx context "role_id": roleID, "actions": actions, } - if err := ram.callOut(ctx, session, roles.OpRoleCheckActionsExists.String(roles.OperationNames), params); err != nil { + if err := ram.callOut(ctx, session, roles.OpRoleCheckActionsExists.String(), params); err != nil { return false, err } return ram.svc.RoleCheckActionsExists(ctx, session, entityID, roleID, actions) @@ -263,7 +252,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleRemoveActions(ctx context.Cont "role_id": roleID, "actions": actions, } - if err := ram.callOut(ctx, session, roles.OpRoleRemoveActions.String(roles.OperationNames), params); err != nil { + if err := ram.callOut(ctx, session, roles.OpRoleRemoveActions.String(), params); err != nil { return err } @@ -285,7 +274,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleRemoveAllActions(ctx context.C "entity_id": entityID, "role_id": roleID, } - if err := ram.callOut(ctx, session, roles.OpRoleRemoveAllActions.String(roles.OperationNames), params); err != nil { + if err := ram.callOut(ctx, session, roles.OpRoleRemoveAllActions.String(), params); err != nil { return err } return ram.svc.RoleRemoveAllActions(ctx, session, entityID, roleID) @@ -311,7 +300,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleAddMembers(ctx context.Context "role_id": roleID, "members": members, } - if err := ram.callOut(ctx, session, roles.OpRoleAddMembers.String(roles.OperationNames), params); err != nil { + if err := ram.callOut(ctx, session, roles.OpRoleAddMembers.String(), params); err != nil { return []string{}, err } return ram.svc.RoleAddMembers(ctx, session, entityID, roleID, members) @@ -334,7 +323,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleListMembers(ctx context.Contex "limit": limit, "offset": offset, } - if err := ram.callOut(ctx, session, roles.OpRoleListMembers.String(roles.OperationNames), params); err != nil { + if err := ram.callOut(ctx, session, roles.OpRoleListMembers.String(), params); err != nil { return roles.MembersPage{}, err } return ram.svc.RoleListMembers(ctx, session, entityID, roleID, limit, offset) @@ -356,7 +345,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleCheckMembersExists(ctx context "role_id": roleID, "members": members, } - if err := ram.callOut(ctx, session, roles.OpRoleCheckMembersExists.String(roles.OperationNames), params); err != nil { + if err := ram.callOut(ctx, session, roles.OpRoleCheckMembersExists.String(), params); err != nil { return false, err } return ram.svc.RoleCheckMembersExists(ctx, session, entityID, roleID, members) @@ -377,7 +366,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleRemoveAllMembers(ctx context.C "entity_id": entityID, "role_id": roleID, } - if err := ram.callOut(ctx, session, roles.OpRoleRemoveAllMembers.String(roles.OperationNames), params); err != nil { + if err := ram.callOut(ctx, session, roles.OpRoleRemoveAllMembers.String(), params); err != nil { return err } return ram.svc.RoleRemoveAllMembers(ctx, session, entityID, roleID) @@ -398,7 +387,7 @@ func (ram RoleManagerAuthorizationMiddleware) ListEntityMembers(ctx context.Cont "entity_id": entityID, "page_query": pageQuery, } - if err := ram.callOut(ctx, session, roles.OpRoleListMembers.String(roles.OperationNames), params); err != nil { + if err := ram.callOut(ctx, session, roles.OpRoleListMembers.String(), params); err != nil { return roles.MembersRolePage{}, err } return ram.svc.ListEntityMembers(ctx, session, entityID, pageQuery) @@ -419,7 +408,7 @@ func (ram RoleManagerAuthorizationMiddleware) RemoveEntityMembers(ctx context.Co "entity_id": entityID, "members": members, } - if err := ram.callOut(ctx, session, roles.OpRoleRemoveAllMembers.String(roles.OperationNames), params); err != nil { + if err := ram.callOut(ctx, session, roles.OpRoleRemoveAllMembers.String(), params); err != nil { return err } return ram.svc.RemoveEntityMembers(ctx, session, entityID, members) @@ -441,13 +430,13 @@ func (ram RoleManagerAuthorizationMiddleware) RoleRemoveMembers(ctx context.Cont "role_id": roleID, "members": members, } - if err := ram.callOut(ctx, session, roles.OpRoleRemoveMembers.String(roles.OperationNames), params); err != nil { + if err := ram.callOut(ctx, session, roles.OpRoleRemoveMembers.String(), params); err != nil { return err } return ram.svc.RoleRemoveMembers(ctx, session, entityID, roleID, members) } -func (ram RoleManagerAuthorizationMiddleware) authorize(ctx context.Context, op svcutil.Operation, pr smqauthz.PolicyReq) error { +func (ram RoleManagerAuthorizationMiddleware) authorize(ctx context.Context, op roles.Operation, pr smqauthz.PolicyReq) error { perm, err := ram.opp.GetPermission(op) if err != nil { return err diff --git a/pkg/roles/roles.go b/pkg/roles/roles.go index 6ada6121cc..d2ad2b6c9b 100644 --- a/pkg/roles/roles.go +++ b/pkg/roles/roles.go @@ -9,7 +9,6 @@ import ( "github.com/absmach/supermq/pkg/authn" "github.com/absmach/supermq/pkg/policies" - "github.com/absmach/supermq/pkg/svcutil" ) type Action string @@ -184,63 +183,3 @@ type Repository interface { RemoveEntityMembers(ctx context.Context, entityID string, members []string) error RemoveMemberFromAllRoles(ctx context.Context, memberID string) (err error) } - -const ( - OpAddRole svcutil.Operation = iota - OpRemoveRole - OpUpdateRoleName - OpRetrieveRole - OpRetrieveAllRoles - OpRoleAddActions - OpRoleListActions - OpRoleCheckActionsExists - OpRoleRemoveActions - OpRoleRemoveAllActions - OpRoleAddMembers - OpRoleListMembers - OpRoleCheckMembersExists - OpRoleRemoveMembers - OpRoleRemoveAllMembers - OpListAvailableActions -) - -var expectedOperations = []svcutil.Operation{ - OpAddRole, - OpRemoveRole, - OpUpdateRoleName, - OpRetrieveRole, - OpRetrieveAllRoles, - OpRoleAddActions, - OpRoleListActions, - OpRoleCheckActionsExists, - OpRoleRemoveActions, - OpRoleRemoveAllActions, - OpRoleAddMembers, - OpRoleListMembers, - OpRoleCheckMembersExists, - OpRoleRemoveMembers, - OpRoleRemoveAllMembers, -} - -var OperationNames = []string{ - "OpAddRole", - "OpRemoveRole", - "OpUpdateRoleName", - "OpRetrieveRole", - "OpRetrieveAllRoles", - "OpRoleAddActions", - "OpRoleListActions", - "OpRoleCheckActionsExists", - "OpRoleRemoveActions", - "OpRoleRemoveAllActions", - "OpRoleAddMembers", - "OpRoleListMembers", - "OpRoleCheckMembersExists", - "OpRoleRemoveMembers", - "OpRoleRemoveAllMembers", - "OpListAvailableActions", -} - -func NewOperationPerm() svcutil.OperationPerm { - return svcutil.NewOperationPerm(expectedOperations, OperationNames) -} diff --git a/pkg/svcutil/externaloperationperm.go b/pkg/svcutil/externaloperationperm.go deleted file mode 100644 index d89ec45e66..0000000000 --- a/pkg/svcutil/externaloperationperm.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package svcutil - -import "fmt" - -type ExternalOperation int - -func (op ExternalOperation) String(operations []string) string { - if (int(op) < 0) || (int(op) == len(operations)) { - return fmt.Sprintf("UnknownOperation(%d)", op) - } - return operations[op] -} - -type ExternalOperationPerm struct { - opPerm map[ExternalOperation]Permission - expectedOps []ExternalOperation - opNames []string -} - -func NewExternalOperationPerm(expectedOps []ExternalOperation, opNames []string) ExternalOperationPerm { - return ExternalOperationPerm{ - opPerm: make(map[ExternalOperation]Permission), - expectedOps: expectedOps, - opNames: opNames, - } -} - -func (eopp ExternalOperationPerm) isKeyRequired(eop ExternalOperation) bool { - for _, key := range eopp.expectedOps { - if key == eop { - return true - } - } - return false -} - -func (eopp ExternalOperationPerm) AddOperationPermissionMap(eopMap map[ExternalOperation]Permission) error { - // First iteration check all the keys are valid, If any one key is invalid then no key should be added. - for eop := range eopMap { - if !eopp.isKeyRequired(eop) { - return fmt.Errorf("%v is not a valid external operation", eop.String(eopp.opNames)) - } - } - for eop, perm := range eopMap { - eopp.opPerm[eop] = perm - } - return nil -} - -func (eopp ExternalOperationPerm) AddOperationPermission(eop ExternalOperation, perm Permission) error { - if !eopp.isKeyRequired(eop) { - return fmt.Errorf("%v is not a valid external operation", eop.String(eopp.opNames)) - } - eopp.opPerm[eop] = perm - return nil -} - -func (eopp ExternalOperationPerm) Validate() error { - for eop := range eopp.opPerm { - if !eopp.isKeyRequired(eop) { - return fmt.Errorf("ExternalOperationPerm: \"%s\" is not a valid external operation", eop.String(eopp.opNames)) - } - } - for _, eeo := range eopp.expectedOps { - if _, ok := eopp.opPerm[eeo]; !ok { - return fmt.Errorf("ExternalOperationPerm: \"%s\" external operation is missing", eeo.String(eopp.opNames)) - } - } - return nil -} - -func (eopp ExternalOperationPerm) GetPermission(eop ExternalOperation) (Permission, error) { - if perm, ok := eopp.opPerm[eop]; ok { - return perm, nil - } - return "", fmt.Errorf("external operation \"%s\" doesn't have any permissions", eop.String(eopp.opNames)) -}