222 lines
6.1 KiB
Go
222 lines
6.1 KiB
Go
// Copyright (c) Mainflux
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package groups
|
|
|
|
import (
|
|
"context"
|
|
"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"
|
|
tpolicies "github.com/mainflux/mainflux/things/policies"
|
|
upolicies "github.com/mainflux/mainflux/users/policies"
|
|
)
|
|
|
|
const (
|
|
thingsObjectKey = "things"
|
|
updateRelationKey = "g_update"
|
|
listRelationKey = "g_list"
|
|
deleteRelationKey = "g_delete"
|
|
entityType = "group"
|
|
)
|
|
|
|
type service struct {
|
|
uauth upolicies.AuthServiceClient
|
|
policies tpolicies.Service
|
|
groups groups.Repository
|
|
idProvider mainflux.IDProvider
|
|
}
|
|
|
|
// NewService returns a new Clients service implementation.
|
|
func NewService(uauth upolicies.AuthServiceClient, policies tpolicies.Service, g groups.Repository, idp mainflux.IDProvider) Service {
|
|
return service{
|
|
uauth: uauth,
|
|
policies: policies,
|
|
groups: g,
|
|
idProvider: idp,
|
|
}
|
|
}
|
|
|
|
func (svc service) CreateGroups(ctx context.Context, token string, gs ...groups.Group) ([]groups.Group, error) {
|
|
userID, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return []groups.Group{}, err
|
|
}
|
|
|
|
var grps []groups.Group
|
|
for _, g := range gs {
|
|
if g.ID == "" {
|
|
groupID, err := svc.idProvider.ID()
|
|
if err != nil {
|
|
return []groups.Group{}, err
|
|
}
|
|
g.ID = groupID
|
|
}
|
|
if g.Owner == "" {
|
|
g.Owner = userID
|
|
}
|
|
|
|
if g.Status != mfclients.EnabledStatus && g.Status != mfclients.DisabledStatus {
|
|
return []groups.Group{}, apiutil.ErrInvalidStatus
|
|
}
|
|
g.CreatedAt = time.Now()
|
|
|
|
grp, err := svc.groups.Save(ctx, g)
|
|
if err != nil {
|
|
return []groups.Group{}, err
|
|
}
|
|
grps = append(grps, grp)
|
|
}
|
|
return grps, nil
|
|
}
|
|
|
|
func (svc service) ViewGroup(ctx context.Context, token string, id string) (groups.Group, error) {
|
|
userID, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
if err := svc.authorize(ctx, userID, id, listRelationKey); err != nil {
|
|
return groups.Group{}, errors.Wrap(errors.ErrNotFound, err)
|
|
}
|
|
return svc.groups.RetrieveByID(ctx, id)
|
|
}
|
|
|
|
func (svc service) ListGroups(ctx context.Context, token string, gm groups.GroupsPage) (groups.GroupsPage, error) {
|
|
userID, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return groups.GroupsPage{}, err
|
|
}
|
|
|
|
// If the user is admin, fetch all channels from the database.
|
|
if err := svc.authorize(ctx, userID, thingsObjectKey, listRelationKey); err == nil {
|
|
return svc.groups.RetrieveAll(ctx, gm)
|
|
}
|
|
|
|
gm.Subject = userID
|
|
gm.OwnerID = userID
|
|
gm.Action = "g_list"
|
|
return svc.groups.RetrieveAll(ctx, gm)
|
|
}
|
|
|
|
func (svc service) ListMemberships(ctx context.Context, token, clientID string, gm groups.GroupsPage) (groups.MembershipsPage, error) {
|
|
userID, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return groups.MembershipsPage{}, err
|
|
}
|
|
|
|
// If the user is admin, fetch all channels from the database.
|
|
if err := svc.authorize(ctx, userID, thingsObjectKey, listRelationKey); err == nil {
|
|
return svc.groups.Memberships(ctx, clientID, gm)
|
|
}
|
|
|
|
gm.OwnerID = userID
|
|
gm.Action = listRelationKey
|
|
return svc.groups.Memberships(ctx, clientID, gm)
|
|
}
|
|
|
|
func (svc service) UpdateGroup(ctx context.Context, token string, g groups.Group) (groups.Group, error) {
|
|
userID, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
|
|
if err := svc.authorize(ctx, userID, g.ID, updateRelationKey); err != nil {
|
|
return groups.Group{}, errors.Wrap(errors.ErrNotFound, err)
|
|
}
|
|
|
|
g.Owner = userID
|
|
g.UpdatedAt = time.Now()
|
|
g.UpdatedBy = userID
|
|
|
|
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{}, errors.Wrap(groups.ErrEnableGroup, 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{}, errors.Wrap(groups.ErrDisableGroup, err)
|
|
}
|
|
return group, nil
|
|
}
|
|
|
|
func (svc service) changeGroupStatus(ctx context.Context, token string, group groups.Group) (groups.Group, error) {
|
|
userID, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return groups.Group{}, err
|
|
}
|
|
if err := svc.authorize(ctx, userID, group.ID, deleteRelationKey); err != nil {
|
|
return groups.Group{}, errors.Wrap(errors.ErrNotFound, 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 = userID
|
|
return svc.groups.ChangeStatus(ctx, group)
|
|
}
|
|
|
|
func (svc service) identify(ctx context.Context, token string) (string, error) {
|
|
req := &upolicies.Token{Value: token}
|
|
res, err := svc.uauth.Identify(ctx, req)
|
|
if err != nil {
|
|
return "", errors.Wrap(errors.ErrAuthorization, err)
|
|
}
|
|
return res.GetId(), nil
|
|
}
|
|
|
|
func (svc service) authorize(ctx context.Context, subject, object, action string) error {
|
|
// If the user is admin, skip authorization.
|
|
if err := svc.checkAdmin(ctx, subject, thingsObjectKey, action); err == nil {
|
|
return nil
|
|
}
|
|
|
|
areq := tpolicies.AccessRequest{Subject: subject, Object: object, Action: action, Entity: entityType}
|
|
|
|
_, err := svc.policies.Authorize(ctx, areq)
|
|
return err
|
|
}
|
|
|
|
func (svc service) checkAdmin(ctx context.Context, subject, object, action string) error {
|
|
// for checking admin rights policy object, action and entity type are not important
|
|
req := &upolicies.AuthorizeReq{
|
|
Sub: subject,
|
|
Obj: object,
|
|
Act: action,
|
|
EntityType: entityType,
|
|
}
|
|
res, err := svc.uauth.Authorize(ctx, req)
|
|
if err != nil {
|
|
return errors.Wrap(errors.ErrAuthorization, err)
|
|
}
|
|
if !res.GetAuthorized() {
|
|
return errors.ErrAuthorization
|
|
}
|
|
return nil
|
|
}
|