From 3355332ebdcc8bfd0b35ef8915a3734462f40a3f Mon Sep 17 00:00:00 2001 From: Steve Munene <61874077+nyagamunene@users.noreply.github.com> Date: Wed, 12 Jun 2024 17:19:06 +0300 Subject: [PATCH] MG-2075 - Add guest relation (#2228) Signed-off-by: nyagamunene --- api/openapi/auth.yml | 6 +++--- api/openapi/invitations.yml | 3 +++ auth/policies.go | 6 ++++-- auth/service.go | 6 ++++-- auth/service_test.go | 5 +++++ docker/spicedb/schema.zed | 10 +++++++--- internal/groups/service.go | 2 +- internal/groups/service_test.go | 2 +- invitations/invitations.go | 1 + invitations/invitations_test.go | 1 + pkg/sdk/go/sdk.go | 32 ++++++++++++++++---------------- things/service.go | 2 +- 12 files changed, 47 insertions(+), 29 deletions(-) diff --git a/api/openapi/auth.yml b/api/openapi/auth.yml index 56a547347c..f9c6acfa97 100644 --- a/api/openapi/auth.yml +++ b/api/openapi/auth.yml @@ -613,7 +613,7 @@ components: UserDomainRelationReq: type: object properties: - users_ids: + user_ids: type: array minItems: 1 items: @@ -626,11 +626,11 @@ components: ] relation: type: string - enum: ["administrator", "editor", "contributor", "member"] + enum: ["administrator", "editor", "contributor", "member", "guest"] example: "administrator" description: Policy relations. required: - - users_ids + - user_ids - relation Key: type: object diff --git a/api/openapi/invitations.yml b/api/openapi/invitations.yml index 321249c8a6..dd84d3f004 100644 --- a/api/openapi/invitations.yml +++ b/api/openapi/invitations.yml @@ -199,6 +199,7 @@ components: - editor - contributor - member + - guest - domain - parent_group - role_group @@ -240,6 +241,7 @@ components: - editor - contributor - member + - guest - domain - parent_group - role_group @@ -408,6 +410,7 @@ components: - editor - contributor - member + - guest - domain - parent_group - role_group diff --git a/auth/policies.go b/auth/policies.go index 040177b8c7..e3464e508b 100644 --- a/auth/policies.go +++ b/auth/policies.go @@ -39,6 +39,7 @@ const ( RoleGroupRelation = "role_group" GroupRelation = "group" PlatformRelation = "platform" + GuestRelation = "guest" ) const ( @@ -50,6 +51,7 @@ const ( SharePermission = "share" PublishPermission = "publish" SubscribePermission = "subscribe" + CreatePermission = "create" ) const MagistralaObject = "magistrala" @@ -86,10 +88,10 @@ type PolicyReq struct { // platform, group, domain, thing, users. ObjectType string `json:"object_type"` - // Relation contains the relation. Supported relations are administrator, editor, contributor, member,parent_group,group,domain. + // Relation contains the relation. Supported relations are administrator, editor, contributor, member, guest, parent_group,group,domain. Relation string `json:"relation,omitempty"` - // Permission contains the permission. Supported permissions are admin, delete, edit, share, view, membership, + // Permission contains the permission. Supported permissions are admin, delete, edit, share, view, membership, create. // admin_only, edit_only, viewer_only, membership_only, ext_admin, ext_edit, ext_view. Permission string `json:"permission,omitempty"` } diff --git a/auth/service.go b/auth/service.go index 4ba816a79e..b7003eba93 100644 --- a/auth/service.go +++ b/auth/service.go @@ -575,6 +575,8 @@ func SwitchToPermission(relation string) string { return ViewPermission case MemberRelation: return MembershipPermission + case GuestRelation: + return ViewPermission default: return relation } @@ -661,7 +663,7 @@ func (svc service) RetrieveDomainPermissions(ctx context.Context, token, id stri Subject: res.Subject, Object: id, ObjectType: DomainType, - }, []string{AdminPermission, EditPermission, ViewPermission, MembershipPermission}) + }, []string{AdminPermission, EditPermission, ViewPermission, MembershipPermission, CreatePermission}) if err != nil { return []string{}, errors.Wrap(svcerr.ErrViewEntity, err) } @@ -824,7 +826,7 @@ func (svc service) UnassignUsers(ctx context.Context, token, id string, userIds userIds = ids } - for _, rel := range []string{MemberRelation, ContributorRelation, EditorRelation} { + for _, rel := range []string{MemberRelation, ContributorRelation, EditorRelation, GuestRelation} { // Remove only non-admins. if err := svc.removeDomainPolicies(ctx, id, rel, userIds...); err != nil { return err diff --git a/auth/service_test.go b/auth/service_test.go index b50d625735..e8ff314a07 100644 --- a/auth/service_test.go +++ b/auth/service_test.go @@ -1690,6 +1690,11 @@ func TestSwitchToPermission(t *testing.T) { relation: auth.GroupRelation, result: auth.GroupRelation, }, + { + desc: "switch to guest permission", + relation: auth.GuestRelation, + result: auth.ViewPermission, + }, } for _, tc := range cases { result := auth.SwitchToPermission(tc.relation) diff --git a/docker/spicedb/schema.zed b/docker/spicedb/schema.zed index a6b28d9ed3..3a50e88ae7 100644 --- a/docker/spicedb/schema.zed +++ b/docker/spicedb/schema.zed @@ -27,6 +27,7 @@ definition group { relation editor: user relation contributor: user relation member: user + relation guest: user relation parent_group: group relation domain: domain @@ -35,8 +36,9 @@ definition group { permission delete = admin permission edit = admin + editor + parent_group->edit + domain->edit permission share = edit - permission view = contributor + edit + parent_group->view + domain->view + permission view = contributor + edit + parent_group->view + domain->view + guest permission membership = view + member + permission create = membership - guest // These permissions are made for listing purposes. They enable listing users who have only particular permission excluding higher-level permissions users. permission admin_only = admin @@ -55,14 +57,16 @@ definition domain { relation editor: user relation contributor: user relation member: user + relation guest: user relation platform: platform permission admin = administrator + platform->admin permission edit = admin + editor permission share = edit - permission view = edit + contributor - permission membership = view + member + permission view = edit + contributor + guest + permission membership = view + member + permission create = membership - guest } definition platform { diff --git a/internal/groups/service.go b/internal/groups/service.go index 439cacf972..da936c7e5b 100644 --- a/internal/groups/service.go +++ b/internal/groups/service.go @@ -45,7 +45,7 @@ func (svc service) CreateGroup(ctx context.Context, token, kind string, g groups return groups.Group{}, err } // If domain is disabled , then this authorization will fail for all non-admin domain users - if _, err := svc.authorizeKind(ctx, "", auth.UserType, auth.UsersKind, res.GetId(), auth.MembershipPermission, auth.DomainType, res.GetDomainId()); err != nil { + if _, err := svc.authorizeKind(ctx, "", auth.UserType, auth.UsersKind, res.GetId(), auth.CreatePermission, auth.DomainType, res.GetDomainId()); err != nil { return groups.Group{}, err } groupID, err := svc.idProvider.ID() diff --git a/internal/groups/service_test.go b/internal/groups/service_test.go index bef663d0f5..f85ffbc4eb 100644 --- a/internal/groups/service_test.go +++ b/internal/groups/service_test.go @@ -292,7 +292,7 @@ func TestCreateGroup(t *testing.T) { SubjectType: auth.UserType, SubjectKind: auth.UsersKind, Subject: tc.idResp.GetId(), - Permission: auth.MembershipPermission, + Permission: auth.CreatePermission, Object: tc.idResp.GetDomainId(), ObjectType: auth.DomainType, }).Return(tc.authzResp, tc.authzErr) diff --git a/invitations/invitations.go b/invitations/invitations.go index 8ed3e960ea..bafa8bde92 100644 --- a/invitations/invitations.go +++ b/invitations/invitations.go @@ -126,6 +126,7 @@ func CheckRelation(relation string) error { relation != auth.EditorRelation && relation != auth.ContributorRelation && relation != auth.MemberRelation && + relation != auth.GuestRelation && relation != auth.DomainRelation && relation != auth.ParentGroupRelation && relation != auth.RoleGroupRelation && diff --git a/invitations/invitations_test.go b/invitations/invitations_test.go index f05d5eb90a..b6c38e2fd9 100644 --- a/invitations/invitations_test.go +++ b/invitations/invitations_test.go @@ -60,6 +60,7 @@ func TestCheckRelation(t *testing.T) { {"editor", nil}, {"contributor", nil}, {"member", nil}, + {"guest", nil}, {"domain", nil}, {"parent_group", nil}, {"role_group", nil}, diff --git a/pkg/sdk/go/sdk.go b/pkg/sdk/go/sdk.go index d2527bd8d9..39ad90273a 100644 --- a/pkg/sdk/go/sdk.go +++ b/pkg/sdk/go/sdk.go @@ -280,7 +280,7 @@ type SDK interface { // pm := sdk.PageMetadata{ // Offset: 0, // Limit: 10, - // Permission: "edit", // available Options: "administrator", "delete", edit", "view", "share", "owner", "admin", "editor", "contributor" + // Permission: "edit", // available Options: "administrator", "administrator", "delete", edit", "view", "share", "owner", "owner", "admin", "editor", "viewer", "guest", "editor", "contributor", "create" // } // channels, _ := sdk.ListUserChannels("user_id_1", pm, "token") // fmt.Println(channels) @@ -292,7 +292,7 @@ type SDK interface { // pm := sdk.PageMetadata{ // Offset: 0, // Limit: 10, - // Permission: "edit", // available Options: "administrator", "delete", edit", "view", "share", "owner", "admin", "editor", "contributor" + // Permission: "edit", // available Options: "administrator", "administrator", "delete", edit", "view", "share", "owner", "owner", "admin", "editor", "contributor", "editor", "viewer", "guest", "create" // } // groups, _ := sdk.ListUserGroups("user_id_1", pm, "token") // fmt.Println(channels) @@ -304,7 +304,7 @@ type SDK interface { // pm := sdk.PageMetadata{ // Offset: 0, // Limit: 10, - // Permission: "edit", // available Options: "administrator", "delete", edit", "view", "share", "owner", "admin", "editor", "contributor" + // Permission: "edit", // available Options: "administrator", "administrator", "delete", edit", "view", "share", "owner", "owner", "admin", "editor", "contributor", "editor", "viewer", "guest", "create" // } // things, _ := sdk.ListUserThings("user_id_1", pm, "token") // fmt.Println(things) @@ -439,7 +439,7 @@ type SDK interface { // // example: // req := sdk.UsersRelationRequest{ - // Relation: "contributor", // available options: "owner", "admin", "editor", "contributor" + // Relation: "contributor", // available options: "owner", "admin", "editor", "contributor", "guest" // UserIDs: ["user_id_1", "user_id_2", "user_id_3"] // } // err := sdk.ShareThing("thing_id", req, "token") @@ -450,7 +450,7 @@ type SDK interface { // // example: // req := sdk.UsersRelationRequest{ - // Relation: "contributor", // available options: "owner", "admin", "editor", "contributor" + // Relation: "contributor", // available options: "owner", "admin", "editor", "contributor", "guest" // UserIDs: ["user_id_1", "user_id_2", "user_id_3"] // } // err := sdk.UnshareThing("thing_id", req, "token") @@ -463,7 +463,7 @@ type SDK interface { // pm := sdk.PageMetadata{ // Offset: 0, // Limit: 10, - // Permission: "edit", // available Options: "administrator", "delete", edit", "view", "share", "owner", "admin", "editor", "contributor" + // Permission: "edit", // available Options: "administrator", "administrator", "delete", edit", "view", "share", "owner", "owner", "admin", "editor", "contributor", "editor", "viewer", "guest", "create" // } // users, _ := sdk.ListThingUsers("thing_id", pm, "token") // fmt.Println(users) @@ -571,7 +571,7 @@ type SDK interface { // // example: // req := sdk.UsersRelationRequest{ - // Relation: "contributor", // available options: "owner", "admin", "editor", "contributor" + // Relation: "contributor", // available options: "owner", "admin", "editor", "contributor", "guest" // UserIDs: ["user_id_1", "user_id_2", "user_id_3"] // } // err := sdk.AddUserToGroup("groupID",req, "token") @@ -582,7 +582,7 @@ type SDK interface { // // example: // req := sdk.UsersRelationRequest{ - // Relation: "contributor", // available options: "owner", "admin", "editor", "contributor" + // Relation: "contributor", // available options: "owner", "admin", "editor", "contributor", "guest" // UserIDs: ["user_id_1", "user_id_2", "user_id_3"] // } // err := sdk.RemoveUserFromGroup("groupID",req, "token") @@ -595,7 +595,7 @@ type SDK interface { // pm := sdk.PageMetadata{ // Offset: 0, // Limit: 10, - // Permission: "edit", // available Options: "administrator", "delete", edit", "view", "share", "owner", "admin", "editor", "contributor" + // Permission: "edit", // available Options: "administrator", "administrator", "delete", edit", "view", "share", "owner", "owner", "admin", "editor", "contributor", "editor", "viewer", "guest", "create" // } // groups, _ := sdk.ListGroupUsers("groupID", pm, "token") // fmt.Println(groups) @@ -607,7 +607,7 @@ type SDK interface { // pm := sdk.PageMetadata{ // Offset: 0, // Limit: 10, - // Permission: "edit", // available Options: "administrator", "delete", edit", "view", "share", "owner", "admin", "editor", "contributor" + // Permission: "edit", // available Options: "administrator", "administrator", "delete", edit", "view", "share", "owner", "owner", "admin", "editor", "contributor", "editor", "viewer", "guest", "create" // } // groups, _ := sdk.ListGroupChannels("groupID", pm, "token") // fmt.Println(groups) @@ -703,7 +703,7 @@ type SDK interface { // // example: // req := sdk.UsersRelationRequest{ - // Relation: "contributor", // available options: "owner", "admin", "editor", "contributor" + // Relation: "contributor", // available options: "owner", "admin", "editor", "contributor", "guest" // UserIDs: ["user_id_1", "user_id_2", "user_id_3"] // } // err := sdk.AddUserToChannel("channel_id", req, "token") @@ -714,7 +714,7 @@ type SDK interface { // // example: // req := sdk.UsersRelationRequest{ - // Relation: "contributor", // available options: "owner", "admin", "editor", "contributor" + // Relation: "contributor", // available options: "owner", "admin", "editor", "contributor", "guest" // UserIDs: ["user_id_1", "user_id_2", "user_id_3"] // } // err := sdk.RemoveUserFromChannel("channel_id", req, "token") @@ -727,7 +727,7 @@ type SDK interface { // pm := sdk.PageMetadata{ // Offset: 0, // Limit: 10, - // Permission: "edit", // available Options: "administrator", "delete", edit", "view", "share", "owner", "admin", "editor", "contributor" + // Permission: "edit", // available Options: "administrator", "administrator", "delete", edit", "view", "share", "owner", "owner", "admin", "editor", "contributor", "editor", "viewer", "guest", "create" // } // users, _ := sdk.ListChannelUsers("channel_id", pm, "token") // fmt.Println(users) @@ -1096,7 +1096,7 @@ type SDK interface { // // example: // req := sdk.UsersRelationRequest{ - // Relation: "contributor", // available options: "owner", "admin", "editor", "contributor", "member" + // Relation: "contributor", // available options: "owner", "admin", "editor", "contributor", "member", "guest" // UserIDs: ["user_id_1", "user_id_2", "user_id_3"] // } // err := sdk.AddUserToDomain("domainID", req, "token") @@ -1107,7 +1107,7 @@ type SDK interface { // // example: // req := sdk.UsersRelationRequest{ - // Relation: "contributor", // available options: "owner", "admin", "editor", "contributor" , "member" + // Relation: "contributor", // available options: "owner", "admin", "editor", "contributor" , "member", "guest" // UserIDs: ["user_id_1", "user_id_2", "user_id_3"] // } // err := sdk.RemoveUserFromDomain("domainID", req, "token") @@ -1120,7 +1120,7 @@ type SDK interface { // invitation := sdk.Invitation{ // DomainID: "domainID", // UserID: "userID", - // Relation: "contributor", // available options: "owner", "admin", "editor", "contributor" + // Relation: "contributor", // available options: "owner", "admin", "editor", "contributor", "guest" // } // err := sdk.SendInvitation(invitation, "token") // fmt.Println(err) diff --git a/things/service.go b/things/service.go index c5c4623d3a..741d2550ec 100644 --- a/things/service.go +++ b/things/service.go @@ -65,7 +65,7 @@ func (svc service) CreateThings(ctx context.Context, token string, cls ...mgclie return []mgclients.Client{}, err } // If domain is disabled , then this authorization will fail for all non-admin domain users - if _, err := svc.authorize(ctx, "", auth.UserType, auth.UsersKind, user.GetId(), auth.MembershipPermission, auth.DomainType, user.GetDomainId()); err != nil { + if _, err := svc.authorize(ctx, "", auth.UserType, auth.UsersKind, user.GetId(), auth.CreatePermission, auth.DomainType, user.GetDomainId()); err != nil { return []mgclients.Client{}, err }