1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-21 12:05:42 +01:00

[PM-8004] Move Unmanaged collection logic out of component for better reuse (#4108)

* Updated sprocs to return unmanaged collection column, updated reponse to return to return unmanaged

* reformatted sproc
This commit is contained in:
SmithThe4th 2024-05-21 14:42:47 -04:00 committed by GitHub
parent b863454a4e
commit aee180adfc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 256 additions and 9 deletions

View File

@ -89,6 +89,7 @@ public class CollectionAccessDetailsResponseModel : CollectionResponseModel
ReadOnly = collection.ReadOnly;
HidePasswords = collection.HidePasswords;
Manage = collection.Manage;
Unmanaged = collection.Unmanaged;
Groups = collection.Groups?.Select(g => new SelectionReadOnlyResponseModel(g)) ?? Enumerable.Empty<SelectionReadOnlyResponseModel>();
Users = collection.Users?.Select(g => new SelectionReadOnlyResponseModel(g)) ?? Enumerable.Empty<SelectionReadOnlyResponseModel>();
}
@ -104,4 +105,5 @@ public class CollectionAccessDetailsResponseModel : CollectionResponseModel
public bool ReadOnly { get; set; }
public bool HidePasswords { get; set; }
public bool Manage { get; set; }
public bool Unmanaged { get; set; }
}

View File

@ -14,4 +14,9 @@ public class CollectionAdminDetails : CollectionDetails
/// Flag for whether the user has been explicitly assigned to the collection either directly or through a group.
/// </summary>
public bool Assigned { get; set; }
/// <summary>
/// Flag for whether a collection is managed by an active user or group.
/// </summary>
public bool Unmanaged { get; set; }
}

View File

@ -391,7 +391,8 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
c.Name,
c.CreationDate,
c.RevisionDate,
c.ExternalId
c.ExternalId,
c.Unmanaged
}).Select(collectionGroup => new CollectionAdminDetails
{
Id = collectionGroup.Key.Id,
@ -404,7 +405,8 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
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)))
Assigned = Convert.ToBoolean(collectionGroup.Max(c => Convert.ToInt32(c.Assigned))),
Unmanaged = collectionGroup.Key.Unmanaged
}).ToList();
}
else
@ -417,7 +419,8 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
c.Name,
c.CreationDate,
c.RevisionDate,
c.ExternalId
c.ExternalId,
c.Unmanaged
}
into collectionGroup
select new CollectionAdminDetails
@ -432,7 +435,8 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
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)))
Assigned = Convert.ToBoolean(collectionGroup.Max(c => Convert.ToInt32(c.Assigned))),
Unmanaged = collectionGroup.Key.Unmanaged
}).ToListAsync();
}
@ -511,7 +515,8 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
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)))
Assigned = Convert.ToBoolean(collectionGroup.Max(c => Convert.ToInt32(c.Assigned))),
Unmanaged = collectionGroup.Select(c => c.Unmanaged).FirstOrDefault()
}).FirstOrDefault();
}
else
@ -539,7 +544,8 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
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)))
Assigned = Convert.ToBoolean(collectionGroup.Max(c => Convert.ToInt32(c.Assigned))),
Unmanaged = collectionGroup.Select(c => c.Unmanaged).FirstOrDefault()
}).FirstOrDefaultAsync();
}

View File

@ -1,4 +1,5 @@
using Bit.Core.Models.Data;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
namespace Bit.Infrastructure.EntityFramework.Repositories.Queries;
@ -46,6 +47,17 @@ public class CollectionAdminDetailsQuery : IQuery<CollectionAdminDetails>
from cg in cg_g.DefaultIfEmpty()
select new { c, cu, cg };
// Subqueries to determine if a colection is managed by an active user or group.
var activeUserManageRights = from cu in dbContext.CollectionUsers
join ou in dbContext.OrganizationUsers
on cu.OrganizationUserId equals ou.Id
where ou.Status == OrganizationUserStatusType.Confirmed && cu.Manage
select cu.CollectionId;
var activeGroupManageRights = from cg in dbContext.CollectionGroups
where cg.Manage
select cg.CollectionId;
if (_organizationId.HasValue)
{
baseCollectionQuery = baseCollectionQuery.Where(x => x.c.OrganizationId == _organizationId);
@ -71,6 +83,7 @@ public class CollectionAdminDetailsQuery : IQuery<CollectionAdminDetails>
HidePasswords = (bool?)x.cu.HidePasswords ?? (bool?)x.cg.HidePasswords ?? false,
Manage = (bool?)x.cu.Manage ?? (bool?)x.cg.Manage ?? false,
Assigned = x.cu != null || x.cg != null,
Unmanaged = !activeUserManageRights.Contains(x.c.Id) && !activeGroupManageRights.Contains(x.c.Id),
});
}

View File

@ -31,7 +31,29 @@ BEGIN
CU.[CollectionId] IS NULL AND CG.[CollectionId] IS NULL
THEN 0
ELSE 1
END) AS [Assigned]
END) AS [Assigned],
CASE
WHEN
-- No active user or group has manage rights
NOT EXISTS(
SELECT 1
FROM [dbo].[CollectionUser] CU2
JOIN [dbo].[OrganizationUser] OU2 ON CU2.[OrganizationUserId] = OU2.[Id]
WHERE
CU2.[CollectionId] = C.[Id] AND
OU2.[Status] = 2 AND
CU2.[Manage] = 1
)
AND NOT EXISTS (
SELECT 1
FROM [dbo].[CollectionGroup] CG2
WHERE
CG2.[CollectionId] = C.[Id] AND
CG2.[Manage] = 1
)
THEN 1
ELSE 0
END AS [Unmanaged]
FROM
[dbo].[CollectionView] C
LEFT JOIN

View File

@ -31,7 +31,29 @@ BEGIN
CU.[CollectionId] IS NULL AND CG.[CollectionId] IS NULL
THEN 0
ELSE 1
END) AS [Assigned]
END) AS [Assigned],
CASE
WHEN
-- No active user or group has manage rights
NOT EXISTS(
SELECT 1
FROM [dbo].[CollectionUser] CU2
JOIN [dbo].[OrganizationUser] OU2 ON CU2.[OrganizationUserId] = OU2.[Id]
WHERE
CU2.[CollectionId] = C.[Id] AND
OU2.[Status] = 2 AND
CU2.[Manage] = 1
)
AND NOT EXISTS (
SELECT 1
FROM [dbo].[CollectionGroup] CG2
WHERE
CG2.[CollectionId] = C.[Id] AND
CG2.[Manage] = 1
)
THEN 1
ELSE 0
END AS [Unmanaged]
FROM
[dbo].[CollectionView] C
LEFT JOIN

View File

@ -310,6 +310,7 @@ public class CollectionRepositoryTests
Assert.True(c1.Manage);
Assert.False(c1.ReadOnly);
Assert.False(c1.HidePasswords);
Assert.False(c1.Unmanaged);
}, c2 =>
{
Assert.NotNull(c2);
@ -319,6 +320,7 @@ public class CollectionRepositoryTests
Assert.False(c2.Manage);
Assert.True(c2.ReadOnly);
Assert.False(c2.HidePasswords);
Assert.True(c2.Unmanaged);
}, c3 =>
{
Assert.NotNull(c3);
@ -328,6 +330,7 @@ public class CollectionRepositoryTests
Assert.False(c3.Manage);
Assert.False(c3.ReadOnly);
Assert.False(c3.HidePasswords);
Assert.False(c3.Unmanaged);
});
}
@ -436,6 +439,7 @@ public class CollectionRepositoryTests
Assert.True(c1.Manage);
Assert.False(c1.ReadOnly);
Assert.False(c1.HidePasswords);
Assert.False(c1.Unmanaged);
}, c2 =>
{
Assert.NotNull(c2);
@ -445,6 +449,7 @@ public class CollectionRepositoryTests
Assert.False(c2.Manage);
Assert.True(c2.ReadOnly);
Assert.False(c2.HidePasswords);
Assert.True(c2.Unmanaged);
}, c3 =>
{
Assert.NotNull(c3);
@ -454,6 +459,7 @@ public class CollectionRepositoryTests
Assert.True(c3.Manage); // Group 2 is Manage
Assert.False(c3.ReadOnly);
Assert.False(c3.HidePasswords);
Assert.False(c3.Unmanaged);
});
}
}

View File

@ -0,0 +1,171 @@
CREATE OR ALTER PROCEDURE [dbo].[Collection_ReadByOrganizationIdWithPermissions]
@OrganizationId UNIQUEIDENTIFIER,
@UserId UNIQUEIDENTIFIER,
@IncludeAccessRelationships BIT
AS
BEGIN
SET NOCOUNT ON
SELECT
C.*,
MIN(CASE
WHEN
COALESCE(CU.[ReadOnly], CG.[ReadOnly], 0) = 0
THEN 0
ELSE 1
END) AS [ReadOnly],
MIN(CASE
WHEN
COALESCE(CU.[HidePasswords], CG.[HidePasswords], 0) = 0
THEN 0
ELSE 1
END) AS [HidePasswords],
MAX(CASE
WHEN
COALESCE(CU.[Manage], CG.[Manage], 0) = 0
THEN 0
ELSE 1
END) AS [Manage],
MAX(CASE
WHEN
CU.[CollectionId] IS NULL AND CG.[CollectionId] IS NULL
THEN 0
ELSE 1
END) AS [Assigned],
CASE
WHEN
-- No active user or group has manage rights
NOT EXISTS(
SELECT 1
FROM [dbo].[CollectionUser] CU2
JOIN [dbo].[OrganizationUser] OU2 ON CU2.[OrganizationUserId] = OU2.[Id]
WHERE
CU2.[CollectionId] = C.[Id] AND
OU2.[Status] = 2 AND
CU2.[Manage] = 1
)
AND NOT EXISTS (
SELECT 1
FROM [dbo].[CollectionGroup] CG2
WHERE
CG2.[CollectionId] = C.[Id] AND
CG2.[Manage] = 1
)
THEN 1
ELSE 0
END AS [Unmanaged]
FROM
[dbo].[CollectionView] C
LEFT JOIN
[dbo].[OrganizationUser] OU ON C.[OrganizationId] = OU.[OrganizationId] AND OU.[UserId] = @UserId
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
C.[OrganizationId] = @OrganizationId
GROUP BY
C.[Id],
C.[OrganizationId],
C.[Name],
C.[CreationDate],
C.[RevisionDate],
C.[ExternalId]
IF (@IncludeAccessRelationships = 1)
BEGIN
EXEC [dbo].[CollectionGroup_ReadByOrganizationId] @OrganizationId
EXEC [dbo].[CollectionUser_ReadByOrganizationId] @OrganizationId
END
END
GO
CREATE OR ALTER PROCEDURE [dbo].[Collection_ReadByIdWithPermissions]
@CollectionId UNIQUEIDENTIFIER,
@UserId UNIQUEIDENTIFIER,
@IncludeAccessRelationships BIT
AS
BEGIN
SET NOCOUNT ON
SELECT
C.*,
MIN(CASE
WHEN
COALESCE(CU.[ReadOnly], CG.[ReadOnly], 0) = 0
THEN 0
ELSE 1
END) AS [ReadOnly],
MIN (CASE
WHEN
COALESCE(CU.[HidePasswords], CG.[HidePasswords], 0) = 0
THEN 0
ELSE 1
END) AS [HidePasswords],
MAX(CASE
WHEN
COALESCE(CU.[Manage], CG.[Manage], 0) = 0
THEN 0
ELSE 1
END) AS [Manage],
MAX(CASE
WHEN
CU.[CollectionId] IS NULL AND CG.[CollectionId] IS NULL
THEN 0
ELSE 1
END) AS [Assigned],
CASE
WHEN
-- No active user or group has manage rights
NOT EXISTS(
SELECT 1
FROM [dbo].[CollectionUser] CU2
JOIN [dbo].[OrganizationUser] OU2 ON CU2.[OrganizationUserId] = OU2.[Id]
WHERE
CU2.[CollectionId] = C.[Id] AND
OU2.[Status] = 2 AND
CU2.[Manage] = 1
)
AND NOT EXISTS (
SELECT 1
FROM [dbo].[CollectionGroup] CG2
WHERE
CG2.[CollectionId] = C.[Id] AND
CG2.[Manage] = 1
)
THEN 1
ELSE 0
END AS [Unmanaged]
FROM
[dbo].[CollectionView] C
LEFT JOIN
[dbo].[OrganizationUser] OU ON C.[OrganizationId] = OU.[OrganizationId] AND OU.[UserId] = @UserId
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
C.[Id] = @CollectionId
GROUP BY
C.[Id],
C.[OrganizationId],
C.[Name],
C.[CreationDate],
C.[RevisionDate],
C.[ExternalId]
IF (@IncludeAccessRelationships = 1)
BEGIN
EXEC [dbo].[CollectionGroup_ReadByCollectionId] @CollectionId
EXEC [dbo].[CollectionUser_ReadByCollectionId] @CollectionId
END
END
GO