mirror of
https://github.com/absmach/magistrala.git
synced 2026-06-23 04:10:28 +00:00
7f03134d8e
Property Based Tests / api-test (push) Has been cancelled
Continuous Delivery / lint-and-build (push) Has been cancelled
Deploy GitHub Pages / swagger-ui (push) Has been cancelled
CI Pipeline / Lint Proto (push) Has been cancelled
CI Pipeline / Detect Changes (push) Has been cancelled
Continuous Delivery / Build and Push Docker Images (push) Has been cancelled
CI Pipeline / lint-and-build (push) Has been cancelled
CI Pipeline / Test ${{ matrix.module }} (push) Has been cancelled
CI Pipeline / Upload Coverage (push) Has been cancelled
Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> Signed-off-by: JeffMboya <jangina.mboya@gmail.com> Co-authored-by: JeffMboya <jangina.mboya@gmail.com>
384 lines
14 KiB
Go
384 lines
14 KiB
Go
// Copyright (c) Abstract Machines
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package middleware
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/absmach/magistrala/auth"
|
|
"github.com/absmach/magistrala/channels"
|
|
"github.com/absmach/magistrala/channels/operations"
|
|
cOperations "github.com/absmach/magistrala/clients/operations"
|
|
dOperations "github.com/absmach/magistrala/domains/operations"
|
|
gOperations "github.com/absmach/magistrala/groups/operations"
|
|
"github.com/absmach/magistrala/pkg/authn"
|
|
smqauthz "github.com/absmach/magistrala/pkg/authz"
|
|
"github.com/absmach/magistrala/pkg/connections"
|
|
"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 (
|
|
errView = errors.New("not authorized to view channel")
|
|
errList = errors.New("not authorized to list user channels")
|
|
errUpdate = errors.New("not authorized to update channel")
|
|
errUpdateTags = errors.New("not authorized to update channel tags")
|
|
errEnable = errors.New("not authorized to enable channel")
|
|
errDisable = errors.New("not authorized to disable channel")
|
|
errDelete = errors.New("not authorized to delete channel")
|
|
errConnect = errors.New("not authorized to connect to channel")
|
|
errDisconnect = errors.New("not authorized to disconnect from channel")
|
|
errSetParentGroup = errors.New("not authorized to set parent group to channel")
|
|
errRemoveParentGroup = errors.New("not authorized to remove parent group from channel")
|
|
errDomainCreateChannels = errors.New("not authorized to create channel in domain")
|
|
errGroupSetChildChannels = errors.New("not authorized to set child channel for group")
|
|
errGroupRemoveChildChannels = errors.New("not authorized to remove child channel for group")
|
|
errClientDisConnectChannels = errors.New("not authorized to disconnect channel for client")
|
|
errClientConnectChannels = errors.New("not authorized to connect channel for client")
|
|
)
|
|
|
|
var _ channels.Service = (*authorizationMiddleware)(nil)
|
|
|
|
type authorizationMiddleware struct {
|
|
svc channels.Service
|
|
repo channels.Repository
|
|
authz smqauthz.Authorization
|
|
entitiesOps permissions.EntitiesOperations[permissions.Operation]
|
|
rolemgr.RoleManagerAuthorizationMiddleware
|
|
}
|
|
|
|
// NewAuthorization adds authorization to the channels service.
|
|
func NewAuthorization(
|
|
entityType string,
|
|
svc channels.Service,
|
|
authz smqauthz.Authorization,
|
|
repo channels.Repository,
|
|
entitiesOps permissions.EntitiesOperations[permissions.Operation],
|
|
roleOps permissions.Operations[permissions.RoleOperation],
|
|
) (channels.Service, error) {
|
|
if err := entitiesOps.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
ram, err := rolemgr.NewAuthorization(policies.ChannelType, svc, authz, roleOps)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &authorizationMiddleware{
|
|
svc: svc,
|
|
authz: authz,
|
|
repo: repo,
|
|
entitiesOps: entitiesOps,
|
|
RoleManagerAuthorizationMiddleware: ram,
|
|
}, nil
|
|
}
|
|
|
|
func (am *authorizationMiddleware) CreateChannels(ctx context.Context, session authn.Session, chs ...channels.Channel) ([]channels.Channel, []roles.RoleProvision, error) {
|
|
if err := am.authorize(ctx, session, policies.DomainType, dOperations.OpCreateDomainChannels, smqauthz.PolicyReq{
|
|
Domain: session.DomainID,
|
|
SubjectType: policies.UserType,
|
|
Subject: session.DomainUserID,
|
|
ObjectType: policies.DomainType,
|
|
Object: session.DomainID,
|
|
}); err != nil {
|
|
return []channels.Channel{}, []roles.RoleProvision{}, errors.Wrap(err, errDomainCreateChannels)
|
|
}
|
|
|
|
for _, ch := range chs {
|
|
if ch.ParentGroup != "" {
|
|
if err := am.authorize(ctx, session, policies.GroupType, gOperations.OpGroupSetChildChannel, smqauthz.PolicyReq{
|
|
Domain: session.DomainID,
|
|
SubjectType: policies.UserType,
|
|
Subject: session.DomainUserID,
|
|
ObjectType: policies.GroupType,
|
|
Object: ch.ParentGroup,
|
|
}); err != nil {
|
|
return []channels.Channel{}, []roles.RoleProvision{}, errors.Wrap(err, errors.Wrap(errGroupSetChildChannels, fmt.Errorf("channel name %s parent group id %s", ch.Name, ch.ParentGroup)))
|
|
}
|
|
}
|
|
}
|
|
|
|
return am.svc.CreateChannels(ctx, session, chs...)
|
|
}
|
|
|
|
func (am *authorizationMiddleware) ViewChannel(ctx context.Context, session authn.Session, id string, withRoles bool) (channels.Channel, error) {
|
|
if err := am.authorize(ctx, session, policies.ChannelType, operations.OpViewChannel, smqauthz.PolicyReq{
|
|
Domain: session.DomainID,
|
|
SubjectType: policies.UserType,
|
|
Subject: session.DomainUserID,
|
|
ObjectType: policies.ChannelType,
|
|
Object: id,
|
|
}); err != nil {
|
|
return channels.Channel{}, errors.Wrap(err, errView)
|
|
}
|
|
|
|
return am.svc.ViewChannel(ctx, session, id, withRoles)
|
|
}
|
|
|
|
func (am *authorizationMiddleware) ListChannels(ctx context.Context, session authn.Session, pm channels.Page) (channels.ChannelsPage, error) {
|
|
switch err := am.checkSuperAdmin(ctx, session); {
|
|
case err == nil:
|
|
session.SuperAdmin = true
|
|
case errors.Contains(err, svcerr.ErrSuperAdminAction):
|
|
default:
|
|
return channels.ChannelsPage{}, err
|
|
}
|
|
|
|
return am.svc.ListChannels(ctx, session, pm)
|
|
}
|
|
|
|
func (am *authorizationMiddleware) ListUserChannels(ctx context.Context, session authn.Session, userID string, pm channels.Page) (channels.ChannelsPage, error) {
|
|
if err := am.checkSuperAdmin(ctx, session); err != nil {
|
|
return channels.ChannelsPage{}, errors.Wrap(err, errList)
|
|
}
|
|
|
|
return am.svc.ListUserChannels(ctx, session, userID, pm)
|
|
}
|
|
|
|
func (am *authorizationMiddleware) UpdateChannel(ctx context.Context, session authn.Session, channel channels.Channel) (channels.Channel, error) {
|
|
if err := am.authorize(ctx, session, policies.ChannelType, operations.OpUpdateChannel, smqauthz.PolicyReq{
|
|
Domain: session.DomainID,
|
|
SubjectType: policies.UserType,
|
|
Subject: session.DomainUserID,
|
|
ObjectType: policies.ChannelType,
|
|
Object: channel.ID,
|
|
}); err != nil {
|
|
return channels.Channel{}, errors.Wrap(err, errUpdate)
|
|
}
|
|
|
|
return am.svc.UpdateChannel(ctx, session, channel)
|
|
}
|
|
|
|
func (am *authorizationMiddleware) UpdateChannelTags(ctx context.Context, session authn.Session, channel channels.Channel) (channels.Channel, error) {
|
|
if err := am.authorize(ctx, session, policies.ChannelType, operations.OpUpdateChannelTags, smqauthz.PolicyReq{
|
|
Domain: session.DomainID,
|
|
SubjectType: policies.UserType,
|
|
Subject: session.DomainUserID,
|
|
ObjectType: policies.ChannelType,
|
|
Object: channel.ID,
|
|
}); err != nil {
|
|
return channels.Channel{}, errors.Wrap(err, errUpdateTags)
|
|
}
|
|
|
|
return am.svc.UpdateChannelTags(ctx, session, channel)
|
|
}
|
|
|
|
func (am *authorizationMiddleware) EnableChannel(ctx context.Context, session authn.Session, id string) (channels.Channel, error) {
|
|
if err := am.authorize(ctx, session, policies.ChannelType, operations.OpEnableChannel, smqauthz.PolicyReq{
|
|
Domain: session.DomainID,
|
|
SubjectType: policies.UserType,
|
|
Subject: session.DomainUserID,
|
|
ObjectType: policies.ChannelType,
|
|
Object: id,
|
|
}); err != nil {
|
|
return channels.Channel{}, errors.Wrap(err, errEnable)
|
|
}
|
|
|
|
return am.svc.EnableChannel(ctx, session, id)
|
|
}
|
|
|
|
func (am *authorizationMiddleware) DisableChannel(ctx context.Context, session authn.Session, id string) (channels.Channel, error) {
|
|
if err := am.authorize(ctx, session, policies.ChannelType, operations.OpDisableChannel, smqauthz.PolicyReq{
|
|
Domain: session.DomainID,
|
|
SubjectType: policies.UserType,
|
|
Subject: session.DomainUserID,
|
|
ObjectType: policies.ChannelType,
|
|
Object: id,
|
|
}); err != nil {
|
|
return channels.Channel{}, errors.Wrap(err, errDisable)
|
|
}
|
|
|
|
return am.svc.DisableChannel(ctx, session, id)
|
|
}
|
|
|
|
func (am *authorizationMiddleware) RemoveChannel(ctx context.Context, session authn.Session, id string) error {
|
|
if err := am.authorize(ctx, session, policies.ChannelType, operations.OpDeleteChannel, smqauthz.PolicyReq{
|
|
Domain: session.DomainID,
|
|
SubjectType: policies.UserType,
|
|
Subject: session.DomainUserID,
|
|
ObjectType: policies.ChannelType,
|
|
Object: id,
|
|
}); err != nil {
|
|
return errors.Wrap(err, errDelete)
|
|
}
|
|
|
|
return am.svc.RemoveChannel(ctx, session, id)
|
|
}
|
|
|
|
func (am *authorizationMiddleware) Connect(ctx context.Context, session authn.Session, chIDs, thIDs []string, connTypes []connections.ConnType) error {
|
|
for _, chID := range chIDs {
|
|
if err := am.authorize(ctx, session, policies.ChannelType, operations.OpConnectClient, smqauthz.PolicyReq{
|
|
Domain: session.DomainID,
|
|
SubjectType: policies.UserType,
|
|
Subject: session.DomainUserID,
|
|
ObjectType: policies.ChannelType,
|
|
Object: chID,
|
|
}); err != nil {
|
|
return errors.Wrap(err, errConnect)
|
|
}
|
|
}
|
|
|
|
for _, thID := range thIDs {
|
|
if err := am.authorize(ctx, session, policies.ClientType, cOperations.OpConnectToChannel, smqauthz.PolicyReq{
|
|
Domain: session.DomainID,
|
|
SubjectType: policies.UserType,
|
|
Subject: session.DomainUserID,
|
|
ObjectType: policies.ClientType,
|
|
Object: thID,
|
|
}); err != nil {
|
|
return errors.Wrap(err, errClientConnectChannels)
|
|
}
|
|
}
|
|
|
|
return am.svc.Connect(ctx, session, chIDs, thIDs, connTypes)
|
|
}
|
|
|
|
func (am *authorizationMiddleware) Disconnect(ctx context.Context, session authn.Session, chIDs, thIDs []string, connTypes []connections.ConnType) error {
|
|
for _, chID := range chIDs {
|
|
if err := am.authorize(ctx, session, policies.ChannelType, operations.OpDisconnectClient, smqauthz.PolicyReq{
|
|
Domain: session.DomainID,
|
|
SubjectType: policies.UserType,
|
|
Subject: session.DomainUserID,
|
|
ObjectType: policies.ChannelType,
|
|
Object: chID,
|
|
}); err != nil {
|
|
return errors.Wrap(err, errDisconnect)
|
|
}
|
|
}
|
|
|
|
for _, thID := range thIDs {
|
|
if err := am.authorize(ctx, session, policies.ClientType, cOperations.OpDisconnectFromChannel, smqauthz.PolicyReq{
|
|
Domain: session.DomainID,
|
|
SubjectType: policies.UserType,
|
|
Subject: session.DomainUserID,
|
|
ObjectType: policies.ClientType,
|
|
Object: thID,
|
|
}); err != nil {
|
|
return errors.Wrap(err, errClientDisConnectChannels)
|
|
}
|
|
}
|
|
|
|
return am.svc.Disconnect(ctx, session, chIDs, thIDs, connTypes)
|
|
}
|
|
|
|
func (am *authorizationMiddleware) SetParentGroup(ctx context.Context, session authn.Session, parentGroupID string, id string) error {
|
|
if err := am.authorize(ctx, session, policies.ChannelType, operations.OpSetParentGroup, smqauthz.PolicyReq{
|
|
Domain: session.DomainID,
|
|
SubjectType: policies.UserType,
|
|
Subject: session.DomainUserID,
|
|
ObjectType: policies.ChannelType,
|
|
Object: id,
|
|
}); err != nil {
|
|
return errors.Wrap(err, errSetParentGroup)
|
|
}
|
|
|
|
if err := am.authorize(ctx, session, policies.GroupType, gOperations.OpGroupSetChildChannel, smqauthz.PolicyReq{
|
|
Domain: session.DomainID,
|
|
SubjectType: policies.UserType,
|
|
Subject: session.DomainUserID,
|
|
ObjectType: policies.GroupType,
|
|
Object: parentGroupID,
|
|
}); err != nil {
|
|
return errors.Wrap(err, errGroupSetChildChannels)
|
|
}
|
|
|
|
return am.svc.SetParentGroup(ctx, session, parentGroupID, id)
|
|
}
|
|
|
|
func (am *authorizationMiddleware) RemoveParentGroup(ctx context.Context, session authn.Session, id string) error {
|
|
if err := am.authorize(ctx, session, policies.ChannelType, operations.OpSetParentGroup, smqauthz.PolicyReq{
|
|
Domain: session.DomainID,
|
|
SubjectType: policies.UserType,
|
|
Subject: session.DomainUserID,
|
|
ObjectType: policies.ChannelType,
|
|
Object: id,
|
|
}); err != nil {
|
|
return errors.Wrap(err, errRemoveParentGroup)
|
|
}
|
|
|
|
ch, err := am.repo.RetrieveByID(ctx, id)
|
|
if err != nil {
|
|
return errors.Wrap(svcerr.ErrRemoveEntity, err)
|
|
}
|
|
|
|
if ch.ParentGroup != "" {
|
|
if err := am.authorize(ctx, session, policies.GroupType, gOperations.OpGroupRemoveChildChannel, smqauthz.PolicyReq{
|
|
Domain: session.DomainID,
|
|
SubjectType: policies.UserType,
|
|
Subject: session.DomainUserID,
|
|
ObjectType: policies.GroupType,
|
|
Object: ch.ParentGroup,
|
|
}); err != nil {
|
|
return errors.Wrap(err, errGroupRemoveChildChannels)
|
|
}
|
|
|
|
return am.svc.RemoveParentGroup(ctx, session, id)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (am *authorizationMiddleware) authorize(ctx context.Context, session authn.Session, entityType string, op permissions.Operation, req smqauthz.PolicyReq) error {
|
|
req.Domain = session.DomainID
|
|
|
|
perm, err := am.entitiesOps.GetPermission(entityType, op)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req.Permission = perm.String()
|
|
|
|
var pat *smqauthz.PATReq
|
|
if session.PatID != "" {
|
|
entityID := req.Object
|
|
opName := am.entitiesOps.OperationName(entityType, op)
|
|
if op == operations.OpListUserChannels || op == dOperations.OpCreateDomainChannels || op == dOperations.OpListDomainChannels {
|
|
entityID = auth.AnyIDs
|
|
}
|
|
pat = &smqauthz.PATReq{
|
|
UserID: session.UserID,
|
|
PatID: session.PatID,
|
|
EntityID: entityID,
|
|
EntityType: patEntityType(entityType),
|
|
Operation: opName,
|
|
Domain: session.DomainID,
|
|
}
|
|
}
|
|
|
|
if err := am.authz.Authorize(ctx, req, pat); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func patEntityType(entityType string) string {
|
|
switch entityType {
|
|
case policies.ClientType:
|
|
return auth.ClientsType.String()
|
|
default:
|
|
return auth.ChannelsType.String()
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|