mirror of
https://github.com/bitwarden/server
synced 2026-01-09 12:03:21 +00:00
[PM-23987] Fix saving to default collections by updating collection lookup (#6122)
* Refactor ICollectionRepository.GetManyByOrganizationIdAsync logic to include default user collections * Add stored procedure Collection_ReadSharedCollectionsByOrganizationId to retrieve collections by organization ID, excluding default user collections. * Add GetManySharedCollectionsByOrganizationIdAsync method to ICollectionRepository and its implementations to retrieve collections excluding default user collections. * Add unit test for GetManySharedCollectionsByOrganizationIdAsync method in CollectionRepositoryTests to verify retrieval of collections excluding default user collections. * Refactor controllers to use GetManySharedCollectionsByOrganizationIdAsync for retrieving shared collections * Update unit tests to use GetManySharedCollectionsByOrganizationIdAsync for verifying shared collections retrieval * Revert CiphersController.CanEditItemsInCollections to use GetManyByOrganizationIdAsync for retrieving organization collections * Update stored procedures to retrieve only DefaultUserCollection by modifying the WHERE clause in Collection_ReadSharedCollectionsByOrganizationId.sql and its corresponding migration script. * Update EF CollectionRepository.GetManySharedCollectionsByOrganizationIdAsync to filter collections by SharedCollection * Update OrganizationUserRepository.GetManyDetailsByOrganizationAsync_vNext to only include Shared collections * Update comments in stored procedure and migration script to clarify filtering for SharedCollections only
This commit is contained in:
@@ -109,7 +109,7 @@ public class CollectionsController : Controller
|
||||
var readAllAuthorized = (await _authorizationService.AuthorizeAsync(User, CollectionOperations.ReadAll(orgId))).Succeeded;
|
||||
if (readAllAuthorized)
|
||||
{
|
||||
orgCollections = await _collectionRepository.GetManyByOrganizationIdAsync(orgId);
|
||||
orgCollections = await _collectionRepository.GetManySharedCollectionsByOrganizationIdAsync(orgId);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -65,7 +65,7 @@ public class CollectionsController : Controller
|
||||
[ProducesResponseType(typeof(ListResponseModel<CollectionResponseModel>), (int)HttpStatusCode.OK)]
|
||||
public async Task<IActionResult> List()
|
||||
{
|
||||
var collections = await _collectionRepository.GetManyByOrganizationIdAsync(
|
||||
var collections = await _collectionRepository.GetManySharedCollectionsByOrganizationIdAsync(
|
||||
_currentContext.OrganizationId.Value);
|
||||
// TODO: Get all CollectionGroup associations for the organization and marry them up here for the response.
|
||||
var collectionResponses = collections.Select(c => new CollectionResponseModel(c, null));
|
||||
|
||||
@@ -16,10 +16,16 @@ public interface ICollectionRepository : IRepository<Collection, Guid>
|
||||
|
||||
/// <summary>
|
||||
/// Return all collections that belong to the organization. Does not include any permission details or group/user
|
||||
/// access relationships. Excludes default collections (My Items collections).
|
||||
/// access relationships.
|
||||
/// </summary>
|
||||
Task<ICollection<Collection>> GetManyByOrganizationIdAsync(Guid organizationId);
|
||||
|
||||
/// <inheritdoc cref="GetManyByOrganizationIdAsync"/>
|
||||
/// <remarks>
|
||||
/// Excludes default collections (My Items collections) - used by Admin Console Collections tab.
|
||||
/// </remarks>
|
||||
Task<ICollection<Collection>> GetManySharedCollectionsByOrganizationIdAsync(Guid organizationId);
|
||||
|
||||
/// <summary>
|
||||
/// Return all shared collections that belong to the organization. Includes group/user access relationships for each collection.
|
||||
/// </summary>
|
||||
|
||||
@@ -79,6 +79,19 @@ public class CollectionRepository : Repository<Collection, Guid>, ICollectionRep
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<Collection>> GetManySharedCollectionsByOrganizationIdAsync(Guid organizationId)
|
||||
{
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<Collection>(
|
||||
$"[{Schema}].[{Table}_ReadSharedCollectionsByOrganizationId]",
|
||||
new { OrganizationId = organizationId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return results.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<Tuple<Collection, CollectionAccessDetails>>> GetManyByOrganizationIdWithAccessAsync(Guid organizationId)
|
||||
{
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
|
||||
@@ -441,13 +441,15 @@ public class OrganizationUserRepository : Repository<Core.Entities.OrganizationU
|
||||
: new List<Guid>(),
|
||||
|
||||
Collections = includeCollections
|
||||
? ou.CollectionUsers.Select(cu => new CollectionAccessSelection
|
||||
{
|
||||
Id = cu.CollectionId,
|
||||
ReadOnly = cu.ReadOnly,
|
||||
HidePasswords = cu.HidePasswords,
|
||||
Manage = cu.Manage
|
||||
}).ToList()
|
||||
? ou.CollectionUsers
|
||||
.Where(cu => cu.Collection.Type == CollectionType.SharedCollection)
|
||||
.Select(cu => new CollectionAccessSelection
|
||||
{
|
||||
Id = cu.CollectionId,
|
||||
ReadOnly = cu.ReadOnly,
|
||||
HidePasswords = cu.HidePasswords,
|
||||
Manage = cu.Manage
|
||||
}).ToList()
|
||||
: new List<CollectionAccessSelection>()
|
||||
};
|
||||
|
||||
|
||||
@@ -212,13 +212,26 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
|
||||
}
|
||||
|
||||
public async Task<ICollection<Core.Entities.Collection>> GetManyByOrganizationIdAsync(Guid organizationId)
|
||||
{
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
var query = from c in dbContext.Collections
|
||||
where c.OrganizationId == organizationId
|
||||
select c;
|
||||
var collections = await query.ToArrayAsync();
|
||||
return collections;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<Core.Entities.Collection>> GetManySharedCollectionsByOrganizationIdAsync(Guid organizationId)
|
||||
{
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
var query = from c in dbContext.Collections
|
||||
where c.OrganizationId == organizationId &&
|
||||
c.Type != CollectionType.DefaultUserCollection
|
||||
c.Type == CollectionType.SharedCollection
|
||||
select c;
|
||||
var collections = await query.ToArrayAsync();
|
||||
return collections;
|
||||
|
||||
@@ -9,6 +9,5 @@ BEGIN
|
||||
FROM
|
||||
[dbo].[CollectionView]
|
||||
WHERE
|
||||
[OrganizationId] = @OrganizationId AND
|
||||
[Type] != 1 -- Exclude DefaultUserCollection
|
||||
[OrganizationId] = @OrganizationId
|
||||
END
|
||||
@@ -0,0 +1,14 @@
|
||||
CREATE PROCEDURE [dbo].[Collection_ReadSharedCollectionsByOrganizationId]
|
||||
@OrganizationId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
[dbo].[CollectionView]
|
||||
WHERE
|
||||
[OrganizationId] = @OrganizationId AND
|
||||
[Type] = 0 -- SharedCollections only
|
||||
END
|
||||
@@ -26,6 +26,8 @@ BEGIN
|
||||
SELECT cu.*
|
||||
FROM [dbo].[CollectionUser] cu
|
||||
INNER JOIN [dbo].[OrganizationUser] ou ON cu.OrganizationUserId = ou.Id
|
||||
WHERE ou.OrganizationId = @OrganizationId
|
||||
INNER JOIN [dbo].[Collection] c ON cu.CollectionId = c.Id
|
||||
WHERE ou.OrganizationId = @OrganizationId
|
||||
AND c.Type = 0 -- SharedCollections only
|
||||
END
|
||||
END
|
||||
|
||||
Reference in New Issue
Block a user