mirror of
https://github.com/absmach/supermq.git
synced 2026-06-23 07:10:19 +00:00
ee3716623c
Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com> Signed-off-by: Rodney Osodo <socials@rodneyosodo.com> Signed-off-by: rodneyosodo <blackd0t@protonmail.com>
781 lines
24 KiB
Go
781 lines
24 KiB
Go
// Copyright (c) Abstract Machines
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package groups
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/absmach/magistrala"
|
|
"github.com/absmach/magistrala/auth"
|
|
"github.com/absmach/magistrala/pkg/apiutil"
|
|
mgclients "github.com/absmach/magistrala/pkg/clients"
|
|
"github.com/absmach/magistrala/pkg/errors"
|
|
svcerr "github.com/absmach/magistrala/pkg/errors/service"
|
|
"github.com/absmach/magistrala/pkg/groups"
|
|
"golang.org/x/sync/errgroup"
|
|
)
|
|
|
|
var (
|
|
errParentUnAuthz = errors.New("failed to authorize parent group")
|
|
errMemberKind = errors.New("invalid member kind")
|
|
errGroupIDs = errors.New("invalid group ids")
|
|
)
|
|
|
|
type service struct {
|
|
groups groups.Repository
|
|
auth magistrala.AuthServiceClient
|
|
idProvider magistrala.IDProvider
|
|
}
|
|
|
|
// NewService returns a new Clients service implementation.
|
|
func NewService(g groups.Repository, idp magistrala.IDProvider, authClient magistrala.AuthServiceClient) groups.Service {
|
|
return service{
|
|
groups: g,
|
|
idProvider: idp,
|
|
auth: authClient,
|
|
}
|
|
}
|
|
|
|
func (svc service) CreateGroup(ctx context.Context, token, kind string, g groups.Group) (gr groups.Group, err error) {
|
|
res, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
// If domain is disabled , then this authorization will fail for all non-admin domain users
|
|
if _, err := svc.authorizeKind(ctx, "", auth.UserType, auth.UsersKind, res.GetId(), auth.CreatePermission, auth.DomainType, res.GetDomainId()); err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
groupID, err := svc.idProvider.ID()
|
|
if err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
if g.Status != mgclients.EnabledStatus && g.Status != mgclients.DisabledStatus {
|
|
return groups.Group{}, svcerr.ErrInvalidStatus
|
|
}
|
|
|
|
g.ID = groupID
|
|
g.CreatedAt = time.Now()
|
|
g.Domain = res.GetDomainId()
|
|
if g.Parent != "" {
|
|
_, err := svc.authorizeToken(ctx, auth.UserType, token, auth.EditPermission, auth.GroupType, g.Parent)
|
|
if err != nil {
|
|
return groups.Group{}, errors.Wrap(errParentUnAuthz, err)
|
|
}
|
|
}
|
|
|
|
if err := svc.addGroupPolicy(ctx, res.GetId(), res.GetDomainId(), g.ID, g.Parent, kind); err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
defer func() {
|
|
if err != nil {
|
|
if errRollback := svc.addGroupPolicyRollback(ctx, res.GetId(), res.GetDomainId(), g.ID, g.Parent, kind); errRollback != nil {
|
|
err = errors.Wrap(errors.Wrap(errors.ErrRollbackTx, errRollback), err)
|
|
}
|
|
}
|
|
}()
|
|
|
|
saved, err := svc.groups.Save(ctx, g)
|
|
if err != nil {
|
|
return groups.Group{}, errors.Wrap(svcerr.ErrCreateEntity, err)
|
|
}
|
|
|
|
return saved, nil
|
|
}
|
|
|
|
func (svc service) ViewGroup(ctx context.Context, token, id string) (groups.Group, error) {
|
|
_, err := svc.authorizeToken(ctx, auth.UserType, token, auth.ViewPermission, auth.GroupType, id)
|
|
if err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
|
|
group, err := svc.groups.RetrieveByID(ctx, id)
|
|
if err != nil {
|
|
return groups.Group{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
|
}
|
|
|
|
return group, nil
|
|
}
|
|
|
|
func (svc service) ViewGroupPerms(ctx context.Context, token, id string) ([]string, error) {
|
|
res, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return svc.listUserGroupPermission(ctx, res.GetId(), id)
|
|
}
|
|
|
|
func (svc service) ListGroups(ctx context.Context, token, memberKind, memberID string, gm groups.Page) (groups.Page, error) {
|
|
var ids []string
|
|
res, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
switch memberKind {
|
|
case auth.ThingsKind:
|
|
if _, err := svc.authorizeKind(ctx, res.GetDomainId(), auth.UserType, auth.UsersKind, res.GetId(), auth.ViewPermission, auth.ThingType, memberID); err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
cids, err := svc.auth.ListAllSubjects(ctx, &magistrala.ListSubjectsReq{
|
|
SubjectType: auth.GroupType,
|
|
Permission: auth.GroupRelation,
|
|
ObjectType: auth.ThingType,
|
|
Object: memberID,
|
|
})
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
ids, err = svc.filterAllowedGroupIDsOfUserID(ctx, res.GetId(), gm.Permission, cids.Policies)
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
case auth.GroupsKind:
|
|
if _, err := svc.authorizeKind(ctx, res.GetDomainId(), auth.UserType, auth.UsersKind, res.GetId(), gm.Permission, auth.GroupType, memberID); err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
|
|
gids, err := svc.auth.ListAllObjects(ctx, &magistrala.ListObjectsReq{
|
|
SubjectType: auth.GroupType,
|
|
Subject: memberID,
|
|
Permission: auth.ParentGroupRelation,
|
|
ObjectType: auth.GroupType,
|
|
})
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
ids, err = svc.filterAllowedGroupIDsOfUserID(ctx, res.GetId(), gm.Permission, gids.Policies)
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
case auth.ChannelsKind:
|
|
if _, err := svc.authorizeKind(ctx, res.GetDomainId(), auth.UserType, auth.UsersKind, res.GetId(), auth.ViewPermission, auth.GroupType, memberID); err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
gids, err := svc.auth.ListAllSubjects(ctx, &magistrala.ListSubjectsReq{
|
|
SubjectType: auth.GroupType,
|
|
Permission: auth.ParentGroupRelation,
|
|
ObjectType: auth.GroupType,
|
|
Object: memberID,
|
|
})
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
|
|
ids, err = svc.filterAllowedGroupIDsOfUserID(ctx, res.GetId(), gm.Permission, gids.Policies)
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
case auth.UsersKind:
|
|
switch {
|
|
case memberID != "" && res.GetUserId() != memberID:
|
|
if _, err := svc.authorizeKind(ctx, res.GetDomainId(), auth.UserType, auth.UsersKind, res.GetId(), auth.AdminPermission, auth.DomainType, res.GetDomainId()); err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
gids, err := svc.auth.ListAllObjects(ctx, &magistrala.ListObjectsReq{
|
|
SubjectType: auth.UserType,
|
|
Subject: auth.EncodeDomainUserID(res.GetDomainId(), memberID),
|
|
Permission: gm.Permission,
|
|
ObjectType: auth.GroupType,
|
|
})
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
ids, err = svc.filterAllowedGroupIDsOfUserID(ctx, res.GetId(), gm.Permission, gids.Policies)
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
default:
|
|
switch svc.checkSuperAdmin(ctx, res.GetUserId()) {
|
|
case nil:
|
|
gm.PageMeta.DomainID = res.GetDomainId()
|
|
default:
|
|
// If domain is disabled , then this authorization will fail for all non-admin domain users
|
|
if _, err := svc.authorizeKind(ctx, "", auth.UserType, auth.UsersKind, res.GetId(), auth.MembershipPermission, auth.DomainType, res.GetDomainId()); err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
ids, err = svc.listAllGroupsOfUserID(ctx, res.GetId(), gm.Permission)
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
return groups.Page{}, errMemberKind
|
|
}
|
|
|
|
gp, err := svc.groups.RetrieveByIDs(ctx, gm, ids...)
|
|
if err != nil {
|
|
return groups.Page{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
|
}
|
|
|
|
if gm.ListPerms && len(gp.Groups) > 0 {
|
|
g, ctx := errgroup.WithContext(ctx)
|
|
|
|
for i := range gp.Groups {
|
|
// Copying loop variable "i" to avoid "loop variable captured by func literal"
|
|
iter := i
|
|
g.Go(func() error {
|
|
return svc.retrievePermissions(ctx, res.GetId(), &gp.Groups[iter])
|
|
})
|
|
}
|
|
|
|
if err := g.Wait(); err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
}
|
|
return gp, nil
|
|
}
|
|
|
|
// Experimental functions used for async calling of svc.listUserThingPermission. This might be helpful during listing of large number of entities.
|
|
func (svc service) retrievePermissions(ctx context.Context, userID string, group *groups.Group) error {
|
|
permissions, err := svc.listUserGroupPermission(ctx, userID, group.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
group.Permissions = permissions
|
|
return nil
|
|
}
|
|
|
|
func (svc service) listUserGroupPermission(ctx context.Context, userID, groupID string) ([]string, error) {
|
|
lp, err := svc.auth.ListPermissions(ctx, &magistrala.ListPermissionsReq{
|
|
SubjectType: auth.UserType,
|
|
Subject: userID,
|
|
Object: groupID,
|
|
ObjectType: auth.GroupType,
|
|
})
|
|
if err != nil {
|
|
return []string{}, err
|
|
}
|
|
if len(lp.GetPermissions()) == 0 {
|
|
return []string{}, svcerr.ErrAuthorization
|
|
}
|
|
return lp.GetPermissions(), nil
|
|
}
|
|
|
|
func (svc service) checkSuperAdmin(ctx context.Context, userID string) error {
|
|
res, err := svc.auth.Authorize(ctx, &magistrala.AuthorizeReq{
|
|
SubjectType: auth.UserType,
|
|
Subject: userID,
|
|
Permission: auth.AdminPermission,
|
|
ObjectType: auth.PlatformType,
|
|
Object: auth.MagistralaObject,
|
|
})
|
|
if err != nil {
|
|
return errors.Wrap(svcerr.ErrAuthorization, err)
|
|
}
|
|
if !res.Authorized {
|
|
return svcerr.ErrAuthorization
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// IMPROVEMENT NOTE: remove this function and all its related auxiliary function, ListMembers are moved to respective service.
|
|
func (svc service) ListMembers(ctx context.Context, token, groupID, permission, memberKind string) (groups.MembersPage, error) {
|
|
_, err := svc.authorizeToken(ctx, auth.UserType, token, auth.ViewPermission, auth.GroupType, groupID)
|
|
if err != nil {
|
|
return groups.MembersPage{}, err
|
|
}
|
|
switch memberKind {
|
|
case auth.ThingsKind:
|
|
tids, err := svc.auth.ListAllObjects(ctx, &magistrala.ListObjectsReq{
|
|
SubjectType: auth.GroupType,
|
|
Subject: groupID,
|
|
Relation: auth.GroupRelation,
|
|
ObjectType: auth.ThingType,
|
|
})
|
|
if err != nil {
|
|
return groups.MembersPage{}, err
|
|
}
|
|
|
|
members := []groups.Member{}
|
|
|
|
for _, id := range tids.Policies {
|
|
members = append(members, groups.Member{
|
|
ID: id,
|
|
Type: auth.ThingType,
|
|
})
|
|
}
|
|
return groups.MembersPage{
|
|
Total: uint64(len(members)),
|
|
Offset: 0,
|
|
Limit: uint64(len(members)),
|
|
Members: members,
|
|
}, nil
|
|
case auth.UsersKind:
|
|
uids, err := svc.auth.ListAllSubjects(ctx, &magistrala.ListSubjectsReq{
|
|
SubjectType: auth.UserType,
|
|
Permission: permission,
|
|
Object: groupID,
|
|
ObjectType: auth.GroupType,
|
|
})
|
|
if err != nil {
|
|
return groups.MembersPage{}, err
|
|
}
|
|
|
|
members := []groups.Member{}
|
|
|
|
for _, id := range uids.Policies {
|
|
members = append(members, groups.Member{
|
|
ID: id,
|
|
Type: auth.UserType,
|
|
})
|
|
}
|
|
return groups.MembersPage{
|
|
Total: uint64(len(members)),
|
|
Offset: 0,
|
|
Limit: uint64(len(members)),
|
|
Members: members,
|
|
}, nil
|
|
default:
|
|
return groups.MembersPage{}, errMemberKind
|
|
}
|
|
}
|
|
|
|
func (svc service) UpdateGroup(ctx context.Context, token string, g groups.Group) (groups.Group, error) {
|
|
id, err := svc.authorizeToken(ctx, auth.UserType, token, auth.EditPermission, auth.GroupType, g.ID)
|
|
if err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
|
|
g.UpdatedAt = time.Now()
|
|
g.UpdatedBy = id
|
|
|
|
return svc.groups.Update(ctx, g)
|
|
}
|
|
|
|
func (svc service) EnableGroup(ctx context.Context, token, id string) (groups.Group, error) {
|
|
group := groups.Group{
|
|
ID: id,
|
|
Status: mgclients.EnabledStatus,
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
group, err := svc.changeGroupStatus(ctx, token, group)
|
|
if err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
return group, nil
|
|
}
|
|
|
|
func (svc service) DisableGroup(ctx context.Context, token, id string) (groups.Group, error) {
|
|
group := groups.Group{
|
|
ID: id,
|
|
Status: mgclients.DisabledStatus,
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
group, err := svc.changeGroupStatus(ctx, token, group)
|
|
if err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
return group, nil
|
|
}
|
|
|
|
func (svc service) Assign(ctx context.Context, token, groupID, relation, memberKind string, memberIDs ...string) error {
|
|
res, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err := svc.authorizeKind(ctx, res.GetDomainId(), auth.UserType, auth.UsersKind, res.GetId(), auth.EditPermission, auth.GroupType, groupID); err != nil {
|
|
return err
|
|
}
|
|
|
|
policies := magistrala.AddPoliciesReq{}
|
|
switch memberKind {
|
|
case auth.ThingsKind:
|
|
for _, memberID := range memberIDs {
|
|
policies.AddPoliciesReq = append(policies.AddPoliciesReq, &magistrala.AddPolicyReq{
|
|
Domain: res.GetDomainId(),
|
|
SubjectType: auth.GroupType,
|
|
SubjectKind: auth.ChannelsKind,
|
|
Subject: groupID,
|
|
Relation: relation,
|
|
ObjectType: auth.ThingType,
|
|
Object: memberID,
|
|
})
|
|
}
|
|
case auth.ChannelsKind:
|
|
for _, memberID := range memberIDs {
|
|
policies.AddPoliciesReq = append(policies.AddPoliciesReq, &magistrala.AddPolicyReq{
|
|
Domain: res.GetDomainId(),
|
|
SubjectType: auth.GroupType,
|
|
Subject: memberID,
|
|
Relation: relation,
|
|
ObjectType: auth.GroupType,
|
|
Object: groupID,
|
|
})
|
|
}
|
|
case auth.GroupsKind:
|
|
return svc.assignParentGroup(ctx, res.GetDomainId(), groupID, memberIDs)
|
|
|
|
case auth.UsersKind:
|
|
for _, memberID := range memberIDs {
|
|
policies.AddPoliciesReq = append(policies.AddPoliciesReq, &magistrala.AddPolicyReq{
|
|
Domain: res.GetDomainId(),
|
|
SubjectType: auth.UserType,
|
|
Subject: auth.EncodeDomainUserID(res.GetDomainId(), memberID),
|
|
Relation: relation,
|
|
ObjectType: auth.GroupType,
|
|
Object: groupID,
|
|
})
|
|
}
|
|
default:
|
|
return errMemberKind
|
|
}
|
|
|
|
if _, err := svc.auth.AddPolicies(ctx, &policies); err != nil {
|
|
return errors.Wrap(svcerr.ErrAddPolicies, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (svc service) assignParentGroup(ctx context.Context, domain, parentGroupID string, groupIDs []string) (err error) {
|
|
groupsPage, err := svc.groups.RetrieveByIDs(ctx, groups.Page{PageMeta: groups.PageMeta{Limit: 1<<63 - 1}}, groupIDs...)
|
|
if err != nil {
|
|
return errors.Wrap(svcerr.ErrViewEntity, err)
|
|
}
|
|
if len(groupsPage.Groups) == 0 {
|
|
return errGroupIDs
|
|
}
|
|
var addPolicies magistrala.AddPoliciesReq
|
|
var deletePolicies magistrala.DeletePoliciesReq
|
|
for _, group := range groupsPage.Groups {
|
|
if group.Parent != "" {
|
|
return errors.Wrap(svcerr.ErrConflict, fmt.Errorf("%s group already have parent", group.ID))
|
|
}
|
|
addPolicies.AddPoliciesReq = append(addPolicies.AddPoliciesReq, &magistrala.AddPolicyReq{
|
|
Domain: domain,
|
|
SubjectType: auth.GroupType,
|
|
Subject: parentGroupID,
|
|
Relation: auth.ParentGroupRelation,
|
|
ObjectType: auth.GroupType,
|
|
Object: group.ID,
|
|
})
|
|
deletePolicies.DeletePoliciesReq = append(deletePolicies.DeletePoliciesReq, &magistrala.DeletePolicyReq{
|
|
Domain: domain,
|
|
SubjectType: auth.GroupType,
|
|
Subject: parentGroupID,
|
|
Relation: auth.ParentGroupRelation,
|
|
ObjectType: auth.GroupType,
|
|
Object: group.ID,
|
|
})
|
|
}
|
|
|
|
if _, err := svc.auth.AddPolicies(ctx, &addPolicies); err != nil {
|
|
return errors.Wrap(svcerr.ErrAddPolicies, err)
|
|
}
|
|
defer func() {
|
|
if err != nil {
|
|
if _, errRollback := svc.auth.DeletePolicies(ctx, &deletePolicies); errRollback != nil {
|
|
err = errors.Wrap(err, errors.Wrap(apiutil.ErrRollbackTx, errRollback))
|
|
}
|
|
}
|
|
}()
|
|
|
|
return svc.groups.AssignParentGroup(ctx, parentGroupID, groupIDs...)
|
|
}
|
|
|
|
func (svc service) unassignParentGroup(ctx context.Context, domain, parentGroupID string, groupIDs []string) (err error) {
|
|
groupsPage, err := svc.groups.RetrieveByIDs(ctx, groups.Page{PageMeta: groups.PageMeta{Limit: 1<<63 - 1}}, groupIDs...)
|
|
if err != nil {
|
|
return errors.Wrap(svcerr.ErrViewEntity, err)
|
|
}
|
|
if len(groupsPage.Groups) == 0 {
|
|
return errGroupIDs
|
|
}
|
|
var addPolicies magistrala.AddPoliciesReq
|
|
var deletePolicies magistrala.DeletePoliciesReq
|
|
for _, group := range groupsPage.Groups {
|
|
if group.Parent != "" && group.Parent != parentGroupID {
|
|
return errors.Wrap(svcerr.ErrConflict, fmt.Errorf("%s group doesn't have same parent", group.ID))
|
|
}
|
|
addPolicies.AddPoliciesReq = append(addPolicies.AddPoliciesReq, &magistrala.AddPolicyReq{
|
|
Domain: domain,
|
|
SubjectType: auth.GroupType,
|
|
Subject: parentGroupID,
|
|
Relation: auth.ParentGroupRelation,
|
|
ObjectType: auth.GroupType,
|
|
Object: group.ID,
|
|
})
|
|
deletePolicies.DeletePoliciesReq = append(deletePolicies.DeletePoliciesReq, &magistrala.DeletePolicyReq{
|
|
Domain: domain,
|
|
SubjectType: auth.GroupType,
|
|
Subject: parentGroupID,
|
|
Relation: auth.ParentGroupRelation,
|
|
ObjectType: auth.GroupType,
|
|
Object: group.ID,
|
|
})
|
|
}
|
|
|
|
if _, err := svc.auth.DeletePolicies(ctx, &deletePolicies); err != nil {
|
|
return errors.Wrap(svcerr.ErrDeletePolicies, err)
|
|
}
|
|
defer func() {
|
|
if err != nil {
|
|
if _, errRollback := svc.auth.AddPolicies(ctx, &addPolicies); errRollback != nil {
|
|
err = errors.Wrap(err, errors.Wrap(apiutil.ErrRollbackTx, errRollback))
|
|
}
|
|
}
|
|
}()
|
|
|
|
return svc.groups.UnassignParentGroup(ctx, parentGroupID, groupIDs...)
|
|
}
|
|
|
|
func (svc service) Unassign(ctx context.Context, token, groupID, relation, memberKind string, memberIDs ...string) error {
|
|
res, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err := svc.authorizeKind(ctx, res.GetDomainId(), auth.UserType, auth.UsersKind, res.GetId(), auth.EditPermission, auth.GroupType, groupID); err != nil {
|
|
return err
|
|
}
|
|
|
|
policies := magistrala.DeletePoliciesReq{}
|
|
|
|
switch memberKind {
|
|
case auth.ThingsKind:
|
|
for _, memberID := range memberIDs {
|
|
policies.DeletePoliciesReq = append(policies.DeletePoliciesReq, &magistrala.DeletePolicyReq{
|
|
Domain: res.GetDomainId(),
|
|
SubjectType: auth.GroupType,
|
|
SubjectKind: auth.ChannelsKind,
|
|
Subject: groupID,
|
|
Relation: relation,
|
|
ObjectType: auth.ThingType,
|
|
Object: memberID,
|
|
})
|
|
}
|
|
case auth.ChannelsKind:
|
|
for _, memberID := range memberIDs {
|
|
policies.DeletePoliciesReq = append(policies.DeletePoliciesReq, &magistrala.DeletePolicyReq{
|
|
Domain: res.GetDomainId(),
|
|
SubjectType: auth.GroupType,
|
|
Subject: memberID,
|
|
Relation: relation,
|
|
ObjectType: auth.GroupType,
|
|
Object: groupID,
|
|
})
|
|
}
|
|
case auth.GroupsKind:
|
|
return svc.unassignParentGroup(ctx, res.GetDomainId(), groupID, memberIDs)
|
|
case auth.UsersKind:
|
|
for _, memberID := range memberIDs {
|
|
policies.DeletePoliciesReq = append(policies.DeletePoliciesReq, &magistrala.DeletePolicyReq{
|
|
Domain: res.GetDomainId(),
|
|
SubjectType: auth.UserType,
|
|
Subject: auth.EncodeDomainUserID(res.GetDomainId(), memberID),
|
|
Relation: relation,
|
|
ObjectType: auth.GroupType,
|
|
Object: groupID,
|
|
})
|
|
}
|
|
default:
|
|
return errMemberKind
|
|
}
|
|
|
|
if _, err := svc.auth.DeletePolicies(ctx, &policies); err != nil {
|
|
return errors.Wrap(svcerr.ErrDeletePolicies, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (svc service) DeleteGroup(ctx context.Context, token, id string) error {
|
|
res, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err := svc.authorizeKind(ctx, res.GetDomainId(), auth.UserType, auth.UsersKind, res.GetId(), auth.DeletePermission, auth.GroupType, id); err != nil {
|
|
return err
|
|
}
|
|
|
|
deleteRes, err := svc.auth.DeleteEntityPolicies(ctx, &magistrala.DeleteEntityPoliciesReq{
|
|
EntityType: auth.GroupType,
|
|
Id: id,
|
|
})
|
|
if err != nil {
|
|
return errors.Wrap(svcerr.ErrDeletePolicies, err)
|
|
}
|
|
if !deleteRes.Deleted {
|
|
return svcerr.ErrAuthorization
|
|
}
|
|
|
|
if err := svc.groups.Delete(ctx, id); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (svc service) filterAllowedGroupIDsOfUserID(ctx context.Context, userID, permission string, groupIDs []string) ([]string, error) {
|
|
var ids []string
|
|
allowedIDs, err := svc.listAllGroupsOfUserID(ctx, userID, permission)
|
|
if err != nil {
|
|
return []string{}, err
|
|
}
|
|
|
|
for _, gid := range groupIDs {
|
|
for _, id := range allowedIDs {
|
|
if id == gid {
|
|
ids = append(ids, id)
|
|
}
|
|
}
|
|
}
|
|
return ids, nil
|
|
}
|
|
|
|
func (svc service) listAllGroupsOfUserID(ctx context.Context, userID, permission string) ([]string, error) {
|
|
allowedIDs, err := svc.auth.ListAllObjects(ctx, &magistrala.ListObjectsReq{
|
|
SubjectType: auth.UserType,
|
|
Subject: userID,
|
|
Permission: permission,
|
|
ObjectType: auth.GroupType,
|
|
})
|
|
if err != nil {
|
|
return []string{}, err
|
|
}
|
|
return allowedIDs.Policies, nil
|
|
}
|
|
|
|
func (svc service) changeGroupStatus(ctx context.Context, token string, group groups.Group) (groups.Group, error) {
|
|
id, err := svc.authorizeToken(ctx, auth.UserType, token, auth.EditPermission, auth.GroupType, group.ID)
|
|
if err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
dbGroup, err := svc.groups.RetrieveByID(ctx, group.ID)
|
|
if err != nil {
|
|
return groups.Group{}, errors.Wrap(svcerr.ErrViewEntity, err)
|
|
}
|
|
if dbGroup.Status == group.Status {
|
|
return groups.Group{}, errors.ErrStatusAlreadyAssigned
|
|
}
|
|
|
|
group.UpdatedBy = id
|
|
return svc.groups.ChangeStatus(ctx, group)
|
|
}
|
|
|
|
func (svc service) identify(ctx context.Context, token string) (*magistrala.IdentityRes, error) {
|
|
res, err := svc.auth.Identify(ctx, &magistrala.IdentityReq{Token: token})
|
|
if err != nil {
|
|
return nil, errors.Wrap(svcerr.ErrAuthentication, err)
|
|
}
|
|
if res.GetId() == "" || res.GetDomainId() == "" {
|
|
return nil, svcerr.ErrDomainAuthorization
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (svc service) authorizeToken(ctx context.Context, subjectType, subject, permission, objectType, object string) (string, error) {
|
|
req := &magistrala.AuthorizeReq{
|
|
SubjectType: subjectType,
|
|
SubjectKind: auth.TokenKind,
|
|
Subject: subject,
|
|
Permission: permission,
|
|
Object: object,
|
|
ObjectType: objectType,
|
|
}
|
|
res, err := svc.auth.Authorize(ctx, req)
|
|
if err != nil {
|
|
return "", errors.Wrap(svcerr.ErrAuthorization, err)
|
|
}
|
|
if !res.GetAuthorized() {
|
|
return "", svcerr.ErrAuthorization
|
|
}
|
|
return res.GetId(), nil
|
|
}
|
|
|
|
func (svc service) authorizeKind(ctx context.Context, domainID, subjectType, subjectKind, subject, permission, objectType, object string) (string, error) {
|
|
req := &magistrala.AuthorizeReq{
|
|
Domain: domainID,
|
|
SubjectType: subjectType,
|
|
SubjectKind: subjectKind,
|
|
Subject: subject,
|
|
Permission: permission,
|
|
Object: object,
|
|
ObjectType: objectType,
|
|
}
|
|
res, err := svc.auth.Authorize(ctx, req)
|
|
if err != nil {
|
|
return "", errors.Wrap(svcerr.ErrAuthorization, err)
|
|
}
|
|
if !res.GetAuthorized() {
|
|
return "", svcerr.ErrAuthorization
|
|
}
|
|
return res.GetId(), nil
|
|
}
|
|
|
|
func (svc service) addGroupPolicy(ctx context.Context, userID, domainID, id, parentID, kind string) error {
|
|
policies := magistrala.AddPoliciesReq{}
|
|
policies.AddPoliciesReq = append(policies.AddPoliciesReq, &magistrala.AddPolicyReq{
|
|
Domain: domainID,
|
|
SubjectType: auth.UserType,
|
|
Subject: userID,
|
|
Relation: auth.AdministratorRelation,
|
|
ObjectKind: kind,
|
|
ObjectType: auth.GroupType,
|
|
Object: id,
|
|
})
|
|
policies.AddPoliciesReq = append(policies.AddPoliciesReq, &magistrala.AddPolicyReq{
|
|
Domain: domainID,
|
|
SubjectType: auth.DomainType,
|
|
Subject: domainID,
|
|
Relation: auth.DomainRelation,
|
|
ObjectType: auth.GroupType,
|
|
Object: id,
|
|
})
|
|
if parentID != "" {
|
|
policies.AddPoliciesReq = append(policies.AddPoliciesReq, &magistrala.AddPolicyReq{
|
|
Domain: domainID,
|
|
SubjectType: auth.GroupType,
|
|
Subject: parentID,
|
|
Relation: auth.ParentGroupRelation,
|
|
ObjectKind: kind,
|
|
ObjectType: auth.GroupType,
|
|
Object: id,
|
|
})
|
|
}
|
|
if _, err := svc.auth.AddPolicies(ctx, &policies); err != nil {
|
|
return errors.Wrap(svcerr.ErrAddPolicies, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (svc service) addGroupPolicyRollback(ctx context.Context, userID, domainID, id, parentID, kind string) error {
|
|
policies := magistrala.DeletePoliciesReq{}
|
|
policies.DeletePoliciesReq = append(policies.DeletePoliciesReq, &magistrala.DeletePolicyReq{
|
|
Domain: domainID,
|
|
SubjectType: auth.UserType,
|
|
Subject: userID,
|
|
Relation: auth.AdministratorRelation,
|
|
ObjectKind: kind,
|
|
ObjectType: auth.GroupType,
|
|
Object: id,
|
|
})
|
|
policies.DeletePoliciesReq = append(policies.DeletePoliciesReq, &magistrala.DeletePolicyReq{
|
|
Domain: domainID,
|
|
SubjectType: auth.DomainType,
|
|
Subject: domainID,
|
|
Relation: auth.DomainRelation,
|
|
ObjectType: auth.GroupType,
|
|
Object: id,
|
|
})
|
|
if parentID != "" {
|
|
policies.DeletePoliciesReq = append(policies.DeletePoliciesReq, &magistrala.DeletePolicyReq{
|
|
Domain: domainID,
|
|
SubjectType: auth.GroupType,
|
|
Subject: parentID,
|
|
Relation: auth.ParentGroupRelation,
|
|
ObjectKind: kind,
|
|
ObjectType: auth.GroupType,
|
|
Object: id,
|
|
})
|
|
}
|
|
if _, err := svc.auth.DeletePolicies(ctx, &policies); err != nil {
|
|
return errors.Wrap(svcerr.ErrDeletePolicies, err)
|
|
}
|
|
|
|
return nil
|
|
}
|