diff --git a/services/graph/pkg/service/v0/api_driveitem_permissions.go b/services/graph/pkg/service/v0/api_driveitem_permissions.go index 1896cdb27b..5ca847738f 100644 --- a/services/graph/pkg/service/v0/api_driveitem_permissions.go +++ b/services/graph/pkg/service/v0/api_driveitem_permissions.go @@ -185,6 +185,48 @@ func (s DriveItemPermissionsService) Invite(ctx context.Context, resourceId *sto shareid = createShareResponse.GetShare().GetId().GetOpaqueId() cTime = createShareResponse.GetShare().GetCtime() expiration = createShareResponse.GetShare().GetExpiration() + case "mail": + email := strings.TrimSpace(objectID) + if len(email) == 0 { + return libregraph.Permission{}, errorcode.New(errorcode.InvalidRequest, "invalid mail recipient") + } + + createShareRequest := &collaboration.CreateShareRequest{ + ResourceInfo: statResponse.GetInfo(), + Grant: &collaboration.ShareGrant{ + Grantee: &storageprovider.Grantee{ + Type: storageprovider.GranteeType_GRANTEE_TYPE_MAIL, + Id: &storageprovider.Grantee_Mail{ + Mail: strings.ToLower(email), + }, + }, + Permissions: &collaboration.SharePermissions{ + Permissions: cs3ResourcePermissions, + }, + }, + } + if invite.ExpirationDateTime != nil { + createShareRequest.GetGrant().Expiration = utils.TimeToTS(*invite.ExpirationDateTime) + } + createShareResponse, err := gatewayClient.CreateShare(ctx, createShareRequest) + if err := errorcode.FromCS3Status(createShareResponse.GetStatus(), err); err != nil { + s.logger.Debug().Err(err).Msg("share creation failed") + return libregraph.Permission{}, err + } + shareid = createShareResponse.GetShare().GetId().GetOpaqueId() + cTime = createShareResponse.GetShare().GetCtime() + expiration = createShareResponse.GetShare().GetExpiration() + + identity := &libregraph.Identity{ + Id: conversions.ToPointer(email), + DisplayName: email, + LibreGraphUserType: conversions.ToPointer("Mail"), + } + + permission.GrantedToV2 = &libregraph.SharePointIdentitySet{ + User: identity, + } + default: user, err := s.identityCache.GetCS3User(ctx, tenantId, objectID) if errors.Is(err, identity.ErrNotFound) && s.config.IncludeOCMSharees { diff --git a/services/graph/pkg/service/v0/api_driveitem_permissions_test.go b/services/graph/pkg/service/v0/api_driveitem_permissions_test.go index 4a088664dd..31237768c1 100644 --- a/services/graph/pkg/service/v0/api_driveitem_permissions_test.go +++ b/services/graph/pkg/service/v0/api_driveitem_permissions_test.go @@ -167,6 +167,34 @@ var _ = Describe("DriveItemPermissionsService", func() { Expect(permission.GrantedToV2.Group.GetId()).To(Equal("2")) }) + It("creates guest share using an email address", func() { + gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) + gatewayClient.On("CreateShare", mock.Anything, mock.Anything).Return(createShareResponse, nil) + driveItemInvite.Recipients = []libregraph.DriveRecipient{ + {ObjectId: libregraph.PtrString("guest@example.com"), LibreGraphRecipientType: libregraph.PtrString("mail")}, + } + createShareResponse.Share = &collaboration.Share{ + Id: &collaboration.ShareId{OpaqueId: "guest123"}, + } + + permission, err := driveItemPermissionsService.Invite(ctx, driveItemId, driveItemInvite) + Expect(err).ToNot(HaveOccurred()) + Expect(permission.GetId()).To(Equal("guest123")) + Expect(permission.GrantedToV2.User.GetDisplayName()).To(Equal("guest@example.com")) + Expect(permission.GrantedToV2.User.GetId()).To(Equal("guest@example.com")) + Expect(permission.GrantedToV2.User.GetLibreGraphUserType()).To(Equal("Mail")) + }) + + It("verifies that empty email addresses are handled", func() { + driveItemInvite.Recipients = []libregraph.DriveRecipient{ + {ObjectId: libregraph.PtrString(" "), LibreGraphRecipientType: libregraph.PtrString("mail")}, + } + + _, err := driveItemPermissionsService.Invite(ctx, driveItemId, driveItemInvite) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("invalid mail recipient")) + }) + It("succeeds with file roles (happy path)", func() { gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) gatewayClient.On("CreateShare", mock.Anything, mock.Anything).Return(createShareResponse, nil) @@ -326,6 +354,7 @@ var _ = Describe("DriveItemPermissionsService", func() { gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listSpacesResponse, nil) gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(statResponse, nil) + gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) gatewayClient.On("CreateShare", mock.Anything, mock.Anything).Return(createShareResponse, nil) driveItemInvite.Recipients = []libregraph.DriveRecipient{ {ObjectId: libregraph.PtrString("1"), LibreGraphRecipientType: libregraph.PtrString("user")}, diff --git a/services/graph/pkg/service/v0/base.go b/services/graph/pkg/service/v0/base.go index fbbfa1edfe..720d266c7c 100644 --- a/services/graph/pkg/service/v0/base.go +++ b/services/graph/pkg/service/v0/base.go @@ -7,6 +7,7 @@ import ( "fmt" "net/url" "path" + "strings" "time" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" @@ -274,6 +275,7 @@ func (g BaseGraphService) listSharesWithSpaceRootFilter(ctx context.Context, spa share.UserGranteeFilter(), share.GroupGranteeFilter(), share.SpaceRootFilter(spaceRoot), + share.MailGranteeFilter(), } concreteFilters = append(concreteFilters, filters...) @@ -585,6 +587,14 @@ func (g BaseGraphService) cs3UserShareToPermission(ctx context.Context, share *c default: grantedTo.SetGroup(group) } + case storageprovider.GranteeType_GRANTEE_TYPE_MAIL: + grantedTo.SetUser(libregraph.Identity{ + Id: libregraph.PtrString("mail:" + strings.ToLower(share.Grantee.GetMail())), + DisplayName: share.Grantee.GetMail(), + }) + if roleCondition == unifiedrole.UnifiedRoleConditionDrive { + perm.SetId("m:" + strings.ToLower(share.Grantee.GetMail())) + } } // set expiration date @@ -667,6 +677,13 @@ func (g BaseGraphService) cs3OCMShareToPermission(ctx context.Context, share *oc perm.SetId("g:" + group.GetId()) } } + case storageprovider.GranteeType_GRANTEE_TYPE_MAIL: + grantedTo.SetUser(libregraph.Identity{ + Id: libregraph.PtrString(share.Grantee.GetMail()), + }) + if roleCondition == unifiedrole.UnifiedRoleConditionDrive { + perm.SetId("m:" + share.Grantee.GetMail()) + } } // set expiration date diff --git a/services/graph/pkg/validate/libregraph.go b/services/graph/pkg/validate/libregraph.go index 572427b6b1..77fc98586f 100644 --- a/services/graph/pkg/validate/libregraph.go +++ b/services/graph/pkg/validate/libregraph.go @@ -47,7 +47,7 @@ func libregraphDriveItemInvite(v *validator.Validate) { func libregraphDriveRecipient(v *validator.Validate) { v.RegisterStructValidationMapRules(map[string]string{ "ObjectId": "ne=", - "LibreGraphRecipientType": "oneof=user group", + "LibreGraphRecipientType": "oneof=user group mail", }, libregraph.DriveRecipient{}) }