mirror of
https://github.com/bitwarden/server.git
synced 2024-11-21 12:05:42 +01:00
[AC-1330] [AC-1850] Deprecate AccessAll in UserCollectionDetails and related sprocs (#3505)
This commit is contained in:
parent
f527623318
commit
985c438f03
2583
perf/MicroBenchmarks/packages.lock.json
Normal file
2583
perf/MicroBenchmarks/packages.lock.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -104,7 +104,7 @@ public class CollectionsController : Controller
|
||||
else
|
||||
{
|
||||
(var collection, var access) = await _collectionRepository.GetByIdWithAccessAsync(id,
|
||||
_currentContext.UserId.Value);
|
||||
_currentContext.UserId.Value, FlexibleCollectionsIsEnabled);
|
||||
if (collection == null || collection.OrganizationId != orgId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
@ -131,7 +131,8 @@ public class CollectionsController : Controller
|
||||
|
||||
// We always need to know which collections the current user is assigned to
|
||||
var assignedOrgCollections =
|
||||
await _collectionRepository.GetManyByUserIdWithAccessAsync(_currentContext.UserId.Value, orgId);
|
||||
await _collectionRepository.GetManyByUserIdWithAccessAsync(_currentContext.UserId.Value, orgId,
|
||||
FlexibleCollectionsIsEnabled);
|
||||
|
||||
if (await _currentContext.ViewAllCollections(orgId) || await _currentContext.ManageUsers(orgId))
|
||||
{
|
||||
@ -190,7 +191,7 @@ public class CollectionsController : Controller
|
||||
public async Task<ListResponseModel<CollectionDetailsResponseModel>> GetUser()
|
||||
{
|
||||
var collections = await _collectionRepository.GetManyByUserIdAsync(
|
||||
_userService.GetProperUserId(User).Value);
|
||||
_userService.GetProperUserId(User).Value, FlexibleCollectionsIsEnabled);
|
||||
var responses = collections.Select(c => new CollectionDetailsResponseModel(c));
|
||||
return new ListResponseModel<CollectionDetailsResponseModel>(responses);
|
||||
}
|
||||
@ -416,7 +417,7 @@ public class CollectionsController : Controller
|
||||
}
|
||||
else if (await _currentContext.ViewAssignedCollections(orgId))
|
||||
{
|
||||
collection = await _collectionRepository.GetByIdAsync(id, _currentContext.UserId.Value);
|
||||
collection = await _collectionRepository.GetByIdAsync(id, _currentContext.UserId.Value, FlexibleCollectionsIsEnabled);
|
||||
}
|
||||
|
||||
if (collection == null || collection.OrganizationId != orgId)
|
||||
@ -459,7 +460,7 @@ public class CollectionsController : Controller
|
||||
if (await _currentContext.EditAssignedCollections(orgId))
|
||||
{
|
||||
var collectionDetails =
|
||||
await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
|
||||
await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value, FlexibleCollectionsIsEnabled);
|
||||
return collectionDetails != null;
|
||||
}
|
||||
|
||||
@ -484,7 +485,7 @@ public class CollectionsController : Controller
|
||||
if (await _currentContext.DeleteAssignedCollections(orgId))
|
||||
{
|
||||
var collectionDetails =
|
||||
await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
|
||||
await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value, FlexibleCollectionsIsEnabled);
|
||||
return collectionDetails != null;
|
||||
}
|
||||
|
||||
@ -519,7 +520,7 @@ public class CollectionsController : Controller
|
||||
if (await _currentContext.ViewAssignedCollections(orgId))
|
||||
{
|
||||
var collectionDetails =
|
||||
await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
|
||||
await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value, FlexibleCollectionsIsEnabled);
|
||||
return collectionDetails != null;
|
||||
}
|
||||
|
||||
@ -563,7 +564,7 @@ public class CollectionsController : Controller
|
||||
{
|
||||
// We always need to know which collections the current user is assigned to
|
||||
var assignedOrgCollections = await _collectionRepository
|
||||
.GetManyByUserIdWithAccessAsync(_currentContext.UserId.Value, orgId);
|
||||
.GetManyByUserIdWithAccessAsync(_currentContext.UserId.Value, orgId, FlexibleCollectionsIsEnabled);
|
||||
|
||||
var readAllAuthorized =
|
||||
(await _authorizationService.AuthorizeAsync(User, CollectionOperations.ReadAllWithAccess(orgId))).Succeeded;
|
||||
@ -608,7 +609,7 @@ public class CollectionsController : Controller
|
||||
}
|
||||
else
|
||||
{
|
||||
var collections = await _collectionRepository.GetManyByUserIdAsync(_currentContext.UserId.Value);
|
||||
var collections = await _collectionRepository.GetManyByUserIdAsync(_currentContext.UserId.Value, FlexibleCollectionsIsEnabled);
|
||||
var readAuthorized = (await _authorizationService.AuthorizeAsync(User, collections, BulkCollectionOperations.Read)).Succeeded;
|
||||
if (readAuthorized)
|
||||
{
|
||||
|
@ -252,7 +252,7 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<BulkC
|
||||
{
|
||||
// List of collection Ids the acting user has access to
|
||||
var assignedCollectionIds =
|
||||
(await _collectionRepository.GetManyByUserIdAsync(_currentContext.UserId!.Value))
|
||||
(await _collectionRepository.GetManyByUserIdAsync(_currentContext.UserId!.Value, useFlexibleCollections: true))
|
||||
.Where(c =>
|
||||
// Check Collections with Manage permission
|
||||
(!requireManagePermission || c.Manage) && c.OrganizationId == org.Id)
|
||||
|
@ -94,9 +94,8 @@ public class SyncController : Controller
|
||||
|
||||
if (hasEnabledOrgs)
|
||||
{
|
||||
collections = await _collectionRepository.GetManyByUserIdAsync(user.Id);
|
||||
collections = await _collectionRepository.GetManyByUserIdAsync(user.Id, UseFlexibleCollections);
|
||||
var collectionCiphers = await _collectionCipherRepository.GetManyByUserIdAsync(user.Id, UseFlexibleCollections);
|
||||
collectionCiphersGroupDict = collectionCiphers.GroupBy(c => c.CipherId).ToDictionary(s => s.Key);
|
||||
}
|
||||
|
||||
var userTwoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
|
||||
|
@ -7,13 +7,13 @@ public interface ICollectionRepository : IRepository<Collection, Guid>
|
||||
{
|
||||
Task<int> GetCountByOrganizationIdAsync(Guid organizationId);
|
||||
Task<Tuple<Collection, CollectionAccessDetails>> GetByIdWithAccessAsync(Guid id);
|
||||
Task<Tuple<CollectionDetails, CollectionAccessDetails>> GetByIdWithAccessAsync(Guid id, Guid userId);
|
||||
Task<Tuple<CollectionDetails, CollectionAccessDetails>> GetByIdWithAccessAsync(Guid id, Guid userId, bool useFlexibleCollections);
|
||||
Task<ICollection<Collection>> GetManyByOrganizationIdAsync(Guid organizationId);
|
||||
Task<ICollection<Tuple<Collection, CollectionAccessDetails>>> GetManyByOrganizationIdWithAccessAsync(Guid organizationId);
|
||||
Task<ICollection<Tuple<CollectionDetails, CollectionAccessDetails>>> GetManyByUserIdWithAccessAsync(Guid userId, Guid organizationId);
|
||||
Task<CollectionDetails> GetByIdAsync(Guid id, Guid userId);
|
||||
Task<ICollection<Tuple<CollectionDetails, CollectionAccessDetails>>> GetManyByUserIdWithAccessAsync(Guid userId, Guid organizationId, bool useFlexibleCollections);
|
||||
Task<CollectionDetails> GetByIdAsync(Guid id, Guid userId, bool useFlexibleCollections);
|
||||
Task<ICollection<Collection>> GetManyByManyIdsAsync(IEnumerable<Guid> collectionIds);
|
||||
Task<ICollection<CollectionDetails>> GetManyByUserIdAsync(Guid userId);
|
||||
Task<ICollection<CollectionDetails>> GetManyByUserIdAsync(Guid userId, bool useFlexibleCollections);
|
||||
Task CreateAsync(Collection obj, IEnumerable<CollectionAccessSelection> groups, IEnumerable<CollectionAccessSelection> users);
|
||||
Task ReplaceAsync(Collection obj, IEnumerable<CollectionAccessSelection> groups, IEnumerable<CollectionAccessSelection> users);
|
||||
Task DeleteUserAsync(Guid collectionId, Guid organizationUserId);
|
||||
|
@ -43,6 +43,9 @@ public class CollectionService : ICollectionService
|
||||
_featureService = featureService;
|
||||
}
|
||||
|
||||
private bool UseFlexibleCollections =>
|
||||
_featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext);
|
||||
|
||||
public async Task SaveAsync(Collection collection, IEnumerable<CollectionAccessSelection> groups = null,
|
||||
IEnumerable<CollectionAccessSelection> users = null)
|
||||
{
|
||||
@ -56,7 +59,7 @@ public class CollectionService : ICollectionService
|
||||
var usersList = users?.ToList();
|
||||
|
||||
// If using Flexible Collections - a collection should always have someone with Can Manage permissions
|
||||
if (_featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext))
|
||||
if (UseFlexibleCollections)
|
||||
{
|
||||
var groupHasManageAccess = groupsList?.Any(g => g.Manage) ?? false;
|
||||
var userHasManageAccess = usersList?.Any(u => u.Manage) ?? false;
|
||||
@ -122,7 +125,7 @@ public class CollectionService : ICollectionService
|
||||
}
|
||||
else
|
||||
{
|
||||
var collections = await _collectionRepository.GetManyByUserIdAsync(_currentContext.UserId.Value);
|
||||
var collections = await _collectionRepository.GetManyByUserIdAsync(_currentContext.UserId.Value, UseFlexibleCollections);
|
||||
orgCollections = collections.Where(c => c.OrganizationId == organizationId);
|
||||
}
|
||||
|
||||
|
2453
src/Core/packages.lock.json
Normal file
2453
src/Core/packages.lock.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -51,12 +51,16 @@ public class CollectionRepository : Repository<Collection, Guid>, ICollectionRep
|
||||
}
|
||||
|
||||
public async Task<Tuple<CollectionDetails, CollectionAccessDetails>> GetByIdWithAccessAsync(
|
||||
Guid id, Guid userId)
|
||||
Guid id, Guid userId, bool useFlexibleCollections)
|
||||
{
|
||||
var sprocName = useFlexibleCollections
|
||||
? $"[{Schema}].[Collection_ReadWithGroupsAndUsersByIdUserId_V2]"
|
||||
: $"[{Schema}].[Collection_ReadWithGroupsAndUsersByIdUserId]";
|
||||
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryMultipleAsync(
|
||||
$"[{Schema}].[Collection_ReadWithGroupsAndUsersByIdUserId]",
|
||||
sprocName,
|
||||
new { Id = id, UserId = userId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
@ -139,12 +143,16 @@ public class CollectionRepository : Repository<Collection, Guid>, ICollectionRep
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<Tuple<CollectionDetails, CollectionAccessDetails>>> GetManyByUserIdWithAccessAsync(Guid userId, Guid organizationId)
|
||||
public async Task<ICollection<Tuple<CollectionDetails, CollectionAccessDetails>>> GetManyByUserIdWithAccessAsync(Guid userId, Guid organizationId, bool useFlexibleCollections)
|
||||
{
|
||||
var sprocName = useFlexibleCollections
|
||||
? $"[{Schema}].[Collection_ReadWithGroupsAndUsersByUserId_V2]"
|
||||
: $"[{Schema}].[Collection_ReadWithGroupsAndUsersByUserId]";
|
||||
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryMultipleAsync(
|
||||
$"[{Schema}].[Collection_ReadWithGroupsAndUsersByUserId]",
|
||||
sprocName,
|
||||
new { UserId = userId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
@ -183,12 +191,16 @@ public class CollectionRepository : Repository<Collection, Guid>, ICollectionRep
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<CollectionDetails> GetByIdAsync(Guid id, Guid userId)
|
||||
public async Task<CollectionDetails> GetByIdAsync(Guid id, Guid userId, bool useFlexibleCollections)
|
||||
{
|
||||
var sprocName = useFlexibleCollections
|
||||
? $"[{Schema}].[Collection_ReadByIdUserId_V2]"
|
||||
: $"[{Schema}].[Collection_ReadByIdUserId]";
|
||||
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<CollectionDetails>(
|
||||
$"[{Schema}].[Collection_ReadByIdUserId]",
|
||||
sprocName,
|
||||
new { Id = id, UserId = userId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
@ -196,12 +208,16 @@ public class CollectionRepository : Repository<Collection, Guid>, ICollectionRep
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<CollectionDetails>> GetManyByUserIdAsync(Guid userId)
|
||||
public async Task<ICollection<CollectionDetails>> GetManyByUserIdAsync(Guid userId, bool useFlexibleCollections)
|
||||
{
|
||||
var sprocName = useFlexibleCollections
|
||||
? $"[{Schema}].[Collection_ReadByUserId_V2]"
|
||||
: $"[{Schema}].[Collection_ReadByUserId]";
|
||||
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<CollectionDetails>(
|
||||
$"[{Schema}].[Collection_ReadByUserId]",
|
||||
sprocName,
|
||||
new { UserId = userId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
|
@ -110,12 +110,12 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<CollectionDetails> GetByIdAsync(Guid id, Guid userId)
|
||||
public async Task<CollectionDetails> GetByIdAsync(Guid id, Guid userId, bool useFlexibleCollections)
|
||||
{
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
return (await GetManyByUserIdAsync(userId)).FirstOrDefault(c => c.Id == id);
|
||||
return (await GetManyByUserIdAsync(userId, useFlexibleCollections)).FirstOrDefault(c => c.Id == id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,9 +152,10 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Tuple<CollectionDetails, CollectionAccessDetails>> GetByIdWithAccessAsync(Guid id, Guid userId)
|
||||
public async Task<Tuple<CollectionDetails, CollectionAccessDetails>> GetByIdWithAccessAsync(Guid id, Guid userId,
|
||||
bool useFlexibleCollections)
|
||||
{
|
||||
var collection = await GetByIdAsync(id, userId);
|
||||
var collection = await GetByIdAsync(id, userId, useFlexibleCollections);
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
@ -231,9 +232,9 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<Tuple<CollectionDetails, CollectionAccessDetails>>> GetManyByUserIdWithAccessAsync(Guid userId, Guid organizationId)
|
||||
public async Task<ICollection<Tuple<CollectionDetails, CollectionAccessDetails>>> GetManyByUserIdWithAccessAsync(Guid userId, Guid organizationId, bool useFlexibleCollections)
|
||||
{
|
||||
var collections = (await GetManyByUserIdAsync(userId)).Where(c => c.OrganizationId == organizationId).ToList();
|
||||
var collections = (await GetManyByUserIdAsync(userId, useFlexibleCollections)).Where(c => c.OrganizationId == organizationId).ToList();
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
@ -309,13 +310,13 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<CollectionDetails>> GetManyByUserIdAsync(Guid userId)
|
||||
public async Task<ICollection<CollectionDetails>> GetManyByUserIdAsync(Guid userId, bool useFlexibleCollections)
|
||||
{
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
|
||||
var baseCollectionQuery = new UserCollectionDetailsQuery(userId).Run(dbContext);
|
||||
var baseCollectionQuery = new UserCollectionDetailsQuery(userId, useFlexibleCollections).Run(dbContext);
|
||||
|
||||
if (dbContext.Database.IsSqlite())
|
||||
{
|
||||
|
@ -6,12 +6,71 @@ namespace Bit.Infrastructure.EntityFramework.Repositories.Queries;
|
||||
public class UserCollectionDetailsQuery : IQuery<CollectionDetails>
|
||||
{
|
||||
private readonly Guid? _userId;
|
||||
public UserCollectionDetailsQuery(Guid? userId)
|
||||
private readonly bool _useFlexibleCollections;
|
||||
|
||||
public UserCollectionDetailsQuery(Guid? userId, bool useFlexibleCollections)
|
||||
{
|
||||
_userId = userId;
|
||||
_useFlexibleCollections = useFlexibleCollections;
|
||||
}
|
||||
|
||||
public virtual IQueryable<CollectionDetails> Run(DatabaseContext dbContext)
|
||||
{
|
||||
return _useFlexibleCollections
|
||||
? Run_vNext(dbContext)
|
||||
: Run_vLegacy(dbContext);
|
||||
}
|
||||
|
||||
private IQueryable<CollectionDetails> Run_vNext(DatabaseContext dbContext)
|
||||
{
|
||||
var query = from c in dbContext.Collections
|
||||
|
||||
join ou in dbContext.OrganizationUsers
|
||||
on c.OrganizationId equals ou.OrganizationId
|
||||
|
||||
join o in dbContext.Organizations
|
||||
on c.OrganizationId equals o.Id
|
||||
|
||||
join cu in dbContext.CollectionUsers
|
||||
on new { CollectionId = c.Id, 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 { CollectionId = c.Id, gu.GroupId } equals
|
||||
new { cg.CollectionId, cg.GroupId } into cg_g
|
||||
from cg in cg_g.DefaultIfEmpty()
|
||||
|
||||
where ou.UserId == _userId &&
|
||||
ou.Status == OrganizationUserStatusType.Confirmed &&
|
||||
o.Enabled &&
|
||||
(cu.CollectionId != null || cg.CollectionId != null)
|
||||
select new { c, ou, o, cu, gu, g, cg };
|
||||
|
||||
return query.Select(x => new CollectionDetails
|
||||
{
|
||||
Id = x.c.Id,
|
||||
OrganizationId = x.c.OrganizationId,
|
||||
Name = x.c.Name,
|
||||
ExternalId = x.c.ExternalId,
|
||||
CreationDate = x.c.CreationDate,
|
||||
RevisionDate = x.c.RevisionDate,
|
||||
ReadOnly = (bool?)x.cu.ReadOnly ?? (bool?)x.cg.ReadOnly ?? false,
|
||||
HidePasswords = (bool?)x.cu.HidePasswords ?? (bool?)x.cg.HidePasswords ?? false,
|
||||
Manage = (bool?)x.cu.Manage ?? (bool?)x.cg.Manage ?? false,
|
||||
});
|
||||
}
|
||||
|
||||
private IQueryable<CollectionDetails> Run_vLegacy(DatabaseContext dbContext)
|
||||
{
|
||||
var query = from c in dbContext.Collections
|
||||
|
||||
|
45
src/Sql/dbo/Functions/UserCollectionDetails_V2.sql
Normal file
45
src/Sql/dbo/Functions/UserCollectionDetails_V2.sql
Normal file
@ -0,0 +1,45 @@
|
||||
CREATE FUNCTION [dbo].[UserCollectionDetails_V2](@UserId UNIQUEIDENTIFIER)
|
||||
RETURNS TABLE
|
||||
AS RETURN
|
||||
SELECT
|
||||
C.*,
|
||||
CASE
|
||||
WHEN
|
||||
COALESCE(CU.[ReadOnly], CG.[ReadOnly], 0) = 0
|
||||
THEN 0
|
||||
ELSE 1
|
||||
END [ReadOnly],
|
||||
CASE
|
||||
WHEN
|
||||
COALESCE(CU.[HidePasswords], CG.[HidePasswords], 0) = 0
|
||||
THEN 0
|
||||
ELSE 1
|
||||
END [HidePasswords],
|
||||
CASE
|
||||
WHEN
|
||||
COALESCE(CU.[Manage], CG.[Manage], 0) = 0
|
||||
THEN 0
|
||||
ELSE 1
|
||||
END [Manage]
|
||||
FROM
|
||||
[dbo].[CollectionView] C
|
||||
INNER JOIN
|
||||
[dbo].[OrganizationUser] OU ON C.[OrganizationId] = OU.[OrganizationId]
|
||||
INNER JOIN
|
||||
[dbo].[Organization] O ON O.[Id] = C.[OrganizationId]
|
||||
LEFT JOIN
|
||||
[dbo].[CollectionUser] CU ON CU.[CollectionId] = C.[Id] AND CU.[OrganizationUserId] = [OU].[Id]
|
||||
LEFT JOIN
|
||||
[dbo].[GroupUser] GU ON CU.[CollectionId] IS NULL AND GU.[OrganizationUserId] = OU.[Id]
|
||||
LEFT JOIN
|
||||
[dbo].[Group] G ON G.[Id] = GU.[GroupId]
|
||||
LEFT JOIN
|
||||
[dbo].[CollectionGroup] CG ON CG.[CollectionId] = C.[Id] AND CG.[GroupId] = GU.[GroupId]
|
||||
WHERE
|
||||
OU.[UserId] = @UserId
|
||||
AND OU.[Status] = 2 -- 2 = Confirmed
|
||||
AND O.[Enabled] = 1
|
||||
AND (
|
||||
CU.[CollectionId] IS NOT NULL
|
||||
OR CG.[CollectionId] IS NOT NULL
|
||||
)
|
@ -0,0 +1,28 @@
|
||||
CREATE PROCEDURE [dbo].[Collection_ReadByIdUserId_V2]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
SELECT
|
||||
Id,
|
||||
OrganizationId,
|
||||
[Name],
|
||||
CreationDate,
|
||||
RevisionDate,
|
||||
ExternalId,
|
||||
MIN([ReadOnly]) AS [ReadOnly],
|
||||
MIN([HidePasswords]) AS [HidePasswords],
|
||||
MIN([Manage]) AS [Manage]
|
||||
FROM
|
||||
[dbo].[UserCollectionDetails_V2](@UserId)
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
GROUP BY
|
||||
Id,
|
||||
OrganizationId,
|
||||
[Name],
|
||||
CreationDate,
|
||||
RevisionDate,
|
||||
ExternalId
|
||||
END
|
26
src/Sql/dbo/Stored Procedures/Collection_ReadByUserId_V2.sql
Normal file
26
src/Sql/dbo/Stored Procedures/Collection_ReadByUserId_V2.sql
Normal file
@ -0,0 +1,26 @@
|
||||
CREATE PROCEDURE [dbo].[Collection_ReadByUserId_V2]
|
||||
@UserId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
Id,
|
||||
OrganizationId,
|
||||
[Name],
|
||||
CreationDate,
|
||||
RevisionDate,
|
||||
ExternalId,
|
||||
MIN([ReadOnly]) AS [ReadOnly],
|
||||
MIN([HidePasswords]) AS [HidePasswords],
|
||||
MIN([Manage]) AS [Manage]
|
||||
FROM
|
||||
[dbo].[UserCollectionDetails_V2](@UserId)
|
||||
GROUP BY
|
||||
Id,
|
||||
OrganizationId,
|
||||
[Name],
|
||||
CreationDate,
|
||||
RevisionDate,
|
||||
ExternalId
|
||||
END
|
@ -0,0 +1,13 @@
|
||||
CREATE PROCEDURE [dbo].[Collection_ReadWithGroupsAndUsersByIdUserId_V2]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
EXEC [dbo].[Collection_ReadByIdUserId_V2] @Id, @UserId
|
||||
|
||||
EXEC [dbo].[CollectionGroup_ReadByCollectionId] @Id
|
||||
|
||||
EXEC [dbo].[CollectionUser_ReadByCollectionId] @Id
|
||||
END
|
@ -0,0 +1,39 @@
|
||||
CREATE PROCEDURE [dbo].[Collection_ReadWithGroupsAndUsersByUserId_V2]
|
||||
@UserId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
DECLARE @TempUserCollections TABLE(
|
||||
Id UNIQUEIDENTIFIER,
|
||||
OrganizationId UNIQUEIDENTIFIER,
|
||||
Name VARCHAR(MAX),
|
||||
CreationDate DATETIME2(7),
|
||||
RevisionDate DATETIME2(7),
|
||||
ExternalId NVARCHAR(300),
|
||||
ReadOnly BIT,
|
||||
HidePasswords BIT,
|
||||
Manage BIT)
|
||||
|
||||
INSERT INTO @TempUserCollections EXEC [dbo].[Collection_ReadByUserId_V2] @UserId
|
||||
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
@TempUserCollections C
|
||||
|
||||
SELECT
|
||||
CG.*
|
||||
FROM
|
||||
[dbo].[CollectionGroup] CG
|
||||
INNER JOIN
|
||||
@TempUserCollections C ON C.[Id] = CG.[CollectionId]
|
||||
|
||||
SELECT
|
||||
CU.*
|
||||
FROM
|
||||
[dbo].[CollectionUser] CU
|
||||
INNER JOIN
|
||||
@TempUserCollections C ON C.[Id] = CU.[CollectionId]
|
||||
|
||||
END
|
@ -107,7 +107,7 @@ public class CollectionsControllerTests
|
||||
|
||||
await sutProvider.Sut.GetManyWithDetails(organization.Id);
|
||||
|
||||
await sutProvider.GetDependency<ICollectionRepository>().Received(1).GetManyByUserIdWithAccessAsync(userId, organization.Id);
|
||||
await sutProvider.GetDependency<ICollectionRepository>().Received(1).GetManyByUserIdWithAccessAsync(userId, organization.Id, Arg.Any<bool>());
|
||||
await sutProvider.GetDependency<ICollectionRepository>().Received(1).GetManyByOrganizationIdWithAccessAsync(organization.Id);
|
||||
}
|
||||
|
||||
@ -137,7 +137,7 @@ public class CollectionsControllerTests
|
||||
|
||||
await sutProvider.Sut.GetManyWithDetails(organization.Id);
|
||||
|
||||
await sutProvider.GetDependency<ICollectionRepository>().Received(1).GetManyByUserIdWithAccessAsync(userId, organization.Id);
|
||||
await sutProvider.GetDependency<ICollectionRepository>().Received(1).GetManyByUserIdWithAccessAsync(userId, organization.Id, Arg.Any<bool>());
|
||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceive().GetManyByOrganizationIdWithAccessAsync(organization.Id);
|
||||
}
|
||||
|
||||
|
@ -119,7 +119,7 @@ public class LegacyCollectionsControllerTests
|
||||
.Returns(userId);
|
||||
|
||||
sutProvider.GetDependency<ICollectionRepository>()
|
||||
.GetByIdAsync(collectionId, userId)
|
||||
.GetByIdAsync(collectionId, userId, Arg.Any<bool>())
|
||||
.Returns(new CollectionDetails
|
||||
{
|
||||
OrganizationId = orgId,
|
||||
@ -141,7 +141,7 @@ public class LegacyCollectionsControllerTests
|
||||
.Returns(userId);
|
||||
|
||||
sutProvider.GetDependency<ICollectionRepository>()
|
||||
.GetByIdAsync(collectionId, userId)
|
||||
.GetByIdAsync(collectionId, userId, Arg.Any<bool>())
|
||||
.Returns(Task.FromResult<CollectionDetails>(null));
|
||||
|
||||
_ = await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.Put(orgId, collectionId, collectionRequest));
|
||||
@ -154,7 +154,7 @@ public class LegacyCollectionsControllerTests
|
||||
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetManyWithDetails(organization.Id));
|
||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().GetManyByOrganizationIdWithAccessAsync(default);
|
||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().GetManyByUserIdWithAccessAsync(default, default);
|
||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().GetManyByUserIdWithAccessAsync(default, default, default);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
@ -167,7 +167,7 @@ public class LegacyCollectionsControllerTests
|
||||
await sutProvider.Sut.GetManyWithDetails(organization.Id);
|
||||
|
||||
await sutProvider.GetDependency<ICollectionRepository>().Received().GetManyByOrganizationIdWithAccessAsync(organization.Id);
|
||||
await sutProvider.GetDependency<ICollectionRepository>().Received().GetManyByUserIdWithAccessAsync(user.Id, organization.Id);
|
||||
await sutProvider.GetDependency<ICollectionRepository>().Received().GetManyByUserIdWithAccessAsync(user.Id, organization.Id, Arg.Any<bool>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
@ -180,7 +180,7 @@ public class LegacyCollectionsControllerTests
|
||||
await sutProvider.Sut.GetManyWithDetails(organization.Id);
|
||||
|
||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().GetManyByOrganizationIdWithAccessAsync(default);
|
||||
await sutProvider.GetDependency<ICollectionRepository>().Received().GetManyByUserIdWithAccessAsync(user.Id, organization.Id);
|
||||
await sutProvider.GetDependency<ICollectionRepository>().Received().GetManyByUserIdWithAccessAsync(user.Id, organization.Id, Arg.Any<bool>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
@ -194,7 +194,7 @@ public class LegacyCollectionsControllerTests
|
||||
await sutProvider.Sut.GetManyWithDetails(organization.Id);
|
||||
|
||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().GetManyByOrganizationIdWithAccessAsync(default);
|
||||
await sutProvider.GetDependency<ICollectionRepository>().Received().GetManyByUserIdWithAccessAsync(user.Id, organization.Id);
|
||||
await sutProvider.GetDependency<ICollectionRepository>().Received().GetManyByUserIdWithAccessAsync(user.Id, organization.Id, Arg.Any<bool>());
|
||||
}
|
||||
|
||||
|
||||
|
@ -224,7 +224,7 @@ public class BulkCollectionAuthorizationHandlerTests
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
|
||||
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
|
||||
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
|
||||
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId, Arg.Any<bool>()).Returns(collections);
|
||||
|
||||
var context = new AuthorizationHandlerContext(
|
||||
new[] { BulkCollectionOperations.Read },
|
||||
@ -431,7 +431,7 @@ public class BulkCollectionAuthorizationHandlerTests
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
|
||||
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
|
||||
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
|
||||
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId, Arg.Any<bool>()).Returns(collections);
|
||||
|
||||
var context = new AuthorizationHandlerContext(
|
||||
new[] { BulkCollectionOperations.ReadWithAccess },
|
||||
@ -462,7 +462,7 @@ public class BulkCollectionAuthorizationHandlerTests
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
|
||||
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
|
||||
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
|
||||
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId, Arg.Any<bool>()).Returns(collections);
|
||||
|
||||
var context = new AuthorizationHandlerContext(
|
||||
new[] { BulkCollectionOperations.ReadWithAccess },
|
||||
@ -628,7 +628,7 @@ public class BulkCollectionAuthorizationHandlerTests
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
|
||||
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
|
||||
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
|
||||
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId, Arg.Any<bool>()).Returns(collections);
|
||||
|
||||
var context = new AuthorizationHandlerContext(
|
||||
new[] { op },
|
||||
@ -794,7 +794,7 @@ public class BulkCollectionAuthorizationHandlerTests
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
|
||||
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
|
||||
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId).Returns(collections);
|
||||
sutProvider.GetDependency<ICollectionRepository>().GetManyByUserIdAsync(actingUserId, Arg.Any<bool>()).Returns(collections);
|
||||
|
||||
foreach (var c in collections)
|
||||
{
|
||||
|
@ -115,9 +115,8 @@ public class SyncControllerTests
|
||||
policyRepository.GetManyByUserIdAsync(user.Id).Returns(policies);
|
||||
|
||||
// Returns for methods only called if we have enabled orgs
|
||||
collectionRepository.GetManyByUserIdAsync(user.Id).Returns(collections);
|
||||
collectionRepository.GetManyByUserIdAsync(user.Id, Arg.Any<bool>()).Returns(collections);
|
||||
collectionCipherRepository.GetManyByUserIdAsync(user.Id, Arg.Any<bool>()).Returns(new List<CollectionCipher>());
|
||||
|
||||
// Back to standard test setup
|
||||
userService.TwoFactorIsEnabledAsync(user).Returns(false);
|
||||
userService.HasPremiumFromOrganization(user).Returns(false);
|
||||
@ -280,9 +279,8 @@ public class SyncControllerTests
|
||||
policyRepository.GetManyByUserIdAsync(user.Id).Returns(policies);
|
||||
|
||||
// Returns for methods only called if we have enabled orgs
|
||||
collectionRepository.GetManyByUserIdAsync(user.Id).Returns(collections);
|
||||
collectionRepository.GetManyByUserIdAsync(user.Id, Arg.Any<bool>()).Returns(collections);
|
||||
collectionCipherRepository.GetManyByUserIdAsync(user.Id, Arg.Any<bool>()).Returns(new List<CollectionCipher>());
|
||||
|
||||
// Back to standard test setup
|
||||
userService.TwoFactorIsEnabledAsync(user).Returns(false);
|
||||
userService.HasPremiumFromOrganization(user).Returns(false);
|
||||
@ -344,7 +342,7 @@ public class SyncControllerTests
|
||||
if (hasEnabledOrgs)
|
||||
{
|
||||
await collectionRepository.ReceivedWithAnyArgs(1)
|
||||
.GetManyByUserIdAsync(default);
|
||||
.GetManyByUserIdAsync(default, default);
|
||||
await collectionCipherRepository.ReceivedWithAnyArgs(1)
|
||||
.GetManyByUserIdAsync(default, default);
|
||||
}
|
||||
@ -352,7 +350,7 @@ public class SyncControllerTests
|
||||
{
|
||||
// all disabled orgs
|
||||
await collectionRepository.ReceivedWithAnyArgs(0)
|
||||
.GetManyByUserIdAsync(default);
|
||||
.GetManyByUserIdAsync(default, default);
|
||||
await collectionCipherRepository.ReceivedWithAnyArgs(0)
|
||||
.GetManyByUserIdAsync(default, default);
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ public class CollectionServiceTest
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(userId);
|
||||
sutProvider.GetDependency<ICollectionRepository>()
|
||||
.GetManyByUserIdAsync(userId)
|
||||
.GetManyByUserIdAsync(userId, Arg.Any<bool>())
|
||||
.Returns(new List<CollectionDetails> { collectionDetails });
|
||||
sutProvider.GetDependency<ICurrentContext>().ViewAssignedCollections(organizationId).Returns(true);
|
||||
|
||||
@ -197,7 +197,7 @@ public class CollectionServiceTest
|
||||
Assert.Equal(collectionDetails, result.First());
|
||||
|
||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().GetManyByOrganizationIdAsync(default);
|
||||
await sutProvider.GetDependency<ICollectionRepository>().Received(1).GetManyByUserIdAsync(userId);
|
||||
await sutProvider.GetDependency<ICollectionRepository>().Received(1).GetManyByUserIdAsync(userId, Arg.Any<bool>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
@ -217,7 +217,7 @@ public class CollectionServiceTest
|
||||
Assert.Equal(collection, result.First());
|
||||
|
||||
await sutProvider.GetDependency<ICollectionRepository>().Received(1).GetManyByOrganizationIdAsync(organizationId);
|
||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().GetManyByUserIdAsync(default);
|
||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().GetManyByUserIdAsync(default, default);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
@ -229,6 +229,6 @@ public class CollectionServiceTest
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetOrganizationCollectionsAsync(organizationId));
|
||||
|
||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().GetManyByOrganizationIdAsync(default);
|
||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().GetManyByUserIdAsync(default);
|
||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().GetManyByUserIdAsync(default, default);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,168 @@
|
||||
-- Flexible Collections: create new UserCollectionDetails function that doesn't use AccessAll logic
|
||||
|
||||
CREATE OR ALTER FUNCTION [dbo].[UserCollectionDetails_V2](@UserId UNIQUEIDENTIFIER)
|
||||
RETURNS TABLE
|
||||
AS RETURN
|
||||
SELECT
|
||||
C.*,
|
||||
CASE
|
||||
WHEN
|
||||
COALESCE(CU.[ReadOnly], CG.[ReadOnly], 0) = 0
|
||||
THEN 0
|
||||
ELSE 1
|
||||
END [ReadOnly],
|
||||
CASE
|
||||
WHEN
|
||||
COALESCE(CU.[HidePasswords], CG.[HidePasswords], 0) = 0
|
||||
THEN 0
|
||||
ELSE 1
|
||||
END [HidePasswords],
|
||||
CASE
|
||||
WHEN
|
||||
COALESCE(CU.[Manage], CG.[Manage], 0) = 0
|
||||
THEN 0
|
||||
ELSE 1
|
||||
END [Manage]
|
||||
FROM
|
||||
[dbo].[CollectionView] C
|
||||
INNER JOIN
|
||||
[dbo].[OrganizationUser] OU ON C.[OrganizationId] = OU.[OrganizationId]
|
||||
INNER JOIN
|
||||
[dbo].[Organization] O ON O.[Id] = C.[OrganizationId]
|
||||
LEFT JOIN
|
||||
[dbo].[CollectionUser] CU ON CU.[CollectionId] = C.[Id] AND CU.[OrganizationUserId] = [OU].[Id]
|
||||
LEFT JOIN
|
||||
[dbo].[GroupUser] GU ON CU.[CollectionId] IS NULL AND GU.[OrganizationUserId] = OU.[Id]
|
||||
LEFT JOIN
|
||||
[dbo].[Group] G ON G.[Id] = GU.[GroupId]
|
||||
LEFT JOIN
|
||||
[dbo].[CollectionGroup] CG ON CG.[CollectionId] = C.[Id] AND CG.[GroupId] = GU.[GroupId]
|
||||
WHERE
|
||||
OU.[UserId] = @UserId
|
||||
AND OU.[Status] = 2 -- 2 = Confirmed
|
||||
AND O.[Enabled] = 1
|
||||
AND (
|
||||
CU.[CollectionId] IS NOT NULL
|
||||
OR CG.[CollectionId] IS NOT NULL
|
||||
)
|
||||
GO
|
||||
|
||||
-- Create v2 sprocs for all sprocs that call UserCollectionDetails
|
||||
|
||||
-- Collection_ReadByIdUserId_V2
|
||||
CREATE OR ALTER PROCEDURE [dbo].[Collection_ReadByIdUserId_V2]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
SELECT
|
||||
Id,
|
||||
OrganizationId,
|
||||
[Name],
|
||||
CreationDate,
|
||||
RevisionDate,
|
||||
ExternalId,
|
||||
MIN([ReadOnly]) AS [ReadOnly],
|
||||
MIN([HidePasswords]) AS [HidePasswords],
|
||||
MIN([Manage]) AS [Manage]
|
||||
FROM
|
||||
[dbo].[UserCollectionDetails_V2](@UserId)
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
GROUP BY
|
||||
Id,
|
||||
OrganizationId,
|
||||
[Name],
|
||||
CreationDate,
|
||||
RevisionDate,
|
||||
ExternalId
|
||||
END
|
||||
GO
|
||||
|
||||
-- Collection_ReadByUserId_V2
|
||||
CREATE OR ALTER PROCEDURE [dbo].[Collection_ReadByUserId_V2]
|
||||
@UserId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
SELECT
|
||||
Id,
|
||||
OrganizationId,
|
||||
[Name],
|
||||
CreationDate,
|
||||
RevisionDate,
|
||||
ExternalId,
|
||||
MIN([ReadOnly]) AS [ReadOnly],
|
||||
MIN([HidePasswords]) AS [HidePasswords],
|
||||
MIN([Manage]) AS [Manage]
|
||||
FROM
|
||||
[dbo].[UserCollectionDetails_V2](@UserId)
|
||||
GROUP BY
|
||||
Id,
|
||||
OrganizationId,
|
||||
[Name],
|
||||
CreationDate,
|
||||
RevisionDate,
|
||||
ExternalId
|
||||
END
|
||||
GO
|
||||
|
||||
-- Collection_ReadWithGroupsAndUsersByIdUserId_V2
|
||||
CREATE OR ALTER PROCEDURE [dbo].[Collection_ReadWithGroupsAndUsersByIdUserId_V2]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
EXEC [dbo].[Collection_ReadByIdUserId_V2] @Id, @UserId
|
||||
|
||||
EXEC [dbo].[CollectionGroup_ReadByCollectionId] @Id
|
||||
|
||||
EXEC [dbo].[CollectionUser_ReadByCollectionId] @Id
|
||||
END
|
||||
GO
|
||||
|
||||
-- Collection_ReadWithGroupsAndUsersByUserId_V2
|
||||
CREATE OR ALTER PROCEDURE [dbo].[Collection_ReadWithGroupsAndUsersByUserId_V2]
|
||||
@UserId UNIQUEIDENTIFIER
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
DECLARE @TempUserCollections TABLE(
|
||||
Id UNIQUEIDENTIFIER,
|
||||
OrganizationId UNIQUEIDENTIFIER,
|
||||
Name VARCHAR(MAX),
|
||||
CreationDate DATETIME2(7),
|
||||
RevisionDate DATETIME2(7),
|
||||
ExternalId NVARCHAR(300),
|
||||
ReadOnly BIT,
|
||||
HidePasswords BIT,
|
||||
Manage BIT)
|
||||
|
||||
INSERT INTO @TempUserCollections EXEC [dbo].[Collection_ReadByUserId_V2] @UserId
|
||||
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
@TempUserCollections C
|
||||
|
||||
SELECT
|
||||
CG.*
|
||||
FROM
|
||||
[dbo].[CollectionGroup] CG
|
||||
INNER JOIN
|
||||
@TempUserCollections C ON C.[Id] = CG.[CollectionId]
|
||||
|
||||
SELECT
|
||||
CU.*
|
||||
FROM
|
||||
[dbo].[CollectionUser] CU
|
||||
INNER JOIN
|
||||
@TempUserCollections C ON C.[Id] = CU.[CollectionId]
|
||||
|
||||
END
|
||||
GO
|
Loading…
Reference in New Issue
Block a user