From 4e1e514e836e63057a2e20221d55fe483359f36b Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Tue, 28 Jan 2025 09:49:51 -0600 Subject: [PATCH 1/5] [PM-11249] Update cipher revision date when an attachment is added or deleted (#4873) * update the cipher revision date when an attachment is added or deleted * store the updated cipher in the DB when an attachment is altered * return cipher from delete attachment endpoint --- .../Vault/Controllers/CiphersController.cs | 4 ++-- .../Data/DeleteAttachmentReponseData.cs | 13 +++++++++++ src/Core/Vault/Services/ICipherService.cs | 2 +- .../Services/Implementations/CipherService.cs | 23 +++++++++++++++---- 4 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 src/Core/Vault/Models/Data/DeleteAttachmentReponseData.cs diff --git a/src/Api/Vault/Controllers/CiphersController.cs b/src/Api/Vault/Controllers/CiphersController.cs index 59984683e556..c8ebb8c402b4 100644 --- a/src/Api/Vault/Controllers/CiphersController.cs +++ b/src/Api/Vault/Controllers/CiphersController.cs @@ -1097,7 +1097,7 @@ await _cipherService.CreateAttachmentShareAsync(cipher, stream, fileName, key, [HttpDelete("{id}/attachment/{attachmentId}")] [HttpPost("{id}/attachment/{attachmentId}/delete")] - public async Task DeleteAttachment(Guid id, string attachmentId) + public async Task DeleteAttachment(Guid id, string attachmentId) { var userId = _userService.GetProperUserId(User).Value; var cipher = await GetByIdAsync(id, userId); @@ -1106,7 +1106,7 @@ public async Task DeleteAttachment(Guid id, string attachmentId) throw new NotFoundException(); } - await _cipherService.DeleteAttachmentAsync(cipher, attachmentId, userId, false); + return await _cipherService.DeleteAttachmentAsync(cipher, attachmentId, userId, false); } [HttpDelete("{id}/attachment/{attachmentId}/admin")] diff --git a/src/Core/Vault/Models/Data/DeleteAttachmentReponseData.cs b/src/Core/Vault/Models/Data/DeleteAttachmentReponseData.cs new file mode 100644 index 000000000000..0a5e75557295 --- /dev/null +++ b/src/Core/Vault/Models/Data/DeleteAttachmentReponseData.cs @@ -0,0 +1,13 @@ +using Bit.Core.Vault.Entities; + +namespace Bit.Core.Vault.Models.Data; + +public class DeleteAttachmentResponseData +{ + public Cipher Cipher { get; set; } + + public DeleteAttachmentResponseData(Cipher cipher) + { + Cipher = cipher; + } +} diff --git a/src/Core/Vault/Services/ICipherService.cs b/src/Core/Vault/Services/ICipherService.cs index b332fbb96fee..17f55cb47d38 100644 --- a/src/Core/Vault/Services/ICipherService.cs +++ b/src/Core/Vault/Services/ICipherService.cs @@ -17,7 +17,7 @@ Task CreateAttachmentShareAsync(Cipher cipher, Stream stream, string fileName, s string attachmentId, Guid organizationShareId); Task DeleteAsync(Cipher cipher, Guid deletingUserId, bool orgAdmin = false); Task DeleteManyAsync(IEnumerable cipherIds, Guid deletingUserId, Guid? organizationId = null, bool orgAdmin = false); - Task DeleteAttachmentAsync(Cipher cipher, string attachmentId, Guid deletingUserId, bool orgAdmin = false); + Task DeleteAttachmentAsync(Cipher cipher, string attachmentId, Guid deletingUserId, bool orgAdmin = false); Task PurgeAsync(Guid organizationId); Task MoveManyAsync(IEnumerable cipherIds, Guid? destinationFolderId, Guid movingUserId); Task SaveFolderAsync(Folder folder); diff --git a/src/Core/Vault/Services/Implementations/CipherService.cs b/src/Core/Vault/Services/Implementations/CipherService.cs index ea4db01fd77b..90c03df90b0a 100644 --- a/src/Core/Vault/Services/Implementations/CipherService.cs +++ b/src/Core/Vault/Services/Implementations/CipherService.cs @@ -210,6 +210,11 @@ await _cipherRepository.UpdateAttachmentAsync(new CipherAttachment AttachmentData = JsonSerializer.Serialize(data) }); cipher.AddAttachment(attachmentId, data); + + // Update the revision date when an attachment is added + cipher.RevisionDate = DateTime.UtcNow; + await _cipherRepository.ReplaceAsync((CipherDetails)cipher); + await _pushService.PushSyncCipherUpdateAsync(cipher, null); return (attachmentId, uploadUrl); @@ -259,6 +264,10 @@ public async Task CreateAttachmentAsync(Cipher cipher, Stream stream, string fil throw; } + // Update the revision date when an attachment is added + cipher.RevisionDate = DateTime.UtcNow; + await _cipherRepository.ReplaceAsync((CipherDetails)cipher); + // push await _pushService.PushSyncCipherUpdateAsync(cipher, null); } @@ -441,7 +450,7 @@ public async Task DeleteManyAsync(IEnumerable cipherIds, Guid deletingUser await _pushService.PushSyncCiphersAsync(deletingUserId); } - public async Task DeleteAttachmentAsync(Cipher cipher, string attachmentId, Guid deletingUserId, + public async Task DeleteAttachmentAsync(Cipher cipher, string attachmentId, Guid deletingUserId, bool orgAdmin = false) { if (!orgAdmin && !(await UserCanEditAsync(cipher, deletingUserId))) @@ -454,7 +463,7 @@ public async Task DeleteAttachmentAsync(Cipher cipher, string attachmentId, Guid throw new NotFoundException(); } - await DeleteAttachmentAsync(cipher, cipher.GetAttachments()[attachmentId]); + return await DeleteAttachmentAsync(cipher, cipher.GetAttachments()[attachmentId]); } public async Task PurgeAsync(Guid organizationId) @@ -834,11 +843,11 @@ private void ValidateCipherLastKnownRevisionDateAsync(Cipher cipher, DateTime? l } } - private async Task DeleteAttachmentAsync(Cipher cipher, CipherAttachment.MetaData attachmentData) + private async Task DeleteAttachmentAsync(Cipher cipher, CipherAttachment.MetaData attachmentData) { if (attachmentData == null || string.IsNullOrWhiteSpace(attachmentData.AttachmentId)) { - return; + return null; } await _cipherRepository.DeleteAttachmentAsync(cipher.Id, attachmentData.AttachmentId); @@ -846,8 +855,14 @@ private async Task DeleteAttachmentAsync(Cipher cipher, CipherAttachment.MetaDat await _attachmentStorageService.DeleteAttachmentAsync(cipher.Id, attachmentData); await _eventService.LogCipherEventAsync(cipher, Bit.Core.Enums.EventType.Cipher_AttachmentDeleted); + // Update the revision date when an attachment is deleted + cipher.RevisionDate = DateTime.UtcNow; + await _cipherRepository.ReplaceAsync((CipherDetails)cipher); + // push await _pushService.PushSyncCipherUpdateAsync(cipher, null); + + return new DeleteAttachmentResponseData(cipher); } private async Task ValidateCipherEditForAttachmentAsync(Cipher cipher, Guid savingUserId, bool orgAdmin, From 6d7bdb6ec000f3f2c795a9b9b8f658b4fafea7bc Mon Sep 17 00:00:00 2001 From: Jimmy Vo Date: Tue, 28 Jan 2025 12:23:15 -0500 Subject: [PATCH 2/5] Ac/pm 17217/add use policy check for accept endpoint(#5324) --- .../OrganizationUsersController.cs | 23 ++++++-- .../OrganizationUsersControllerTests.cs | 58 +++++++++++++++++-- 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs index 12d11fbc1885..265aefc4caf8 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs @@ -311,10 +311,8 @@ public async Task Accept(Guid orgId, Guid organizationUserId, [FromBody] Organiz throw new UnauthorizedAccessException(); } - var masterPasswordPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword); - var useMasterPasswordPolicy = masterPasswordPolicy != null && - masterPasswordPolicy.Enabled && - masterPasswordPolicy.GetDataModel().AutoEnrollEnabled; + var useMasterPasswordPolicy = await ShouldHandleResetPasswordAsync(orgId); + if (useMasterPasswordPolicy && string.IsNullOrWhiteSpace(model.ResetPasswordKey)) { throw new BadRequestException(string.Empty, "Master Password reset is required, but not provided."); @@ -328,6 +326,23 @@ public async Task Accept(Guid orgId, Guid organizationUserId, [FromBody] Organiz } } + private async Task ShouldHandleResetPasswordAsync(Guid orgId) + { + var organizationAbility = await _applicationCacheService.GetOrganizationAbilityAsync(orgId); + + if (organizationAbility is not { UsePolicies: true }) + { + return false; + } + + var masterPasswordPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword); + var useMasterPasswordPolicy = masterPasswordPolicy != null && + masterPasswordPolicy.Enabled && + masterPasswordPolicy.GetDataModel().AutoEnrollEnabled; + + return useMasterPasswordPolicy; + } + [HttpPost("{id}/confirm")] public async Task Confirm(string orgId, string id, [FromBody] OrganizationUserConfirmRequestModel model) { diff --git a/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs index 0ba8a101d7db..e3071bd2271f 100644 --- a/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs @@ -123,24 +123,74 @@ await sutProvider.GetDependency().DidNotReceiveWithAnyArgs [Theory] [BitAutoData] - public async Task Accept_RequireMasterPasswordReset(Guid orgId, Guid orgUserId, + public async Task Accept_WhenOrganizationUsePoliciesIsEnabledAndResetPolicyIsEnabled_ShouldHandleResetPassword(Guid orgId, Guid orgUserId, OrganizationUserAcceptRequestModel model, User user, SutProvider sutProvider) { + // Arrange + var applicationCacheService = sutProvider.GetDependency(); + applicationCacheService.GetOrganizationAbilityAsync(orgId).Returns(new OrganizationAbility { UsePolicies = true }); + var policy = new Policy { Enabled = true, Data = CoreHelpers.ClassToJsonData(new ResetPasswordDataModel { AutoEnrollEnabled = true, }), }; - sutProvider.GetDependency().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user); - sutProvider.GetDependency().GetByOrganizationIdTypeAsync(orgId, + var userService = sutProvider.GetDependency(); + userService.GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user); + + + var policyRepository = sutProvider.GetDependency(); + policyRepository.GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword).Returns(policy); + // Act await sutProvider.Sut.Accept(orgId, orgUserId, model); + // Assert await sutProvider.GetDependency().Received(1) - .AcceptOrgUserByEmailTokenAsync(orgUserId, user, model.Token, sutProvider.GetDependency()); + .AcceptOrgUserByEmailTokenAsync(orgUserId, user, model.Token, userService); await sutProvider.GetDependency().Received(1) .UpdateUserResetPasswordEnrollmentAsync(orgId, user.Id, model.ResetPasswordKey, user.Id); + + await userService.Received(1).GetUserByPrincipalAsync(default); + await applicationCacheService.Received(1).GetOrganizationAbilityAsync(orgId); + await policyRepository.Received(1).GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword); + + } + + [Theory] + [BitAutoData] + public async Task Accept_WhenOrganizationUsePoliciesIsDisabled_ShouldNotHandleResetPassword(Guid orgId, Guid orgUserId, + OrganizationUserAcceptRequestModel model, User user, SutProvider sutProvider) + { + // Arrange + var applicationCacheService = sutProvider.GetDependency(); + applicationCacheService.GetOrganizationAbilityAsync(orgId).Returns(new OrganizationAbility { UsePolicies = false }); + + var policy = new Policy + { + Enabled = true, + Data = CoreHelpers.ClassToJsonData(new ResetPasswordDataModel { AutoEnrollEnabled = true, }), + }; + var userService = sutProvider.GetDependency(); + userService.GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user); + + var policyRepository = sutProvider.GetDependency(); + policyRepository.GetByOrganizationIdTypeAsync(orgId, + PolicyType.ResetPassword).Returns(policy); + + // Act + await sutProvider.Sut.Accept(orgId, orgUserId, model); + + // Assert + await userService.Received(1).GetUserByPrincipalAsync(default); + await sutProvider.GetDependency().Received(1) + .AcceptOrgUserByEmailTokenAsync(orgUserId, user, model.Token, userService); + await sutProvider.GetDependency().Received(0) + .UpdateUserResetPasswordEnrollmentAsync(orgId, user.Id, model.ResetPasswordKey, user.Id); + + await policyRepository.Received(0).GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword); + await applicationCacheService.Received(1).GetOrganizationAbilityAsync(orgId); } [Theory] From 93f5b34223d1f6648ce75bd8180240dc06949b41 Mon Sep 17 00:00:00 2001 From: Jimmy Vo Date: Tue, 28 Jan 2025 12:58:04 -0500 Subject: [PATCH 3/5] Add limit item deletion server code (#5308) --- .../Models/Response/Organizations/OrganizationResponseModel.cs | 2 ++ .../Models/Response/ProfileOrganizationResponseModel.cs | 2 ++ .../Response/ProfileProviderOrganizationResponseModel.cs | 1 + .../OrganizationCollectionManagementUpdateRequestModel.cs | 2 ++ .../Models/Data/Organizations/OrganizationAbility.cs | 2 ++ .../OrganizationUsers/OrganizationUserOrganizationDetails.cs | 1 + .../Models/Data/Organizations/SelfHostedOrganizationDetails.cs | 1 + .../Models/Data/Provider/ProviderUserOrganizationDetails.cs | 1 + src/Core/Models/PushNotification.cs | 1 + .../NotificationHub/NotificationHubPushNotificationService.cs | 3 ++- .../Push/Services/AzureQueuePushNotificationService.cs | 3 ++- .../Push/Services/NotificationsApiPushNotificationService.cs | 3 ++- .../Platform/Push/Services/RelayPushNotificationService.cs | 3 ++- .../AdminConsole/Repositories/OrganizationRepository.cs | 1 + .../Queries/OrganizationUserOrganizationDetailsViewQuery.cs | 1 + .../Queries/ProviderUserOrganizationDetailsViewQuery.cs | 1 + 16 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/Api/AdminConsole/Models/Response/Organizations/OrganizationResponseModel.cs b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationResponseModel.cs index 116b4b1238f5..272aaf6f9c71 100644 --- a/src/Api/AdminConsole/Models/Response/Organizations/OrganizationResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationResponseModel.cs @@ -57,6 +57,7 @@ public OrganizationResponseModel(Organization organization, string obj = "organi MaxAutoscaleSmServiceAccounts = organization.MaxAutoscaleSmServiceAccounts; LimitCollectionCreation = organization.LimitCollectionCreation; LimitCollectionDeletion = organization.LimitCollectionDeletion; + LimitItemDeletion = organization.LimitItemDeletion; AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems; UseRiskInsights = organization.UseRiskInsights; } @@ -102,6 +103,7 @@ public OrganizationResponseModel(Organization organization, string obj = "organi public int? MaxAutoscaleSmServiceAccounts { get; set; } public bool LimitCollectionCreation { get; set; } public bool LimitCollectionDeletion { get; set; } + public bool LimitItemDeletion { get; set; } public bool AllowAdminAccessToAllCollectionItems { get; set; } public bool UseRiskInsights { get; set; } } diff --git a/src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs b/src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs index 75e4c44a6dff..d08298de6ed5 100644 --- a/src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs @@ -67,6 +67,7 @@ public ProfileOrganizationResponseModel( AccessSecretsManager = organization.AccessSecretsManager; LimitCollectionCreation = organization.LimitCollectionCreation; LimitCollectionDeletion = organization.LimitCollectionDeletion; + LimitItemDeletion = organization.LimitItemDeletion; AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems; UserIsManagedByOrganization = organizationIdsManagingUser.Contains(organization.OrganizationId); UseRiskInsights = organization.UseRiskInsights; @@ -128,6 +129,7 @@ public ProfileOrganizationResponseModel( public bool AccessSecretsManager { get; set; } public bool LimitCollectionCreation { get; set; } public bool LimitCollectionDeletion { get; set; } + public bool LimitItemDeletion { get; set; } public bool AllowAdminAccessToAllCollectionItems { get; set; } /// /// Indicates if the organization manages the user. diff --git a/src/Api/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModel.cs b/src/Api/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModel.cs index 211476dca16e..589744c7df51 100644 --- a/src/Api/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModel.cs @@ -47,6 +47,7 @@ public ProfileProviderOrganizationResponseModel(ProviderUserOrganizationDetails ProductTierType = StaticStore.GetPlan(organization.PlanType).ProductTier; LimitCollectionCreation = organization.LimitCollectionCreation; LimitCollectionDeletion = organization.LimitCollectionDeletion; + LimitItemDeletion = organization.LimitItemDeletion; AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems; UseRiskInsights = organization.UseRiskInsights; } diff --git a/src/Api/Models/Request/Organizations/OrganizationCollectionManagementUpdateRequestModel.cs b/src/Api/Models/Request/Organizations/OrganizationCollectionManagementUpdateRequestModel.cs index 94f842ca1e2e..829840c89696 100644 --- a/src/Api/Models/Request/Organizations/OrganizationCollectionManagementUpdateRequestModel.cs +++ b/src/Api/Models/Request/Organizations/OrganizationCollectionManagementUpdateRequestModel.cs @@ -7,12 +7,14 @@ public class OrganizationCollectionManagementUpdateRequestModel { public bool LimitCollectionCreation { get; set; } public bool LimitCollectionDeletion { get; set; } + public bool LimitItemDeletion { get; set; } public bool AllowAdminAccessToAllCollectionItems { get; set; } public virtual Organization ToOrganization(Organization existingOrganization, IFeatureService featureService) { existingOrganization.LimitCollectionCreation = LimitCollectionCreation; existingOrganization.LimitCollectionDeletion = LimitCollectionDeletion; + existingOrganization.LimitItemDeletion = LimitItemDeletion; existingOrganization.AllowAdminAccessToAllCollectionItems = AllowAdminAccessToAllCollectionItems; return existingOrganization; } diff --git a/src/Core/AdminConsole/Models/Data/Organizations/OrganizationAbility.cs b/src/Core/AdminConsole/Models/Data/Organizations/OrganizationAbility.cs index 6392e483cebe..62914f6fa8b6 100644 --- a/src/Core/AdminConsole/Models/Data/Organizations/OrganizationAbility.cs +++ b/src/Core/AdminConsole/Models/Data/Organizations/OrganizationAbility.cs @@ -23,6 +23,7 @@ public OrganizationAbility(Organization organization) UsePolicies = organization.UsePolicies; LimitCollectionCreation = organization.LimitCollectionCreation; LimitCollectionDeletion = organization.LimitCollectionDeletion; + LimitItemDeletion = organization.LimitItemDeletion; AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems; UseRiskInsights = organization.UseRiskInsights; } @@ -41,6 +42,7 @@ public OrganizationAbility(Organization organization) public bool UsePolicies { get; set; } public bool LimitCollectionCreation { get; set; } public bool LimitCollectionDeletion { get; set; } + public bool LimitItemDeletion { get; set; } public bool AllowAdminAccessToAllCollectionItems { get; set; } public bool UseRiskInsights { get; set; } } diff --git a/src/Core/AdminConsole/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs b/src/Core/AdminConsole/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs index e06b6bd66a91..18d68af2209e 100644 --- a/src/Core/AdminConsole/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs +++ b/src/Core/AdminConsole/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs @@ -56,6 +56,7 @@ public class OrganizationUserOrganizationDetails public int? SmServiceAccounts { get; set; } public bool LimitCollectionCreation { get; set; } public bool LimitCollectionDeletion { get; set; } + public bool LimitItemDeletion { get; set; } public bool AllowAdminAccessToAllCollectionItems { get; set; } public bool UseRiskInsights { get; set; } } diff --git a/src/Core/AdminConsole/Models/Data/Organizations/SelfHostedOrganizationDetails.cs b/src/Core/AdminConsole/Models/Data/Organizations/SelfHostedOrganizationDetails.cs index bd727f707b7e..c53ac8745c2a 100644 --- a/src/Core/AdminConsole/Models/Data/Organizations/SelfHostedOrganizationDetails.cs +++ b/src/Core/AdminConsole/Models/Data/Organizations/SelfHostedOrganizationDetails.cs @@ -146,6 +146,7 @@ public Organization ToOrganization() OwnersNotifiedOfAutoscaling = OwnersNotifiedOfAutoscaling, LimitCollectionCreation = LimitCollectionCreation, LimitCollectionDeletion = LimitCollectionDeletion, + LimitItemDeletion = LimitItemDeletion, AllowAdminAccessToAllCollectionItems = AllowAdminAccessToAllCollectionItems, Status = Status }; diff --git a/src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs b/src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs index bd5592edfc46..57f176666a6c 100644 --- a/src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs +++ b/src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs @@ -42,6 +42,7 @@ public class ProviderUserOrganizationDetails public PlanType PlanType { get; set; } public bool LimitCollectionCreation { get; set; } public bool LimitCollectionDeletion { get; set; } + public bool LimitItemDeletion { get; set; } public bool AllowAdminAccessToAllCollectionItems { get; set; } public bool UseRiskInsights { get; set; } public ProviderType ProviderType { get; set; } diff --git a/src/Core/Models/PushNotification.cs b/src/Core/Models/PushNotification.cs index 9907abcb65c4..e2247881eaea 100644 --- a/src/Core/Models/PushNotification.cs +++ b/src/Core/Models/PushNotification.cs @@ -62,4 +62,5 @@ public class OrganizationCollectionManagementPushNotification public Guid OrganizationId { get; init; } public bool LimitCollectionCreation { get; init; } public bool LimitCollectionDeletion { get; init; } + public bool LimitItemDeletion { get; init; } } diff --git a/src/Core/NotificationHub/NotificationHubPushNotificationService.cs b/src/Core/NotificationHub/NotificationHubPushNotificationService.cs index d90ebdd74450..d99cbf3fe719 100644 --- a/src/Core/NotificationHub/NotificationHubPushNotificationService.cs +++ b/src/Core/NotificationHub/NotificationHubPushNotificationService.cs @@ -246,7 +246,8 @@ await SendPayloadToOrganizationAsync( { OrganizationId = organization.Id, LimitCollectionCreation = organization.LimitCollectionCreation, - LimitCollectionDeletion = organization.LimitCollectionDeletion + LimitCollectionDeletion = organization.LimitCollectionDeletion, + LimitItemDeletion = organization.LimitItemDeletion }, false ); diff --git a/src/Core/Platform/Push/Services/AzureQueuePushNotificationService.cs b/src/Core/Platform/Push/Services/AzureQueuePushNotificationService.cs index 0503a22a60f0..33272ce870d8 100644 --- a/src/Core/Platform/Push/Services/AzureQueuePushNotificationService.cs +++ b/src/Core/Platform/Push/Services/AzureQueuePushNotificationService.cs @@ -239,6 +239,7 @@ await SendMessageAsync(PushType.SyncOrganizationCollectionSettingChanged, { OrganizationId = organization.Id, LimitCollectionCreation = organization.LimitCollectionCreation, - LimitCollectionDeletion = organization.LimitCollectionDeletion + LimitCollectionDeletion = organization.LimitCollectionDeletion, + LimitItemDeletion = organization.LimitItemDeletion }, false); } diff --git a/src/Core/Platform/Push/Services/NotificationsApiPushNotificationService.cs b/src/Core/Platform/Push/Services/NotificationsApiPushNotificationService.cs index 849ae1b765ff..5ebfc811ef59 100644 --- a/src/Core/Platform/Push/Services/NotificationsApiPushNotificationService.cs +++ b/src/Core/Platform/Push/Services/NotificationsApiPushNotificationService.cs @@ -248,6 +248,7 @@ await SendMessageAsync(PushType.SyncOrganizationCollectionSettingChanged, { OrganizationId = organization.Id, LimitCollectionCreation = organization.LimitCollectionCreation, - LimitCollectionDeletion = organization.LimitCollectionDeletion + LimitCollectionDeletion = organization.LimitCollectionDeletion, + LimitItemDeletion = organization.LimitItemDeletion }, false); } diff --git a/src/Core/Platform/Push/Services/RelayPushNotificationService.cs b/src/Core/Platform/Push/Services/RelayPushNotificationService.cs index e41244a1b8b1..6549ab47c353 100644 --- a/src/Core/Platform/Push/Services/RelayPushNotificationService.cs +++ b/src/Core/Platform/Push/Services/RelayPushNotificationService.cs @@ -273,7 +273,8 @@ await SendPayloadToOrganizationAsync( { OrganizationId = organization.Id, LimitCollectionCreation = organization.LimitCollectionCreation, - LimitCollectionDeletion = organization.LimitCollectionDeletion + LimitCollectionDeletion = organization.LimitCollectionDeletion, + LimitItemDeletion = organization.LimitItemDeletion }, false ); diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs index fb3766c6c7fe..c1c78eee6081 100644 --- a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs @@ -101,6 +101,7 @@ public async Task> GetManyAbilitiesAsync() UsePolicies = e.UsePolicies, LimitCollectionCreation = e.LimitCollectionCreation, LimitCollectionDeletion = e.LimitCollectionDeletion, + LimitItemDeletion = e.LimitItemDeletion, AllowAdminAccessToAllCollectionItems = e.AllowAdminAccessToAllCollectionItems, UseRiskInsights = e.UseRiskInsights }).ToListAsync(); diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationUserOrganizationDetailsViewQuery.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationUserOrganizationDetailsViewQuery.cs index c93903ab0fb2..73aea4433269 100644 --- a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationUserOrganizationDetailsViewQuery.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationUserOrganizationDetailsViewQuery.cs @@ -68,6 +68,7 @@ from os in os_g.DefaultIfEmpty() SmServiceAccounts = o.SmServiceAccounts, LimitCollectionCreation = o.LimitCollectionCreation, LimitCollectionDeletion = o.LimitCollectionDeletion, + LimitItemDeletion = o.LimitItemDeletion, AllowAdminAccessToAllCollectionItems = o.AllowAdminAccessToAllCollectionItems, UseRiskInsights = o.UseRiskInsights, }; diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderUserOrganizationDetailsViewQuery.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderUserOrganizationDetailsViewQuery.cs index 3f3d3d389eff..9483ccbc9264 100644 --- a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderUserOrganizationDetailsViewQuery.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/ProviderUserOrganizationDetailsViewQuery.cs @@ -46,6 +46,7 @@ join p in dbContext.Providers on pu.ProviderId equals p.Id PlanType = x.o.PlanType, LimitCollectionCreation = x.o.LimitCollectionCreation, LimitCollectionDeletion = x.o.LimitCollectionDeletion, + LimitItemDeletion = x.o.LimitItemDeletion, AllowAdminAccessToAllCollectionItems = x.o.AllowAdminAccessToAllCollectionItems, UseRiskInsights = x.o.UseRiskInsights, ProviderType = x.p.Type From 3d273bf494666a26217169d7956d8a63b736d13f Mon Sep 17 00:00:00 2001 From: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com> Date: Tue, 28 Jan 2025 13:39:19 -0500 Subject: [PATCH 4/5] [PM-15906] Add feature flags for Android single tap passkey flows (#5334) Add feature flags to control single tap passkey creation and authentication in the Android client. --- src/Core/Constants.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index 848a8805b77c..6f08ee4f6bd9 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -167,6 +167,8 @@ public static class FeatureFlagKeys public const string EnablePasswordManagerSyncAndroid = "enable-password-manager-sync-android"; public const string EnablePasswordManagerSynciOS = "enable-password-manager-sync-ios"; public const string AccountDeprovisioningBanner = "pm-17120-account-deprovisioning-admin-console-banner"; + public const string SingleTapPasskeyCreation = "single-tap-passkey-creation"; + public const string SingleTapPasskeyAuthentication = "single-tap-passkey-authentication"; public static List GetAllKeys() { From f1c94a1400137e150ef1b999448a2e6476cbf1b5 Mon Sep 17 00:00:00 2001 From: Tom <144813356+ttalty@users.noreply.github.com> Date: Tue, 28 Jan 2025 13:52:11 -0500 Subject: [PATCH 5/5] Risk insights feature flag for server (#5328) --- src/Core/Constants.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index 6f08ee4f6bd9..6d70f0b3ce3b 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -169,6 +169,7 @@ public static class FeatureFlagKeys public const string AccountDeprovisioningBanner = "pm-17120-account-deprovisioning-admin-console-banner"; public const string SingleTapPasskeyCreation = "single-tap-passkey-creation"; public const string SingleTapPasskeyAuthentication = "single-tap-passkey-authentication"; + public const string EnableRiskInsightsNotifications = "enable-risk-insights-notifications"; public static List GetAllKeys() {