1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-04-24 13:48:49 +08:00
Arvindh cd82cc5a43
NOISSUE: Listing of shared things with users & Update SDK (#1923)
* 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>
2023-10-17 15:38:06 +02:00

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
}