1
0
mirror of https://github.com/bitwarden/server.git synced 2024-12-11 15:17:44 +01:00
bitwarden-server/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

566 lines
28 KiB
C#
Raw Normal View History

using System.Security.Claims;
using Bit.Api.AdminConsole.Controllers;
using Bit.Api.AdminConsole.Models.Request.Organizations;
using Bit.Api.Models.Request;
using Bit.Api.Vault.AuthorizationHandlers.Collections;
using Bit.Core;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Business;
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
Auth/PM-3275 - Changes to support TDE User without MP being able to Set a Password + misc refactoring (#3242) * PM-3275 - Add new GetMasterPasswordPolicy endpoint which will allow authenticated clients to get an enabled MP org policy if it exists for the purposes of enforcing those policy requirements when setting a password. * PM-3275 - AccountsController.cs - PostSetPasswordAsync - (1) Convert UserService.setPasswordAsync into new SetInitialMasterPasswordCommand (2) Refactor SetInitialMasterPasswordCommand to only accept post SSO users who are in the invited state (3) Add TODOs for more cleanup work and more commands * PM-3275 - Update AccountsControllerTests.cs to add new SetInitialMasterPasswordCommand * PM-3275 - UserService.cs - Remove non implemented ChangePasswordAsync method * PM-3275 - The new SetInitialMasterPasswordCommand leveraged the OrganizationService.cs AcceptUserAsync method so while I was in here I converted the AcceptUserAsync methods into a new AcceptOrgUserCommand.cs and turned the private method which accepted an existing org user public for use in the SetInitialMasterPasswordCommand * PM-3275 - Dotnet format * PM-3275 - Test SetInitialMasterPasswordCommand * Dotnet format * PM-3275 - In process AcceptOrgUserCommandTests.cs * PM-3275 - Migrate changes from AC-244 / #3199 over into new AcceptOrgUserCommand * PM-3275 - AcceptOrgUserCommand.cs - create data protector specifically for this command * PM-3275 - Add TODO for renaming / removing overloading of methods to improve readability / clarity * PM-3275 - AcceptOrgUserCommand.cs - refactor AcceptOrgUserAsync by OrgId to retrieve orgUser with _organizationUserRepository.GetByOrganizationAsync which gets a single user instead of a collection * PM-3275 - AcceptOrgUserCommand.cs - update name in TODO for evaluation later * PM-3275 / PM-1196 - (1) Slightly refactor SsoEmail2faSessionTokenable to provide public static GetTokenLifeTime() method for testing (2) Add missed tests to SsoEmail2faSessionTokenable in preparation for building tests for new OrgUserInviteTokenable.cs * PM-3275 / PM-1196 - Removing SsoEmail2faSessionTokenable.cs changes + tests as I've handled that separately in a new PR (#3270) for newly created task PM-3925 * PM-3275 - ExpiringTokenable.cs - add clarifying comments to help distinguish between the Valid property and the TokenIsValid method. * PM-3275 - Create OrgUserInviteTokenable.cs and add tests in OrgUserInviteTokenableTests.cs * PM-3275 - OrganizationService.cs - Refactor Org User Invite methods to use new OrgUserInviteTokenable instead of manual creation of a token * PM-3275 - OrgUserInviteTokenable.cs - clarify backwards compat note * PM-3275 - AcceptOrgUserCommand.cs - Add TODOs + minor name refactor * PM-3275 - AcceptOrgUserCommand.cs - replace method overloading with more easily readable names. * PM-3275 - AcceptOrgUserCommand.cs - Update ValidateOrgUserInviteToken to add new token validation while maintaining backwards compatibility for 1 release. * dotnet format * PM-3275 - AcceptOrgUserCommand.cs - Move private method below where it is used * PM-3275 - ServiceCollectionExtensions.cs - Must register IDataProtectorTokenFactory<OrgUserInviteTokenable> for new tokenable * PM-3275 - OrgUserInviteTokenable needed access to global settings to set its token lifetime to the _globalSettings.OrganizationInviteExpirationHours value. Creating a factory seemed the most straightforward way to encapsulate the desired creation logic. Unsure if in the correct location in ServiceCollectionExtensions.cs but will figure that out later. * PM-3275 - In process work of creating AcceptOrgUserCommandTests.cs * PM-3275 - Remove no longer relevant AcceptOrgUser tests from OrganizationServiceTests.cs * PM-3275 - Register OrgUserInviteTokenableFactory alongside tokenizer * PM-3275 - AcceptOrgUserCommandTests.cs - AcceptOrgUserAsync basic test suite completed. * PM-3275 - AcceptOrgUserCommandTests.cs - tweak test names * PM-3275 - AcceptOrgUserCommandTests.cs - (1) Remove old tests from OrganizationServiceTests as no longer needed to reference (2) Add summary for SetupCommonAcceptOrgUserMocks (3) Get AcceptOrgUserByToken_OldToken_AcceptsUserAndVerifiesEmail passing * PM-3275 - Create interface for OrgUserInviteTokenableFactory b/c that's the right thing to do + enables test substitution * PM-3275 - AcceptOrgUserCommandTests.cs - (1) Start work on AcceptOrgUserByToken_NewToken_AcceptsUserAndVerifiesEmail (2) Create and use SetupCommonAcceptOrgUserByTokenMocks() (3) Create generic FakeDataProtectorTokenFactory for tokenable testing * PM-3275 - (1) Get AcceptOrgUserByToken_NewToken_AcceptsUserAndVerifiesEmail test passing (2) Move FakeDataProtectorTokenFactory to own file * PM-3275 - AcceptOrgUserCommandTests.cs - Finish up tests for AcceptOrgUserByTokenAsync * PM-3275 - Add pseudo section comments * PM-3275 - Clean up unused params on AcceptOrgUserByToken_EmailMismatch_ThrowsBadRequest test * PM-3275 - (1) Tests written for AcceptOrgUserByOrgSsoIdAsync (2) Refactor happy path assertions into helper function AssertValidAcceptedOrgUser to reduce code duplication * PM-3275 - Finish up testing AcceptOrgUserCommandTests.cs by adding tests for AcceptOrgUserByOrgIdAsync * PM-3275 - Tweaking test naming to ensure consistency. * PM-3275 - Bugfix - OrgUserInviteTokenableFactory implementation required when declaring singleton service in ServiceCollectionExtensions.cs * PM-3275 - Resolve failing OrganizationServiceTests.cs * dotnet format * PM-3275 - PoliciesController.cs - GetMasterPasswordPolicy bugfix - for orgs without a MP policy, policy comes back as null and we should return notFound in that case. * PM-3275 - Add PoliciesControllerTests.cs specifically for new GetMasterPasswordPolicy(...) endpoint. * PM-3275 - dotnet format PoliciesControllerTests.cs * PM-3275 - PoliciesController.cs - (1) Add tech debt task number (2) Properly flag endpoint as deprecated * PM-3275 - Add new hasManageResetPasswordPermission property to ProfileResponseModel.cs primarily for sync so that we can condition client side if TDE user obtains elevated permissions * PM-3275 - Fix AccountsControllerTests.cs * PM-3275 - OrgUserInviteTokenable.cs - clarify TODO * PM-3275 - AcceptOrgUserCommand.cs - Refactor token validation to use short circuiting to only run old token validation if new token validation fails. * PM-3275 - OrgUserInviteTokenable.cs - (1) Add new static methods to centralize validation logic to avoid repetition (2) Add new token validation method so we can avoid having to pass in a full org user (and hitting the db to do so) * PM-3275 - Realized that the old token validation was used in the PoliciesController.cs (existing user clicks invite link in email and goes to log in) and UserService.cs (user clicks invite link in email and registers for a new acct). Added tech debt item for cleaning up backwards compatibility in future. * dotnet format * PM-3275 - (1) AccountsController.cs - Update PostSetPasswordAsync SetPasswordRequestModel to allow null keys for the case where we have a TDE user who obtains elevated permissions - they already have a user public and user encrypted private key saved in the db. (2) AccountsControllerTests.cs - test PostSetPasswordAsync scenarios to ensure changes will work as expected. * PM-3275 - PR review feedback - (1) set CurrentContext to private (2) Refactor GetProfile to use variables to improve clarity and simplify debugging. * PM-3275 - SyncController.cs - PR Review Feedback - Set current context as private instead of protected. * PM-3275 - CurrentContextExtensions.cs - PR Feedback - move parenthesis up from own line. * PM-3275 - SetInitialMasterPasswordCommandTests.cs - Replace unnecessary variable * PM-3275 - SetInitialMasterPasswordCommandTests.cs - PR Feedback - Add expected outcome statement to test name * PM-3275 - Set Initial Password command and tests - PR Feedback changes - (1) Rename orgIdentifier --> OrgSsoIdentifier for clarity (2) Update SetInitialMasterPasswordAsync to not allow null orgSsoId with explicit message saying this vs letting null org trigger invalid organization (3) Add test to cover this new scenario. * PM-3275 - SetInitialMasterPasswordCommand.cs - Move summary from implementation to interface to better respect standards and the fact that the interface is the more seen piece of code. * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, rename AcceptOrgUserByTokenAsync -> AcceptOrgUserByEmailTokenAsync + replace generic name token with emailToken * PM-3275 - OrganizationService.cs - Per PR feedback, remove dupe line * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, remove new lines in error messages for consistency. * PM-3275 - SetInitialMasterPasswordCommand.cs - Per PR feedback, adjust formatting of constructor for improved readability. * PM-3275 - CurrentContextExtensions.cs - Refactor AnyOrgUserHasManageResetPasswordPermission per PR feedback to remove unnecessary var. * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, remove completed TODO * PM-3275 - PoliciesController.cs - Per PR feedback, update GetByInvitedUser param to be guid instead of string. * PM-3275 - OrgUserInviteTokenable.cs - per PR feedback, add tech debt item info. * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, use const purpose from tokenable instead of magic string. * PM-3275 - Restore non duplicate line to fix tests * PM-3275 - Per PR feedback, revert all sync controller changes as the ProfileResponseModel.organizations array has org objects which have permissions which have the ManageResetPassword permission. So, I have the information that I need clientside already to determine if the user has the ManageResetPassword in any org. * PM-3275 - PoliciesControllerTests.cs - Update imports as the PoliciesController was moved under the admin console team's domain. * PM-3275 - Resolve issues from merge conflict resolutions to get solution building. * PM-3275 / PM-4633 - PoliciesController.cs - use orgUserId to look up user instead of orgId. Oops. * Fix user service tests * Resolve merge conflict
2023-11-02 16:02:25 +01:00
using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Microsoft.AspNetCore.Authorization;
using NSubstitute;
using Xunit;
namespace Bit.Api.Test.AdminConsole.Controllers;
2022-08-29 22:06:55 +02:00
[ControllerCustomize(typeof(OrganizationUsersController))]
[SutProviderCustomize]
public class OrganizationUsersControllerTests
{
Trusted Device Encryption feature (#3151) * [PM-1203] feat: allow verification for all passwordless accounts (#3038) * [PM-1033] Org invite user creation flow 1 (#3028) * [PM-1033] feat: remove user verification from password enrollment * [PM-1033] feat: auto accept invitation when enrolling into password reset * [PM-1033] fix: controller tests * [PM-1033] refactor: `UpdateUserResetPasswordEnrollmentCommand` * [PM-1033] refactor(wip): make `AcceptUserCommand` * Revert "[PM-1033] refactor(wip): make `AcceptUserCommand`" This reverts commit dc1319e7fa70c4844bbc70e0b01089b682ac2843. * Revert "[PM-1033] refactor: `UpdateUserResetPasswordEnrollmentCommand`" This reverts commit 43df689c7f244af4f7ffec1f9768a72081a624c3. * [PM-1033] refactor: move invite accept to controller This avoids creating yet another method that depends on having `IUserService` passed in as a parameter * [PM-1033] fix: add missing changes * [PM-1381] Add Trusted Device Keys to Auth Response (#3066) * Return Keys for Trusted Device - Check whether the current logging in device is trusted - Return their keys on successful login * Formatting * Address PR Feedback * Add Remarks Comment * [PM-1338] `AuthRequest` Event Logs (#3046) * Update AuthRequestController - Only allow AdminApproval Requests to be created from authed endpoint - Add endpoint that has authentication to be able to create admin approval * Add PasswordlessAuthSettings - Add settings for customizing expiration times * Add new EventTypes * Add Logic for AdminApproval Type - Add logic for validating AdminApproval expiration - Add event logging for Approval/Disapproval of AdminApproval - Add logic for creating AdminApproval types * Add Test Helpers - Change BitAutoData to allow you to use string representations of common types. * Add/Update AuthRequestService Tests * Run Formatting * Switch to 7 Days * Add Test Covering ResponseDate Being Set * Address PR Feedback - Create helper for checking if date is expired - Move validation logic into smaller methods * Switch to User Event Type - Make RequestDeviceApproval user type - User types will log for each org user is in * [PM-2998] Move Approving Device Check (#3101) * Move Check for Approving Devices - Exclude currently logging in device - Remove old way of checking - Add tests asserting behavior * Update DeviceType list * Update Naming & Address PR Feedback * Fix Tests * Address PR Feedback * Formatting * Now Fully Update Naming? * Feature/auth/pm 2759/add can reset password to user decryption options (#3113) * PM-2759 - BaseRequestValidator.cs - CreateUserDecryptionOptionsAsync - Add new hasManageResetPasswordPermission for post SSO redirect logic required on client. * PM-2759 - Update IdentityServerSsoTests.cs to all pass based on the addition of HasManageResetPasswordPermission to TrustedDeviceUserDecryptionOption * IdentityServerSsoTests.cs - fix typo in test name: LoggingApproval --> LoginApproval * PM1259 - Add test case for verifying that TrustedDeviceOption.hasManageResetPasswordPermission is set properly based on user permission * dotnet format run * Feature/auth/pm 2759/add can reset password to user decryption options fix jit users (#3120) * PM-2759 - IdentityServer - CreateUserDecryptionOptionsAsync - hasManageResetPasswordPermission set logic was broken for JIT provisioned users as I assumed we would always have a list of at least 1 org during the SSO process. Added TODO for future test addition but getting this out there now as QA is blocked by being unable to create JIT provisioned users. * dotnet format * Tiny tweak * [PM-1339] Allow Rotating Device Keys (#3096) * Allow Rotation of Trusted Device Keys - Add endpoint for getting keys relating to rotation - Add endpoint for rotating your current device - In the same endpoint allow a list of other devices to rotate * Formatting * Use Extension Method * Add Tests from PR Co-authored-by: Jared Snider <jsnider@bitwarden.com> --------- Co-authored-by: Jared Snider <jsnider@bitwarden.com> * Check the user directly if they have the ResetPasswordKey (#3153) * PM-3327 - UpdateKeyAsync must exempt the currently calling device from the logout notification in order to prevent prematurely logging the user out before the client side key rotation process can complete. The calling device will log itself out once it is done. (#3170) * Allow OTP Requests When Users Are On TDE (#3184) * [PM-3356][PM-3292] Allow OTP For All (#3188) * Allow OTP For All - On a trusted device isn't a good check because a user might be using a trusted device locally but not trusted it long term - The logic wasn't working for KC users anyways * Remove Old Comment * [AC-1601] Added RequireSso policy as a dependency of TDE (#3209) * Added RequireSso policy as a dependency of TDE. * Added test for RequireSso for TDE. * Added save. * Fixed policy name. --------- Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com> Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> Co-authored-by: Vincent Salucci <vincesalucci21@gmail.com> Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Co-authored-by: Jared Snider <jsnider@bitwarden.com>
2023-08-17 22:03:06 +02:00
[Theory]
[BitAutoData]
public async Task PutResetPasswordEnrollment_InivitedUser_AcceptsInvite(Guid orgId, Guid userId, OrganizationUserResetPasswordEnrollmentRequestModel model,
User user, OrganizationUser orgUser, SutProvider<OrganizationUsersController> sutProvider)
{
orgUser.Status = Core.Enums.OrganizationUserStatusType.Invited;
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user);
sutProvider.GetDependency<IOrganizationUserRepository>().GetByOrganizationAsync(default, default).ReturnsForAnyArgs(orgUser);
await sutProvider.Sut.PutResetPasswordEnrollment(orgId, userId, model);
Auth/PM-3275 - Changes to support TDE User without MP being able to Set a Password + misc refactoring (#3242) * PM-3275 - Add new GetMasterPasswordPolicy endpoint which will allow authenticated clients to get an enabled MP org policy if it exists for the purposes of enforcing those policy requirements when setting a password. * PM-3275 - AccountsController.cs - PostSetPasswordAsync - (1) Convert UserService.setPasswordAsync into new SetInitialMasterPasswordCommand (2) Refactor SetInitialMasterPasswordCommand to only accept post SSO users who are in the invited state (3) Add TODOs for more cleanup work and more commands * PM-3275 - Update AccountsControllerTests.cs to add new SetInitialMasterPasswordCommand * PM-3275 - UserService.cs - Remove non implemented ChangePasswordAsync method * PM-3275 - The new SetInitialMasterPasswordCommand leveraged the OrganizationService.cs AcceptUserAsync method so while I was in here I converted the AcceptUserAsync methods into a new AcceptOrgUserCommand.cs and turned the private method which accepted an existing org user public for use in the SetInitialMasterPasswordCommand * PM-3275 - Dotnet format * PM-3275 - Test SetInitialMasterPasswordCommand * Dotnet format * PM-3275 - In process AcceptOrgUserCommandTests.cs * PM-3275 - Migrate changes from AC-244 / #3199 over into new AcceptOrgUserCommand * PM-3275 - AcceptOrgUserCommand.cs - create data protector specifically for this command * PM-3275 - Add TODO for renaming / removing overloading of methods to improve readability / clarity * PM-3275 - AcceptOrgUserCommand.cs - refactor AcceptOrgUserAsync by OrgId to retrieve orgUser with _organizationUserRepository.GetByOrganizationAsync which gets a single user instead of a collection * PM-3275 - AcceptOrgUserCommand.cs - update name in TODO for evaluation later * PM-3275 / PM-1196 - (1) Slightly refactor SsoEmail2faSessionTokenable to provide public static GetTokenLifeTime() method for testing (2) Add missed tests to SsoEmail2faSessionTokenable in preparation for building tests for new OrgUserInviteTokenable.cs * PM-3275 / PM-1196 - Removing SsoEmail2faSessionTokenable.cs changes + tests as I've handled that separately in a new PR (#3270) for newly created task PM-3925 * PM-3275 - ExpiringTokenable.cs - add clarifying comments to help distinguish between the Valid property and the TokenIsValid method. * PM-3275 - Create OrgUserInviteTokenable.cs and add tests in OrgUserInviteTokenableTests.cs * PM-3275 - OrganizationService.cs - Refactor Org User Invite methods to use new OrgUserInviteTokenable instead of manual creation of a token * PM-3275 - OrgUserInviteTokenable.cs - clarify backwards compat note * PM-3275 - AcceptOrgUserCommand.cs - Add TODOs + minor name refactor * PM-3275 - AcceptOrgUserCommand.cs - replace method overloading with more easily readable names. * PM-3275 - AcceptOrgUserCommand.cs - Update ValidateOrgUserInviteToken to add new token validation while maintaining backwards compatibility for 1 release. * dotnet format * PM-3275 - AcceptOrgUserCommand.cs - Move private method below where it is used * PM-3275 - ServiceCollectionExtensions.cs - Must register IDataProtectorTokenFactory<OrgUserInviteTokenable> for new tokenable * PM-3275 - OrgUserInviteTokenable needed access to global settings to set its token lifetime to the _globalSettings.OrganizationInviteExpirationHours value. Creating a factory seemed the most straightforward way to encapsulate the desired creation logic. Unsure if in the correct location in ServiceCollectionExtensions.cs but will figure that out later. * PM-3275 - In process work of creating AcceptOrgUserCommandTests.cs * PM-3275 - Remove no longer relevant AcceptOrgUser tests from OrganizationServiceTests.cs * PM-3275 - Register OrgUserInviteTokenableFactory alongside tokenizer * PM-3275 - AcceptOrgUserCommandTests.cs - AcceptOrgUserAsync basic test suite completed. * PM-3275 - AcceptOrgUserCommandTests.cs - tweak test names * PM-3275 - AcceptOrgUserCommandTests.cs - (1) Remove old tests from OrganizationServiceTests as no longer needed to reference (2) Add summary for SetupCommonAcceptOrgUserMocks (3) Get AcceptOrgUserByToken_OldToken_AcceptsUserAndVerifiesEmail passing * PM-3275 - Create interface for OrgUserInviteTokenableFactory b/c that's the right thing to do + enables test substitution * PM-3275 - AcceptOrgUserCommandTests.cs - (1) Start work on AcceptOrgUserByToken_NewToken_AcceptsUserAndVerifiesEmail (2) Create and use SetupCommonAcceptOrgUserByTokenMocks() (3) Create generic FakeDataProtectorTokenFactory for tokenable testing * PM-3275 - (1) Get AcceptOrgUserByToken_NewToken_AcceptsUserAndVerifiesEmail test passing (2) Move FakeDataProtectorTokenFactory to own file * PM-3275 - AcceptOrgUserCommandTests.cs - Finish up tests for AcceptOrgUserByTokenAsync * PM-3275 - Add pseudo section comments * PM-3275 - Clean up unused params on AcceptOrgUserByToken_EmailMismatch_ThrowsBadRequest test * PM-3275 - (1) Tests written for AcceptOrgUserByOrgSsoIdAsync (2) Refactor happy path assertions into helper function AssertValidAcceptedOrgUser to reduce code duplication * PM-3275 - Finish up testing AcceptOrgUserCommandTests.cs by adding tests for AcceptOrgUserByOrgIdAsync * PM-3275 - Tweaking test naming to ensure consistency. * PM-3275 - Bugfix - OrgUserInviteTokenableFactory implementation required when declaring singleton service in ServiceCollectionExtensions.cs * PM-3275 - Resolve failing OrganizationServiceTests.cs * dotnet format * PM-3275 - PoliciesController.cs - GetMasterPasswordPolicy bugfix - for orgs without a MP policy, policy comes back as null and we should return notFound in that case. * PM-3275 - Add PoliciesControllerTests.cs specifically for new GetMasterPasswordPolicy(...) endpoint. * PM-3275 - dotnet format PoliciesControllerTests.cs * PM-3275 - PoliciesController.cs - (1) Add tech debt task number (2) Properly flag endpoint as deprecated * PM-3275 - Add new hasManageResetPasswordPermission property to ProfileResponseModel.cs primarily for sync so that we can condition client side if TDE user obtains elevated permissions * PM-3275 - Fix AccountsControllerTests.cs * PM-3275 - OrgUserInviteTokenable.cs - clarify TODO * PM-3275 - AcceptOrgUserCommand.cs - Refactor token validation to use short circuiting to only run old token validation if new token validation fails. * PM-3275 - OrgUserInviteTokenable.cs - (1) Add new static methods to centralize validation logic to avoid repetition (2) Add new token validation method so we can avoid having to pass in a full org user (and hitting the db to do so) * PM-3275 - Realized that the old token validation was used in the PoliciesController.cs (existing user clicks invite link in email and goes to log in) and UserService.cs (user clicks invite link in email and registers for a new acct). Added tech debt item for cleaning up backwards compatibility in future. * dotnet format * PM-3275 - (1) AccountsController.cs - Update PostSetPasswordAsync SetPasswordRequestModel to allow null keys for the case where we have a TDE user who obtains elevated permissions - they already have a user public and user encrypted private key saved in the db. (2) AccountsControllerTests.cs - test PostSetPasswordAsync scenarios to ensure changes will work as expected. * PM-3275 - PR review feedback - (1) set CurrentContext to private (2) Refactor GetProfile to use variables to improve clarity and simplify debugging. * PM-3275 - SyncController.cs - PR Review Feedback - Set current context as private instead of protected. * PM-3275 - CurrentContextExtensions.cs - PR Feedback - move parenthesis up from own line. * PM-3275 - SetInitialMasterPasswordCommandTests.cs - Replace unnecessary variable * PM-3275 - SetInitialMasterPasswordCommandTests.cs - PR Feedback - Add expected outcome statement to test name * PM-3275 - Set Initial Password command and tests - PR Feedback changes - (1) Rename orgIdentifier --> OrgSsoIdentifier for clarity (2) Update SetInitialMasterPasswordAsync to not allow null orgSsoId with explicit message saying this vs letting null org trigger invalid organization (3) Add test to cover this new scenario. * PM-3275 - SetInitialMasterPasswordCommand.cs - Move summary from implementation to interface to better respect standards and the fact that the interface is the more seen piece of code. * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, rename AcceptOrgUserByTokenAsync -> AcceptOrgUserByEmailTokenAsync + replace generic name token with emailToken * PM-3275 - OrganizationService.cs - Per PR feedback, remove dupe line * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, remove new lines in error messages for consistency. * PM-3275 - SetInitialMasterPasswordCommand.cs - Per PR feedback, adjust formatting of constructor for improved readability. * PM-3275 - CurrentContextExtensions.cs - Refactor AnyOrgUserHasManageResetPasswordPermission per PR feedback to remove unnecessary var. * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, remove completed TODO * PM-3275 - PoliciesController.cs - Per PR feedback, update GetByInvitedUser param to be guid instead of string. * PM-3275 - OrgUserInviteTokenable.cs - per PR feedback, add tech debt item info. * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, use const purpose from tokenable instead of magic string. * PM-3275 - Restore non duplicate line to fix tests * PM-3275 - Per PR feedback, revert all sync controller changes as the ProfileResponseModel.organizations array has org objects which have permissions which have the ManageResetPassword permission. So, I have the information that I need clientside already to determine if the user has the ManageResetPassword in any org. * PM-3275 - PoliciesControllerTests.cs - Update imports as the PoliciesController was moved under the admin console team's domain. * PM-3275 - Resolve issues from merge conflict resolutions to get solution building. * PM-3275 / PM-4633 - PoliciesController.cs - use orgUserId to look up user instead of orgId. Oops. * Fix user service tests * Resolve merge conflict
2023-11-02 16:02:25 +01:00
await sutProvider.GetDependency<IAcceptOrgUserCommand>().Received(1).AcceptOrgUserByOrgIdAsync(orgId, user, sutProvider.GetDependency<IUserService>());
Trusted Device Encryption feature (#3151) * [PM-1203] feat: allow verification for all passwordless accounts (#3038) * [PM-1033] Org invite user creation flow 1 (#3028) * [PM-1033] feat: remove user verification from password enrollment * [PM-1033] feat: auto accept invitation when enrolling into password reset * [PM-1033] fix: controller tests * [PM-1033] refactor: `UpdateUserResetPasswordEnrollmentCommand` * [PM-1033] refactor(wip): make `AcceptUserCommand` * Revert "[PM-1033] refactor(wip): make `AcceptUserCommand`" This reverts commit dc1319e7fa70c4844bbc70e0b01089b682ac2843. * Revert "[PM-1033] refactor: `UpdateUserResetPasswordEnrollmentCommand`" This reverts commit 43df689c7f244af4f7ffec1f9768a72081a624c3. * [PM-1033] refactor: move invite accept to controller This avoids creating yet another method that depends on having `IUserService` passed in as a parameter * [PM-1033] fix: add missing changes * [PM-1381] Add Trusted Device Keys to Auth Response (#3066) * Return Keys for Trusted Device - Check whether the current logging in device is trusted - Return their keys on successful login * Formatting * Address PR Feedback * Add Remarks Comment * [PM-1338] `AuthRequest` Event Logs (#3046) * Update AuthRequestController - Only allow AdminApproval Requests to be created from authed endpoint - Add endpoint that has authentication to be able to create admin approval * Add PasswordlessAuthSettings - Add settings for customizing expiration times * Add new EventTypes * Add Logic for AdminApproval Type - Add logic for validating AdminApproval expiration - Add event logging for Approval/Disapproval of AdminApproval - Add logic for creating AdminApproval types * Add Test Helpers - Change BitAutoData to allow you to use string representations of common types. * Add/Update AuthRequestService Tests * Run Formatting * Switch to 7 Days * Add Test Covering ResponseDate Being Set * Address PR Feedback - Create helper for checking if date is expired - Move validation logic into smaller methods * Switch to User Event Type - Make RequestDeviceApproval user type - User types will log for each org user is in * [PM-2998] Move Approving Device Check (#3101) * Move Check for Approving Devices - Exclude currently logging in device - Remove old way of checking - Add tests asserting behavior * Update DeviceType list * Update Naming & Address PR Feedback * Fix Tests * Address PR Feedback * Formatting * Now Fully Update Naming? * Feature/auth/pm 2759/add can reset password to user decryption options (#3113) * PM-2759 - BaseRequestValidator.cs - CreateUserDecryptionOptionsAsync - Add new hasManageResetPasswordPermission for post SSO redirect logic required on client. * PM-2759 - Update IdentityServerSsoTests.cs to all pass based on the addition of HasManageResetPasswordPermission to TrustedDeviceUserDecryptionOption * IdentityServerSsoTests.cs - fix typo in test name: LoggingApproval --> LoginApproval * PM1259 - Add test case for verifying that TrustedDeviceOption.hasManageResetPasswordPermission is set properly based on user permission * dotnet format run * Feature/auth/pm 2759/add can reset password to user decryption options fix jit users (#3120) * PM-2759 - IdentityServer - CreateUserDecryptionOptionsAsync - hasManageResetPasswordPermission set logic was broken for JIT provisioned users as I assumed we would always have a list of at least 1 org during the SSO process. Added TODO for future test addition but getting this out there now as QA is blocked by being unable to create JIT provisioned users. * dotnet format * Tiny tweak * [PM-1339] Allow Rotating Device Keys (#3096) * Allow Rotation of Trusted Device Keys - Add endpoint for getting keys relating to rotation - Add endpoint for rotating your current device - In the same endpoint allow a list of other devices to rotate * Formatting * Use Extension Method * Add Tests from PR Co-authored-by: Jared Snider <jsnider@bitwarden.com> --------- Co-authored-by: Jared Snider <jsnider@bitwarden.com> * Check the user directly if they have the ResetPasswordKey (#3153) * PM-3327 - UpdateKeyAsync must exempt the currently calling device from the logout notification in order to prevent prematurely logging the user out before the client side key rotation process can complete. The calling device will log itself out once it is done. (#3170) * Allow OTP Requests When Users Are On TDE (#3184) * [PM-3356][PM-3292] Allow OTP For All (#3188) * Allow OTP For All - On a trusted device isn't a good check because a user might be using a trusted device locally but not trusted it long term - The logic wasn't working for KC users anyways * Remove Old Comment * [AC-1601] Added RequireSso policy as a dependency of TDE (#3209) * Added RequireSso policy as a dependency of TDE. * Added test for RequireSso for TDE. * Added save. * Fixed policy name. --------- Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com> Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> Co-authored-by: Vincent Salucci <vincesalucci21@gmail.com> Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Co-authored-by: Jared Snider <jsnider@bitwarden.com>
2023-08-17 22:03:06 +02:00
}
[Theory]
[BitAutoData]
public async Task PutResetPasswordEnrollment_ConfirmedUser_AcceptsInvite(Guid orgId, Guid userId, OrganizationUserResetPasswordEnrollmentRequestModel model,
User user, OrganizationUser orgUser, SutProvider<OrganizationUsersController> sutProvider)
{
orgUser.Status = Core.Enums.OrganizationUserStatusType.Confirmed;
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user);
sutProvider.GetDependency<IOrganizationUserRepository>().GetByOrganizationAsync(default, default).ReturnsForAnyArgs(orgUser);
await sutProvider.Sut.PutResetPasswordEnrollment(orgId, userId, model);
Auth/PM-3275 - Changes to support TDE User without MP being able to Set a Password + misc refactoring (#3242) * PM-3275 - Add new GetMasterPasswordPolicy endpoint which will allow authenticated clients to get an enabled MP org policy if it exists for the purposes of enforcing those policy requirements when setting a password. * PM-3275 - AccountsController.cs - PostSetPasswordAsync - (1) Convert UserService.setPasswordAsync into new SetInitialMasterPasswordCommand (2) Refactor SetInitialMasterPasswordCommand to only accept post SSO users who are in the invited state (3) Add TODOs for more cleanup work and more commands * PM-3275 - Update AccountsControllerTests.cs to add new SetInitialMasterPasswordCommand * PM-3275 - UserService.cs - Remove non implemented ChangePasswordAsync method * PM-3275 - The new SetInitialMasterPasswordCommand leveraged the OrganizationService.cs AcceptUserAsync method so while I was in here I converted the AcceptUserAsync methods into a new AcceptOrgUserCommand.cs and turned the private method which accepted an existing org user public for use in the SetInitialMasterPasswordCommand * PM-3275 - Dotnet format * PM-3275 - Test SetInitialMasterPasswordCommand * Dotnet format * PM-3275 - In process AcceptOrgUserCommandTests.cs * PM-3275 - Migrate changes from AC-244 / #3199 over into new AcceptOrgUserCommand * PM-3275 - AcceptOrgUserCommand.cs - create data protector specifically for this command * PM-3275 - Add TODO for renaming / removing overloading of methods to improve readability / clarity * PM-3275 - AcceptOrgUserCommand.cs - refactor AcceptOrgUserAsync by OrgId to retrieve orgUser with _organizationUserRepository.GetByOrganizationAsync which gets a single user instead of a collection * PM-3275 - AcceptOrgUserCommand.cs - update name in TODO for evaluation later * PM-3275 / PM-1196 - (1) Slightly refactor SsoEmail2faSessionTokenable to provide public static GetTokenLifeTime() method for testing (2) Add missed tests to SsoEmail2faSessionTokenable in preparation for building tests for new OrgUserInviteTokenable.cs * PM-3275 / PM-1196 - Removing SsoEmail2faSessionTokenable.cs changes + tests as I've handled that separately in a new PR (#3270) for newly created task PM-3925 * PM-3275 - ExpiringTokenable.cs - add clarifying comments to help distinguish between the Valid property and the TokenIsValid method. * PM-3275 - Create OrgUserInviteTokenable.cs and add tests in OrgUserInviteTokenableTests.cs * PM-3275 - OrganizationService.cs - Refactor Org User Invite methods to use new OrgUserInviteTokenable instead of manual creation of a token * PM-3275 - OrgUserInviteTokenable.cs - clarify backwards compat note * PM-3275 - AcceptOrgUserCommand.cs - Add TODOs + minor name refactor * PM-3275 - AcceptOrgUserCommand.cs - replace method overloading with more easily readable names. * PM-3275 - AcceptOrgUserCommand.cs - Update ValidateOrgUserInviteToken to add new token validation while maintaining backwards compatibility for 1 release. * dotnet format * PM-3275 - AcceptOrgUserCommand.cs - Move private method below where it is used * PM-3275 - ServiceCollectionExtensions.cs - Must register IDataProtectorTokenFactory<OrgUserInviteTokenable> for new tokenable * PM-3275 - OrgUserInviteTokenable needed access to global settings to set its token lifetime to the _globalSettings.OrganizationInviteExpirationHours value. Creating a factory seemed the most straightforward way to encapsulate the desired creation logic. Unsure if in the correct location in ServiceCollectionExtensions.cs but will figure that out later. * PM-3275 - In process work of creating AcceptOrgUserCommandTests.cs * PM-3275 - Remove no longer relevant AcceptOrgUser tests from OrganizationServiceTests.cs * PM-3275 - Register OrgUserInviteTokenableFactory alongside tokenizer * PM-3275 - AcceptOrgUserCommandTests.cs - AcceptOrgUserAsync basic test suite completed. * PM-3275 - AcceptOrgUserCommandTests.cs - tweak test names * PM-3275 - AcceptOrgUserCommandTests.cs - (1) Remove old tests from OrganizationServiceTests as no longer needed to reference (2) Add summary for SetupCommonAcceptOrgUserMocks (3) Get AcceptOrgUserByToken_OldToken_AcceptsUserAndVerifiesEmail passing * PM-3275 - Create interface for OrgUserInviteTokenableFactory b/c that's the right thing to do + enables test substitution * PM-3275 - AcceptOrgUserCommandTests.cs - (1) Start work on AcceptOrgUserByToken_NewToken_AcceptsUserAndVerifiesEmail (2) Create and use SetupCommonAcceptOrgUserByTokenMocks() (3) Create generic FakeDataProtectorTokenFactory for tokenable testing * PM-3275 - (1) Get AcceptOrgUserByToken_NewToken_AcceptsUserAndVerifiesEmail test passing (2) Move FakeDataProtectorTokenFactory to own file * PM-3275 - AcceptOrgUserCommandTests.cs - Finish up tests for AcceptOrgUserByTokenAsync * PM-3275 - Add pseudo section comments * PM-3275 - Clean up unused params on AcceptOrgUserByToken_EmailMismatch_ThrowsBadRequest test * PM-3275 - (1) Tests written for AcceptOrgUserByOrgSsoIdAsync (2) Refactor happy path assertions into helper function AssertValidAcceptedOrgUser to reduce code duplication * PM-3275 - Finish up testing AcceptOrgUserCommandTests.cs by adding tests for AcceptOrgUserByOrgIdAsync * PM-3275 - Tweaking test naming to ensure consistency. * PM-3275 - Bugfix - OrgUserInviteTokenableFactory implementation required when declaring singleton service in ServiceCollectionExtensions.cs * PM-3275 - Resolve failing OrganizationServiceTests.cs * dotnet format * PM-3275 - PoliciesController.cs - GetMasterPasswordPolicy bugfix - for orgs without a MP policy, policy comes back as null and we should return notFound in that case. * PM-3275 - Add PoliciesControllerTests.cs specifically for new GetMasterPasswordPolicy(...) endpoint. * PM-3275 - dotnet format PoliciesControllerTests.cs * PM-3275 - PoliciesController.cs - (1) Add tech debt task number (2) Properly flag endpoint as deprecated * PM-3275 - Add new hasManageResetPasswordPermission property to ProfileResponseModel.cs primarily for sync so that we can condition client side if TDE user obtains elevated permissions * PM-3275 - Fix AccountsControllerTests.cs * PM-3275 - OrgUserInviteTokenable.cs - clarify TODO * PM-3275 - AcceptOrgUserCommand.cs - Refactor token validation to use short circuiting to only run old token validation if new token validation fails. * PM-3275 - OrgUserInviteTokenable.cs - (1) Add new static methods to centralize validation logic to avoid repetition (2) Add new token validation method so we can avoid having to pass in a full org user (and hitting the db to do so) * PM-3275 - Realized that the old token validation was used in the PoliciesController.cs (existing user clicks invite link in email and goes to log in) and UserService.cs (user clicks invite link in email and registers for a new acct). Added tech debt item for cleaning up backwards compatibility in future. * dotnet format * PM-3275 - (1) AccountsController.cs - Update PostSetPasswordAsync SetPasswordRequestModel to allow null keys for the case where we have a TDE user who obtains elevated permissions - they already have a user public and user encrypted private key saved in the db. (2) AccountsControllerTests.cs - test PostSetPasswordAsync scenarios to ensure changes will work as expected. * PM-3275 - PR review feedback - (1) set CurrentContext to private (2) Refactor GetProfile to use variables to improve clarity and simplify debugging. * PM-3275 - SyncController.cs - PR Review Feedback - Set current context as private instead of protected. * PM-3275 - CurrentContextExtensions.cs - PR Feedback - move parenthesis up from own line. * PM-3275 - SetInitialMasterPasswordCommandTests.cs - Replace unnecessary variable * PM-3275 - SetInitialMasterPasswordCommandTests.cs - PR Feedback - Add expected outcome statement to test name * PM-3275 - Set Initial Password command and tests - PR Feedback changes - (1) Rename orgIdentifier --> OrgSsoIdentifier for clarity (2) Update SetInitialMasterPasswordAsync to not allow null orgSsoId with explicit message saying this vs letting null org trigger invalid organization (3) Add test to cover this new scenario. * PM-3275 - SetInitialMasterPasswordCommand.cs - Move summary from implementation to interface to better respect standards and the fact that the interface is the more seen piece of code. * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, rename AcceptOrgUserByTokenAsync -> AcceptOrgUserByEmailTokenAsync + replace generic name token with emailToken * PM-3275 - OrganizationService.cs - Per PR feedback, remove dupe line * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, remove new lines in error messages for consistency. * PM-3275 - SetInitialMasterPasswordCommand.cs - Per PR feedback, adjust formatting of constructor for improved readability. * PM-3275 - CurrentContextExtensions.cs - Refactor AnyOrgUserHasManageResetPasswordPermission per PR feedback to remove unnecessary var. * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, remove completed TODO * PM-3275 - PoliciesController.cs - Per PR feedback, update GetByInvitedUser param to be guid instead of string. * PM-3275 - OrgUserInviteTokenable.cs - per PR feedback, add tech debt item info. * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, use const purpose from tokenable instead of magic string. * PM-3275 - Restore non duplicate line to fix tests * PM-3275 - Per PR feedback, revert all sync controller changes as the ProfileResponseModel.organizations array has org objects which have permissions which have the ManageResetPassword permission. So, I have the information that I need clientside already to determine if the user has the ManageResetPassword in any org. * PM-3275 - PoliciesControllerTests.cs - Update imports as the PoliciesController was moved under the admin console team's domain. * PM-3275 - Resolve issues from merge conflict resolutions to get solution building. * PM-3275 / PM-4633 - PoliciesController.cs - use orgUserId to look up user instead of orgId. Oops. * Fix user service tests * Resolve merge conflict
2023-11-02 16:02:25 +01:00
await sutProvider.GetDependency<IAcceptOrgUserCommand>().Received(0).AcceptOrgUserByOrgIdAsync(orgId, user, sutProvider.GetDependency<IUserService>());
Trusted Device Encryption feature (#3151) * [PM-1203] feat: allow verification for all passwordless accounts (#3038) * [PM-1033] Org invite user creation flow 1 (#3028) * [PM-1033] feat: remove user verification from password enrollment * [PM-1033] feat: auto accept invitation when enrolling into password reset * [PM-1033] fix: controller tests * [PM-1033] refactor: `UpdateUserResetPasswordEnrollmentCommand` * [PM-1033] refactor(wip): make `AcceptUserCommand` * Revert "[PM-1033] refactor(wip): make `AcceptUserCommand`" This reverts commit dc1319e7fa70c4844bbc70e0b01089b682ac2843. * Revert "[PM-1033] refactor: `UpdateUserResetPasswordEnrollmentCommand`" This reverts commit 43df689c7f244af4f7ffec1f9768a72081a624c3. * [PM-1033] refactor: move invite accept to controller This avoids creating yet another method that depends on having `IUserService` passed in as a parameter * [PM-1033] fix: add missing changes * [PM-1381] Add Trusted Device Keys to Auth Response (#3066) * Return Keys for Trusted Device - Check whether the current logging in device is trusted - Return their keys on successful login * Formatting * Address PR Feedback * Add Remarks Comment * [PM-1338] `AuthRequest` Event Logs (#3046) * Update AuthRequestController - Only allow AdminApproval Requests to be created from authed endpoint - Add endpoint that has authentication to be able to create admin approval * Add PasswordlessAuthSettings - Add settings for customizing expiration times * Add new EventTypes * Add Logic for AdminApproval Type - Add logic for validating AdminApproval expiration - Add event logging for Approval/Disapproval of AdminApproval - Add logic for creating AdminApproval types * Add Test Helpers - Change BitAutoData to allow you to use string representations of common types. * Add/Update AuthRequestService Tests * Run Formatting * Switch to 7 Days * Add Test Covering ResponseDate Being Set * Address PR Feedback - Create helper for checking if date is expired - Move validation logic into smaller methods * Switch to User Event Type - Make RequestDeviceApproval user type - User types will log for each org user is in * [PM-2998] Move Approving Device Check (#3101) * Move Check for Approving Devices - Exclude currently logging in device - Remove old way of checking - Add tests asserting behavior * Update DeviceType list * Update Naming & Address PR Feedback * Fix Tests * Address PR Feedback * Formatting * Now Fully Update Naming? * Feature/auth/pm 2759/add can reset password to user decryption options (#3113) * PM-2759 - BaseRequestValidator.cs - CreateUserDecryptionOptionsAsync - Add new hasManageResetPasswordPermission for post SSO redirect logic required on client. * PM-2759 - Update IdentityServerSsoTests.cs to all pass based on the addition of HasManageResetPasswordPermission to TrustedDeviceUserDecryptionOption * IdentityServerSsoTests.cs - fix typo in test name: LoggingApproval --> LoginApproval * PM1259 - Add test case for verifying that TrustedDeviceOption.hasManageResetPasswordPermission is set properly based on user permission * dotnet format run * Feature/auth/pm 2759/add can reset password to user decryption options fix jit users (#3120) * PM-2759 - IdentityServer - CreateUserDecryptionOptionsAsync - hasManageResetPasswordPermission set logic was broken for JIT provisioned users as I assumed we would always have a list of at least 1 org during the SSO process. Added TODO for future test addition but getting this out there now as QA is blocked by being unable to create JIT provisioned users. * dotnet format * Tiny tweak * [PM-1339] Allow Rotating Device Keys (#3096) * Allow Rotation of Trusted Device Keys - Add endpoint for getting keys relating to rotation - Add endpoint for rotating your current device - In the same endpoint allow a list of other devices to rotate * Formatting * Use Extension Method * Add Tests from PR Co-authored-by: Jared Snider <jsnider@bitwarden.com> --------- Co-authored-by: Jared Snider <jsnider@bitwarden.com> * Check the user directly if they have the ResetPasswordKey (#3153) * PM-3327 - UpdateKeyAsync must exempt the currently calling device from the logout notification in order to prevent prematurely logging the user out before the client side key rotation process can complete. The calling device will log itself out once it is done. (#3170) * Allow OTP Requests When Users Are On TDE (#3184) * [PM-3356][PM-3292] Allow OTP For All (#3188) * Allow OTP For All - On a trusted device isn't a good check because a user might be using a trusted device locally but not trusted it long term - The logic wasn't working for KC users anyways * Remove Old Comment * [AC-1601] Added RequireSso policy as a dependency of TDE (#3209) * Added RequireSso policy as a dependency of TDE. * Added test for RequireSso for TDE. * Added save. * Fixed policy name. --------- Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com> Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> Co-authored-by: Vincent Salucci <vincesalucci21@gmail.com> Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Co-authored-by: Jared Snider <jsnider@bitwarden.com>
2023-08-17 22:03:06 +02:00
}
[Theory]
[BitAutoData]
public async Task Accept_RequiresKnownUser(Guid orgId, Guid orgUserId, OrganizationUserAcceptRequestModel model,
SutProvider<OrganizationUsersController> sutProvider)
{
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs((User)null);
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => sutProvider.Sut.Accept(orgId, orgUserId, model));
}
[Theory]
[BitAutoData]
public async Task Accept_NoMasterPasswordReset(Guid orgId, Guid orgUserId,
OrganizationUserAcceptRequestModel model, User user, SutProvider<OrganizationUsersController> sutProvider)
{
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user);
await sutProvider.Sut.Accept(orgId, orgUserId, model);
Auth/PM-3275 - Changes to support TDE User without MP being able to Set a Password + misc refactoring (#3242) * PM-3275 - Add new GetMasterPasswordPolicy endpoint which will allow authenticated clients to get an enabled MP org policy if it exists for the purposes of enforcing those policy requirements when setting a password. * PM-3275 - AccountsController.cs - PostSetPasswordAsync - (1) Convert UserService.setPasswordAsync into new SetInitialMasterPasswordCommand (2) Refactor SetInitialMasterPasswordCommand to only accept post SSO users who are in the invited state (3) Add TODOs for more cleanup work and more commands * PM-3275 - Update AccountsControllerTests.cs to add new SetInitialMasterPasswordCommand * PM-3275 - UserService.cs - Remove non implemented ChangePasswordAsync method * PM-3275 - The new SetInitialMasterPasswordCommand leveraged the OrganizationService.cs AcceptUserAsync method so while I was in here I converted the AcceptUserAsync methods into a new AcceptOrgUserCommand.cs and turned the private method which accepted an existing org user public for use in the SetInitialMasterPasswordCommand * PM-3275 - Dotnet format * PM-3275 - Test SetInitialMasterPasswordCommand * Dotnet format * PM-3275 - In process AcceptOrgUserCommandTests.cs * PM-3275 - Migrate changes from AC-244 / #3199 over into new AcceptOrgUserCommand * PM-3275 - AcceptOrgUserCommand.cs - create data protector specifically for this command * PM-3275 - Add TODO for renaming / removing overloading of methods to improve readability / clarity * PM-3275 - AcceptOrgUserCommand.cs - refactor AcceptOrgUserAsync by OrgId to retrieve orgUser with _organizationUserRepository.GetByOrganizationAsync which gets a single user instead of a collection * PM-3275 - AcceptOrgUserCommand.cs - update name in TODO for evaluation later * PM-3275 / PM-1196 - (1) Slightly refactor SsoEmail2faSessionTokenable to provide public static GetTokenLifeTime() method for testing (2) Add missed tests to SsoEmail2faSessionTokenable in preparation for building tests for new OrgUserInviteTokenable.cs * PM-3275 / PM-1196 - Removing SsoEmail2faSessionTokenable.cs changes + tests as I've handled that separately in a new PR (#3270) for newly created task PM-3925 * PM-3275 - ExpiringTokenable.cs - add clarifying comments to help distinguish between the Valid property and the TokenIsValid method. * PM-3275 - Create OrgUserInviteTokenable.cs and add tests in OrgUserInviteTokenableTests.cs * PM-3275 - OrganizationService.cs - Refactor Org User Invite methods to use new OrgUserInviteTokenable instead of manual creation of a token * PM-3275 - OrgUserInviteTokenable.cs - clarify backwards compat note * PM-3275 - AcceptOrgUserCommand.cs - Add TODOs + minor name refactor * PM-3275 - AcceptOrgUserCommand.cs - replace method overloading with more easily readable names. * PM-3275 - AcceptOrgUserCommand.cs - Update ValidateOrgUserInviteToken to add new token validation while maintaining backwards compatibility for 1 release. * dotnet format * PM-3275 - AcceptOrgUserCommand.cs - Move private method below where it is used * PM-3275 - ServiceCollectionExtensions.cs - Must register IDataProtectorTokenFactory<OrgUserInviteTokenable> for new tokenable * PM-3275 - OrgUserInviteTokenable needed access to global settings to set its token lifetime to the _globalSettings.OrganizationInviteExpirationHours value. Creating a factory seemed the most straightforward way to encapsulate the desired creation logic. Unsure if in the correct location in ServiceCollectionExtensions.cs but will figure that out later. * PM-3275 - In process work of creating AcceptOrgUserCommandTests.cs * PM-3275 - Remove no longer relevant AcceptOrgUser tests from OrganizationServiceTests.cs * PM-3275 - Register OrgUserInviteTokenableFactory alongside tokenizer * PM-3275 - AcceptOrgUserCommandTests.cs - AcceptOrgUserAsync basic test suite completed. * PM-3275 - AcceptOrgUserCommandTests.cs - tweak test names * PM-3275 - AcceptOrgUserCommandTests.cs - (1) Remove old tests from OrganizationServiceTests as no longer needed to reference (2) Add summary for SetupCommonAcceptOrgUserMocks (3) Get AcceptOrgUserByToken_OldToken_AcceptsUserAndVerifiesEmail passing * PM-3275 - Create interface for OrgUserInviteTokenableFactory b/c that's the right thing to do + enables test substitution * PM-3275 - AcceptOrgUserCommandTests.cs - (1) Start work on AcceptOrgUserByToken_NewToken_AcceptsUserAndVerifiesEmail (2) Create and use SetupCommonAcceptOrgUserByTokenMocks() (3) Create generic FakeDataProtectorTokenFactory for tokenable testing * PM-3275 - (1) Get AcceptOrgUserByToken_NewToken_AcceptsUserAndVerifiesEmail test passing (2) Move FakeDataProtectorTokenFactory to own file * PM-3275 - AcceptOrgUserCommandTests.cs - Finish up tests for AcceptOrgUserByTokenAsync * PM-3275 - Add pseudo section comments * PM-3275 - Clean up unused params on AcceptOrgUserByToken_EmailMismatch_ThrowsBadRequest test * PM-3275 - (1) Tests written for AcceptOrgUserByOrgSsoIdAsync (2) Refactor happy path assertions into helper function AssertValidAcceptedOrgUser to reduce code duplication * PM-3275 - Finish up testing AcceptOrgUserCommandTests.cs by adding tests for AcceptOrgUserByOrgIdAsync * PM-3275 - Tweaking test naming to ensure consistency. * PM-3275 - Bugfix - OrgUserInviteTokenableFactory implementation required when declaring singleton service in ServiceCollectionExtensions.cs * PM-3275 - Resolve failing OrganizationServiceTests.cs * dotnet format * PM-3275 - PoliciesController.cs - GetMasterPasswordPolicy bugfix - for orgs without a MP policy, policy comes back as null and we should return notFound in that case. * PM-3275 - Add PoliciesControllerTests.cs specifically for new GetMasterPasswordPolicy(...) endpoint. * PM-3275 - dotnet format PoliciesControllerTests.cs * PM-3275 - PoliciesController.cs - (1) Add tech debt task number (2) Properly flag endpoint as deprecated * PM-3275 - Add new hasManageResetPasswordPermission property to ProfileResponseModel.cs primarily for sync so that we can condition client side if TDE user obtains elevated permissions * PM-3275 - Fix AccountsControllerTests.cs * PM-3275 - OrgUserInviteTokenable.cs - clarify TODO * PM-3275 - AcceptOrgUserCommand.cs - Refactor token validation to use short circuiting to only run old token validation if new token validation fails. * PM-3275 - OrgUserInviteTokenable.cs - (1) Add new static methods to centralize validation logic to avoid repetition (2) Add new token validation method so we can avoid having to pass in a full org user (and hitting the db to do so) * PM-3275 - Realized that the old token validation was used in the PoliciesController.cs (existing user clicks invite link in email and goes to log in) and UserService.cs (user clicks invite link in email and registers for a new acct). Added tech debt item for cleaning up backwards compatibility in future. * dotnet format * PM-3275 - (1) AccountsController.cs - Update PostSetPasswordAsync SetPasswordRequestModel to allow null keys for the case where we have a TDE user who obtains elevated permissions - they already have a user public and user encrypted private key saved in the db. (2) AccountsControllerTests.cs - test PostSetPasswordAsync scenarios to ensure changes will work as expected. * PM-3275 - PR review feedback - (1) set CurrentContext to private (2) Refactor GetProfile to use variables to improve clarity and simplify debugging. * PM-3275 - SyncController.cs - PR Review Feedback - Set current context as private instead of protected. * PM-3275 - CurrentContextExtensions.cs - PR Feedback - move parenthesis up from own line. * PM-3275 - SetInitialMasterPasswordCommandTests.cs - Replace unnecessary variable * PM-3275 - SetInitialMasterPasswordCommandTests.cs - PR Feedback - Add expected outcome statement to test name * PM-3275 - Set Initial Password command and tests - PR Feedback changes - (1) Rename orgIdentifier --> OrgSsoIdentifier for clarity (2) Update SetInitialMasterPasswordAsync to not allow null orgSsoId with explicit message saying this vs letting null org trigger invalid organization (3) Add test to cover this new scenario. * PM-3275 - SetInitialMasterPasswordCommand.cs - Move summary from implementation to interface to better respect standards and the fact that the interface is the more seen piece of code. * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, rename AcceptOrgUserByTokenAsync -> AcceptOrgUserByEmailTokenAsync + replace generic name token with emailToken * PM-3275 - OrganizationService.cs - Per PR feedback, remove dupe line * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, remove new lines in error messages for consistency. * PM-3275 - SetInitialMasterPasswordCommand.cs - Per PR feedback, adjust formatting of constructor for improved readability. * PM-3275 - CurrentContextExtensions.cs - Refactor AnyOrgUserHasManageResetPasswordPermission per PR feedback to remove unnecessary var. * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, remove completed TODO * PM-3275 - PoliciesController.cs - Per PR feedback, update GetByInvitedUser param to be guid instead of string. * PM-3275 - OrgUserInviteTokenable.cs - per PR feedback, add tech debt item info. * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, use const purpose from tokenable instead of magic string. * PM-3275 - Restore non duplicate line to fix tests * PM-3275 - Per PR feedback, revert all sync controller changes as the ProfileResponseModel.organizations array has org objects which have permissions which have the ManageResetPassword permission. So, I have the information that I need clientside already to determine if the user has the ManageResetPassword in any org. * PM-3275 - PoliciesControllerTests.cs - Update imports as the PoliciesController was moved under the admin console team's domain. * PM-3275 - Resolve issues from merge conflict resolutions to get solution building. * PM-3275 / PM-4633 - PoliciesController.cs - use orgUserId to look up user instead of orgId. Oops. * Fix user service tests * Resolve merge conflict
2023-11-02 16:02:25 +01:00
await sutProvider.GetDependency<IAcceptOrgUserCommand>().Received(1)
.AcceptOrgUserByEmailTokenAsync(orgUserId, user, model.Token, sutProvider.GetDependency<IUserService>());
await sutProvider.GetDependency<IOrganizationService>().DidNotReceiveWithAnyArgs()
.UpdateUserResetPasswordEnrollmentAsync(default, default, default, default);
}
[Theory]
[BitAutoData]
public async Task Accept_RequireMasterPasswordReset(Guid orgId, Guid orgUserId,
OrganizationUserAcceptRequestModel model, User user, SutProvider<OrganizationUsersController> sutProvider)
2022-08-29 22:06:55 +02:00
{
var policy = new Policy
{
Enabled = true,
Data = CoreHelpers.ClassToJsonData(new ResetPasswordDataModel { AutoEnrollEnabled = true, }),
};
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user);
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(orgId,
PolicyType.ResetPassword).Returns(policy);
await sutProvider.Sut.Accept(orgId, orgUserId, model);
Auth/PM-3275 - Changes to support TDE User without MP being able to Set a Password + misc refactoring (#3242) * PM-3275 - Add new GetMasterPasswordPolicy endpoint which will allow authenticated clients to get an enabled MP org policy if it exists for the purposes of enforcing those policy requirements when setting a password. * PM-3275 - AccountsController.cs - PostSetPasswordAsync - (1) Convert UserService.setPasswordAsync into new SetInitialMasterPasswordCommand (2) Refactor SetInitialMasterPasswordCommand to only accept post SSO users who are in the invited state (3) Add TODOs for more cleanup work and more commands * PM-3275 - Update AccountsControllerTests.cs to add new SetInitialMasterPasswordCommand * PM-3275 - UserService.cs - Remove non implemented ChangePasswordAsync method * PM-3275 - The new SetInitialMasterPasswordCommand leveraged the OrganizationService.cs AcceptUserAsync method so while I was in here I converted the AcceptUserAsync methods into a new AcceptOrgUserCommand.cs and turned the private method which accepted an existing org user public for use in the SetInitialMasterPasswordCommand * PM-3275 - Dotnet format * PM-3275 - Test SetInitialMasterPasswordCommand * Dotnet format * PM-3275 - In process AcceptOrgUserCommandTests.cs * PM-3275 - Migrate changes from AC-244 / #3199 over into new AcceptOrgUserCommand * PM-3275 - AcceptOrgUserCommand.cs - create data protector specifically for this command * PM-3275 - Add TODO for renaming / removing overloading of methods to improve readability / clarity * PM-3275 - AcceptOrgUserCommand.cs - refactor AcceptOrgUserAsync by OrgId to retrieve orgUser with _organizationUserRepository.GetByOrganizationAsync which gets a single user instead of a collection * PM-3275 - AcceptOrgUserCommand.cs - update name in TODO for evaluation later * PM-3275 / PM-1196 - (1) Slightly refactor SsoEmail2faSessionTokenable to provide public static GetTokenLifeTime() method for testing (2) Add missed tests to SsoEmail2faSessionTokenable in preparation for building tests for new OrgUserInviteTokenable.cs * PM-3275 / PM-1196 - Removing SsoEmail2faSessionTokenable.cs changes + tests as I've handled that separately in a new PR (#3270) for newly created task PM-3925 * PM-3275 - ExpiringTokenable.cs - add clarifying comments to help distinguish between the Valid property and the TokenIsValid method. * PM-3275 - Create OrgUserInviteTokenable.cs and add tests in OrgUserInviteTokenableTests.cs * PM-3275 - OrganizationService.cs - Refactor Org User Invite methods to use new OrgUserInviteTokenable instead of manual creation of a token * PM-3275 - OrgUserInviteTokenable.cs - clarify backwards compat note * PM-3275 - AcceptOrgUserCommand.cs - Add TODOs + minor name refactor * PM-3275 - AcceptOrgUserCommand.cs - replace method overloading with more easily readable names. * PM-3275 - AcceptOrgUserCommand.cs - Update ValidateOrgUserInviteToken to add new token validation while maintaining backwards compatibility for 1 release. * dotnet format * PM-3275 - AcceptOrgUserCommand.cs - Move private method below where it is used * PM-3275 - ServiceCollectionExtensions.cs - Must register IDataProtectorTokenFactory<OrgUserInviteTokenable> for new tokenable * PM-3275 - OrgUserInviteTokenable needed access to global settings to set its token lifetime to the _globalSettings.OrganizationInviteExpirationHours value. Creating a factory seemed the most straightforward way to encapsulate the desired creation logic. Unsure if in the correct location in ServiceCollectionExtensions.cs but will figure that out later. * PM-3275 - In process work of creating AcceptOrgUserCommandTests.cs * PM-3275 - Remove no longer relevant AcceptOrgUser tests from OrganizationServiceTests.cs * PM-3275 - Register OrgUserInviteTokenableFactory alongside tokenizer * PM-3275 - AcceptOrgUserCommandTests.cs - AcceptOrgUserAsync basic test suite completed. * PM-3275 - AcceptOrgUserCommandTests.cs - tweak test names * PM-3275 - AcceptOrgUserCommandTests.cs - (1) Remove old tests from OrganizationServiceTests as no longer needed to reference (2) Add summary for SetupCommonAcceptOrgUserMocks (3) Get AcceptOrgUserByToken_OldToken_AcceptsUserAndVerifiesEmail passing * PM-3275 - Create interface for OrgUserInviteTokenableFactory b/c that's the right thing to do + enables test substitution * PM-3275 - AcceptOrgUserCommandTests.cs - (1) Start work on AcceptOrgUserByToken_NewToken_AcceptsUserAndVerifiesEmail (2) Create and use SetupCommonAcceptOrgUserByTokenMocks() (3) Create generic FakeDataProtectorTokenFactory for tokenable testing * PM-3275 - (1) Get AcceptOrgUserByToken_NewToken_AcceptsUserAndVerifiesEmail test passing (2) Move FakeDataProtectorTokenFactory to own file * PM-3275 - AcceptOrgUserCommandTests.cs - Finish up tests for AcceptOrgUserByTokenAsync * PM-3275 - Add pseudo section comments * PM-3275 - Clean up unused params on AcceptOrgUserByToken_EmailMismatch_ThrowsBadRequest test * PM-3275 - (1) Tests written for AcceptOrgUserByOrgSsoIdAsync (2) Refactor happy path assertions into helper function AssertValidAcceptedOrgUser to reduce code duplication * PM-3275 - Finish up testing AcceptOrgUserCommandTests.cs by adding tests for AcceptOrgUserByOrgIdAsync * PM-3275 - Tweaking test naming to ensure consistency. * PM-3275 - Bugfix - OrgUserInviteTokenableFactory implementation required when declaring singleton service in ServiceCollectionExtensions.cs * PM-3275 - Resolve failing OrganizationServiceTests.cs * dotnet format * PM-3275 - PoliciesController.cs - GetMasterPasswordPolicy bugfix - for orgs without a MP policy, policy comes back as null and we should return notFound in that case. * PM-3275 - Add PoliciesControllerTests.cs specifically for new GetMasterPasswordPolicy(...) endpoint. * PM-3275 - dotnet format PoliciesControllerTests.cs * PM-3275 - PoliciesController.cs - (1) Add tech debt task number (2) Properly flag endpoint as deprecated * PM-3275 - Add new hasManageResetPasswordPermission property to ProfileResponseModel.cs primarily for sync so that we can condition client side if TDE user obtains elevated permissions * PM-3275 - Fix AccountsControllerTests.cs * PM-3275 - OrgUserInviteTokenable.cs - clarify TODO * PM-3275 - AcceptOrgUserCommand.cs - Refactor token validation to use short circuiting to only run old token validation if new token validation fails. * PM-3275 - OrgUserInviteTokenable.cs - (1) Add new static methods to centralize validation logic to avoid repetition (2) Add new token validation method so we can avoid having to pass in a full org user (and hitting the db to do so) * PM-3275 - Realized that the old token validation was used in the PoliciesController.cs (existing user clicks invite link in email and goes to log in) and UserService.cs (user clicks invite link in email and registers for a new acct). Added tech debt item for cleaning up backwards compatibility in future. * dotnet format * PM-3275 - (1) AccountsController.cs - Update PostSetPasswordAsync SetPasswordRequestModel to allow null keys for the case where we have a TDE user who obtains elevated permissions - they already have a user public and user encrypted private key saved in the db. (2) AccountsControllerTests.cs - test PostSetPasswordAsync scenarios to ensure changes will work as expected. * PM-3275 - PR review feedback - (1) set CurrentContext to private (2) Refactor GetProfile to use variables to improve clarity and simplify debugging. * PM-3275 - SyncController.cs - PR Review Feedback - Set current context as private instead of protected. * PM-3275 - CurrentContextExtensions.cs - PR Feedback - move parenthesis up from own line. * PM-3275 - SetInitialMasterPasswordCommandTests.cs - Replace unnecessary variable * PM-3275 - SetInitialMasterPasswordCommandTests.cs - PR Feedback - Add expected outcome statement to test name * PM-3275 - Set Initial Password command and tests - PR Feedback changes - (1) Rename orgIdentifier --> OrgSsoIdentifier for clarity (2) Update SetInitialMasterPasswordAsync to not allow null orgSsoId with explicit message saying this vs letting null org trigger invalid organization (3) Add test to cover this new scenario. * PM-3275 - SetInitialMasterPasswordCommand.cs - Move summary from implementation to interface to better respect standards and the fact that the interface is the more seen piece of code. * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, rename AcceptOrgUserByTokenAsync -> AcceptOrgUserByEmailTokenAsync + replace generic name token with emailToken * PM-3275 - OrganizationService.cs - Per PR feedback, remove dupe line * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, remove new lines in error messages for consistency. * PM-3275 - SetInitialMasterPasswordCommand.cs - Per PR feedback, adjust formatting of constructor for improved readability. * PM-3275 - CurrentContextExtensions.cs - Refactor AnyOrgUserHasManageResetPasswordPermission per PR feedback to remove unnecessary var. * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, remove completed TODO * PM-3275 - PoliciesController.cs - Per PR feedback, update GetByInvitedUser param to be guid instead of string. * PM-3275 - OrgUserInviteTokenable.cs - per PR feedback, add tech debt item info. * PM-3275 - AcceptOrgUserCommand.cs - Per PR feedback, use const purpose from tokenable instead of magic string. * PM-3275 - Restore non duplicate line to fix tests * PM-3275 - Per PR feedback, revert all sync controller changes as the ProfileResponseModel.organizations array has org objects which have permissions which have the ManageResetPassword permission. So, I have the information that I need clientside already to determine if the user has the ManageResetPassword in any org. * PM-3275 - PoliciesControllerTests.cs - Update imports as the PoliciesController was moved under the admin console team's domain. * PM-3275 - Resolve issues from merge conflict resolutions to get solution building. * PM-3275 / PM-4633 - PoliciesController.cs - use orgUserId to look up user instead of orgId. Oops. * Fix user service tests * Resolve merge conflict
2023-11-02 16:02:25 +01:00
await sutProvider.GetDependency<IAcceptOrgUserCommand>().Received(1)
.AcceptOrgUserByEmailTokenAsync(orgUserId, user, model.Token, sutProvider.GetDependency<IUserService>());
await sutProvider.GetDependency<IOrganizationService>().Received(1)
.UpdateUserResetPasswordEnrollmentAsync(orgId, user.Id, model.ResetPasswordKey, user.Id);
}
[Theory]
[BitAutoData]
public async Task Invite_Success(OrganizationAbility organizationAbility, OrganizationUserInviteRequestModel model,
Guid userId, SutProvider<OrganizationUsersController> sutProvider)
{
organizationAbility.FlexibleCollections = true;
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organizationAbility.Id).Returns(true);
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organizationAbility.Id)
.Returns(organizationAbility);
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
Arg.Any<IEnumerable<Collection>>(),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(reqs => reqs.Contains(BulkCollectionOperations.ModifyUserAccess)))
.Returns(AuthorizationResult.Success());
sutProvider.GetDependency<IUserService>().GetProperUserId(Arg.Any<ClaimsPrincipal>()).Returns(userId);
await sutProvider.Sut.Invite(organizationAbility.Id, model);
await sutProvider.GetDependency<IOrganizationService>().Received(1).InviteUsersAsync(organizationAbility.Id,
userId, Arg.Is<IEnumerable<(OrganizationUserInvite, string)>>(invites =>
invites.Count() == 1 &&
invites.First().Item1.Emails.SequenceEqual(model.Emails) &&
invites.First().Item1.Type == model.Type &&
invites.First().Item1.AccessSecretsManager == model.AccessSecretsManager));
}
[Theory]
[BitAutoData]
public async Task Invite_NotAuthorizedToGiveAccessToCollections_Throws(OrganizationAbility organizationAbility, OrganizationUserInviteRequestModel model,
Guid userId, SutProvider<OrganizationUsersController> sutProvider)
{
organizationAbility.FlexibleCollections = true;
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1).Returns(true);
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organizationAbility.Id).Returns(true);
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organizationAbility.Id)
.Returns(organizationAbility);
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
Arg.Any<IEnumerable<Collection>>(),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(reqs => reqs.Contains(BulkCollectionOperations.ModifyUserAccess)))
.Returns(AuthorizationResult.Failed());
sutProvider.GetDependency<IUserService>().GetProperUserId(Arg.Any<ClaimsPrincipal>()).Returns(userId);
var exception = await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.Invite(organizationAbility.Id, model));
Assert.Contains("You are not authorized", exception.Message);
}
[Theory]
[BitAutoData]
public async Task Put_Success(OrganizationUserUpdateRequestModel model,
OrganizationUser organizationUser, OrganizationAbility organizationAbility,
SutProvider<OrganizationUsersController> sutProvider, Guid savingUserId)
{
organizationAbility.FlexibleCollections = false;
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1).Returns(false);
Put_Setup(sutProvider, organizationAbility, organizationUser, savingUserId, model, false);
// Save these for later - organizationUser object will be mutated
var orgUserId = organizationUser.Id;
var orgUserEmail = organizationUser.Email;
await sutProvider.Sut.Put(organizationAbility.Id, organizationUser.Id, model);
await sutProvider.GetDependency<IUpdateOrganizationUserCommand>().Received(1).UpdateUserAsync(Arg.Is<OrganizationUser>(ou =>
ou.Type == model.Type &&
ou.Permissions == CoreHelpers.ClassToJsonData(model.Permissions) &&
ou.AccessSecretsManager == model.AccessSecretsManager &&
ou.Id == orgUserId &&
ou.Email == orgUserEmail),
savingUserId,
Arg.Is<List<CollectionAccessSelection>>(cas =>
cas.All(c => model.Collections.Any(m => m.Id == c.Id))),
model.Groups);
}
[Theory]
[BitAutoData]
public async Task Put_UpdateSelf_WithoutAllowAdminAccessToAllCollectionItems_CannotAddSelfToCollections(OrganizationUserUpdateRequestModel model,
OrganizationUser organizationUser, OrganizationAbility organizationAbility,
SutProvider<OrganizationUsersController> sutProvider, Guid savingUserId)
{
// Updating self
organizationUser.UserId = savingUserId;
organizationAbility.AllowAdminAccessToAllCollectionItems = false;
organizationAbility.FlexibleCollections = true;
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1).Returns(true);
Put_Setup(sutProvider, organizationAbility, organizationUser, savingUserId, model, false);
// User is not currently assigned to any collections, which means they're adding themselves
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetByIdWithCollectionsAsync(organizationUser.Id)
.Returns(new Tuple<OrganizationUser, ICollection<CollectionAccessSelection>>(organizationUser,
new List<CollectionAccessSelection>()));
sutProvider.GetDependency<ICollectionRepository>()
.GetManyByManyIdsAsync(Arg.Any<IEnumerable<Guid>>())
.Returns(new List<Collection>());
var orgUserId = organizationUser.Id;
var orgUserEmail = organizationUser.Email;
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.Put(organizationAbility.Id, organizationUser.Id, model));
Assert.Contains("You cannot add yourself to a collection.", exception.Message);
}
[Theory]
[BitAutoData]
public async Task Put_UpdateSelf_WithoutAllowAdminAccessToAllCollectionItems_DoesNotUpdateGroups(OrganizationUserUpdateRequestModel model,
OrganizationUser organizationUser, OrganizationAbility organizationAbility,
SutProvider<OrganizationUsersController> sutProvider, Guid savingUserId)
{
// Updating self
organizationUser.UserId = savingUserId;
organizationAbility.AllowAdminAccessToAllCollectionItems = false;
organizationAbility.FlexibleCollections = true;
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1).Returns(true);
Put_Setup(sutProvider, organizationAbility, organizationUser, savingUserId, model, true);
var orgUserId = organizationUser.Id;
var orgUserEmail = organizationUser.Email;
await sutProvider.Sut.Put(organizationAbility.Id, organizationUser.Id, model);
await sutProvider.GetDependency<IUpdateOrganizationUserCommand>().Received(1).UpdateUserAsync(Arg.Is<OrganizationUser>(ou =>
ou.Type == model.Type &&
ou.Permissions == CoreHelpers.ClassToJsonData(model.Permissions) &&
ou.AccessSecretsManager == model.AccessSecretsManager &&
ou.Id == orgUserId &&
ou.Email == orgUserEmail),
savingUserId,
Arg.Is<List<CollectionAccessSelection>>(cas =>
cas.All(c => model.Collections.Any(m => m.Id == c.Id))),
null);
}
[Theory]
[BitAutoData]
public async Task Put_UpdateSelf_WithAllowAdminAccessToAllCollectionItems_DoesUpdateGroups(OrganizationUserUpdateRequestModel model,
OrganizationUser organizationUser, OrganizationAbility organizationAbility,
SutProvider<OrganizationUsersController> sutProvider, Guid savingUserId)
{
// Updating self
organizationUser.UserId = savingUserId;
organizationAbility.FlexibleCollections = true;
organizationAbility.AllowAdminAccessToAllCollectionItems = true;
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1).Returns(true);
Put_Setup(sutProvider, organizationAbility, organizationUser, savingUserId, model, true);
var orgUserId = organizationUser.Id;
var orgUserEmail = organizationUser.Email;
await sutProvider.Sut.Put(organizationAbility.Id, organizationUser.Id, model);
await sutProvider.GetDependency<IUpdateOrganizationUserCommand>().Received(1).UpdateUserAsync(Arg.Is<OrganizationUser>(ou =>
ou.Type == model.Type &&
ou.Permissions == CoreHelpers.ClassToJsonData(model.Permissions) &&
ou.AccessSecretsManager == model.AccessSecretsManager &&
ou.Id == orgUserId &&
ou.Email == orgUserEmail),
savingUserId,
Arg.Is<List<CollectionAccessSelection>>(cas =>
cas.All(c => model.Collections.Any(m => m.Id == c.Id))),
model.Groups);
}
[Theory]
[BitAutoData]
public async Task Put_UpdateCollections_OnlyUpdatesCollectionsTheSavingUserCanUpdate(OrganizationUserUpdateRequestModel model,
OrganizationUser organizationUser, OrganizationAbility organizationAbility,
SutProvider<OrganizationUsersController> sutProvider, Guid savingUserId)
{
organizationAbility.FlexibleCollections = true;
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1).Returns(true);
Put_Setup(sutProvider, organizationAbility, organizationUser, savingUserId, model, false);
var editedCollectionId = CoreHelpers.GenerateComb();
var readonlyCollectionId1 = CoreHelpers.GenerateComb();
var readonlyCollectionId2 = CoreHelpers.GenerateComb();
var currentCollectionAccess = new List<CollectionAccessSelection>
{
new()
{
Id = editedCollectionId,
HidePasswords = true,
Manage = false,
ReadOnly = true
},
new()
{
Id = readonlyCollectionId1,
HidePasswords = false,
Manage = true,
ReadOnly = false
},
new()
{
Id = readonlyCollectionId2,
HidePasswords = false,
Manage = false,
ReadOnly = false
},
};
// User is upgrading editedCollectionId to manage
model.Collections = new List<SelectionReadOnlyRequestModel>
{
new() { Id = editedCollectionId, HidePasswords = false, Manage = true, ReadOnly = false }
};
// Save these for later - organizationUser object will be mutated
var orgUserId = organizationUser.Id;
var orgUserEmail = organizationUser.Email;
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetByIdWithCollectionsAsync(organizationUser.Id)
.Returns(new Tuple<OrganizationUser, ICollection<CollectionAccessSelection>>(organizationUser,
currentCollectionAccess));
var currentCollections = currentCollectionAccess
.Select(cas => new Collection { Id = cas.Id }).ToList();
sutProvider.GetDependency<ICollectionRepository>()
.GetManyByManyIdsAsync(Arg.Any<IEnumerable<Guid>>())
.Returns(currentCollections);
// Authorize the editedCollection
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), Arg.Is<Collection>(c => c.Id == editedCollectionId),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(reqs => reqs.Contains(BulkCollectionOperations.ModifyUserAccess)))
.Returns(AuthorizationResult.Success());
// Do not authorize the readonly collections
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), Arg.Is<Collection>(c => c.Id == readonlyCollectionId1 || c.Id == readonlyCollectionId2),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(reqs => reqs.Contains(BulkCollectionOperations.ModifyUserAccess)))
.Returns(AuthorizationResult.Failed());
await sutProvider.Sut.Put(organizationAbility.Id, organizationUser.Id, model);
// Expect all collection access (modified and unmodified) to be saved
await sutProvider.GetDependency<IUpdateOrganizationUserCommand>().Received(1).UpdateUserAsync(Arg.Is<OrganizationUser>(ou =>
ou.Type == model.Type &&
ou.Permissions == CoreHelpers.ClassToJsonData(model.Permissions) &&
ou.AccessSecretsManager == model.AccessSecretsManager &&
ou.Id == orgUserId &&
ou.Email == orgUserEmail),
savingUserId,
Arg.Is<List<CollectionAccessSelection>>(cas =>
cas.Select(c => c.Id).SequenceEqual(currentCollectionAccess.Select(c => c.Id)) &&
cas.First(c => c.Id == editedCollectionId).Manage == true &&
cas.First(c => c.Id == editedCollectionId).ReadOnly == false &&
cas.First(c => c.Id == editedCollectionId).HidePasswords == false),
model.Groups);
}
[Theory]
[BitAutoData]
public async Task Put_UpdateCollections_ThrowsIfSavingUserCannotUpdateCollections(OrganizationUserUpdateRequestModel model,
OrganizationUser organizationUser, OrganizationAbility organizationAbility,
SutProvider<OrganizationUsersController> sutProvider, Guid savingUserId)
{
organizationAbility.FlexibleCollections = true;
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1).Returns(true);
Put_Setup(sutProvider, organizationAbility, organizationUser, savingUserId, model, false);
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetByIdWithCollectionsAsync(organizationUser.Id)
.Returns(new Tuple<OrganizationUser, ICollection<CollectionAccessSelection>>(organizationUser,
model.Collections.Select(cas => cas.ToSelectionReadOnly()).ToList()));
var collections = model.Collections.Select(cas => new Collection { Id = cas.Id }).ToList();
sutProvider.GetDependency<ICollectionRepository>()
.GetManyByManyIdsAsync(Arg.Is<IEnumerable<Guid>>(guids => guids.SequenceEqual(collections.Select(c => c.Id))))
.Returns(collections);
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), Arg.Is<Collection>(c => collections.Contains(c)),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(reqs => reqs.Contains(BulkCollectionOperations.ModifyUserAccess)))
.Returns(AuthorizationResult.Failed());
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.Put(organizationAbility.Id, organizationUser.Id, model));
Assert.Contains("You must have Can Manage permission", exception.Message);
}
[Theory]
[BitAutoData]
public async Task Get_WithFlexibleCollections_ReturnsUsers(
ICollection<OrganizationUserUserDetails> organizationUsers, OrganizationAbility organizationAbility,
SutProvider<OrganizationUsersController> sutProvider)
{
Get_Setup(organizationAbility, organizationUsers, sutProvider);
var response = await sutProvider.Sut.Get(organizationAbility.Id);
Assert.True(response.Data.All(r => organizationUsers.Any(ou => ou.Id == r.Id)));
}
[Theory]
[BitAutoData]
public async Task Get_WithFlexibleCollections_HandlesNullPermissionsObject(
ICollection<OrganizationUserUserDetails> organizationUsers, OrganizationAbility organizationAbility,
SutProvider<OrganizationUsersController> sutProvider)
{
Get_Setup(organizationAbility, organizationUsers, sutProvider);
organizationUsers.First().Permissions = "null";
var response = await sutProvider.Sut.Get(organizationAbility.Id);
Assert.True(response.Data.All(r => organizationUsers.Any(ou => ou.Id == r.Id)));
}
[Theory]
[BitAutoData]
public async Task Get_WithFlexibleCollections_SetsDeprecatedCustomPermissionstoFalse(
ICollection<OrganizationUserUserDetails> organizationUsers, OrganizationAbility organizationAbility,
SutProvider<OrganizationUsersController> sutProvider)
{
Get_Setup(organizationAbility, organizationUsers, sutProvider);
var customUser = organizationUsers.First();
customUser.Type = OrganizationUserType.Custom;
customUser.Permissions = CoreHelpers.ClassToJsonData(new Permissions
{
AccessReports = true,
EditAssignedCollections = true,
DeleteAssignedCollections = true,
AccessEventLogs = true
});
var response = await sutProvider.Sut.Get(organizationAbility.Id);
var customUserResponse = response.Data.First(r => r.Id == organizationUsers.First().Id);
Assert.Equal(OrganizationUserType.Custom, customUserResponse.Type);
Assert.True(customUserResponse.Permissions.AccessReports);
Assert.True(customUserResponse.Permissions.AccessEventLogs);
Assert.False(customUserResponse.Permissions.EditAssignedCollections);
Assert.False(customUserResponse.Permissions.DeleteAssignedCollections);
}
[Theory]
[BitAutoData]
public async Task Get_WithFlexibleCollections_DowngradesCustomUsersWithDeprecatedPermissions(
ICollection<OrganizationUserUserDetails> organizationUsers, OrganizationAbility organizationAbility,
SutProvider<OrganizationUsersController> sutProvider)
{
Get_Setup(organizationAbility, organizationUsers, sutProvider);
var customUser = organizationUsers.First();
customUser.Type = OrganizationUserType.Custom;
customUser.Permissions = CoreHelpers.ClassToJsonData(new Permissions
{
EditAssignedCollections = true,
DeleteAssignedCollections = true,
});
var response = await sutProvider.Sut.Get(organizationAbility.Id);
var customUserResponse = response.Data.First(r => r.Id == organizationUsers.First().Id);
Assert.Equal(OrganizationUserType.User, customUserResponse.Type);
Assert.False(customUserResponse.Permissions.EditAssignedCollections);
Assert.False(customUserResponse.Permissions.DeleteAssignedCollections);
}
[Theory]
[BitAutoData]
public async Task GetAccountRecoveryDetails_ReturnsDetails(
Guid organizationId,
OrganizationUserBulkRequestModel bulkRequestModel,
ICollection<OrganizationUserResetPasswordDetails> resetPasswordDetails,
SutProvider<OrganizationUsersController> sutProvider)
{
sutProvider.GetDependency<ICurrentContext>().ManageResetPassword(organizationId).Returns(true);
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyAccountRecoveryDetailsByOrganizationUserAsync(organizationId, bulkRequestModel.Ids)
.Returns(resetPasswordDetails);
var response = await sutProvider.Sut.GetAccountRecoveryDetails(organizationId, bulkRequestModel);
Assert.Equal(resetPasswordDetails.Count, response.Data.Count());
Assert.True(response.Data.All(r =>
resetPasswordDetails.Any(ou =>
ou.OrganizationUserId == r.OrganizationUserId &&
ou.Kdf == r.Kdf &&
ou.KdfIterations == r.KdfIterations &&
ou.KdfMemory == r.KdfMemory &&
ou.KdfParallelism == r.KdfParallelism &&
ou.ResetPasswordKey == r.ResetPasswordKey &&
ou.EncryptedPrivateKey == r.EncryptedPrivateKey)));
}
[Theory]
[BitAutoData]
public async Task GetAccountRecoveryDetails_WithoutManageResetPasswordPermission_Throws(
Guid organizationId,
OrganizationUserBulkRequestModel bulkRequestModel,
SutProvider<OrganizationUsersController> sutProvider)
{
sutProvider.GetDependency<ICurrentContext>().ManageResetPassword(organizationId).Returns(false);
await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.GetAccountRecoveryDetails(organizationId, bulkRequestModel));
}
private void Put_Setup(SutProvider<OrganizationUsersController> sutProvider, OrganizationAbility organizationAbility,
OrganizationUser organizationUser, Guid savingUserId, OrganizationUserUpdateRequestModel model, bool authorizeAll)
{
var orgId = organizationAbility.Id = organizationUser.OrganizationId;
sutProvider.GetDependency<ICurrentContext>().ManageUsers(orgId).Returns(true);
sutProvider.GetDependency<IOrganizationUserRepository>().GetByIdAsync(organizationUser.Id).Returns(organizationUser);
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(orgId)
.Returns(organizationAbility);
sutProvider.GetDependency<IUserService>().GetProperUserId(Arg.Any<ClaimsPrincipal>()).Returns(savingUserId);
if (authorizeAll)
{
// Simple case: saving user can edit all collections, all collection access is replaced
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetByIdWithCollectionsAsync(organizationUser.Id)
.Returns(new Tuple<OrganizationUser, ICollection<CollectionAccessSelection>>(organizationUser,
model.Collections.Select(cas => cas.ToSelectionReadOnly()).ToList()));
var collections = model.Collections.Select(cas => new Collection { Id = cas.Id }).ToList();
sutProvider.GetDependency<ICollectionRepository>()
.GetManyByManyIdsAsync(Arg.Is<IEnumerable<Guid>>(guids => guids.SequenceEqual(collections.Select(c => c.Id))))
.Returns(collections);
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), Arg.Is<Collection>(c => collections.Contains(c)),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(r => r.Contains(BulkCollectionOperations.ModifyUserAccess)))
.Returns(AuthorizationResult.Success());
}
}
private void Get_Setup(OrganizationAbility organizationAbility,
ICollection<OrganizationUserUserDetails> organizationUsers,
SutProvider<OrganizationUsersController> sutProvider)
{
organizationAbility.FlexibleCollections = true;
foreach (var orgUser in organizationUsers)
{
orgUser.Permissions = null;
}
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilityAsync(organizationAbility.Id)
.Returns(organizationAbility);
sutProvider.GetDependency<IAuthorizationService>().AuthorizeAsync(
user: Arg.Any<ClaimsPrincipal>(),
resource: Arg.Any<Object>(),
requirements: Arg.Any<IEnumerable<IAuthorizationRequirement>>())
.Returns(AuthorizationResult.Success());
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyDetailsByOrganizationAsync(organizationAbility.Id, Arg.Any<bool>(), Arg.Any<bool>())
.Returns(organizationUsers);
}
}