Files: added invitation by link without email, added existing user to room by link

This commit is contained in:
Maksim Chegulov 2022-06-16 18:39:50 +03:00
parent dad4b12985
commit 34d55db6a6
8 changed files with 150 additions and 40 deletions

View File

@ -0,0 +1,33 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Files.Core.ApiModels.RequestDto;
public class InviteLinkDto
{
public FileShare Access { get; set; }
public EmployeeType EmployeeType { get; set; }
}

View File

@ -31,4 +31,6 @@ public class SecurityInfoRequestDto : BaseBatchRequestDto
public IEnumerable<FileShareParams> Share { get; set; }
public bool Notify { get; set; }
public string SharingMessage { get; set; }
public FileShare Access { get; set; }
public string Key { get; set; }
}

View File

@ -2133,9 +2133,9 @@ public class FileStorageService<T> //: IFileStorageService
#endregion
public Task<List<AceWrapper>> GetSharedInfoAsync(IEnumerable<T> fileIds, IEnumerable<T> folderIds)
public Task<List<AceWrapper>> GetSharedInfoAsync(IEnumerable<T> fileIds, IEnumerable<T> folderIds, bool invite = false)
{
return _fileSharing.GetSharedInfoAsync(fileIds, folderIds);
return _fileSharing.GetSharedInfoAsync(fileIds, folderIds, invite);
}
public Task<List<AceShortWrapper>> GetSharedInfoShortFileAsync(T fileId)
@ -2148,7 +2148,7 @@ public class FileStorageService<T> //: IFileStorageService
return _fileSharing.GetSharedInfoShortFolderAsync(folderId);
}
public async Task<List<T>> SetAceObjectAsync(AceCollection<T> aceCollection, bool notify)
public async Task<List<T>> SetAceObjectAsync(AceCollection<T> aceCollection, bool notify, bool invite = false)
{
var fileDao = GetFileDao();
var folderDao = GetFolderDao();
@ -2170,7 +2170,7 @@ public class FileStorageService<T> //: IFileStorageService
{
try
{
var changed = await _fileSharingAceHelper.SetAceObjectAsync(aceCollection.Aces, entry, notify, aceCollection.Message, !_coreBaseSettings.DisableDocSpace);
var changed = await _fileSharingAceHelper.SetAceObjectAsync(aceCollection.Aces, entry, notify, aceCollection.Message, !_coreBaseSettings.DisableDocSpace, invite);
if (changed)
{
if (entry.FileEntryType == FileEntryType.Folder && DocSpaceHelper.IsRoom(((Folder<T>)entry).FolderType))

View File

@ -72,14 +72,14 @@ public class FileSharingAceHelper<T>
_fileSecurityCommon = fileSecurityCommon;
}
public async Task<bool> SetAceObjectAsync(List<AceWrapper> aceWrappers, FileEntry<T> entry, bool notify, string message, bool handleForRooms = false)
public async Task<bool> SetAceObjectAsync(List<AceWrapper> aceWrappers, FileEntry<T> entry, bool notify, string message, bool handleForRooms = false, bool invite = false)
{
if (entry == null)
{
throw new ArgumentNullException(FilesCommonResource.ErrorMassage_BadRequest);
}
if (!await _fileSharingHelper.CanSetAccessAsync(entry))
if (!await _fileSharingHelper.CanSetAccessAsync(entry, invite))
{
throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException);
}
@ -91,7 +91,7 @@ public class FileSharingAceHelper<T>
var usersWithoutRight = new List<Guid>();
var changed = false;
aceWrappers = handleForRooms ? await FilterForRoomsAsync(entry, aceWrappers) : aceWrappers;
aceWrappers = handleForRooms ? await FilterForRoomsAsync(entry, aceWrappers, invite) : aceWrappers;
foreach (var w in aceWrappers.OrderByDescending(ace => ace.SubjectGroup))
{
@ -231,9 +231,9 @@ public class FileSharingAceHelper<T>
});
}
private async Task<List<AceWrapper>> FilterForRoomsAsync(FileEntry<T> entry, List<AceWrapper> aceWrappers)
private async Task<List<AceWrapper>> FilterForRoomsAsync(FileEntry<T> entry, List<AceWrapper> aceWrappers, bool invite)
{
if (entry.FileEntryType == FileEntryType.File || entry.RootFolderType == FolderType.Archive)
if (entry.FileEntryType == FileEntryType.File || entry.RootFolderType == FolderType.Archive || invite)
{
return aceWrappers;
}
@ -248,7 +248,7 @@ public class FileSharingAceHelper<T>
var result = new List<AceWrapper>(aceWrappers.Count);
var isAdmin = _fileSecurityCommon.IsAdministrator(_authContext.CurrentAccount.ID);
var isRoomManager = !isAdmin ? await _fileSecurity.CanEditRoomAsync(entry) : true;
var isRoomManager = isAdmin || await _fileSecurity.CanEditRoomAsync(entry);
foreach (var ace in aceWrappers)
{
@ -312,8 +312,13 @@ public class FileSharingHelper
private readonly AuthContext _authContext;
private readonly UserManager _userManager;
public async Task<bool> CanSetAccessAsync<T>(FileEntry<T> entry)
public async Task<bool> CanSetAccessAsync<T>(FileEntry<T> entry, bool invite = false)
{
if (invite)
{
return true;
}
var folder = entry as Folder<T>;
return
@ -366,19 +371,19 @@ public class FileSharing
_logger = logger;
}
public Task<bool> CanSetAccessAsync<T>(FileEntry<T> entry)
public Task<bool> CanSetAccessAsync<T>(FileEntry<T> entry, bool invite = false)
{
return _fileSharingHelper.CanSetAccessAsync(entry);
return _fileSharingHelper.CanSetAccessAsync(entry, invite);
}
public async Task<List<AceWrapper>> GetSharedInfoAsync<T>(FileEntry<T> entry)
public async Task<List<AceWrapper>> GetSharedInfoAsync<T>(FileEntry<T> entry, bool invite = false)
{
if (entry == null)
{
throw new ArgumentNullException(FilesCommonResource.ErrorMassage_BadRequest);
}
if (!await CanSetAccessAsync(entry))
if (!await CanSetAccessAsync(entry, invite))
{
_logger.ErrorUserCanTGetSharedInfo(_authContext.CurrentAccount.ID, entry.FileEntryType, entry.Id.ToString());
@ -525,7 +530,7 @@ public class FileSharing
return result;
}
public async Task<List<AceWrapper>> GetSharedInfoAsync<T>(IEnumerable<T> fileIds, IEnumerable<T> folderIds)
public async Task<List<AceWrapper>> GetSharedInfoAsync<T>(IEnumerable<T> fileIds, IEnumerable<T> folderIds, bool invite = false)
{
if (!_authContext.IsAuthenticated)
{
@ -547,7 +552,7 @@ public class FileSharing
IEnumerable<AceWrapper> acesForObject;
try
{
acesForObject = await GetSharedInfoAsync(entry);
acesForObject = await GetSharedInfoAsync(entry, invite);
}
catch (Exception e)
{

View File

@ -29,7 +29,7 @@ namespace ASC.Files.Api;
[ConstraintRoute("int")]
public class VirtualRoomsInternalController : VirtualRoomsController<int>
{
public VirtualRoomsInternalController(FoldersControllerHelper<int> foldersControllerHelper, GlobalFolderHelper globalFolderHelper, FileOperationDtoHelper fileOperationDtoHelper, SecurityControllerHelper<int> securityControllerHelper, CoreBaseSettings coreBaseSettings, AuthContext authContext, RoomLinksService roomLinksManager, CustomTagsService<int> customTagsService, RoomLogoManager roomLogoManager, FileStorageService<int> fileStorageService, FolderDtoHelper folderDtoHelper) : base(foldersControllerHelper, globalFolderHelper, fileOperationDtoHelper, securityControllerHelper, coreBaseSettings, authContext, roomLinksManager, customTagsService, roomLogoManager, fileStorageService, folderDtoHelper)
public VirtualRoomsInternalController(FoldersControllerHelper<int> foldersControllerHelper, GlobalFolderHelper globalFolderHelper, FileOperationDtoHelper fileOperationDtoHelper, SecurityControllerHelper<int> securityControllerHelper, CoreBaseSettings coreBaseSettings, AuthContext authContext, RoomInvitationLinksService roomLinksService, CustomTagsService<int> customTagsService, RoomLogoManager roomLogoManager, StudioNotifyService studioNotifyService, FileStorageService<int> fileStorageService, FolderDtoHelper folderDtoHelper, FileSecurity fileSecurity, EmailValidationKeyProvider emailValidationKeyProvider) : base(foldersControllerHelper, globalFolderHelper, fileOperationDtoHelper, securityControllerHelper, coreBaseSettings, authContext, roomLinksService, customTagsService, roomLogoManager, studioNotifyService, fileStorageService, folderDtoHelper, fileSecurity, emailValidationKeyProvider)
{
}
@ -46,7 +46,7 @@ public class VirtualRoomsInternalController : VirtualRoomsController<int>
public class VirtualRoomsThirdpartyController : VirtualRoomsController<string>
{
public VirtualRoomsThirdpartyController(FoldersControllerHelper<string> foldersControllerHelper, GlobalFolderHelper globalFolderHelper, FileOperationDtoHelper fileOperationDtoHelper, SecurityControllerHelper<string> securityControllerHelper, CoreBaseSettings coreBaseSettings, AuthContext authContext, RoomLinksService roomLinksManager, CustomTagsService<string> customTagsService, RoomLogoManager roomLogoManager, FileStorageService<string> fileStorageService, FolderDtoHelper folderDtoHelper) : base(foldersControllerHelper, globalFolderHelper, fileOperationDtoHelper, securityControllerHelper, coreBaseSettings, authContext, roomLinksManager, customTagsService, roomLogoManager, fileStorageService, folderDtoHelper)
public VirtualRoomsThirdpartyController(FoldersControllerHelper<string> foldersControllerHelper, GlobalFolderHelper globalFolderHelper, FileOperationDtoHelper fileOperationDtoHelper, SecurityControllerHelper<string> securityControllerHelper, CoreBaseSettings coreBaseSettings, AuthContext authContext, RoomInvitationLinksService roomLinksService, CustomTagsService<string> customTagsService, RoomLogoManager roomLogoManager, StudioNotifyService studioNotifyService, FileStorageService<string> fileStorageService, FolderDtoHelper folderDtoHelper, FileSecurity fileSecurity, EmailValidationKeyProvider emailValidationKeyProvider) : base(foldersControllerHelper, globalFolderHelper, fileOperationDtoHelper, securityControllerHelper, coreBaseSettings, authContext, roomLinksService, customTagsService, roomLogoManager, studioNotifyService, fileStorageService, folderDtoHelper, fileSecurity, emailValidationKeyProvider)
{
}
@ -69,13 +69,16 @@ public abstract class VirtualRoomsController<T> : ApiControllerBase
private readonly SecurityControllerHelper<T> _securityControllerHelper;
private readonly CoreBaseSettings _coreBaseSettings;
private readonly AuthContext _authContext;
private readonly RoomLinksService _roomLinksManager;
private readonly RoomInvitationLinksService _roomLinksService;
private readonly CustomTagsService<T> _customTagsService;
private readonly RoomLogoManager _roomLogoManager;
private readonly StudioNotifyService _studioNotifyService;
protected readonly FileStorageService<T> _fileStorageService;
protected readonly FolderDtoHelper _folderDtoHelper;
private readonly FileSecurity _fileSecurity;
protected readonly EmailValidationKeyProvider _emailValidationKeyProvider;
protected VirtualRoomsController(FoldersControllerHelper<T> foldersControllerHelper, GlobalFolderHelper globalFolderHelper, FileOperationDtoHelper fileOperationDtoHelper, SecurityControllerHelper<T> securityControllerHelper, CoreBaseSettings coreBaseSettings, AuthContext authContext, RoomLinksService roomLinksManager, CustomTagsService<T> customTagsService, RoomLogoManager roomLogoManager, FileStorageService<T> fileStorageService, FolderDtoHelper folderDtoHelper)
protected VirtualRoomsController(FoldersControllerHelper<T> foldersControllerHelper, GlobalFolderHelper globalFolderHelper, FileOperationDtoHelper fileOperationDtoHelper, SecurityControllerHelper<T> securityControllerHelper, CoreBaseSettings coreBaseSettings, AuthContext authContext, RoomInvitationLinksService roomLinksService, CustomTagsService<T> customTagsService, RoomLogoManager roomLogoManager, StudioNotifyService studioNotifyService, FileStorageService<T> fileStorageService, FolderDtoHelper folderDtoHelper, FileSecurity fileSecurity, EmailValidationKeyProvider emailValidationKeyProvider)
{
_foldersControllerHelper = foldersControllerHelper;
_globalFolderHelper = globalFolderHelper;
@ -83,11 +86,14 @@ public abstract class VirtualRoomsController<T> : ApiControllerBase
_securityControllerHelper = securityControllerHelper;
_coreBaseSettings = coreBaseSettings;
_authContext = authContext;
_roomLinksManager = roomLinksManager;
_roomLinksService = roomLinksService;
_customTagsService = customTagsService;
_roomLogoManager = roomLogoManager;
_studioNotifyService = studioNotifyService;
_fileStorageService = fileStorageService;
_folderDtoHelper = folderDtoHelper;
_fileSecurity = fileSecurity;
_emailValidationKeyProvider = emailValidationKeyProvider;
}
[HttpGet("rooms/{id}")]
@ -152,15 +158,60 @@ public abstract class VirtualRoomsController<T> : ApiControllerBase
{
ErrorIfNotDocSpace();
if (!string.IsNullOrEmpty(inDto.Key))
{
return SetRoomSecurityByLinkAsync(id, _authContext.CurrentAccount.ID, inDto.Access, inDto.Key);
}
return _securityControllerHelper.SetFolderSecurityInfoAsync(id, inDto.Share, inDto.Notify, inDto.SharingMessage);
}
[HttpGet("rooms/{id}/invite")]
public object GetInvitationLink(T id, InviteUserRequestDto inDto)
[HttpGet("rooms/{id}/links")]
public object GetInvitationLink(T id, InviteLinkDto inDto)
{
ErrorIfNotDocSpace();
return _roomLinksManager.GenerateLink(id, inDto.Email, (int)inDto.Access, inDto.EmployeeType, _authContext.CurrentAccount.ID);
return _roomLinksService.GenerateLink(id, (int)inDto.Access, inDto.EmployeeType, _authContext.CurrentAccount.ID);
}
[HttpPut("rooms/{id}/links/send")]
public async Task<IEnumerable<InviteResultDto>> SendInvitesToRoomByEmail(T id, InviteUsersByEmailRequestDto inDto)
{
ErrorIfNotDocSpace();
var room = await _fileStorageService.GetFolderAsync(id);
if (!await _fileSecurity.CanEditRoomAsync(room))
{
throw new InvalidOperationException("You don't have the rights to invite users to the room");
}
var results = new List<InviteResultDto>();
foreach (var email in inDto.Emails)
{
var result = new InviteResultDto
{
Email = email
};
try
{
var link = _roomLinksService.GenerateLink(id, email, (int)inDto.Access, inDto.EmployeeType, _authContext.CurrentAccount.ID);
_studioNotifyService.SendEmailRoomInvite(email, link);
result.Success = true;
}
catch (Exception e)
{
result.Success = false;
result.Message = e.Message;
}
results.Add(result);
}
return results;
}
[HttpPut("rooms/{id}/tags")]
@ -230,6 +281,25 @@ public abstract class VirtualRoomsController<T> : ApiControllerBase
throw new NotSupportedException();
}
}
private async Task<IEnumerable<FileShareDto>> SetRoomSecurityByLinkAsync(T id, Guid userId, Core.Security.FileShare access, string key)
{
var result = _emailValidationKeyProvider.ValidateEmailKey(string.Empty + ConfirmType.RoomInvite + ((int)EmployeeType.User + (int)access + id.ToString()), key,
_emailValidationKeyProvider.ValidEmailKeyInterval);
if (result != EmailValidationKeyProvider.ValidationResult.Ok)
{
throw new InvalidDataException();
}
var share = new FileShareParams
{
ShareTo = userId,
Access = access
};
return await _securityControllerHelper.SetFolderSecurityInfoAsync(id, new[] { share }, false, null, true);
}
}
public class VirtualRoomsCommonController : ApiControllerBase
@ -264,7 +334,7 @@ public class VirtualRoomsCommonController : ApiControllerBase
var parentId = await _globalFolderHelper.GetFolderVirtualRooms<int>();
var roomTypes = !string.IsNullOrEmpty(types) ? JsonSerializer.Deserialize <IEnumerable<RoomType>>(types) : null;
var roomTypes = !string.IsNullOrEmpty(types) ? JsonSerializer.Deserialize<IEnumerable<RoomType>>(types) : null;
var filterTypes = roomTypes != null ? roomTypes.Select(t => t switch
{

View File

@ -61,8 +61,7 @@ global using ASC.Web.Api.Routing;
global using ASC.Web.Core.Files;
global using ASC.Web.Core.PublicResources;
global using ASC.Web.Core.Users;
global using ASC.Web.Files;
global using ASC.Web.Core;
global using ASC.Web.Files;
global using ASC.Web.Files.Classes;
global using ASC.Web.Files.Configuration;
global using ASC.Web.Files.Core.Compress;
@ -75,7 +74,8 @@ global using ASC.Web.Files.Services.WCFService.FileOperations;
global using ASC.Web.Files.Utils;
global using ASC.Web.Studio.Core;
global using ASC.Web.Studio.Core.Notify;
global using ASC.Web.Studio.Utility;
global using ASC.Web.Studio.Utility;
global using ASC.Security.Cryptography;
global using Autofac;

View File

@ -105,9 +105,9 @@ public class SecurityControllerHelper<T> : FilesHelperBase<T>
return GetSecurityInfoAsync(new List<T> { }, new List<T> { folderId });
}
public async Task<IEnumerable<FileShareDto>> GetSecurityInfoAsync(IEnumerable<T> fileIds, IEnumerable<T> folderIds)
public async Task<IEnumerable<FileShareDto>> GetSecurityInfoAsync(IEnumerable<T> fileIds, IEnumerable<T> folderIds, bool invite = false)
{
var fileShares = await _fileStorageService.GetSharedInfoAsync(fileIds, folderIds);
var fileShares = await _fileStorageService.GetSharedInfoAsync(fileIds, folderIds, invite);
return fileShares.Select(_fileShareDtoHelper.Get).ToList();
}
@ -124,12 +124,12 @@ public class SecurityControllerHelper<T> : FilesHelperBase<T>
return SetSecurityInfoAsync(new List<T> { fileId }, new List<T>(), share, notify, sharingMessage);
}
public Task<IEnumerable<FileShareDto>> SetFolderSecurityInfoAsync(T folderId, IEnumerable<FileShareParams> share, bool notify, string sharingMessage)
public Task<IEnumerable<FileShareDto>> SetFolderSecurityInfoAsync(T folderId, IEnumerable<FileShareParams> share, bool notify, string sharingMessage, bool invite = false)
{
return SetSecurityInfoAsync(new List<T>(), new List<T> { folderId }, share, notify, sharingMessage);
return SetSecurityInfoAsync(new List<T>(), new List<T> { folderId }, share, notify, sharingMessage, invite);
}
public async Task<IEnumerable<FileShareDto>> SetSecurityInfoAsync(IEnumerable<T> fileIds, IEnumerable<T> folderIds, IEnumerable<FileShareParams> share, bool notify, string sharingMessage)
public async Task<IEnumerable<FileShareDto>> SetSecurityInfoAsync(IEnumerable<T> fileIds, IEnumerable<T> folderIds, IEnumerable<FileShareParams> share, bool notify, string sharingMessage, bool invite = false)
{
if (share != null && share.Any())
{
@ -143,9 +143,9 @@ public class SecurityControllerHelper<T> : FilesHelperBase<T>
Message = sharingMessage
};
await _fileStorageService.SetAceObjectAsync(aceCollection, notify);
await _fileStorageService.SetAceObjectAsync(aceCollection, notify, invite);
}
return await GetSecurityInfoAsync(fileIds, folderIds);
return await GetSecurityInfoAsync(fileIds, folderIds, invite);
}
}

View File

@ -24,8 +24,6 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using ASC.Core.Users;
using Module = ASC.Api.Core.Module;
using SecurityContext = ASC.Core.SecurityContext;
@ -206,10 +204,12 @@ public class UserController : PeopleControllerBase
if (inDto.FromInviteLink && !string.IsNullOrEmpty(inDto.RoomId))
{
var employeeType = inDto.IsVisitor ? EmployeeType.Visitor : EmployeeType.User;
var result = _validationKeyProvider.ValidateEmailKey(inDto.Email + ConfirmType.RoomInvite + ((int)employeeType + inDto.RoomAccess + inDto.RoomId), inDto.Key,
var resultWithEmail = _validationKeyProvider.ValidateEmailKey(inDto.Email + ConfirmType.RoomInvite + ((int)employeeType + inDto.RoomAccess + inDto.RoomId), inDto.Key,
_validationKeyProvider.ValidEmailKeyInterval);
var resultWithoutEmail = _validationKeyProvider.ValidateEmailKey(string.Empty + ConfirmType.RoomInvite + ((int)employeeType + inDto.RoomAccess + inDto.RoomId), inDto.Key,
_validationKeyProvider.ValidEmailKeyInterval);
if (result != EmailValidationKeyProvider.ValidationResult.Ok)
if (resultWithEmail != EmailValidationKeyProvider.ValidationResult.Ok && resultWithoutEmail != EmailValidationKeyProvider.ValidationResult.Ok)
{
throw new SecurityException("Invalid data");
}