Skip to content

Commit

Permalink
MG-1977 - Check whether user is domain member before sending invitati…
Browse files Browse the repository at this point in the history
…on (#2213)

Signed-off-by: 1998-felix <[email protected]>
  • Loading branch information
felixgateru authored May 10, 2024
1 parent 3e63dfb commit 551d5cf
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 99 deletions.
15 changes: 12 additions & 3 deletions invitations/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ package invitations

import (
"context"
"errors"
"time"

"github.com/absmach/magistrala"
"github.com/absmach/magistrala/auth"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
mgsdk "github.com/absmach/magistrala/pkg/sdk/go"
)
Expand All @@ -20,6 +20,9 @@ type service struct {
sdk mgsdk.SDK
}

// ErrMemberExist indicates that the user is already a member of the domain.
var ErrMemberExist = errors.New("user is already a member of the domain")

func NewService(repo Repository, authClient magistrala.AuthServiceClient, sdk mgsdk.SDK) Service {
return &service{
repo: repo,
Expand All @@ -39,6 +42,12 @@ func (svc *service) SendInvitation(ctx context.Context, token string, invitation
}
invitation.InvitedBy = user.GetUserId()

domainUserId := auth.EncodeDomainUserID(invitation.DomainID, invitation.UserID)
if err := svc.authorize(ctx, domainUserId, auth.MembershipPermission, auth.DomainType, invitation.DomainID); err == nil {
// return error if the user is already a member of the domain
return errors.Wrap(svcerr.ErrConflict, ErrMemberExist)
}

if err := svc.checkAdmin(ctx, user.GetId(), invitation.DomainID); err != nil {
return err
}
Expand Down Expand Up @@ -184,11 +193,11 @@ func (svc *service) authorize(ctx context.Context, subj, perm, objType, obj stri
}
res, err := svc.auth.Authorize(ctx, req)
if err != nil {
return errors.Join(svcerr.ErrAuthorization, err)
return errors.Wrap(svcerr.ErrAuthorization, err)
}

if !res.GetAuthorized() {
return errors.Join(svcerr.ErrAuthorization, err)
return errors.Wrap(svcerr.ErrAuthorization, err)
}

return nil
Expand Down
225 changes: 129 additions & 96 deletions invitations/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/absmach/magistrala/internal/testsutil"
"github.com/absmach/magistrala/invitations"
"github.com/absmach/magistrala/invitations/mocks"
"github.com/absmach/magistrala/pkg/errors"
svcerr "github.com/absmach/magistrala/pkg/errors/service"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
Expand All @@ -36,95 +37,116 @@ func TestSendInvitation(t *testing.T) {
svc := invitations.NewService(repo, authsvc, nil)

cases := []struct {
desc string
token string
tokenUserID string
req invitations.Invitation
err error
authNErr error
domainErr error
adminErr error
authorised bool
issueErr error
repoErr error
desc string
token string
tokenUserID string
req invitations.Invitation
err error
authNErr error
domainMemberErr error
domainAdminErr error
adminErr error
authorised bool
issueErr error
repoErr error
}{
{
desc: "send invitation successful",
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
req: validInvitation,
err: nil,
authNErr: nil,
domainErr: nil,
adminErr: nil,
authorised: true,
issueErr: nil,
repoErr: nil,
},
{
desc: "invalid token",
token: "invalid",
tokenUserID: "",
req: validInvitation,
err: svcerr.ErrAuthentication,
authNErr: svcerr.ErrAuthentication,
domainErr: nil,
adminErr: nil,
authorised: false,
issueErr: nil,
repoErr: nil,
},
{
desc: "invalid relation",
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
req: invitations.Invitation{Relation: "invalid"},
err: apiutil.ErrInvalidRelation,
authNErr: nil,
domainErr: nil,
adminErr: nil,
authorised: false,
issueErr: nil,
repoErr: nil,
},
{
desc: "error during domain admin check",
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
req: validInvitation,
err: svcerr.ErrAuthorization,
authNErr: nil,
domainErr: svcerr.ErrAuthorization,
adminErr: nil,
authorised: false,
issueErr: nil,
repoErr: nil,
},
{
desc: "error during platform admin check",
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
req: validInvitation,
err: svcerr.ErrAuthorization,
authNErr: nil,
domainErr: svcerr.ErrAuthorization,
adminErr: svcerr.ErrAuthorization,
authorised: false,
issueErr: nil,
repoErr: nil,
},
{
desc: "error during token issuance",
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
req: validInvitation,
err: svcerr.ErrAuthentication,
authNErr: nil,
domainErr: nil,
adminErr: nil,
authorised: true,
issueErr: svcerr.ErrAuthentication,
repoErr: nil,
desc: "send invitation successful",
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
req: validInvitation,
err: nil,
authNErr: nil,
domainMemberErr: svcerr.ErrAuthorization,
domainAdminErr: nil,
adminErr: nil,
authorised: true,
issueErr: nil,
repoErr: nil,
},
{
desc: "existing domain member",
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
req: validInvitation,
err: errors.Wrap(svcerr.ErrConflict, invitations.ErrMemberExist),
authNErr: nil,
domainMemberErr: nil,
domainAdminErr: nil,
adminErr: nil,
authorised: true,
issueErr: nil,
repoErr: nil,
},
{
desc: "invalid token",
token: "invalid",
tokenUserID: "",
req: validInvitation,
err: svcerr.ErrAuthentication,
authNErr: svcerr.ErrAuthentication,
domainMemberErr: svcerr.ErrAuthorization,
domainAdminErr: nil,
adminErr: nil,
authorised: false,
issueErr: nil,
repoErr: nil,
},
{
desc: "invalid relation",
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
req: invitations.Invitation{Relation: "invalid"},
err: apiutil.ErrInvalidRelation,
authNErr: nil,
domainMemberErr: svcerr.ErrAuthorization,
domainAdminErr: nil,
adminErr: nil,
authorised: false,
issueErr: nil,
repoErr: nil,
},
{
desc: "error during domain admin check",
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
req: validInvitation,
err: svcerr.ErrAuthorization,
authNErr: nil,
domainMemberErr: svcerr.ErrAuthorization,
domainAdminErr: svcerr.ErrAuthorization,
adminErr: nil,
authorised: false,
issueErr: nil,
repoErr: nil,
},
{
desc: "error during platform admin check",
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
req: validInvitation,
err: svcerr.ErrAuthorization,
authNErr: nil,
domainMemberErr: svcerr.ErrAuthorization,
domainAdminErr: svcerr.ErrAuthorization,
adminErr: svcerr.ErrAuthorization,
authorised: false,
issueErr: nil,
repoErr: nil,
},
{
desc: "error during token issuance",
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
req: validInvitation,
err: svcerr.ErrAuthentication,
authNErr: nil,
domainMemberErr: svcerr.ErrAuthorization,
domainAdminErr: nil,
adminErr: nil,
authorised: true,
issueErr: svcerr.ErrAuthentication,
repoErr: nil,
},
{
desc: "resend invitation",
Expand All @@ -136,13 +158,14 @@ func TestSendInvitation(t *testing.T) {
Relation: auth.ViewerRelation,
Resend: true,
},
err: nil,
authNErr: nil,
domainErr: nil,
adminErr: nil,
authorised: true,
issueErr: nil,
repoErr: nil,
err: nil,
authNErr: nil,
domainMemberErr: svcerr.ErrAuthorization,
domainAdminErr: nil,
adminErr: nil,
authorised: true,
issueErr: nil,
repoErr: nil,
},
}

Expand All @@ -151,16 +174,25 @@ func TestSendInvitation(t *testing.T) {
UserId: tc.tokenUserID,
Id: testsutil.GenerateUUID(t) + "_" + tc.tokenUserID,
}
domainMemberReq := magistrala.AuthorizeReq{
SubjectType: auth.UserType,
SubjectKind: auth.UsersKind,
Subject: auth.EncodeDomainUserID(tc.req.DomainID, tc.req.UserID),
Permission: auth.MembershipPermission,
ObjectType: auth.DomainType,
Object: tc.req.DomainID,
}
domaincall := authsvc.On("Authorize", context.Background(), &domainMemberReq).Return(&magistrala.AuthorizeRes{Authorized: tc.authorised}, tc.domainMemberErr)
repocall := authsvc.On("Identify", context.Background(), &magistrala.IdentityReq{Token: tc.token}).Return(idRes, tc.authNErr)
domainReq := magistrala.AuthorizeReq{
domainAdminReq := magistrala.AuthorizeReq{
SubjectType: auth.UserType,
SubjectKind: auth.UsersKind,
Subject: idRes.GetId(),
Permission: auth.AdminPermission,
ObjectType: auth.DomainType,
Object: tc.req.DomainID,
}
domaincall := authsvc.On("Authorize", context.Background(), &domainReq).Return(&magistrala.AuthorizeRes{Authorized: tc.authorised}, tc.domainErr)
domaincall1 := authsvc.On("Authorize", context.Background(), &domainAdminReq).Return(&magistrala.AuthorizeRes{Authorized: tc.authorised}, tc.domainAdminErr)
platformReq := magistrala.AuthorizeReq{
SubjectType: auth.UserType,
SubjectKind: auth.UsersKind,
Expand All @@ -179,6 +211,7 @@ func TestSendInvitation(t *testing.T) {
assert.Equal(t, tc.err, err, tc.desc)
repocall.Unset()
domaincall.Unset()
domaincall1.Unset()
platformcall.Unset()
repocall1.Unset()
repocall2.Unset()
Expand Down

0 comments on commit 551d5cf

Please sign in to comment.