From 81cc5c056ce214c33ed281bbfc900e026be3e9b1 Mon Sep 17 00:00:00 2001 From: Maksim Chegulov Date: Tue, 6 Aug 2024 13:57:00 +0300 Subject: [PATCH] Added creation user with a free role when quota is overflowed --- .../Core/Core/Security/FileSecurity.cs | 7 ++ .../Core/VirtualRooms/InvitationService.cs | 93 ++++++++++--------- .../Server/Api/ThirdpartyController.cs | 43 ++++----- .../ASC.People/Server/Api/UserController.cs | 48 +++------- 4 files changed, 89 insertions(+), 102 deletions(-) diff --git a/products/ASC.Files/Core/Core/Security/FileSecurity.cs b/products/ASC.Files/Core/Core/Security/FileSecurity.cs index b14c04546b..fd86421b83 100644 --- a/products/ASC.Files/Core/Core/Security/FileSecurity.cs +++ b/products/ASC.Files/Core/Core/Security/FileSecurity.cs @@ -2236,6 +2236,13 @@ public IDictionary GetFileAccesses(File file, SubjectType su return result; } + + public static bool IsAvailableAccess(FileShare share, SubjectType subjectType, FolderType roomType) + { + return AvailableRoomAccesses.TryGetValue(roomType, out var availableRoles) && + availableRoles.TryGetValue(subjectType, out var availableRolesBySubject) && + availableRolesBySubject.Contains(share); + } private async Task> GetUserSubjectsAsync(Guid userId, FileEntry entry) { diff --git a/products/ASC.Files/Core/Core/VirtualRooms/InvitationService.cs b/products/ASC.Files/Core/Core/VirtualRooms/InvitationService.cs index bf7c84e3a6..f9f409872a 100644 --- a/products/ASC.Files/Core/Core/VirtualRooms/InvitationService.cs +++ b/products/ASC.Files/Core/Core/VirtualRooms/InvitationService.cs @@ -24,7 +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.Billing; using ASC.MessagingSystem.EF.Context; namespace ASC.Files.Core.VirtualRooms; @@ -34,16 +33,16 @@ public class InvitationService( CommonLinkUtility commonLinkUtility, IDaoFactory daoFactory, InvitationValidator invitationValidator, - ITariffService tariffService, TenantManager tenantManager, - CountPaidUserChecker countPaidUserChecker, FileSecurity fileSecurity, UserManager userManager, IPSecurity.IPSecurity iPSecurity, AuthContext authContext, IDbContextFactory dbContextFactory, FilesMessageService filesMessageService, - DisplayUserSettingsHelper displayUserSettingsHelper) + DisplayUserSettingsHelper displayUserSettingsHelper, + IDistributedLockProvider distributedLockProvider, + UsersInRoomChecker usersInRoomChecker) { public string GetInvitationLink(Guid linkId, Guid createdBy) { @@ -123,14 +122,14 @@ async Task ResolveAccessAsync(Folder folder) query = query.Where(x => x.Target == data.RoomId); } - var userId = authContext.CurrentAccount.ID; + var currentUserId = authContext.CurrentAccount.ID; await foreach(var auditEvent in query.ToAsyncEnumerable()) { var description = JsonSerializer.Deserialize>(auditEvent.DescriptionRaw); var info = JsonSerializer.Deserialize>(description.Last()); - if (!info.UserIds.Contains(userId) || auditEvent.UserId == userId) + if (!info.UserIds.Contains(currentUserId) || auditEvent.UserId == currentUserId) { continue; } @@ -139,32 +138,29 @@ async Task ResolveAccessAsync(Folder folder) return false; } - if (FileSecurity.PaidShares.Contains(data.Share) && await userManager.GetUserTypeAsync(userId) is EmployeeType.User) + if (FileSecurity.PaidShares.Contains(data.Share) && await userManager.GetUserTypeAsync(currentUserId) is EmployeeType.User) { data.Share = FileSecurity.GetHighFreeRole(folder.FolderType); - if (data.Share == FileShare.None || - !FileSecurity.AvailableRoomAccesses.TryGetValue(folder.FolderType, out var availableRoles) || - !availableRoles.TryGetValue(SubjectType.InvitationLink, out var availableRolesBySubject) || - !availableRolesBySubject.Contains(data.Share)) + if (data.Share == FileShare.None || !FileSecurity.IsAvailableAccess(data.Share, SubjectType.InvitationLink, folder.FolderType)) { validation.Result = EmailValidationKeyProvider.ValidationResult.QuotaFailed; return false; } } - var user = await userManager.GetUsersAsync(userId); + var user = await userManager.GetUsersAsync(currentUserId); - await fileSecurity.ShareAsync(folder.Id, FileEntryType.Folder, userId, data.Share); + await fileSecurity.ShareAsync(folder.Id, FileEntryType.Folder, currentUserId, data.Share); switch (entry) { case FileEntry entryInt: - await filesMessageService.SendAsync(MessageAction.RoomCreateUser, entryInt, userId, data.Share, null, true, + await filesMessageService.SendAsync(MessageAction.RoomCreateUser, entryInt, currentUserId, data.Share, null, true, user.DisplayUserName(false, displayUserSettingsHelper)); break; case FileEntry entryString: - await filesMessageService.SendAsync(MessageAction.RoomCreateUser, entryString, userId, data.Share, null, true, + await filesMessageService.SendAsync(MessageAction.RoomCreateUser, entryString, currentUserId, data.Share, null, true, user.DisplayUserName(false, displayUserSettingsHelper)); break; } @@ -184,8 +180,6 @@ await filesMessageService.SendAsync(MessageAction.RoomCreateUser, entryString, u return validation; } - - validation.Result = await GetQuotaBasedResultAsync(data); if (validation.Result is EmailValidationKeyProvider.ValidationResult.Ok) { @@ -197,17 +191,8 @@ await filesMessageService.SendAsync(MessageAction.RoomCreateUser, entryString, u return validation; } - - public async Task GetInvitationDataAsync(string key, string email, EmployeeType employeeType = EmployeeType.All, Guid? userId = default) - { - var data = await GetLinkDataAsync(key, email, employeeType, userId); - - data.Result = await GetQuotaBasedResultAsync(data); - - return data; - } - private async Task GetLinkDataAsync(string key, string email, EmployeeType employeeType = EmployeeType.All, Guid? userId = default) + public async Task GetLinkDataAsync(string key, string email, EmployeeType employeeType = EmployeeType.All, Guid? userId = default) { var result = await invitationValidator.ValidateAsync(key, email, employeeType, userId); var data = new InvitationLinkData @@ -244,32 +229,50 @@ private async Task GetLinkDataAsync(string key, string email return data; } - - private async Task GetQuotaBasedResultAsync(InvitationLinkData data) + + public async Task AddUserToRoomByInviteAsync(InvitationLinkData data, UserInfo user, bool quotaLimit = false) { - var tenant = await tenantManager.GetCurrentTenantAsync(); - - // preferential rate does not allow invite users - if ((await tariffService.GetTariffAsync(tenant.Id)).State > TariffState.Paid) + if (data is not { LinkType: InvitationLinkType.CommonToRoom }) { - return EmailValidationKeyProvider.ValidationResult.Invalid; + return; } - if (data.LinkType is InvitationLinkType.Individual || data.EmployeeType is EmployeeType.User) - { - return data.Result; - } - - try + var success = int.TryParse(data.RoomId, out var id); + var tenantId = await tenantManager.GetCurrentTenantIdAsync(); + + await using (await distributedLockProvider.TryAcquireFairLockAsync(LockKeyHelper.GetUsersInRoomCountCheckKey(tenantId))) { - await countPaidUserChecker.CheckAppend(); + if (success) + { + await AddToRoomAsync(id); + } + else + { + await AddToRoomAsync(data.RoomId); + } } - catch (TenantQuotaException) + + return; + + async Task AddToRoomAsync(T roomId) { - return EmailValidationKeyProvider.ValidationResult.TariffLimit; - } + await usersInRoomChecker.CheckAppend(); + var room = await daoFactory.GetFolderDao().GetFolderAsync(roomId); - return data.Result; + if (quotaLimit && FileSecurity.PaidShares.Contains(data.Share)) + { + data.Share = FileSecurity.GetHighFreeRole(room.FolderType); + if (data.Share == FileShare.None || !FileSecurity.IsAvailableAccess(data.Share, SubjectType.InvitationLink, room.FolderType)) + { + return; + } + } + + await fileSecurity.ShareAsync(roomId, FileEntryType.Folder, user.Id, data.Share); + + await filesMessageService.SendAsync(MessageAction.RoomCreateUser, room, user.Id, data.Share, null, true, + user.DisplayUserName(false, displayUserSettingsHelper)); + } } private async Task<(string, string)> GetRoomDataAsync(string roomId, Func> accessResolver = null) diff --git a/products/ASC.People/Server/Api/ThirdpartyController.cs b/products/ASC.People/Server/Api/ThirdpartyController.cs index 51447789a5..1d9013f125 100644 --- a/products/ASC.People/Server/Api/ThirdpartyController.cs +++ b/products/ASC.People/Server/Api/ThirdpartyController.cs @@ -47,9 +47,6 @@ public class ThirdpartyController( StudioNotifyService studioNotifyService, TenantManager tenantManager, InvitationService invitationService, - FileSecurity fileSecurity, - UsersInRoomChecker usersInRoomChecker, - IDistributedLockProvider distributedLockProvider, LoginProfileTransport loginProfileTransport, EmailValidationKeyModelHelper emailValidationKeyModelHelper) : ApiControllerBase @@ -189,7 +186,7 @@ public async Task SignupAccountAsync(SignupAccountRequestDto inDto) } var model = emailValidationKeyModelHelper.GetModel(); - var linkData = await invitationService.GetInvitationDataAsync(inDto.Key, inDto.Email, inDto.EmployeeType ?? EmployeeType.RoomAdmin, model?.UiD); + var linkData = await invitationService.GetLinkDataAsync(inDto.Key, inDto.Email, inDto.EmployeeType ?? EmployeeType.RoomAdmin, model?.UiD); if (!linkData.IsCorrect) { @@ -197,6 +194,7 @@ public async Task SignupAccountAsync(SignupAccountRequestDto inDto) } var employeeType = linkData.EmployeeType; + bool quotaLimit; Guid userId; try @@ -205,7 +203,7 @@ public async Task SignupAccountAsync(SignupAccountRequestDto inDto) var invitedByEmail = linkData.LinkType == InvitationLinkType.Individual; - var newUser = await CreateNewUser( + (var newUser, quotaLimit) = await CreateNewUser( GetFirstName(inDto, thirdPartyProfile), GetLastName(inDto, thirdPartyProfile), GetEmailAddress(inDto, thirdPartyProfile), @@ -245,22 +243,7 @@ public async Task SignupAccountAsync(SignupAccountRequestDto inDto) if (linkData is { LinkType: InvitationLinkType.CommonToRoom }) { - var success = int.TryParse(linkData.RoomId, out var id); - var tenantId = await tenantManager.GetCurrentTenantIdAsync(); - - await using (await distributedLockProvider.TryAcquireFairLockAsync(LockKeyHelper.GetUsersInRoomCountCheckKey(tenantId))) - { - if (success) - { - await usersInRoomChecker.CheckAppend(); - await fileSecurity.ShareAsync(id, FileEntryType.Folder, user.Id, linkData.Share); - } - else - { - await usersInRoomChecker.CheckAppend(); - await fileSecurity.ShareAsync(linkData.RoomId, FileEntryType.Folder, user.Id, linkData.Share); - } - } + await invitationService.AddUserToRoomByInviteAsync(linkData, user, quotaLimit); } } @@ -283,7 +266,7 @@ public async Task UnlinkAccountAsync(string provider) await messageService.SendAsync(MessageAction.UserUnlinkedSocialAccount, GetMeaningfulProviderName(provider)); } - private async Task CreateNewUser(string firstName, string lastName, string email, string passwordHash, EmployeeType employeeType, bool fromInviteLink, bool inviteByEmail, string cultureName) + private async Task<(UserInfo, bool)> CreateNewUser(string firstName, string lastName, string email, string passwordHash, EmployeeType employeeType, bool fromInviteLink, bool inviteByEmail, string cultureName) { if (SetupInfo.IsSecretEmail(email)) { @@ -310,8 +293,20 @@ private async Task CreateNewUser(string firstName, string lastName, st { user.CultureName = cultureName; } - - return await userManagerWrapper.AddUserAsync(user, passwordHash, true, true, employeeType, fromInviteLink, updateExising: inviteByEmail); + + var quotaLimit = false; + + try + { + user = await userManagerWrapper.AddUserAsync(user, passwordHash, true, true, employeeType, fromInviteLink, updateExising: inviteByEmail); + } + catch (TenantQuotaException) + { + quotaLimit = true; + user = await userManagerWrapper.AddUserAsync(user, passwordHash, true, true, EmployeeType.User, fromInviteLink, updateExising: inviteByEmail); + } + + return (user, quotaLimit); } private async Task SaveContactImage(Guid userID, string url) diff --git a/products/ASC.People/Server/Api/UserController.cs b/products/ASC.People/Server/Api/UserController.cs index 747006dfd8..35d8065e7a 100644 --- a/products/ASC.People/Server/Api/UserController.cs +++ b/products/ASC.People/Server/Api/UserController.cs @@ -66,15 +66,12 @@ public class UserController( UsersQuotaSyncOperation usersQuotaSyncOperation, CountPaidUserChecker countPaidUserChecker, CountUserChecker activeUsersChecker, - UsersInRoomChecker usersInRoomChecker, IUrlShortener urlShortener, FileSecurityCommon fileSecurityCommon, IDistributedLockProvider distributedLockProvider, QuotaSocketManager quotaSocketManager, IQuotaService quotaService, CustomQuota customQuota, - IDaoFactory daoFactory, - FilesMessageService filesMessageService, AuditEventsRepository auditEventsRepository, EmailValidationKeyModelHelper emailValidationKeyModelHelper) : PeopleControllerBase(userManager, permissionContext, apiContext, userPhotoManager, httpClientFactory, httpContextAccessor) @@ -172,7 +169,7 @@ public async Task AddMember(MemberRequestDto inDto) { await _apiContext.AuthByClaimAsync(); var model = emailValidationKeyModelHelper.GetModel(); - var linkData = inDto.FromInviteLink ? await invitationService.GetInvitationDataAsync(inDto.Key, inDto.Email, inDto.Type, model?.UiD) : null; + var linkData = inDto.FromInviteLink ? await invitationService.GetLinkDataAsync(inDto.Key, inDto.Email, inDto.Type, model?.UiD) : null; if (linkData is { IsCorrect: false }) { throw new SecurityException(FilesCommonResource.ErrorMessage_InvintationLink); @@ -249,8 +246,19 @@ public async Task AddMember(MemberRequestDto inDto) cache.Insert("REWRITE_URL" + await tenantManager.GetCurrentTenantIdAsync(), HttpContext.Request.GetDisplayUrl(), TimeSpan.FromMinutes(5)); - user = await userManagerWrapper.AddUserAsync(user, inDto.PasswordHash, inDto.FromInviteLink, true, inDto.Type, - inDto.FromInviteLink && linkData is { IsCorrect: true, ConfirmType: not ConfirmType.EmpInvite }, true, true, byEmail); + var quotaLimit = false; + + try + { + user = await userManagerWrapper.AddUserAsync(user, inDto.PasswordHash, inDto.FromInviteLink, true, inDto.Type, + inDto.FromInviteLink && linkData is { IsCorrect: true, ConfirmType: not ConfirmType.EmpInvite }, true, true, byEmail); + } + catch (TenantQuotaException) + { + quotaLimit = true; + user = await userManagerWrapper.AddUserAsync(user, inDto.PasswordHash, inDto.FromInviteLink, true, EmployeeType.User, + inDto.FromInviteLink && linkData is { IsCorrect: true, ConfirmType: not ConfirmType.EmpInvite }, true, true, byEmail); + } await UpdateDepartmentsAsync(inDto.Department, user); @@ -261,20 +269,7 @@ public async Task AddMember(MemberRequestDto inDto) if (linkData is { LinkType: InvitationLinkType.CommonToRoom }) { - var success = int.TryParse(linkData.RoomId, out var id); - var tenantId = await tenantManager.GetCurrentTenantIdAsync(); - - await using (await distributedLockProvider.TryAcquireFairLockAsync(LockKeyHelper.GetUsersInRoomCountCheckKey(tenantId))) - { - if (success) - { - await AddUserToRoomAsync(id); - } - else - { - await AddUserToRoomAsync(linkData.RoomId); - } - } + await invitationService.AddUserToRoomByInviteAsync(linkData, user, quotaLimit); } if (inDto.IsUser.GetValueOrDefault(false)) @@ -287,19 +282,6 @@ public async Task AddMember(MemberRequestDto inDto) } return await employeeFullDtoHelper.GetFullAsync(user); - - async Task AddUserToRoomAsync(T roomId) - { - await usersInRoomChecker.CheckAppend(); - var roomTask = daoFactory.GetFolderDao().GetFolderAsync(roomId); - - await fileSecurity.ShareAsync(roomId, FileEntryType.Folder, user.Id, linkData.Share); - - var room = await roomTask; - - await filesMessageService.SendAsync(MessageAction.RoomCreateUser, room, user.Id, linkData.Share, null, true, - user.DisplayUserName(false, displayUserSettingsHelper)); - } } ///