mirror of
https://github.com/mainflux/mainflux.git
synced 2025-04-24 13:48:49 +08:00

* NOISSUE - Fix Bugs (#20) * fix bugs Signed-off-by: Arvindh <arvindh91@gmail.com> * fix bugs Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add Connect Disconnect endpoints (#23) * fix bugs Signed-off-by: Arvindh <arvindh91@gmail.com> * fix bugs Signed-off-by: Arvindh <arvindh91@gmail.com> * fix list of things in a channel and Add connect disconnect endpoint Signed-off-by: Arvindh <arvindh91@gmail.com> * fix list of things in a channel and Add connect disconnect endpoint Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add: Things share with users (#25) * fix list of things in a channel and Add connect disconnect endpoint Signed-off-by: Arvindh <arvindh91@gmail.com> * add: things share with other users Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add: Listing of things, channels, groups, users (#26) * add: listing of channels, users, groups, things Signed-off-by: Arvindh <arvindh91@gmail.com> * add: listing of channels, users, groups, things Signed-off-by: Arvindh <arvindh91@gmail.com> * add: listing of channels, users, groups, things Signed-off-by: Arvindh <arvindh91@gmail.com> * add: listing of channels, users, groups, things Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * Add: List of user groups & removed repeating code in groups (#29) * removed repeating code in list groups Signed-off-by: Arvindh <arvindh91@gmail.com> * add: list of user group Signed-off-by: Arvindh <arvindh91@gmail.com> * fix: otel handler operator name for endpoints Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> * add: listing of shared things and users Signed-off-by: Arvindh <arvindh91@gmail.com> * fix: listing of shared things and users Signed-off-by: Arvindh <arvindh91@gmail.com> * add: new SDK Signed-off-by: Arvindh <arvindh91@gmail.com> * add: new SDK Signed-off-by: Arvindh <arvindh91@gmail.com> * fix: comment Signed-off-by: Arvindh <arvindh91@gmail.com> * fix: sdk function names Signed-off-by: Arvindh <arvindh91@gmail.com> * update: api spec Signed-off-by: Arvindh <arvindh91@gmail.com> * fix: channels connect request Signed-off-by: Arvindh <arvindh91@gmail.com> * fix: listing of clients and groups Signed-off-by: Arvindh <arvindh91@gmail.com> * fix: CLI Signed-off-by: Arvindh <arvindh91@gmail.com> * fix: array len comparision Signed-off-by: Arvindh <arvindh91@gmail.com> * fix: nginx Signed-off-by: Arvindh <arvindh91@gmail.com> --------- Signed-off-by: Arvindh <arvindh91@gmail.com> Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com> Co-authored-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
520 lines
13 KiB
Go
520 lines
13 KiB
Go
// Copyright (c) Mainflux
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package groups
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/mainflux/mainflux"
|
|
"github.com/mainflux/mainflux/internal/apiutil"
|
|
mfclients "github.com/mainflux/mainflux/pkg/clients"
|
|
"github.com/mainflux/mainflux/pkg/errors"
|
|
"github.com/mainflux/mainflux/pkg/groups"
|
|
)
|
|
|
|
var errParentUnAuthz = errors.New("failed to authorize parent group")
|
|
|
|
const (
|
|
ownerRelation = "owner"
|
|
channelRelation = "channel"
|
|
groupRelation = "group"
|
|
parentGroupRelation = "parent_group"
|
|
|
|
usersKind = "users"
|
|
groupsKind = "groups"
|
|
thingsKind = "things"
|
|
channelsKind = "channels"
|
|
|
|
userType = "user"
|
|
groupType = "group"
|
|
thingType = "thing"
|
|
channelType = "channel"
|
|
|
|
adminPermission = "admin"
|
|
ownerPermission = "delete"
|
|
deletePermission = "delete"
|
|
sharePermission = "share"
|
|
editPermission = "edit"
|
|
disconnectPermission = "disconnect"
|
|
connectPermission = "connect"
|
|
viewPermission = "view"
|
|
memberPermission = "member"
|
|
|
|
tokenKind = "token"
|
|
)
|
|
|
|
type service struct {
|
|
groups groups.Repository
|
|
auth mainflux.AuthServiceClient
|
|
idProvider mainflux.IDProvider
|
|
}
|
|
|
|
// NewService returns a new Clients service implementation.
|
|
func NewService(g groups.Repository, idp mainflux.IDProvider, auth mainflux.AuthServiceClient) groups.Service {
|
|
return service{
|
|
groups: g,
|
|
idProvider: idp,
|
|
auth: auth,
|
|
}
|
|
}
|
|
|
|
func (svc service) CreateGroup(ctx context.Context, token string, g groups.Group) (groups.Group, error) {
|
|
ownerID, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
groupID, err := svc.idProvider.ID()
|
|
if err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
if g.Status != mfclients.EnabledStatus && g.Status != mfclients.DisabledStatus {
|
|
return groups.Group{}, apiutil.ErrInvalidStatus
|
|
}
|
|
if g.Owner == "" {
|
|
g.Owner = ownerID
|
|
}
|
|
|
|
g.ID = groupID
|
|
g.CreatedAt = time.Now()
|
|
|
|
if g.Parent != "" {
|
|
_, err := svc.authorize(ctx, userType, token, editPermission, groupType, g.Parent)
|
|
if err != nil {
|
|
return groups.Group{}, errors.Wrap(errParentUnAuthz, err)
|
|
}
|
|
}
|
|
|
|
g, err = svc.groups.Save(ctx, g)
|
|
if err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
|
|
policy := mainflux.AddPolicyReq{
|
|
SubjectType: userType,
|
|
Subject: ownerID,
|
|
Relation: ownerRelation,
|
|
ObjectType: groupType,
|
|
Object: g.ID,
|
|
}
|
|
if _, err := svc.auth.AddPolicy(ctx, &policy); err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
|
|
if g.Parent != "" {
|
|
policy = mainflux.AddPolicyReq{
|
|
SubjectType: groupType,
|
|
Subject: g.Parent,
|
|
Relation: parentGroupRelation,
|
|
ObjectType: groupType,
|
|
Object: g.ID,
|
|
}
|
|
if _, err := svc.auth.AddPolicy(ctx, &policy); err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
}
|
|
|
|
return g, nil
|
|
}
|
|
|
|
func (svc service) ViewGroup(ctx context.Context, token string, id string) (groups.Group, error) {
|
|
_, err := svc.authorize(ctx, userType, token, viewPermission, groupType, id)
|
|
if err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
|
|
return svc.groups.RetrieveByID(ctx, id)
|
|
}
|
|
|
|
func (svc service) ListGroups(ctx context.Context, token string, memberKind, memberID string, gm groups.Page) (groups.Page, error) {
|
|
var ids []string
|
|
userID, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
switch memberKind {
|
|
case thingsKind:
|
|
if _, err := svc.authorizeKind(ctx, userType, usersKind, userID, viewPermission, thingType, memberID); err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
cids, err := svc.auth.ListAllSubjects(ctx, &mainflux.ListSubjectsReq{
|
|
SubjectType: groupType,
|
|
Permission: groupRelation,
|
|
ObjectType: thingType,
|
|
Object: memberID,
|
|
})
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
ids, err = svc.filterAllowedGroupIDsOfUserID(ctx, userID, gm.Permission, cids.Policies)
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
case groupsKind:
|
|
if _, err := svc.authorizeKind(ctx, userType, usersKind, userID, gm.Permission, groupType, memberID); err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
|
|
gids, err := svc.auth.ListAllObjects(ctx, &mainflux.ListObjectsReq{
|
|
SubjectType: groupType,
|
|
Subject: memberID,
|
|
Permission: parentGroupRelation,
|
|
ObjectType: groupType,
|
|
})
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
ids, err = svc.filterAllowedGroupIDsOfUserID(ctx, userID, gm.Permission, gids.Policies)
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
case channelsKind:
|
|
if _, err := svc.authorizeKind(ctx, userType, usersKind, userID, viewPermission, groupType, memberID); err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
gids, err := svc.auth.ListAllSubjects(ctx, &mainflux.ListSubjectsReq{
|
|
SubjectType: groupType,
|
|
Permission: parentGroupRelation,
|
|
ObjectType: groupType,
|
|
Object: memberID,
|
|
})
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
|
|
ids, err = svc.filterAllowedGroupIDsOfUserID(ctx, userID, gm.Permission, gids.Policies)
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
case usersKind:
|
|
if memberID != "" && userID != memberID {
|
|
if _, err := svc.authorizeKind(ctx, userType, usersKind, userID, ownerRelation, userType, memberID); err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
gids, err := svc.auth.ListAllObjects(ctx, &mainflux.ListObjectsReq{
|
|
SubjectType: userType,
|
|
Subject: memberID,
|
|
Permission: gm.Permission,
|
|
ObjectType: groupType,
|
|
})
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
ids, err = svc.filterAllowedGroupIDsOfUserID(ctx, userID, gm.Permission, gids.Policies)
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
} else {
|
|
ids, err = svc.listAllGroupsOfUserID(ctx, userID, gm.Permission)
|
|
if err != nil {
|
|
return groups.Page{}, err
|
|
}
|
|
}
|
|
default:
|
|
return groups.Page{}, fmt.Errorf("invalid member kind")
|
|
}
|
|
|
|
if len(ids) == 0 {
|
|
return groups.Page{
|
|
PageMeta: gm.PageMeta,
|
|
}, nil
|
|
}
|
|
return svc.groups.RetrieveByIDs(ctx, gm, ids...)
|
|
}
|
|
|
|
func (svc service) ListMembers(ctx context.Context, token, groupID, permission, memberKind string) (groups.MembersPage, error) {
|
|
_, err := svc.authorize(ctx, userType, token, viewPermission, groupType, groupID)
|
|
if err != nil {
|
|
return groups.MembersPage{}, err
|
|
}
|
|
switch memberKind {
|
|
case thingsKind:
|
|
tids, err := svc.auth.ListAllObjects(ctx, &mainflux.ListObjectsReq{
|
|
SubjectType: groupType,
|
|
Subject: groupID,
|
|
Relation: groupRelation,
|
|
ObjectType: thingType,
|
|
})
|
|
if err != nil {
|
|
return groups.MembersPage{}, err
|
|
}
|
|
|
|
members := []groups.Member{}
|
|
|
|
for _, id := range tids.Policies {
|
|
members = append(members, groups.Member{
|
|
ID: id,
|
|
Type: thingType,
|
|
})
|
|
}
|
|
return groups.MembersPage{
|
|
Total: uint64(len(members)),
|
|
Offset: 0,
|
|
Limit: uint64(len(members)),
|
|
Members: members,
|
|
}, nil
|
|
case usersKind:
|
|
uids, err := svc.auth.ListAllSubjects(ctx, &mainflux.ListSubjectsReq{
|
|
SubjectType: userType,
|
|
Permission: permission,
|
|
Object: groupID,
|
|
ObjectType: groupType,
|
|
})
|
|
if err != nil {
|
|
return groups.MembersPage{}, err
|
|
}
|
|
|
|
members := []groups.Member{}
|
|
|
|
for _, id := range uids.Policies {
|
|
members = append(members, groups.Member{
|
|
ID: id,
|
|
Type: userType,
|
|
})
|
|
}
|
|
return groups.MembersPage{
|
|
Total: uint64(len(members)),
|
|
Offset: 0,
|
|
Limit: uint64(len(members)),
|
|
Members: members,
|
|
}, nil
|
|
default:
|
|
return groups.MembersPage{}, fmt.Errorf("invalid member_kind")
|
|
}
|
|
}
|
|
|
|
func (svc service) UpdateGroup(ctx context.Context, token string, g groups.Group) (groups.Group, error) {
|
|
id, err := svc.authorize(ctx, userType, token, editPermission, 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: mfclients.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: mfclients.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 {
|
|
_, err := svc.authorize(ctx, userType, token, editPermission, groupType, groupID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
prs := []*mainflux.AddPolicyReq{}
|
|
switch memberKind {
|
|
case thingsKind:
|
|
for _, memberID := range memberIDs {
|
|
prs = append(prs, &mainflux.AddPolicyReq{
|
|
SubjectType: groupType,
|
|
Subject: groupID,
|
|
Relation: relation,
|
|
ObjectType: thingType,
|
|
Object: memberID,
|
|
})
|
|
}
|
|
case groupsKind:
|
|
for _, memberID := range memberIDs {
|
|
prs = append(prs, &mainflux.AddPolicyReq{
|
|
SubjectType: groupType,
|
|
Subject: memberID,
|
|
Relation: relation,
|
|
ObjectType: groupType,
|
|
Object: groupID,
|
|
})
|
|
}
|
|
case usersKind:
|
|
for _, memberID := range memberIDs {
|
|
prs = append(prs, &mainflux.AddPolicyReq{
|
|
SubjectType: userType,
|
|
Subject: memberID,
|
|
Relation: relation,
|
|
ObjectType: groupType,
|
|
Object: groupID,
|
|
})
|
|
}
|
|
default:
|
|
return fmt.Errorf("invalid member kind")
|
|
}
|
|
|
|
for _, pr := range prs {
|
|
if _, err := svc.auth.AddPolicy(ctx, pr); err != nil {
|
|
return fmt.Errorf("failed to add policies : %w", err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (svc service) Unassign(ctx context.Context, token, groupID, relation, memberKind string, memberIDs ...string) error {
|
|
_, err := svc.authorize(ctx, userType, token, editPermission, groupType, groupID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
prs := []*mainflux.DeletePolicyReq{}
|
|
|
|
switch memberKind {
|
|
case thingsKind:
|
|
for _, memberID := range memberIDs {
|
|
prs = append(prs, &mainflux.DeletePolicyReq{
|
|
SubjectType: groupType,
|
|
Subject: groupID,
|
|
Relation: relation,
|
|
ObjectType: thingType,
|
|
Object: memberID,
|
|
})
|
|
}
|
|
case groupsKind:
|
|
for _, memberID := range memberIDs {
|
|
prs = append(prs, &mainflux.DeletePolicyReq{
|
|
SubjectType: groupType,
|
|
Subject: memberID,
|
|
Relation: relation,
|
|
ObjectType: groupType,
|
|
Object: groupID,
|
|
})
|
|
}
|
|
case usersKind:
|
|
for _, memberID := range memberIDs {
|
|
prs = append(prs, &mainflux.DeletePolicyReq{
|
|
SubjectType: userType,
|
|
Subject: memberID,
|
|
Relation: relation,
|
|
ObjectType: groupType,
|
|
Object: groupID,
|
|
})
|
|
}
|
|
default:
|
|
return fmt.Errorf("invalid member kind")
|
|
}
|
|
|
|
for _, pr := range prs {
|
|
if _, err := svc.auth.DeletePolicy(ctx, pr); err != nil {
|
|
return fmt.Errorf("failed to delete policies : %w", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (svc service) filterAllowedGroupIDsOfUserID(ctx context.Context, userID string, 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 string, permission string) ([]string, error) {
|
|
allowedIDs, err := svc.auth.ListAllObjects(ctx, &mainflux.ListObjectsReq{
|
|
SubjectType: userType,
|
|
Subject: userID,
|
|
Permission: permission,
|
|
ObjectType: 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.authorize(ctx, userType, token, editPermission, groupType, group.ID)
|
|
if err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
dbGroup, err := svc.groups.RetrieveByID(ctx, group.ID)
|
|
if err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
if dbGroup.Status == group.Status {
|
|
return groups.Group{}, mfclients.ErrStatusAlreadyAssigned
|
|
}
|
|
|
|
group.UpdatedBy = id
|
|
return svc.groups.ChangeStatus(ctx, group)
|
|
}
|
|
|
|
func (svc service) identify(ctx context.Context, token string) (string, error) {
|
|
user, err := svc.auth.Identify(ctx, &mainflux.IdentityReq{Token: token})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return user.GetId(), nil
|
|
}
|
|
|
|
func (svc service) authorize(ctx context.Context, subjectType, subject, permission, objectType, object string) (string, error) {
|
|
req := &mainflux.AuthorizeReq{
|
|
SubjectType: subjectType,
|
|
SubjectKind: tokenKind,
|
|
Subject: subject,
|
|
Permission: permission,
|
|
Object: object,
|
|
ObjectType: objectType,
|
|
}
|
|
res, err := svc.auth.Authorize(ctx, req)
|
|
if err != nil {
|
|
return "", errors.Wrap(errors.ErrAuthorization, err)
|
|
}
|
|
if !res.GetAuthorized() {
|
|
return "", errors.ErrAuthorization
|
|
}
|
|
return res.GetId(), nil
|
|
}
|
|
|
|
func (svc service) authorizeKind(ctx context.Context, subjectType, subjectKind, subject, permission, objectType, object string) (string, error) {
|
|
req := &mainflux.AuthorizeReq{
|
|
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(errors.ErrAuthorization, err)
|
|
}
|
|
if !res.GetAuthorized() {
|
|
return "", errors.ErrAuthorization
|
|
}
|
|
return res.GetId(), nil
|
|
}
|