Skip to content

Commit

Permalink
Add: Things share with users (absmach#25)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
arvindh123 authored and dborovcanin committed Oct 13, 2023
1 parent 45bf807 commit 06f63c9
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 2 deletions.
46 changes: 46 additions & 0 deletions things/api/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,20 @@ func clientsHandler(svc things.Service, r *chi.Mux, logger mflog.Logger) http.Ha
opts...,
), "disable_thing").ServeHTTP)

r.Post("/{thingID}/share", otelhttp.NewHandler(kithttp.NewServer(
thingShareEndpoint(svc),
decodeThingShareRequest,
api.EncodeResponse,
opts...,
), "thing_share").ServeHTTP)

r.Post("/{thingID}/unshare", otelhttp.NewHandler(kithttp.NewServer(
thingUnshareEndpoint(svc),
decodeThingUnshareRequest,
api.EncodeResponse,
opts...,
), "thing_delete_share").ServeHTTP)

})

return r
Expand Down Expand Up @@ -302,3 +316,35 @@ func decodeListMembersRequest(_ context.Context, r *http.Request) (interface{},
}
return req, nil
}

func decodeThingShareRequest(_ context.Context, r *http.Request) (interface{}, error) {
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
}

req := thingShareRequest{
token: apiutil.ExtractBearerToken(r),
thingID: chi.URLParam(r, "thingID"),
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
}

return req, nil
}

func decodeThingUnshareRequest(_ context.Context, r *http.Request) (interface{}, error) {
if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) {
return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType)
}

req := thingUnshareRequest{
token: apiutil.ExtractBearerToken(r),
thingID: chi.URLParam(r, "thingID"),
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err))
}

return req, nil
}
26 changes: 26 additions & 0 deletions things/api/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,29 @@ func disconnectEndpoint(svc groups.Service) endpoint.Endpoint {
return disconnectChannelThingRes{}, nil
}
}

func thingShareEndpoint(svc things.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(thingShareRequest)
if err := req.validate(); err != nil {
return thingShareRes{}, errors.Wrap(apiutil.ErrValidation, err)
}
if err := svc.Share(ctx, req.token, req.thingID, req.Relation, req.UserIDs...); err != nil {
return thingShareRes{}, err
}
return thingShareRes{}, nil
}
}

func thingUnshareEndpoint(svc things.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(thingUnshareRequest)
if err := req.validate(); err != nil {
return thingUnshareRes{}, errors.Wrap(apiutil.ErrValidation, err)
}
if err := svc.Unshare(ctx, req.token, req.thingID, req.Relation, req.UserIDs...); err != nil {
return thingShareRes{}, err
}
return thingUnshareRes{}, nil
}
}
26 changes: 24 additions & 2 deletions things/api/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,6 @@ func (lm *loggingMiddleware) Identify(ctx context.Context, key string) (id strin
return lm.svc.Identify(ctx, key)
}



func (lm *loggingMiddleware) Authorize(ctx context.Context, req *mainflux.AuthorizeReq) (id string, err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method authorize for thing key %s and channnel %s took %s to complete", req.Subject, req.Object, time.Since(begin))
Expand All @@ -170,3 +168,27 @@ func (lm *loggingMiddleware) Authorize(ctx context.Context, req *mainflux.Author
}(time.Now())
return lm.svc.Authorize(ctx, req)
}

func (lm *loggingMiddleware) Share(ctx context.Context, token, id string, relation string, userids ...string) (err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method share for thing id %s with relation %s for users %v took %s to complete", id, relation, userids, time.Since(begin))
if err != nil {
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
return
}
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
}(time.Now())
return lm.svc.Share(ctx, token, id, relation, userids...)
}

func (lm *loggingMiddleware) Unshare(ctx context.Context, token, id string, relation string, userids ...string) (err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method unshare for thing id %s with relation %s for users %v took %s to complete", id, relation, userids, time.Since(begin))
if err != nil {
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
return
}
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
}(time.Now())
return lm.svc.Unshare(ctx, token, id, relation, userids...)
}
16 changes: 16 additions & 0 deletions things/api/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,19 @@ func (ms *metricsMiddleware) Authorize(ctx context.Context, req *mainflux.Author
}(time.Now())
return ms.svc.Authorize(ctx, req)
}

func (ms *metricsMiddleware) Share(ctx context.Context, token, id string, relation string, userids ...string) error {
defer func(begin time.Time) {
ms.counter.With("method", "share").Add(1)
ms.latency.With("method", "share").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.Share(ctx, token, id, relation, userids...)
}

func (ms *metricsMiddleware) Unshare(ctx context.Context, token, id string, relation string, userids ...string) error {
defer func(begin time.Time) {
ms.counter.With("method", "unshare").Add(1)
ms.latency.With("method", "unshare").Observe(time.Since(begin).Seconds())
}(time.Now())
return ms.svc.Unshare(ctx, token, id, relation, userids...)
}
34 changes: 34 additions & 0 deletions things/api/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,37 @@ func (req *disconnectChannelThingRequest) validate() error {
}
return nil
}

type thingShareRequest struct {
token string
thingID string
Relation string `json:"relation,omitempty"`
UserIDs []string `json:"user_ids,omitempty"`
}

func (req *thingShareRequest) validate() error {
if req.thingID == "" {
return errors.ErrMalformedEntity
}
if req.Relation == "" || len(req.UserIDs) <= 0 {
return errors.ErrCreateEntity
}
return nil
}

type thingUnshareRequest struct {
token string
thingID string
Relation string `json:"relation,omitempty"`
UserIDs []string `json:"user_ids,omitempty"`
}

func (req *thingUnshareRequest) validate() error {
if req.thingID == "" {
return errors.ErrMalformedEntity
}
if req.Relation == "" || len(req.UserIDs) <= 0 {
return errors.ErrCreateEntity
}
return nil
}
28 changes: 28 additions & 0 deletions things/api/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,31 @@ func (res disconnectChannelThingRes) Headers() map[string]string {
func (res disconnectChannelThingRes) Empty() bool {
return true
}

type thingShareRes struct{}

func (res thingShareRes) Code() int {
return http.StatusOK
}

func (res thingShareRes) Headers() map[string]string {
return map[string]string{}
}

func (res thingShareRes) Empty() bool {
return true
}

type thingUnshareRes struct{}

func (res thingUnshareRes) Code() int {
return http.StatusNoContent
}

func (res thingUnshareRes) Headers() map[string]string {
return map[string]string{}
}

func (res thingUnshareRes) Empty() bool {
return true
}
8 changes: 8 additions & 0 deletions things/events/streams.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,11 @@ func (es *eventStore) Identify(ctx context.Context, key string) (string, error)
func (es *eventStore) Authorize(ctx context.Context, req *mainflux.AuthorizeReq) (string, error) {
return es.svc.Authorize(ctx, req)
}

func (es *eventStore) Share(ctx context.Context, token, id string, relation string, userids ...string) error {
return es.svc.Share(ctx, token, id, relation, userids...)
}

func (es *eventStore) Unshare(ctx context.Context, token, id string, relation string, userids ...string) error {
return es.svc.Unshare(ctx, token, id, relation, userids...)
}
54 changes: 54 additions & 0 deletions things/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,60 @@ func (svc service) DisableClient(ctx context.Context, token, id string) (mfclien
return client, nil
}

func (svc service) Share(ctx context.Context, token, id, relation string, userids ...string) error {
_, err := svc.authorize(ctx, userType, tokenKind, token, ownerPermission, thingType, id)
if err != nil {
return err
}

for _, userid := range userids {

addPolicyReq := &mainflux.AddPolicyReq{
SubjectType: userType,
Subject: userid,
Relation: relation,
ObjectType: thingType,
Object: id,
}

res, err := svc.auth.AddPolicy(ctx, addPolicyReq)
if err != nil {
return err
}
if !res.Authorized {
return errors.ErrAuthorization
}
}
return nil
}

func (svc service) Unshare(ctx context.Context, token, id, relation string, userids ...string) error {
_, err := svc.authorize(ctx, userType, tokenKind, token, ownerPermission, thingType, id)
if err != nil {
return err
}

for _, userid := range userids {

delPolicyReq := &mainflux.DeletePolicyReq{
SubjectType: userType,
Subject: userid,
Relation: relation,
ObjectType: thingType,
Object: id,
}

res, err := svc.auth.DeletePolicy(ctx, delPolicyReq)
if err != nil {
return err
}
if !res.Deleted {
return errors.ErrAuthorization
}
}
return nil
}

func (svc service) changeClientStatus(ctx context.Context, token string, client mfclients.Client) (mfclients.Client, error) {
userID, err := svc.authorize(ctx, userType, tokenKind, token, deletePermission, thingType, client.ID)
if err != nil {
Expand Down
6 changes: 6 additions & 0 deletions things/things.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ type Service interface {
// DisableClient logically disables the client identified with the provided ID
DisableClient(ctx context.Context, token, id string) (clients.Client, error)

// Share add share policy to thing id with given relation for given user ids
Share(ctx context.Context, token, id string, relation string, userids ...string) error

// Unshare remove share policy to thing id with given relation for given user ids
Unshare(ctx context.Context, token, id string, relation string, userids ...string) error

// Identify returns thing ID for given thing key.
Identify(ctx context.Context, key string) (string, error)

Expand Down
14 changes: 14 additions & 0 deletions things/tracing/tracing.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,17 @@ func (tm *tracingMiddleware) Authorize(ctx context.Context, req *mainflux.Author
defer span.End()
return tm.svc.Authorize(ctx, req)
}

// Share traces the "Share" operation of the wrapped things.Service.
func (tm *tracingMiddleware) Share(ctx context.Context, token, id string, relation string, userids ...string) error {
ctx, span := tm.tracer.Start(ctx, "share", trace.WithAttributes(attribute.String("id", id), attribute.String("relation", relation), attribute.StringSlice("user_ids", userids)))
defer span.End()
return tm.svc.Share(ctx, token, id, relation, userids...)
}

// Unshare traces the "Unshare" operation of the wrapped things.Service.
func (tm *tracingMiddleware) Unshare(ctx context.Context, token, id string, relation string, userids ...string) error {
ctx, span := tm.tracer.Start(ctx, "unshare", trace.WithAttributes(attribute.String("id", id), attribute.String("relation", relation), attribute.StringSlice("user_ids", userids)))
defer span.End()
return tm.svc.Unshare(ctx, token, id, relation, userids...)
}

0 comments on commit 06f63c9

Please sign in to comment.