mirror of
https://github.com/bitwarden/server.git
synced 2024-11-21 12:05:42 +01:00
[EC-787] Create a method in PolicyService to check if a policy applies to a user (#2537)
* [EC-787] Add new stored procedure OrganizationUser_ReadByUserIdWithPolicyDetails * [EC-787] Add new method IOrganizationUserRepository.GetByUserIdWithPolicyDetailsAsync * [EC-787] Add OrganizationUserPolicyDetails to represent policies applicable to a specific user * [EC-787] Add method IPolicyService.GetPoliciesApplicableToUser to filter the obtained policy data * [EC-787] Returning PolicyData on stored procedures * [EC-787] Changed GetPoliciesApplicableToUserAsync to return ICollection * [EC-787] Switched all usings of IPolicyRepository.GetManyByTypeApplicableToUserIdAsync to IPolicyService.GetPoliciesApplicableToUserAsync * [EC-787] Removed policy logic from BaseRequestValidator and added usage of IPolicyService.GetPoliciesApplicableToUserAsync * [EC-787] Added unit tests for IPolicyService.GetPoliciesApplicableToUserAsync * [EC-787] Added unit tests for OrganizationUserRepository.GetByUserIdWithPolicyDetailsAsync * [EC-787] Changed integration test to check for single result * [EC-787] Marked IPolicyRepository methods GetManyByTypeApplicableToUserIdAsync and GetCountByTypeApplicableToUserIdAsync as obsolete * [EC-787] Returning OrganizationUserId on OrganizationUser_ReadByUserIdWithPolicyDetails * [EC-787] Remove deprecated stored procedures Policy_CountByTypeApplicableToUser, Policy_ReadByTypeApplicableToUser and function PolicyApplicableToUser * [EC-787] Added method IPolicyService.AnyPoliciesApplicableToUserAsync * [EC-787] Removed 'OrganizationUserType' parameter from queries * [EC-787] Formatted OrganizationUserPolicyDetailsCompare * [EC-787] Renamed SQL migration files * [EC-787] Changed OrganizationUser_ReadByUserIdWithPolicyDetails to return Permissions json * [EC-787] Refactored excluded user types for each Policy * [EC-787] Updated dates on dbo_future files * [EC-787] Remove dbo_future files from sql proj * [EC-787] Added parameter PolicyType to IOrganizationUserRepository.GetByUserIdWithPolicyDetailsAsync * [EC-787] Rewrote OrganizationUser_ReadByUserIdWithPolicyDetails and added parameter for PolicyType * Update util/Migrator/DbScripts/2023-03-10_00_OrganizationUserReadByUserIdWithPolicyDetails.sql Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
This commit is contained in:
parent
99b0953acd
commit
8d3fe12170
@ -0,0 +1,24 @@
|
|||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
|
|
||||||
|
public class OrganizationUserPolicyDetails
|
||||||
|
{
|
||||||
|
public Guid OrganizationUserId { get; set; }
|
||||||
|
|
||||||
|
public Guid OrganizationId { get; set; }
|
||||||
|
|
||||||
|
public PolicyType PolicyType { get; set; }
|
||||||
|
|
||||||
|
public bool PolicyEnabled { get; set; }
|
||||||
|
|
||||||
|
public string PolicyData { get; set; }
|
||||||
|
|
||||||
|
public OrganizationUserType OrganizationUserType { get; set; }
|
||||||
|
|
||||||
|
public OrganizationUserStatusType OrganizationUserStatus { get; set; }
|
||||||
|
|
||||||
|
public string OrganizationUserPermissionsData { get; set; }
|
||||||
|
|
||||||
|
public bool IsProvider { get; set; }
|
||||||
|
}
|
@ -39,4 +39,5 @@ 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);
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,4 @@ public interface IPolicyRepository : IRepository<Policy, Guid>
|
|||||||
Task<Policy> GetByOrganizationIdTypeAsync(Guid organizationId, PolicyType type);
|
Task<Policy> GetByOrganizationIdTypeAsync(Guid organizationId, PolicyType type);
|
||||||
Task<ICollection<Policy>> GetManyByOrganizationIdAsync(Guid organizationId);
|
Task<ICollection<Policy>> GetManyByOrganizationIdAsync(Guid organizationId);
|
||||||
Task<ICollection<Policy>> GetManyByUserIdAsync(Guid userId);
|
Task<ICollection<Policy>> GetManyByUserIdAsync(Guid userId);
|
||||||
Task<ICollection<Policy>> GetManyByTypeApplicableToUserIdAsync(Guid userId, PolicyType policyType,
|
|
||||||
OrganizationUserStatusType minStatus = OrganizationUserStatusType.Accepted);
|
|
||||||
Task<int> GetCountByTypeApplicableToUserIdAsync(Guid userId, PolicyType policyType,
|
|
||||||
OrganizationUserStatusType minStatus = OrganizationUserStatusType.Accepted);
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
using Bit.Core.Models.Data.Organizations.Policies;
|
using Bit.Core.Models.Data.Organizations.Policies;
|
||||||
|
|
||||||
namespace Bit.Core.Services;
|
namespace Bit.Core.Services;
|
||||||
@ -12,4 +14,6 @@ public interface IPolicyService
|
|||||||
/// Get the combined master password policy options for the specified user.
|
/// Get the combined master password policy options for the specified user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<MasterPasswordPolicyData> GetMasterPasswordPolicyForUserAsync(User user);
|
Task<MasterPasswordPolicyData> GetMasterPasswordPolicyForUserAsync(User user);
|
||||||
|
Task<ICollection<OrganizationUserPolicyDetails>> GetPoliciesApplicableToUserAsync(Guid userId, PolicyType policyType, OrganizationUserStatusType minStatus = OrganizationUserStatusType.Accepted);
|
||||||
|
Task<bool> AnyPoliciesApplicableToUserAsync(Guid userId, PolicyType policyType, OrganizationUserStatusType minStatus = OrganizationUserStatusType.Accepted);
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
private readonly IApplicationCacheService _applicationCacheService;
|
private readonly IApplicationCacheService _applicationCacheService;
|
||||||
private readonly IPaymentService _paymentService;
|
private readonly IPaymentService _paymentService;
|
||||||
private readonly IPolicyRepository _policyRepository;
|
private readonly IPolicyRepository _policyRepository;
|
||||||
|
private readonly IPolicyService _policyService;
|
||||||
private readonly ISsoConfigRepository _ssoConfigRepository;
|
private readonly ISsoConfigRepository _ssoConfigRepository;
|
||||||
private readonly ISsoUserRepository _ssoUserRepository;
|
private readonly ISsoUserRepository _ssoUserRepository;
|
||||||
private readonly IReferenceEventService _referenceEventService;
|
private readonly IReferenceEventService _referenceEventService;
|
||||||
@ -70,6 +71,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
IApplicationCacheService applicationCacheService,
|
IApplicationCacheService applicationCacheService,
|
||||||
IPaymentService paymentService,
|
IPaymentService paymentService,
|
||||||
IPolicyRepository policyRepository,
|
IPolicyRepository policyRepository,
|
||||||
|
IPolicyService policyService,
|
||||||
ISsoConfigRepository ssoConfigRepository,
|
ISsoConfigRepository ssoConfigRepository,
|
||||||
ISsoUserRepository ssoUserRepository,
|
ISsoUserRepository ssoUserRepository,
|
||||||
IReferenceEventService referenceEventService,
|
IReferenceEventService referenceEventService,
|
||||||
@ -97,6 +99,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
_applicationCacheService = applicationCacheService;
|
_applicationCacheService = applicationCacheService;
|
||||||
_paymentService = paymentService;
|
_paymentService = paymentService;
|
||||||
_policyRepository = policyRepository;
|
_policyRepository = policyRepository;
|
||||||
|
_policyService = policyService;
|
||||||
_ssoConfigRepository = ssoConfigRepository;
|
_ssoConfigRepository = ssoConfigRepository;
|
||||||
_ssoUserRepository = ssoUserRepository;
|
_ssoUserRepository = ssoUserRepository;
|
||||||
_referenceEventService = referenceEventService;
|
_referenceEventService = referenceEventService;
|
||||||
@ -690,8 +693,8 @@ public class OrganizationService : IOrganizationService
|
|||||||
|
|
||||||
private async Task ValidateSignUpPoliciesAsync(Guid ownerId)
|
private async Task ValidateSignUpPoliciesAsync(Guid ownerId)
|
||||||
{
|
{
|
||||||
var singleOrgPolicyCount = await _policyRepository.GetCountByTypeApplicableToUserIdAsync(ownerId, PolicyType.SingleOrg);
|
var anySingleOrgPolicies = await _policyService.AnyPoliciesApplicableToUserAsync(ownerId, PolicyType.SingleOrg);
|
||||||
if (singleOrgPolicyCount > 0)
|
if (anySingleOrgPolicies)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("You may not create an organization. You belong to an organization " +
|
throw new BadRequestException("You may not create an organization. You belong to an organization " +
|
||||||
"which has a policy that prohibits you from being a member of any other organization.");
|
"which has a policy that prohibits you from being a member of any other organization.");
|
||||||
@ -1296,7 +1299,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
// Enforce Single Organization Policy of organization user is trying to join
|
// Enforce Single Organization Policy of organization user is trying to join
|
||||||
var allOrgUsers = await _organizationUserRepository.GetManyByUserAsync(user.Id);
|
var allOrgUsers = await _organizationUserRepository.GetManyByUserAsync(user.Id);
|
||||||
var hasOtherOrgs = allOrgUsers.Any(ou => ou.OrganizationId != orgUser.OrganizationId);
|
var hasOtherOrgs = allOrgUsers.Any(ou => ou.OrganizationId != orgUser.OrganizationId);
|
||||||
var invitedSingleOrgPolicies = await _policyRepository.GetManyByTypeApplicableToUserIdAsync(user.Id,
|
var invitedSingleOrgPolicies = await _policyService.GetPoliciesApplicableToUserAsync(user.Id,
|
||||||
PolicyType.SingleOrg, OrganizationUserStatusType.Invited);
|
PolicyType.SingleOrg, OrganizationUserStatusType.Invited);
|
||||||
|
|
||||||
if (hasOtherOrgs && invitedSingleOrgPolicies.Any(p => p.OrganizationId == orgUser.OrganizationId))
|
if (hasOtherOrgs && invitedSingleOrgPolicies.Any(p => p.OrganizationId == orgUser.OrganizationId))
|
||||||
@ -1306,9 +1309,9 @@ public class OrganizationService : IOrganizationService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Enforce Single Organization Policy of other organizations user is a member of
|
// Enforce Single Organization Policy of other organizations user is a member of
|
||||||
var singleOrgPolicyCount = await _policyRepository.GetCountByTypeApplicableToUserIdAsync(user.Id,
|
var anySingleOrgPolicies = await _policyService.AnyPoliciesApplicableToUserAsync(user.Id,
|
||||||
PolicyType.SingleOrg);
|
PolicyType.SingleOrg);
|
||||||
if (singleOrgPolicyCount > 0)
|
if (anySingleOrgPolicies)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("You cannot join this organization because you are a member of " +
|
throw new BadRequestException("You cannot join this organization because you are a member of " +
|
||||||
"another organization which forbids it");
|
"another organization which forbids it");
|
||||||
@ -1317,7 +1320,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
// Enforce Two Factor Authentication Policy of organization user is trying to join
|
// Enforce Two Factor Authentication Policy of organization user is trying to join
|
||||||
if (!await userService.TwoFactorIsEnabledAsync(user))
|
if (!await userService.TwoFactorIsEnabledAsync(user))
|
||||||
{
|
{
|
||||||
var invitedTwoFactorPolicies = await _policyRepository.GetManyByTypeApplicableToUserIdAsync(user.Id,
|
var invitedTwoFactorPolicies = await _policyService.GetPoliciesApplicableToUserAsync(user.Id,
|
||||||
PolicyType.TwoFactorAuthentication, OrganizationUserStatusType.Invited);
|
PolicyType.TwoFactorAuthentication, OrganizationUserStatusType.Invited);
|
||||||
if (invitedTwoFactorPolicies.Any(p => p.OrganizationId == orgUser.OrganizationId))
|
if (invitedTwoFactorPolicies.Any(p => p.OrganizationId == orgUser.OrganizationId))
|
||||||
{
|
{
|
||||||
@ -2384,7 +2387,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
// Enforce Single Organization Policy of organization user is being restored to
|
// Enforce Single Organization Policy of organization user is being restored to
|
||||||
var allOrgUsers = await _organizationUserRepository.GetManyByUserAsync(userId);
|
var allOrgUsers = await _organizationUserRepository.GetManyByUserAsync(userId);
|
||||||
var hasOtherOrgs = allOrgUsers.Any(ou => ou.OrganizationId != orgUser.OrganizationId);
|
var hasOtherOrgs = allOrgUsers.Any(ou => ou.OrganizationId != orgUser.OrganizationId);
|
||||||
var singleOrgPoliciesApplyingToRevokedUsers = await _policyRepository.GetManyByTypeApplicableToUserIdAsync(userId,
|
var singleOrgPoliciesApplyingToRevokedUsers = await _policyService.GetPoliciesApplicableToUserAsync(userId,
|
||||||
PolicyType.SingleOrg, OrganizationUserStatusType.Revoked);
|
PolicyType.SingleOrg, OrganizationUserStatusType.Revoked);
|
||||||
var singleOrgPolicyApplies = singleOrgPoliciesApplyingToRevokedUsers.Any(p => p.OrganizationId == orgUser.OrganizationId);
|
var singleOrgPolicyApplies = singleOrgPoliciesApplyingToRevokedUsers.Any(p => p.OrganizationId == orgUser.OrganizationId);
|
||||||
|
|
||||||
@ -2395,9 +2398,9 @@ public class OrganizationService : IOrganizationService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Enforce Single Organization Policy of other organizations user is a member of
|
// Enforce Single Organization Policy of other organizations user is a member of
|
||||||
var singleOrgPolicyCount = await _policyRepository.GetCountByTypeApplicableToUserIdAsync(userId,
|
var anySingleOrgPolicies = await _policyService.AnyPoliciesApplicableToUserAsync(userId,
|
||||||
PolicyType.SingleOrg);
|
PolicyType.SingleOrg);
|
||||||
if (singleOrgPolicyCount > 0)
|
if (anySingleOrgPolicies)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("You cannot restore this user because they are a member of " +
|
throw new BadRequestException("You cannot restore this user because they are a member of " +
|
||||||
"another organization which forbids it");
|
"another organization which forbids it");
|
||||||
@ -2407,7 +2410,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
var user = await _userRepository.GetByIdAsync(userId);
|
var user = await _userRepository.GetByIdAsync(userId);
|
||||||
if (!await userService.TwoFactorIsEnabledAsync(user))
|
if (!await userService.TwoFactorIsEnabledAsync(user))
|
||||||
{
|
{
|
||||||
var invitedTwoFactorPolicies = await _policyRepository.GetManyByTypeApplicableToUserIdAsync(userId,
|
var invitedTwoFactorPolicies = await _policyService.GetPoliciesApplicableToUserAsync(userId,
|
||||||
PolicyType.TwoFactorAuthentication, OrganizationUserStatusType.Invited);
|
PolicyType.TwoFactorAuthentication, OrganizationUserStatusType.Invited);
|
||||||
if (invitedTwoFactorPolicies.Any(p => p.OrganizationId == orgUser.OrganizationId))
|
if (invitedTwoFactorPolicies.Any(p => p.OrganizationId == orgUser.OrganizationId))
|
||||||
{
|
{
|
||||||
|
@ -3,8 +3,10 @@ using Bit.Core.Auth.Repositories;
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
using Bit.Core.Models.Data.Organizations.Policies;
|
using Bit.Core.Models.Data.Organizations.Policies;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Settings;
|
||||||
|
|
||||||
namespace Bit.Core.Services;
|
namespace Bit.Core.Services;
|
||||||
|
|
||||||
@ -16,6 +18,7 @@ public class PolicyService : IPolicyService
|
|||||||
private readonly IPolicyRepository _policyRepository;
|
private readonly IPolicyRepository _policyRepository;
|
||||||
private readonly ISsoConfigRepository _ssoConfigRepository;
|
private readonly ISsoConfigRepository _ssoConfigRepository;
|
||||||
private readonly IMailService _mailService;
|
private readonly IMailService _mailService;
|
||||||
|
private readonly GlobalSettings _globalSettings;
|
||||||
|
|
||||||
public PolicyService(
|
public PolicyService(
|
||||||
IEventService eventService,
|
IEventService eventService,
|
||||||
@ -23,7 +26,8 @@ public class PolicyService : IPolicyService
|
|||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
IPolicyRepository policyRepository,
|
IPolicyRepository policyRepository,
|
||||||
ISsoConfigRepository ssoConfigRepository,
|
ISsoConfigRepository ssoConfigRepository,
|
||||||
IMailService mailService)
|
IMailService mailService,
|
||||||
|
GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
_eventService = eventService;
|
_eventService = eventService;
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
@ -31,6 +35,7 @@ public class PolicyService : IPolicyService
|
|||||||
_policyRepository = policyRepository;
|
_policyRepository = policyRepository;
|
||||||
_ssoConfigRepository = ssoConfigRepository;
|
_ssoConfigRepository = ssoConfigRepository;
|
||||||
_mailService = mailService;
|
_mailService = mailService;
|
||||||
|
_globalSettings = globalSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SaveAsync(Policy policy, IUserService userService, IOrganizationService organizationService,
|
public async Task SaveAsync(Policy policy, IUserService userService, IOrganizationService organizationService,
|
||||||
@ -164,6 +169,47 @@ public class PolicyService : IPolicyService
|
|||||||
return enforcedOptions;
|
return enforcedOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<ICollection<OrganizationUserPolicyDetails>> GetPoliciesApplicableToUserAsync(Guid userId, PolicyType policyType, OrganizationUserStatusType minStatus = OrganizationUserStatusType.Accepted)
|
||||||
|
{
|
||||||
|
var result = await QueryOrganizationUserPolicyDetailsAsync(userId, policyType, minStatus);
|
||||||
|
return result.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> AnyPoliciesApplicableToUserAsync(Guid userId, PolicyType policyType, OrganizationUserStatusType minStatus = OrganizationUserStatusType.Accepted)
|
||||||
|
{
|
||||||
|
var result = await QueryOrganizationUserPolicyDetailsAsync(userId, policyType, minStatus);
|
||||||
|
return result.Any();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IEnumerable<OrganizationUserPolicyDetails>> QueryOrganizationUserPolicyDetailsAsync(Guid userId, PolicyType policyType, OrganizationUserStatusType minStatus = OrganizationUserStatusType.Accepted)
|
||||||
|
{
|
||||||
|
var organizationUserPolicyDetails = await _organizationUserRepository.GetByUserIdWithPolicyDetailsAsync(userId, policyType);
|
||||||
|
var excludedUserTypes = GetUserTypesExcludedFromPolicy(policyType);
|
||||||
|
return organizationUserPolicyDetails.Where(o =>
|
||||||
|
o.PolicyEnabled &&
|
||||||
|
!excludedUserTypes.Contains(o.OrganizationUserType) &&
|
||||||
|
o.OrganizationUserStatus >= minStatus &&
|
||||||
|
!o.IsProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OrganizationUserType[] GetUserTypesExcludedFromPolicy(PolicyType policyType)
|
||||||
|
{
|
||||||
|
switch (policyType)
|
||||||
|
{
|
||||||
|
case PolicyType.MasterPassword:
|
||||||
|
return Array.Empty<OrganizationUserType>();
|
||||||
|
case PolicyType.RequireSso:
|
||||||
|
// If 'EnforceSsoPolicyForAllUsers' is set to true then SSO policy applies to all user types otherwise it does not apply to Owner or Admin
|
||||||
|
if (_globalSettings.Sso.EnforceSsoPolicyForAllUsers)
|
||||||
|
{
|
||||||
|
return Array.Empty<OrganizationUserType>();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new[] { OrganizationUserType.Owner, OrganizationUserType.Admin };
|
||||||
|
}
|
||||||
|
|
||||||
private async Task DependsOnSingleOrgAsync(Organization org)
|
private async Task DependsOnSingleOrgAsync(Organization org)
|
||||||
{
|
{
|
||||||
var singleOrg = await _policyRepository.GetByOrganizationIdTypeAsync(org.Id, PolicyType.SingleOrg);
|
var singleOrg = await _policyRepository.GetByOrganizationIdTypeAsync(org.Id, PolicyType.SingleOrg);
|
||||||
|
@ -46,6 +46,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
private readonly IApplicationCacheService _applicationCacheService;
|
private readonly IApplicationCacheService _applicationCacheService;
|
||||||
private readonly IPaymentService _paymentService;
|
private readonly IPaymentService _paymentService;
|
||||||
private readonly IPolicyRepository _policyRepository;
|
private readonly IPolicyRepository _policyRepository;
|
||||||
|
private readonly IPolicyService _policyService;
|
||||||
private readonly IDataProtector _organizationServiceDataProtector;
|
private readonly IDataProtector _organizationServiceDataProtector;
|
||||||
private readonly IReferenceEventService _referenceEventService;
|
private readonly IReferenceEventService _referenceEventService;
|
||||||
private readonly IFido2 _fido2;
|
private readonly IFido2 _fido2;
|
||||||
@ -77,6 +78,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
IDataProtectionProvider dataProtectionProvider,
|
IDataProtectionProvider dataProtectionProvider,
|
||||||
IPaymentService paymentService,
|
IPaymentService paymentService,
|
||||||
IPolicyRepository policyRepository,
|
IPolicyRepository policyRepository,
|
||||||
|
IPolicyService policyService,
|
||||||
IReferenceEventService referenceEventService,
|
IReferenceEventService referenceEventService,
|
||||||
IFido2 fido2,
|
IFido2 fido2,
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
@ -110,6 +112,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
_applicationCacheService = applicationCacheService;
|
_applicationCacheService = applicationCacheService;
|
||||||
_paymentService = paymentService;
|
_paymentService = paymentService;
|
||||||
_policyRepository = policyRepository;
|
_policyRepository = policyRepository;
|
||||||
|
_policyService = policyService;
|
||||||
_organizationServiceDataProtector = dataProtectionProvider.CreateProtector(
|
_organizationServiceDataProtector = dataProtectionProvider.CreateProtector(
|
||||||
"OrganizationServiceDataProtector");
|
"OrganizationServiceDataProtector");
|
||||||
_referenceEventService = referenceEventService;
|
_referenceEventService = referenceEventService;
|
||||||
@ -1414,8 +1417,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
|
|
||||||
private async Task CheckPoliciesOnTwoFactorRemovalAsync(User user, IOrganizationService organizationService)
|
private async Task CheckPoliciesOnTwoFactorRemovalAsync(User user, IOrganizationService organizationService)
|
||||||
{
|
{
|
||||||
var twoFactorPolicies = await _policyRepository.GetManyByTypeApplicableToUserIdAsync(user.Id,
|
var twoFactorPolicies = await _policyService.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication);
|
||||||
PolicyType.TwoFactorAuthentication);
|
|
||||||
|
|
||||||
var removeOrgUserTasks = twoFactorPolicies.Select(async p =>
|
var removeOrgUserTasks = twoFactorPolicies.Select(async p =>
|
||||||
{
|
{
|
||||||
|
@ -24,6 +24,7 @@ public class SendService : ISendService
|
|||||||
private readonly ISendRepository _sendRepository;
|
private readonly ISendRepository _sendRepository;
|
||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
private readonly IPolicyRepository _policyRepository;
|
private readonly IPolicyRepository _policyRepository;
|
||||||
|
private readonly IPolicyService _policyService;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
private readonly IOrganizationRepository _organizationRepository;
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
private readonly ISendFileStorageService _sendFileStorageService;
|
private readonly ISendFileStorageService _sendFileStorageService;
|
||||||
@ -45,12 +46,14 @@ public class SendService : ISendService
|
|||||||
IReferenceEventService referenceEventService,
|
IReferenceEventService referenceEventService,
|
||||||
GlobalSettings globalSettings,
|
GlobalSettings globalSettings,
|
||||||
IPolicyRepository policyRepository,
|
IPolicyRepository policyRepository,
|
||||||
|
IPolicyService policyService,
|
||||||
ICurrentContext currentContext)
|
ICurrentContext currentContext)
|
||||||
{
|
{
|
||||||
_sendRepository = sendRepository;
|
_sendRepository = sendRepository;
|
||||||
_userRepository = userRepository;
|
_userRepository = userRepository;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
_policyRepository = policyRepository;
|
_policyRepository = policyRepository;
|
||||||
|
_policyService = policyService;
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_sendFileStorageService = sendFileStorageService;
|
_sendFileStorageService = sendFileStorageService;
|
||||||
_passwordHasher = passwordHasher;
|
_passwordHasher = passwordHasher;
|
||||||
@ -282,17 +285,17 @@ public class SendService : ISendService
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var disableSendPolicyCount = await _policyRepository.GetCountByTypeApplicableToUserIdAsync(userId.Value,
|
var anyDisableSendPolicies = await _policyService.AnyPoliciesApplicableToUserAsync(userId.Value,
|
||||||
PolicyType.DisableSend);
|
PolicyType.DisableSend);
|
||||||
if (disableSendPolicyCount > 0)
|
if (anyDisableSendPolicies)
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Due to an Enterprise Policy, you are only able to delete an existing Send.");
|
throw new BadRequestException("Due to an Enterprise Policy, you are only able to delete an existing Send.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (send.HideEmail.GetValueOrDefault())
|
if (send.HideEmail.GetValueOrDefault())
|
||||||
{
|
{
|
||||||
var sendOptionsPolicies = await _policyRepository.GetManyByTypeApplicableToUserIdAsync(userId.Value, PolicyType.SendOptions);
|
var sendOptionsPolicies = await _policyService.GetPoliciesApplicableToUserAsync(userId.Value, PolicyType.SendOptions);
|
||||||
if (sendOptionsPolicies.Any(p => p.GetDataModel<SendOptionsPolicyData>()?.DisableHideEmail ?? false))
|
if (sendOptionsPolicies.Any(p => CoreHelpers.LoadClassFromJsonData<SendOptionsPolicyData>(p.PolicyData)?.DisableHideEmail ?? false))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Due to an Enterprise Policy, you are not allowed to hide your email address from recipients when creating or editing a Send.");
|
throw new BadRequestException("Due to an Enterprise Policy, you are not allowed to hide your email address from recipients when creating or editing a Send.");
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ public class CipherService : ICipherService
|
|||||||
private readonly IAttachmentStorageService _attachmentStorageService;
|
private readonly IAttachmentStorageService _attachmentStorageService;
|
||||||
private readonly IEventService _eventService;
|
private readonly IEventService _eventService;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
private readonly IPolicyRepository _policyRepository;
|
private readonly IPolicyService _policyService;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
private const long _fileSizeLeeway = 1024L * 1024L; // 1MB
|
private const long _fileSizeLeeway = 1024L * 1024L; // 1MB
|
||||||
private readonly IReferenceEventService _referenceEventService;
|
private readonly IReferenceEventService _referenceEventService;
|
||||||
@ -47,7 +47,7 @@ public class CipherService : ICipherService
|
|||||||
IAttachmentStorageService attachmentStorageService,
|
IAttachmentStorageService attachmentStorageService,
|
||||||
IEventService eventService,
|
IEventService eventService,
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
IPolicyRepository policyRepository,
|
IPolicyService policyService,
|
||||||
GlobalSettings globalSettings,
|
GlobalSettings globalSettings,
|
||||||
IReferenceEventService referenceEventService,
|
IReferenceEventService referenceEventService,
|
||||||
ICurrentContext currentContext)
|
ICurrentContext currentContext)
|
||||||
@ -62,7 +62,7 @@ public class CipherService : ICipherService
|
|||||||
_attachmentStorageService = attachmentStorageService;
|
_attachmentStorageService = attachmentStorageService;
|
||||||
_eventService = eventService;
|
_eventService = eventService;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
_policyRepository = policyRepository;
|
_policyService = policyService;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
_referenceEventService = referenceEventService;
|
_referenceEventService = referenceEventService;
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
@ -134,9 +134,8 @@ public class CipherService : ICipherService
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Make sure the user can save new ciphers to their personal vault
|
// Make sure the user can save new ciphers to their personal vault
|
||||||
var personalOwnershipPolicyCount = await _policyRepository.GetCountByTypeApplicableToUserIdAsync(savingUserId,
|
var anyPersonalOwnershipPolicies = await _policyService.AnyPoliciesApplicableToUserAsync(savingUserId, PolicyType.PersonalOwnership);
|
||||||
PolicyType.PersonalOwnership);
|
if (anyPersonalOwnershipPolicies)
|
||||||
if (personalOwnershipPolicyCount > 0)
|
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Due to an Enterprise Policy, you are restricted from saving items to your personal vault.");
|
throw new BadRequestException("Due to an Enterprise Policy, you are restricted from saving items to your personal vault.");
|
||||||
}
|
}
|
||||||
@ -632,9 +631,8 @@ public class CipherService : ICipherService
|
|||||||
var userId = folders.FirstOrDefault()?.UserId ?? ciphers.FirstOrDefault()?.UserId;
|
var userId = folders.FirstOrDefault()?.UserId ?? ciphers.FirstOrDefault()?.UserId;
|
||||||
|
|
||||||
// Make sure the user can save new ciphers to their personal vault
|
// Make sure the user can save new ciphers to their personal vault
|
||||||
var personalOwnershipPolicyCount = await _policyRepository.GetCountByTypeApplicableToUserIdAsync(userId.Value,
|
var anyPersonalOwnershipPolicies = await _policyService.AnyPoliciesApplicableToUserAsync(userId.Value, PolicyType.PersonalOwnership);
|
||||||
PolicyType.PersonalOwnership);
|
if (anyPersonalOwnershipPolicies)
|
||||||
if (personalOwnershipPolicyCount > 0)
|
|
||||||
{
|
{
|
||||||
throw new BadRequestException("You cannot import items into your personal vault because you are " +
|
throw new BadRequestException("You cannot import items into your personal vault because you are " +
|
||||||
"a member of an organization which forbids it.");
|
"a member of an organization which forbids it.");
|
||||||
|
@ -39,9 +39,8 @@ public abstract class BaseRequestValidator<T> where T : class
|
|||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
private readonly IPolicyRepository _policyRepository;
|
|
||||||
private readonly IUserRepository _userRepository;
|
|
||||||
private readonly IPolicyService _policyService;
|
private readonly IPolicyService _policyService;
|
||||||
|
private readonly IUserRepository _userRepository;
|
||||||
private readonly IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> _tokenDataFactory;
|
private readonly IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> _tokenDataFactory;
|
||||||
|
|
||||||
public BaseRequestValidator(
|
public BaseRequestValidator(
|
||||||
@ -76,7 +75,7 @@ public abstract class BaseRequestValidator<T> where T : class
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
_policyRepository = policyRepository;
|
_policyService = policyService;
|
||||||
_userRepository = userRepository;
|
_userRepository = userRepository;
|
||||||
_policyService = policyService;
|
_policyService = policyService;
|
||||||
_tokenDataFactory = tokenDataFactory;
|
_tokenDataFactory = tokenDataFactory;
|
||||||
@ -341,33 +340,11 @@ public abstract class BaseRequestValidator<T> where T : class
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is user apart of any orgs? Use cache for initial checks.
|
// Check if user belongs to any organization with an active SSO policy
|
||||||
var orgs = (await _currentContext.OrganizationMembershipAsync(_organizationUserRepository, user.Id))
|
var anySsoPoliciesApplicableToUser = await _policyService.AnyPoliciesApplicableToUserAsync(user.Id, PolicyType.RequireSso, OrganizationUserStatusType.Confirmed);
|
||||||
.ToList();
|
if (anySsoPoliciesApplicableToUser)
|
||||||
if (orgs.Any())
|
|
||||||
{
|
{
|
||||||
// Get all org abilities
|
return false;
|
||||||
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
|
||||||
// Parse all user orgs that are enabled and have the ability to use sso
|
|
||||||
var ssoOrgs = orgs.Where(o => OrgCanUseSso(orgAbilities, o.Id));
|
|
||||||
if (ssoOrgs.Any())
|
|
||||||
{
|
|
||||||
// Parse users orgs and determine if require sso policy is enabled
|
|
||||||
var userOrgs = await _organizationUserRepository.GetManyDetailsByUserAsync(user.Id,
|
|
||||||
OrganizationUserStatusType.Confirmed);
|
|
||||||
foreach (var userOrg in userOrgs.Where(o => o.Enabled && o.UseSso))
|
|
||||||
{
|
|
||||||
var orgPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(userOrg.OrganizationId,
|
|
||||||
PolicyType.RequireSso);
|
|
||||||
// Owners and Admins are exempt from this policy
|
|
||||||
if (orgPolicy != null && orgPolicy.Enabled &&
|
|
||||||
(_globalSettings.Sso.EnforceSsoPolicyForAllUsers ||
|
|
||||||
(userOrg.Type != OrganizationUserType.Owner && userOrg.Type != OrganizationUserType.Admin)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default - continue validation process
|
// Default - continue validation process
|
||||||
@ -380,12 +357,6 @@ public abstract class BaseRequestValidator<T> where T : class
|
|||||||
orgAbilities[orgId].Enabled && orgAbilities[orgId].Using2fa;
|
orgAbilities[orgId].Enabled && orgAbilities[orgId].Using2fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool OrgCanUseSso(IDictionary<Guid, OrganizationAbility> orgAbilities, Guid orgId)
|
|
||||||
{
|
|
||||||
return orgAbilities != null && orgAbilities.ContainsKey(orgId) &&
|
|
||||||
orgAbilities[orgId].Enabled && orgAbilities[orgId].UseSso;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Device GetDeviceFromRequest(ValidatedRequest request)
|
private Device GetDeviceFromRequest(ValidatedRequest request)
|
||||||
{
|
{
|
||||||
var deviceIdentifier = request.Raw["DeviceIdentifier"]?.ToString();
|
var deviceIdentifier = request.Raw["DeviceIdentifier"]?.ToString();
|
||||||
|
@ -491,4 +491,17 @@ public class OrganizationUserRepository : Repository<OrganizationUser, Guid>, IO
|
|||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<OrganizationUserPolicyDetails>> GetByUserIdWithPolicyDetailsAsync(Guid userId, PolicyType policyType)
|
||||||
|
{
|
||||||
|
using (var connection = new SqlConnection(ConnectionString))
|
||||||
|
{
|
||||||
|
var results = await connection.QueryAsync<OrganizationUserPolicyDetails>(
|
||||||
|
$"[{Schema}].[{Table}_ReadByUserIdWithPolicyDetails]",
|
||||||
|
new { UserId = userId, PolicyType = policyType },
|
||||||
|
commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
|
return results.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,32 +56,4 @@ public class PolicyRepository : Repository<Policy, Guid>, IPolicyRepository
|
|||||||
return results.ToList();
|
return results.ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ICollection<Policy>> GetManyByTypeApplicableToUserIdAsync(Guid userId, PolicyType policyType,
|
|
||||||
OrganizationUserStatusType minStatus)
|
|
||||||
{
|
|
||||||
using (var connection = new SqlConnection(ConnectionString))
|
|
||||||
{
|
|
||||||
var results = await connection.QueryAsync<Policy>(
|
|
||||||
$"[{Schema}].[{Table}_ReadByTypeApplicableToUser]",
|
|
||||||
new { UserId = userId, PolicyType = policyType, MinimumStatus = minStatus },
|
|
||||||
commandType: CommandType.StoredProcedure);
|
|
||||||
|
|
||||||
return results.ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> GetCountByTypeApplicableToUserIdAsync(Guid userId, PolicyType policyType,
|
|
||||||
OrganizationUserStatusType minStatus)
|
|
||||||
{
|
|
||||||
using (var connection = new SqlConnection(ConnectionString))
|
|
||||||
{
|
|
||||||
var result = await connection.ExecuteScalarAsync<int>(
|
|
||||||
$"[{Schema}].[{Table}_CountByTypeApplicableToUser]",
|
|
||||||
new { UserId = userId, PolicyType = policyType, MinimumStatus = minStatus },
|
|
||||||
commandType: CommandType.StoredProcedure);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -587,4 +587,38 @@ public class OrganizationUserRepository : Repository<Core.Entities.OrganizationU
|
|||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<OrganizationUserPolicyDetails>> GetByUserIdWithPolicyDetailsAsync(Guid userId, PolicyType policyType)
|
||||||
|
{
|
||||||
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
|
||||||
|
var providerOrganizations = from pu in dbContext.ProviderUsers
|
||||||
|
where pu.UserId == userId
|
||||||
|
join po in dbContext.ProviderOrganizations
|
||||||
|
on pu.ProviderId equals po.ProviderId
|
||||||
|
select po;
|
||||||
|
|
||||||
|
var query = from p in dbContext.Policies
|
||||||
|
join ou in dbContext.OrganizationUsers
|
||||||
|
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
|
||||||
|
where p.Type == policyType &&
|
||||||
|
(ou.UserId == userId || ou.Email == email)
|
||||||
|
select new OrganizationUserPolicyDetails
|
||||||
|
{
|
||||||
|
OrganizationUserId = ou.Id,
|
||||||
|
OrganizationId = p.OrganizationId,
|
||||||
|
PolicyType = p.Type,
|
||||||
|
PolicyEnabled = p.Enabled,
|
||||||
|
PolicyData = p.Data,
|
||||||
|
OrganizationUserType = ou.Type,
|
||||||
|
OrganizationUserStatus = ou.Status,
|
||||||
|
OrganizationUserPermissionsData = ou.Permissions,
|
||||||
|
IsProvider = providerOrganizations.Any(po => po.OrganizationId == p.OrganizationId)
|
||||||
|
};
|
||||||
|
return await query.ToListAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,29 +48,4 @@ public class PolicyRepository : Repository<Core.Entities.Policy, Policy, Guid>,
|
|||||||
return Mapper.Map<List<Core.Entities.Policy>>(results);
|
return Mapper.Map<List<Core.Entities.Policy>>(results);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ICollection<Core.Entities.Policy>> GetManyByTypeApplicableToUserIdAsync(Guid userId, PolicyType policyType,
|
|
||||||
OrganizationUserStatusType minStatus)
|
|
||||||
{
|
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
|
||||||
{
|
|
||||||
var dbContext = GetDatabaseContext(scope);
|
|
||||||
|
|
||||||
var query = new PolicyReadByTypeApplicableToUserQuery(userId, policyType, minStatus);
|
|
||||||
var results = await query.Run(dbContext).ToListAsync();
|
|
||||||
return Mapper.Map<List<Core.Entities.Policy>>(results);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<int> GetCountByTypeApplicableToUserIdAsync(Guid userId, PolicyType policyType,
|
|
||||||
OrganizationUserStatusType minStatus)
|
|
||||||
{
|
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
|
||||||
{
|
|
||||||
var dbContext = GetDatabaseContext(scope);
|
|
||||||
|
|
||||||
var query = new PolicyReadByTypeApplicableToUserQuery(userId, policyType, minStatus);
|
|
||||||
return await GetCountFromQuery(query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.Infrastructure.EntityFramework.Models;
|
|
||||||
|
|
||||||
namespace Bit.Infrastructure.EntityFramework.Repositories.Queries;
|
|
||||||
|
|
||||||
public class PolicyReadByTypeApplicableToUserQuery : IQuery<Policy>
|
|
||||||
{
|
|
||||||
private readonly Guid _userId;
|
|
||||||
private readonly PolicyType _policyType;
|
|
||||||
private readonly OrganizationUserStatusType _minimumStatus;
|
|
||||||
|
|
||||||
public PolicyReadByTypeApplicableToUserQuery(Guid userId, PolicyType policyType, OrganizationUserStatusType minimumStatus)
|
|
||||||
{
|
|
||||||
_userId = userId;
|
|
||||||
_policyType = policyType;
|
|
||||||
_minimumStatus = minimumStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IQueryable<Policy> Run(DatabaseContext dbContext)
|
|
||||||
{
|
|
||||||
var providerOrganizations = from pu in dbContext.ProviderUsers
|
|
||||||
where pu.UserId == _userId
|
|
||||||
join po in dbContext.ProviderOrganizations
|
|
||||||
on pu.ProviderId equals po.ProviderId
|
|
||||||
select po;
|
|
||||||
|
|
||||||
string userEmail = null;
|
|
||||||
if (_minimumStatus == OrganizationUserStatusType.Invited)
|
|
||||||
{
|
|
||||||
// Invited orgUsers do not have a UserId associated with them, so we have to match up their email
|
|
||||||
userEmail = dbContext.Users.Find(_userId)?.Email;
|
|
||||||
}
|
|
||||||
|
|
||||||
var query = from p in dbContext.Policies
|
|
||||||
join ou in dbContext.OrganizationUsers
|
|
||||||
on p.OrganizationId equals ou.OrganizationId
|
|
||||||
where
|
|
||||||
((_minimumStatus > OrganizationUserStatusType.Invited && ou.UserId == _userId) ||
|
|
||||||
(_minimumStatus == OrganizationUserStatusType.Invited && ou.Email == userEmail)) &&
|
|
||||||
p.Type == _policyType &&
|
|
||||||
p.Enabled &&
|
|
||||||
ou.Status >= _minimumStatus &&
|
|
||||||
ou.Type >= OrganizationUserType.User &&
|
|
||||||
(ou.Permissions == null ||
|
|
||||||
ou.Permissions.Contains($"\"managePolicies\":false")) &&
|
|
||||||
!providerOrganizations.Any(po => po.OrganizationId == p.OrganizationId)
|
|
||||||
select p;
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,34 @@
|
|||||||
|
CREATE PROCEDURE [dbo].[OrganizationUser_ReadByUserIdWithPolicyDetails]
|
||||||
|
@UserId UNIQUEIDENTIFIER,
|
||||||
|
@PolicyType TINYINT
|
||||||
|
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 EXISTS (
|
||||||
|
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
|
||||||
|
INNER JOIN [dbo].[OrganizationUserView] OU
|
||||||
|
ON P.[OrganizationId] = OU.[OrganizationId]
|
||||||
|
WHERE P.[Type] = @PolicyType AND
|
||||||
|
(
|
||||||
|
(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
|
2
src/Sql/dbo_future/Functions/PolicyApplicableToUser.sql
Normal file
2
src/Sql/dbo_future/Functions/PolicyApplicableToUser.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
-- Created 2023-03
|
||||||
|
-- DELETE FILE
|
@ -0,0 +1,2 @@
|
|||||||
|
-- Created 2023-03
|
||||||
|
-- DELETE FILE
|
@ -0,0 +1,2 @@
|
|||||||
|
-- Created 2023-03
|
||||||
|
-- DELETE FILE
|
@ -5,12 +5,14 @@ using Bit.Core.Auth.Repositories;
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using GlobalSettings = Bit.Core.Settings.GlobalSettings;
|
||||||
using PolicyFixtures = Bit.Core.Test.AutoFixture.PolicyFixtures;
|
using PolicyFixtures = Bit.Core.Test.AutoFixture.PolicyFixtures;
|
||||||
|
|
||||||
namespace Bit.Core.Test.Services;
|
namespace Bit.Core.Test.Services;
|
||||||
@ -394,10 +396,132 @@ public class PolicyServiceTests
|
|||||||
Assert.True(policy.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
|
Assert.True(policy.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task GetPoliciesApplicableToUserAsync_WithRequireSsoTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsNoPolicies(Guid userId, SutProvider<PolicyService> sutProvider)
|
||||||
|
{
|
||||||
|
SetupUserPolicies(userId, sutProvider);
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut
|
||||||
|
.GetPoliciesApplicableToUserAsync(userId, PolicyType.RequireSso);
|
||||||
|
|
||||||
|
Assert.Empty(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task GetPoliciesApplicableToUserAsync_WithRequireSsoTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsOnePolicy(Guid userId, SutProvider<PolicyService> sutProvider)
|
||||||
|
{
|
||||||
|
SetupUserPolicies(userId, sutProvider);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<GlobalSettings>().Sso.EnforceSsoPolicyForAllUsers.Returns(true);
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut
|
||||||
|
.GetPoliciesApplicableToUserAsync(userId, PolicyType.RequireSso);
|
||||||
|
|
||||||
|
Assert.Single(result);
|
||||||
|
Assert.True(result.All(details => details.PolicyEnabled));
|
||||||
|
Assert.True(result.All(details => details.PolicyType == PolicyType.RequireSso));
|
||||||
|
Assert.True(result.All(details => details.OrganizationUserType == OrganizationUserType.Owner));
|
||||||
|
Assert.True(result.All(details => details.OrganizationUserStatus == OrganizationUserStatusType.Confirmed));
|
||||||
|
Assert.True(result.All(details => !details.IsProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task GetPoliciesApplicableToUserAsync_WithDisableTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsNoPolicies(Guid userId, SutProvider<PolicyService> sutProvider)
|
||||||
|
{
|
||||||
|
SetupUserPolicies(userId, sutProvider);
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut
|
||||||
|
.GetPoliciesApplicableToUserAsync(userId, PolicyType.DisableSend);
|
||||||
|
|
||||||
|
Assert.Empty(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task GetPoliciesApplicableToUserAsync_WithDisableSendTypeFilter_WithInvitedUserStatusFilter_ReturnsOnePolicy(Guid userId, SutProvider<PolicyService> sutProvider)
|
||||||
|
{
|
||||||
|
SetupUserPolicies(userId, sutProvider);
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut
|
||||||
|
.GetPoliciesApplicableToUserAsync(userId, PolicyType.DisableSend, OrganizationUserStatusType.Invited);
|
||||||
|
|
||||||
|
Assert.Single(result);
|
||||||
|
Assert.True(result.All(details => details.PolicyEnabled));
|
||||||
|
Assert.True(result.All(details => details.PolicyType == PolicyType.DisableSend));
|
||||||
|
Assert.True(result.All(details => details.OrganizationUserType == OrganizationUserType.User));
|
||||||
|
Assert.True(result.All(details => details.OrganizationUserStatus == OrganizationUserStatusType.Invited));
|
||||||
|
Assert.True(result.All(details => !details.IsProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task AnyPoliciesApplicableToUserAsync_WithRequireSsoTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsFalse(Guid userId, SutProvider<PolicyService> sutProvider)
|
||||||
|
{
|
||||||
|
SetupUserPolicies(userId, sutProvider);
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut
|
||||||
|
.AnyPoliciesApplicableToUserAsync(userId, PolicyType.RequireSso);
|
||||||
|
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task AnyPoliciesApplicableToUserAsync_WithRequireSsoTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsTrue(Guid userId, SutProvider<PolicyService> sutProvider)
|
||||||
|
{
|
||||||
|
SetupUserPolicies(userId, sutProvider);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<GlobalSettings>().Sso.EnforceSsoPolicyForAllUsers.Returns(true);
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut
|
||||||
|
.AnyPoliciesApplicableToUserAsync(userId, PolicyType.RequireSso);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task AnyPoliciesApplicableToUserAsync_WithDisableTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsFalse(Guid userId, SutProvider<PolicyService> sutProvider)
|
||||||
|
{
|
||||||
|
SetupUserPolicies(userId, sutProvider);
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut
|
||||||
|
.AnyPoliciesApplicableToUserAsync(userId, PolicyType.DisableSend);
|
||||||
|
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task AnyPoliciesApplicableToUserAsync_WithDisableSendTypeFilter_WithInvitedUserStatusFilter_ReturnsTrue(Guid userId, SutProvider<PolicyService> sutProvider)
|
||||||
|
{
|
||||||
|
SetupUserPolicies(userId, sutProvider);
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut
|
||||||
|
.AnyPoliciesApplicableToUserAsync(userId, PolicyType.DisableSend, OrganizationUserStatusType.Invited);
|
||||||
|
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
private static void SetupOrg(SutProvider<PolicyService> sutProvider, Guid organizationId, Organization organization)
|
private static void SetupOrg(SutProvider<PolicyService> sutProvider, Guid organizationId, Organization organization)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IOrganizationRepository>()
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
.GetByIdAsync(organizationId)
|
.GetByIdAsync(organizationId)
|
||||||
.Returns(Task.FromResult(organization));
|
.Returns(Task.FromResult(organization));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void SetupUserPolicies(Guid userId, SutProvider<PolicyService> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
|
.GetByUserIdWithPolicyDetailsAsync(userId, PolicyType.RequireSso)
|
||||||
|
.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 = 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 }
|
||||||
|
});
|
||||||
|
|
||||||
|
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 = true }
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ using System.Text.Json;
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
using Bit.Core.Models.Data.Organizations.Policies;
|
using Bit.Core.Models.Data.Organizations.Policies;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
@ -33,8 +34,8 @@ public class SendServiceTests
|
|||||||
send.Id = default;
|
send.Id = default;
|
||||||
send.Type = sendType;
|
send.Type = sendType;
|
||||||
|
|
||||||
sutProvider.GetDependency<IPolicyRepository>().GetCountByTypeApplicableToUserIdAsync(
|
sutProvider.GetDependency<IPolicyService>().AnyPoliciesApplicableToUserAsync(
|
||||||
Arg.Any<Guid>(), PolicyType.DisableSend).Returns(disableSendPolicyAppliesToUser ? 1 : 0);
|
Arg.Any<Guid>(), PolicyType.DisableSend).Returns(disableSendPolicyAppliesToUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable Send policy check
|
// Disable Send policy check
|
||||||
@ -79,10 +80,10 @@ public class SendServiceTests
|
|||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
});
|
});
|
||||||
|
|
||||||
sutProvider.GetDependency<IPolicyRepository>().GetManyByTypeApplicableToUserIdAsync(
|
sutProvider.GetDependency<IPolicyService>().GetPoliciesApplicableToUserAsync(
|
||||||
Arg.Any<Guid>(), PolicyType.SendOptions).Returns(new List<Policy>
|
Arg.Any<Guid>(), PolicyType.SendOptions).Returns(new List<OrganizationUserPolicyDetails>()
|
||||||
{
|
{
|
||||||
policy,
|
new() { PolicyType = policy.Type, PolicyData = policy.Data, OrganizationId = policy.OrganizationId, PolicyEnabled = policy.Enabled }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers;
|
||||||
|
|
||||||
|
public class OrganizationUserPolicyDetailsCompare : IEqualityComparer<OrganizationUserPolicyDetails>
|
||||||
|
{
|
||||||
|
public bool Equals(OrganizationUserPolicyDetails x, OrganizationUserPolicyDetails y)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(x, y))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ReferenceEquals(x, null))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ReferenceEquals(y, null))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x.GetType() != y.GetType())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return x.OrganizationId.Equals(y.OrganizationId) &&
|
||||||
|
x.PolicyType == y.PolicyType &&
|
||||||
|
x.PolicyEnabled == y.PolicyEnabled &&
|
||||||
|
x.PolicyData == y.PolicyData &&
|
||||||
|
x.OrganizationUserType == y.OrganizationUserType &&
|
||||||
|
x.OrganizationUserStatus == y.OrganizationUserStatus &&
|
||||||
|
x.OrganizationUserPermissionsData == y.OrganizationUserPermissionsData &&
|
||||||
|
x.IsProvider == y.IsProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(OrganizationUserPolicyDetails obj)
|
||||||
|
{
|
||||||
|
return HashCode.Combine(obj.OrganizationId, (int)obj.PolicyType, obj.PolicyEnabled, obj.PolicyData, (int)obj.OrganizationUserType, (int)obj.OrganizationUserStatus, obj.OrganizationUserPermissionsData, obj.IsProvider);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,10 @@
|
|||||||
using Bit.Core.Entities;
|
using System.Text.Json;
|
||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Entities.Provider;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Test.AutoFixture.Attributes;
|
using Bit.Core.Test.AutoFixture.Attributes;
|
||||||
using Bit.Infrastructure.EFIntegration.Test.AutoFixture;
|
using Bit.Infrastructure.EFIntegration.Test.AutoFixture;
|
||||||
using Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers;
|
using Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers;
|
||||||
@ -144,4 +150,137 @@ public class OrganizationUserRepositoryTests
|
|||||||
savedSqlOrgUser = await sqlOrgUserRepo.GetByIdAsync(postSqlOrgUser.Id);
|
savedSqlOrgUser = await sqlOrgUserRepo.GetByIdAsync(postSqlOrgUser.Id);
|
||||||
Assert.True(savedSqlOrgUser == null);
|
Assert.True(savedSqlOrgUser == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CiSkippedTheory]
|
||||||
|
[EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, false, OrganizationUserStatusType.Confirmed, true, false)] // Ordinary user
|
||||||
|
[EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, false, OrganizationUserStatusType.Invited, true, false)] // Invited user
|
||||||
|
[EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.Owner, false, OrganizationUserStatusType.Confirmed, true, false)] // Owner
|
||||||
|
[EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.Admin, false, OrganizationUserStatusType.Confirmed, true, false)] // Admin
|
||||||
|
[EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, true, OrganizationUserStatusType.Confirmed, true, false)] // canManagePolicies
|
||||||
|
[EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, false, OrganizationUserStatusType.Confirmed, true, true)] // Provider
|
||||||
|
[EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, false, OrganizationUserStatusType.Confirmed, false, false)] // Policy disabled
|
||||||
|
[EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, false, OrganizationUserStatusType.Confirmed, true, false)] // No policy of Type
|
||||||
|
[EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, false, OrganizationUserStatusType.Invited, true, false)] // User not minStatus
|
||||||
|
public async void GetByUserIdWithPolicyDetailsAsync_Works_DataMatches(
|
||||||
|
// Inline data
|
||||||
|
OrganizationUserType userType,
|
||||||
|
bool canManagePolicies,
|
||||||
|
OrganizationUserStatusType orgUserStatus,
|
||||||
|
bool policyEnabled,
|
||||||
|
bool isProvider,
|
||||||
|
|
||||||
|
// Auto data - models
|
||||||
|
Policy policy,
|
||||||
|
User user,
|
||||||
|
Organization organization,
|
||||||
|
OrganizationUser orgUser,
|
||||||
|
Provider provider,
|
||||||
|
ProviderOrganization providerOrganization,
|
||||||
|
ProviderUser providerUser,
|
||||||
|
OrganizationUserPolicyDetailsCompare equalityComparer,
|
||||||
|
|
||||||
|
// Auto data - EF repos
|
||||||
|
List<EfRepo.PolicyRepository> efPolicyRepository,
|
||||||
|
List<EfRepo.UserRepository> efUserRepository,
|
||||||
|
List<EfRepo.OrganizationRepository> efOrganizationRepository,
|
||||||
|
List<EfRepo.OrganizationUserRepository> suts,
|
||||||
|
List<EfRepo.ProviderRepository> efProviderRepository,
|
||||||
|
List<EfRepo.ProviderOrganizationRepository> efProviderOrganizationRepository,
|
||||||
|
List<EfRepo.ProviderUserRepository> efProviderUserRepository,
|
||||||
|
|
||||||
|
// Auto data - SQL repos
|
||||||
|
SqlRepo.PolicyRepository sqlPolicyRepo,
|
||||||
|
SqlRepo.UserRepository sqlUserRepo,
|
||||||
|
SqlRepo.OrganizationRepository sqlOrganizationRepo,
|
||||||
|
SqlRepo.ProviderRepository sqlProviderRepo,
|
||||||
|
SqlRepo.OrganizationUserRepository sqlOrganizationUserRepo,
|
||||||
|
SqlRepo.ProviderOrganizationRepository sqlProviderOrganizationRepo,
|
||||||
|
SqlRepo.ProviderUserRepository sqlProviderUserRepo
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Combine EF and SQL repos into one list per type
|
||||||
|
var policyRepos = efPolicyRepository.ToList<IPolicyRepository>();
|
||||||
|
policyRepos.Add(sqlPolicyRepo);
|
||||||
|
var userRepos = efUserRepository.ToList<IUserRepository>();
|
||||||
|
userRepos.Add(sqlUserRepo);
|
||||||
|
var orgRepos = efOrganizationRepository.ToList<IOrganizationRepository>();
|
||||||
|
orgRepos.Add(sqlOrganizationRepo);
|
||||||
|
var orgUserRepos = suts.ToList<IOrganizationUserRepository>();
|
||||||
|
orgUserRepos.Add(sqlOrganizationUserRepo);
|
||||||
|
var providerRepos = efProviderRepository.ToList<IProviderRepository>();
|
||||||
|
providerRepos.Add(sqlProviderRepo);
|
||||||
|
var providerOrgRepos = efProviderOrganizationRepository.ToList<IProviderOrganizationRepository>();
|
||||||
|
providerOrgRepos.Add(sqlProviderOrganizationRepo);
|
||||||
|
var providerUserRepos = efProviderUserRepository.ToList<IProviderUserRepository>();
|
||||||
|
providerUserRepos.Add(sqlProviderUserRepo);
|
||||||
|
|
||||||
|
// Arrange data
|
||||||
|
var savedPolicyType = PolicyType.SingleOrg;
|
||||||
|
|
||||||
|
orgUser.Type = userType;
|
||||||
|
orgUser.Status = orgUserStatus;
|
||||||
|
var permissionsData = new Permissions { ManagePolicies = canManagePolicies };
|
||||||
|
orgUser.Permissions = JsonSerializer.Serialize(permissionsData, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
|
});
|
||||||
|
|
||||||
|
policy.Enabled = policyEnabled;
|
||||||
|
policy.Type = savedPolicyType;
|
||||||
|
|
||||||
|
var results = new List<OrganizationUserPolicyDetails>();
|
||||||
|
|
||||||
|
foreach (var policyRepo in policyRepos)
|
||||||
|
{
|
||||||
|
var i = policyRepos.IndexOf(policyRepo);
|
||||||
|
|
||||||
|
// Seed database
|
||||||
|
user.CreationDate = user.RevisionDate = DateTime.Now;
|
||||||
|
var savedUser = await userRepos[i].CreateAsync(user);
|
||||||
|
var savedOrg = await orgRepos[i].CreateAsync(organization);
|
||||||
|
|
||||||
|
// Invited orgUsers are not associated with an account yet, so they are identified by Email not UserId
|
||||||
|
if (orgUserStatus == OrganizationUserStatusType.Invited)
|
||||||
|
{
|
||||||
|
orgUser.Email = savedUser.Email;
|
||||||
|
orgUser.UserId = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
orgUser.UserId = savedUser.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
orgUser.OrganizationId = savedOrg.Id;
|
||||||
|
await orgUserRepos[i].CreateAsync(orgUser);
|
||||||
|
|
||||||
|
if (isProvider)
|
||||||
|
{
|
||||||
|
var savedProvider = await providerRepos[i].CreateAsync(provider);
|
||||||
|
|
||||||
|
providerOrganization.OrganizationId = savedOrg.Id;
|
||||||
|
providerOrganization.ProviderId = savedProvider.Id;
|
||||||
|
await providerOrgRepos[i].CreateAsync(providerOrganization);
|
||||||
|
|
||||||
|
providerUser.UserId = savedUser.Id;
|
||||||
|
providerUser.ProviderId = savedProvider.Id;
|
||||||
|
await providerUserRepos[i].CreateAsync(providerUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
policy.OrganizationId = savedOrg.Id;
|
||||||
|
await policyRepo.CreateAsync(policy);
|
||||||
|
if (efPolicyRepository.Contains(policyRepo))
|
||||||
|
{
|
||||||
|
(policyRepo as EfRepo.BaseEntityFrameworkRepository).ClearChangeTracking();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await orgUserRepos[i].GetByUserIdWithPolicyDetailsAsync(savedUser.Id, policy.Type);
|
||||||
|
results.Add(result.FirstOrDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var distinctItems = results.Distinct(equalityComparer);
|
||||||
|
|
||||||
|
Assert.Single(distinctItems);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
using System.Text.Json;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Entities;
|
|
||||||
using Bit.Core.Entities.Provider;
|
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.Core.Models.Data;
|
|
||||||
using Bit.Core.Repositories;
|
|
||||||
using Bit.Core.Test.AutoFixture.Attributes;
|
using Bit.Core.Test.AutoFixture.Attributes;
|
||||||
using Bit.Infrastructure.EFIntegration.Test.AutoFixture;
|
using Bit.Infrastructure.EFIntegration.Test.AutoFixture;
|
||||||
using Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers;
|
using Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers;
|
||||||
@ -53,143 +48,4 @@ public class PolicyRepositoryTests
|
|||||||
var distinctItems = savedPolicys.Distinct(equalityComparer);
|
var distinctItems = savedPolicys.Distinct(equalityComparer);
|
||||||
Assert.True(!distinctItems.Skip(1).Any());
|
Assert.True(!distinctItems.Skip(1).Any());
|
||||||
}
|
}
|
||||||
|
|
||||||
[CiSkippedTheory]
|
|
||||||
[EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, false, OrganizationUserStatusType.Confirmed, false, true, true, false)] // Ordinary user
|
|
||||||
[EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, false, OrganizationUserStatusType.Invited, true, true, true, false)] // Invited user
|
|
||||||
[EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.Owner, false, OrganizationUserStatusType.Confirmed, false, true, true, false)] // Owner
|
|
||||||
[EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.Admin, false, OrganizationUserStatusType.Confirmed, false, true, true, false)] // Admin
|
|
||||||
[EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, true, OrganizationUserStatusType.Confirmed, false, true, true, false)] // canManagePolicies
|
|
||||||
[EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, false, OrganizationUserStatusType.Confirmed, false, true, true, true)] // Provider
|
|
||||||
[EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, false, OrganizationUserStatusType.Confirmed, false, false, true, false)] // Policy disabled
|
|
||||||
[EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, false, OrganizationUserStatusType.Confirmed, false, true, false, false)] // No policy of Type
|
|
||||||
[EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, false, OrganizationUserStatusType.Invited, false, true, true, false)] // User not minStatus
|
|
||||||
|
|
||||||
public async void GetManyByTypeApplicableToUser_Works_DataMatches(
|
|
||||||
// Inline data
|
|
||||||
OrganizationUserType userType,
|
|
||||||
bool canManagePolicies,
|
|
||||||
OrganizationUserStatusType orgUserStatus,
|
|
||||||
bool includeInvited,
|
|
||||||
bool policyEnabled,
|
|
||||||
bool policySameType,
|
|
||||||
bool isProvider,
|
|
||||||
|
|
||||||
// Auto data - models
|
|
||||||
Policy policy,
|
|
||||||
User user,
|
|
||||||
Organization organization,
|
|
||||||
OrganizationUser orgUser,
|
|
||||||
Provider provider,
|
|
||||||
ProviderOrganization providerOrganization,
|
|
||||||
ProviderUser providerUser,
|
|
||||||
PolicyCompareIncludingOrganization equalityComparer,
|
|
||||||
|
|
||||||
// Auto data - EF repos
|
|
||||||
List<EfRepo.PolicyRepository> suts,
|
|
||||||
List<EfRepo.UserRepository> efUserRepository,
|
|
||||||
List<EfRepo.OrganizationRepository> efOrganizationRepository,
|
|
||||||
List<EfRepo.OrganizationUserRepository> efOrganizationUserRepository,
|
|
||||||
List<EfRepo.ProviderRepository> efProviderRepository,
|
|
||||||
List<EfRepo.ProviderOrganizationRepository> efProviderOrganizationRepository,
|
|
||||||
List<EfRepo.ProviderUserRepository> efProviderUserRepository,
|
|
||||||
|
|
||||||
// Auto data - SQL repos
|
|
||||||
SqlRepo.PolicyRepository sqlPolicyRepo,
|
|
||||||
SqlRepo.UserRepository sqlUserRepo,
|
|
||||||
SqlRepo.OrganizationRepository sqlOrganizationRepo,
|
|
||||||
SqlRepo.ProviderRepository sqlProviderRepo,
|
|
||||||
SqlRepo.OrganizationUserRepository sqlOrganizationUserRepo,
|
|
||||||
SqlRepo.ProviderOrganizationRepository sqlProviderOrganizationRepo,
|
|
||||||
SqlRepo.ProviderUserRepository sqlProviderUserRepo
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Combine EF and SQL repos into one list per type
|
|
||||||
var policyRepos = suts.ToList<IPolicyRepository>();
|
|
||||||
policyRepos.Add(sqlPolicyRepo);
|
|
||||||
var userRepos = efUserRepository.ToList<IUserRepository>();
|
|
||||||
userRepos.Add(sqlUserRepo);
|
|
||||||
var orgRepos = efOrganizationRepository.ToList<IOrganizationRepository>();
|
|
||||||
orgRepos.Add(sqlOrganizationRepo);
|
|
||||||
var orgUserRepos = efOrganizationUserRepository.ToList<IOrganizationUserRepository>();
|
|
||||||
orgUserRepos.Add(sqlOrganizationUserRepo);
|
|
||||||
var providerRepos = efProviderRepository.ToList<IProviderRepository>();
|
|
||||||
providerRepos.Add(sqlProviderRepo);
|
|
||||||
var providerOrgRepos = efProviderOrganizationRepository.ToList<IProviderOrganizationRepository>();
|
|
||||||
providerOrgRepos.Add(sqlProviderOrganizationRepo);
|
|
||||||
var providerUserRepos = efProviderUserRepository.ToList<IProviderUserRepository>();
|
|
||||||
providerUserRepos.Add(sqlProviderUserRepo);
|
|
||||||
|
|
||||||
// Arrange data
|
|
||||||
var savedPolicyType = PolicyType.SingleOrg;
|
|
||||||
var queriedPolicyType = policySameType ? savedPolicyType : PolicyType.DisableSend;
|
|
||||||
|
|
||||||
orgUser.Type = userType;
|
|
||||||
orgUser.Status = orgUserStatus;
|
|
||||||
var permissionsData = new Permissions { ManagePolicies = canManagePolicies };
|
|
||||||
orgUser.Permissions = JsonSerializer.Serialize(permissionsData, new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
||||||
});
|
|
||||||
|
|
||||||
policy.Enabled = policyEnabled;
|
|
||||||
policy.Type = savedPolicyType;
|
|
||||||
|
|
||||||
var results = new List<Policy>();
|
|
||||||
|
|
||||||
foreach (var policyRepo in policyRepos)
|
|
||||||
{
|
|
||||||
var i = policyRepos.IndexOf(policyRepo);
|
|
||||||
|
|
||||||
// Seed database
|
|
||||||
var savedUser = await userRepos[i].CreateAsync(user);
|
|
||||||
var savedOrg = await orgRepos[i].CreateAsync(organization);
|
|
||||||
|
|
||||||
// Invited orgUsers are not associated with an account yet, so they are identified by Email not UserId
|
|
||||||
if (orgUserStatus == OrganizationUserStatusType.Invited)
|
|
||||||
{
|
|
||||||
orgUser.Email = savedUser.Email;
|
|
||||||
orgUser.UserId = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
orgUser.UserId = savedUser.Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
orgUser.OrganizationId = savedOrg.Id;
|
|
||||||
await orgUserRepos[i].CreateAsync(orgUser);
|
|
||||||
|
|
||||||
if (isProvider)
|
|
||||||
{
|
|
||||||
var savedProvider = await providerRepos[i].CreateAsync(provider);
|
|
||||||
|
|
||||||
providerOrganization.OrganizationId = savedOrg.Id;
|
|
||||||
providerOrganization.ProviderId = savedProvider.Id;
|
|
||||||
await providerOrgRepos[i].CreateAsync(providerOrganization);
|
|
||||||
|
|
||||||
providerUser.UserId = savedUser.Id;
|
|
||||||
providerUser.ProviderId = savedProvider.Id;
|
|
||||||
await providerUserRepos[i].CreateAsync(providerUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
policy.OrganizationId = savedOrg.Id;
|
|
||||||
await policyRepo.CreateAsync(policy);
|
|
||||||
if (suts.Contains(policyRepo))
|
|
||||||
{
|
|
||||||
(policyRepo as EfRepo.BaseEntityFrameworkRepository).ClearChangeTracking();
|
|
||||||
}
|
|
||||||
|
|
||||||
var minStatus = includeInvited ? OrganizationUserStatusType.Invited : OrganizationUserStatusType.Accepted;
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = await policyRepo.GetManyByTypeApplicableToUserIdAsync(savedUser.Id, queriedPolicyType, minStatus);
|
|
||||||
results.Add(result.FirstOrDefault());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var distinctItems = results.Distinct(equalityComparer);
|
|
||||||
|
|
||||||
Assert.True(results.All(r => r == null) ||
|
|
||||||
!distinctItems.Skip(1).Any());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_ReadByUserIdWithPolicyDetails]
|
||||||
|
@UserId UNIQUEIDENTIFIER,
|
||||||
|
@PolicyType TINYINT
|
||||||
|
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 EXISTS (
|
||||||
|
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
|
||||||
|
INNER JOIN [dbo].[OrganizationUserView] OU
|
||||||
|
ON P.[OrganizationId] = OU.[OrganizationId]
|
||||||
|
WHERE P.[Type] = @PolicyType AND
|
||||||
|
(
|
||||||
|
(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
|
11
util/Migrator/DbScripts_future/2023-03-FutureMigration.sql
Normal file
11
util/Migrator/DbScripts_future/2023-03-FutureMigration.sql
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
-- Stored Procedure: Policy_CountByTypeApplicableToUser
|
||||||
|
DROP PROCEDURE [dbo].[Policy_CountByTypeApplicableToUser];
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- Stored Procedure: Policy_ReadByTypeApplicableToUser
|
||||||
|
DROP PROCEDURE [dbo].[Policy_ReadByTypeApplicableToUser];
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- Function: PolicyApplicableToUser
|
||||||
|
DROP FUNCTION [dbo].[PolicyApplicableToUser];
|
||||||
|
GO
|
Loading…
Reference in New Issue
Block a user