mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 15:42:48 -05:00
[PM-14378] SecurityTask Authorization Handler (#5039)
* [PM-14378] Introduce GetCipherPermissionsForOrganization query for Dapper CipherRepository * [PM-14378] Introduce GetCipherPermissionsForOrganization method for Entity Framework * [PM-14378] Add integration tests for new repository method * [PM-14378] Introduce IGetCipherPermissionsForUserQuery CQRS query * [PM-14378] Introduce SecurityTaskOperationRequirement * [PM-14378] Introduce SecurityTaskAuthorizationHandler.cs * [PM-14378] Introduce SecurityTaskOrganizationAuthorizationHandler.cs * [PM-14378] Register new authorization handlers * [PM-14378] Formatting * [PM-14378] Add unit tests for GetCipherPermissionsForUserQuery * [PM-15378] Cleanup SecurityTaskAuthorizationHandler and add tests * [PM-14378] Add tests for SecurityTaskOrganizationAuthorizationHandler * [PM-14378] Formatting * [PM-14378] Update date in migration file * [PM-14378] Add missing awaits * [PM-14378] Bump migration script date * [PM-14378] Remove Unassigned property from OrganizationCipherPermission as it was making the query too complicated * [PM-14378] Update sproc to use Union All to improve query performance * [PM-14378] Bump migration script date
This commit is contained in:
@ -0,0 +1,238 @@
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data.Organizations;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Vault.Models.Data;
|
||||
using Bit.Core.Vault.Queries;
|
||||
using Bit.Core.Vault.Repositories;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Vault.Queries;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class GetCipherPermissionsForUserQueryTests
|
||||
{
|
||||
private static Guid _noAccessCipherId = Guid.NewGuid();
|
||||
private static Guid _readOnlyCipherId = Guid.NewGuid();
|
||||
private static Guid _editCipherId = Guid.NewGuid();
|
||||
private static Guid _manageCipherId = Guid.NewGuid();
|
||||
private static Guid _readExceptPasswordCipherId = Guid.NewGuid();
|
||||
private static Guid _unassignedCipherId = Guid.NewGuid();
|
||||
|
||||
private static List<Guid> _cipherIds = new[]
|
||||
{
|
||||
_noAccessCipherId,
|
||||
_readOnlyCipherId,
|
||||
_editCipherId,
|
||||
_manageCipherId,
|
||||
_readExceptPasswordCipherId,
|
||||
_unassignedCipherId
|
||||
}.ToList();
|
||||
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetCipherPermissionsForUserQuery_Base(Guid userId, CurrentContextOrganization org, SutProvider<GetCipherPermissionsForUserQuery> sutProvider
|
||||
)
|
||||
{
|
||||
var organizationId = org.Id;
|
||||
org.Type = OrganizationUserType.User;
|
||||
org.Permissions.EditAnyCollection = false;
|
||||
var cipherPermissions = CreateCipherPermissions();
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organizationId).Returns(org);
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
|
||||
|
||||
sutProvider.GetDependency<ICipherRepository>().GetCipherPermissionsForOrganizationAsync(organizationId, userId)
|
||||
.Returns(cipherPermissions);
|
||||
sutProvider.GetDependency<ICipherRepository>()
|
||||
.GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organizationId)
|
||||
.Returns(new List<CipherOrganizationDetails>
|
||||
{
|
||||
new() { Id = _unassignedCipherId }
|
||||
});
|
||||
|
||||
var result = await sutProvider.Sut.GetByOrganization(organizationId);
|
||||
|
||||
Assert.Equal(6, result.Count);
|
||||
Assert.All(result, x => Assert.Contains(x.Key, _cipherIds));
|
||||
Assert.False(result[_noAccessCipherId].Read);
|
||||
Assert.True(result[_readOnlyCipherId].Read);
|
||||
Assert.False(result[_readOnlyCipherId].Edit);
|
||||
Assert.True(result[_editCipherId].Edit);
|
||||
Assert.True(result[_manageCipherId].Manage);
|
||||
Assert.True(result[_readExceptPasswordCipherId].Read);
|
||||
Assert.False(result[_readExceptPasswordCipherId].ViewPassword);
|
||||
Assert.False(result[_unassignedCipherId].Read);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetCipherPermissionsForUserQuery_CanEditAllCiphers_CustomUser(Guid userId, CurrentContextOrganization org, SutProvider<GetCipherPermissionsForUserQuery> sutProvider
|
||||
)
|
||||
{
|
||||
var organizationId = org.Id;
|
||||
var cipherPermissions = CreateCipherPermissions();
|
||||
org.Permissions.EditAnyCollection = true;
|
||||
org.Type = OrganizationUserType.Custom;
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organizationId).Returns(org);
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
|
||||
|
||||
sutProvider.GetDependency<ICipherRepository>().GetCipherPermissionsForOrganizationAsync(organizationId, userId)
|
||||
.Returns(cipherPermissions);
|
||||
sutProvider.GetDependency<ICipherRepository>()
|
||||
.GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organizationId)
|
||||
.Returns(new List<CipherOrganizationDetails>
|
||||
{
|
||||
new() { Id = _unassignedCipherId }
|
||||
});
|
||||
|
||||
var result = await sutProvider.Sut.GetByOrganization(organizationId);
|
||||
|
||||
Assert.Equal(6, result.Count);
|
||||
Assert.All(result, x => Assert.Contains(x.Key, _cipherIds));
|
||||
Assert.All(result, x => Assert.True(x.Value.Read && x.Value.Edit && x.Value.Manage && x.Value.ViewPassword));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetCipherPermissionsForUserQuery_CanEditAllCiphers_Admin(Guid userId, CurrentContextOrganization org, SutProvider<GetCipherPermissionsForUserQuery> sutProvider
|
||||
)
|
||||
{
|
||||
var organizationId = org.Id;
|
||||
var cipherPermissions = CreateCipherPermissions();
|
||||
org.Permissions.EditAnyCollection = false;
|
||||
org.Type = OrganizationUserType.Admin;
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organizationId).Returns(org);
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
|
||||
|
||||
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(org.Id).Returns(new OrganizationAbility
|
||||
{
|
||||
AllowAdminAccessToAllCollectionItems = true
|
||||
});
|
||||
|
||||
sutProvider.GetDependency<ICipherRepository>().GetCipherPermissionsForOrganizationAsync(organizationId, userId)
|
||||
.Returns(cipherPermissions);
|
||||
sutProvider.GetDependency<ICipherRepository>()
|
||||
.GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organizationId)
|
||||
.Returns(new List<CipherOrganizationDetails>
|
||||
{
|
||||
new() { Id = _unassignedCipherId }
|
||||
});
|
||||
|
||||
var result = await sutProvider.Sut.GetByOrganization(organizationId);
|
||||
|
||||
Assert.Equal(6, result.Count);
|
||||
Assert.All(result, x => Assert.Contains(x.Key, _cipherIds));
|
||||
Assert.All(result, x => Assert.True(x.Value.Read && x.Value.Edit && x.Value.Manage && x.Value.ViewPassword));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetCipherPermissionsForUserQuery_CanEditUnassignedCiphers(Guid userId, CurrentContextOrganization org, SutProvider<GetCipherPermissionsForUserQuery> sutProvider
|
||||
)
|
||||
{
|
||||
var organizationId = org.Id;
|
||||
var cipherPermissions = CreateCipherPermissions();
|
||||
org.Type = OrganizationUserType.Owner;
|
||||
org.Permissions.EditAnyCollection = false;
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organizationId).Returns(org);
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
|
||||
|
||||
sutProvider.GetDependency<ICipherRepository>().GetCipherPermissionsForOrganizationAsync(organizationId, userId)
|
||||
.Returns(cipherPermissions);
|
||||
sutProvider.GetDependency<ICipherRepository>()
|
||||
.GetManyUnassignedOrganizationDetailsByOrganizationIdAsync(organizationId)
|
||||
.Returns(new List<CipherOrganizationDetails>
|
||||
{
|
||||
new() { Id = _unassignedCipherId }
|
||||
});
|
||||
|
||||
var result = await sutProvider.Sut.GetByOrganization(organizationId);
|
||||
|
||||
Assert.Equal(6, result.Count);
|
||||
Assert.All(result, x => Assert.Contains(x.Key, _cipherIds));
|
||||
Assert.False(result[_noAccessCipherId].Read);
|
||||
Assert.True(result[_readOnlyCipherId].Read);
|
||||
Assert.False(result[_readOnlyCipherId].Edit);
|
||||
Assert.True(result[_editCipherId].Edit);
|
||||
Assert.True(result[_manageCipherId].Manage);
|
||||
Assert.True(result[_readExceptPasswordCipherId].Read);
|
||||
Assert.False(result[_readExceptPasswordCipherId].ViewPassword);
|
||||
|
||||
Assert.True(result[_unassignedCipherId].Read);
|
||||
Assert.True(result[_unassignedCipherId].Edit);
|
||||
Assert.True(result[_unassignedCipherId].ViewPassword);
|
||||
Assert.True(result[_unassignedCipherId].Manage);
|
||||
}
|
||||
|
||||
private List<OrganizationCipherPermission> CreateCipherPermissions()
|
||||
{
|
||||
// User has no relationship with the cipher
|
||||
var noAccessCipher = new OrganizationCipherPermission
|
||||
{
|
||||
Id = _noAccessCipherId,
|
||||
Read = false,
|
||||
Edit = false,
|
||||
Manage = false,
|
||||
ViewPassword = false,
|
||||
};
|
||||
|
||||
var readOnlyCipher = new OrganizationCipherPermission
|
||||
{
|
||||
Id = _readOnlyCipherId,
|
||||
Read = true,
|
||||
Edit = false,
|
||||
Manage = false,
|
||||
ViewPassword = true,
|
||||
};
|
||||
|
||||
var editCipher = new OrganizationCipherPermission
|
||||
{
|
||||
Id = _editCipherId,
|
||||
Read = true,
|
||||
Edit = true,
|
||||
Manage = false,
|
||||
ViewPassword = true,
|
||||
};
|
||||
|
||||
var manageCipher = new OrganizationCipherPermission
|
||||
{
|
||||
Id = _manageCipherId,
|
||||
Read = true,
|
||||
Edit = true,
|
||||
Manage = true,
|
||||
ViewPassword = true,
|
||||
};
|
||||
|
||||
var readExceptPasswordCipher = new OrganizationCipherPermission
|
||||
{
|
||||
Id = _readExceptPasswordCipherId,
|
||||
Read = true,
|
||||
Edit = false,
|
||||
Manage = false,
|
||||
ViewPassword = false,
|
||||
};
|
||||
|
||||
var unassignedCipher = new OrganizationCipherPermission
|
||||
{
|
||||
Id = _unassignedCipherId,
|
||||
Read = false,
|
||||
Edit = false,
|
||||
Manage = false,
|
||||
ViewPassword = false,
|
||||
};
|
||||
|
||||
return new List<OrganizationCipherPermission>
|
||||
{
|
||||
noAccessCipher,
|
||||
readOnlyCipher,
|
||||
editCipher,
|
||||
manageCipher,
|
||||
readExceptPasswordCipher,
|
||||
unassignedCipher
|
||||
};
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user