-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[PM-10319] - Revoke Non Complaint Users for 2FA and Single Org Policy Enablement #5037
base: main
Are you sure you want to change the base?
Conversation
# Conflicts: # src/Api/AdminConsole/Models/Response/Organizations/CommandResult.cs # src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/SingleOrgPolicyValidator.cs
New Issues
Fixed Issues
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, left some suggestions
src/Sql/dbo/Stored Procedures/OrganizationUser_SetStatusForUsersById.sql
Outdated
Show resolved
Hide resolved
src/Sql/dbo/Stored Procedures/OrganizationUser_SetStatusForUsersById.sql
Outdated
Show resolved
Hide resolved
src/Core/AdminConsole/OrganizationFeatures/Shared/CommandResult.cs
Outdated
Show resolved
Hide resolved
...nConsole/OrganizationFeatures/OrganizationUsers/RevokeNonCompliantOrganizationUserCommand.cs
Outdated
Show resolved
Hide resolved
src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Requests/RevokeOrganizationUser.cs
Outdated
Show resolved
Hide resolved
src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Requests/RevokeOrganizationUser.cs
Outdated
Show resolved
Hide resolved
…evokeNonCompliantOrganizationUserCommand.cs Co-authored-by: Rui Tomé <[email protected]>
…ation. Adding unknown type to event system user. Adding optional parameter to SaveAsync in policy service in order to pass in event system user.
...nConsole/OrganizationFeatures/OrganizationUsers/RevokeNonCompliantOrganizationUserCommand.cs
Show resolved
Hide resolved
...Core/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/SingleOrgPolicyValidator.cs
Outdated
Show resolved
Hide resolved
...Core/AdminConsole/OrganizationFeatures/Policies/PolicyValidators/SingleOrgPolicyValidator.cs
Outdated
Show resolved
Hide resolved
...ole/OrganizationFeatures/Policies/PolicyValidators/TwoFactorAuthenticationPolicyValidator.cs
Show resolved
Hide resolved
src/Core/AdminConsole/Repositories/IOrganizationUserRepository.cs
Outdated
Show resolved
Hide resolved
src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationUserRepository.cs
Outdated
Show resolved
Hide resolved
...AdminConsole/OrganizationFeatures/Policies/PolicyValidators/SingleOrgPolicyValidatorTests.cs
Outdated
Show resolved
Hide resolved
...rganizationFeatures/Policies/PolicyValidators/TwoFactorAuthenticationPolicyValidatorTests.cs
Outdated
Show resolved
Hide resolved
...ole/OrganizationFeatures/Policies/PolicyValidators/TwoFactorAuthenticationPolicyValidator.cs
Outdated
Show resolved
Hide resolved
|
||
|
||
public async Task<OrganizationDomain> UserVerifyOrganizationDomainAsync(OrganizationDomain organizationDomain) | ||
{ | ||
var domainVerificationResult = await VerifyOrganizationDomainAsync(organizationDomain); | ||
var actingUser = new StandardUser(currentContext.UserId ?? Guid.Empty, await currentContext.OrganizationOwner(organizationDomain.OrganizationId)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why pass an empty Guid and not the nullable Guid in the first parameter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather not have null. I could throw at this point instead of using Guid.Empty. I was just defaulting to Guid.Empty in the event that a caller hasn't initialized the CurrentContext.
Do we know which applications would call this method without the CurrentContext populated with a user?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about this method specifically, but generally:
- the Public API and SCIM -
CurrentContext.UserId
is null because it's the organization that's authenticated, not the user - a system call (jobs) - also not tied to a user
A regular call from a client through the private api will always have UserId populated (to my knowledge) because CurrentContext is always initialized in the middleware and the private api always requires user authentication.
What this means for you here I'm not sure. This method seems specifically designed to be called by a user, so throwing if there is no UserId available seems reasonable. And if the UserId is null for some reason, then _currentContext.OrganizationOwner
is probably not going to be accurate either (as this is also user specific). Really it seems like an invalid state.
src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/Requests/RevokeOrganizationUser.cs
Outdated
Show resolved
Hide resolved
...AdminConsole/OrganizationFeatures/Policies/PolicyValidators/SingleOrgPolicyValidatorTests.cs
Outdated
Show resolved
Hide resolved
...AdminConsole/OrganizationFeatures/Policies/PolicyValidators/SingleOrgPolicyValidatorTests.cs
Show resolved
Hide resolved
...rganizationFeatures/Policies/PolicyValidators/TwoFactorAuthenticationPolicyValidatorTests.cs
Show resolved
Hide resolved
...rganizationFeatures/Policies/PolicyValidators/TwoFactorAuthenticationPolicyValidatorTests.cs
Show resolved
Hide resolved
{ | ||
if (_featureService.IsEnabled(FeatureFlagKeys.Pm13322AddPolicyDefinitions)) | ||
{ | ||
// Transitional mapping - this will be moved to callers once the feature flag is removed | ||
// TODO make sure to populate with SystemUser if not an actual user |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO no longer needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This TODO will get addressed with a subsequent story.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can do this when I remove the policy definition feature flag shortly. Callers will have to construct their own PolicyUpdate
objects, so I'll need to pass in the right ActingUser
at that time.
src/Core/AdminConsole/Repositories/IOrganizationUserRepository.cs
Outdated
Show resolved
Hide resolved
...ole/OrganizationFeatures/OrganizationUsers/RevokeNonCompliantOrganizationUserCommandTests.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good, just a couple of things.
Also see my feedback above re: empty guid vs. throwing.
performedBy is StandardUser stdUser && | ||
stdUser.UserId != ou.UserId) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these last conditions need to be nested. If the ActingUser is a SystemUser, this will never evaluate to true for anyone.
var organizationUsersTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(orgUsers); | ||
var removableOrgUsers = orgUsers.Where(ou => | ||
ou.Status != OrganizationUserStatusType.Invited && ou.Status != OrganizationUserStatusType.Revoked && | ||
ou.Type != OrganizationUserType.Owner && ou.Type != OrganizationUserType.Admin && | ||
ou.UserId != savingUserId); | ||
|
||
// this can get moved into the private method after AccountDeprovisiong flag check is removed. | ||
var organizationUsersTwoFactorEnabled = | ||
(await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(orgUsers)).ToList(); | ||
|
||
var revocableOrgUsers = orgUsers.Where(ou => | ||
ou.Status != OrganizationUserStatusType.Invited && ou.Status != OrganizationUserStatusType.Revoked && | ||
ou.Type != OrganizationUserType.Owner && ou.Type != OrganizationUserType.Admin && | ||
ou.UserId != savingUserId) | ||
.ToList(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are unreverted changes here. I think it's functionally the same but would be best to properly revert. (e.g. it still refers to revocableOrgUsers
.)
performedBy is StandardUser stdUser && | ||
stdUser.UserId != ou.UserId) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
var organizationUsersTwoFactorEnabled = | ||
await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(currentActiveRevocableOrganizationUsers); | ||
|
||
if (DoMembersNotHaveAPasswordWithTwoFactorAuthenticationEnabled(currentActiveRevocableOrganizationUsers, organizationUsersTwoFactorEnabled)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure this method name is accurate; shouldn't it be "WithoutTwoFactorAuthenticationEnabled"? Or maybe the double negative is throwing me. How about if (NonCompliantMembersWillLoseAccess)
?
if (isAccountDeprovisioningEnabled) | ||
{ | ||
await sutProvider.GetDependency<IRevokeNonCompliantOrganizationUserCommand>() | ||
.Received(1) | ||
.RevokeNonCompliantOrganizationUsersAsync(Arg.Any<RevokeOrganizationUsersRequest>()); | ||
await sutProvider.GetDependency<IMailService>() | ||
.Received(1) | ||
.SendOrganizationUserRevokedForPolicySingleOrgEmailAsync(organization.DisplayName(), | ||
"[email protected]"); | ||
} | ||
else | ||
{ | ||
await sutProvider.GetDependency<IRemoveOrganizationUserCommand>() | ||
.Received(1) | ||
.RemoveUserAsync(policyUpdate.OrganizationId, nonCompliantUser.Id, savingUserId); | ||
await sutProvider.GetDependency<IMailService>() | ||
.Received(1) | ||
.SendOrganizationUserRemovedForPolicySingleOrgEmailAsync(organization.DisplayName(), | ||
"[email protected]"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a blocker, but I generally avoid conditional assertions in tests; this suggests to me that you should just have 2 tests. That's easier to clean up as well, because you can just delete the whole test.
🎟️ Tracking
PM-10319
📔 Objective
This change revokes non-compliant users when 2FA or Single Org policies are enabled. This adds the a
RevokeNonCompliantOrganizationUserCommand
along with a bulk set status stored procedure. It also adds two new email templates to be sent when a user is revoked from an organization.📸 Screenshots
Two FA Policy Revocation Email
Single Org Policy Email
⏰ Reminders before review
🦮 Reviewer guidelines
:+1:
) or similar for great changes:memo:
) or ℹ️ (:information_source:
) for notes or general info:question:
) for questions:thinking:
) or 💭 (:thought_balloon:
) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion:art:
) for suggestions / improvements:x:
) or:warning:
) for more significant problems or concerns needing attention:seedling:
) or ♻️ (:recycle:
) for future improvements or indications of technical debt:pick:
) for minor or nitpick changes