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

* Initial Commit: Sync Env Veriables With Docker Deployment Signed-off-by: rodneyosodo <blackd0t@protonmail.com> * Sync Env Vars With Master Signed-off-by: rodneyosodo <blackd0t@protonmail.com> * Remove Altprefix Signed-off-by: rodneyosodo <blackd0t@protonmail.com> * Rename HttpPort to HTTPPort Signed-off-by: rodneyosodo <blackd0t@protonmail.com> * Fix envPrefixDB After Rebase Signed-off-by: rodneyosodo <blackd0t@protonmail.com> * Remove Server Parse Signed-off-by: rodneyosodo <blackd0t@protonmail.com> * Add Provision For TLS on CoAP Signed-off-by: rodneyosodo <blackd0t@protonmail.com> * Fix Exit After Defer Signed-off-by: rodneyosodo <blackd0t@protonmail.com> * Remove Unused Function Signed-off-by: rodneyosodo <blackd0t@protonmail.com> * Document Undocumentated Env Variables Signed-off-by: rodneyosodo <blackd0t@protonmail.com> --------- Signed-off-by: rodneyosodo <blackd0t@protonmail.com> Co-authored-by: Drasko DRASKOVIC <drasko.draskovic@gmail.com>
331 lines
9.2 KiB
Go
331 lines
9.2 KiB
Go
// Copyright (c) Mainflux
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
package clients
|
|
|
|
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"
|
|
mfgroups "github.com/mainflux/mainflux/pkg/groups"
|
|
tpolicies "github.com/mainflux/mainflux/things/policies"
|
|
upolicies "github.com/mainflux/mainflux/users/policies"
|
|
)
|
|
|
|
const (
|
|
MyKey = "mine"
|
|
thingsObjectKey = "things"
|
|
updateRelationKey = "c_update"
|
|
listRelationKey = "c_list"
|
|
deleteRelationKey = "c_delete"
|
|
clientEntityType = "client"
|
|
)
|
|
|
|
var AdminRelationKey = []string{updateRelationKey, listRelationKey, deleteRelationKey}
|
|
|
|
type service struct {
|
|
uauth upolicies.AuthServiceClient
|
|
policies tpolicies.Service
|
|
clients mfclients.Repository
|
|
clientCache Cache
|
|
idProvider mainflux.IDProvider
|
|
grepo mfgroups.Repository
|
|
}
|
|
|
|
// NewService returns a new Clients service implementation.
|
|
func NewService(uauth upolicies.AuthServiceClient, policies tpolicies.Service, c mfclients.Repository, grepo mfgroups.Repository, tcache Cache, idp mainflux.IDProvider) Service {
|
|
return service{
|
|
uauth: uauth,
|
|
policies: policies,
|
|
clients: c,
|
|
grepo: grepo,
|
|
clientCache: tcache,
|
|
idProvider: idp,
|
|
}
|
|
}
|
|
|
|
func (svc service) CreateThings(ctx context.Context, token string, clis ...mfclients.Client) ([]mfclients.Client, error) {
|
|
userID, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return []mfclients.Client{}, err
|
|
}
|
|
var clients []mfclients.Client
|
|
for _, cli := range clis {
|
|
if cli.ID == "" {
|
|
clientID, err := svc.idProvider.ID()
|
|
if err != nil {
|
|
return []mfclients.Client{}, err
|
|
}
|
|
cli.ID = clientID
|
|
}
|
|
if cli.Credentials.Secret == "" {
|
|
key, err := svc.idProvider.ID()
|
|
if err != nil {
|
|
return []mfclients.Client{}, err
|
|
}
|
|
cli.Credentials.Secret = key
|
|
}
|
|
if cli.Owner == "" {
|
|
cli.Owner = userID
|
|
}
|
|
if cli.Status != mfclients.DisabledStatus && cli.Status != mfclients.EnabledStatus {
|
|
return []mfclients.Client{}, apiutil.ErrInvalidStatus
|
|
}
|
|
cli.CreatedAt = time.Now()
|
|
clients = append(clients, cli)
|
|
}
|
|
return svc.clients.Save(ctx, clients...)
|
|
}
|
|
|
|
func (svc service) ViewClient(ctx context.Context, token string, id string) (mfclients.Client, error) {
|
|
userID, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return mfclients.Client{}, err
|
|
}
|
|
if err := svc.authorize(ctx, userID, id, listRelationKey); err != nil {
|
|
return mfclients.Client{}, errors.Wrap(errors.ErrNotFound, err)
|
|
}
|
|
return svc.clients.RetrieveByID(ctx, id)
|
|
}
|
|
|
|
func (svc service) ListClients(ctx context.Context, token string, pm mfclients.Page) (mfclients.ClientsPage, error) {
|
|
userID, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return mfclients.ClientsPage{}, err
|
|
}
|
|
|
|
switch err = svc.checkAdmin(ctx, userID, thingsObjectKey, listRelationKey); err {
|
|
// If the user is admin, fetch all things from database.
|
|
case nil:
|
|
switch {
|
|
case pm.SharedBy == MyKey && pm.Owner == MyKey:
|
|
pm.SharedBy = ""
|
|
pm.Owner = ""
|
|
case pm.SharedBy == MyKey && pm.Owner != MyKey:
|
|
pm.SharedBy = userID
|
|
case pm.Owner == MyKey && pm.SharedBy != MyKey:
|
|
pm.Owner = userID
|
|
}
|
|
|
|
default:
|
|
// If the user is not admin, check 'sharedby' parameter from page metadata.
|
|
// If user provides 'sharedby' key, fetch things from policies. Otherwise,
|
|
// fetch things from the database based on thing's 'owner' field.
|
|
switch {
|
|
case pm.SharedBy == MyKey && pm.Owner == MyKey:
|
|
pm.SharedBy = userID
|
|
case pm.SharedBy == MyKey && pm.Owner != MyKey:
|
|
pm.SharedBy = userID
|
|
pm.Owner = ""
|
|
case pm.Owner == MyKey && pm.SharedBy != MyKey:
|
|
pm.Owner = userID
|
|
default:
|
|
pm.Owner = userID
|
|
}
|
|
pm.Action = listRelationKey
|
|
}
|
|
|
|
return svc.clients.RetrieveAll(ctx, pm)
|
|
}
|
|
|
|
func (svc service) UpdateClient(ctx context.Context, token string, cli mfclients.Client) (mfclients.Client, error) {
|
|
userID, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return mfclients.Client{}, err
|
|
}
|
|
if err := svc.authorize(ctx, userID, cli.ID, updateRelationKey); err != nil {
|
|
return mfclients.Client{}, err
|
|
}
|
|
|
|
client := mfclients.Client{
|
|
ID: cli.ID,
|
|
Name: cli.Name,
|
|
Metadata: cli.Metadata,
|
|
UpdatedAt: time.Now(),
|
|
UpdatedBy: userID,
|
|
}
|
|
|
|
return svc.clients.Update(ctx, client)
|
|
}
|
|
|
|
func (svc service) UpdateClientTags(ctx context.Context, token string, cli mfclients.Client) (mfclients.Client, error) {
|
|
userID, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return mfclients.Client{}, err
|
|
}
|
|
if err := svc.authorize(ctx, userID, cli.ID, updateRelationKey); err != nil {
|
|
return mfclients.Client{}, err
|
|
}
|
|
|
|
client := mfclients.Client{
|
|
ID: cli.ID,
|
|
Tags: cli.Tags,
|
|
UpdatedAt: time.Now(),
|
|
UpdatedBy: userID,
|
|
}
|
|
|
|
return svc.clients.UpdateTags(ctx, client)
|
|
}
|
|
|
|
func (svc service) UpdateClientSecret(ctx context.Context, token, id, key string) (mfclients.Client, error) {
|
|
userID, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return mfclients.Client{}, err
|
|
}
|
|
if err := svc.authorize(ctx, userID, id, updateRelationKey); err != nil {
|
|
return mfclients.Client{}, err
|
|
}
|
|
|
|
client := mfclients.Client{
|
|
ID: id,
|
|
Credentials: mfclients.Credentials{
|
|
Secret: key,
|
|
},
|
|
UpdatedAt: time.Now(),
|
|
UpdatedBy: userID,
|
|
}
|
|
|
|
return svc.clients.UpdateSecret(ctx, client)
|
|
}
|
|
|
|
func (svc service) UpdateClientOwner(ctx context.Context, token string, cli mfclients.Client) (mfclients.Client, error) {
|
|
userID, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return mfclients.Client{}, err
|
|
}
|
|
if err := svc.authorize(ctx, userID, cli.ID, updateRelationKey); err != nil {
|
|
return mfclients.Client{}, err
|
|
}
|
|
|
|
client := mfclients.Client{
|
|
ID: cli.ID,
|
|
Owner: cli.Owner,
|
|
UpdatedAt: time.Now(),
|
|
UpdatedBy: userID,
|
|
}
|
|
|
|
return svc.clients.UpdateOwner(ctx, client)
|
|
}
|
|
|
|
func (svc service) EnableClient(ctx context.Context, token, id string) (mfclients.Client, error) {
|
|
client := mfclients.Client{
|
|
ID: id,
|
|
Status: mfclients.EnabledStatus,
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
client, err := svc.changeClientStatus(ctx, token, client)
|
|
if err != nil {
|
|
return mfclients.Client{}, errors.Wrap(mfclients.ErrEnableClient, err)
|
|
}
|
|
|
|
return client, nil
|
|
}
|
|
|
|
func (svc service) DisableClient(ctx context.Context, token, id string) (mfclients.Client, error) {
|
|
client := mfclients.Client{
|
|
ID: id,
|
|
Status: mfclients.DisabledStatus,
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
client, err := svc.changeClientStatus(ctx, token, client)
|
|
if err != nil {
|
|
return mfclients.Client{}, errors.Wrap(mfclients.ErrDisableClient, err)
|
|
}
|
|
|
|
if err := svc.clientCache.Remove(ctx, client.ID); err != nil {
|
|
return client, err
|
|
}
|
|
|
|
return client, nil
|
|
}
|
|
|
|
func (svc service) changeClientStatus(ctx context.Context, token string, client mfclients.Client) (mfclients.Client, error) {
|
|
userID, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return mfclients.Client{}, err
|
|
}
|
|
if err := svc.authorize(ctx, userID, client.ID, deleteRelationKey); err != nil {
|
|
return mfclients.Client{}, err
|
|
}
|
|
dbClient, err := svc.clients.RetrieveByID(ctx, client.ID)
|
|
if err != nil {
|
|
return mfclients.Client{}, err
|
|
}
|
|
if dbClient.Status == client.Status {
|
|
return mfclients.Client{}, mfclients.ErrStatusAlreadyAssigned
|
|
}
|
|
client.UpdatedBy = userID
|
|
return svc.clients.ChangeStatus(ctx, client)
|
|
}
|
|
|
|
func (svc service) ListClientsByGroup(ctx context.Context, token, groupID string, pm mfclients.Page) (mfclients.MembersPage, error) {
|
|
userID, err := svc.identify(ctx, token)
|
|
if err != nil {
|
|
return mfclients.MembersPage{}, err
|
|
}
|
|
// If the user is admin, fetch all things connected to the channel.
|
|
if err := svc.checkAdmin(ctx, userID, thingsObjectKey, listRelationKey); err == nil {
|
|
return svc.clients.Members(ctx, groupID, pm)
|
|
}
|
|
pm.Owner = userID
|
|
|
|
return svc.clients.Members(ctx, groupID, pm)
|
|
}
|
|
|
|
func (svc service) Identify(ctx context.Context, key string) (string, error) {
|
|
id, err := svc.clientCache.ID(ctx, key)
|
|
if err == nil {
|
|
return id, nil
|
|
}
|
|
client, err := svc.clients.RetrieveBySecret(ctx, key)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if err := svc.clientCache.Save(ctx, key, client.ID); err != nil {
|
|
return "", err
|
|
}
|
|
return client.ID, nil
|
|
}
|
|
|
|
func (svc service) identify(ctx context.Context, token string) (string, error) {
|
|
req := &upolicies.IdentifyReq{Token: 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
|
|
}
|
|
|
|
policy := tpolicies.AccessRequest{Subject: subject, Object: object, Action: action, Entity: clientEntityType}
|
|
|
|
_, err := svc.policies.Authorize(ctx, policy)
|
|
return err
|
|
}
|
|
|
|
// TODO : Only accept token as parameter since object and action are irrelevant.
|
|
func (svc service) checkAdmin(ctx context.Context, subject, object, action string) error {
|
|
req := &upolicies.AuthorizeReq{
|
|
Subject: subject,
|
|
Object: object,
|
|
Action: action,
|
|
EntityType: clientEntityType,
|
|
}
|
|
res, err := svc.uauth.Authorize(ctx, req)
|
|
if err != nil {
|
|
return errors.Wrap(errors.ErrAuthorization, err)
|
|
}
|
|
if !res.GetAuthorized() {
|
|
return errors.ErrAuthorization
|
|
}
|
|
return nil
|
|
}
|