From 961574a27f28f2981c6d57308c8612197d5b8072 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 29 May 2025 13:20:27 +0300 Subject: [PATCH 01/12] initial implementation Signed-off-by: nyagamunene --- clients/middleware/authorization.go | 44 ++++---- clients/operationperm.go | 152 ++++++++++++++++++++++++++++ clients/roleoperations.go | 118 ++++++++++++++------- 3 files changed, 260 insertions(+), 54 deletions(-) create mode 100644 clients/operationperm.go diff --git a/clients/middleware/authorization.go b/clients/middleware/authorization.go index 45dd1d255f..978ad9303f 100644 --- a/clients/middleware/authorization.go +++ b/clients/middleware/authorization.go @@ -42,8 +42,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 +54,8 @@ 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, rolesOpPerm map[clients.Operation]clients.Permission, + extOpPerm map[clients.ExternalOperation]clients.Permission, callout callout.Callout, ) (clients.Service, error) { opp := clients.NewOperationPerm() @@ -65,7 +65,13 @@ func AuthorizationMiddleware( if err := opp.Validate(); err != nil { return nil, err } - ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(policies.ClientType, svc, authz, rolesOpPerm, callout) + + res := make(map[svcutil.Operation]svcutil.Permission, len(rolesOpPerm)) + for op, perm := range rolesOpPerm { + res[svcutil.Operation(op)] = svcutil.Permission(perm) + } + + ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(policies.ClientType, svc, authz, res, callout) if err != nil { return nil, err } @@ -116,7 +122,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 +157,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 +185,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 +213,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 +248,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 +283,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 +318,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 +352,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 +387,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 +421,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 +467,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 +518,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 +527,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 +542,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..0c33c30b13 --- /dev/null +++ b/clients/operationperm.go @@ -0,0 +1,152 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package clients + +import "fmt" + +type Operation int + +func (op Operation) String() string { + if (int(op) < 0) || (int(op) == len(OperationNames)) { + return fmt.Sprintf("UnknownOperation(%d)", op) + } + return OperationNames[op] +} + +type OperationPerm struct { + opPerm map[Operation]Permission + expectedOps []Operation + opNames []string +} + +func newOperationPerm(expectedOps []Operation, opNames []string) 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()) + } + } + 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(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) 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)) +} diff --git a/clients/roleoperations.go b/clients/roleoperations.go index 8c99825ff5..5d31a7e9e3 100644 --- a/clients/roleoperations.go +++ b/clients/roleoperations.go @@ -3,15 +3,9 @@ package clients -import ( - "github.com/absmach/supermq/pkg/roles" - "github.com/absmach/supermq/pkg/svcutil" -) - // Internal Operations - const ( - OpViewClient svcutil.Operation = iota + OpViewClient Operation = iota OpUpdateClient OpUpdateClientTags OpUpdateClientSecret @@ -27,7 +21,7 @@ const ( OpListUserClients ) -var expectedOperations = []svcutil.Operation{ +var expectedOperations = []Operation{ OpViewClient, OpUpdateClient, OpUpdateClientTags, @@ -58,13 +52,13 @@ var OperationNames = []string{ "OpListUserClients", } -func NewOperationPerm() svcutil.OperationPerm { - return svcutil.NewOperationPerm(expectedOperations, OperationNames) +func NewOperationPerm() OperationPerm { + return newOperationPerm(expectedOperations, OperationNames) } // External Operations. const ( - DomainOpCreateClient svcutil.ExternalOperation = iota + DomainOpCreateClient ExternalOperation = iota DomainOpListClients GroupOpSetChildClient GroupsOpRemoveChildClient @@ -72,7 +66,7 @@ const ( ChannelsOpDisconnectChannel ) -var expectedExternalOperations = []svcutil.ExternalOperation{ +var expectedExternalOperations = []ExternalOperation{ DomainOpCreateClient, DomainOpListClients, GroupOpSetChildClient, @@ -90,8 +84,8 @@ var externalOperationNames = []string{ "ChannelsOpDisconnectChannel", } -func NewExternalOperationPerm() svcutil.ExternalOperationPerm { - return svcutil.NewExternalOperationPerm(expectedExternalOperations, externalOperationNames) +func NewExternalOperationPerm() ExternalOperationPerm { + return newExternalOperationPerm(expectedExternalOperations, externalOperationNames) } // Below codes should moved out of service, may be can be kept in `cmd//main.go` @@ -109,8 +103,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,23 +120,77 @@ func NewOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { return opPerm } -func NewRolesOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { - opPerm := map[svcutil.Operation]svcutil.Permission{ - roles.OpAddRole: manageRolePermission, - roles.OpRemoveRole: manageRolePermission, - roles.OpUpdateRoleName: manageRolePermission, - roles.OpRetrieveRole: manageRolePermission, - roles.OpRetrieveAllRoles: manageRolePermission, - roles.OpRoleAddActions: manageRolePermission, - roles.OpRoleListActions: manageRolePermission, - roles.OpRoleCheckActionsExists: manageRolePermission, - roles.OpRoleRemoveActions: manageRolePermission, - roles.OpRoleRemoveAllActions: manageRolePermission, - roles.OpRoleAddMembers: addRoleUsersPermission, - roles.OpRoleListMembers: viewRoleUsersPermission, - roles.OpRoleCheckMembersExists: viewRoleUsersPermission, - roles.OpRoleRemoveMembers: removeRoleUsersPermission, - roles.OpRoleRemoveAllMembers: manageRolePermission, +const ( + OpAddRole Operation = iota + OpRemoveRole + OpUpdateRoleName + OpRetrieveRole + OpRetrieveAllRoles + OpRoleAddActions + OpRoleListActions + OpRoleCheckActionsExists + OpRoleRemoveActions + OpRoleRemoveAllActions + OpRoleAddMembers + OpRoleListMembers + OpRoleCheckMembersExists + OpRoleRemoveMembers + OpRoleRemoveAllMembers +) + +var rolesExpectedOperations = []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", +} + +func NewRolesOperationPermissionMap() map[Operation]Permission { + opPerm := map[Operation]Permission{ + OpAddRole: manageRolePermission, + OpRemoveRole: manageRolePermission, + OpUpdateRoleName: manageRolePermission, + OpRetrieveRole: manageRolePermission, + OpRetrieveAllRoles: manageRolePermission, + OpRoleAddActions: manageRolePermission, + OpRoleListActions: manageRolePermission, + OpRoleCheckActionsExists: manageRolePermission, + OpRoleRemoveActions: manageRolePermission, + OpRoleRemoveAllActions: manageRolePermission, + OpRoleAddMembers: addRoleUsersPermission, + OpRoleListMembers: viewRoleUsersPermission, + OpRoleCheckMembersExists: viewRoleUsersPermission, + OpRoleRemoveMembers: removeRoleUsersPermission, + OpRoleRemoveAllMembers: manageRolePermission, } return opPerm } @@ -159,8 +207,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, From 3bf0044f3a3f25d45f9935c59df34a5c6b2daeff Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 2 Jun 2025 23:38:12 +0300 Subject: [PATCH 02/12] update string method Signed-off-by: nyagamunene --- clients/operationperm.go | 78 ++++++++++++++++++++++++++++++--------- clients/roleoperations.go | 56 ++++++++++++++-------------- 2 files changed, 89 insertions(+), 45 deletions(-) diff --git a/clients/operationperm.go b/clients/operationperm.go index 0c33c30b13..85e69c1ffc 100644 --- a/clients/operationperm.go +++ b/clients/operationperm.go @@ -8,23 +8,49 @@ import "fmt" type Operation int func (op Operation) String() string { - if (int(op) < 0) || (int(op) == len(OperationNames)) { - return fmt.Sprintf("UnknownOperation(%d)", op) + 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) } - return OperationNames[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, } } @@ -87,24 +113,42 @@ func (p Permission) String() string { type ExternalOperation int -func (op ExternalOperation) String(operations []string) string { - if (int(op) < 0) || (int(op) == len(operations)) { - return fmt.Sprintf("UnknownOperation(%d)", op) +// func (op ExternalOperation) String(operations []string) string { +// if (int(op) < 0) || (int(op) == len(operations)) { +// return fmt.Sprintf("UnknownOperation(%d)", op) +// } +// return operations[op] +// } + +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) } - return operations[op] } type ExternalOperationPerm struct { opPerm map[ExternalOperation]Permission expectedOps []ExternalOperation - opNames []string + // opNames []string } -func newExternalOperationPerm(expectedOps []ExternalOperation, opNames []string) ExternalOperationPerm { +func newExternalOperationPerm(expectedOps []ExternalOperation) ExternalOperationPerm { return ExternalOperationPerm{ opPerm: make(map[ExternalOperation]Permission), expectedOps: expectedOps, - opNames: opNames, } } @@ -121,7 +165,7 @@ func (eopp ExternalOperationPerm) AddOperationPermissionMap(eopMap map[ExternalO // 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)) + return fmt.Errorf("%v is not a valid external operation", eop.String()) } } for eop, perm := range eopMap { @@ -133,12 +177,12 @@ func (eopp ExternalOperationPerm) AddOperationPermissionMap(eopMap map[ExternalO 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)) + 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(eopp.opNames)) + return fmt.Errorf("ExternalOperationPerm: \"%s\" external operation is missing", eeo.String()) } } return nil @@ -148,5 +192,5 @@ func (eopp ExternalOperationPerm) GetPermission(eop ExternalOperation) (Permissi 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)) + return "", fmt.Errorf("external operation \"%s\" doesn't have any permissions", eop.String()) } diff --git a/clients/roleoperations.go b/clients/roleoperations.go index 5d31a7e9e3..d93489b1a9 100644 --- a/clients/roleoperations.go +++ b/clients/roleoperations.go @@ -21,6 +21,23 @@ const ( OpListUserClients ) +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, @@ -35,25 +52,8 @@ var expectedOperations = []Operation{ OpDisconnectFromChannel, } -var OperationNames = []string{ - "OpViewClient", - "OpUpdateClient", - "OpUpdateClientTags", - "OpUpdateClientSecret", - "OpEnableClient", - "OpDisableClient", - "OpDeleteClient", - "OpSetParentGroup", - "OpRemoveParentGroup", - "OpConnectToChannel", - "OpDisconnectFromChannel", - "OpCreateClient", - "OpListClients", - "OpListUserClients", -} - func NewOperationPerm() OperationPerm { - return newOperationPerm(expectedOperations, OperationNames) + return newOperationPerm(expectedOperations) } // External Operations. @@ -66,6 +66,15 @@ const ( ChannelsOpDisconnectChannel ) +const ( + DomainOpCreateClientStr = "DomainOpCreateClient" + DomainOpListClientsStr = "DomainOpListClients" + GroupOpSetChildClientStr = "GroupOpSetChildClient" + GroupsOpRemoveChildClientStr = "GroupsOpRemoveChildClient" + ChannelsOpConnectChannelStr = "ChannelsOpConnectChannel" + ChannelsOpDisconnectChannelStr = "ChannelsOpDisconnectChannel" +) + var expectedExternalOperations = []ExternalOperation{ DomainOpCreateClient, DomainOpListClients, @@ -75,17 +84,8 @@ var expectedExternalOperations = []ExternalOperation{ ChannelsOpDisconnectChannel, } -var externalOperationNames = []string{ - "DomainOpCreateClient", - "DomainOpListClients", - "GroupOpSetChildClient", - "GroupsOpRemoveChildClient", - "ChannelsOpConnectChannel", - "ChannelsOpDisconnectChannel", -} - func NewExternalOperationPerm() ExternalOperationPerm { - return newExternalOperationPerm(expectedExternalOperations, externalOperationNames) + return newExternalOperationPerm(expectedExternalOperations) } // Below codes should moved out of service, may be can be kept in `cmd//main.go` From ce61731e02cbb3fbd3f23b5f00e9ed9b505f9aa4 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Mon, 2 Jun 2025 23:50:13 +0300 Subject: [PATCH 03/12] remove unused code Signed-off-by: nyagamunene --- clients/roleoperations.go | 38 +------------------------------------- 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/clients/roleoperations.go b/clients/roleoperations.go index d93489b1a9..ed085c7a42 100644 --- a/clients/roleoperations.go +++ b/clients/roleoperations.go @@ -3,7 +3,7 @@ package clients -// Internal Operations +// Internal Operations. const ( OpViewClient Operation = iota OpUpdateClient @@ -138,42 +138,6 @@ const ( OpRoleRemoveAllMembers ) -var rolesExpectedOperations = []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", -} - func NewRolesOperationPermissionMap() map[Operation]Permission { opPerm := map[Operation]Permission{ OpAddRole: manageRolePermission, From 78caf1efc8cc65bcb4b2b51c5445d2eaf35290b7 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Tue, 3 Jun 2025 00:23:17 +0300 Subject: [PATCH 04/12] remove commented code Signed-off-by: nyagamunene --- clients/operationperm.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/clients/operationperm.go b/clients/operationperm.go index 85e69c1ffc..c03a77b8b5 100644 --- a/clients/operationperm.go +++ b/clients/operationperm.go @@ -113,13 +113,6 @@ func (p Permission) String() string { 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] -// } - func (op ExternalOperation) String() string { switch op { case DomainOpCreateClient: From 764272b39b1e4ec375fb22bc0aebfd515f6dcbe3 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Fri, 30 May 2025 19:05:45 +0300 Subject: [PATCH 05/12] initial implementation Signed-off-by: nyagamunene --- channels/middleware/authorization.go | 46 ++++--- channels/operationperm.go | 186 +++++++++++++++++++++++++++ channels/roleoperations.go | 131 ++++++++++--------- 3 files changed, 284 insertions(+), 79 deletions(-) create mode 100644 channels/operationperm.go diff --git a/channels/middleware/authorization.go b/channels/middleware/authorization.go index 8de28bb9b7..6235673a4f 100644 --- a/channels/middleware/authorization.go +++ b/channels/middleware/authorization.go @@ -48,8 +48,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 +59,8 @@ 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, rolesOpPerm map[channels.Operation]channels.Permission, + extOpPerm map[channels.ExternalOperation]channels.Permission, callout callout.Callout, ) (channels.Service, error) { opp := channels.NewOperationPerm() @@ -78,7 +78,13 @@ func AuthorizationMiddleware( if err := extOpp.Validate(); err != nil { return nil, err } - ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(policies.ChannelType, svc, authz, rolesOpPerm, callout) + + res := make(map[svcutil.Operation]svcutil.Permission, len(rolesOpPerm)) + for op, perm := range rolesOpPerm { + res[svcutil.Operation(op)] = svcutil.Permission(perm) + } + + ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(policies.ChannelType, svc, authz, res, callout) if err != nil { return nil, err } @@ -134,7 +140,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 +173,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 +199,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 +225,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 +257,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 +289,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 +321,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 +353,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 +384,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 +446,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 +508,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 +551,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 +599,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 +607,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 +622,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..15e5dc28ce 100644 --- a/channels/roleoperations.go +++ b/channels/roleoperations.go @@ -3,15 +3,10 @@ package channels -import ( - "github.com/absmach/supermq/pkg/roles" - "github.com/absmach/supermq/pkg/svcutil" -) - // Internal Operations const ( - OpViewChannel svcutil.Operation = iota + OpViewChannel Operation = iota OpUpdateChannel OpUpdateChannelTags OpEnableChannel @@ -26,7 +21,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 +50,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 +64,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 +82,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 +101,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,23 +117,41 @@ func NewOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { return opPerm } -func NewRolesOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { - opPerm := map[svcutil.Operation]svcutil.Permission{ - roles.OpAddRole: manageRolePermission, - roles.OpRemoveRole: manageRolePermission, - roles.OpUpdateRoleName: manageRolePermission, - roles.OpRetrieveRole: manageRolePermission, - roles.OpRetrieveAllRoles: manageRolePermission, - roles.OpRoleAddActions: manageRolePermission, - roles.OpRoleListActions: manageRolePermission, - roles.OpRoleCheckActionsExists: manageRolePermission, - roles.OpRoleRemoveActions: manageRolePermission, - roles.OpRoleRemoveAllActions: manageRolePermission, - roles.OpRoleAddMembers: addRoleUsersPermission, - roles.OpRoleListMembers: viewRoleUsersPermission, - roles.OpRoleCheckMembersExists: viewRoleUsersPermission, - roles.OpRoleRemoveMembers: removeRoleUsersPermission, - roles.OpRoleRemoveAllMembers: manageRolePermission, +const ( + OpAddRole Operation = iota + OpRemoveRole + OpUpdateRoleName + OpRetrieveRole + OpRetrieveAllRoles + OpRoleAddActions + OpRoleListActions + OpRoleCheckActionsExists + OpRoleRemoveActions + OpRoleRemoveAllActions + OpRoleAddMembers + OpRoleListMembers + OpRoleCheckMembersExists + OpRoleRemoveMembers + OpRoleRemoveAllMembers +) + +func NewRolesOperationPermissionMap() map[Operation]Permission { + opPerm := map[Operation]Permission{ + OpAddRole: manageRolePermission, + OpRemoveRole: manageRolePermission, + OpUpdateRoleName: manageRolePermission, + OpRetrieveRole: manageRolePermission, + OpRetrieveAllRoles: manageRolePermission, + OpRoleAddActions: manageRolePermission, + OpRoleListActions: manageRolePermission, + OpRoleCheckActionsExists: manageRolePermission, + OpRoleRemoveActions: manageRolePermission, + OpRoleRemoveAllActions: manageRolePermission, + OpRoleAddMembers: addRoleUsersPermission, + OpRoleListMembers: viewRoleUsersPermission, + OpRoleCheckMembersExists: viewRoleUsersPermission, + OpRoleRemoveMembers: removeRoleUsersPermission, + OpRoleRemoveAllMembers: manageRolePermission, } return opPerm } @@ -156,8 +169,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, From 5ff5e60784fa652427490cc51a566f323f84bdae Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Tue, 3 Jun 2025 00:57:52 +0300 Subject: [PATCH 06/12] fix comments formatting Signed-off-by: nyagamunene --- channels/roleoperations.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/channels/roleoperations.go b/channels/roleoperations.go index 15e5dc28ce..61e167d6ba 100644 --- a/channels/roleoperations.go +++ b/channels/roleoperations.go @@ -3,8 +3,7 @@ package channels -// Internal Operations - +// Internal Operations. const ( OpViewChannel Operation = iota OpUpdateChannel From 73d66b75c0a62c0ec81abbd77dfd9d47cda259b0 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Tue, 3 Jun 2025 00:22:02 +0300 Subject: [PATCH 07/12] initial implementation Signed-off-by: nyagamunene --- groups/middleware/authorization.go | 51 ++++---- groups/operationperm.go | 190 +++++++++++++++++++++++++++++ groups/roleoperations.go | 134 +++++++++++--------- 3 files changed, 291 insertions(+), 84 deletions(-) create mode 100644 groups/operationperm.go diff --git a/groups/middleware/authorization.go b/groups/middleware/authorization.go index 7ed6ce8a0c..1addc2026e 100644 --- a/groups/middleware/authorization.go +++ b/groups/middleware/authorization.go @@ -48,8 +48,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 +59,8 @@ 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, rolesOpPerm map[groups.Operation]groups.Permission, + extOpPerm map[groups.ExternalOperation]groups.Permission, callout callout.Callout, ) (groups.Service, error) { opp := groups.NewOperationPerm() @@ -79,7 +79,12 @@ func AuthorizationMiddleware(entityType string, return nil, err } - ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(entityType, svc, authz, rolesOpPerm, callout) + res := make(map[svcutil.Operation]svcutil.Permission, len(rolesOpPerm)) + for op, perm := range rolesOpPerm { + res[svcutil.Operation(op)] = svcutil.Permission(perm) + } + + ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(entityType, svc, authz, res, callout) if err != nil { return nil, err } @@ -136,7 +141,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 +175,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 +208,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 +241,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 +281,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 +308,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 +340,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 +373,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 +405,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 +439,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 +482,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 +532,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 +577,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 +611,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 +644,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 +680,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 +700,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 +713,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..1a207a89a8 100644 --- a/groups/roleoperations.go +++ b/groups/roleoperations.go @@ -3,15 +3,9 @@ package groups -import ( - "github.com/absmach/supermq/pkg/roles" - "github.com/absmach/supermq/pkg/svcutil" -) - -// Internal Operations - +// Internal Operations. const ( - OpViewGroup svcutil.Operation = iota + OpViewGroup Operation = iota OpUpdateGroup OpUpdateGroupTags OpEnableGroup @@ -29,7 +23,7 @@ const ( OpListUserGroups ) -var expectedOperations = []svcutil.Operation{ +var expectedOperations = []Operation{ OpViewGroup, OpUpdateGroup, OpUpdateGroupTags, @@ -45,39 +39,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 +79,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 +106,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,23 +125,41 @@ func NewOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { return opPerm } -func NewRolesOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { - opPerm := map[svcutil.Operation]svcutil.Permission{ - roles.OpAddRole: manageRolePermission, - roles.OpRemoveRole: manageRolePermission, - roles.OpUpdateRoleName: manageRolePermission, - roles.OpRetrieveRole: manageRolePermission, - roles.OpRetrieveAllRoles: manageRolePermission, - roles.OpRoleAddActions: manageRolePermission, - roles.OpRoleListActions: manageRolePermission, - roles.OpRoleCheckActionsExists: manageRolePermission, - roles.OpRoleRemoveActions: manageRolePermission, - roles.OpRoleRemoveAllActions: manageRolePermission, - roles.OpRoleAddMembers: addRoleUsersPermission, - roles.OpRoleListMembers: viewRoleUsersPermission, - roles.OpRoleCheckMembersExists: viewRoleUsersPermission, - roles.OpRoleRemoveMembers: removeRoleUsersPermission, - roles.OpRoleRemoveAllMembers: manageRolePermission, +const ( + OpAddRole Operation = iota + OpRemoveRole + OpUpdateRoleName + OpRetrieveRole + OpRetrieveAllRoles + OpRoleAddActions + OpRoleListActions + OpRoleCheckActionsExists + OpRoleRemoveActions + OpRoleRemoveAllActions + OpRoleAddMembers + OpRoleListMembers + OpRoleCheckMembersExists + OpRoleRemoveMembers + OpRoleRemoveAllMembers +) + +func NewRolesOperationPermissionMap() map[Operation]Permission { + opPerm := map[Operation]Permission{ + OpAddRole: manageRolePermission, + OpRemoveRole: manageRolePermission, + OpUpdateRoleName: manageRolePermission, + OpRetrieveRole: manageRolePermission, + OpRetrieveAllRoles: manageRolePermission, + OpRoleAddActions: manageRolePermission, + OpRoleListActions: manageRolePermission, + OpRoleCheckActionsExists: manageRolePermission, + OpRoleRemoveActions: manageRolePermission, + OpRoleRemoveAllActions: manageRolePermission, + OpRoleAddMembers: addRoleUsersPermission, + OpRoleListMembers: viewRoleUsersPermission, + OpRoleCheckMembersExists: viewRoleUsersPermission, + OpRoleRemoveMembers: removeRoleUsersPermission, + OpRoleRemoveAllMembers: manageRolePermission, } return opPerm } @@ -161,8 +173,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, From 03c5c6a74eb9aa1a1982731d181824583e7ad8dc Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Tue, 3 Jun 2025 00:56:35 +0300 Subject: [PATCH 08/12] initial implementation Signed-off-by: nyagamunene --- domains/middleware/authorization.go | 39 +++++----- domains/operationperm.go | 110 ++++++++++++++++++++++++++++ domains/roleoperations.go | 99 ++++++++++++++----------- 3 files changed, 188 insertions(+), 60 deletions(-) create mode 100644 domains/operationperm.go diff --git a/domains/middleware/authorization.go b/domains/middleware/authorization.go index 38efae1220..0723b31d04 100644 --- a/domains/middleware/authorization.go +++ b/domains/middleware/authorization.go @@ -30,13 +30,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, rolesOpPerm map[domains.Operation]domains.Permission, callout callout.Callout) (domains.Service, error) { opp := domains.NewOperationPerm() if err := opp.AddOperationPermissionMap(domainsOpPerm); err != nil { return nil, err @@ -45,7 +45,12 @@ func AuthorizationMiddleware(entityType string, svc domains.Service, authz smqau return nil, err } - ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(entityType, svc, authz, rolesOpPerm, callout) + res := make(map[svcutil.Operation]svcutil.Permission, len(rolesOpPerm)) + for op, perm := range rolesOpPerm { + res[svcutil.Operation(op)] = svcutil.Permission(perm) + } + + ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(entityType, svc, authz, res, callout) if err != nil { return nil, err } @@ -62,7 +67,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 +92,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 +112,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 +131,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 +150,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 +171,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 +184,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 +204,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 +223,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 +251,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 +262,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 +272,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 +288,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/domains/operationperm.go b/domains/operationperm.go new file mode 100644 index 0000000000..812f936830 --- /dev/null +++ b/domains/operationperm.go @@ -0,0 +1,110 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package domains + +import "fmt" + +type Operation int + +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) + } +} + +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) +} diff --git a/domains/roleoperations.go b/domains/roleoperations.go index 518532d936..353b8643b9 100644 --- a/domains/roleoperations.go +++ b/domains/roleoperations.go @@ -3,13 +3,8 @@ package domains -import ( - "github.com/absmach/supermq/pkg/roles" - "github.com/absmach/supermq/pkg/svcutil" -) - const ( - OpUpdateDomain svcutil.Operation = iota + OpUpdateDomain Operation = iota OpRetrieveDomain OpEnableDomain OpDisableDomain @@ -24,31 +19,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 +66,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,23 +76,41 @@ func NewOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { return opPerm } -func NewRolesOperationPermissionMap() map[svcutil.Operation]svcutil.Permission { - opPerm := map[svcutil.Operation]svcutil.Permission{ - roles.OpAddRole: manageRolePermission, - roles.OpRemoveRole: manageRolePermission, - roles.OpUpdateRoleName: manageRolePermission, - roles.OpRetrieveRole: manageRolePermission, - roles.OpRetrieveAllRoles: manageRolePermission, - roles.OpRoleAddActions: manageRolePermission, - roles.OpRoleListActions: manageRolePermission, - roles.OpRoleCheckActionsExists: manageRolePermission, - roles.OpRoleRemoveActions: manageRolePermission, - roles.OpRoleRemoveAllActions: manageRolePermission, - roles.OpRoleAddMembers: addRoleUsersPermission, - roles.OpRoleListMembers: viewRoleUsersPermission, - roles.OpRoleCheckMembersExists: viewRoleUsersPermission, - roles.OpRoleRemoveMembers: removeRoleUsersPermission, - roles.OpRoleRemoveAllMembers: manageRolePermission, +const ( + OpAddRole Operation = iota + OpRemoveRole + OpUpdateRoleName + OpRetrieveRole + OpRetrieveAllRoles + OpRoleAddActions + OpRoleListActions + OpRoleCheckActionsExists + OpRoleRemoveActions + OpRoleRemoveAllActions + OpRoleAddMembers + OpRoleListMembers + OpRoleCheckMembersExists + OpRoleRemoveMembers + OpRoleRemoveAllMembers +) + +func NewRolesOperationPermissionMap() map[Operation]Permission { + opPerm := map[Operation]Permission{ + OpAddRole: manageRolePermission, + OpRemoveRole: manageRolePermission, + OpUpdateRoleName: manageRolePermission, + OpRetrieveRole: manageRolePermission, + OpRetrieveAllRoles: manageRolePermission, + OpRoleAddActions: manageRolePermission, + OpRoleListActions: manageRolePermission, + OpRoleCheckActionsExists: manageRolePermission, + OpRoleRemoveActions: manageRolePermission, + OpRoleRemoveAllActions: manageRolePermission, + OpRoleAddMembers: addRoleUsersPermission, + OpRoleListMembers: viewRoleUsersPermission, + OpRoleCheckMembersExists: viewRoleUsersPermission, + OpRoleRemoveMembers: removeRoleUsersPermission, + OpRoleRemoveAllMembers: manageRolePermission, } return opPerm } From 309f06f39ebc7fd72519332bb5b2719466dfaefd Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Wed, 4 Jun 2025 14:41:59 +0300 Subject: [PATCH 09/12] initial implementation Signed-off-by: nyagamunene --- channels/middleware/authorization.go | 11 +- channels/roleoperations.go | 54 ++---- clients/middleware/authorization.go | 11 +- clients/roleoperations.go | 54 ++---- domains/middleware/authorization.go | 10 +- domains/roleoperations.go | 54 ++---- groups/middleware/authorization.go | 11 +- groups/roleoperations.go | 54 ++---- pkg/roles/operationperm.go | 164 ++++++++++++++++++ .../rolemanager/middleware/authorization.go | 59 ++++++- pkg/roles/roles.go | 61 ------- pkg/svcutil/externaloperationperm.go | 80 --------- pkg/svcutil/operationperm.go | 86 --------- 13 files changed, 301 insertions(+), 408 deletions(-) create mode 100644 pkg/roles/operationperm.go delete mode 100644 pkg/svcutil/externaloperationperm.go delete mode 100644 pkg/svcutil/operationperm.go diff --git a/channels/middleware/authorization.go b/channels/middleware/authorization.go index 6235673a4f..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 ( @@ -59,7 +58,8 @@ func AuthorizationMiddleware( svc channels.Service, repo channels.Repository, authz smqauthz.Authorization, - channelsOpPerm, rolesOpPerm map[channels.Operation]channels.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) { @@ -79,12 +79,7 @@ func AuthorizationMiddleware( return nil, err } - res := make(map[svcutil.Operation]svcutil.Permission, len(rolesOpPerm)) - for op, perm := range rolesOpPerm { - res[svcutil.Operation(op)] = svcutil.Permission(perm) - } - - ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(policies.ChannelType, svc, authz, res, callout) + ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(policies.ChannelType, svc, authz, rolesOpPerm, callout) if err != nil { return nil, err } diff --git a/channels/roleoperations.go b/channels/roleoperations.go index 61e167d6ba..a50bed9bf2 100644 --- a/channels/roleoperations.go +++ b/channels/roleoperations.go @@ -3,6 +3,8 @@ package channels +import "github.com/absmach/supermq/pkg/roles" + // Internal Operations. const ( OpViewChannel Operation = iota @@ -116,41 +118,23 @@ func NewOperationPermissionMap() map[Operation]Permission { return opPerm } -const ( - OpAddRole Operation = iota - OpRemoveRole - OpUpdateRoleName - OpRetrieveRole - OpRetrieveAllRoles - OpRoleAddActions - OpRoleListActions - OpRoleCheckActionsExists - OpRoleRemoveActions - OpRoleRemoveAllActions - OpRoleAddMembers - OpRoleListMembers - OpRoleCheckMembersExists - OpRoleRemoveMembers - OpRoleRemoveAllMembers -) - -func NewRolesOperationPermissionMap() map[Operation]Permission { - opPerm := map[Operation]Permission{ - OpAddRole: manageRolePermission, - OpRemoveRole: manageRolePermission, - OpUpdateRoleName: manageRolePermission, - OpRetrieveRole: manageRolePermission, - OpRetrieveAllRoles: manageRolePermission, - OpRoleAddActions: manageRolePermission, - OpRoleListActions: manageRolePermission, - OpRoleCheckActionsExists: manageRolePermission, - OpRoleRemoveActions: manageRolePermission, - OpRoleRemoveAllActions: manageRolePermission, - OpRoleAddMembers: addRoleUsersPermission, - OpRoleListMembers: viewRoleUsersPermission, - OpRoleCheckMembersExists: viewRoleUsersPermission, - OpRoleRemoveMembers: removeRoleUsersPermission, - OpRoleRemoveAllMembers: manageRolePermission, +func NewRolesOperationPermissionMap() map[roles.Operation]roles.Permission { + opPerm := map[roles.Operation]roles.Permission{ + roles.OpAddRole: manageRolePermission, + roles.OpRemoveRole: manageRolePermission, + roles.OpUpdateRoleName: manageRolePermission, + roles.OpRetrieveRole: manageRolePermission, + roles.OpRetrieveAllRoles: manageRolePermission, + roles.OpRoleAddActions: manageRolePermission, + roles.OpRoleListActions: manageRolePermission, + roles.OpRoleCheckActionsExists: manageRolePermission, + roles.OpRoleRemoveActions: manageRolePermission, + roles.OpRoleRemoveAllActions: manageRolePermission, + roles.OpRoleAddMembers: addRoleUsersPermission, + roles.OpRoleListMembers: viewRoleUsersPermission, + roles.OpRoleCheckMembersExists: viewRoleUsersPermission, + roles.OpRoleRemoveMembers: removeRoleUsersPermission, + roles.OpRoleRemoveAllMembers: manageRolePermission, } return opPerm } diff --git a/clients/middleware/authorization.go b/clients/middleware/authorization.go index 978ad9303f..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 ( @@ -54,7 +53,8 @@ func AuthorizationMiddleware( svc clients.Service, authz smqauthz.Authorization, repo clients.Repository, - thingsOpPerm, rolesOpPerm map[clients.Operation]clients.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) { @@ -66,12 +66,7 @@ func AuthorizationMiddleware( return nil, err } - res := make(map[svcutil.Operation]svcutil.Permission, len(rolesOpPerm)) - for op, perm := range rolesOpPerm { - res[svcutil.Operation(op)] = svcutil.Permission(perm) - } - - ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(policies.ClientType, svc, authz, res, callout) + ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(policies.ClientType, svc, authz, rolesOpPerm, callout) if err != nil { return nil, err } diff --git a/clients/roleoperations.go b/clients/roleoperations.go index ed085c7a42..99cf066057 100644 --- a/clients/roleoperations.go +++ b/clients/roleoperations.go @@ -3,6 +3,8 @@ package clients +import "github.com/absmach/supermq/pkg/roles" + // Internal Operations. const ( OpViewClient Operation = iota @@ -120,41 +122,23 @@ func NewOperationPermissionMap() map[Operation]Permission { return opPerm } -const ( - OpAddRole Operation = iota - OpRemoveRole - OpUpdateRoleName - OpRetrieveRole - OpRetrieveAllRoles - OpRoleAddActions - OpRoleListActions - OpRoleCheckActionsExists - OpRoleRemoveActions - OpRoleRemoveAllActions - OpRoleAddMembers - OpRoleListMembers - OpRoleCheckMembersExists - OpRoleRemoveMembers - OpRoleRemoveAllMembers -) - -func NewRolesOperationPermissionMap() map[Operation]Permission { - opPerm := map[Operation]Permission{ - OpAddRole: manageRolePermission, - OpRemoveRole: manageRolePermission, - OpUpdateRoleName: manageRolePermission, - OpRetrieveRole: manageRolePermission, - OpRetrieveAllRoles: manageRolePermission, - OpRoleAddActions: manageRolePermission, - OpRoleListActions: manageRolePermission, - OpRoleCheckActionsExists: manageRolePermission, - OpRoleRemoveActions: manageRolePermission, - OpRoleRemoveAllActions: manageRolePermission, - OpRoleAddMembers: addRoleUsersPermission, - OpRoleListMembers: viewRoleUsersPermission, - OpRoleCheckMembersExists: viewRoleUsersPermission, - OpRoleRemoveMembers: removeRoleUsersPermission, - OpRoleRemoveAllMembers: manageRolePermission, +func NewRolesOperationPermissionMap() map[roles.Operation]roles.Permission { + opPerm := map[roles.Operation]roles.Permission{ + roles.OpAddRole: manageRolePermission, + roles.OpRemoveRole: manageRolePermission, + roles.OpUpdateRoleName: manageRolePermission, + roles.OpRetrieveRole: manageRolePermission, + roles.OpRetrieveAllRoles: manageRolePermission, + roles.OpRoleAddActions: manageRolePermission, + roles.OpRoleListActions: manageRolePermission, + roles.OpRoleCheckActionsExists: manageRolePermission, + roles.OpRoleRemoveActions: manageRolePermission, + roles.OpRoleRemoveAllActions: manageRolePermission, + roles.OpRoleAddMembers: addRoleUsersPermission, + roles.OpRoleListMembers: viewRoleUsersPermission, + roles.OpRoleCheckMembersExists: viewRoleUsersPermission, + roles.OpRoleRemoveMembers: removeRoleUsersPermission, + roles.OpRoleRemoveAllMembers: manageRolePermission, } return opPerm } diff --git a/domains/middleware/authorization.go b/domains/middleware/authorization.go index 0723b31d04..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) @@ -36,7 +35,7 @@ type authorizationMiddleware struct { } // AuthorizationMiddleware adds authorization to the clients service. -func AuthorizationMiddleware(entityType string, svc domains.Service, authz smqauthz.Authorization, domainsOpPerm, rolesOpPerm map[domains.Operation]domains.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 @@ -45,12 +44,7 @@ func AuthorizationMiddleware(entityType string, svc domains.Service, authz smqau return nil, err } - res := make(map[svcutil.Operation]svcutil.Permission, len(rolesOpPerm)) - for op, perm := range rolesOpPerm { - res[svcutil.Operation(op)] = svcutil.Permission(perm) - } - - ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(entityType, svc, authz, res, callout) + ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(entityType, svc, authz, rolesOpPerm, callout) if err != nil { return nil, err } diff --git a/domains/roleoperations.go b/domains/roleoperations.go index 353b8643b9..17ee4417db 100644 --- a/domains/roleoperations.go +++ b/domains/roleoperations.go @@ -3,6 +3,8 @@ package domains +import "github.com/absmach/supermq/pkg/roles" + const ( OpUpdateDomain Operation = iota OpRetrieveDomain @@ -76,41 +78,23 @@ func NewOperationPermissionMap() map[Operation]Permission { return opPerm } -const ( - OpAddRole Operation = iota - OpRemoveRole - OpUpdateRoleName - OpRetrieveRole - OpRetrieveAllRoles - OpRoleAddActions - OpRoleListActions - OpRoleCheckActionsExists - OpRoleRemoveActions - OpRoleRemoveAllActions - OpRoleAddMembers - OpRoleListMembers - OpRoleCheckMembersExists - OpRoleRemoveMembers - OpRoleRemoveAllMembers -) - -func NewRolesOperationPermissionMap() map[Operation]Permission { - opPerm := map[Operation]Permission{ - OpAddRole: manageRolePermission, - OpRemoveRole: manageRolePermission, - OpUpdateRoleName: manageRolePermission, - OpRetrieveRole: manageRolePermission, - OpRetrieveAllRoles: manageRolePermission, - OpRoleAddActions: manageRolePermission, - OpRoleListActions: manageRolePermission, - OpRoleCheckActionsExists: manageRolePermission, - OpRoleRemoveActions: manageRolePermission, - OpRoleRemoveAllActions: manageRolePermission, - OpRoleAddMembers: addRoleUsersPermission, - OpRoleListMembers: viewRoleUsersPermission, - OpRoleCheckMembersExists: viewRoleUsersPermission, - OpRoleRemoveMembers: removeRoleUsersPermission, - OpRoleRemoveAllMembers: manageRolePermission, +func NewRolesOperationPermissionMap() map[roles.Operation]roles.Permission { + opPerm := map[roles.Operation]roles.Permission{ + roles.OpAddRole: manageRolePermission, + roles.OpRemoveRole: manageRolePermission, + roles.OpUpdateRoleName: manageRolePermission, + roles.OpRetrieveRole: manageRolePermission, + roles.OpRetrieveAllRoles: manageRolePermission, + roles.OpRoleAddActions: manageRolePermission, + roles.OpRoleListActions: manageRolePermission, + roles.OpRoleCheckActionsExists: manageRolePermission, + roles.OpRoleRemoveActions: manageRolePermission, + roles.OpRoleRemoveAllActions: manageRolePermission, + roles.OpRoleAddMembers: addRoleUsersPermission, + roles.OpRoleListMembers: viewRoleUsersPermission, + roles.OpRoleCheckMembersExists: viewRoleUsersPermission, + roles.OpRoleRemoveMembers: removeRoleUsersPermission, + roles.OpRoleRemoveAllMembers: manageRolePermission, } return opPerm } diff --git a/groups/middleware/authorization.go b/groups/middleware/authorization.go index 1addc2026e..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 ( @@ -59,7 +58,8 @@ func AuthorizationMiddleware(entityType string, svc groups.Service, repo groups.Repository, authz smqauthz.Authorization, - groupsOpPerm, rolesOpPerm map[groups.Operation]groups.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) { @@ -79,12 +79,7 @@ func AuthorizationMiddleware(entityType string, return nil, err } - res := make(map[svcutil.Operation]svcutil.Permission, len(rolesOpPerm)) - for op, perm := range rolesOpPerm { - res[svcutil.Operation(op)] = svcutil.Permission(perm) - } - - ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(entityType, svc, authz, res, callout) + ram, err := rmMW.NewRoleManagerAuthorizationMiddleware(entityType, svc, authz, rolesOpPerm, callout) if err != nil { return nil, err } diff --git a/groups/roleoperations.go b/groups/roleoperations.go index 1a207a89a8..4ce74cc24c 100644 --- a/groups/roleoperations.go +++ b/groups/roleoperations.go @@ -3,6 +3,8 @@ package groups +import "github.com/absmach/supermq/pkg/roles" + // Internal Operations. const ( OpViewGroup Operation = iota @@ -125,41 +127,23 @@ func NewOperationPermissionMap() map[Operation]Permission { return opPerm } -const ( - OpAddRole Operation = iota - OpRemoveRole - OpUpdateRoleName - OpRetrieveRole - OpRetrieveAllRoles - OpRoleAddActions - OpRoleListActions - OpRoleCheckActionsExists - OpRoleRemoveActions - OpRoleRemoveAllActions - OpRoleAddMembers - OpRoleListMembers - OpRoleCheckMembersExists - OpRoleRemoveMembers - OpRoleRemoveAllMembers -) - -func NewRolesOperationPermissionMap() map[Operation]Permission { - opPerm := map[Operation]Permission{ - OpAddRole: manageRolePermission, - OpRemoveRole: manageRolePermission, - OpUpdateRoleName: manageRolePermission, - OpRetrieveRole: manageRolePermission, - OpRetrieveAllRoles: manageRolePermission, - OpRoleAddActions: manageRolePermission, - OpRoleListActions: manageRolePermission, - OpRoleCheckActionsExists: manageRolePermission, - OpRoleRemoveActions: manageRolePermission, - OpRoleRemoveAllActions: manageRolePermission, - OpRoleAddMembers: addRoleUsersPermission, - OpRoleListMembers: viewRoleUsersPermission, - OpRoleCheckMembersExists: viewRoleUsersPermission, - OpRoleRemoveMembers: removeRoleUsersPermission, - OpRoleRemoveAllMembers: manageRolePermission, +func NewRolesOperationPermissionMap() map[roles.Operation]roles.Permission { + opPerm := map[roles.Operation]roles.Permission{ + roles.OpAddRole: manageRolePermission, + roles.OpRemoveRole: manageRolePermission, + roles.OpUpdateRoleName: manageRolePermission, + roles.OpRetrieveRole: manageRolePermission, + roles.OpRetrieveAllRoles: manageRolePermission, + roles.OpRoleAddActions: manageRolePermission, + roles.OpRoleListActions: manageRolePermission, + roles.OpRoleCheckActionsExists: manageRolePermission, + roles.OpRoleRemoveActions: manageRolePermission, + roles.OpRoleRemoveAllActions: manageRolePermission, + roles.OpRoleAddMembers: addRoleUsersPermission, + roles.OpRoleListMembers: viewRoleUsersPermission, + roles.OpRoleCheckMembersExists: viewRoleUsersPermission, + roles.OpRoleRemoveMembers: removeRoleUsersPermission, + roles.OpRoleRemoveAllMembers: manageRolePermission, } return opPerm } diff --git a/pkg/roles/operationperm.go b/pkg/roles/operationperm.go new file mode 100644 index 0000000000..6c317e1633 --- /dev/null +++ b/pkg/roles/operationperm.go @@ -0,0 +1,164 @@ +// 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 +) + +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" +) + +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 + 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..4d70f0f083 100644 --- a/pkg/roles/rolemanager/middleware/authorization.go +++ b/pkg/roles/rolemanager/middleware/authorization.go @@ -5,9 +5,14 @@ package middleware import ( "context" + "fmt" "maps" "time" + "github.com/absmach/supermq/channels" + "github.com/absmach/supermq/clients" + "github.com/absmach/supermq/domains" + "github.com/absmach/supermq/groups" "github.com/absmach/supermq/pkg/authn" smqauthz "github.com/absmach/supermq/pkg/authz" "github.com/absmach/supermq/pkg/callout" @@ -24,11 +29,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 @@ -42,7 +46,6 @@ func NewRoleManagerAuthorizationMiddleware(entityType string, svc roles.RoleMana svc: svc, authz: authz, opp: opp, - callout: callout, } if err := ram.validate(); err != nil { return RoleManagerAuthorizationMiddleware{}, err @@ -50,10 +53,48 @@ func NewRoleManagerAuthorizationMiddleware(entityType string, svc roles.RoleMana return ram, nil } -func (ram RoleManagerAuthorizationMiddleware) validate() error { - if err := ram.opp.Validate(); err != nil { - return err +func addOperationPermissionMap(OperationPerm *any, rolesOpMap any) error { + switch op := (*OperationPerm).(type) { + case *channels.OperationPerm: + mp, ok := rolesOpMap.(map[channels.Operation]channels.Permission) + if !ok { + return fmt.Errorf("invalid type") + } + err := op.AddOperationPermissionMap(mp) + if err != nil { + return fmt.Errorf("failed adding channels operation permission map") + } + case *domains.OperationPerm: + mp, ok := rolesOpMap.(map[domains.Operation]domains.Permission) + if !ok { + return fmt.Errorf("invalid type") + } + err := op.AddOperationPermissionMap(mp) + if err != nil { + return fmt.Errorf("failed adding domains operation permission map") + } + case *clients.OperationPerm: + mp, ok := rolesOpMap.(map[clients.Operation]clients.Permission) + if !ok { + return fmt.Errorf("invalid type") + } + err := op.AddOperationPermissionMap(mp) + if err != nil { + return fmt.Errorf("failed adding clients operation permission map") + } + case *groups.OperationPerm: + mp, ok := rolesOpMap.(map[groups.Operation]groups.Permission) + if !ok { + return fmt.Errorf("invalid type") + } + err := op.AddOperationPermissionMap(mp) + if err != nil { + return fmt.Errorf("failed adding groups operation permission map") + } + default: + return fmt.Errorf("opp does not implement AddOperationPermissionMap method") } + return nil } @@ -106,7 +147,7 @@ func (ram RoleManagerAuthorizationMiddleware) RemoveRole(ctx context.Context, se } 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, @@ -447,7 +488,7 @@ func (ram RoleManagerAuthorizationMiddleware) RoleRemoveMembers(ctx context.Cont 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)) -} diff --git a/pkg/svcutil/operationperm.go b/pkg/svcutil/operationperm.go deleted file mode 100644 index 40e909c4ec..0000000000 --- a/pkg/svcutil/operationperm.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Abstract Machines -// SPDX-License-Identifier: Apache-2.0 - -package svcutil - -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) - } - return operations[op] -} - -type OperationPerm struct { - opPerm map[Operation]Permission - expectedOps []Operation - opNames []string -} - -func NewOperationPerm(expectedOps []Operation, opNames []string) 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)) - } - } - 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.opNames)) - } - 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(opp.opNames)) - } - } - for _, eeo := range opp.expectedOps { - if _, ok := opp.opPerm[eeo]; !ok { - return fmt.Errorf("OperationPerm: \"%s\" operation is missing", eeo.String(opp.opNames)) - } - } - 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(opp.opNames)) -} From 4d2646f8535666315aff8c00ce547c88a5cdffde Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Wed, 4 Jun 2025 17:51:38 +0300 Subject: [PATCH 10/12] update roles method Signed-off-by: nyagamunene --- pkg/roles/operationperm.go | 5 ++ .../rolemanager/middleware/authorization.go | 90 ++++--------------- 2 files changed, 23 insertions(+), 72 deletions(-) diff --git a/pkg/roles/operationperm.go b/pkg/roles/operationperm.go index 6c317e1633..b6b64b2aaa 100644 --- a/pkg/roles/operationperm.go +++ b/pkg/roles/operationperm.go @@ -23,6 +23,7 @@ const ( OpRoleCheckMembersExists OpRoleRemoveMembers OpRoleRemoveAllMembers + OpListAvailableActions ) var expectedOperations = []Operation{ @@ -41,6 +42,7 @@ var expectedOperations = []Operation{ OpRoleCheckMembersExists, OpRoleRemoveMembers, OpRoleRemoveAllMembers, + OpListAvailableActions, } const ( @@ -59,6 +61,7 @@ const ( OpRoleCheckMembersExistsStr = "OpRoleCheckMembersExists" OpRoleRemoveMembersStr = "OpRoleRemoveMembers" OpRoleRemoveAllMembersStr = "OpRoleRemoveAllMembers" + OpListAvailableActionsStr = "OpListAvailableActions" ) func (op Operation) String() string { @@ -93,6 +96,8 @@ func (op Operation) String() string { return OpRoleRemoveMembersStr case OpRoleRemoveAllMembers: return OpRoleRemoveAllMembersStr + case OpListAvailableActions: + return OpListAvailableActionsStr default: return fmt.Sprintf("unknown operation: %d", op) } diff --git a/pkg/roles/rolemanager/middleware/authorization.go b/pkg/roles/rolemanager/middleware/authorization.go index 4d70f0f083..3f60716df2 100644 --- a/pkg/roles/rolemanager/middleware/authorization.go +++ b/pkg/roles/rolemanager/middleware/authorization.go @@ -5,21 +5,15 @@ package middleware import ( "context" - "fmt" "maps" "time" - "github.com/absmach/supermq/channels" - "github.com/absmach/supermq/clients" - "github.com/absmach/supermq/domains" - "github.com/absmach/supermq/groups" "github.com/absmach/supermq/pkg/authn" smqauthz "github.com/absmach/supermq/pkg/authz" "github.com/absmach/supermq/pkg/callout" "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) @@ -47,57 +41,9 @@ func NewRoleManagerAuthorizationMiddleware(entityType string, svc roles.RoleMana authz: authz, opp: opp, } - if err := ram.validate(); err != nil { - return RoleManagerAuthorizationMiddleware{}, err - } return ram, nil } -func addOperationPermissionMap(OperationPerm *any, rolesOpMap any) error { - switch op := (*OperationPerm).(type) { - case *channels.OperationPerm: - mp, ok := rolesOpMap.(map[channels.Operation]channels.Permission) - if !ok { - return fmt.Errorf("invalid type") - } - err := op.AddOperationPermissionMap(mp) - if err != nil { - return fmt.Errorf("failed adding channels operation permission map") - } - case *domains.OperationPerm: - mp, ok := rolesOpMap.(map[domains.Operation]domains.Permission) - if !ok { - return fmt.Errorf("invalid type") - } - err := op.AddOperationPermissionMap(mp) - if err != nil { - return fmt.Errorf("failed adding domains operation permission map") - } - case *clients.OperationPerm: - mp, ok := rolesOpMap.(map[clients.Operation]clients.Permission) - if !ok { - return fmt.Errorf("invalid type") - } - err := op.AddOperationPermissionMap(mp) - if err != nil { - return fmt.Errorf("failed adding clients operation permission map") - } - case *groups.OperationPerm: - mp, ok := rolesOpMap.(map[groups.Operation]groups.Permission) - if !ok { - return fmt.Errorf("invalid type") - } - err := op.AddOperationPermissionMap(mp) - if err != nil { - return fmt.Errorf("failed adding groups operation permission map") - } - default: - return fmt.Errorf("opp does not implement AddOperationPermissionMap method") - } - - 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, @@ -119,7 +65,7 @@ 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) @@ -140,7 +86,7 @@ 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) @@ -162,7 +108,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) @@ -183,7 +129,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) @@ -205,7 +151,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) @@ -213,7 +159,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) @@ -236,7 +182,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 } @@ -259,7 +205,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 } @@ -282,7 +228,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) @@ -304,7 +250,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 } @@ -326,7 +272,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) @@ -352,7 +298,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) @@ -375,7 +321,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) @@ -397,7 +343,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) @@ -418,7 +364,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) @@ -439,7 +385,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) @@ -460,7 +406,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) @@ -482,7 +428,7 @@ 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) From 6452fc76dba74b8f63cbc72825cdfcd285b015e4 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Wed, 4 Jun 2025 18:52:28 +0300 Subject: [PATCH 11/12] fix initialization Signed-off-by: nyagamunene --- pkg/roles/operationperm.go | 1 - pkg/roles/rolemanager/middleware/authorization.go | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/roles/operationperm.go b/pkg/roles/operationperm.go index b6b64b2aaa..23f47ae1bd 100644 --- a/pkg/roles/operationperm.go +++ b/pkg/roles/operationperm.go @@ -42,7 +42,6 @@ var expectedOperations = []Operation{ OpRoleCheckMembersExists, OpRoleRemoveMembers, OpRoleRemoveAllMembers, - OpListAvailableActions, } const ( diff --git a/pkg/roles/rolemanager/middleware/authorization.go b/pkg/roles/rolemanager/middleware/authorization.go index 3f60716df2..f9f87aad03 100644 --- a/pkg/roles/rolemanager/middleware/authorization.go +++ b/pkg/roles/rolemanager/middleware/authorization.go @@ -40,6 +40,7 @@ func NewRoleManagerAuthorizationMiddleware(entityType string, svc roles.RoleMana svc: svc, authz: authz, opp: opp, + callout: callout, } return ram, nil } @@ -68,6 +69,7 @@ func (ram RoleManagerAuthorizationMiddleware) AddRole(ctx context.Context, sessi 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) } From d6fc2430bd5a1b2e44501f06ac3372abf946e073 Mon Sep 17 00:00:00 2001 From: nyagamunene Date: Thu, 5 Jun 2025 11:34:41 +0300 Subject: [PATCH 12/12] remove roles op in clients Signed-off-by: nyagamunene --- clients/operationperm.go | 1 - 1 file changed, 1 deletion(-) diff --git a/clients/operationperm.go b/clients/operationperm.go index c03a77b8b5..d127a05717 100644 --- a/clients/operationperm.go +++ b/clients/operationperm.go @@ -135,7 +135,6 @@ func (op ExternalOperation) String() string { type ExternalOperationPerm struct { opPerm map[ExternalOperation]Permission expectedOps []ExternalOperation - // opNames []string } func newExternalOperationPerm(expectedOps []ExternalOperation) ExternalOperationPerm {