1
0
mirror of https://github.com/bitwarden/server synced 2025-12-25 12:43:14 +00:00

[AC-2084] Include Collection permissions for admin endpoints (#3793)

* [AC-2084] Add documentation to existing collection repository getters

* [AC-2084] Add new CollectionAdminDetails model

* [AC-2084] Add SQL and migration scripts

* [AC-2084] Introduce new repository methods to include permission details for collections

* [AC-2084] Add EF repository methods and integration tests

* [AC-2084] Update CollectionsController and response models

* [AC-2084] Fix failing SqlServer test

* [AC-2084] Clean up admin endpoint response models
- vNext endpoints should now always return CollectionDetailsResponse models
- Update constructors in CollectionDetailsResponseModel to be more explicit and add named static constructors for additional clarity

* [AC-2084] Fix failing tests

* [AC-2084] Fix potential provider/member bug

* [AC-2084] Fix broken collections controller

* [AC-2084] Cleanup collection response model types and constructors

* [AC-2084] Remove redundant authorization check

* [AC-2084] Cleanup ambiguous model name

* [AC-2084] Add GroupBy clause to sprocs

* [AC-2084] Add GroupBy logic to EF repository

* [AC-2084] Update collection repository tests

* [AC-2084] Update migration script date

* Update migration script date

---------

Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
Co-authored-by: kejaeger <138028972+kejaeger@users.noreply.github.com>
This commit is contained in:
Shane Melton
2024-05-03 06:33:06 -07:00
committed by GitHub
parent 25c87214ff
commit d965166a37
14 changed files with 1232 additions and 45 deletions

View File

@@ -370,6 +370,210 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
}
}
public async Task<ICollection<CollectionAdminDetails>> GetManyByOrganizationIdWithPermissionsAsync(
Guid organizationId, Guid userId, bool includeAccessRelationships)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = CollectionAdminDetailsQuery.ByOrganizationId(organizationId, userId).Run(dbContext);
ICollection<CollectionAdminDetails> collections;
// SQLite does not support the GROUP BY clause
if (dbContext.Database.IsSqlite())
{
collections = (await query.ToListAsync())
.GroupBy(c => new
{
c.Id,
c.OrganizationId,
c.Name,
c.CreationDate,
c.RevisionDate,
c.ExternalId
}).Select(collectionGroup => new CollectionAdminDetails
{
Id = collectionGroup.Key.Id,
OrganizationId = collectionGroup.Key.OrganizationId,
Name = collectionGroup.Key.Name,
CreationDate = collectionGroup.Key.CreationDate,
RevisionDate = collectionGroup.Key.RevisionDate,
ExternalId = collectionGroup.Key.ExternalId,
ReadOnly = Convert.ToBoolean(collectionGroup.Min(c => Convert.ToInt32(c.ReadOnly))),
HidePasswords =
Convert.ToBoolean(collectionGroup.Min(c => Convert.ToInt32(c.HidePasswords))),
Manage = Convert.ToBoolean(collectionGroup.Max(c => Convert.ToInt32(c.Manage))),
Assigned = Convert.ToBoolean(collectionGroup.Max(c => Convert.ToInt32(c.Assigned)))
}).ToList();
}
else
{
collections = await (from c in query
group c by new
{
c.Id,
c.OrganizationId,
c.Name,
c.CreationDate,
c.RevisionDate,
c.ExternalId
}
into collectionGroup
select new CollectionAdminDetails
{
Id = collectionGroup.Key.Id,
OrganizationId = collectionGroup.Key.OrganizationId,
Name = collectionGroup.Key.Name,
CreationDate = collectionGroup.Key.CreationDate,
RevisionDate = collectionGroup.Key.RevisionDate,
ExternalId = collectionGroup.Key.ExternalId,
ReadOnly = Convert.ToBoolean(collectionGroup.Min(c => Convert.ToInt32(c.ReadOnly))),
HidePasswords =
Convert.ToBoolean(collectionGroup.Min(c => Convert.ToInt32(c.HidePasswords))),
Manage = Convert.ToBoolean(collectionGroup.Max(c => Convert.ToInt32(c.Manage))),
Assigned = Convert.ToBoolean(collectionGroup.Max(c => Convert.ToInt32(c.Assigned)))
}).ToListAsync();
}
if (!includeAccessRelationships)
{
return collections;
}
var groups = (from c in collections
join cg in dbContext.CollectionGroups on c.Id equals cg.CollectionId
group cg by cg.CollectionId into g
select g).ToList();
var users = (from c in collections
join cu in dbContext.CollectionUsers on c.Id equals cu.CollectionId
group cu by cu.CollectionId into u
select u).ToList();
foreach (var collection in collections)
{
collection.Groups = groups
.FirstOrDefault(g => g.Key == collection.Id)?
.Select(g => new CollectionAccessSelection
{
Id = g.GroupId,
HidePasswords = g.HidePasswords,
ReadOnly = g.ReadOnly,
Manage = g.Manage,
}).ToList() ?? new List<CollectionAccessSelection>();
collection.Users = users
.FirstOrDefault(u => u.Key == collection.Id)?
.Select(c => new CollectionAccessSelection
{
Id = c.OrganizationUserId,
HidePasswords = c.HidePasswords,
ReadOnly = c.ReadOnly,
Manage = c.Manage
}).ToList() ?? new List<CollectionAccessSelection>();
}
return collections;
}
}
public async Task<CollectionAdminDetails> GetByIdWithPermissionsAsync(Guid collectionId, Guid? userId,
bool includeAccessRelationships)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = CollectionAdminDetailsQuery.ByCollectionId(collectionId, userId).Run(dbContext);
CollectionAdminDetails collectionDetails;
// SQLite does not support the GROUP BY clause
if (dbContext.Database.IsSqlite())
{
collectionDetails = (await query.ToListAsync())
.GroupBy(c => new
{
c.Id,
c.OrganizationId,
c.Name,
c.CreationDate,
c.RevisionDate,
c.ExternalId
}).Select(collectionGroup => new CollectionAdminDetails
{
Id = collectionGroup.Key.Id,
OrganizationId = collectionGroup.Key.OrganizationId,
Name = collectionGroup.Key.Name,
CreationDate = collectionGroup.Key.CreationDate,
RevisionDate = collectionGroup.Key.RevisionDate,
ExternalId = collectionGroup.Key.ExternalId,
ReadOnly = Convert.ToBoolean(collectionGroup.Min(c => Convert.ToInt32(c.ReadOnly))),
HidePasswords =
Convert.ToBoolean(collectionGroup.Min(c => Convert.ToInt32(c.HidePasswords))),
Manage = Convert.ToBoolean(collectionGroup.Max(c => Convert.ToInt32(c.Manage))),
Assigned = Convert.ToBoolean(collectionGroup.Max(c => Convert.ToInt32(c.Assigned)))
}).FirstOrDefault();
}
else
{
collectionDetails = await (from c in query
group c by new
{
c.Id,
c.OrganizationId,
c.Name,
c.CreationDate,
c.RevisionDate,
c.ExternalId
}
into collectionGroup
select new CollectionAdminDetails
{
Id = collectionGroup.Key.Id,
OrganizationId = collectionGroup.Key.OrganizationId,
Name = collectionGroup.Key.Name,
CreationDate = collectionGroup.Key.CreationDate,
RevisionDate = collectionGroup.Key.RevisionDate,
ExternalId = collectionGroup.Key.ExternalId,
ReadOnly = Convert.ToBoolean(collectionGroup.Min(c => Convert.ToInt32(c.ReadOnly))),
HidePasswords =
Convert.ToBoolean(collectionGroup.Min(c => Convert.ToInt32(c.HidePasswords))),
Manage = Convert.ToBoolean(collectionGroup.Max(c => Convert.ToInt32(c.Manage))),
Assigned = Convert.ToBoolean(collectionGroup.Max(c => Convert.ToInt32(c.Assigned)))
}).FirstOrDefaultAsync();
}
if (!includeAccessRelationships)
{
return collectionDetails;
}
var groupsQuery = from cg in dbContext.CollectionGroups
where cg.CollectionId.Equals(collectionId)
select new CollectionAccessSelection
{
Id = cg.GroupId,
ReadOnly = cg.ReadOnly,
HidePasswords = cg.HidePasswords,
Manage = cg.Manage
};
collectionDetails.Groups = await groupsQuery.ToListAsync();
var usersQuery = from cg in dbContext.CollectionUsers
where cg.CollectionId.Equals(collectionId)
select new CollectionAccessSelection
{
Id = cg.OrganizationUserId,
ReadOnly = cg.ReadOnly,
HidePasswords = cg.HidePasswords,
Manage = cg.Manage
};
collectionDetails.Users = await usersQuery.ToListAsync();
return collectionDetails;
}
}
public async Task<ICollection<CollectionAccessSelection>> GetManyUsersByIdAsync(Guid id)
{
using (var scope = ServiceScopeFactory.CreateScope())