Files
Steve Munene dc72811048
Continuous Delivery / lint-and-build (push) Has been cancelled
Continuous Delivery / Build and Push Docker Images (push) Has been cancelled
Deploy GitHub Pages / swagger-ui (push) Has been cancelled
CI Pipeline / Lint Proto (push) Has been cancelled
CI Pipeline / lint-and-build (push) Has been cancelled
CI Pipeline / Detect Changes (push) Has been cancelled
CI Pipeline / Test ${{ matrix.module }} (push) Has been cancelled
CI Pipeline / Upload Coverage (push) Has been cancelled
NOISSUE - Update superadmin check (#3394)
Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>
2026-04-16 18:08:14 +02:00

286 lines
9.8 KiB
Go

// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package middleware
import (
"context"
"github.com/absmach/magistrala/auth"
"github.com/absmach/magistrala/domains"
"github.com/absmach/magistrala/domains/operations"
"github.com/absmach/magistrala/pkg/authn"
"github.com/absmach/magistrala/pkg/authz"
smqauthz "github.com/absmach/magistrala/pkg/authz"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
"github.com/absmach/magistrala/pkg/permissions"
"github.com/absmach/magistrala/pkg/policies"
"github.com/absmach/magistrala/pkg/roles"
rolemgr "github.com/absmach/magistrala/pkg/roles/rolemanager/middleware"
)
var _ domains.Service = (*authorizationMiddleware)(nil)
// ErrMemberExist indicates that the user is already a member of the domain.
var ErrMemberExist = errors.New("user is already a member of the domain")
type authorizationMiddleware struct {
svc domains.Service
authz smqauthz.Authorization
entitiesOps permissions.EntitiesOperations[permissions.Operation]
rOps permissions.Operations[permissions.RoleOperation]
rolemgr.RoleManagerAuthorizationMiddleware
}
// NewAuthorization adds authorization to the domains service.
func NewAuthorization(entityType string, svc domains.Service, authz smqauthz.Authorization, entitiesOps permissions.EntitiesOperations[permissions.Operation], domainRoleOps permissions.Operations[permissions.RoleOperation]) (domains.Service, error) {
if err := entitiesOps.Validate(); err != nil {
return &authorizationMiddleware{}, err
}
ram, err := rolemgr.NewAuthorization(entityType, svc, authz, domainRoleOps)
if err != nil {
return &authorizationMiddleware{}, err
}
return &authorizationMiddleware{
svc: svc,
authz: authz,
entitiesOps: entitiesOps,
rOps: domainRoleOps,
RoleManagerAuthorizationMiddleware: ram,
}, nil
}
func (am *authorizationMiddleware) CreateDomain(ctx context.Context, session authn.Session, d domains.Domain) (domains.Domain, []roles.RoleProvision, error) {
return am.svc.CreateDomain(ctx, session, d)
}
func (am *authorizationMiddleware) RetrieveDomain(ctx context.Context, session authn.Session, id string, withRoles bool) (domains.Domain, error) {
switch err := am.checkSuperAdmin(ctx, session); {
case err == nil:
session.SuperAdmin = true
return am.svc.RetrieveDomain(ctx, session, id, withRoles)
case errors.Contains(err, svcerr.ErrSuperAdminAction):
default:
return domains.Domain{}, err
}
if err := am.authorize(ctx, session, policies.DomainType, operations.OpRetrieveDomain, authz.PolicyReq{
Subject: session.DomainUserID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Object: id,
ObjectType: policies.DomainType,
}); err != nil {
return domains.Domain{}, err
}
return am.svc.RetrieveDomain(ctx, session, id, withRoles)
}
func (am *authorizationMiddleware) UpdateDomain(ctx context.Context, session authn.Session, id string, d domains.DomainReq) (domains.Domain, error) {
if err := am.authorize(ctx, session, policies.DomainType, operations.OpUpdateDomain, authz.PolicyReq{
Subject: session.DomainUserID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Object: id,
ObjectType: policies.DomainType,
}); err != nil {
return domains.Domain{}, err
}
return am.svc.UpdateDomain(ctx, session, id, d)
}
func (am *authorizationMiddleware) EnableDomain(ctx context.Context, session authn.Session, id string) (domains.Domain, error) {
if err := am.authorize(ctx, session, policies.DomainType, operations.OpEnableDomain, authz.PolicyReq{
Subject: session.DomainUserID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Object: id,
ObjectType: policies.DomainType,
}); err != nil {
return domains.Domain{}, err
}
return am.svc.EnableDomain(ctx, session, id)
}
func (am *authorizationMiddleware) DisableDomain(ctx context.Context, session authn.Session, id string) (domains.Domain, error) {
if err := am.authorize(ctx, session, policies.DomainType, operations.OpDisableDomain, authz.PolicyReq{
Subject: session.DomainUserID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Object: id,
ObjectType: policies.DomainType,
}); err != nil {
return domains.Domain{}, err
}
return am.svc.DisableDomain(ctx, session, id)
}
func (am *authorizationMiddleware) FreezeDomain(ctx context.Context, session authn.Session, id string) (domains.Domain, error) {
// Only SuperAdmin can freeze the domain
if err := am.authz.Authorize(ctx, authz.PolicyReq{
Subject: session.UserID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Permission: policies.AdminPermission,
Object: policies.MagistralaObject,
ObjectType: policies.PlatformType,
}, nil); err != nil {
return domains.Domain{}, err
}
return am.svc.FreezeDomain(ctx, session, id)
}
func (am *authorizationMiddleware) ListDomains(ctx context.Context, session authn.Session, page domains.Page) (domains.DomainsPage, error) {
switch err := am.checkSuperAdmin(ctx, session); {
case err == nil:
session.SuperAdmin = true
case errors.Contains(err, svcerr.ErrSuperAdminAction):
default:
return domains.DomainsPage{}, err
}
return am.svc.ListDomains(ctx, session, page)
}
func (am *authorizationMiddleware) SendInvitation(ctx context.Context, session authn.Session, invitation domains.Invitation) (domains.Invitation, error) {
if err := am.authorize(ctx, session, policies.DomainType, operations.OpSendDomainInvitation, authz.PolicyReq{
Subject: session.DomainUserID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Object: session.DomainID,
ObjectType: policies.DomainType,
}); err != nil {
return domains.Invitation{}, err
}
if err := am.checkAdmin(ctx, session); err != nil {
return domains.Invitation{}, err
}
return am.svc.SendInvitation(ctx, session, invitation)
}
func (am *authorizationMiddleware) ListInvitations(ctx context.Context, session authn.Session, page domains.InvitationPageMeta) (invs domains.InvitationPage, err error) {
return am.svc.ListInvitations(ctx, session, page)
}
func (am *authorizationMiddleware) ListDomainInvitations(ctx context.Context, session authn.Session, page domains.InvitationPageMeta) (invs domains.InvitationPage, err error) {
if err := am.authorize(ctx, session, policies.DomainType, operations.OpListDomainInvitations, authz.PolicyReq{
Subject: session.DomainUserID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Object: session.DomainID,
ObjectType: policies.DomainType,
}); err != nil {
return domains.InvitationPage{}, err
}
return am.svc.ListDomainInvitations(ctx, session, page)
}
func (am *authorizationMiddleware) AcceptInvitation(ctx context.Context, session authn.Session, domainID string) (inv domains.Invitation, err error) {
return am.svc.AcceptInvitation(ctx, session, domainID)
}
func (am *authorizationMiddleware) RejectInvitation(ctx context.Context, session authn.Session, domainID string) (domains.Invitation, error) {
return am.svc.RejectInvitation(ctx, session, domainID)
}
func (am *authorizationMiddleware) DeleteInvitation(ctx context.Context, session authn.Session, inviteeUserID, domainID string) (err error) {
if err := am.authorize(ctx, session, policies.DomainType, operations.OpDeleteDomainInvitation, authz.PolicyReq{
Subject: session.DomainUserID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Object: session.DomainID,
ObjectType: policies.DomainType,
}); err != nil {
return err
}
return am.svc.DeleteInvitation(ctx, session, inviteeUserID, domainID)
}
func (am *authorizationMiddleware) authorize(ctx context.Context, session authn.Session, entityType string, op permissions.Operation, authReq authz.PolicyReq) error {
authReq.Domain = session.DomainID
perm, err := am.entitiesOps.GetPermission(entityType, op)
if err != nil {
return err
}
authReq.Permission = perm.String()
var pat *smqauthz.PATReq
if session.PatID != "" {
entityID := authReq.Object
opName := am.entitiesOps.OperationName(entityType, op)
pat = &smqauthz.PATReq{
UserID: session.UserID,
PatID: session.PatID,
EntityID: entityID,
EntityType: auth.DomainsType.String(),
Operation: opName,
Domain: session.DomainID,
}
}
if err := am.authz.Authorize(ctx, authReq, pat); err != nil {
return err
}
return nil
}
// checkAdmin checks if the given user is a domain or platform administrator.
func (am *authorizationMiddleware) checkAdmin(ctx context.Context, session authn.Session) error {
req := smqauthz.PolicyReq{
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Permission: policies.AdminPermission,
ObjectType: policies.DomainType,
Object: session.DomainID,
}
if err := am.authz.Authorize(ctx, req, nil); err == nil {
return nil
}
req = smqauthz.PolicyReq{
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.UserID,
Permission: policies.AdminPermission,
ObjectType: policies.PlatformType,
Object: policies.MagistralaObject,
}
if err := am.authz.Authorize(ctx, req, nil); err == nil {
return nil
}
return svcerr.ErrAuthorization
}
func (am *authorizationMiddleware) checkSuperAdmin(ctx context.Context, session authn.Session) error {
if session.Role != authn.SuperAdminRole {
return svcerr.ErrSuperAdminAction
}
if err := am.authz.Authorize(ctx, smqauthz.PolicyReq{
SubjectType: policies.UserType,
Subject: session.UserID,
Permission: policies.AdminPermission,
ObjectType: policies.PlatformType,
Object: policies.MagistralaObject,
}, nil); err != nil {
return err
}
return nil
}