mirror of
https://github.com/bitwarden/server.git
synced 2025-07-13 13:47:30 -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:
@ -302,6 +302,52 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<OrganizationCipherPermission>>
|
||||
GetCipherPermissionsForOrganizationAsync(Guid organizationId, Guid userId)
|
||||
{
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
var query = new CipherOrganizationPermissionsQuery(organizationId, userId).Run(dbContext);
|
||||
|
||||
ICollection<OrganizationCipherPermission> permissions;
|
||||
|
||||
// SQLite does not support the GROUP BY clause
|
||||
if (dbContext.Database.IsSqlite())
|
||||
{
|
||||
permissions = (await query.ToListAsync())
|
||||
.GroupBy(c => new { c.Id, c.OrganizationId })
|
||||
.Select(g => new OrganizationCipherPermission
|
||||
{
|
||||
Id = g.Key.Id,
|
||||
OrganizationId = g.Key.OrganizationId,
|
||||
Read = Convert.ToBoolean(g.Max(c => Convert.ToInt32(c.Read))),
|
||||
ViewPassword = Convert.ToBoolean(g.Max(c => Convert.ToInt32(c.ViewPassword))),
|
||||
Edit = Convert.ToBoolean(g.Max(c => Convert.ToInt32(c.Edit))),
|
||||
Manage = Convert.ToBoolean(g.Max(c => Convert.ToInt32(c.Manage))),
|
||||
}).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
var groupByQuery = from p in query
|
||||
group p by new { p.Id, p.OrganizationId }
|
||||
into g
|
||||
select new OrganizationCipherPermission
|
||||
{
|
||||
Id = g.Key.Id,
|
||||
OrganizationId = g.Key.OrganizationId,
|
||||
Read = Convert.ToBoolean(g.Max(c => Convert.ToInt32(c.Read))),
|
||||
ViewPassword = Convert.ToBoolean(g.Max(c => Convert.ToInt32(c.ViewPassword))),
|
||||
Edit = Convert.ToBoolean(g.Max(c => Convert.ToInt32(c.Edit))),
|
||||
Manage = Convert.ToBoolean(g.Max(c => Convert.ToInt32(c.Manage))),
|
||||
};
|
||||
permissions = await groupByQuery.ToListAsync();
|
||||
}
|
||||
|
||||
return permissions;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<CipherDetails> GetByIdAsync(Guid id, Guid userId)
|
||||
{
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
|
@ -0,0 +1,63 @@
|
||||
using Bit.Core.Vault.Models.Data;
|
||||
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||
using Bit.Infrastructure.EntityFramework.Repositories.Queries;
|
||||
|
||||
namespace Bit.Infrastructure.EntityFramework.Vault.Repositories.Queries;
|
||||
|
||||
public class CipherOrganizationPermissionsQuery : IQuery<OrganizationCipherPermission>
|
||||
{
|
||||
private readonly Guid _organizationId;
|
||||
private readonly Guid _userId;
|
||||
|
||||
public CipherOrganizationPermissionsQuery(Guid organizationId, Guid userId)
|
||||
{
|
||||
_organizationId = organizationId;
|
||||
_userId = userId;
|
||||
}
|
||||
|
||||
public IQueryable<OrganizationCipherPermission> Run(DatabaseContext dbContext)
|
||||
{
|
||||
return from c in dbContext.Ciphers
|
||||
|
||||
join ou in dbContext.OrganizationUsers
|
||||
on new { CipherUserId = c.UserId, c.OrganizationId, UserId = (Guid?)_userId } equals
|
||||
new { CipherUserId = (Guid?)null, OrganizationId = (Guid?)ou.OrganizationId, ou.UserId, }
|
||||
|
||||
join o in dbContext.Organizations
|
||||
on new { c.OrganizationId, OuOrganizationId = ou.OrganizationId, Enabled = true } equals
|
||||
new { OrganizationId = (Guid?)o.Id, OuOrganizationId = o.Id, o.Enabled }
|
||||
|
||||
join cc in dbContext.CollectionCiphers
|
||||
on c.Id equals cc.CipherId into cc_g
|
||||
from cc in cc_g.DefaultIfEmpty()
|
||||
|
||||
join cu in dbContext.CollectionUsers
|
||||
on new { cc.CollectionId, OrganizationUserId = ou.Id } equals
|
||||
new { cu.CollectionId, cu.OrganizationUserId } into cu_g
|
||||
from cu in cu_g.DefaultIfEmpty()
|
||||
|
||||
join gu in dbContext.GroupUsers
|
||||
on new { CollectionId = (Guid?)cu.CollectionId, OrganizationUserId = ou.Id } equals
|
||||
new { CollectionId = (Guid?)null, gu.OrganizationUserId } into gu_g
|
||||
from gu in gu_g.DefaultIfEmpty()
|
||||
|
||||
join g in dbContext.Groups
|
||||
on gu.GroupId equals g.Id into g_g
|
||||
from g in g_g.DefaultIfEmpty()
|
||||
|
||||
join cg in dbContext.CollectionGroups
|
||||
on new { cc.CollectionId, gu.GroupId } equals
|
||||
new { cg.CollectionId, cg.GroupId } into cg_g
|
||||
from cg in cg_g.DefaultIfEmpty()
|
||||
|
||||
select new OrganizationCipherPermission()
|
||||
{
|
||||
Id = c.Id,
|
||||
OrganizationId = o.Id,
|
||||
Read = cu != null || cg != null,
|
||||
ViewPassword = !((bool?)cu.HidePasswords ?? (bool?)cg.HidePasswords ?? true),
|
||||
Edit = !((bool?)cu.ReadOnly ?? (bool?)cg.ReadOnly ?? true),
|
||||
Manage = (bool?)cu.Manage ?? (bool?)cg.Manage ?? false,
|
||||
};
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user