1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-24 04:38:48 -05:00

[PM-22503] Fix manage cipher permission (#5972)

* Added new tests to validate that the ciphers are being grouped and filtered correctly when assigned to multiple collections and changing order of grouping properties.
This commit is contained in:
Jared McCannon 2025-06-23 12:11:32 -04:00 committed by GitHub
parent cdfe51f9d6
commit d2410747d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 72 additions and 3 deletions

View File

@ -98,7 +98,10 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository
return results
.GroupBy(c => c.Id)
.Select(g => g.OrderByDescending(og => og.Edit).ThenByDescending(og => og.ViewPassword).First())
.Select(g =>
g.OrderByDescending(og => og.Manage)
.ThenByDescending(og => og.Edit)
.ThenByDescending(og => og.ViewPassword).First())
.ToList();
}
}

View File

@ -457,7 +457,7 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
IQueryable<CipherDetails> cipherDetailsView = withOrganizations ?
var cipherDetailsView = withOrganizations ?
new UserCipherDetailsQuery(userId).Run(dbContext) :
new CipherDetailsQuery(userId).Run(dbContext);
if (!withOrganizations)
@ -485,8 +485,15 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
Key = c.Key
};
}
var ciphers = await cipherDetailsView.ToListAsync();
return ciphers;
return ciphers.GroupBy(c => c.Id)
.Select(g => g.OrderByDescending(c => c.Manage)
.ThenByDescending(c => c.Edit)
.ThenByDescending(c => c.ViewPassword)
.First())
.ToList();
}
}

View File

@ -571,6 +571,65 @@ public class CipherRepositoryTests
Assert.True(personalDetails.Manage, "Personal ciphers should always have Manage permission");
}
[DatabaseTheory, DatabaseData]
public async Task GetManyByUserIdAsync_WhenOneCipherIsAssignedToTwoCollectionsWithDifferentPermissions_MostPrivilegedAccessIsReturnedOnTheCipher(
ICipherRepository cipherRepository,
IUserRepository userRepository,
ICollectionCipherRepository collectionCipherRepository,
ICollectionRepository collectionRepository,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository)
{
//Arrange
var (user, organization, orgUser) = await CreateTestUserAndOrganization(userRepository, organizationRepository, organizationUserRepository);
var cipher = await cipherRepository.CreateAsync(new Cipher
{
Type = CipherType.Login,
OrganizationId = organization.Id,
Data = ""
});
var managedPermissionsCollection = await collectionRepository.CreateAsync(new Collection
{
Name = "Managed",
OrganizationId = organization.Id
});
var unmanagedPermissionsCollection = await collectionRepository.CreateAsync(new Collection
{
Name = "Unmanaged",
OrganizationId = organization.Id
});
await collectionCipherRepository.UpdateCollectionsForAdminAsync(cipher.Id, organization.Id,
[managedPermissionsCollection.Id, unmanagedPermissionsCollection.Id]);
await collectionRepository.UpdateUsersAsync(managedPermissionsCollection.Id, new List<CollectionAccessSelection>
{
new() { Id = orgUser.Id, HidePasswords = false, ReadOnly = false, Manage = true }
});
await collectionRepository.UpdateUsersAsync(unmanagedPermissionsCollection.Id, new List<CollectionAccessSelection>
{
new() { Id = orgUser.Id, HidePasswords = false, ReadOnly = false, Manage = false }
});
// Act
var ciphers = await cipherRepository.GetManyByUserIdAsync(user.Id);
// Assert
Assert.Single(ciphers);
var deletableCipher = ciphers.SingleOrDefault(x => x.Id == cipher.Id);
Assert.NotNull(deletableCipher);
Assert.True(deletableCipher.Manage);
// Annul
await cipherRepository.DeleteAsync(cipher);
await organizationUserRepository.DeleteAsync(orgUser);
await organizationRepository.DeleteAsync(organization);
await userRepository.DeleteAsync(user);
}
private async Task<(User user, Organization org, OrganizationUser orgUser)> CreateTestUserAndOrganization(
IUserRepository userRepository,
IOrganizationRepository organizationRepository,