1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-04 01:22:50 -05:00

[AC-1330] [AC-1815] [Server] Deprecate access control indicator - UserCipherDetails (#3372)

* Create UserCipherDetails_v2 and update logic to remove AccessAll
* Create v2 variants of all sprocs that rely on it
* Add feature flag logic to call old or new sproc
* Make equivalent changes to EF queries
This commit is contained in:
Thomas Rittson
2023-11-28 11:14:33 +10:00
committed by GitHub
parent b062ab8043
commit 12667dbb3f
22 changed files with 904 additions and 107 deletions

View File

@ -5,6 +5,7 @@ using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Auth.Models.Data;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
@ -33,6 +34,11 @@ public class EmergencyAccessService : IEmergencyAccessService
private readonly IPasswordHasher<User> _passwordHasher;
private readonly IOrganizationService _organizationService;
private readonly IDataProtectorTokenFactory<EmergencyAccessInviteTokenable> _dataProtectorTokenizer;
private readonly ICurrentContext _currentContext;
private readonly IFeatureService _featureService;
private bool UseFlexibleCollections =>
_featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext);
public EmergencyAccessService(
IEmergencyAccessRepository emergencyAccessRepository,
@ -46,7 +52,9 @@ public class EmergencyAccessService : IEmergencyAccessService
IPasswordHasher<User> passwordHasher,
GlobalSettings globalSettings,
IOrganizationService organizationService,
IDataProtectorTokenFactory<EmergencyAccessInviteTokenable> dataProtectorTokenizer)
IDataProtectorTokenFactory<EmergencyAccessInviteTokenable> dataProtectorTokenizer,
ICurrentContext currentContext,
IFeatureService featureService)
{
_emergencyAccessRepository = emergencyAccessRepository;
_organizationUserRepository = organizationUserRepository;
@ -60,6 +68,8 @@ public class EmergencyAccessService : IEmergencyAccessService
_globalSettings = globalSettings;
_organizationService = organizationService;
_dataProtectorTokenizer = dataProtectorTokenizer;
_currentContext = currentContext;
_featureService = featureService;
}
public async Task<EmergencyAccess> InviteAsync(User invitingUser, string email, EmergencyAccessType type, int waitTime)
@ -387,7 +397,7 @@ public class EmergencyAccessService : IEmergencyAccessService
throw new BadRequestException("Emergency Access not valid.");
}
var ciphers = await _cipherRepository.GetManyByUserIdAsync(emergencyAccess.GrantorId, false);
var ciphers = await _cipherRepository.GetManyByUserIdAsync(emergencyAccess.GrantorId, useFlexibleCollections: UseFlexibleCollections, withOrganizations: false);
return new EmergencyAccessViewData
{
@ -405,7 +415,7 @@ public class EmergencyAccessService : IEmergencyAccessService
throw new BadRequestException("Emergency Access not valid.");
}
var cipher = await _cipherRepository.GetByIdAsync(cipherId, emergencyAccess.GrantorId);
var cipher = await _cipherRepository.GetByIdAsync(cipherId, emergencyAccess.GrantorId, UseFlexibleCollections);
return await _cipherService.GetAttachmentDownloadDataAsync(cipher, attachmentId);
}

View File

@ -8,11 +8,11 @@ namespace Bit.Core.Vault.Repositories;
public interface ICipherRepository : IRepository<Cipher, Guid>
{
Task<CipherDetails> GetByIdAsync(Guid id, Guid userId);
Task<CipherDetails> GetByIdAsync(Guid id, Guid userId, bool useFlexibleCollections);
Task<CipherOrganizationDetails> GetOrganizationDetailsByIdAsync(Guid id);
Task<ICollection<CipherOrganizationDetails>> GetManyOrganizationDetailsByOrganizationIdAsync(Guid organizationId);
Task<bool> GetCanEditByIdAsync(Guid userId, Guid cipherId);
Task<ICollection<CipherDetails>> GetManyByUserIdAsync(Guid userId, bool withOrganizations = true);
Task<ICollection<CipherDetails>> GetManyByUserIdAsync(Guid userId, bool useFlexibleCollections, bool withOrganizations = true);
Task<ICollection<Cipher>> GetManyByOrganizationIdAsync(Guid organizationId);
Task CreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds);
Task CreateAsync(CipherDetails cipher);
@ -23,9 +23,9 @@ public interface ICipherRepository : IRepository<Cipher, Guid>
Task UpdatePartialAsync(Guid id, Guid userId, Guid? folderId, bool favorite);
Task UpdateAttachmentAsync(CipherAttachment attachment);
Task DeleteAttachmentAsync(Guid cipherId, string attachmentId);
Task DeleteAsync(IEnumerable<Guid> ids, Guid userId);
Task DeleteAsync(IEnumerable<Guid> ids, Guid userId, bool useFlexibleCollections);
Task DeleteByIdsOrganizationIdAsync(IEnumerable<Guid> ids, Guid organizationId);
Task MoveAsync(IEnumerable<Guid> ids, Guid? folderId, Guid userId);
Task MoveAsync(IEnumerable<Guid> ids, Guid? folderId, Guid userId, bool useFlexibleCollections);
Task DeleteByUserIdAsync(Guid userId);
Task DeleteByOrganizationIdAsync(Guid organizationId);
Task UpdateUserKeysAndCiphersAsync(User user, IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders, IEnumerable<Send> sends);
@ -33,9 +33,9 @@ public interface ICipherRepository : IRepository<Cipher, Guid>
Task CreateAsync(IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders);
Task CreateAsync(IEnumerable<Cipher> ciphers, IEnumerable<Collection> collections,
IEnumerable<CollectionCipher> collectionCiphers, IEnumerable<CollectionUser> collectionUsers);
Task SoftDeleteAsync(IEnumerable<Guid> ids, Guid userId);
Task SoftDeleteAsync(IEnumerable<Guid> ids, Guid userId, bool useFlexibleCollections);
Task SoftDeleteByIdsOrganizationIdAsync(IEnumerable<Guid> ids, Guid organizationId);
Task<DateTime> RestoreAsync(IEnumerable<Guid> ids, Guid userId);
Task<DateTime> RestoreAsync(IEnumerable<Guid> ids, Guid userId, bool useFlexibleCollections);
Task<DateTime> RestoreByIdsOrganizationIdAsync(IEnumerable<Guid> ids, Guid organizationId);
Task DeleteDeletedAsync(DateTime deletedDateBefore);
}

View File

@ -38,6 +38,10 @@ public class CipherService : ICipherService
private const long _fileSizeLeeway = 1024L * 1024L; // 1MB
private readonly IReferenceEventService _referenceEventService;
private readonly ICurrentContext _currentContext;
private readonly IFeatureService _featureService;
private bool UseFlexibleCollections =>
_featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext);
public CipherService(
ICipherRepository cipherRepository,
@ -54,7 +58,8 @@ public class CipherService : ICipherService
IPolicyService policyService,
GlobalSettings globalSettings,
IReferenceEventService referenceEventService,
ICurrentContext currentContext)
ICurrentContext currentContext,
IFeatureService featureService)
{
_cipherRepository = cipherRepository;
_folderRepository = folderRepository;
@ -71,6 +76,7 @@ public class CipherService : ICipherService
_globalSettings = globalSettings;
_referenceEventService = referenceEventService;
_currentContext = currentContext;
_featureService = featureService;
}
public async Task SaveAsync(Cipher cipher, Guid savingUserId, DateTime? lastKnownRevisionDate,
@ -424,9 +430,10 @@ public class CipherService : ICipherService
}
else
{
var ciphers = await _cipherRepository.GetManyByUserIdAsync(deletingUserId);
var ciphers = await _cipherRepository.GetManyByUserIdAsync(deletingUserId, useFlexibleCollections: UseFlexibleCollections);
deletingCiphers = ciphers.Where(c => cipherIdsSet.Contains(c.Id) && c.Edit).Select(x => (Cipher)x).ToList();
await _cipherRepository.DeleteAsync(deletingCiphers.Select(c => c.Id), deletingUserId);
await _cipherRepository.DeleteAsync(deletingCiphers.Select(c => c.Id), deletingUserId, UseFlexibleCollections);
}
var events = deletingCiphers.Select(c =>
@ -478,7 +485,7 @@ public class CipherService : ICipherService
}
}
await _cipherRepository.MoveAsync(cipherIds, destinationFolderId, movingUserId);
await _cipherRepository.MoveAsync(cipherIds, destinationFolderId, movingUserId, UseFlexibleCollections);
// push
await _pushService.PushSyncCiphersAsync(movingUserId);
}
@ -865,9 +872,10 @@ public class CipherService : ICipherService
}
else
{
var ciphers = await _cipherRepository.GetManyByUserIdAsync(deletingUserId);
var ciphers = await _cipherRepository.GetManyByUserIdAsync(deletingUserId, useFlexibleCollections: UseFlexibleCollections);
deletingCiphers = ciphers.Where(c => cipherIdsSet.Contains(c.Id) && c.Edit).Select(x => (Cipher)x).ToList();
await _cipherRepository.SoftDeleteAsync(deletingCiphers.Select(c => c.Id), deletingUserId);
await _cipherRepository.SoftDeleteAsync(deletingCiphers.Select(c => c.Id), deletingUserId, UseFlexibleCollections);
}
var events = deletingCiphers.Select(c =>
@ -930,9 +938,10 @@ public class CipherService : ICipherService
}
else
{
var ciphers = await _cipherRepository.GetManyByUserIdAsync(restoringUserId);
var ciphers = await _cipherRepository.GetManyByUserIdAsync(restoringUserId, useFlexibleCollections: UseFlexibleCollections);
restoringCiphers = ciphers.Where(c => cipherIdsSet.Contains(c.Id) && c.Edit).Select(c => (CipherOrganizationDetails)c).ToList();
revisionDate = await _cipherRepository.RestoreAsync(restoringCiphers.Select(c => c.Id), restoringUserId);
revisionDate = await _cipherRepository.RestoreAsync(restoringCiphers.Select(c => c.Id), restoringUserId, UseFlexibleCollections);
}
var events = restoringCiphers.Select(c =>
@ -967,7 +976,7 @@ public class CipherService : ICipherService
}
else
{
var ciphers = await _cipherRepository.GetManyByUserIdAsync(userId, true);
var ciphers = await _cipherRepository.GetManyByUserIdAsync(userId, useFlexibleCollections: UseFlexibleCollections, withOrganizations: true);
orgCiphers = ciphers.Where(c => c.OrganizationId == organizationId);
}