Conversation
…icable to a specific user
…ter the obtained policy data
…ableToUserIdAsync to IPolicyService.GetPoliciesApplicableToUserAsync
…ge of IPolicyService.GetPoliciesApplicableToUserAsync
…o-check-if-a-policy-applies-to-a-user
…ithPolicyDetailsAsync
…o-check-if-a-policy-applies-to-a-user
…erIdAsync and GetCountByTypeApplicableToUserIdAsync as obsolete
eliykat
left a comment
There was a problem hiding this comment.
I like it! Thank you for cleaning up my past sins (when I wrote that SQL query 😁 )
src/Core/Models/Data/Organizations/OrganizationUsers/OrganizationUserPolicyDetails.cs
Show resolved
Hide resolved
util/Migrator/DbScripts/2022-12-26_00_OrganizationUserReadByUserIdWithPolicyDetails.sql
Outdated
Show resolved
Hide resolved
| LEFT JOIN | ||
| (SELECT | ||
| PU.UserId, | ||
| PO.OrganizationId | ||
| FROM | ||
| [dbo].[ProviderUserView] PU | ||
| INNER JOIN | ||
| [ProviderOrganizationView] PO ON PO.[ProviderId] = PU.[ProviderId]) PUPO | ||
| ON PUPO.UserId = OU.UserId | ||
| AND PUPO.OrganizationId = P.OrganizationId |
There was a problem hiding this comment.
I don't like this nested query/join but I don't know if we can avoid it. Any ideas?
There was a problem hiding this comment.
I rewrote the query, let me know what you think.
src/Sql/dbo/Stored Procedures/OrganizationUser_ReadByUserIdWithPolicyDetails.sql
Show resolved
Hide resolved
...re.EFIntegration.Test/Repositories/EqualityComparers/OrganizationUserPolicyDetailsCompare.cs
Outdated
Show resolved
Hide resolved
...re.EFIntegration.Test/Repositories/EqualityComparers/OrganizationUserPolicyDetailsCompare.cs
Outdated
Show resolved
Hide resolved
|
Also please resolve conflicts (my fault, I'll make sure to re-review promptly) |
…o-check-if-a-policy-applies-to-a-user
…dWithPolicyDetails
…ableToUser, Policy_ReadByTypeApplicableToUser and function PolicyApplicableToUser
…o-check-if-a-policy-applies-to-a-user # Conflicts: # src/Sql/Sql.sqlproj
…turn Permissions json
| } | ||
|
|
||
| [Theory, BitAutoData] | ||
| public async Task AnyPoliciesApplicableToUserAsync_WithRequireSsoTypeFilter_WithDefaultUserTypeFilter_WithDefaultOrganizationUserStatusFilter_ReturnsFalse(Guid userId, SutProvider<PolicyService> sutProvider) |
There was a problem hiding this comment.
suggestion only: these test names are too focused on inputs and outputs and do not describe the actual behaviour being tested. What's the general idea here? "returns false if policy does not apply to user"? "RequireSSO policy does not apply to Owners"? I know we have to jam it into a method name so it won't look exactly like that (I prefer Jest for this reason 😁 ) but you get the idea.
There was a problem hiding this comment.
This is non-blocking, a suggestion only.
eliykat
left a comment
There was a problem hiding this comment.
This looks great, and good job picking it back up again - I know it's hard to load it all back into memory 😁 a few final changes/questions, then let's be done with it!
| var organizationUserPolicyDetails = await _organizationUserRepository.GetByUserIdWithPolicyDetailsAsync(userId); | ||
| var excludedUserTypes = GetUserTypesExcludedFromPolicy(policyType); | ||
| return organizationUserPolicyDetails.Where(o => | ||
| o.PolicyType == policyType && |
There was a problem hiding this comment.
I didn't notice this before (sorry!) but we don't need to fetch every policy type from the database just to filter them here.
The other checks here are fine because they're business logic that is more complex and likely to change in the future. But just getting the requested policy type seems like it should be done at the sproc level to minimize the amount of data being fetched from the db.
There was a problem hiding this comment.
You're right, good point. I've added a parameter for PolicyType in the sproc.
| LEFT JOIN | ||
| (SELECT | ||
| PU.UserId, | ||
| PO.OrganizationId | ||
| FROM | ||
| [dbo].[ProviderUserView] PU | ||
| INNER JOIN | ||
| [ProviderOrganizationView] PO ON PO.[ProviderId] = PU.[ProviderId]) PUPO | ||
| ON PUPO.UserId = OU.UserId | ||
| AND PUPO.OrganizationId = P.OrganizationId |
There was a problem hiding this comment.
Is there any benefit to this nested SELECT query, or can we just move the JOINS out into the main query?
| o.PolicyEnabled && | ||
| !excludedUserTypes.Contains(o.OrganizationUserType) && | ||
| o.OrganizationUserStatus >= minStatus && | ||
| !o.IsProvider); |
There was a problem hiding this comment.
Can you please confirm with Product that a ProviderUser for an organization is never subject to that organization's policies (e.g. not even RequireSSO or password complexity). They apply to owners so we may want to extend it there. Or Providers might be responsible for managing their own security.
There was a problem hiding this comment.
At the moment Providers are not subject to any policies - if this is going to change, we can do it in a separate PR just so we can merge this in the meantime. It might require client changes as well.
…tByUserIdWithPolicyDetailsAsync
…dded parameter for PolicyType
|
Kudos, SonarCloud Quality Gate passed!
|
…o-check-if-a-policy-applies-to-a-user # Conflicts: # src/Core/Services/IPolicyService.cs # src/Core/Services/Implementations/PolicyService.cs # src/Identity/IdentityServer/BaseRequestValidator.cs # src/Identity/IdentityServer/CustomTokenRequestValidator.cs # src/Identity/IdentityServer/ResourceOwnerPasswordValidator.cs # test/Core.Test/Services/PolicyServiceTests.cs # test/Core.Test/Tools/Services/SendServiceTests.cs
util/Migrator/DbScripts/2023-03-10_00_OrganizationUserReadByUserIdWithPolicyDetails.sql
Outdated
Show resolved
Hide resolved
…erIdWithPolicyDetails.sql Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
…o-check-if-a-policy-applies-to-a-user
…o-check-if-a-policy-applies-to-a-user # Conflicts: # src/Sql/Sql.sqlproj
…o-check-if-a-policy-applies-to-a-user # Conflicts: # src/Identity/IdentityServer/BaseRequestValidator.cs








Type of change
Objective
Whilst working on EC-758: Add environment var to enforce SSO for all users it was identified that the logic to check if a policy applies to a user is duplicated in:
Stored procedure
[dbo].[PolicyApplicableToUser]which is used by methodsPolicyRepository.GetManyByTypeApplicableToUserIdAsyncandPolicyRepository.GetCountByTypeApplicableToUserIdAsyncBaseRequestValidatorhas the same logic to check for the SSO policyThe goal is to lift the business logic from the stored procedure
[dbo].[PolicyApplicableToUser]into a method (PolicyService.GetPoliciesApplicableToUser(OrganizationUser orgUser, PolicyType policyType, OrganizationUserType minUserType, OrganizationUserStatusType minStatus)) which will retrieve the user’s organization policies from the database and then filter the results in memory according to our business rules.Code changes
[dbo].[OrganizationUser_ReadByUserIdWithPolicyDetails]GetByUserIdWithPolicyDetailsAsync(Guid userId)to call Stored Procedure[dbo].[OrganizationUser_ReadByUserIdWithPolicyDetails]GetPoliciesApplicableToUserAsyncthat will callIOrganizationUserRepository.GetByUserIdWithPolicyDetailsAsyncand apply business rules to filter resultsIPolicyRepository. GetCountByTypeApplicableToUserIdAsynctoIPolicyService.GetPoliciesApplicableToUserAsyncIPolicyRepository. GetCountByTypeApplicableToUserIdAsynctoIPolicyService.GetPoliciesApplicableToUserAsyncGetPoliciesApplicableToUserAsyncthat will callIOrganizationUserRepository.GetByUserIdWithPolicyDetailsAsyncand apply business rules to filter resultsIPolicyRepository. GetCountByTypeApplicableToUserIdAsynctoIPolicyService.GetPoliciesApplicableToUserAsyncIPolicyRepository. GetCountByTypeApplicableToUserIdAsynctoIPolicyService.GetPoliciesApplicableToUserAsyncIPolicyService.GetPoliciesApplicableToUserAsyncIPolicyServiceIPolicyServiceGetByUserIdWithPolicyDetailsAsync(Guid userId)to call Stored Procedure[dbo].[OrganizationUser_ReadByUserIdWithPolicyDetails][dbo].[OrganizationUser_ReadByUserIdWithPolicyDetails][dbo].[OrganizationUser_ReadByUserIdWithPolicyDetails][dbo].[OrganizationUser_ReadByUserIdWithPolicyDetails][dbo].[OrganizationUser_ReadByUserIdWithPolicyDetails]Before you submit
dotnet format --verify-no-changes) (required)