1
0
mirror of https://github.com/mainflux/mainflux.git synced 2025-05-11 19:29:16 +08:00
b1ackd0t f5bc71c7c9
NOISSUE - Evaluate Group Access (#1818)
Signed-off-by: rodneyosodo <blackd0t@protonmail.com>
2023-06-19 14:52:01 +02:00

199 lines
5.4 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"
"github.com/mainflux/mainflux/users/jwt"
"github.com/mainflux/mainflux/users/policies"
)
// Possible token types are access and refresh tokens.
const (
RefreshToken = "refresh"
AccessToken = "access"
MyKey = "mine"
groupsObjectKey = "groups"
updateRelationKey = "g_update"
listRelationKey = "g_list"
deleteRelationKey = "g_delete"
)
type service struct {
groups groups.Repository
policies policies.Repository
tokens jwt.Repository
idProvider mainflux.IDProvider
}
// NewService returns a new Clients service implementation.
func NewService(g groups.Repository, p policies.Repository, t jwt.Repository, idp mainflux.IDProvider) Service {
return service{
groups: g,
policies: p,
tokens: t,
idProvider: idp,
}
}
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()
return svc.groups.Save(ctx, g)
}
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.authorizeByID(ctx, userID, id, listRelationKey); err != nil {
return groups.Group{}, err
}
return svc.groups.RetrieveByID(ctx, id)
}
func (svc service) ListGroups(ctx context.Context, token string, gm groups.GroupsPage) (groups.GroupsPage, error) {
id, err := svc.identify(ctx, token)
if err != nil {
return groups.GroupsPage{}, err
}
// If the user is admin, fetch all groups from the database.
if err := svc.authorizeByID(ctx, id, groupsObjectKey, listRelationKey); err == nil {
return svc.groups.RetrieveAll(ctx, gm)
}
gm.Subject = id
gm.OwnerID = id
gm.Action = listRelationKey
return svc.groups.RetrieveAll(ctx, gm)
}
func (svc service) ListMemberships(ctx context.Context, token, clientID string, gm groups.GroupsPage) (groups.MembershipsPage, error) {
id, err := svc.identify(ctx, token)
if err != nil {
return groups.MembershipsPage{}, err
}
// If the user is admin, fetch all members from the database.
if err := svc.authorizeByID(ctx, id, groupsObjectKey, listRelationKey); err == nil {
return svc.groups.Memberships(ctx, clientID, gm)
}
gm.Subject = id
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) {
id, err := svc.identify(ctx, token)
if err != nil {
return groups.Group{}, err
}
if err := svc.authorizeByID(ctx, id, g.ID, updateRelationKey); 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) changeGroupStatus(ctx context.Context, token string, group groups.Group) (groups.Group, error) {
id, err := svc.identify(ctx, token)
if err != nil {
return groups.Group{}, err
}
if err := svc.authorizeByID(ctx, id, group.ID, deleteRelationKey); 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) authorizeByID(ctx context.Context, subject, object, action string) error {
policy := policies.Policy{Subject: subject, Object: object, Actions: []string{action}}
if err := policy.Validate(); err != nil {
return err
}
if err := svc.policies.CheckAdmin(ctx, policy.Subject); err == nil {
return nil
}
aReq := policies.AccessRequest{Subject: subject, Object: object, Action: action}
if _, err := svc.policies.EvaluateGroupAccess(ctx, aReq); err != nil {
return err
}
return nil
}
func (svc service) identify(ctx context.Context, token string) (string, error) {
claims, err := svc.tokens.Parse(ctx, token)
if err != nil {
return "", errors.Wrap(errors.ErrAuthentication, err)
}
if claims.Type != AccessToken {
return "", errors.ErrAuthentication
}
return claims.ClientID, nil
}