mirror of
https://github.com/bitwarden/server.git
synced 2024-11-21 12:05:42 +01:00
[PM-3007] Caching user policies on PolicyService variable (#3117)
* [PM-3007] Caching user policies on PolicyService variable * [PM-3007] Added missing newlines on sql files
This commit is contained in:
parent
73c6421bd3
commit
78588d0246
@ -39,6 +39,6 @@ public interface IOrganizationUserRepository : IRepository<OrganizationUser, Gui
|
|||||||
Task<IEnumerable<OrganizationUserUserDetails>> GetManyByMinimumRoleAsync(Guid organizationId, OrganizationUserType minRole);
|
Task<IEnumerable<OrganizationUserUserDetails>> GetManyByMinimumRoleAsync(Guid organizationId, OrganizationUserType minRole);
|
||||||
Task RevokeAsync(Guid id);
|
Task RevokeAsync(Guid id);
|
||||||
Task RestoreAsync(Guid id, OrganizationUserStatusType status);
|
Task RestoreAsync(Guid id, OrganizationUserStatusType status);
|
||||||
Task<IEnumerable<OrganizationUserPolicyDetails>> GetByUserIdWithPolicyDetailsAsync(Guid userId, PolicyType policyType);
|
Task<IEnumerable<OrganizationUserPolicyDetails>> GetByUserIdWithPolicyDetailsAsync(Guid userId);
|
||||||
Task<int> GetOccupiedSmSeatCountByOrganizationIdAsync(Guid organizationId);
|
Task<int> GetOccupiedSmSeatCountByOrganizationIdAsync(Guid organizationId);
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@ public class PolicyService : IPolicyService
|
|||||||
private readonly IMailService _mailService;
|
private readonly IMailService _mailService;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
|
|
||||||
|
private IEnumerable<OrganizationUserPolicyDetails> _cachedOrganizationUserPolicyDetails;
|
||||||
|
|
||||||
public PolicyService(
|
public PolicyService(
|
||||||
IEventService eventService,
|
IEventService eventService,
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
@ -194,18 +196,25 @@ public class PolicyService : IPolicyService
|
|||||||
return result.Any();
|
return result.Any();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IEnumerable<OrganizationUserPolicyDetails>> QueryOrganizationUserPolicyDetailsAsync(Guid userId, PolicyType policyType, OrganizationUserStatusType minStatus = OrganizationUserStatusType.Accepted)
|
private async Task<IEnumerable<OrganizationUserPolicyDetails>> QueryOrganizationUserPolicyDetailsAsync(Guid userId, PolicyType? policyType, OrganizationUserStatusType minStatus = OrganizationUserStatusType.Accepted)
|
||||||
{
|
{
|
||||||
var organizationUserPolicyDetails = await _organizationUserRepository.GetByUserIdWithPolicyDetailsAsync(userId, policyType);
|
// Check if the cached policies are available
|
||||||
|
if (_cachedOrganizationUserPolicyDetails == null)
|
||||||
|
{
|
||||||
|
// Cached policies not available, retrieve from the repository
|
||||||
|
_cachedOrganizationUserPolicyDetails = await _organizationUserRepository.GetByUserIdWithPolicyDetailsAsync(userId);
|
||||||
|
}
|
||||||
|
|
||||||
var excludedUserTypes = GetUserTypesExcludedFromPolicy(policyType);
|
var excludedUserTypes = GetUserTypesExcludedFromPolicy(policyType);
|
||||||
return organizationUserPolicyDetails.Where(o =>
|
return _cachedOrganizationUserPolicyDetails.Where(o =>
|
||||||
|
(policyType == null || o.PolicyType == policyType) &&
|
||||||
o.PolicyEnabled &&
|
o.PolicyEnabled &&
|
||||||
!excludedUserTypes.Contains(o.OrganizationUserType) &&
|
!excludedUserTypes.Contains(o.OrganizationUserType) &&
|
||||||
o.OrganizationUserStatus >= minStatus &&
|
o.OrganizationUserStatus >= minStatus &&
|
||||||
!o.IsProvider);
|
!o.IsProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
private OrganizationUserType[] GetUserTypesExcludedFromPolicy(PolicyType policyType)
|
private OrganizationUserType[] GetUserTypesExcludedFromPolicy(PolicyType? policyType)
|
||||||
{
|
{
|
||||||
switch (policyType)
|
switch (policyType)
|
||||||
{
|
{
|
||||||
|
@ -505,13 +505,13 @@ public class OrganizationUserRepository : Repository<OrganizationUser, Guid>, IO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<OrganizationUserPolicyDetails>> GetByUserIdWithPolicyDetailsAsync(Guid userId, PolicyType policyType)
|
public async Task<IEnumerable<OrganizationUserPolicyDetails>> GetByUserIdWithPolicyDetailsAsync(Guid userId)
|
||||||
{
|
{
|
||||||
using (var connection = new SqlConnection(ConnectionString))
|
using (var connection = new SqlConnection(ConnectionString))
|
||||||
{
|
{
|
||||||
var results = await connection.QueryAsync<OrganizationUserPolicyDetails>(
|
var results = await connection.QueryAsync<OrganizationUserPolicyDetails>(
|
||||||
$"[{Schema}].[{Table}_ReadByUserIdWithPolicyDetails]",
|
$"[{Schema}].[{Table}_ReadByUserIdWithPolicyDetails]",
|
||||||
new { UserId = userId, PolicyType = policyType },
|
new { UserId = userId },
|
||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
return results.ToList();
|
return results.ToList();
|
||||||
|
@ -588,7 +588,7 @@ public class OrganizationUserRepository : Repository<Core.Entities.OrganizationU
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<OrganizationUserPolicyDetails>> GetByUserIdWithPolicyDetailsAsync(Guid userId, PolicyType policyType)
|
public async Task<IEnumerable<OrganizationUserPolicyDetails>> GetByUserIdWithPolicyDetailsAsync(Guid userId)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
@ -604,8 +604,7 @@ public class OrganizationUserRepository : Repository<Core.Entities.OrganizationU
|
|||||||
join ou in dbContext.OrganizationUsers
|
join ou in dbContext.OrganizationUsers
|
||||||
on p.OrganizationId equals ou.OrganizationId
|
on p.OrganizationId equals ou.OrganizationId
|
||||||
let email = dbContext.Users.Find(userId).Email // Invited orgUsers do not have a UserId associated with them, so we have to match up their email
|
let email = dbContext.Users.Find(userId).Email // Invited orgUsers do not have a UserId associated with them, so we have to match up their email
|
||||||
where p.Type == policyType &&
|
where ou.UserId == userId || ou.Email == email
|
||||||
(ou.UserId == userId || ou.Email == email)
|
|
||||||
select new OrganizationUserPolicyDetails
|
select new OrganizationUserPolicyDetails
|
||||||
{
|
{
|
||||||
OrganizationUserId = ou.Id,
|
OrganizationUserId = ou.Id,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
CREATE PROCEDURE [dbo].[OrganizationUser_ReadByUserIdWithPolicyDetails]
|
CREATE PROCEDURE [dbo].[OrganizationUser_ReadByUserIdWithPolicyDetails]
|
||||||
@UserId UNIQUEIDENTIFIER,
|
@UserId UNIQUEIDENTIFIER
|
||||||
@PolicyType TINYINT
|
|
||||||
AS
|
AS
|
||||||
BEGIN
|
BEGIN
|
||||||
SET NOCOUNT ON
|
SET NOCOUNT ON
|
||||||
@ -13,22 +12,19 @@ SELECT
|
|||||||
OU.[Type] AS OrganizationUserType,
|
OU.[Type] AS OrganizationUserType,
|
||||||
OU.[Status] AS OrganizationUserStatus,
|
OU.[Status] AS OrganizationUserStatus,
|
||||||
OU.[Permissions] AS OrganizationUserPermissionsData,
|
OU.[Permissions] AS OrganizationUserPermissionsData,
|
||||||
CASE WHEN EXISTS (
|
CASE WHEN PU.[ProviderId] IS NOT NULL THEN 1 ELSE 0 END AS IsProvider
|
||||||
SELECT 1
|
|
||||||
FROM [dbo].[ProviderUserView] PU
|
|
||||||
INNER JOIN [dbo].[ProviderOrganizationView] PO ON PO.[ProviderId] = PU.[ProviderId]
|
|
||||||
WHERE PU.[UserId] = OU.[UserId] AND PO.[OrganizationId] = P.[OrganizationId]
|
|
||||||
) THEN 1 ELSE 0 END AS IsProvider
|
|
||||||
FROM [dbo].[PolicyView] P
|
FROM [dbo].[PolicyView] P
|
||||||
INNER JOIN [dbo].[OrganizationUserView] OU
|
INNER JOIN [dbo].[OrganizationUserView] OU
|
||||||
ON P.[OrganizationId] = OU.[OrganizationId]
|
ON P.[OrganizationId] = OU.[OrganizationId]
|
||||||
WHERE P.[Type] = @PolicyType AND
|
LEFT JOIN [dbo].[ProviderUserView] PU
|
||||||
(
|
ON PU.[UserId] = OU.[UserId]
|
||||||
(OU.[Status] != 0 AND OU.[UserId] = @UserId) -- OrgUsers who have accepted their invite and are linked to a UserId
|
LEFT JOIN [dbo].[ProviderOrganizationView] PO
|
||||||
OR EXISTS (
|
ON PO.[ProviderId] = PU.[ProviderId] AND PO.[OrganizationId] = P.[OrganizationId]
|
||||||
SELECT 1
|
WHERE
|
||||||
FROM [dbo].[UserView] U
|
(OU.[Status] != 0 AND OU.[UserId] = @UserId) -- OrgUsers who have accepted their invite and are linked to a UserId
|
||||||
WHERE U.[Id] = @UserId AND OU.[Email] = U.[Email] AND OU.[Status] = 0 -- 'Invited' OrgUsers are not linked to a UserId yet, so we have to look up their email
|
OR EXISTS (
|
||||||
)
|
SELECT 1
|
||||||
|
FROM [dbo].[UserView] U
|
||||||
|
WHERE U.[Id] = @UserId AND OU.[Email] = U.[Email] AND OU.[Status] = 0 -- 'Invited' OrgUsers are not linked to a UserId yet, so we have to look up their email
|
||||||
)
|
)
|
||||||
END
|
END
|
||||||
|
@ -624,18 +624,12 @@ public class PolicyServiceTests
|
|||||||
private static void SetupUserPolicies(Guid userId, SutProvider<PolicyService> sutProvider)
|
private static void SetupUserPolicies(Guid userId, SutProvider<PolicyService> sutProvider)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
.GetByUserIdWithPolicyDetailsAsync(userId, PolicyType.RequireSso)
|
.GetByUserIdWithPolicyDetailsAsync(userId)
|
||||||
.Returns(new List<OrganizationUserPolicyDetails>
|
.Returns(new List<OrganizationUserPolicyDetails>
|
||||||
{
|
{
|
||||||
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.RequireSso, PolicyEnabled = false, OrganizationUserType = OrganizationUserType.Owner, OrganizationUserStatus = OrganizationUserStatusType.Confirmed, IsProvider = false},
|
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.RequireSso, PolicyEnabled = false, OrganizationUserType = OrganizationUserType.Owner, OrganizationUserStatus = OrganizationUserStatusType.Confirmed, IsProvider = false},
|
||||||
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.RequireSso, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.Owner, OrganizationUserStatus = OrganizationUserStatusType.Confirmed, IsProvider = false },
|
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.RequireSso, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.Owner, OrganizationUserStatus = OrganizationUserStatusType.Confirmed, IsProvider = false },
|
||||||
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.RequireSso, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.Owner, OrganizationUserStatus = OrganizationUserStatusType.Confirmed, IsProvider = true }
|
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.RequireSso, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.Owner, OrganizationUserStatus = OrganizationUserStatusType.Confirmed, IsProvider = true },
|
||||||
});
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
||||||
.GetByUserIdWithPolicyDetailsAsync(userId, PolicyType.DisableSend)
|
|
||||||
.Returns(new List<OrganizationUserPolicyDetails>
|
|
||||||
{
|
|
||||||
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.DisableSend, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.User, OrganizationUserStatus = OrganizationUserStatusType.Invited, IsProvider = false },
|
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.DisableSend, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.User, OrganizationUserStatus = OrganizationUserStatusType.Invited, IsProvider = false },
|
||||||
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.DisableSend, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.User, OrganizationUserStatus = OrganizationUserStatusType.Invited, IsProvider = true }
|
new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.DisableSend, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.User, OrganizationUserStatus = OrganizationUserStatusType.Invited, IsProvider = true }
|
||||||
});
|
});
|
||||||
|
@ -274,7 +274,7 @@ public class OrganizationUserRepositoryTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await orgUserRepos[i].GetByUserIdWithPolicyDetailsAsync(savedUser.Id, policy.Type);
|
var result = await orgUserRepos[i].GetByUserIdWithPolicyDetailsAsync(savedUser.Id);
|
||||||
results.Add(result.FirstOrDefault());
|
results.Add(result.FirstOrDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_ReadByUserIdWithPolicyDetails]
|
||||||
|
@UserId UNIQUEIDENTIFIER
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
SELECT
|
||||||
|
OU.[Id] AS OrganizationUserId,
|
||||||
|
P.[OrganizationId],
|
||||||
|
P.[Type] AS PolicyType,
|
||||||
|
P.[Enabled] AS PolicyEnabled,
|
||||||
|
P.[Data] AS PolicyData,
|
||||||
|
OU.[Type] AS OrganizationUserType,
|
||||||
|
OU.[Status] AS OrganizationUserStatus,
|
||||||
|
OU.[Permissions] AS OrganizationUserPermissionsData,
|
||||||
|
CASE WHEN PU.[ProviderId] IS NOT NULL THEN 1 ELSE 0 END AS IsProvider
|
||||||
|
FROM [dbo].[PolicyView] P
|
||||||
|
INNER JOIN [dbo].[OrganizationUserView] OU
|
||||||
|
ON P.[OrganizationId] = OU.[OrganizationId]
|
||||||
|
LEFT JOIN [dbo].[ProviderUserView] PU
|
||||||
|
ON PU.[UserId] = OU.[UserId]
|
||||||
|
LEFT JOIN [dbo].[ProviderOrganizationView] PO
|
||||||
|
ON PO.[ProviderId] = PU.[ProviderId] AND PO.[OrganizationId] = P.[OrganizationId]
|
||||||
|
WHERE
|
||||||
|
(OU.[Status] != 0 AND OU.[UserId] = @UserId) -- OrgUsers who have accepted their invite and are linked to a UserId
|
||||||
|
OR EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM [dbo].[UserView] U
|
||||||
|
WHERE U.[Id] = @UserId AND OU.[Email] = U.[Email] AND OU.[Status] = 0 -- 'Invited' OrgUsers are not linked to a UserId yet, so we have to look up their email
|
||||||
|
)
|
||||||
|
END
|
||||||
|
GO
|
Loading…
Reference in New Issue
Block a user