2022-06-30 01:46:41 +02:00
|
|
|
|
using System.Text.Json;
|
2023-10-26 19:38:29 +02:00
|
|
|
|
using Bit.Core.AdminConsole.Entities.Provider;
|
2023-11-22 22:07:37 +01:00
|
|
|
|
using Bit.Core.AdminConsole.Enums;
|
2023-10-26 19:38:29 +02:00
|
|
|
|
using Bit.Core.AdminConsole.Enums.Provider;
|
2023-10-26 23:47:44 +02:00
|
|
|
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
2023-10-26 19:38:29 +02:00
|
|
|
|
using Bit.Core.AdminConsole.Repositories;
|
2024-05-17 21:28:51 +02:00
|
|
|
|
using Bit.Core.AdminConsole.Services;
|
2023-04-14 19:25:56 +02:00
|
|
|
|
using Bit.Core.Auth.Entities;
|
2023-05-10 21:52:08 +02:00
|
|
|
|
using Bit.Core.Auth.Enums;
|
2024-08-08 16:43:45 +02:00
|
|
|
|
using Bit.Core.Auth.Models;
|
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.Auth.Models.Business.Tokenables;
|
2023-04-14 19:25:56 +02:00
|
|
|
|
using Bit.Core.Auth.Models.Data;
|
|
|
|
|
using Bit.Core.Auth.Repositories;
|
2024-08-08 16:43:45 +02:00
|
|
|
|
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
2024-06-14 21:34:47 +02:00
|
|
|
|
using Bit.Core.Billing.Enums;
|
2021-07-01 14:31:05 +02:00
|
|
|
|
using Bit.Core.Context;
|
2022-01-11 10:40:51 +01:00
|
|
|
|
using Bit.Core.Entities;
|
2021-01-12 17:02:39 +01:00
|
|
|
|
using Bit.Core.Enums;
|
|
|
|
|
using Bit.Core.Exceptions;
|
|
|
|
|
using Bit.Core.Models.Business;
|
2019-02-19 22:27:05 +01:00
|
|
|
|
using Bit.Core.Models.Data;
|
2022-05-10 23:12:09 +02:00
|
|
|
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
2023-12-18 17:16:17 +01:00
|
|
|
|
using Bit.Core.Models.Mail;
|
2023-07-25 00:05:05 +02:00
|
|
|
|
using Bit.Core.Models.StaticStore;
|
2023-08-04 23:51:12 +02:00
|
|
|
|
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
|
2019-02-19 22:27:05 +01:00
|
|
|
|
using Bit.Core.Repositories;
|
|
|
|
|
using Bit.Core.Services;
|
2021-01-12 17:02:39 +01:00
|
|
|
|
using Bit.Core.Settings;
|
2023-11-22 22:07:37 +01:00
|
|
|
|
using Bit.Core.Test.AdminConsole.AutoFixture;
|
2021-01-12 17:02:39 +01:00
|
|
|
|
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
|
2021-05-25 19:23:47 +02:00
|
|
|
|
using Bit.Core.Test.AutoFixture.OrganizationUserFixtures;
|
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.Tokens;
|
2023-04-18 14:05:17 +02:00
|
|
|
|
using Bit.Core.Tools.Enums;
|
|
|
|
|
using Bit.Core.Tools.Models.Business;
|
|
|
|
|
using Bit.Core.Tools.Services;
|
2023-07-25 00:05:05 +02:00
|
|
|
|
using Bit.Core.Utilities;
|
Families for Enterprise (#1714)
* Create common test infrastructure project
* Add helpers to further type PlanTypes
* Enable testing of ASP.net MVC controllers
Controller properties have all kinds of validations in the background.
In general, we don't user properties on our Controllers, so the easiest
way to allow for Autofixture-based testing of our Controllers is to just
omit setting all properties on them.
* Workaround for broken MemberAutoDataAttribute
https://github.com/AutoFixture/AutoFixture/pull/1164 shows that only
the first test case is pulled for this attribute.
This is a workaround that populates the provided parameters, left to
right, using AutoFixture to populate any remaining.
* WIP: Organization sponsorship flow
* Add Attribute to use the Bit Autodata dependency chain
BitAutoDataAttribute is used to mark a Theory as autopopulating
parameters.
Extract common attribute methods to to a helper class. Cannot
inherit a common base, since both require inheriting from different
Xunit base classes to work.
* WIP: scaffolding for families for enterprise sponsorship flow
* Fix broken tests
* Create sponsorship offer (#1688)
* Initial db work (#1687)
* Add organization sponsorship databases to all providers
* Generalize create and update for database, specialize in code
* Add PlanSponsorshipType to db model
* Write valid json for test entries
* Initial scaffolding of emails (#1686)
* Initial scaffolding of emails
* Work on adding models for FamilyForEnterprise emails
* Switch verbage
* Put preliminary copy in emails
* Skip test
* Families for enterprise/stripe integrations (#1699)
* Add PlanSponsorshipType to static store
* Add sponsorship type to token and creates sponsorship
* PascalCase properties
* Require sponsorship for remove
* Create subscription sponsorship helper class
* Handle Sponsored subscription changes
* Add sponsorship id to subscription metadata
* Make sponsoring references nullable
This state indicates that a sponsorship has lapsed, but was not able to
be reverted for billing reasons
* WIP: Validate and remove subscriptions
* Update sponsorships on organization and org user delete
* Add friendly name to organization sponsorship
* Add sponsorship available boolean to orgDetails
* Add sponsorship service to DI
* Use userId to find org users
* Send f4e offer email
* Simplify names of f4e mail messages
* Fix Stripe org default tax rates
* Universal sponsorship redeem api
* Populate user in current context
* Add product type to organization details
* Use upgrade path to change sponsorship
Sponsorships need to be annual to match the GB add-on charge rate
* Use organization and auth to find organization sponsorship
* Add resend sponsorship offer api endpoint
* Fix double email send
* Fix sponsorship upgrade options
* Add is sponsored item to subscription response
* Add sponsorship validation to upcoming invoice webhook
* Add sponsorship validation to upcoming invoice webhook
* Fix organization delete sponsorship hooks
* Test org sponsorship service
* Fix sproc
* Create common test infrastructure project
* Add helpers to further type PlanTypes
* Enable testing of ASP.net MVC controllers
Controller properties have all kinds of validations in the background.
In general, we don't user properties on our Controllers, so the easiest
way to allow for Autofixture-based testing of our Controllers is to just
omit setting all properties on them.
* Workaround for broken MemberAutoDataAttribute
https://github.com/AutoFixture/AutoFixture/pull/1164 shows that only
the first test case is pulled for this attribute.
This is a workaround that populates the provided parameters, left to
right, using AutoFixture to populate any remaining.
* WIP: Organization sponsorship flow
* Add Attribute to use the Bit Autodata dependency chain
BitAutoDataAttribute is used to mark a Theory as autopopulating
parameters.
Extract common attribute methods to to a helper class. Cannot
inherit a common base, since both require inheriting from different
Xunit base classes to work.
* WIP: scaffolding for families for enterprise sponsorship flow
* Fix broken tests
* Create sponsorship offer (#1688)
* Initial db work (#1687)
* Add organization sponsorship databases to all providers
* Generalize create and update for database, specialize in code
* Add PlanSponsorshipType to db model
* Write valid json for test entries
* Initial scaffolding of emails (#1686)
* Initial scaffolding of emails
* Work on adding models for FamilyForEnterprise emails
* Switch verbage
* Put preliminary copy in emails
* Skip test
* Families for enterprise/stripe integrations (#1699)
* Add PlanSponsorshipType to static store
* Add sponsorship type to token and creates sponsorship
* PascalCase properties
* Require sponsorship for remove
* Create subscription sponsorship helper class
* Handle Sponsored subscription changes
* Add sponsorship id to subscription metadata
* Make sponsoring references nullable
This state indicates that a sponsorship has lapsed, but was not able to
be reverted for billing reasons
* WIP: Validate and remove subscriptions
* Update sponsorships on organization and org user delete
* Add friendly name to organization sponsorship
* Add sponsorship available boolean to orgDetails
* Add sponsorship service to DI
* Use userId to find org users
* Send f4e offer email
* Simplify names of f4e mail messages
* Fix Stripe org default tax rates
* Universal sponsorship redeem api
* Populate user in current context
* Add product type to organization details
* Use upgrade path to change sponsorship
Sponsorships need to be annual to match the GB add-on charge rate
* Use organization and auth to find organization sponsorship
* Add resend sponsorship offer api endpoint
* Fix double email send
* Fix sponsorship upgrade options
* Add is sponsored item to subscription response
* Add sponsorship validation to upcoming invoice webhook
* Add sponsorship validation to upcoming invoice webhook
* Fix organization delete sponsorship hooks
* Test org sponsorship service
* Fix sproc
* Fix build error
* Update emails
* Fix tests
* Skip local test
* Add newline
* Fix stripe subscription update
* Finish emails
* Skip test
* Fix unit tests
* Remove unused variable
* Fix unit tests
* Switch to handlebars ifs
* Remove ending email
* Remove reconfirmation template
* Switch naming convention
* Switch naming convention
* Fix migration
* Update copy and links
* Switch to using Guid in the method
* Remove unneeded css styles
* Add sql files to Sql.sqlproj
* Removed old comments
* Made name more verbose
* Fix SQL error
* Move unit tests to service
* Fix sp
* Revert "Move unit tests to service"
This reverts commit 1185bf3ec8ca36ccd75717ed2463adf8885159a6.
* Do repository validation in service layer
* Fix tests
* Fix merge conflicts and remove TODO
* Remove unneeded models
* Fix spacing and formatting
* Switch Org -> Organization
* Remove single use variables
* Switch method name
* Fix Controller
* Switch to obfuscating email
* Fix unit tests
Co-authored-by: Justin Baur <admin@justinbaur.com>
2021-11-19 23:25:06 +01:00
|
|
|
|
using Bit.Test.Common.AutoFixture;
|
|
|
|
|
using Bit.Test.Common.AutoFixture.Attributes;
|
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.Test.Common.Fakes;
|
2019-02-19 22:27:05 +01:00
|
|
|
|
using NSubstitute;
|
2023-08-04 23:51:12 +02:00
|
|
|
|
using NSubstitute.ExceptionExtensions;
|
2023-11-20 15:05:35 +01:00
|
|
|
|
using NSubstitute.ReturnsExtensions;
|
2019-02-19 22:27:05 +01:00
|
|
|
|
using Xunit;
|
2023-11-29 00:18:08 +01:00
|
|
|
|
using Organization = Bit.Core.AdminConsole.Entities.Organization;
|
2022-01-11 10:40:51 +01:00
|
|
|
|
using OrganizationUser = Bit.Core.Entities.OrganizationUser;
|
2019-02-19 22:27:05 +01:00
|
|
|
|
|
|
|
|
|
namespace Bit.Core.Test.Services;
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[SutProviderCustomize]
|
2019-02-19 22:27:05 +01:00
|
|
|
|
public class OrganizationServiceTests
|
|
|
|
|
{
|
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
|
|
|
|
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory = new FakeDataProtectorTokenFactory<OrgUserInviteTokenable>();
|
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, PaidOrganizationCustomize, BitAutoData]
|
2021-05-17 16:43:02 +02:00
|
|
|
|
public async Task OrgImportCreateNewUsers(SutProvider<OrganizationService> sutProvider, Guid userId,
|
|
|
|
|
Organization org, List<OrganizationUserUserDetails> existingUsers, List<ImportedOrganizationUser> newUsers)
|
2021-01-12 17:02:39 +01:00
|
|
|
|
{
|
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
|
|
|
|
// Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks
|
|
|
|
|
sutProvider.SetDependency(_orgUserInviteTokenDataFactory, "orgUserInviteTokenDataFactory");
|
|
|
|
|
sutProvider.Create();
|
|
|
|
|
|
2021-01-12 17:02:39 +01:00
|
|
|
|
org.UseDirectory = true;
|
|
|
|
|
org.Seats = 10;
|
|
|
|
|
newUsers.Add(new ImportedOrganizationUser
|
2022-08-29 21:53:48 +02:00
|
|
|
|
{
|
2021-01-12 17:02:39 +01:00
|
|
|
|
Email = existingUsers.First().Email,
|
|
|
|
|
ExternalId = existingUsers.First().ExternalId
|
|
|
|
|
});
|
|
|
|
|
var expectedNewUsersCount = newUsers.Count - 1;
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-01-12 17:02:39 +01:00
|
|
|
|
existingUsers.First().Type = OrganizationUserType.Owner;
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-01-12 17:02:39 +01:00
|
|
|
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(org.Id).Returns(org);
|
2023-12-18 17:16:17 +01:00
|
|
|
|
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
|
|
|
|
|
|
|
|
|
organizationUserRepository.GetManyDetailsByOrganizationAsync(org.Id)
|
2021-01-12 17:02:39 +01:00
|
|
|
|
.Returns(existingUsers);
|
2023-12-18 17:16:17 +01:00
|
|
|
|
organizationUserRepository.GetCountByOrganizationIdAsync(org.Id)
|
2021-01-12 17:02:39 +01:00
|
|
|
|
.Returns(existingUsers.Count);
|
2023-12-18 17:16:17 +01:00
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(org.Id, OrganizationUserType.Owner)
|
2021-01-12 17:02:39 +01:00
|
|
|
|
.Returns(existingUsers.Select(u => new OrganizationUser { Status = OrganizationUserStatusType.Confirmed, Type = OrganizationUserType.Owner, Id = u.Id }).ToList());
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().ManageUsers(org.Id).Returns(true);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
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
|
|
|
|
// Mock tokenable factory to return a token that expires in 5 days
|
|
|
|
|
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
|
|
|
|
.CreateToken(Arg.Any<OrganizationUser>())
|
|
|
|
|
.Returns(
|
|
|
|
|
info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
|
|
|
|
{
|
|
|
|
|
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2021-01-12 17:02:39 +01:00
|
|
|
|
await sutProvider.Sut.ImportAsync(org.Id, userId, null, newUsers, null, false);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-01-12 17:02:39 +01:00
|
|
|
|
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs()
|
|
|
|
|
.UpsertAsync(default);
|
|
|
|
|
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1)
|
2024-02-05 19:03:42 +01:00
|
|
|
|
.UpsertManyAsync(Arg.Is<IEnumerable<OrganizationUser>>(users => !users.Any()));
|
2021-01-12 17:02:39 +01:00
|
|
|
|
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs()
|
2021-09-23 12:36:08 +02:00
|
|
|
|
.CreateAsync(default);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-09-23 12:36:08 +02:00
|
|
|
|
// Create new users
|
2022-07-13 15:21:28 +02:00
|
|
|
|
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1)
|
|
|
|
|
.CreateManyAsync(Arg.Is<IEnumerable<OrganizationUser>>(users => users.Count() == expectedNewUsersCount));
|
2023-12-18 17:16:17 +01:00
|
|
|
|
|
2022-07-13 15:21:28 +02:00
|
|
|
|
await sutProvider.GetDependency<IMailService>().Received(1)
|
2023-12-18 17:16:17 +01:00
|
|
|
|
.SendOrganizationInviteEmailsAsync(
|
|
|
|
|
Arg.Is<OrganizationInvitesInfo>(info => info.OrgUserTokenPairs.Count() == expectedNewUsersCount && info.IsFreeOrg == (org.PlanType == PlanType.Free) && info.OrganizationName == org.Name));
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2022-07-13 15:21:28 +02:00
|
|
|
|
// Send events
|
|
|
|
|
await sutProvider.GetDependency<IEventService>().Received(1)
|
|
|
|
|
.LogOrganizationUserEventsAsync(Arg.Is<IEnumerable<(OrganizationUser, EventType, DateTime?)>>(events =>
|
|
|
|
|
events.Count() == expectedNewUsersCount));
|
|
|
|
|
await sutProvider.GetDependency<IReferenceEventService>().Received(1)
|
|
|
|
|
.RaiseEventAsync(Arg.Is<ReferenceEvent>(referenceEvent =>
|
|
|
|
|
referenceEvent.Type == ReferenceEventType.InvitedUsers && referenceEvent.Id == org.Id &&
|
|
|
|
|
referenceEvent.Users == expectedNewUsersCount));
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, PaidOrganizationCustomize, BitAutoData]
|
2021-07-08 17:05:32 +02:00
|
|
|
|
public async Task OrgImportCreateNewUsersAndMarryExistingUser(SutProvider<OrganizationService> sutProvider,
|
|
|
|
|
Guid userId, Organization org, List<OrganizationUserUserDetails> existingUsers,
|
2021-05-17 16:43:02 +02:00
|
|
|
|
List<ImportedOrganizationUser> newUsers)
|
2022-08-29 21:53:48 +02:00
|
|
|
|
{
|
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
|
|
|
|
// Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks
|
|
|
|
|
sutProvider.SetDependency(_orgUserInviteTokenDataFactory, "orgUserInviteTokenDataFactory");
|
|
|
|
|
sutProvider.Create();
|
|
|
|
|
|
2021-05-17 16:43:02 +02:00
|
|
|
|
org.UseDirectory = true;
|
2021-08-17 15:37:00 +02:00
|
|
|
|
org.Seats = newUsers.Count + existingUsers.Count + 1;
|
2021-05-17 16:43:02 +02:00
|
|
|
|
var reInvitedUser = existingUsers.First();
|
|
|
|
|
reInvitedUser.ExternalId = null;
|
|
|
|
|
newUsers.Add(new ImportedOrganizationUser
|
2022-08-29 21:53:48 +02:00
|
|
|
|
{
|
2021-05-17 16:43:02 +02:00
|
|
|
|
Email = reInvitedUser.Email,
|
|
|
|
|
ExternalId = reInvitedUser.Email,
|
2022-08-29 21:53:48 +02:00
|
|
|
|
});
|
2021-05-17 16:43:02 +02:00
|
|
|
|
var expectedNewUsersCount = newUsers.Count - 1;
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-07-08 17:05:32 +02:00
|
|
|
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(org.Id).Returns(org);
|
2021-05-17 16:43:02 +02:00
|
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyDetailsByOrganizationAsync(org.Id)
|
2021-07-08 17:05:32 +02:00
|
|
|
|
.Returns(existingUsers);
|
|
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>().GetCountByOrganizationIdAsync(org.Id)
|
|
|
|
|
.Returns(existingUsers.Count);
|
|
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>().GetByIdAsync(reInvitedUser.Id)
|
2021-05-17 16:43:02 +02:00
|
|
|
|
.Returns(new OrganizationUser { Id = reInvitedUser.Id });
|
2023-12-18 17:16:17 +01:00
|
|
|
|
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
|
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(org.Id, OrganizationUserType.Owner)
|
2021-09-23 12:36:08 +02:00
|
|
|
|
.Returns(existingUsers.Select(u => new OrganizationUser { Status = OrganizationUserStatusType.Confirmed, Type = OrganizationUserType.Owner, Id = u.Id }).ToList());
|
2023-12-18 17:16:17 +01:00
|
|
|
|
|
|
|
|
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
|
|
|
|
|
2021-09-23 12:36:08 +02:00
|
|
|
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
|
|
|
currentContext.ManageUsers(org.Id).Returns(true);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
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
|
|
|
|
// Mock tokenable factory to return a token that expires in 5 days
|
|
|
|
|
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
|
|
|
|
.CreateToken(Arg.Any<OrganizationUser>())
|
|
|
|
|
.Returns(
|
|
|
|
|
info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
|
|
|
|
{
|
|
|
|
|
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2021-09-23 12:36:08 +02:00
|
|
|
|
await sutProvider.Sut.ImportAsync(org.Id, userId, null, newUsers, null, false);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-09-23 12:36:08 +02:00
|
|
|
|
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs()
|
|
|
|
|
.UpsertAsync(default);
|
|
|
|
|
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs()
|
|
|
|
|
.CreateAsync(default);
|
|
|
|
|
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs()
|
2021-05-17 16:43:02 +02:00
|
|
|
|
.CreateAsync(default, default);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-09-23 12:36:08 +02:00
|
|
|
|
// Upserted existing user
|
2021-05-17 16:43:02 +02:00
|
|
|
|
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1)
|
|
|
|
|
.UpsertManyAsync(Arg.Is<IEnumerable<OrganizationUser>>(users => users.Count() == 1));
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-09-23 12:36:08 +02:00
|
|
|
|
// Created and invited new users
|
2021-05-17 16:43:02 +02:00
|
|
|
|
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1)
|
2021-09-23 12:36:08 +02:00
|
|
|
|
.CreateManyAsync(Arg.Is<IEnumerable<OrganizationUser>>(users => users.Count() == expectedNewUsersCount));
|
2023-12-18 17:16:17 +01:00
|
|
|
|
|
2021-07-08 17:05:32 +02:00
|
|
|
|
await sutProvider.GetDependency<IMailService>().Received(1)
|
2023-12-18 17:16:17 +01:00
|
|
|
|
.SendOrganizationInviteEmailsAsync(Arg.Is<OrganizationInvitesInfo>(info =>
|
|
|
|
|
info.OrgUserTokenPairs.Count() == expectedNewUsersCount && info.IsFreeOrg == (org.PlanType == PlanType.Free) && info.OrganizationName == org.Name));
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-07-08 17:05:32 +02:00
|
|
|
|
// Sent events
|
|
|
|
|
await sutProvider.GetDependency<IEventService>().Received(1)
|
|
|
|
|
.LogOrganizationUserEventsAsync(Arg.Is<IEnumerable<(OrganizationUser, EventType, DateTime?)>>(events =>
|
|
|
|
|
events.Where(e => e.Item2 == EventType.OrganizationUser_Invited).Count() == expectedNewUsersCount));
|
2021-05-17 16:43:02 +02:00
|
|
|
|
await sutProvider.GetDependency<IReferenceEventService>().Received(1)
|
2021-07-08 17:05:32 +02:00
|
|
|
|
.RaiseEventAsync(Arg.Is<ReferenceEvent>(referenceEvent =>
|
|
|
|
|
referenceEvent.Type == ReferenceEventType.InvitedUsers && referenceEvent.Id == org.Id &&
|
2021-05-17 16:43:02 +02:00
|
|
|
|
referenceEvent.Users == expectedNewUsersCount));
|
2021-07-08 17:05:32 +02:00
|
|
|
|
}
|
2021-01-12 17:02:39 +01:00
|
|
|
|
|
2023-08-04 23:51:12 +02:00
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData(PlanType.FamiliesAnnually)]
|
|
|
|
|
public async Task SignUp_PM_Family_Passes(PlanType planType, OrganizationSignup signup, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
signup.Plan = planType;
|
|
|
|
|
|
2023-10-17 16:56:35 +02:00
|
|
|
|
var plan = StaticStore.GetPlan(signup.Plan);
|
2023-08-04 23:51:12 +02:00
|
|
|
|
|
|
|
|
|
signup.AdditionalSeats = 0;
|
|
|
|
|
signup.PaymentMethodType = PaymentMethodType.Card;
|
|
|
|
|
signup.PremiumAccessAddon = false;
|
|
|
|
|
signup.UseSecretsManager = false;
|
2024-01-29 17:10:27 +01:00
|
|
|
|
signup.IsFromSecretsManagerTrial = false;
|
2023-08-04 23:51:12 +02:00
|
|
|
|
|
2023-10-17 16:56:35 +02:00
|
|
|
|
var purchaseOrganizationPlan = StaticStore.GetPlan(signup.Plan);
|
2023-08-04 23:51:12 +02:00
|
|
|
|
|
|
|
|
|
var result = await sutProvider.Sut.SignUpAsync(signup);
|
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IOrganizationRepository>().Received(1).CreateAsync(
|
|
|
|
|
Arg.Is<Organization>(o =>
|
2023-10-17 16:56:35 +02:00
|
|
|
|
o.Seats == plan.PasswordManager.BaseSeats + signup.AdditionalSeats
|
2023-08-04 23:51:12 +02:00
|
|
|
|
&& o.SmSeats == null
|
|
|
|
|
&& o.SmServiceAccounts == null));
|
|
|
|
|
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).CreateAsync(
|
|
|
|
|
Arg.Is<OrganizationUser>(o => o.AccessSecretsManager == signup.UseSecretsManager));
|
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IReferenceEventService>().Received(1)
|
|
|
|
|
.RaiseEventAsync(Arg.Is<ReferenceEvent>(referenceEvent =>
|
|
|
|
|
referenceEvent.Type == ReferenceEventType.Signup &&
|
2023-10-17 16:56:35 +02:00
|
|
|
|
referenceEvent.PlanName == plan.Name &&
|
|
|
|
|
referenceEvent.PlanType == plan.Type &&
|
2023-08-04 23:51:12 +02:00
|
|
|
|
referenceEvent.Seats == result.Item1.Seats &&
|
|
|
|
|
referenceEvent.Storage == result.Item1.MaxStorageGb));
|
|
|
|
|
// TODO: add reference events for SmSeats and Service Accounts - see AC-1481
|
|
|
|
|
|
|
|
|
|
Assert.NotNull(result.Item1);
|
|
|
|
|
Assert.NotNull(result.Item2);
|
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IPaymentService>().Received(1).PurchaseOrganizationAsync(
|
|
|
|
|
Arg.Any<Organization>(),
|
|
|
|
|
signup.PaymentMethodType.Value,
|
|
|
|
|
signup.PaymentToken,
|
2023-10-17 16:56:35 +02:00
|
|
|
|
plan,
|
2023-08-04 23:51:12 +02:00
|
|
|
|
signup.AdditionalStorageGb,
|
|
|
|
|
signup.AdditionalSeats,
|
|
|
|
|
signup.PremiumAccessAddon,
|
|
|
|
|
signup.TaxInfo,
|
|
|
|
|
false,
|
|
|
|
|
signup.AdditionalSmSeats.GetValueOrDefault(),
|
2024-01-29 17:10:27 +01:00
|
|
|
|
signup.AdditionalServiceAccounts.GetValueOrDefault(),
|
|
|
|
|
signup.UseSecretsManager
|
2023-08-04 23:51:12 +02:00
|
|
|
|
);
|
|
|
|
|
}
|
2021-01-12 17:02:39 +01:00
|
|
|
|
|
2024-01-21 23:56:20 +01:00
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData(PlanType.FamiliesAnnually)]
|
2024-05-23 01:15:12 +02:00
|
|
|
|
public async Task SignUp_EnablesFlexibleCollectionsFeatures
|
2024-01-21 23:56:20 +01:00
|
|
|
|
(PlanType planType, OrganizationSignup signup, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
signup.Plan = planType;
|
|
|
|
|
signup.AdditionalSeats = 0;
|
|
|
|
|
signup.PaymentMethodType = PaymentMethodType.Card;
|
|
|
|
|
signup.PremiumAccessAddon = false;
|
|
|
|
|
signup.UseSecretsManager = false;
|
|
|
|
|
|
2024-02-11 23:50:41 +01:00
|
|
|
|
// Extract orgUserId when created
|
|
|
|
|
Guid? orgUserId = null;
|
|
|
|
|
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
|
|
|
.CreateAsync(Arg.Do<OrganizationUser>(ou => orgUserId = ou.Id));
|
|
|
|
|
|
2024-01-21 23:56:20 +01:00
|
|
|
|
var result = await sutProvider.Sut.SignUpAsync(signup);
|
|
|
|
|
|
2024-02-11 23:50:41 +01:00
|
|
|
|
// Assert: AccessAll is not used
|
2024-01-21 23:56:20 +01:00
|
|
|
|
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).CreateAsync(
|
|
|
|
|
Arg.Is<OrganizationUser>(o =>
|
|
|
|
|
o.UserId == signup.Owner.Id &&
|
|
|
|
|
o.AccessAll == false));
|
|
|
|
|
|
2024-02-11 23:50:41 +01:00
|
|
|
|
// Assert: created a Can Manage association for the default collection instead
|
|
|
|
|
Assert.NotNull(orgUserId);
|
|
|
|
|
await sutProvider.GetDependency<ICollectionRepository>().Received(1).CreateAsync(
|
|
|
|
|
Arg.Any<Collection>(),
|
|
|
|
|
Arg.Is<IEnumerable<CollectionAccessSelection>>(cas => cas == null),
|
|
|
|
|
Arg.Is<IEnumerable<CollectionAccessSelection>>(cas =>
|
|
|
|
|
cas.Count() == 1 &&
|
|
|
|
|
cas.All(c =>
|
|
|
|
|
c.Id == orgUserId &&
|
|
|
|
|
!c.ReadOnly &&
|
|
|
|
|
!c.HidePasswords &&
|
|
|
|
|
c.Manage)));
|
|
|
|
|
|
2024-01-21 23:56:20 +01:00
|
|
|
|
Assert.NotNull(result.Item1);
|
|
|
|
|
Assert.NotNull(result.Item2);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-25 00:05:05 +02:00
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData(PlanType.EnterpriseAnnually)]
|
|
|
|
|
[BitAutoData(PlanType.EnterpriseMonthly)]
|
|
|
|
|
[BitAutoData(PlanType.TeamsAnnually)]
|
|
|
|
|
[BitAutoData(PlanType.TeamsMonthly)]
|
|
|
|
|
public async Task SignUp_SM_Passes(PlanType planType, OrganizationSignup signup, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 21:53:48 +02:00
|
|
|
|
{
|
2023-07-25 00:05:05 +02:00
|
|
|
|
signup.Plan = planType;
|
|
|
|
|
|
2023-10-17 16:56:35 +02:00
|
|
|
|
var plan = StaticStore.GetPlan(signup.Plan);
|
2023-07-25 00:05:05 +02:00
|
|
|
|
|
|
|
|
|
signup.UseSecretsManager = true;
|
|
|
|
|
signup.AdditionalSeats = 15;
|
|
|
|
|
signup.AdditionalSmSeats = 10;
|
|
|
|
|
signup.AdditionalServiceAccounts = 20;
|
|
|
|
|
signup.PaymentMethodType = PaymentMethodType.Card;
|
|
|
|
|
signup.PremiumAccessAddon = false;
|
2024-01-29 17:10:27 +01:00
|
|
|
|
signup.IsFromSecretsManagerTrial = false;
|
2023-07-25 00:05:05 +02:00
|
|
|
|
|
|
|
|
|
var result = await sutProvider.Sut.SignUpAsync(signup);
|
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IOrganizationRepository>().Received(1).CreateAsync(
|
|
|
|
|
Arg.Is<Organization>(o =>
|
2023-10-17 16:56:35 +02:00
|
|
|
|
o.Seats == plan.PasswordManager.BaseSeats + signup.AdditionalSeats
|
|
|
|
|
&& o.SmSeats == plan.SecretsManager.BaseSeats + signup.AdditionalSmSeats
|
|
|
|
|
&& o.SmServiceAccounts == plan.SecretsManager.BaseServiceAccount + signup.AdditionalServiceAccounts));
|
2023-07-25 00:05:05 +02:00
|
|
|
|
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).CreateAsync(
|
|
|
|
|
Arg.Is<OrganizationUser>(o => o.AccessSecretsManager == signup.UseSecretsManager));
|
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IReferenceEventService>().Received(1)
|
|
|
|
|
.RaiseEventAsync(Arg.Is<ReferenceEvent>(referenceEvent =>
|
|
|
|
|
referenceEvent.Type == ReferenceEventType.Signup &&
|
2023-10-17 16:56:35 +02:00
|
|
|
|
referenceEvent.PlanName == plan.Name &&
|
|
|
|
|
referenceEvent.PlanType == plan.Type &&
|
2023-07-25 00:05:05 +02:00
|
|
|
|
referenceEvent.Seats == result.Item1.Seats &&
|
|
|
|
|
referenceEvent.Storage == result.Item1.MaxStorageGb));
|
|
|
|
|
// TODO: add reference events for SmSeats and Service Accounts - see AC-1481
|
|
|
|
|
|
|
|
|
|
Assert.NotNull(result.Item1);
|
|
|
|
|
Assert.NotNull(result.Item2);
|
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IPaymentService>().Received(1).PurchaseOrganizationAsync(
|
|
|
|
|
Arg.Any<Organization>(),
|
|
|
|
|
signup.PaymentMethodType.Value,
|
|
|
|
|
signup.PaymentToken,
|
2023-10-17 16:56:35 +02:00
|
|
|
|
Arg.Is<Plan>(plan),
|
2023-07-25 00:05:05 +02:00
|
|
|
|
signup.AdditionalStorageGb,
|
|
|
|
|
signup.AdditionalSeats,
|
|
|
|
|
signup.PremiumAccessAddon,
|
|
|
|
|
signup.TaxInfo,
|
|
|
|
|
false,
|
|
|
|
|
signup.AdditionalSmSeats.GetValueOrDefault(),
|
2024-01-29 17:10:27 +01:00
|
|
|
|
signup.AdditionalServiceAccounts.GetValueOrDefault(),
|
|
|
|
|
signup.IsFromSecretsManagerTrial
|
2023-07-25 00:05:05 +02:00
|
|
|
|
);
|
2022-08-29 21:53:48 +02:00
|
|
|
|
}
|
2021-01-12 17:02:39 +01:00
|
|
|
|
|
2023-10-12 16:56:50 +02:00
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData(PlanType.EnterpriseAnnually)]
|
|
|
|
|
public async Task SignUp_SM_Throws_WhenManagedByMSP(PlanType planType, OrganizationSignup signup, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
signup.Plan = planType;
|
|
|
|
|
signup.UseSecretsManager = true;
|
|
|
|
|
signup.AdditionalSeats = 15;
|
|
|
|
|
signup.AdditionalSmSeats = 10;
|
|
|
|
|
signup.AdditionalServiceAccounts = 20;
|
|
|
|
|
signup.PaymentMethodType = PaymentMethodType.Card;
|
|
|
|
|
signup.PremiumAccessAddon = false;
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.SignUpAsync(signup, true));
|
|
|
|
|
Assert.Contains("Organizations with a Managed Service Provider do not support Secrets Manager.", exception.Message);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-25 00:05:05 +02:00
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData]
|
|
|
|
|
public async Task SignUpAsync_SecretManager_AdditionalServiceAccounts_NotAllowedByPlan_ShouldThrowException(OrganizationSignup signup, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
signup.AdditionalSmSeats = 0;
|
|
|
|
|
signup.AdditionalSeats = 0;
|
|
|
|
|
signup.Plan = PlanType.Free;
|
|
|
|
|
signup.UseSecretsManager = true;
|
|
|
|
|
signup.PaymentMethodType = PaymentMethodType.Card;
|
|
|
|
|
signup.PremiumAccessAddon = false;
|
|
|
|
|
signup.AdditionalServiceAccounts = 10;
|
|
|
|
|
signup.AdditionalStorageGb = 0;
|
|
|
|
|
|
2021-01-12 17:02:39 +01:00
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
2023-07-25 00:05:05 +02:00
|
|
|
|
() => sutProvider.Sut.SignUpAsync(signup));
|
2024-04-05 14:54:36 +02:00
|
|
|
|
Assert.Contains("Plan does not allow additional Machine Accounts.", exception.Message);
|
2021-01-12 17:02:39 +01:00
|
|
|
|
}
|
|
|
|
|
|
2023-07-25 00:05:05 +02:00
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData]
|
|
|
|
|
public async Task SignUpAsync_SMSeatsGreatThanPMSeat_ShouldThrowException(OrganizationSignup signup, SutProvider<OrganizationService> sutProvider)
|
2021-01-12 17:02:39 +01:00
|
|
|
|
{
|
2023-07-25 00:05:05 +02:00
|
|
|
|
signup.AdditionalSmSeats = 100;
|
|
|
|
|
signup.AdditionalSeats = 10;
|
|
|
|
|
signup.Plan = PlanType.EnterpriseAnnually;
|
|
|
|
|
signup.UseSecretsManager = true;
|
|
|
|
|
signup.PaymentMethodType = PaymentMethodType.Card;
|
|
|
|
|
signup.PremiumAccessAddon = false;
|
|
|
|
|
signup.AdditionalServiceAccounts = 10;
|
|
|
|
|
|
2021-07-01 14:31:05 +02:00
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
2023-07-25 00:05:05 +02:00
|
|
|
|
() => sutProvider.Sut.SignUpAsync(signup));
|
|
|
|
|
Assert.Contains("You cannot have more Secrets Manager seats than Password Manager seats", exception.Message);
|
2022-08-29 21:53:48 +02:00
|
|
|
|
}
|
2021-01-12 17:02:39 +01:00
|
|
|
|
|
2022-08-29 21:53:48 +02:00
|
|
|
|
[Theory]
|
2023-07-25 00:05:05 +02:00
|
|
|
|
[BitAutoData]
|
|
|
|
|
public async Task SignUpAsync_InvalidateServiceAccount_ShouldThrowException(OrganizationSignup signup, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 21:53:48 +02:00
|
|
|
|
{
|
2023-07-25 00:05:05 +02:00
|
|
|
|
signup.AdditionalSmSeats = 10;
|
|
|
|
|
signup.AdditionalSeats = 10;
|
|
|
|
|
signup.Plan = PlanType.EnterpriseAnnually;
|
|
|
|
|
signup.UseSecretsManager = true;
|
|
|
|
|
signup.PaymentMethodType = PaymentMethodType.Card;
|
|
|
|
|
signup.PremiumAccessAddon = false;
|
|
|
|
|
signup.AdditionalServiceAccounts = -10;
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.SignUpAsync(signup));
|
2024-04-05 14:54:36 +02:00
|
|
|
|
Assert.Contains("You can't subtract Machine Accounts!", exception.Message);
|
2022-08-29 21:53:48 +02:00
|
|
|
|
}
|
2021-01-12 17:02:39 +01:00
|
|
|
|
|
2024-04-16 19:55:00 +02:00
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task SignupClientAsync_Succeeds(
|
|
|
|
|
OrganizationSignup signup,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling).Returns(true);
|
|
|
|
|
|
|
|
|
|
signup.Plan = PlanType.TeamsMonthly;
|
|
|
|
|
|
|
|
|
|
var (organization, _, _) = await sutProvider.Sut.SignupClientAsync(signup);
|
|
|
|
|
|
|
|
|
|
var plan = StaticStore.GetPlan(signup.Plan);
|
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IOrganizationRepository>().Received(1).CreateAsync(Arg.Is<Organization>(org =>
|
|
|
|
|
org.Id == organization.Id &&
|
|
|
|
|
org.Name == signup.Name &&
|
|
|
|
|
org.Plan == plan.Name &&
|
|
|
|
|
org.PlanType == plan.Type &&
|
|
|
|
|
org.UsePolicies == plan.HasPolicies &&
|
|
|
|
|
org.PublicKey == signup.PublicKey &&
|
|
|
|
|
org.PrivateKey == signup.PrivateKey &&
|
|
|
|
|
org.UseSecretsManager == false));
|
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IOrganizationApiKeyRepository>().Received(1)
|
|
|
|
|
.CreateAsync(Arg.Is<OrganizationApiKey>(orgApiKey =>
|
|
|
|
|
orgApiKey.OrganizationId == organization.Id));
|
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IApplicationCacheService>().Received(1)
|
|
|
|
|
.UpsertOrganizationAbilityAsync(organization);
|
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
|
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<ICollectionRepository>().Received(1)
|
|
|
|
|
.CreateAsync(Arg.Is<Collection>(c => c.Name == signup.CollectionName && c.OrganizationId == organization.Id), null, null);
|
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IReferenceEventService>().Received(1).RaiseEventAsync(Arg.Is<ReferenceEvent>(
|
|
|
|
|
re =>
|
|
|
|
|
re.Type == ReferenceEventType.Signup &&
|
|
|
|
|
re.PlanType == plan.Type));
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-29 21:53:48 +02:00
|
|
|
|
[Theory]
|
2022-11-09 13:13:29 +01:00
|
|
|
|
[OrganizationInviteCustomize(InviteeUserType = OrganizationUserType.User,
|
2024-07-26 07:07:33 +02:00
|
|
|
|
InvitorUserType = OrganizationUserType.Owner), OrganizationCustomize, BitAutoData]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUsers_NoEmails_Throws(Organization organization, OrganizationUser invitor,
|
2021-01-12 17:02:39 +01:00
|
|
|
|
OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 21:53:48 +02:00
|
|
|
|
{
|
2021-01-12 17:02:39 +01:00
|
|
|
|
invite.Emails = null;
|
2023-05-17 15:17:37 +02:00
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(organization.Id).Returns(true);
|
2022-11-09 13:13:29 +01:00
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
2021-01-12 17:02:39 +01:00
|
|
|
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
|
|
|
|
await Assert.ThrowsAsync<NotFoundException>(
|
2024-05-31 01:23:31 +02:00
|
|
|
|
() => sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, systemUser: null, new (OrganizationUserInvite, string)[] { (invite, null) }));
|
2021-01-12 17:02:39 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-12 04:36:23 +02:00
|
|
|
|
[Theory]
|
2024-07-26 07:07:33 +02:00
|
|
|
|
[OrganizationInviteCustomize, OrganizationCustomize, BitAutoData]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUsers_DuplicateEmails_PassesWithoutDuplicates(Organization organization, OrganizationUser invitor,
|
2021-05-12 04:36:23 +02:00
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
2021-01-12 17:02:39 +01:00
|
|
|
|
OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
2021-05-12 04:36:23 +02:00
|
|
|
|
{
|
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
|
|
|
|
// Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks
|
|
|
|
|
sutProvider.SetDependency(_orgUserInviteTokenDataFactory, "orgUserInviteTokenDataFactory");
|
|
|
|
|
sutProvider.Create();
|
|
|
|
|
|
2021-05-12 04:36:23 +02:00
|
|
|
|
invite.Emails = invite.Emails.Append(invite.Emails.First());
|
2021-01-12 17:02:39 +01:00
|
|
|
|
|
|
|
|
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
2021-11-09 16:37:32 +01:00
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(organization.Id).Returns(true);
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
2021-01-12 17:02:39 +01:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
2021-07-01 14:31:05 +02:00
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new[] { owner });
|
2021-01-12 17:02:39 +01:00
|
|
|
|
|
2023-12-18 17:16:17 +01:00
|
|
|
|
// Must set guids in order for dictionary of guids to not throw aggregate exceptions
|
|
|
|
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
|
|
|
|
|
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
|
|
|
|
// Mock tokenable factory to return a token that expires in 5 days
|
|
|
|
|
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
|
|
|
|
.CreateToken(Arg.Any<OrganizationUser>())
|
|
|
|
|
.Returns(
|
|
|
|
|
info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
|
|
|
|
{
|
|
|
|
|
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2023-12-18 17:16:17 +01:00
|
|
|
|
|
2024-05-31 01:23:31 +02:00
|
|
|
|
await sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, systemUser: null, new (OrganizationUserInvite, string)[] { (invite, null) });
|
2021-01-13 21:14:28 +01:00
|
|
|
|
|
2021-12-14 17:29:31 +01:00
|
|
|
|
await sutProvider.GetDependency<IMailService>().Received(1)
|
2023-12-18 17:16:17 +01:00
|
|
|
|
.SendOrganizationInviteEmailsAsync(Arg.Is<OrganizationInvitesInfo>(info =>
|
|
|
|
|
info.OrgUserTokenPairs.Count() == invite.Emails.Distinct().Count() &&
|
|
|
|
|
info.IsFreeOrg == (organization.PlanType == PlanType.Free) &&
|
|
|
|
|
info.OrganizationName == organization.Name));
|
|
|
|
|
|
2021-01-12 17:02:39 +01:00
|
|
|
|
}
|
|
|
|
|
|
2023-12-19 01:34:56 +01:00
|
|
|
|
[Theory]
|
2024-07-26 07:07:33 +02:00
|
|
|
|
[OrganizationInviteCustomize, OrganizationCustomize, BitAutoData]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUsers_SsoOrgWithNullSsoConfig_Passes(Organization organization, OrganizationUser invitor,
|
2023-12-19 01:34:56 +01:00
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
// Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks
|
|
|
|
|
sutProvider.SetDependency(_orgUserInviteTokenDataFactory, "orgUserInviteTokenDataFactory");
|
|
|
|
|
sutProvider.Create();
|
|
|
|
|
|
|
|
|
|
// Org must be able to use SSO to trigger this proper test case as we currently only call to retrieve
|
|
|
|
|
// an org's SSO config if the org can use SSO
|
|
|
|
|
organization.UseSso = true;
|
|
|
|
|
|
|
|
|
|
// Return null for sso config
|
|
|
|
|
sutProvider.GetDependency<ISsoConfigRepository>().GetByOrganizationIdAsync(organization.Id).ReturnsNull();
|
|
|
|
|
|
|
|
|
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(organization.Id).Returns(true);
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new[] { owner });
|
|
|
|
|
|
|
|
|
|
// Must set guids in order for dictionary of guids to not throw aggregate exceptions
|
|
|
|
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
|
|
|
|
|
|
|
|
|
// Mock tokenable factory to return a token that expires in 5 days
|
|
|
|
|
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
|
|
|
|
.CreateToken(Arg.Any<OrganizationUser>())
|
|
|
|
|
.Returns(
|
|
|
|
|
info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
|
|
|
|
{
|
|
|
|
|
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-05-31 01:23:31 +02:00
|
|
|
|
await sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, systemUser: null, new (OrganizationUserInvite, string)[] { (invite, null) });
|
2023-12-19 01:34:56 +01:00
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IMailService>().Received(1)
|
|
|
|
|
.SendOrganizationInviteEmailsAsync(Arg.Is<OrganizationInvitesInfo>(info =>
|
|
|
|
|
info.OrgUserTokenPairs.Count() == invite.Emails.Distinct().Count() &&
|
|
|
|
|
info.IsFreeOrg == (organization.PlanType == PlanType.Free) &&
|
|
|
|
|
info.OrganizationName == organization.Name));
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-11 15:58:15 +01:00
|
|
|
|
[Theory]
|
2024-07-26 07:07:33 +02:00
|
|
|
|
[OrganizationInviteCustomize, OrganizationCustomize, BitAutoData]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUsers_SsoOrgWithNeverEnabledRequireSsoPolicy_Passes(Organization organization, SsoConfig ssoConfig, OrganizationUser invitor,
|
2024-01-11 15:58:15 +01:00
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
// Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks
|
|
|
|
|
sutProvider.SetDependency(_orgUserInviteTokenDataFactory, "orgUserInviteTokenDataFactory");
|
|
|
|
|
sutProvider.Create();
|
|
|
|
|
|
|
|
|
|
// Org must be able to use SSO and policies to trigger this test case
|
|
|
|
|
organization.UseSso = true;
|
|
|
|
|
organization.UsePolicies = true;
|
|
|
|
|
|
|
|
|
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(organization.Id).Returns(true);
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new[] { owner });
|
|
|
|
|
|
|
|
|
|
ssoConfig.Enabled = true;
|
|
|
|
|
sutProvider.GetDependency<ISsoConfigRepository>().GetByOrganizationIdAsync(organization.Id).Returns(ssoConfig);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Return null policy to mimic new org that's never turned on the require sso policy
|
|
|
|
|
sutProvider.GetDependency<IPolicyRepository>().GetManyByOrganizationIdAsync(organization.Id).ReturnsNull();
|
|
|
|
|
|
|
|
|
|
// Must set guids in order for dictionary of guids to not throw aggregate exceptions
|
|
|
|
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
|
|
|
|
|
|
|
|
|
// Mock tokenable factory to return a token that expires in 5 days
|
|
|
|
|
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
|
|
|
|
.CreateToken(Arg.Any<OrganizationUser>())
|
|
|
|
|
.Returns(
|
|
|
|
|
info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
|
|
|
|
{
|
|
|
|
|
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2024-05-31 01:23:31 +02:00
|
|
|
|
await sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, systemUser: null, new (OrganizationUserInvite, string)[] { (invite, null) });
|
2024-01-11 15:58:15 +01:00
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IMailService>().Received(1)
|
|
|
|
|
.SendOrganizationInviteEmailsAsync(Arg.Is<OrganizationInvitesInfo>(info =>
|
|
|
|
|
info.OrgUserTokenPairs.Count() == invite.Emails.Distinct().Count() &&
|
|
|
|
|
info.IsFreeOrg == (organization.PlanType == PlanType.Free) &&
|
|
|
|
|
info.OrganizationName == organization.Name));
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-12 17:02:39 +01:00
|
|
|
|
[Theory]
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[OrganizationInviteCustomize(
|
|
|
|
|
InviteeUserType = OrganizationUserType.Admin,
|
|
|
|
|
InvitorUserType = OrganizationUserType.Owner
|
2024-07-26 07:07:33 +02:00
|
|
|
|
), OrganizationCustomize, BitAutoData]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUsers_NoOwner_Throws(Organization organization, OrganizationUser invitor,
|
2021-01-12 17:02:39 +01:00
|
|
|
|
OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(organization.Id).Returns(true);
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
2024-05-31 01:23:31 +02:00
|
|
|
|
() => sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, systemUser: null, new (OrganizationUserInvite, string)[] { (invite, null) }));
|
2021-01-12 17:02:39 +01:00
|
|
|
|
Assert.Contains("Organization must have at least one confirmed owner.", exception.Message);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-29 21:53:48 +02:00
|
|
|
|
[Theory]
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[OrganizationInviteCustomize(
|
|
|
|
|
InviteeUserType = OrganizationUserType.Owner,
|
|
|
|
|
InvitorUserType = OrganizationUserType.Admin
|
2024-07-26 07:07:33 +02:00
|
|
|
|
), OrganizationCustomize, BitAutoData]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUsers_NonOwnerConfiguringOwner_Throws(Organization organization, OrganizationUserInvite invite,
|
2021-05-17 10:10:44 +02:00
|
|
|
|
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 21:53:48 +02:00
|
|
|
|
{
|
2021-01-12 17:02:39 +01:00
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
|
|
|
|
|
|
|
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
2021-07-01 14:31:05 +02:00
|
|
|
|
currentContext.OrganizationAdmin(organization.Id).Returns(true);
|
2021-12-16 15:35:09 +01:00
|
|
|
|
|
2021-05-17 10:10:44 +02:00
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
2024-05-31 01:23:31 +02:00
|
|
|
|
() => sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, systemUser: null, new (OrganizationUserInvite, string)[] { (invite, null) }));
|
2021-05-17 10:10:44 +02:00
|
|
|
|
Assert.Contains("only an owner", exception.Message.ToLowerInvariant());
|
2022-08-29 20:53:16 +02:00
|
|
|
|
}
|
2021-05-17 10:10:44 +02:00
|
|
|
|
|
2022-08-29 20:53:16 +02:00
|
|
|
|
[Theory]
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[OrganizationInviteCustomize(
|
|
|
|
|
InviteeUserType = OrganizationUserType.Custom,
|
|
|
|
|
InvitorUserType = OrganizationUserType.User
|
2024-07-26 07:07:33 +02:00
|
|
|
|
), OrganizationCustomize, BitAutoData]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUsers_NonAdminConfiguringAdmin_Throws(Organization organization, OrganizationUserInvite invite,
|
2021-05-17 10:10:44 +02:00
|
|
|
|
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 20:53:16 +02:00
|
|
|
|
{
|
2022-12-06 10:50:08 +01:00
|
|
|
|
organization.UseCustomPermissions = true;
|
|
|
|
|
|
2021-01-13 21:14:28 +01:00
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
2021-07-01 14:31:05 +02:00
|
|
|
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
2021-05-17 10:10:44 +02:00
|
|
|
|
|
|
|
|
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
|
|
|
|
currentContext.OrganizationUser(organization.Id).Returns(true);
|
2021-12-16 15:35:09 +01:00
|
|
|
|
|
2021-05-17 10:10:44 +02:00
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
2024-05-31 01:23:31 +02:00
|
|
|
|
() => sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, systemUser: null, new (OrganizationUserInvite, string)[] { (invite, null) }));
|
2023-05-17 15:17:37 +02:00
|
|
|
|
Assert.Contains("your account does not have permission to manage users", exception.Message.ToLowerInvariant());
|
2021-05-17 10:10:44 +02:00
|
|
|
|
}
|
2021-12-16 15:35:09 +01:00
|
|
|
|
|
2022-12-06 10:50:08 +01:00
|
|
|
|
[Theory]
|
|
|
|
|
[OrganizationInviteCustomize(
|
|
|
|
|
InviteeUserType = OrganizationUserType.Custom,
|
|
|
|
|
InvitorUserType = OrganizationUserType.Admin
|
2024-07-26 07:07:33 +02:00
|
|
|
|
), OrganizationCustomize, BitAutoData]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUsers_WithCustomType_WhenUseCustomPermissionsIsFalse_Throws(Organization organization, OrganizationUserInvite invite,
|
2022-12-06 10:50:08 +01:00
|
|
|
|
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
organization.UseCustomPermissions = false;
|
|
|
|
|
|
|
|
|
|
invite.Permissions = null;
|
|
|
|
|
invitor.Status = OrganizationUserStatusType.Confirmed;
|
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
|
|
|
|
|
|
|
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new[] { invitor });
|
|
|
|
|
currentContext.OrganizationOwner(organization.Id).Returns(true);
|
|
|
|
|
currentContext.ManageUsers(organization.Id).Returns(true);
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
2024-05-31 01:23:31 +02:00
|
|
|
|
() => sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, systemUser: null, new (OrganizationUserInvite, string)[] { (invite, null) }));
|
2022-12-06 10:50:08 +01:00
|
|
|
|
Assert.Contains("to enable custom permissions", exception.Message.ToLowerInvariant());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[OrganizationInviteCustomize(
|
|
|
|
|
InviteeUserType = OrganizationUserType.Custom,
|
|
|
|
|
InvitorUserType = OrganizationUserType.Admin
|
2024-07-26 07:07:33 +02:00
|
|
|
|
), OrganizationCustomize, BitAutoData]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUsers_WithCustomType_WhenUseCustomPermissionsIsTrue_Passes(Organization organization, OrganizationUserInvite invite,
|
2022-12-06 10:50:08 +01:00
|
|
|
|
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
2023-01-30 11:54:44 +01:00
|
|
|
|
organization.Seats = 10;
|
2022-12-06 10:50:08 +01:00
|
|
|
|
organization.UseCustomPermissions = true;
|
|
|
|
|
|
|
|
|
|
invite.Permissions = null;
|
|
|
|
|
invitor.Status = OrganizationUserStatusType.Confirmed;
|
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
|
|
|
|
|
|
|
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new[] { invitor });
|
2023-12-18 17:16:17 +01:00
|
|
|
|
|
|
|
|
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
|
|
|
|
|
2022-12-06 10:50:08 +01:00
|
|
|
|
currentContext.OrganizationOwner(organization.Id).Returns(true);
|
|
|
|
|
currentContext.ManageUsers(organization.Id).Returns(true);
|
|
|
|
|
|
2024-05-31 01:23:31 +02:00
|
|
|
|
await sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, systemUser: null, new (OrganizationUserInvite, string)[] { (invite, null) });
|
2022-12-06 10:50:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData(OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(OrganizationUserType.Owner)]
|
|
|
|
|
[BitAutoData(OrganizationUserType.User)]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUsers_WithNonCustomType_WhenUseCustomPermissionsIsFalse_Passes(OrganizationUserType inviteUserType, Organization organization, OrganizationUserInvite invite,
|
2022-12-06 10:50:08 +01:00
|
|
|
|
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
2023-01-30 11:54:44 +01:00
|
|
|
|
organization.Seats = 10;
|
2022-12-06 10:50:08 +01:00
|
|
|
|
organization.UseCustomPermissions = false;
|
|
|
|
|
|
|
|
|
|
invite.Type = inviteUserType;
|
|
|
|
|
invite.Permissions = null;
|
|
|
|
|
invitor.Status = OrganizationUserStatusType.Confirmed;
|
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
|
|
|
|
|
|
|
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new[] { invitor });
|
2023-12-18 17:16:17 +01:00
|
|
|
|
|
|
|
|
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
|
|
|
|
|
2022-12-06 10:50:08 +01:00
|
|
|
|
currentContext.OrganizationOwner(organization.Id).Returns(true);
|
|
|
|
|
currentContext.ManageUsers(organization.Id).Returns(true);
|
|
|
|
|
|
2024-05-31 01:23:31 +02:00
|
|
|
|
await sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, systemUser: null, new (OrganizationUserInvite, string)[] { (invite, null) });
|
2022-12-06 10:50:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
2022-08-29 21:53:48 +02:00
|
|
|
|
[Theory]
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[OrganizationInviteCustomize(
|
2024-07-11 22:13:10 +02:00
|
|
|
|
InviteeUserType = OrganizationUserType.User,
|
2022-08-31 15:38:35 +02:00
|
|
|
|
InvitorUserType = OrganizationUserType.Custom
|
2024-07-26 07:07:33 +02:00
|
|
|
|
), OrganizationCustomize, BitAutoData]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUsers_CustomUserWithoutManageUsersConfiguringUser_Throws(Organization organization, OrganizationUserInvite invite,
|
2022-07-13 15:21:28 +02:00
|
|
|
|
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 21:53:48 +02:00
|
|
|
|
{
|
2021-05-12 04:36:23 +02:00
|
|
|
|
invitor.Permissions = JsonSerializer.Serialize(new Permissions() { ManageUsers = false },
|
2021-01-12 17:02:39 +01:00
|
|
|
|
new JsonSerializerOptions
|
2021-09-23 12:36:08 +02:00
|
|
|
|
{
|
2021-01-12 17:02:39 +01:00
|
|
|
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
|
|
|
});
|
2021-05-17 10:10:44 +02:00
|
|
|
|
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
2021-07-01 14:31:05 +02:00
|
|
|
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
2021-12-16 15:35:09 +01:00
|
|
|
|
|
2021-05-17 10:10:44 +02:00
|
|
|
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
2023-12-18 17:16:17 +01:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
2021-07-01 14:31:05 +02:00
|
|
|
|
currentContext.OrganizationCustom(organization.Id).Returns(true);
|
|
|
|
|
currentContext.ManageUsers(organization.Id).Returns(false);
|
2021-12-16 15:35:09 +01:00
|
|
|
|
|
2021-05-17 10:10:44 +02:00
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
2024-05-31 01:23:31 +02:00
|
|
|
|
() => sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, systemUser: null, new (OrganizationUserInvite, string)[] { (invite, null) }));
|
2021-05-12 04:36:23 +02:00
|
|
|
|
Assert.Contains("account does not have permission", exception.Message.ToLowerInvariant());
|
2022-08-29 20:53:16 +02:00
|
|
|
|
}
|
2021-05-17 10:10:44 +02:00
|
|
|
|
|
2021-01-12 17:02:39 +01:00
|
|
|
|
[Theory]
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[OrganizationInviteCustomize(
|
|
|
|
|
InviteeUserType = OrganizationUserType.Admin,
|
|
|
|
|
InvitorUserType = OrganizationUserType.Custom
|
2024-07-26 07:07:33 +02:00
|
|
|
|
), OrganizationCustomize, BitAutoData]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUsers_CustomUserConfiguringAdmin_Throws(Organization organization, OrganizationUserInvite invite,
|
2021-01-12 17:02:39 +01:00
|
|
|
|
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 20:53:16 +02:00
|
|
|
|
{
|
2021-05-17 10:10:44 +02:00
|
|
|
|
invitor.Permissions = JsonSerializer.Serialize(new Permissions() { ManageUsers = true },
|
|
|
|
|
new JsonSerializerOptions
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-05-17 10:10:44 +02:00
|
|
|
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
2021-01-12 17:02:39 +01:00
|
|
|
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
2021-12-16 15:35:09 +01:00
|
|
|
|
|
2021-05-17 10:10:44 +02:00
|
|
|
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
2021-07-01 14:31:05 +02:00
|
|
|
|
currentContext.OrganizationCustom(organization.Id).Returns(true);
|
2021-01-12 17:02:39 +01:00
|
|
|
|
currentContext.ManageUsers(organization.Id).Returns(true);
|
2021-05-17 10:10:44 +02:00
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
2024-05-31 01:23:31 +02:00
|
|
|
|
() => sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, systemUser: null, new (OrganizationUserInvite, string)[] { (invite, null) }));
|
2021-05-17 10:10:44 +02:00
|
|
|
|
Assert.Contains("can not manage admins", exception.Message.ToLowerInvariant());
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
2021-05-17 10:10:44 +02:00
|
|
|
|
|
2022-08-29 22:06:55 +02:00
|
|
|
|
[Theory]
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[OrganizationInviteCustomize(
|
|
|
|
|
InviteeUserType = OrganizationUserType.User,
|
|
|
|
|
InvitorUserType = OrganizationUserType.Owner
|
2024-07-26 07:07:33 +02:00
|
|
|
|
), OrganizationCustomize, BitAutoData]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUsers_NoPermissionsObject_Passes(Organization organization, OrganizationUserInvite invite,
|
2021-05-17 10:10:44 +02:00
|
|
|
|
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-01-13 21:14:28 +01:00
|
|
|
|
invite.Permissions = null;
|
2021-07-08 17:05:32 +02:00
|
|
|
|
invitor.Status = OrganizationUserStatusType.Confirmed;
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
2021-05-17 10:10:44 +02:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
2021-07-01 14:31:05 +02:00
|
|
|
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-05-17 10:10:44 +02:00
|
|
|
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
2021-07-08 17:05:32 +02:00
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new[] { invitor });
|
2023-12-18 17:16:17 +01:00
|
|
|
|
|
|
|
|
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
|
|
|
|
|
2021-05-25 19:23:47 +02:00
|
|
|
|
currentContext.OrganizationOwner(organization.Id).Returns(true);
|
2021-05-17 10:10:44 +02:00
|
|
|
|
currentContext.ManageUsers(organization.Id).Returns(true);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2024-05-31 01:23:31 +02:00
|
|
|
|
await sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, systemUser: null, new (OrganizationUserInvite, string)[] { (invite, null) });
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
2021-12-16 15:35:09 +01:00
|
|
|
|
|
2021-05-17 10:10:44 +02:00
|
|
|
|
[Theory]
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[OrganizationInviteCustomize(
|
|
|
|
|
InviteeUserType = OrganizationUserType.User,
|
|
|
|
|
InvitorUserType = OrganizationUserType.Custom
|
[AC-2646] Remove FC MVP dead code from Core (#4281)
* chore: remove fc refs in CreateGroup and UpdateGroup commands, refs AC-2646
* chore: remove fc refs and update interface to represent usage/get rid of double enumeration warnings, refs AC-2646
* chore: remove org/provider service fc callers, refs AC-2646
* chore: remove collection service fc callers, refs AC-2646
* chore: remove cipher service import ciphers fc callers, refs AC-2646
* fix: UpdateOrganizationUserCommandTests collections to list, refs AC-2646
* fix: update CreateGroupCommandTests, refs AC-2646
* fix: adjust UpdateGroupCommandTests, refs AC-2646
* fix: adjust UpdateOrganizationUserCommandTests for FC always true, refs AC-2646
* fix: update CollectionServiceTests, refs AC-2646
* fix: remove unnecessary test with fc disabled, refs AC-2646
* fix: update tests to account for AccessAll removal and Manager removal, refs AC-2646
* chore: remove dependence on FC flag for tests, refs AC-2646
2024-07-12 19:25:04 +02:00
|
|
|
|
), OrganizationCustomize, BitAutoData]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUser_Passes(Organization organization, OrganizationUserInvite invite, string externalId,
|
2021-07-08 17:05:32 +02:00
|
|
|
|
OrganizationUser invitor,
|
2021-05-17 10:10:44 +02:00
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2024-05-31 01:23:31 +02:00
|
|
|
|
// This method is only used to invite 1 user at a time
|
|
|
|
|
invite.Emails = new[] { invite.Emails.First() };
|
|
|
|
|
|
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
|
|
|
|
// Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks
|
|
|
|
|
sutProvider.SetDependency(_orgUserInviteTokenDataFactory, "orgUserInviteTokenDataFactory");
|
|
|
|
|
sutProvider.Create();
|
|
|
|
|
|
2024-05-31 01:23:31 +02:00
|
|
|
|
InviteUser_ArrangeCurrentContextPermissions(organization, sutProvider);
|
2021-12-16 15:35:09 +01:00
|
|
|
|
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
2021-12-16 15:35:09 +01:00
|
|
|
|
|
2021-05-17 10:10:44 +02:00
|
|
|
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
2021-05-25 19:23:47 +02:00
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new[] { owner });
|
2024-05-31 01:23:31 +02:00
|
|
|
|
|
|
|
|
|
// Mock tokenable factory to return a token that expires in 5 days
|
|
|
|
|
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
|
|
|
|
.CreateToken(Arg.Any<OrganizationUser>())
|
|
|
|
|
.Returns(
|
|
|
|
|
info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
|
|
|
|
{
|
|
|
|
|
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
|
|
|
|
SetupOrgUserRepositoryCreateAsyncMock(organizationUserRepository);
|
|
|
|
|
|
|
|
|
|
await sutProvider.Sut.InviteUserAsync(organization.Id, invitor.UserId, systemUser: null, invite, externalId);
|
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IMailService>().Received(1)
|
|
|
|
|
.SendOrganizationInviteEmailsAsync(Arg.Is<OrganizationInvitesInfo>(info =>
|
|
|
|
|
info.OrgUserTokenPairs.Count() == 1 &&
|
|
|
|
|
info.IsFreeOrg == (organization.PlanType == PlanType.Free) &&
|
|
|
|
|
info.OrganizationName == organization.Name));
|
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[OrganizationInviteCustomize(
|
|
|
|
|
InviteeUserType = OrganizationUserType.User,
|
|
|
|
|
InvitorUserType = OrganizationUserType.Custom
|
2024-07-26 07:07:33 +02:00
|
|
|
|
), OrganizationCustomize, BitAutoData]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUser_InvitingMoreThanOneUser_Throws(Organization organization, OrganizationUserInvite invite, string externalId,
|
|
|
|
|
OrganizationUser invitor,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.InviteUserAsync(organization.Id, invitor.UserId, systemUser: null, invite, externalId));
|
|
|
|
|
Assert.Contains("This method can only be used to invite a single user.", exception.Message);
|
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IMailService>().DidNotReceiveWithAnyArgs()
|
|
|
|
|
.SendOrganizationInviteEmailsAsync(default);
|
|
|
|
|
await sutProvider.GetDependency<IEventService>().DidNotReceive()
|
|
|
|
|
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, EventSystemUser, DateTime?)>>());
|
|
|
|
|
await sutProvider.GetDependency<IEventService>().DidNotReceive()
|
|
|
|
|
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[OrganizationInviteCustomize(
|
|
|
|
|
InviteeUserType = OrganizationUserType.User,
|
|
|
|
|
InvitorUserType = OrganizationUserType.Custom
|
2024-07-26 07:07:33 +02:00
|
|
|
|
), OrganizationCustomize, BitAutoData]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUser_UserAlreadyInvited_Throws(Organization organization, OrganizationUserInvite invite, string externalId,
|
|
|
|
|
OrganizationUser invitor,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
// This method is only used to invite 1 user at a time
|
|
|
|
|
invite.Emails = new[] { invite.Emails.First() };
|
|
|
|
|
|
|
|
|
|
// The user has already been invited
|
|
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
|
|
|
.SelectKnownEmailsAsync(organization.Id, Arg.Any<IEnumerable<string>>(), false)
|
|
|
|
|
.Returns(new List<string> { invite.Emails.First() });
|
|
|
|
|
|
|
|
|
|
// Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks
|
|
|
|
|
sutProvider.SetDependency(_orgUserInviteTokenDataFactory, "orgUserInviteTokenDataFactory");
|
|
|
|
|
sutProvider.Create();
|
|
|
|
|
|
|
|
|
|
InviteUser_ArrangeCurrentContextPermissions(organization, sutProvider);
|
|
|
|
|
|
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
|
|
|
|
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new[] { owner });
|
|
|
|
|
|
|
|
|
|
// Mock tokenable factory to return a token that expires in 5 days
|
|
|
|
|
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
|
|
|
|
.CreateToken(Arg.Any<OrganizationUser>())
|
|
|
|
|
.Returns(
|
|
|
|
|
info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
|
|
|
|
{
|
|
|
|
|
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
|
|
|
|
SetupOrgUserRepositoryCreateAsyncMock(organizationUserRepository);
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut
|
|
|
|
|
.InviteUserAsync(organization.Id, invitor.UserId, systemUser: null, invite, externalId));
|
|
|
|
|
Assert.Contains("This user has already been invited", exception.Message);
|
|
|
|
|
|
|
|
|
|
// MailService and EventService are still called, but with no OrgUsers
|
|
|
|
|
await sutProvider.GetDependency<IMailService>().Received(1)
|
|
|
|
|
.SendOrganizationInviteEmailsAsync(Arg.Is<OrganizationInvitesInfo>(info =>
|
|
|
|
|
!info.OrgUserTokenPairs.Any() &&
|
|
|
|
|
info.IsFreeOrg == (organization.PlanType == PlanType.Free) &&
|
|
|
|
|
info.OrganizationName == organization.Name));
|
|
|
|
|
await sutProvider.GetDependency<IEventService>().Received(1)
|
|
|
|
|
.LogOrganizationUserEventsAsync(Arg.Is<IEnumerable<(OrganizationUser, EventType, DateTime?)>>(events => !events.Any()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void InviteUser_ArrangeCurrentContextPermissions(Organization organization, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
2021-05-25 19:23:47 +02:00
|
|
|
|
currentContext.ManageUsers(organization.Id).Returns(true);
|
2023-05-17 15:17:37 +02:00
|
|
|
|
currentContext.AccessReports(organization.Id).Returns(true);
|
|
|
|
|
currentContext.ManageGroups(organization.Id).Returns(true);
|
|
|
|
|
currentContext.ManagePolicies(organization.Id).Returns(true);
|
|
|
|
|
currentContext.ManageScim(organization.Id).Returns(true);
|
|
|
|
|
currentContext.ManageSso(organization.Id).Returns(true);
|
|
|
|
|
currentContext.AccessEventLogs(organization.Id).Returns(true);
|
|
|
|
|
currentContext.AccessImportExport(organization.Id).Returns(true);
|
|
|
|
|
currentContext.EditAnyCollection(organization.Id).Returns(true);
|
|
|
|
|
currentContext.ManageResetPassword(organization.Id).Returns(true);
|
[AC-1373] Flexible Collections (#3245)
* [AC-1117] Add manage permission (#3126)
* Update sql files to add Manage permission
* Add migration script
* Rename collection manage migration file to remove duplicate migration date
* Migrations
* Add manage to models
* Add manage to repository
* Add constraint to Manage columns
* Migration lint fixes
* Add manage to OrganizationUserUserDetails_ReadWithCollectionsById
* Add missing manage fields
* Add 'Manage' to UserCollectionDetails
* Use CREATE OR ALTER where possible
* [AC-1374] Limit collection creation/deletion to Owner/Admin (#3145)
* feat: update org table with new column, write migration, refs AC-1374
* feat: update views with new column, refs AC-1374
* feat: Alter sprocs (org create/update) to include new column, refs AC-1374
* feat: update entity/data/request/response models to handle new column, refs AC-1374
* feat: update necessary Provider related views during migration, refs AC-1374
* fix: update org create to default new column to false, refs AC-1374
* feat: added new API/request model for collection management and removed property from update request model, refs AC-1374
* fix: renamed migration script to be after secrets manage beta column changes, refs AC-1374
* fix: dotnet format, refs AC-1374
* feat: add ef migrations to reflect mssql changes, refs AC-1374
* fix: dotnet format, refs AC-1374
* feat: update API signature to accept Guid and explain Cd verbiage, refs AC-1374
* fix: merge conflict resolution
* [AC-1174] CollectionUser and CollectionGroup authorization handlers (#3194)
* [AC-1174] Introduce BulkAuthorizationHandler.cs
* [AC-1174] Introduce CollectionUserAuthorizationHandler
* [AC-1174] Add CreateForNewCollection CollectionUser requirement
* [AC-1174] Add some more details to CollectionCustomization
* [AC-1174] Formatting
* [AC-1174] Add CollectionGroupOperation.cs
* [AC-1174] Introduce CollectionGroupAuthorizationHandler.cs
* [AC-1174] Cleanup CollectionFixture customization
Implement and use re-usable extension method to support seeded Guids
* [AC-1174] Introduce WithValueFromList AutoFixtureExtensions
Modify CollectionCustomization to use multiple organization Ids for auto generated test data
* [AC-1174] Simplify CollectionUserAuthorizationHandler.cs
Modify the authorization handler to only perform authorization logic. Validation logic will need to be handled by any calling commands/controllers instead.
* [AC-1174] Introduce shared CollectionAccessAuthorizationHandlerBase
A shared base authorization handler was created for both CollectionUser and CollectionGroup resources, as they share the same underlying management authorization logic.
* [AC-1174] Update CollectionUserAuthorizationHandler and CollectionGroupAuthorizationHandler to use the new CollectionAccessAuthorizationHandlerBase class
* [AC-1174] Formatting
* [AC-1174] Cleanup typo and redundant ToList() call
* [AC-1174] Add check for provider users
* [AC-1174] Reduce nested loops
* [AC-1174] Introduce ICollectionAccess.cs
* [AC-1174] Remove individual CollectionGroup and CollectionUser auth handlers and use base class instead
* [AC-1174] Tweak unit test to fail minimally
* [AC-1174] Reorganize authorization handlers in Core project
* [AC-1174] Introduce new AddCoreAuthorizationHandlers() extension method
* [AC-1174] Move CollectionAccessAuthorizationHandler into Api project
* [AC-1174] Move CollectionFixture to Vault folder
* [AC-1174] Rename operation to CreateUpdateDelete
* [AC-1174] Require single organization for collection access authorization handler
- Add requirement that all target collections must belong to the same organization
- Simplify logic related to multiple organizations
- Update tests and helpers
- Use ToHashSet to improve lookup time
* [AC-1174] Fix null reference exception
* [AC-1174] Throw bad request exception when collections belong to different organizations
* [AC-1174] Switch to CollectionAuthorizationHandler instead of CollectionAccessAuthorizationHandler to reduce complexity
* Fix improper merge conflict resolution
* fix: add permission check for collection management api, refs AC-1647 (#3252)
* [AC-1125] Enforce org setting for creating/deleting collections (#3241)
* [AC-1117] Add manage permission (#3126)
* Update sql files to add Manage permission
* Add migration script
* Rename collection manage migration file to remove duplicate migration date
* Migrations
* Add manage to models
* Add manage to repository
* Add constraint to Manage columns
* Migration lint fixes
* Add manage to OrganizationUserUserDetails_ReadWithCollectionsById
* Add missing manage fields
* Add 'Manage' to UserCollectionDetails
* Use CREATE OR ALTER where possible
* [AC-1374] Limit collection creation/deletion to Owner/Admin (#3145)
* feat: update org table with new column, write migration, refs AC-1374
* feat: update views with new column, refs AC-1374
* feat: Alter sprocs (org create/update) to include new column, refs AC-1374
* feat: update entity/data/request/response models to handle new column, refs AC-1374
* feat: update necessary Provider related views during migration, refs AC-1374
* fix: update org create to default new column to false, refs AC-1374
* feat: added new API/request model for collection management and removed property from update request model, refs AC-1374
* fix: renamed migration script to be after secrets manage beta column changes, refs AC-1374
* fix: dotnet format, refs AC-1374
* feat: add ef migrations to reflect mssql changes, refs AC-1374
* fix: dotnet format, refs AC-1374
* feat: update API signature to accept Guid and explain Cd verbiage, refs AC-1374
* feat: created collection auth handler/operations, added LimitCollectionCdOwnerAdmin to CurrentContentOrganization, refs AC-1125
* feat: create vault service collection extensions and register with base services, refs AC-1125
* feat: deprecated CurrentContext.CreateNewCollections, refs AC-1125
* feat: deprecate DeleteAnyCollection for single resource usages, refs AC-1125
* feat: move service registration to api, update references, refs AC-1125
* feat: add bulk delete authorization handler, refs AC-1125
* feat: always assign user and give manage access on create, refs AC-1125
* fix: updated CurrentContextOrganization type, refs AC-1125
* feat: combined existing collection authorization handlers/operations, refs AC-1125
* fix: OrganizationServiceTests -> CurrentContentOrganization typo, refs AC-1125
* fix: format, refs AC-1125
* fix: update collection controller tests, refs AC-1125
* fix: dotnet format, refs AC-1125
* feat: removed extra BulkAuthorizationHandler, refs AC-1125
* fix: dotnet format, refs AC-1125
* fix: change string to guid for org id, update bulk delete request model, refs AC-1125
* fix: remove delete many collection check, refs AC-1125
* fix: clean up collection auth handler, refs AC-1125
* fix: format fix for CollectionOperations, refs AC-1125
* fix: removed unnecessary owner check, add org null check to custom permission validation, refs AC-1125
* fix: remove unused methods in CurrentContext, refs AC-1125
* fix: removed obsolete test, fixed failling delete many test, refs AC-1125
* fix: CollectionAuthorizationHandlerTests fixes, refs AC-1125
* fix: OrganizationServiceTests fix broken test by mocking GetOrganization, refs AC-1125
* fix: CollectionAuthorizationHandler - remove unused repository, refs AC-1125
* feat: moved UserId null check to common method, refs AC-1125
* fix: updated auth handler tests to remove dependency on requirement for common code checks, refs AC-1125
* feat: updated conditionals/comments for create/delete methods within colleciton auth handler, refs AC-1125
* feat: added create/delete collection auth handler success methods, refs AC-1125
* fix: new up permissions to prevent excessive null checks, refs AC-1125
* fix: remove old reference to CreateNewCollections, refs AC-1125
* fix: typo within ViewAssignedCollections method, refs AC-1125
---------
Co-authored-by: Robyn MacCallum <robyntmaccallum@gmail.com>
* refactor: remove organizationId from CollectionBulkDeleteRequestModel, refs AC-1649 (#3282)
* [AC-1174] Bulk Collection Management (#3229)
* [AC-1174] Update SelectionReadOnlyRequestModel to use Guid for Id property
* [AC-1174] Introduce initial bulk-access collection endpoint
* [AC-1174] Introduce BulkAddCollectionAccessCommand and validation logic/tests
* [AC-1174] Add CreateOrUpdateAccessMany method to CollectionRepository
* [AC-1174] Add event logs for bulk add collection access command
* [AC-1174] Add User_BumpAccountRevisionDateByCollectionIds and database migration script
* [AC-1174] Implement EF repository method
* [AC-1174] Improve null checks
* [AC-1174] Remove unnecessary BulkCollectionAccessRequestModel helpers
* [AC-1174] Add unit tests for new controller endpoint
* [AC-1174] Fix formatting
* [AC-1174] Remove comment
* [AC-1174] Remove redundant organizationId parameter
* [AC-1174] Ensure user and group Ids are distinct
* [AC-1174] Cleanup tests based on PR feedback
* [AC-1174] Formatting
* [AC-1174] Update CollectionGroup alias in the sproc
* [AC-1174] Add some additional comments to SQL sproc
* [AC-1174] Add comment explaining additional SaveChangesAsync call
---------
Co-authored-by: Thomas Rittson <trittson@bitwarden.com>
* [AC-1646] Rename LimitCollectionCdOwnerAdmin column (#3300)
* Rename LimitCollectionCdOwnerAdmin -> LimitCollectionCreationDeletion
* Rename and bump migration script
* [AC-1666] Removed EditAnyCollection from Create/Delete permission checks (#3301)
* fix: remove EditAnyCollection from Create/Delete permission check, refs AC-1666
* fix: updated comment, refs AC-1666
* [AC-1669] Bug - Remove obsolete assignUserId from CollectionService.SaveAsync(...) (#3312)
* fix: remove AssignUserId from CollectionService.SaveAsync, refs AC-1669
* fix: add manage access conditional before creating collection, refs AC-1669
* fix: move access logic for create/update, fix all tests, refs AC-1669
* fix: add CollectionAccessSelection fixture, update tests, update bad reqeuest message, refs AC-1669
* fix: format, refs AC-1669
* fix: update null params with specific arg.is null checks, refs Ac-1669
* fix: update attribute class name, refs AC-1669
* [AC-1713] [Flexible collections] Add feature flags to server (#3334)
* Add feature flags for FlexibleCollections and BulkCollectionAccess
* Flag new routes and behaviour
---------
Co-authored-by: Rui Tomé <108268980+r-tome@users.noreply.github.com>
* Add joint codeownership for auth handlers (#3346)
* [AC-1717] Update default values for LimitCollectionCreationDeletion (#3365)
* Change default value in organization create sproc to 1
* Drop old column name still present in some QA instances
* Set LimitCollectionCreationDeletion value in code based on feature flag
* Fix: add missing namespace after merging in master
* Fix: add missing namespace after merging in master
* [AC-1683] Fix DB migrations for new Manage permission (#3307)
* [AC-1683] Update migration script and introduce V2 procedures and types
* [AC-1683] Update repository calls to use new V2 procedures / types
* [AC-1684] Update bulk add collection migration script to use new V2 type
* [AC-1683] Undo Manage changes to more original procedures
* [AC-1683] Restore whitespace changes
* [AC-1683] Clarify comments regarding explicit column lists
* [AC-1683] Update migration script dates
* [AC-1683] Split the migration script for readability
* [AC-1683] Re-name SelectReadOnlyArray_V2 to CollectionAccessSelectionType
* [AC-1648] [Flexible Collections] Bump migration scripts before feature branch merge (#3371)
* Bump dates on sql migration scripts
* Bump date on ef migrations
---------
Co-authored-by: Robyn MacCallum <robyntmaccallum@gmail.com>
Co-authored-by: Vincent Salucci <26154748+vincentsalucci@users.noreply.github.com>
Co-authored-by: Vincent Salucci <vincesalucci21@gmail.com>
Co-authored-by: Shane Melton <smelton@bitwarden.com>
Co-authored-by: Rui Tomé <108268980+r-tome@users.noreply.github.com>
2023-11-01 10:30:52 +01:00
|
|
|
|
currentContext.GetOrganization(organization.Id)
|
|
|
|
|
.Returns(new CurrentContextOrganization()
|
|
|
|
|
{
|
|
|
|
|
Permissions = new Permissions
|
|
|
|
|
{
|
|
|
|
|
CreateNewCollections = true,
|
|
|
|
|
DeleteAnyCollection = true
|
|
|
|
|
}
|
|
|
|
|
});
|
2024-05-31 01:23:31 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[OrganizationInviteCustomize(
|
|
|
|
|
InviteeUserType = OrganizationUserType.User,
|
|
|
|
|
InvitorUserType = OrganizationUserType.Custom
|
2024-07-26 07:07:33 +02:00
|
|
|
|
), OrganizationCustomize, BitAutoData]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUsers_Passes(Organization organization, IEnumerable<(OrganizationUserInvite invite, string externalId)> invites,
|
|
|
|
|
OrganizationUser invitor,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
// Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks
|
|
|
|
|
sutProvider.SetDependency(_orgUserInviteTokenDataFactory, "orgUserInviteTokenDataFactory");
|
|
|
|
|
sutProvider.Create();
|
|
|
|
|
|
|
|
|
|
InviteUser_ArrangeCurrentContextPermissions(organization, sutProvider);
|
|
|
|
|
|
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
|
|
|
|
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new[] { owner });
|
2021-12-16 15:35:09 +01:00
|
|
|
|
|
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
|
|
|
|
// Mock tokenable factory to return a token that expires in 5 days
|
|
|
|
|
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
|
|
|
|
.CreateToken(Arg.Any<OrganizationUser>())
|
|
|
|
|
.Returns(
|
|
|
|
|
info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
|
|
|
|
{
|
|
|
|
|
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2023-12-18 17:16:17 +01:00
|
|
|
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
|
|
|
|
SetupOrgUserRepositoryCreateAsyncMock(organizationUserRepository);
|
|
|
|
|
|
2024-05-31 01:23:31 +02:00
|
|
|
|
await sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, systemUser: null, invites);
|
2021-12-16 15:35:09 +01:00
|
|
|
|
|
2021-05-25 19:23:47 +02:00
|
|
|
|
await sutProvider.GetDependency<IMailService>().Received(1)
|
2023-12-18 17:16:17 +01:00
|
|
|
|
.SendOrganizationInviteEmailsAsync(Arg.Is<OrganizationInvitesInfo>(info =>
|
|
|
|
|
info.OrgUserTokenPairs.Count() == invites.SelectMany(i => i.invite.Emails).Count() &&
|
|
|
|
|
info.IsFreeOrg == (organization.PlanType == PlanType.Free) &&
|
|
|
|
|
info.OrganizationName == organization.Name));
|
2022-11-09 13:13:29 +01:00
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[OrganizationInviteCustomize(
|
|
|
|
|
InviteeUserType = OrganizationUserType.User,
|
|
|
|
|
InvitorUserType = OrganizationUserType.Custom
|
2024-07-26 07:07:33 +02:00
|
|
|
|
), OrganizationCustomize, BitAutoData]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUsers_WithEventSystemUser_Passes(Organization organization, EventSystemUser eventSystemUser, IEnumerable<(OrganizationUserInvite invite, string externalId)> invites,
|
2022-11-09 13:13:29 +01:00
|
|
|
|
OrganizationUser invitor,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
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
|
|
|
|
// Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks
|
|
|
|
|
sutProvider.SetDependency(_orgUserInviteTokenDataFactory, "orgUserInviteTokenDataFactory");
|
|
|
|
|
sutProvider.Create();
|
|
|
|
|
|
2022-11-09 13:13:29 +01:00
|
|
|
|
invitor.Permissions = JsonSerializer.Serialize(new Permissions() { ManageUsers = true },
|
|
|
|
|
new JsonSerializerOptions
|
|
|
|
|
{
|
|
|
|
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
|
|
|
|
|
|
|
|
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new[] { owner });
|
2023-12-18 17:16:17 +01:00
|
|
|
|
|
|
|
|
|
SetupOrgUserRepositoryCreateAsyncMock(organizationUserRepository);
|
|
|
|
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
|
|
|
|
|
2022-11-09 13:13:29 +01:00
|
|
|
|
currentContext.ManageUsers(organization.Id).Returns(true);
|
|
|
|
|
|
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
|
|
|
|
// Mock tokenable factory to return a token that expires in 5 days
|
|
|
|
|
sutProvider.GetDependency<IOrgUserInviteTokenableFactory>()
|
|
|
|
|
.CreateToken(Arg.Any<OrganizationUser>())
|
|
|
|
|
.Returns(
|
|
|
|
|
info => new OrgUserInviteTokenable(info.Arg<OrganizationUser>())
|
|
|
|
|
{
|
|
|
|
|
ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5))
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2024-05-31 01:23:31 +02:00
|
|
|
|
await sutProvider.Sut.InviteUsersAsync(organization.Id, invitingUserId: null, eventSystemUser, invites);
|
2022-11-09 13:13:29 +01:00
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IMailService>().Received(1)
|
2023-12-18 17:16:17 +01:00
|
|
|
|
.SendOrganizationInviteEmailsAsync(Arg.Is<OrganizationInvitesInfo>(info =>
|
|
|
|
|
info.OrgUserTokenPairs.Count() == invites.SelectMany(i => i.invite.Emails).Count() &&
|
|
|
|
|
info.IsFreeOrg == (organization.PlanType == PlanType.Free) &&
|
|
|
|
|
info.OrganizationName == organization.Name));
|
2022-11-09 13:13:29 +01:00
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, EventSystemUser, DateTime?)>>());
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
2021-05-17 10:10:44 +02:00
|
|
|
|
|
2024-07-26 07:07:33 +02:00
|
|
|
|
[Theory, BitAutoData, OrganizationCustomize, OrganizationInviteCustomize]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUsers_WithSecretsManager_Passes(Organization organization,
|
2023-08-04 23:51:12 +02:00
|
|
|
|
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites,
|
2023-09-04 22:32:47 +02:00
|
|
|
|
OrganizationUser savingUser, SutProvider<OrganizationService> sutProvider)
|
2023-08-04 23:51:12 +02:00
|
|
|
|
{
|
2023-08-28 09:01:00 +02:00
|
|
|
|
organization.PlanType = PlanType.EnterpriseAnnually;
|
2023-08-04 23:51:12 +02:00
|
|
|
|
InviteUserHelper_ArrangeValidPermissions(organization, savingUser, sutProvider);
|
|
|
|
|
|
|
|
|
|
// Set up some invites to grant access to SM
|
|
|
|
|
invites.First().invite.AccessSecretsManager = true;
|
|
|
|
|
var invitedSmUsers = invites.First().invite.Emails.Count();
|
2023-09-04 22:32:47 +02:00
|
|
|
|
foreach (var (invite, externalId) in invites.Skip(1))
|
|
|
|
|
{
|
|
|
|
|
invite.AccessSecretsManager = false;
|
|
|
|
|
}
|
2023-08-04 23:51:12 +02:00
|
|
|
|
|
|
|
|
|
// Assume we need to add seats for all invited SM users
|
|
|
|
|
sutProvider.GetDependency<ICountNewSmSeatsRequiredQuery>()
|
|
|
|
|
.CountNewSmSeatsRequiredAsync(organization.Id, invitedSmUsers).Returns(invitedSmUsers);
|
|
|
|
|
|
2023-12-18 17:16:17 +01:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository);
|
|
|
|
|
SetupOrgUserRepositoryCreateAsyncMock(organizationUserRepository);
|
|
|
|
|
|
2024-05-31 01:23:31 +02:00
|
|
|
|
await sutProvider.Sut.InviteUsersAsync(organization.Id, savingUser.Id, systemUser: null, invites);
|
2023-08-04 23:51:12 +02:00
|
|
|
|
|
|
|
|
|
sutProvider.GetDependency<IUpdateSecretsManagerSubscriptionCommand>().Received(1)
|
|
|
|
|
.UpdateSubscriptionAsync(Arg.Is<SecretsManagerSubscriptionUpdate>(update =>
|
|
|
|
|
update.SmSeats == organization.SmSeats + invitedSmUsers &&
|
|
|
|
|
!update.SmServiceAccountsChanged &&
|
|
|
|
|
!update.MaxAutoscaleSmSeatsChanged &&
|
|
|
|
|
!update.MaxAutoscaleSmSeatsChanged));
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-26 07:07:33 +02:00
|
|
|
|
[Theory, BitAutoData, OrganizationCustomize, OrganizationInviteCustomize]
|
2024-05-31 01:23:31 +02:00
|
|
|
|
public async Task InviteUsers_WithSecretsManager_WhenErrorIsThrown_RevertsAutoscaling(Organization organization,
|
2023-08-04 23:51:12 +02:00
|
|
|
|
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites,
|
2023-09-04 22:32:47 +02:00
|
|
|
|
OrganizationUser savingUser, SutProvider<OrganizationService> sutProvider)
|
2023-08-04 23:51:12 +02:00
|
|
|
|
{
|
|
|
|
|
var initialSmSeats = organization.SmSeats;
|
|
|
|
|
InviteUserHelper_ArrangeValidPermissions(organization, savingUser, sutProvider);
|
|
|
|
|
|
|
|
|
|
// Set up some invites to grant access to SM
|
|
|
|
|
invites.First().invite.AccessSecretsManager = true;
|
|
|
|
|
var invitedSmUsers = invites.First().invite.Emails.Count();
|
2023-09-04 22:32:47 +02:00
|
|
|
|
foreach (var (invite, externalId) in invites.Skip(1))
|
|
|
|
|
{
|
|
|
|
|
invite.AccessSecretsManager = false;
|
|
|
|
|
}
|
2023-08-04 23:51:12 +02:00
|
|
|
|
|
|
|
|
|
// Assume we need to add seats for all invited SM users
|
|
|
|
|
sutProvider.GetDependency<ICountNewSmSeatsRequiredQuery>()
|
|
|
|
|
.CountNewSmSeatsRequiredAsync(organization.Id, invitedSmUsers).Returns(invitedSmUsers);
|
|
|
|
|
|
|
|
|
|
// Mock SecretsManagerSubscriptionUpdateCommand to actually change the organization's subscription in memory
|
|
|
|
|
sutProvider.GetDependency<IUpdateSecretsManagerSubscriptionCommand>()
|
|
|
|
|
.UpdateSubscriptionAsync(Arg.Any<SecretsManagerSubscriptionUpdate>())
|
|
|
|
|
.ReturnsForAnyArgs(Task.FromResult(0)).AndDoes(x => organization.SmSeats += invitedSmUsers);
|
|
|
|
|
|
|
|
|
|
// Throw error at the end of the try block
|
[AC-2646] Remove FC MVP dead code from Core (#4281)
* chore: remove fc refs in CreateGroup and UpdateGroup commands, refs AC-2646
* chore: remove fc refs and update interface to represent usage/get rid of double enumeration warnings, refs AC-2646
* chore: remove org/provider service fc callers, refs AC-2646
* chore: remove collection service fc callers, refs AC-2646
* chore: remove cipher service import ciphers fc callers, refs AC-2646
* fix: UpdateOrganizationUserCommandTests collections to list, refs AC-2646
* fix: update CreateGroupCommandTests, refs AC-2646
* fix: adjust UpdateGroupCommandTests, refs AC-2646
* fix: adjust UpdateOrganizationUserCommandTests for FC always true, refs AC-2646
* fix: update CollectionServiceTests, refs AC-2646
* fix: remove unnecessary test with fc disabled, refs AC-2646
* fix: update tests to account for AccessAll removal and Manager removal, refs AC-2646
* chore: remove dependence on FC flag for tests, refs AC-2646
2024-07-12 19:25:04 +02:00
|
|
|
|
sutProvider.GetDependency<IReferenceEventService>().RaiseEventAsync(default)
|
|
|
|
|
.ThrowsForAnyArgs<BadRequestException>();
|
2023-08-04 23:51:12 +02:00
|
|
|
|
|
[AC-2646] Remove FC MVP dead code from Core (#4281)
* chore: remove fc refs in CreateGroup and UpdateGroup commands, refs AC-2646
* chore: remove fc refs and update interface to represent usage/get rid of double enumeration warnings, refs AC-2646
* chore: remove org/provider service fc callers, refs AC-2646
* chore: remove collection service fc callers, refs AC-2646
* chore: remove cipher service import ciphers fc callers, refs AC-2646
* fix: UpdateOrganizationUserCommandTests collections to list, refs AC-2646
* fix: update CreateGroupCommandTests, refs AC-2646
* fix: adjust UpdateGroupCommandTests, refs AC-2646
* fix: adjust UpdateOrganizationUserCommandTests for FC always true, refs AC-2646
* fix: update CollectionServiceTests, refs AC-2646
* fix: remove unnecessary test with fc disabled, refs AC-2646
* fix: update tests to account for AccessAll removal and Manager removal, refs AC-2646
* chore: remove dependence on FC flag for tests, refs AC-2646
2024-07-12 19:25:04 +02:00
|
|
|
|
await Assert.ThrowsAsync<AggregateException>(async () =>
|
|
|
|
|
await sutProvider.Sut.InviteUsersAsync(organization.Id, savingUser.Id, systemUser: null, invites));
|
2023-08-04 23:51:12 +02:00
|
|
|
|
|
|
|
|
|
// OrgUser is reverted
|
|
|
|
|
// Note: we don't know what their guids are so comparing length is the best we can do
|
|
|
|
|
var invitedEmails = invites.SelectMany(i => i.invite.Emails);
|
|
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).DeleteManyAsync(
|
|
|
|
|
Arg.Is<IEnumerable<Guid>>(ids => ids.Count() == invitedEmails.Count()));
|
|
|
|
|
|
|
|
|
|
Received.InOrder(() =>
|
|
|
|
|
{
|
|
|
|
|
// Initial autoscaling
|
|
|
|
|
sutProvider.GetDependency<IUpdateSecretsManagerSubscriptionCommand>()
|
|
|
|
|
.UpdateSubscriptionAsync(Arg.Is<SecretsManagerSubscriptionUpdate>(update =>
|
|
|
|
|
update.SmSeats == initialSmSeats + invitedSmUsers &&
|
|
|
|
|
!update.SmServiceAccountsChanged &&
|
|
|
|
|
!update.MaxAutoscaleSmSeatsChanged &&
|
|
|
|
|
!update.MaxAutoscaleSmSeatsChanged));
|
|
|
|
|
|
|
|
|
|
// Revert autoscaling
|
|
|
|
|
sutProvider.GetDependency<IUpdateSecretsManagerSubscriptionCommand>()
|
|
|
|
|
.UpdateSubscriptionAsync(Arg.Is<SecretsManagerSubscriptionUpdate>(update =>
|
|
|
|
|
update.SmSeats == initialSmSeats &&
|
|
|
|
|
!update.SmServiceAccountsChanged &&
|
|
|
|
|
!update.MaxAutoscaleSmSeatsChanged &&
|
|
|
|
|
!update.MaxAutoscaleSmSeatsChanged));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void InviteUserHelper_ArrangeValidPermissions(Organization organization, OrganizationUser savingUser,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(organization.Id).Returns(true);
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2021-05-17 10:10:44 +02:00
|
|
|
|
public async Task DeleteUser_InvalidUser(OrganizationUser organizationUser, OrganizationUser deletingUser,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-05-17 10:10:44 +02:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
|
|
|
|
|
organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser);
|
2021-12-16 15:35:09 +01:00
|
|
|
|
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.DeleteUserAsync(Guid.NewGuid(), organizationUser.Id, deletingUser.UserId));
|
|
|
|
|
Assert.Contains("User not valid.", exception.Message);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
2021-05-25 19:23:47 +02:00
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2021-05-25 19:23:47 +02:00
|
|
|
|
public async Task DeleteUser_RemoveYourself(OrganizationUser deletingUser, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
|
|
|
|
|
organizationUserRepository.GetByIdAsync(deletingUser.Id).Returns(deletingUser);
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.DeleteUserAsync(deletingUser.OrganizationId, deletingUser.Id, deletingUser.UserId));
|
|
|
|
|
Assert.Contains("You cannot remove yourself.", exception.Message);
|
2022-08-29 20:53:16 +02:00
|
|
|
|
}
|
2021-05-25 19:23:47 +02:00
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2021-05-25 19:23:47 +02:00
|
|
|
|
public async Task DeleteUser_NonOwnerRemoveOwner(
|
|
|
|
|
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser organizationUser,
|
|
|
|
|
[OrganizationUser(type: OrganizationUserType.Admin)] OrganizationUser deletingUser,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
2022-08-29 20:53:16 +02:00
|
|
|
|
{
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
2021-07-01 14:31:05 +02:00
|
|
|
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
2021-05-25 19:23:47 +02:00
|
|
|
|
|
|
|
|
|
organizationUser.OrganizationId = deletingUser.OrganizationId;
|
|
|
|
|
organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser);
|
|
|
|
|
currentContext.OrganizationAdmin(deletingUser.OrganizationId).Returns(true);
|
2021-08-10 18:16:10 +02:00
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.DeleteUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId));
|
2021-05-25 19:23:47 +02:00
|
|
|
|
Assert.Contains("Only owners can delete other owners.", exception.Message);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2021-08-10 18:16:10 +02:00
|
|
|
|
public async Task DeleteUser_LastOwner(
|
|
|
|
|
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser organizationUser,
|
|
|
|
|
OrganizationUser deletingUser,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
|
2021-05-17 10:10:44 +02:00
|
|
|
|
organizationUser.OrganizationId = deletingUser.OrganizationId;
|
2021-05-25 19:23:47 +02:00
|
|
|
|
organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser);
|
2021-05-17 10:10:44 +02:00
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(deletingUser.OrganizationId, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new[] { organizationUser });
|
2021-08-10 18:16:10 +02:00
|
|
|
|
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.DeleteUserAsync(deletingUser.OrganizationId, organizationUser.Id, null));
|
|
|
|
|
Assert.Contains("Organization must have at least one confirmed owner.", exception.Message);
|
|
|
|
|
}
|
2021-12-16 15:35:09 +01:00
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2021-05-25 19:23:47 +02:00
|
|
|
|
public async Task DeleteUser_Success(
|
|
|
|
|
OrganizationUser organizationUser,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser deletingUser,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
2021-12-16 15:35:09 +01:00
|
|
|
|
|
2021-05-25 19:23:47 +02:00
|
|
|
|
organizationUser.OrganizationId = deletingUser.OrganizationId;
|
|
|
|
|
organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser);
|
|
|
|
|
organizationUserRepository.GetByIdAsync(deletingUser.Id).Returns(deletingUser);
|
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(deletingUser.OrganizationId, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new[] { deletingUser, organizationUser });
|
|
|
|
|
currentContext.OrganizationOwner(deletingUser.OrganizationId).Returns(true);
|
2021-12-16 15:35:09 +01:00
|
|
|
|
|
2021-05-25 19:23:47 +02:00
|
|
|
|
await sutProvider.Sut.DeleteUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId);
|
2022-11-09 13:13:29 +01:00
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task DeleteUser_WithEventSystemUser_Success(
|
|
|
|
|
OrganizationUser organizationUser,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser deletingUser, EventSystemUser eventSystemUser,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
|
|
|
|
|
|
|
|
organizationUser.OrganizationId = deletingUser.OrganizationId;
|
|
|
|
|
organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser);
|
|
|
|
|
organizationUserRepository.GetByIdAsync(deletingUser.Id).Returns(deletingUser);
|
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(deletingUser.OrganizationId, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new[] { deletingUser, organizationUser });
|
|
|
|
|
currentContext.OrganizationOwner(deletingUser.OrganizationId).Returns(true);
|
|
|
|
|
|
|
|
|
|
await sutProvider.Sut.DeleteUserAsync(deletingUser.OrganizationId, organizationUser.Id, eventSystemUser);
|
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed, eventSystemUser);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
2021-07-07 17:08:18 +02:00
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2021-05-17 10:10:44 +02:00
|
|
|
|
public async Task DeleteUsers_FilterInvalid(OrganizationUser organizationUser, OrganizationUser deletingUser,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-05-17 10:10:44 +02:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var organizationUsers = new[] { organizationUser };
|
|
|
|
|
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
2021-05-25 19:23:47 +02:00
|
|
|
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-07-07 17:08:18 +02:00
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.DeleteUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId));
|
|
|
|
|
Assert.Contains("Users invalid.", exception.Message);
|
2022-08-29 20:53:16 +02:00
|
|
|
|
}
|
2021-07-07 17:08:18 +02:00
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2021-07-07 17:08:18 +02:00
|
|
|
|
public async Task DeleteUsers_RemoveYourself(
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser orgUser,
|
|
|
|
|
OrganizationUser deletingUser,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
2022-08-29 20:53:16 +02:00
|
|
|
|
{
|
2021-07-07 17:08:18 +02:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
2021-05-17 10:10:44 +02:00
|
|
|
|
var organizationUsers = new[] { deletingUser };
|
2021-07-07 17:08:18 +02:00
|
|
|
|
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
|
|
|
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers);
|
2021-05-25 19:23:47 +02:00
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(default, default).ReturnsForAnyArgs(new[] { orgUser });
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-07-07 17:08:18 +02:00
|
|
|
|
var result = await sutProvider.Sut.DeleteUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId);
|
|
|
|
|
Assert.Contains("You cannot remove yourself.", result[0].Item2);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
2021-07-07 17:08:18 +02:00
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2021-07-07 17:08:18 +02:00
|
|
|
|
public async Task DeleteUsers_NonOwnerRemoveOwner(
|
2021-05-25 19:23:47 +02:00
|
|
|
|
[OrganizationUser(type: OrganizationUserType.Admin)] OrganizationUser deletingUser,
|
2021-07-07 17:08:18 +02:00
|
|
|
|
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser orgUser1,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed)] OrganizationUser orgUser2,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-07-07 17:08:18 +02:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
|
|
|
|
|
orgUser1.OrganizationId = orgUser2.OrganizationId = deletingUser.OrganizationId;
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var organizationUsers = new[] { orgUser1 };
|
2021-07-07 17:08:18 +02:00
|
|
|
|
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
|
|
|
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers);
|
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(default, default).ReturnsForAnyArgs(new[] { orgUser2 });
|
|
|
|
|
|
2021-08-10 18:16:10 +02:00
|
|
|
|
var result = await sutProvider.Sut.DeleteUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId);
|
2021-07-07 17:08:18 +02:00
|
|
|
|
Assert.Contains("Only owners can delete other owners.", result[0].Item2);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2021-08-10 18:16:10 +02:00
|
|
|
|
public async Task DeleteUsers_LastOwner(
|
2021-07-07 17:08:18 +02:00
|
|
|
|
[OrganizationUser(status: OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser orgUser,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
2021-05-25 19:23:47 +02:00
|
|
|
|
{
|
2021-07-07 17:08:18 +02:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
|
2021-05-17 10:10:44 +02:00
|
|
|
|
var organizationUsers = new[] { orgUser };
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
|
|
|
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers);
|
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(orgUser.OrganizationId, OrganizationUserType.Owner).Returns(organizationUsers);
|
2021-07-07 17:08:18 +02:00
|
|
|
|
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
2021-08-10 18:16:10 +02:00
|
|
|
|
() => sutProvider.Sut.DeleteUsersAsync(orgUser.OrganizationId, organizationUserIds, null));
|
2021-05-25 19:23:47 +02:00
|
|
|
|
Assert.Contains("Organization must have at least one confirmed owner.", exception.Message);
|
2022-08-29 20:53:16 +02:00
|
|
|
|
}
|
2021-07-07 17:08:18 +02:00
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2021-07-07 17:08:18 +02:00
|
|
|
|
public async Task DeleteUsers_Success(
|
2021-05-25 19:23:47 +02:00
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser deletingUser,
|
|
|
|
|
[OrganizationUser(type: OrganizationUserType.Owner)] OrganizationUser orgUser1, OrganizationUser orgUser2,
|
2021-07-07 17:08:18 +02:00
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
2022-08-29 20:53:16 +02:00
|
|
|
|
{
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-05-25 19:23:47 +02:00
|
|
|
|
orgUser1.OrganizationId = orgUser2.OrganizationId = deletingUser.OrganizationId;
|
|
|
|
|
var organizationUsers = new[] { orgUser1, orgUser2 };
|
|
|
|
|
var organizationUserIds = organizationUsers.Select(u => u.Id);
|
|
|
|
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(organizationUsers);
|
|
|
|
|
organizationUserRepository.GetByIdAsync(deletingUser.Id).Returns(deletingUser);
|
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(deletingUser.OrganizationId, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new[] { deletingUser, orgUser1 });
|
2021-07-07 17:08:18 +02:00
|
|
|
|
currentContext.OrganizationOwner(deletingUser.OrganizationId).Returns(true);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-07-07 17:08:18 +02:00
|
|
|
|
await sutProvider.Sut.DeleteUsersAsync(deletingUser.OrganizationId, organizationUserIds, deletingUser.UserId);
|
|
|
|
|
}
|
2021-09-23 12:36:08 +02:00
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2021-09-23 12:36:08 +02:00
|
|
|
|
public async Task ConfirmUser_InvalidStatus(OrganizationUser confirmingUser,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Invited)] OrganizationUser orgUser, string key,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
2022-08-29 20:53:16 +02:00
|
|
|
|
{
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
2021-08-10 18:16:10 +02:00
|
|
|
|
var userService = Substitute.For<IUserService>();
|
2021-09-23 12:36:08 +02:00
|
|
|
|
|
2021-05-25 19:23:47 +02:00
|
|
|
|
organizationUserRepository.GetByIdAsync(orgUser.Id).Returns(orgUser);
|
2021-09-23 12:36:08 +02:00
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id, userService));
|
|
|
|
|
Assert.Contains("User not valid.", exception.Message);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
2021-09-23 12:36:08 +02:00
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2021-09-23 12:36:08 +02:00
|
|
|
|
public async Task ConfirmUser_WrongOrganization(OrganizationUser confirmingUser,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, string key,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-09-23 12:36:08 +02:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
|
|
|
|
|
organizationUserRepository.GetByIdAsync(orgUser.Id).Returns(orgUser);
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.ConfirmUserAsync(confirmingUser.OrganizationId, orgUser.Id, key, confirmingUser.Id, userService));
|
|
|
|
|
Assert.Contains("User not valid.", exception.Message);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
2021-09-23 12:36:08 +02:00
|
|
|
|
|
2022-08-29 22:06:55 +02:00
|
|
|
|
[Theory]
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[BitAutoData(OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(OrganizationUserType.Owner)]
|
2021-08-10 18:16:10 +02:00
|
|
|
|
public async Task ConfirmUserToFree_AlreadyFreeAdminOrOwner_Throws(OrganizationUserType userType, Organization org, OrganizationUser confirmingUser,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user,
|
2021-05-25 19:23:47 +02:00
|
|
|
|
string key, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-05-25 19:23:47 +02:00
|
|
|
|
org.PlanType = PlanType.Free;
|
|
|
|
|
orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
|
|
|
|
orgUser.UserId = user.Id;
|
2021-08-10 18:16:10 +02:00
|
|
|
|
orgUser.Type = userType;
|
2021-05-25 19:23:47 +02:00
|
|
|
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { orgUser });
|
|
|
|
|
organizationUserRepository.GetCountByFreeOrganizationAdminUserAsync(orgUser.UserId.Value).Returns(1);
|
|
|
|
|
organizationRepository.GetByIdAsync(org.Id).Returns(org);
|
|
|
|
|
userRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { user });
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-10-25 17:19:37 +02:00
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id, userService));
|
|
|
|
|
Assert.Contains("User can only be an admin of one free organization.", exception.Message);
|
2022-08-29 20:53:16 +02:00
|
|
|
|
}
|
2021-09-23 12:36:08 +02:00
|
|
|
|
|
|
|
|
|
[Theory]
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[BitAutoData(PlanType.Custom, OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(PlanType.Custom, OrganizationUserType.Owner)]
|
|
|
|
|
[BitAutoData(PlanType.EnterpriseAnnually, OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(PlanType.EnterpriseAnnually, OrganizationUserType.Owner)]
|
2023-11-03 23:26:47 +01:00
|
|
|
|
[BitAutoData(PlanType.EnterpriseAnnually2020, OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(PlanType.EnterpriseAnnually2020, OrganizationUserType.Owner)]
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[BitAutoData(PlanType.EnterpriseAnnually2019, OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(PlanType.EnterpriseAnnually2019, OrganizationUserType.Owner)]
|
|
|
|
|
[BitAutoData(PlanType.EnterpriseMonthly, OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(PlanType.EnterpriseMonthly, OrganizationUserType.Owner)]
|
2023-11-03 23:26:47 +01:00
|
|
|
|
[BitAutoData(PlanType.EnterpriseMonthly2020, OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(PlanType.EnterpriseMonthly2020, OrganizationUserType.Owner)]
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[BitAutoData(PlanType.EnterpriseMonthly2019, OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(PlanType.EnterpriseMonthly2019, OrganizationUserType.Owner)]
|
|
|
|
|
[BitAutoData(PlanType.FamiliesAnnually, OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(PlanType.FamiliesAnnually, OrganizationUserType.Owner)]
|
|
|
|
|
[BitAutoData(PlanType.FamiliesAnnually2019, OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(PlanType.FamiliesAnnually2019, OrganizationUserType.Owner)]
|
|
|
|
|
[BitAutoData(PlanType.TeamsAnnually, OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(PlanType.TeamsAnnually, OrganizationUserType.Owner)]
|
2023-11-03 23:26:47 +01:00
|
|
|
|
[BitAutoData(PlanType.TeamsAnnually2020, OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(PlanType.TeamsAnnually2020, OrganizationUserType.Owner)]
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[BitAutoData(PlanType.TeamsAnnually2019, OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(PlanType.TeamsAnnually2019, OrganizationUserType.Owner)]
|
|
|
|
|
[BitAutoData(PlanType.TeamsMonthly, OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(PlanType.TeamsMonthly, OrganizationUserType.Owner)]
|
2023-11-03 23:26:47 +01:00
|
|
|
|
[BitAutoData(PlanType.TeamsMonthly2020, OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(PlanType.TeamsMonthly2020, OrganizationUserType.Owner)]
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[BitAutoData(PlanType.TeamsMonthly2019, OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(PlanType.TeamsMonthly2019, OrganizationUserType.Owner)]
|
2021-09-23 12:36:08 +02:00
|
|
|
|
public async Task ConfirmUserToNonFree_AlreadyFreeAdminOrOwner_DoesNotThrow(PlanType planType, OrganizationUserType orgUserType, Organization org, OrganizationUser confirmingUser,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user,
|
|
|
|
|
string key, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 20:53:16 +02:00
|
|
|
|
{
|
2021-09-23 12:36:08 +02:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
2021-08-10 18:16:10 +02:00
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
2021-09-23 12:36:08 +02:00
|
|
|
|
var userService = Substitute.For<IUserService>();
|
2021-08-10 18:16:10 +02:00
|
|
|
|
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-09-23 12:36:08 +02:00
|
|
|
|
org.PlanType = planType;
|
2021-08-10 18:16:10 +02:00
|
|
|
|
orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
2021-09-23 12:36:08 +02:00
|
|
|
|
orgUser.UserId = user.Id;
|
2021-08-10 18:16:10 +02:00
|
|
|
|
orgUser.Type = orgUserType;
|
2021-05-25 19:23:47 +02:00
|
|
|
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { orgUser });
|
2021-08-10 18:16:10 +02:00
|
|
|
|
organizationUserRepository.GetCountByFreeOrganizationAdminUserAsync(orgUser.UserId.Value).Returns(1);
|
|
|
|
|
organizationRepository.GetByIdAsync(org.Id).Returns(org);
|
2021-09-23 12:36:08 +02:00
|
|
|
|
userRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { user });
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-09-23 12:36:08 +02:00
|
|
|
|
await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id, userService);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-09-23 12:36:08 +02:00
|
|
|
|
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Confirmed);
|
2024-03-05 11:56:48 +01:00
|
|
|
|
await sutProvider.GetDependency<IMailService>().Received(1).SendOrganizationConfirmedEmailAsync(org.DisplayName(), user.Email);
|
2021-08-10 18:16:10 +02:00
|
|
|
|
await organizationUserRepository.Received(1).ReplaceManyAsync(Arg.Is<List<OrganizationUser>>(users => users.Contains(orgUser) && users.Count == 1));
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
2021-09-23 12:36:08 +02:00
|
|
|
|
|
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2024-05-17 21:28:51 +02:00
|
|
|
|
public async Task ConfirmUser_AsUser_SingleOrgPolicy_AppliedFromConfirmingOrg_Throws(Organization org, OrganizationUser confirmingUser,
|
2021-05-25 19:23:47 +02:00
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user,
|
2024-05-17 21:28:51 +02:00
|
|
|
|
OrganizationUser orgUserAnotherOrg, [OrganizationUserPolicyDetails(PolicyType.SingleOrg)] OrganizationUserPolicyDetails singleOrgPolicy,
|
2021-05-25 19:23:47 +02:00
|
|
|
|
string key, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-07-07 17:08:18 +02:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
2021-09-23 12:36:08 +02:00
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
2024-05-17 21:28:51 +02:00
|
|
|
|
var policyService = sutProvider.GetDependency<IPolicyService>();
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var userService = Substitute.For<IUserService>();
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-05-25 19:23:47 +02:00
|
|
|
|
org.PlanType = PlanType.EnterpriseAnnually;
|
|
|
|
|
orgUser.Status = OrganizationUserStatusType.Accepted;
|
|
|
|
|
orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
|
|
|
|
orgUser.UserId = orgUserAnotherOrg.UserId = user.Id;
|
|
|
|
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { orgUser });
|
|
|
|
|
organizationUserRepository.GetManyByManyUsersAsync(default).ReturnsForAnyArgs(new[] { orgUserAnotherOrg });
|
2021-09-23 12:36:08 +02:00
|
|
|
|
organizationRepository.GetByIdAsync(org.Id).Returns(org);
|
2021-05-25 19:23:47 +02:00
|
|
|
|
userRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { user });
|
2024-05-17 21:28:51 +02:00
|
|
|
|
singleOrgPolicy.OrganizationId = org.Id;
|
|
|
|
|
policyService.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.SingleOrg).Returns(new[] { singleOrgPolicy });
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id, userService));
|
2024-05-17 21:28:51 +02:00
|
|
|
|
Assert.Contains("Cannot confirm this member to the organization until they leave or remove all other organizations.", exception.Message);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
2021-11-09 16:37:32 +01:00
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2024-05-17 21:28:51 +02:00
|
|
|
|
public async Task ConfirmUser_AsUser_SingleOrgPolicy_AppliedFromOtherOrg_Throws(Organization org, OrganizationUser confirmingUser,
|
2021-05-25 19:23:47 +02:00
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user,
|
2024-05-17 21:28:51 +02:00
|
|
|
|
OrganizationUser orgUserAnotherOrg, [OrganizationUserPolicyDetails(PolicyType.SingleOrg)] OrganizationUserPolicyDetails singleOrgPolicy,
|
2021-11-09 16:37:32 +01:00
|
|
|
|
string key, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-09-23 12:36:08 +02:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
2024-05-17 21:28:51 +02:00
|
|
|
|
var policyService = sutProvider.GetDependency<IPolicyService>();
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var userService = Substitute.For<IUserService>();
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-11-09 16:37:32 +01:00
|
|
|
|
org.PlanType = PlanType.EnterpriseAnnually;
|
2024-05-17 21:28:51 +02:00
|
|
|
|
orgUser.Status = OrganizationUserStatusType.Accepted;
|
|
|
|
|
orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
|
|
|
|
orgUser.UserId = orgUserAnotherOrg.UserId = user.Id;
|
|
|
|
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { orgUser });
|
|
|
|
|
organizationUserRepository.GetManyByManyUsersAsync(default).ReturnsForAnyArgs(new[] { orgUserAnotherOrg });
|
|
|
|
|
organizationRepository.GetByIdAsync(org.Id).Returns(org);
|
|
|
|
|
userRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { user });
|
|
|
|
|
singleOrgPolicy.OrganizationId = orgUserAnotherOrg.Id;
|
|
|
|
|
policyService.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.SingleOrg).Returns(new[] { singleOrgPolicy });
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id, userService));
|
|
|
|
|
Assert.Contains("Cannot confirm this member to the organization because they are in another organization which forbids it.", exception.Message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData(OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(OrganizationUserType.Owner)]
|
|
|
|
|
public async Task ConfirmUser_AsOwnerOrAdmin_SingleOrgPolicy_ExcludedViaUserType_Success(
|
|
|
|
|
OrganizationUserType userType, Organization org, OrganizationUser confirmingUser,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user,
|
|
|
|
|
OrganizationUser orgUserAnotherOrg,
|
|
|
|
|
string key, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
|
|
|
|
|
org.PlanType = PlanType.EnterpriseAnnually;
|
|
|
|
|
orgUser.Type = userType;
|
|
|
|
|
orgUser.Status = OrganizationUserStatusType.Accepted;
|
2021-05-25 19:23:47 +02:00
|
|
|
|
orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
2021-11-09 16:37:32 +01:00
|
|
|
|
orgUser.UserId = orgUserAnotherOrg.UserId = user.Id;
|
2021-09-23 12:36:08 +02:00
|
|
|
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { orgUser });
|
2021-05-25 19:23:47 +02:00
|
|
|
|
organizationUserRepository.GetManyByManyUsersAsync(default).ReturnsForAnyArgs(new[] { orgUserAnotherOrg });
|
2021-09-23 12:36:08 +02:00
|
|
|
|
organizationRepository.GetByIdAsync(org.Id).Returns(org);
|
2021-05-25 19:23:47 +02:00
|
|
|
|
userRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { user });
|
2024-05-17 21:28:51 +02:00
|
|
|
|
|
|
|
|
|
await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id, userService);
|
|
|
|
|
|
|
|
|
|
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Confirmed);
|
|
|
|
|
await sutProvider.GetDependency<IMailService>().Received(1).SendOrganizationConfirmedEmailAsync(org.DisplayName(), user.Email);
|
|
|
|
|
await organizationUserRepository.Received(1).ReplaceManyAsync(Arg.Is<List<OrganizationUser>>(users => users.Contains(orgUser) && users.Count == 1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task ConfirmUser_TwoFactorPolicy_NotEnabled_Throws(Organization org, OrganizationUser confirmingUser,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user,
|
|
|
|
|
OrganizationUser orgUserAnotherOrg,
|
|
|
|
|
[OrganizationUserPolicyDetails(PolicyType.TwoFactorAuthentication)] OrganizationUserPolicyDetails twoFactorPolicy,
|
|
|
|
|
string key, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
|
|
|
|
var policyService = sutProvider.GetDependency<IPolicyService>();
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
|
|
|
|
|
org.PlanType = PlanType.EnterpriseAnnually;
|
|
|
|
|
orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
|
|
|
|
orgUser.UserId = orgUserAnotherOrg.UserId = user.Id;
|
|
|
|
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { orgUser });
|
|
|
|
|
organizationUserRepository.GetManyByManyUsersAsync(default).ReturnsForAnyArgs(new[] { orgUserAnotherOrg });
|
|
|
|
|
organizationRepository.GetByIdAsync(org.Id).Returns(org);
|
|
|
|
|
userRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { user });
|
|
|
|
|
twoFactorPolicy.OrganizationId = org.Id;
|
|
|
|
|
policyService.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication).Returns(new[] { twoFactorPolicy });
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-11-09 16:37:32 +01:00
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id, userService));
|
2021-05-25 19:23:47 +02:00
|
|
|
|
Assert.Contains("User does not have two-step login enabled.", exception.Message);
|
2022-08-29 20:53:16 +02:00
|
|
|
|
}
|
2021-11-09 16:37:32 +01:00
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2024-05-17 21:28:51 +02:00
|
|
|
|
public async Task ConfirmUser_TwoFactorPolicy_Enabled_Success(Organization org, OrganizationUser confirmingUser,
|
2021-11-09 16:37:32 +01:00
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, User user,
|
2024-05-17 21:28:51 +02:00
|
|
|
|
[OrganizationUserPolicyDetails(PolicyType.TwoFactorAuthentication)] OrganizationUserPolicyDetails twoFactorPolicy,
|
|
|
|
|
string key, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-11-09 16:37:32 +01:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
2024-05-17 21:28:51 +02:00
|
|
|
|
var policyService = sutProvider.GetDependency<IPolicyService>();
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var userService = Substitute.For<IUserService>();
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-05-25 19:23:47 +02:00
|
|
|
|
org.PlanType = PlanType.EnterpriseAnnually;
|
|
|
|
|
orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
|
|
|
|
orgUser.UserId = user.Id;
|
2021-11-09 16:37:32 +01:00
|
|
|
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { orgUser });
|
|
|
|
|
organizationRepository.GetByIdAsync(org.Id).Returns(org);
|
2021-05-25 19:23:47 +02:00
|
|
|
|
userRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { user });
|
2024-05-17 21:28:51 +02:00
|
|
|
|
twoFactorPolicy.OrganizationId = org.Id;
|
|
|
|
|
policyService.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication).Returns(new[] { twoFactorPolicy });
|
2021-05-25 19:23:47 +02:00
|
|
|
|
userService.TwoFactorIsEnabledAsync(user).Returns(true);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-07-07 17:08:18 +02:00
|
|
|
|
await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id, userService);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
2021-11-09 16:37:32 +01:00
|
|
|
|
|
2024-08-08 16:43:45 +02:00
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task ConfirmUser_vNext_TwoFactorPolicy_NotEnabled_Throws(Organization org, OrganizationUser confirmingUser,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, UserWithCalculatedPremium user,
|
|
|
|
|
OrganizationUser orgUserAnotherOrg,
|
|
|
|
|
[OrganizationUserPolicyDetails(PolicyType.TwoFactorAuthentication)] OrganizationUserPolicyDetails twoFactorPolicy,
|
|
|
|
|
string key, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.MembersTwoFAQueryOptimization).Returns(true);
|
|
|
|
|
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
|
|
|
|
var policyService = sutProvider.GetDependency<IPolicyService>();
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
var twoFactorIsEnabledQuery = sutProvider.GetDependency<ITwoFactorIsEnabledQuery>();
|
|
|
|
|
|
|
|
|
|
org.PlanType = PlanType.EnterpriseAnnually;
|
|
|
|
|
orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
|
|
|
|
orgUser.UserId = orgUserAnotherOrg.UserId = user.Id;
|
|
|
|
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { orgUser });
|
|
|
|
|
organizationUserRepository.GetManyByManyUsersAsync(default).ReturnsForAnyArgs(new[] { orgUserAnotherOrg });
|
|
|
|
|
organizationRepository.GetByIdAsync(org.Id).Returns(org);
|
|
|
|
|
userRepository.GetManyWithCalculatedPremiumAsync(default).ReturnsForAnyArgs(new[] { user });
|
|
|
|
|
twoFactorPolicy.OrganizationId = org.Id;
|
|
|
|
|
policyService.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication).Returns(new[] { twoFactorPolicy });
|
|
|
|
|
twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user.Id)))
|
|
|
|
|
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (user.Id, false) });
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id, userService));
|
|
|
|
|
Assert.Contains("User does not have two-step login enabled.", exception.Message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task ConfirmUser_vNext_TwoFactorPolicy_Enabled_Success(Organization org, OrganizationUser confirmingUser,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser, UserWithCalculatedPremium user,
|
|
|
|
|
[OrganizationUserPolicyDetails(PolicyType.TwoFactorAuthentication)] OrganizationUserPolicyDetails twoFactorPolicy,
|
|
|
|
|
string key, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.MembersTwoFAQueryOptimization).Returns(true);
|
|
|
|
|
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
|
|
|
|
var policyService = sutProvider.GetDependency<IPolicyService>();
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
var twoFactorIsEnabledQuery = sutProvider.GetDependency<ITwoFactorIsEnabledQuery>();
|
|
|
|
|
|
|
|
|
|
org.PlanType = PlanType.EnterpriseAnnually;
|
|
|
|
|
orgUser.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
|
|
|
|
orgUser.UserId = user.Id;
|
|
|
|
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { orgUser });
|
|
|
|
|
organizationRepository.GetByIdAsync(org.Id).Returns(org);
|
|
|
|
|
userRepository.GetManyWithCalculatedPremiumAsync(default).ReturnsForAnyArgs(new[] { user });
|
|
|
|
|
twoFactorPolicy.OrganizationId = org.Id;
|
|
|
|
|
policyService.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication).Returns(new[] { twoFactorPolicy });
|
|
|
|
|
twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user.Id)))
|
|
|
|
|
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (user.Id, true) });
|
|
|
|
|
|
|
|
|
|
await sutProvider.Sut.ConfirmUserAsync(orgUser.OrganizationId, orgUser.Id, key, confirmingUser.Id, userService);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2021-11-09 16:37:32 +01:00
|
|
|
|
public async Task ConfirmUsers_Success(Organization org,
|
2021-05-25 19:23:47 +02:00
|
|
|
|
OrganizationUser confirmingUser,
|
2021-11-09 16:37:32 +01:00
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser1,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser2,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser3,
|
2021-05-25 19:23:47 +02:00
|
|
|
|
OrganizationUser anotherOrgUser, User user1, User user2, User user3,
|
2024-05-17 21:28:51 +02:00
|
|
|
|
[OrganizationUserPolicyDetails(PolicyType.TwoFactorAuthentication)] OrganizationUserPolicyDetails twoFactorPolicy,
|
|
|
|
|
[OrganizationUserPolicyDetails(PolicyType.SingleOrg)] OrganizationUserPolicyDetails singleOrgPolicy,
|
|
|
|
|
string key, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
2024-05-17 21:28:51 +02:00
|
|
|
|
var policyService = sutProvider.GetDependency<IPolicyService>();
|
2021-11-09 16:37:32 +01:00
|
|
|
|
var userService = Substitute.For<IUserService>();
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-05-25 19:23:47 +02:00
|
|
|
|
org.PlanType = PlanType.EnterpriseAnnually;
|
|
|
|
|
orgUser1.OrganizationId = orgUser2.OrganizationId = orgUser3.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
2021-11-09 16:37:32 +01:00
|
|
|
|
orgUser1.UserId = user1.Id;
|
2021-05-25 19:23:47 +02:00
|
|
|
|
orgUser2.UserId = user2.Id;
|
|
|
|
|
orgUser3.UserId = user3.Id;
|
|
|
|
|
anotherOrgUser.UserId = user3.Id;
|
|
|
|
|
var orgUsers = new[] { orgUser1, orgUser2, orgUser3 };
|
|
|
|
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(orgUsers);
|
|
|
|
|
organizationRepository.GetByIdAsync(org.Id).Returns(org);
|
|
|
|
|
userRepository.GetManyAsync(default).ReturnsForAnyArgs(new[] { user1, user2, user3 });
|
2024-05-17 21:28:51 +02:00
|
|
|
|
twoFactorPolicy.OrganizationId = org.Id;
|
|
|
|
|
policyService.GetPoliciesApplicableToUserAsync(Arg.Any<Guid>(), PolicyType.TwoFactorAuthentication).Returns(new[] { twoFactorPolicy });
|
2021-11-09 16:37:32 +01:00
|
|
|
|
userService.TwoFactorIsEnabledAsync(user1).Returns(true);
|
2021-05-25 19:23:47 +02:00
|
|
|
|
userService.TwoFactorIsEnabledAsync(user2).Returns(false);
|
|
|
|
|
userService.TwoFactorIsEnabledAsync(user3).Returns(true);
|
2024-05-17 21:28:51 +02:00
|
|
|
|
singleOrgPolicy.OrganizationId = org.Id;
|
|
|
|
|
policyService.GetPoliciesApplicableToUserAsync(user3.Id, PolicyType.SingleOrg)
|
|
|
|
|
.Returns(new[] { singleOrgPolicy });
|
2021-05-25 19:23:47 +02:00
|
|
|
|
organizationUserRepository.GetManyByManyUsersAsync(default)
|
|
|
|
|
.ReturnsForAnyArgs(new[] { orgUser1, orgUser2, orgUser3, anotherOrgUser });
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-11-09 16:37:32 +01:00
|
|
|
|
var keys = orgUsers.ToDictionary(ou => ou.Id, _ => key);
|
|
|
|
|
var result = await sutProvider.Sut.ConfirmUsersAsync(confirmingUser.OrganizationId, keys, confirmingUser.Id, userService);
|
|
|
|
|
Assert.Contains("", result[0].Item2);
|
|
|
|
|
Assert.Contains("User does not have two-step login enabled.", result[1].Item2);
|
2024-05-17 21:28:51 +02:00
|
|
|
|
Assert.Contains("Cannot confirm this member to the organization until they leave or remove all other organizations.", result[2].Item2);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
2021-11-09 16:37:32 +01:00
|
|
|
|
|
2024-08-08 16:43:45 +02:00
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task ConfirmUsers_vNext_Success(Organization org,
|
|
|
|
|
OrganizationUser confirmingUser,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser1,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser2,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser orgUser3,
|
|
|
|
|
OrganizationUser anotherOrgUser, UserWithCalculatedPremium user1, UserWithCalculatedPremium user2, UserWithCalculatedPremium user3,
|
|
|
|
|
[OrganizationUserPolicyDetails(PolicyType.TwoFactorAuthentication)] OrganizationUserPolicyDetails twoFactorPolicy,
|
|
|
|
|
[OrganizationUserPolicyDetails(PolicyType.SingleOrg)] OrganizationUserPolicyDetails singleOrgPolicy,
|
|
|
|
|
string key, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
|
|
|
|
var policyService = sutProvider.GetDependency<IPolicyService>();
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
var twoFactorIsEnabledQuery = sutProvider.GetDependency<ITwoFactorIsEnabledQuery>();
|
|
|
|
|
|
|
|
|
|
org.PlanType = PlanType.EnterpriseAnnually;
|
|
|
|
|
orgUser1.OrganizationId = orgUser2.OrganizationId = orgUser3.OrganizationId = confirmingUser.OrganizationId = org.Id;
|
|
|
|
|
orgUser1.UserId = user1.Id;
|
|
|
|
|
orgUser2.UserId = user2.Id;
|
|
|
|
|
orgUser3.UserId = user3.Id;
|
|
|
|
|
anotherOrgUser.UserId = user3.Id;
|
|
|
|
|
var orgUsers = new[] { orgUser1, orgUser2, orgUser3 };
|
|
|
|
|
organizationUserRepository.GetManyAsync(default).ReturnsForAnyArgs(orgUsers);
|
|
|
|
|
organizationRepository.GetByIdAsync(org.Id).Returns(org);
|
|
|
|
|
userRepository.GetManyWithCalculatedPremiumAsync(default).ReturnsForAnyArgs(new[] { user1, user2, user3 });
|
|
|
|
|
twoFactorPolicy.OrganizationId = org.Id;
|
|
|
|
|
policyService.GetPoliciesApplicableToUserAsync(Arg.Any<Guid>(), PolicyType.TwoFactorAuthentication).Returns(new[] { twoFactorPolicy });
|
|
|
|
|
twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user1.Id) && ids.Contains(user2.Id) && ids.Contains(user3.Id)))
|
|
|
|
|
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)>()
|
|
|
|
|
{
|
|
|
|
|
(user1.Id, true),
|
|
|
|
|
(user2.Id, false),
|
|
|
|
|
(user3.Id, true)
|
|
|
|
|
});
|
|
|
|
|
singleOrgPolicy.OrganizationId = org.Id;
|
|
|
|
|
policyService.GetPoliciesApplicableToUserAsync(user3.Id, PolicyType.SingleOrg)
|
|
|
|
|
.Returns(new[] { singleOrgPolicy });
|
|
|
|
|
organizationUserRepository.GetManyByManyUsersAsync(default)
|
|
|
|
|
.ReturnsForAnyArgs(new[] { orgUser1, orgUser2, orgUser3, anotherOrgUser });
|
|
|
|
|
|
|
|
|
|
var keys = orgUsers.ToDictionary(ou => ou.Id, _ => key);
|
|
|
|
|
var result = await sutProvider.Sut.ConfirmUsersAsync_vNext(confirmingUser.OrganizationId, keys, confirmingUser.Id);
|
|
|
|
|
Assert.Contains("", result[0].Item2);
|
|
|
|
|
Assert.Contains("User does not have two-step login enabled.", result[1].Item2);
|
|
|
|
|
Assert.Contains("Cannot confirm this member to the organization until they leave or remove all other organizations.", result[2].Item2);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2021-11-09 16:37:32 +01:00
|
|
|
|
public async Task UpdateOrganizationKeysAsync_WithoutManageResetPassword_Throws(Guid orgId, string publicKey,
|
|
|
|
|
string privateKey, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-11-09 16:37:32 +01:00
|
|
|
|
var currentContext = Substitute.For<ICurrentContext>();
|
|
|
|
|
currentContext.ManageResetPassword(orgId).Returns(false);
|
2022-08-29 20:53:16 +02:00
|
|
|
|
|
2021-11-09 16:37:32 +01:00
|
|
|
|
await Assert.ThrowsAsync<UnauthorizedAccessException>(
|
|
|
|
|
() => sutProvider.Sut.UpdateOrganizationKeysAsync(orgId, publicKey, privateKey));
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
2021-11-09 16:37:32 +01:00
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2021-11-09 16:37:32 +01:00
|
|
|
|
public async Task UpdateOrganizationKeysAsync_KeysAlreadySet_Throws(Organization org, string publicKey,
|
|
|
|
|
string privateKey, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-11-09 16:37:32 +01:00
|
|
|
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
|
|
|
|
currentContext.ManageResetPassword(org.Id).Returns(true);
|
|
|
|
|
|
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
organizationRepository.GetByIdAsync(org.Id).Returns(org);
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.UpdateOrganizationKeysAsync(org.Id, publicKey, privateKey));
|
|
|
|
|
Assert.Contains("Organization Keys already exist", exception.Message);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
2021-11-09 16:37:32 +01:00
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2021-09-23 12:36:08 +02:00
|
|
|
|
public async Task UpdateOrganizationKeysAsync_KeysAlreadySet_Success(Organization org, string publicKey,
|
|
|
|
|
string privateKey, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-09-23 12:36:08 +02:00
|
|
|
|
org.PublicKey = null;
|
|
|
|
|
org.PrivateKey = null;
|
2022-08-29 20:53:16 +02:00
|
|
|
|
|
2021-09-23 12:36:08 +02:00
|
|
|
|
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
2021-10-25 17:19:37 +02:00
|
|
|
|
currentContext.ManageResetPassword(org.Id).Returns(true);
|
2022-08-29 20:53:16 +02:00
|
|
|
|
|
2021-09-23 12:36:08 +02:00
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
organizationRepository.GetByIdAsync(org.Id).Returns(org);
|
2022-08-29 20:53:16 +02:00
|
|
|
|
|
2021-05-17 16:43:02 +02:00
|
|
|
|
await sutProvider.Sut.UpdateOrganizationKeysAsync(org.Id, publicKey, privateKey);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
2022-08-29 20:53:16 +02:00
|
|
|
|
|
2022-08-29 22:06:55 +02:00
|
|
|
|
[Theory]
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[PaidOrganizationCustomize(CheckedPlanType = PlanType.EnterpriseAnnually)]
|
|
|
|
|
[BitAutoData("Cannot set max seat autoscaling below seat count", 1, 0, 2)]
|
|
|
|
|
[BitAutoData("Cannot set max seat autoscaling below seat count", 4, -1, 6)]
|
|
|
|
|
public async Task Enterprise_UpdateSubscription_BadInputThrows(string expectedMessage,
|
|
|
|
|
int? maxAutoscaleSeats, int seatAdjustment, int? currentSeats, Organization organization, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
=> await UpdateSubscription_BadInputThrows(expectedMessage, maxAutoscaleSeats, seatAdjustment, currentSeats, organization, sutProvider);
|
|
|
|
|
[Theory]
|
|
|
|
|
[FreeOrganizationCustomize]
|
|
|
|
|
[BitAutoData("Your plan does not allow seat autoscaling", 10, 0, null)]
|
|
|
|
|
public async Task Free_UpdateSubscription_BadInputThrows(string expectedMessage,
|
|
|
|
|
int? maxAutoscaleSeats, int seatAdjustment, int? currentSeats, Organization organization, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
=> await UpdateSubscription_BadInputThrows(expectedMessage, maxAutoscaleSeats, seatAdjustment, currentSeats, organization, sutProvider);
|
|
|
|
|
|
|
|
|
|
private async Task UpdateSubscription_BadInputThrows(string expectedMessage,
|
2021-05-17 10:10:44 +02:00
|
|
|
|
int? maxAutoscaleSeats, int seatAdjustment, int? currentSeats, Organization organization, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-05-17 10:10:44 +02:00
|
|
|
|
organization.Seats = currentSeats;
|
|
|
|
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
2022-08-29 20:53:16 +02:00
|
|
|
|
|
2021-11-09 16:37:32 +01:00
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateSubscription(organization.Id,
|
|
|
|
|
seatAdjustment, maxAutoscaleSeats));
|
2022-08-29 20:53:16 +02:00
|
|
|
|
|
2021-11-09 16:37:32 +01:00
|
|
|
|
Assert.Contains(expectedMessage, exception.Message);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, BitAutoData]
|
2021-05-17 10:10:44 +02:00
|
|
|
|
public async Task UpdateSubscription_NoOrganization_Throws(Guid organizationId, SutProvider<OrganizationService> sutProvider)
|
2021-11-17 11:46:35 +01:00
|
|
|
|
{
|
2021-11-09 16:37:32 +01:00
|
|
|
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organizationId).Returns((Organization)null);
|
2022-08-29 20:53:16 +02:00
|
|
|
|
|
2021-01-12 17:02:39 +01:00
|
|
|
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.UpdateSubscription(organizationId, 0, null));
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
2022-08-29 20:53:16 +02:00
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, PaidOrganizationCustomize]
|
|
|
|
|
[BitAutoData(0, 100, null, true, "")]
|
|
|
|
|
[BitAutoData(0, 100, 100, true, "")]
|
|
|
|
|
[BitAutoData(0, null, 100, true, "")]
|
|
|
|
|
[BitAutoData(1, 100, null, true, "")]
|
2022-09-23 06:30:39 +02:00
|
|
|
|
[BitAutoData(1, 100, 100, false, "Seat limit has been reached")]
|
2023-11-20 15:05:35 +01:00
|
|
|
|
public async Task CanScaleAsync(int seatsToAdd, int? currentSeats, int? maxAutoscaleSeats,
|
2021-05-25 19:23:47 +02:00
|
|
|
|
bool expectedResult, string expectedFailureMessage, Organization organization,
|
2021-05-17 10:10:44 +02:00
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-05-17 10:10:44 +02:00
|
|
|
|
organization.Seats = currentSeats;
|
|
|
|
|
organization.MaxAutoscaleSeats = maxAutoscaleSeats;
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
2023-11-20 15:05:35 +01:00
|
|
|
|
sutProvider.GetDependency<IProviderRepository>().GetByOrganizationIdAsync(organization.Id).ReturnsNull();
|
2022-08-29 20:53:16 +02:00
|
|
|
|
|
2023-11-20 15:05:35 +01:00
|
|
|
|
var (result, failureMessage) = await sutProvider.Sut.CanScaleAsync(organization, seatsToAdd);
|
2022-08-29 20:53:16 +02:00
|
|
|
|
|
2021-11-09 16:37:32 +01:00
|
|
|
|
if (expectedFailureMessage == string.Empty)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-11-09 16:37:32 +01:00
|
|
|
|
Assert.Empty(failureMessage);
|
2022-08-29 21:53:48 +02:00
|
|
|
|
}
|
2022-08-29 22:06:55 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
2021-09-23 12:36:08 +02:00
|
|
|
|
Assert.Contains(expectedFailureMessage, failureMessage);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
2021-09-23 12:36:08 +02:00
|
|
|
|
Assert.Equal(expectedResult, result);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, PaidOrganizationCustomize, BitAutoData]
|
2023-11-20 15:05:35 +01:00
|
|
|
|
public async Task CanScaleAsync_FailsOnSelfHosted(Organization organization,
|
2021-09-23 12:36:08 +02:00
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-11-09 16:37:32 +01:00
|
|
|
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(true);
|
2023-11-20 15:05:35 +01:00
|
|
|
|
var (result, failureMessage) = await sutProvider.Sut.CanScaleAsync(organization, 10);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-09-23 12:36:08 +02:00
|
|
|
|
Assert.False(result);
|
|
|
|
|
Assert.Contains("Cannot autoscale on self-hosted instance", failureMessage);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-20 15:05:35 +01:00
|
|
|
|
[Theory, PaidOrganizationCustomize, BitAutoData]
|
|
|
|
|
public async Task CanScaleAsync_FailsOnResellerManagedOrganization(
|
|
|
|
|
Organization organization,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
var provider = new Provider
|
|
|
|
|
{
|
|
|
|
|
Enabled = true,
|
|
|
|
|
Type = ProviderType.Reseller
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
sutProvider.GetDependency<IProviderRepository>().GetByOrganizationIdAsync(organization.Id).Returns(provider);
|
|
|
|
|
|
|
|
|
|
var (result, failureMessage) = await sutProvider.Sut.CanScaleAsync(organization, 10);
|
|
|
|
|
|
|
|
|
|
Assert.False(result);
|
|
|
|
|
Assert.Contains("Seat limit has been reached. Contact your provider to purchase additional seats.", failureMessage);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, PaidOrganizationCustomize, BitAutoData]
|
2021-01-12 17:02:39 +01:00
|
|
|
|
public async Task Delete_Success(Organization organization, SutProvider<OrganizationService> sutProvider)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-11-09 16:37:32 +01:00
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
var applicationCacheService = sutProvider.GetDependency<IApplicationCacheService>();
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-05-25 19:23:47 +02:00
|
|
|
|
await sutProvider.Sut.DeleteAsync(organization);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-11-09 16:37:32 +01:00
|
|
|
|
await organizationRepository.Received().DeleteAsync(organization);
|
|
|
|
|
await applicationCacheService.Received().DeleteOrganizationAbilityAsync(organization.Id);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-08-31 15:38:35 +02:00
|
|
|
|
[Theory, PaidOrganizationCustomize, BitAutoData]
|
2021-05-17 10:10:44 +02:00
|
|
|
|
public async Task Delete_Fails_KeyConnector(Organization organization, SutProvider<OrganizationService> sutProvider,
|
2021-11-09 16:37:32 +01:00
|
|
|
|
SsoConfig ssoConfig)
|
2022-08-29 22:06:55 +02:00
|
|
|
|
{
|
2021-11-09 16:37:32 +01:00
|
|
|
|
ssoConfig.Enabled = true;
|
2023-05-10 21:52:08 +02:00
|
|
|
|
ssoConfig.SetData(new SsoConfigurationData { MemberDecryptionType = MemberDecryptionType.KeyConnector });
|
2021-11-09 16:37:32 +01:00
|
|
|
|
var ssoConfigRepository = sutProvider.GetDependency<ISsoConfigRepository>();
|
|
|
|
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
|
|
|
|
var applicationCacheService = sutProvider.GetDependency<IApplicationCacheService>();
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-05-17 10:10:44 +02:00
|
|
|
|
ssoConfigRepository.GetByOrganizationIdAsync(organization.Id).Returns(ssoConfig);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-05-25 19:23:47 +02:00
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.DeleteAsync(organization));
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-11-09 16:37:32 +01:00
|
|
|
|
Assert.Contains("You cannot delete an Organization that is using Key Connector.", exception.Message);
|
2022-08-29 22:06:55 +02:00
|
|
|
|
|
2021-11-09 16:37:32 +01:00
|
|
|
|
await organizationRepository.DidNotReceiveWithAnyArgs().DeleteAsync(default);
|
|
|
|
|
await applicationCacheService.DidNotReceiveWithAnyArgs().DeleteOrganizationAbilityAsync(default);
|
2019-02-19 22:27:05 +01:00
|
|
|
|
}
|
2022-11-09 13:13:29 +01:00
|
|
|
|
|
2024-08-08 16:43:45 +02:00
|
|
|
|
private void RestoreRevokeUser_Setup(Organization organization, OrganizationUser restoringUser,
|
|
|
|
|
OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider,
|
|
|
|
|
OrganizationUserType restoringUserType = OrganizationUserType.Owner)
|
2022-11-09 13:13:29 +01:00
|
|
|
|
{
|
|
|
|
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
|
|
|
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organizationUser.OrganizationId).Returns(organization);
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(organization.Id).Returns(true);
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
2024-08-08 16:43:45 +02:00
|
|
|
|
organizationUserRepository.GetManyByOrganizationAsync(organizationUser.OrganizationId, restoringUserType)
|
|
|
|
|
.Returns(new[] { restoringUser });
|
2022-11-09 13:13:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task RevokeUser_Success(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
[OrganizationUser] OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var eventService = sutProvider.GetDependency<IEventService>();
|
|
|
|
|
|
|
|
|
|
await sutProvider.Sut.RevokeUserAsync(organizationUser, owner.Id);
|
|
|
|
|
|
|
|
|
|
await organizationUserRepository.Received().RevokeAsync(organizationUser.Id);
|
|
|
|
|
await eventService.Received()
|
|
|
|
|
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task RevokeUser_WithEventSystemUser_Success(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
[OrganizationUser] OrganizationUser organizationUser, EventSystemUser eventSystemUser, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var eventService = sutProvider.GetDependency<IEventService>();
|
|
|
|
|
|
|
|
|
|
await sutProvider.Sut.RevokeUserAsync(organizationUser, eventSystemUser);
|
|
|
|
|
|
|
|
|
|
await organizationUserRepository.Received().RevokeAsync(organizationUser.Id);
|
|
|
|
|
await eventService.Received()
|
|
|
|
|
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked, eventSystemUser);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task RestoreUser_Success(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var eventService = sutProvider.GetDependency<IEventService>();
|
|
|
|
|
|
|
|
|
|
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, userService);
|
|
|
|
|
|
|
|
|
|
await organizationUserRepository.Received().RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Invited);
|
|
|
|
|
await eventService.Received()
|
|
|
|
|
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task RestoreUser_WithEventSystemUser_Success(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, EventSystemUser eventSystemUser, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var eventService = sutProvider.GetDependency<IEventService>();
|
|
|
|
|
|
|
|
|
|
await sutProvider.Sut.RestoreUserAsync(organizationUser, eventSystemUser, userService);
|
|
|
|
|
|
|
|
|
|
await organizationUserRepository.Received().RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Invited);
|
|
|
|
|
await eventService.Received()
|
|
|
|
|
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored, eventSystemUser);
|
|
|
|
|
}
|
2023-05-17 17:39:08 +02:00
|
|
|
|
|
2024-08-08 16:43:45 +02:00
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task RestoreUser_RestoreThemselves_Fails(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
organizationUser.UserId = owner.Id;
|
|
|
|
|
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var eventService = sutProvider.GetDependency<IEventService>();
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, userService));
|
|
|
|
|
|
|
|
|
|
Assert.Contains("you cannot restore yourself", exception.Message.ToLowerInvariant());
|
|
|
|
|
|
|
|
|
|
await organizationUserRepository.DidNotReceiveWithAnyArgs().RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
|
|
|
|
|
await eventService.DidNotReceiveWithAnyArgs()
|
|
|
|
|
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData(OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(OrganizationUserType.Custom)]
|
|
|
|
|
public async Task RestoreUser_AdminRestoreOwner_Fails(OrganizationUserType restoringUserType, Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed)] OrganizationUser restoringUser,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Revoked, OrganizationUserType.Owner)] OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
restoringUser.Type = restoringUserType;
|
|
|
|
|
RestoreRevokeUser_Setup(organization, restoringUser, organizationUser, sutProvider, OrganizationUserType.Admin);
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var eventService = sutProvider.GetDependency<IEventService>();
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.RestoreUserAsync(organizationUser, restoringUser.Id, userService));
|
|
|
|
|
|
|
|
|
|
Assert.Contains("only owners can restore other owners", exception.Message.ToLowerInvariant());
|
|
|
|
|
|
|
|
|
|
await organizationUserRepository.DidNotReceiveWithAnyArgs().RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
|
|
|
|
|
await eventService.DidNotReceiveWithAnyArgs()
|
|
|
|
|
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData(OrganizationUserStatusType.Invited)]
|
|
|
|
|
[BitAutoData(OrganizationUserStatusType.Accepted)]
|
|
|
|
|
[BitAutoData(OrganizationUserStatusType.Confirmed)]
|
|
|
|
|
public async Task RestoreUser_WithStatusOtherThanRevoked_Fails(OrganizationUserStatusType userStatus, Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
[OrganizationUser] OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
organizationUser.Status = userStatus;
|
|
|
|
|
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var eventService = sutProvider.GetDependency<IEventService>();
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, userService));
|
|
|
|
|
|
|
|
|
|
Assert.Contains("already active", exception.Message.ToLowerInvariant());
|
|
|
|
|
|
|
|
|
|
await organizationUserRepository.DidNotReceiveWithAnyArgs().RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
|
|
|
|
|
await eventService.DidNotReceiveWithAnyArgs()
|
|
|
|
|
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task RestoreUser_WithSingleOrgPolicyEnabled_Fails(
|
|
|
|
|
Organization organization,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser secondOrganizationUser,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke
|
|
|
|
|
secondOrganizationUser.UserId = organizationUser.UserId;
|
|
|
|
|
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var eventService = sutProvider.GetDependency<IEventService>();
|
|
|
|
|
|
|
|
|
|
organizationUserRepository.GetManyByUserAsync(organizationUser.UserId.Value).Returns(new[] { organizationUser, secondOrganizationUser });
|
|
|
|
|
sutProvider.GetDependency<IPolicyService>()
|
|
|
|
|
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.SingleOrg, Arg.Any<OrganizationUserStatusType>())
|
|
|
|
|
.Returns(new[] { new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.SingleOrg } });
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, userService));
|
|
|
|
|
|
|
|
|
|
Assert.Contains("you cannot restore this user until " +
|
|
|
|
|
"they leave or remove all other organizations.", exception.Message.ToLowerInvariant());
|
|
|
|
|
|
|
|
|
|
await organizationUserRepository.DidNotReceiveWithAnyArgs().RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
|
|
|
|
|
await eventService.DidNotReceiveWithAnyArgs()
|
|
|
|
|
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task RestoreUser_WithOtherOrganizationSingleOrgPolicyEnabled_Fails(
|
|
|
|
|
Organization organization,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke
|
|
|
|
|
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var eventService = sutProvider.GetDependency<IEventService>();
|
|
|
|
|
|
|
|
|
|
sutProvider.GetDependency<IPolicyService>()
|
|
|
|
|
.AnyPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.SingleOrg, Arg.Any<OrganizationUserStatusType>())
|
|
|
|
|
.Returns(true);
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, userService));
|
|
|
|
|
|
|
|
|
|
Assert.Contains("you cannot restore this user because they are a member of " +
|
|
|
|
|
"another organization which forbids it", exception.Message.ToLowerInvariant());
|
|
|
|
|
|
|
|
|
|
await organizationUserRepository.DidNotReceiveWithAnyArgs().RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
|
|
|
|
|
await eventService.DidNotReceiveWithAnyArgs()
|
|
|
|
|
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task RestoreUser_With2FAPolicyEnabled_WithoutUser2FAConfigured_Fails(
|
|
|
|
|
Organization organization,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
organizationUser.Email = null;
|
|
|
|
|
sutProvider.GetDependency<IPolicyService>()
|
|
|
|
|
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>())
|
|
|
|
|
.Returns(new[] { new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.TwoFactorAuthentication } });
|
|
|
|
|
|
|
|
|
|
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var eventService = sutProvider.GetDependency<IEventService>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
userService.TwoFactorIsEnabledAsync(Arg.Any<ITwoFactorProvidersUser>()).Returns(false);
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, userService));
|
|
|
|
|
|
|
|
|
|
Assert.Contains("you cannot restore this user until they enable " +
|
|
|
|
|
"two-step login on their user account.", exception.Message.ToLowerInvariant());
|
|
|
|
|
|
|
|
|
|
await organizationUserRepository.DidNotReceiveWithAnyArgs().RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
|
|
|
|
|
await eventService.DidNotReceiveWithAnyArgs()
|
|
|
|
|
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task RestoreUser_With2FAPolicyEnabled_WithUser2FAConfigured_Success(
|
|
|
|
|
Organization organization,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke
|
|
|
|
|
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var eventService = sutProvider.GetDependency<IEventService>();
|
|
|
|
|
|
|
|
|
|
sutProvider.GetDependency<IPolicyService>()
|
|
|
|
|
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>())
|
|
|
|
|
.Returns(new[] { new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.TwoFactorAuthentication } });
|
|
|
|
|
userService.TwoFactorIsEnabledAsync(Arg.Any<ITwoFactorProvidersUser>()).Returns(true);
|
|
|
|
|
|
|
|
|
|
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, userService);
|
|
|
|
|
|
|
|
|
|
await organizationUserRepository.Received().RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Confirmed);
|
|
|
|
|
await eventService.Received()
|
|
|
|
|
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task RestoreUser_vNext_WithSingleOrgPolicyEnabled_Fails(
|
|
|
|
|
Organization organization,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser secondOrganizationUser,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.MembersTwoFAQueryOptimization).Returns(true);
|
|
|
|
|
|
|
|
|
|
organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke
|
|
|
|
|
secondOrganizationUser.UserId = organizationUser.UserId;
|
|
|
|
|
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var eventService = sutProvider.GetDependency<IEventService>();
|
|
|
|
|
|
|
|
|
|
organizationUserRepository.GetManyByUserAsync(organizationUser.UserId.Value).Returns(new[] { organizationUser, secondOrganizationUser });
|
|
|
|
|
sutProvider.GetDependency<IPolicyService>()
|
|
|
|
|
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.SingleOrg, Arg.Any<OrganizationUserStatusType>())
|
|
|
|
|
.Returns(new[]
|
|
|
|
|
{
|
|
|
|
|
new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.SingleOrg, OrganizationUserStatus = OrganizationUserStatusType.Revoked }
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, userService));
|
|
|
|
|
|
|
|
|
|
Assert.Contains("you cannot restore this user until " +
|
|
|
|
|
"they leave or remove all other organizations.", exception.Message.ToLowerInvariant());
|
|
|
|
|
|
|
|
|
|
await organizationUserRepository.DidNotReceiveWithAnyArgs().RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
|
|
|
|
|
await eventService.DidNotReceiveWithAnyArgs()
|
|
|
|
|
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task RestoreUser_vNext_WithOtherOrganizationSingleOrgPolicyEnabled_Fails(
|
|
|
|
|
Organization organization,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Accepted)] OrganizationUser secondOrganizationUser,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.MembersTwoFAQueryOptimization).Returns(true);
|
|
|
|
|
|
|
|
|
|
organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke
|
|
|
|
|
secondOrganizationUser.UserId = organizationUser.UserId;
|
|
|
|
|
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var eventService = sutProvider.GetDependency<IEventService>();
|
|
|
|
|
var twoFactorIsEnabledQuery = sutProvider.GetDependency<ITwoFactorIsEnabledQuery>();
|
|
|
|
|
|
|
|
|
|
twoFactorIsEnabledQuery
|
|
|
|
|
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.UserId.Value)))
|
|
|
|
|
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (organizationUser.UserId.Value, true) });
|
|
|
|
|
|
|
|
|
|
sutProvider.GetDependency<IPolicyService>()
|
|
|
|
|
.AnyPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.SingleOrg, Arg.Any<OrganizationUserStatusType>())
|
|
|
|
|
.Returns(true);
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, userService));
|
|
|
|
|
|
|
|
|
|
Assert.Contains("you cannot restore this user because they are a member of " +
|
|
|
|
|
"another organization which forbids it", exception.Message.ToLowerInvariant());
|
|
|
|
|
|
|
|
|
|
await organizationUserRepository.DidNotReceiveWithAnyArgs().RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
|
|
|
|
|
await eventService.DidNotReceiveWithAnyArgs()
|
|
|
|
|
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task RestoreUser_vNext_With2FAPolicyEnabled_WithoutUser2FAConfigured_Fails(
|
|
|
|
|
Organization organization,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.MembersTwoFAQueryOptimization).Returns(true);
|
|
|
|
|
|
|
|
|
|
organizationUser.Email = null;
|
|
|
|
|
sutProvider.GetDependency<IPolicyService>()
|
|
|
|
|
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>())
|
|
|
|
|
.Returns(new[] { new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.TwoFactorAuthentication } });
|
|
|
|
|
|
|
|
|
|
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var eventService = sutProvider.GetDependency<IEventService>();
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, userService));
|
|
|
|
|
|
|
|
|
|
Assert.Contains("you cannot restore this user until they enable " +
|
|
|
|
|
"two-step login on their user account.", exception.Message.ToLowerInvariant());
|
|
|
|
|
|
|
|
|
|
await organizationUserRepository.DidNotReceiveWithAnyArgs().RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
|
|
|
|
|
await eventService.DidNotReceiveWithAnyArgs()
|
|
|
|
|
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task RestoreUser_vNext_With2FAPolicyEnabled_WithUser2FAConfigured_Success(
|
|
|
|
|
Organization organization,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
|
|
|
|
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
sutProvider.GetDependency<IFeatureService>().IsEnabled(FeatureFlagKeys.MembersTwoFAQueryOptimization).Returns(true);
|
|
|
|
|
|
|
|
|
|
organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke
|
|
|
|
|
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
|
|
|
|
var userService = Substitute.For<IUserService>();
|
|
|
|
|
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
|
|
|
|
var eventService = sutProvider.GetDependency<IEventService>();
|
|
|
|
|
var twoFactorIsEnabledQuery = sutProvider.GetDependency<ITwoFactorIsEnabledQuery>();
|
|
|
|
|
|
|
|
|
|
sutProvider.GetDependency<IPolicyService>()
|
|
|
|
|
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>())
|
|
|
|
|
.Returns(new[] { new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.TwoFactorAuthentication } });
|
|
|
|
|
|
|
|
|
|
twoFactorIsEnabledQuery
|
|
|
|
|
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.UserId.Value)))
|
|
|
|
|
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (organizationUser.UserId.Value, true) });
|
|
|
|
|
|
|
|
|
|
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, userService);
|
|
|
|
|
|
|
|
|
|
await organizationUserRepository.Received().RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Confirmed);
|
|
|
|
|
await eventService.Received()
|
|
|
|
|
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-17 17:39:08 +02:00
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task HasConfirmedOwnersExcept_WithConfirmedOwner_ReturnsTrue(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
|
|
|
.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new List<OrganizationUser> { owner });
|
|
|
|
|
|
|
|
|
|
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organization.Id, new List<Guid>(), true);
|
|
|
|
|
|
|
|
|
|
Assert.True(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task HasConfirmedOwnersExcept_ExcludingConfirmedOwner_ReturnsFalse(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
|
|
|
.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new List<OrganizationUser> { owner });
|
|
|
|
|
|
|
|
|
|
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organization.Id, new List<Guid> { owner.Id }, true);
|
|
|
|
|
|
|
|
|
|
Assert.False(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task HasConfirmedOwnersExcept_WithInvitedOwner_ReturnsFalse(Organization organization, [OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.Owner)] OrganizationUser owner, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
|
|
|
.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new List<OrganizationUser> { owner });
|
|
|
|
|
|
|
|
|
|
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organization.Id, new List<Guid>(), true);
|
|
|
|
|
|
|
|
|
|
Assert.False(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData(true)]
|
|
|
|
|
[BitAutoData(false)]
|
|
|
|
|
public async Task HasConfirmedOwnersExcept_WithConfirmedProviderUser_IncludeProviderTrue_ReturnsTrue(bool includeProvider, Organization organization, ProviderUser providerUser, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
providerUser.Status = ProviderUserStatusType.Confirmed;
|
|
|
|
|
|
|
|
|
|
sutProvider.GetDependency<IProviderUserRepository>()
|
|
|
|
|
.GetManyByOrganizationAsync(organization.Id, ProviderUserStatusType.Confirmed)
|
|
|
|
|
.Returns(new List<ProviderUser> { providerUser });
|
|
|
|
|
|
|
|
|
|
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organization.Id, new List<Guid>(), includeProvider);
|
|
|
|
|
|
|
|
|
|
Assert.Equal(includeProvider, result);
|
|
|
|
|
}
|
2023-07-25 00:05:05 +02:00
|
|
|
|
|
2023-08-04 23:51:12 +02:00
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData(PlanType.TeamsAnnually)]
|
|
|
|
|
[BitAutoData(PlanType.TeamsMonthly)]
|
2023-11-03 23:26:47 +01:00
|
|
|
|
[BitAutoData(PlanType.TeamsStarter)]
|
2023-08-04 23:51:12 +02:00
|
|
|
|
[BitAutoData(PlanType.EnterpriseAnnually)]
|
|
|
|
|
[BitAutoData(PlanType.EnterpriseMonthly)]
|
|
|
|
|
public void ValidateSecretsManagerPlan_ThrowsException_WhenNoSecretsManagerSeats(PlanType planType, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
2023-10-17 16:56:35 +02:00
|
|
|
|
var plan = StaticStore.GetPlan(planType);
|
2023-08-04 23:51:12 +02:00
|
|
|
|
var signup = new OrganizationUpgrade
|
|
|
|
|
{
|
|
|
|
|
UseSecretsManager = true,
|
|
|
|
|
AdditionalSmSeats = 0,
|
|
|
|
|
AdditionalServiceAccounts = 5,
|
|
|
|
|
AdditionalSeats = 2
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var exception = Assert.Throws<BadRequestException>(() => sutProvider.Sut.ValidateSecretsManagerPlan(plan, signup));
|
|
|
|
|
Assert.Contains("You do not have any Secrets Manager seats!", exception.Message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData(PlanType.Free)]
|
|
|
|
|
public void ValidateSecretsManagerPlan_ThrowsException_WhenSubtractingSeats(PlanType planType, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
2023-10-17 16:56:35 +02:00
|
|
|
|
var plan = StaticStore.GetPlan(planType);
|
2023-08-04 23:51:12 +02:00
|
|
|
|
var signup = new OrganizationUpgrade
|
|
|
|
|
{
|
|
|
|
|
UseSecretsManager = true,
|
|
|
|
|
AdditionalSmSeats = -1,
|
|
|
|
|
AdditionalServiceAccounts = 5
|
|
|
|
|
};
|
|
|
|
|
var exception = Assert.Throws<BadRequestException>(() => sutProvider.Sut.ValidateSecretsManagerPlan(plan, signup));
|
|
|
|
|
Assert.Contains("You can't subtract Secrets Manager seats!", exception.Message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData(PlanType.Free)]
|
|
|
|
|
public void ValidateSecretsManagerPlan_ThrowsException_WhenPlanDoesNotAllowAdditionalServiceAccounts(
|
|
|
|
|
PlanType planType,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
2023-10-17 16:56:35 +02:00
|
|
|
|
var plan = StaticStore.GetPlan(planType);
|
2023-08-04 23:51:12 +02:00
|
|
|
|
var signup = new OrganizationUpgrade
|
|
|
|
|
{
|
|
|
|
|
UseSecretsManager = true,
|
|
|
|
|
AdditionalSmSeats = 2,
|
|
|
|
|
AdditionalServiceAccounts = 5,
|
|
|
|
|
AdditionalSeats = 3
|
|
|
|
|
};
|
|
|
|
|
var exception = Assert.Throws<BadRequestException>(() => sutProvider.Sut.ValidateSecretsManagerPlan(plan, signup));
|
2024-04-05 14:54:36 +02:00
|
|
|
|
Assert.Contains("Plan does not allow additional Machine Accounts.", exception.Message);
|
2023-08-04 23:51:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData(PlanType.TeamsAnnually)]
|
|
|
|
|
[BitAutoData(PlanType.TeamsMonthly)]
|
|
|
|
|
[BitAutoData(PlanType.EnterpriseAnnually)]
|
|
|
|
|
[BitAutoData(PlanType.EnterpriseMonthly)]
|
|
|
|
|
public void ValidateSecretsManagerPlan_ThrowsException_WhenMoreSeatsThanPasswordManagerSeats(PlanType planType, SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
2023-10-17 16:56:35 +02:00
|
|
|
|
var plan = StaticStore.GetPlan(planType);
|
2023-08-04 23:51:12 +02:00
|
|
|
|
var signup = new OrganizationUpgrade
|
|
|
|
|
{
|
|
|
|
|
UseSecretsManager = true,
|
|
|
|
|
AdditionalSmSeats = 4,
|
|
|
|
|
AdditionalServiceAccounts = 5,
|
|
|
|
|
AdditionalSeats = 3
|
|
|
|
|
};
|
|
|
|
|
var exception = Assert.Throws<BadRequestException>(() => sutProvider.Sut.ValidateSecretsManagerPlan(plan, signup));
|
|
|
|
|
Assert.Contains("You cannot have more Secrets Manager seats than Password Manager seats.", exception.Message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData(PlanType.TeamsAnnually)]
|
|
|
|
|
[BitAutoData(PlanType.TeamsMonthly)]
|
2023-11-03 23:26:47 +01:00
|
|
|
|
[BitAutoData(PlanType.TeamsStarter)]
|
2023-08-04 23:51:12 +02:00
|
|
|
|
[BitAutoData(PlanType.EnterpriseAnnually)]
|
|
|
|
|
[BitAutoData(PlanType.EnterpriseMonthly)]
|
|
|
|
|
public void ValidateSecretsManagerPlan_ThrowsException_WhenSubtractingServiceAccounts(
|
|
|
|
|
PlanType planType,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
2023-10-17 16:56:35 +02:00
|
|
|
|
var plan = StaticStore.GetPlan(planType);
|
2023-08-04 23:51:12 +02:00
|
|
|
|
var signup = new OrganizationUpgrade
|
|
|
|
|
{
|
|
|
|
|
UseSecretsManager = true,
|
|
|
|
|
AdditionalSmSeats = 4,
|
|
|
|
|
AdditionalServiceAccounts = -5,
|
|
|
|
|
AdditionalSeats = 5
|
|
|
|
|
};
|
|
|
|
|
var exception = Assert.Throws<BadRequestException>(() => sutProvider.Sut.ValidateSecretsManagerPlan(plan, signup));
|
2024-04-05 14:54:36 +02:00
|
|
|
|
Assert.Contains("You can't subtract Machine Accounts!", exception.Message);
|
2023-08-04 23:51:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData(PlanType.Free)]
|
|
|
|
|
public void ValidateSecretsManagerPlan_ThrowsException_WhenPlanDoesNotAllowAdditionalUsers(
|
|
|
|
|
PlanType planType,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
2023-10-17 16:56:35 +02:00
|
|
|
|
var plan = StaticStore.GetPlan(planType);
|
2023-08-04 23:51:12 +02:00
|
|
|
|
var signup = new OrganizationUpgrade
|
|
|
|
|
{
|
|
|
|
|
UseSecretsManager = true,
|
|
|
|
|
AdditionalSmSeats = 2,
|
|
|
|
|
AdditionalServiceAccounts = 0,
|
|
|
|
|
AdditionalSeats = 5
|
|
|
|
|
};
|
|
|
|
|
var exception = Assert.Throws<BadRequestException>(() => sutProvider.Sut.ValidateSecretsManagerPlan(plan, signup));
|
|
|
|
|
Assert.Contains("Plan does not allow additional users.", exception.Message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData(PlanType.TeamsAnnually)]
|
|
|
|
|
[BitAutoData(PlanType.TeamsMonthly)]
|
2023-11-03 23:26:47 +01:00
|
|
|
|
[BitAutoData(PlanType.TeamsStarter)]
|
2023-08-04 23:51:12 +02:00
|
|
|
|
[BitAutoData(PlanType.EnterpriseAnnually)]
|
|
|
|
|
[BitAutoData(PlanType.EnterpriseMonthly)]
|
|
|
|
|
public void ValidateSecretsManagerPlan_ValidPlan_NoExceptionThrown(
|
|
|
|
|
PlanType planType,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
2023-10-17 16:56:35 +02:00
|
|
|
|
var plan = StaticStore.GetPlan(planType);
|
2023-08-04 23:51:12 +02:00
|
|
|
|
var signup = new OrganizationUpgrade
|
|
|
|
|
{
|
|
|
|
|
UseSecretsManager = true,
|
|
|
|
|
AdditionalSmSeats = 2,
|
|
|
|
|
AdditionalServiceAccounts = 0,
|
|
|
|
|
AdditionalSeats = 4
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
sutProvider.Sut.ValidateSecretsManagerPlan(plan, signup);
|
|
|
|
|
}
|
2023-09-01 10:10:02 +02:00
|
|
|
|
|
2024-04-18 12:42:30 +02:00
|
|
|
|
[Theory]
|
|
|
|
|
[OrganizationInviteCustomize(
|
|
|
|
|
InviteeUserType = OrganizationUserType.Custom,
|
|
|
|
|
InvitorUserType = OrganizationUserType.Custom
|
|
|
|
|
), BitAutoData]
|
|
|
|
|
public async Task ValidateOrganizationUserUpdatePermissions_WithCustomPermission_WhenSavingUserHasCustomPermission_Passes(
|
|
|
|
|
CurrentContextOrganization organization,
|
|
|
|
|
OrganizationUserInvite organizationUserInvite,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
var invitePermissions = new Permissions { AccessReports = true };
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().GetOrganization(organization.Id).Returns(organization);
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(organization.Id).Returns(true);
|
|
|
|
|
|
|
|
|
|
await sutProvider.Sut.ValidateOrganizationUserUpdatePermissions(organization.Id, organizationUserInvite.Type.Value, null, invitePermissions);
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-01 10:10:02 +02:00
|
|
|
|
[Theory]
|
|
|
|
|
[OrganizationInviteCustomize(
|
|
|
|
|
InviteeUserType = OrganizationUserType.Owner,
|
|
|
|
|
InvitorUserType = OrganizationUserType.Admin
|
|
|
|
|
), BitAutoData]
|
|
|
|
|
public async Task ValidateOrganizationUserUpdatePermissions_WithAdminAddingOwner_Throws(
|
|
|
|
|
Guid organizationId,
|
|
|
|
|
OrganizationUserInvite organizationUserInvite,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.ValidateOrganizationUserUpdatePermissions(organizationId, organizationUserInvite.Type.Value, null, organizationUserInvite.Permissions));
|
|
|
|
|
|
|
|
|
|
Assert.Contains("only an owner can configure another owner's account.", exception.Message.ToLowerInvariant());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[OrganizationInviteCustomize(
|
|
|
|
|
InviteeUserType = OrganizationUserType.Admin,
|
|
|
|
|
InvitorUserType = OrganizationUserType.Owner
|
|
|
|
|
), BitAutoData]
|
|
|
|
|
public async Task ValidateOrganizationUserUpdatePermissions_WithoutManageUsersPermission_Throws(
|
|
|
|
|
Guid organizationId,
|
|
|
|
|
OrganizationUserInvite organizationUserInvite,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.ValidateOrganizationUserUpdatePermissions(organizationId, organizationUserInvite.Type.Value, null, organizationUserInvite.Permissions));
|
|
|
|
|
|
|
|
|
|
Assert.Contains("your account does not have permission to manage users.", exception.Message.ToLowerInvariant());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[OrganizationInviteCustomize(
|
|
|
|
|
InviteeUserType = OrganizationUserType.Admin,
|
|
|
|
|
InvitorUserType = OrganizationUserType.Custom
|
|
|
|
|
), BitAutoData]
|
|
|
|
|
public async Task ValidateOrganizationUserUpdatePermissions_WithCustomAddingAdmin_Throws(
|
|
|
|
|
Guid organizationId,
|
|
|
|
|
OrganizationUserInvite organizationUserInvite,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organizationId).Returns(true);
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.ValidateOrganizationUserUpdatePermissions(organizationId, organizationUserInvite.Type.Value, null, organizationUserInvite.Permissions));
|
|
|
|
|
|
|
|
|
|
Assert.Contains("custom users can not manage admins or owners.", exception.Message.ToLowerInvariant());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[OrganizationInviteCustomize(
|
|
|
|
|
InviteeUserType = OrganizationUserType.Custom,
|
|
|
|
|
InvitorUserType = OrganizationUserType.Custom
|
|
|
|
|
), BitAutoData]
|
|
|
|
|
public async Task ValidateOrganizationUserUpdatePermissions_WithCustomAddingUser_WithoutPermissions_Throws(
|
|
|
|
|
Guid organizationId,
|
|
|
|
|
OrganizationUserInvite organizationUserInvite,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
var invitePermissions = new Permissions { AccessReports = true };
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organizationId).Returns(true);
|
|
|
|
|
sutProvider.GetDependency<ICurrentContext>().AccessReports(organizationId).Returns(false);
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.ValidateOrganizationUserUpdatePermissions(organizationId, organizationUserInvite.Type.Value, null, invitePermissions));
|
|
|
|
|
|
|
|
|
|
Assert.Contains("custom users can only grant the same custom permissions that they have.", exception.Message.ToLowerInvariant());
|
|
|
|
|
}
|
2023-12-18 17:16:17 +01:00
|
|
|
|
|
2024-04-18 12:42:30 +02:00
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task HasConfirmedOwnersExceptAsync_WithConfirmedOwners_ReturnsTrue(
|
|
|
|
|
Guid organizationId,
|
|
|
|
|
IEnumerable<Guid> organizationUsersId,
|
|
|
|
|
ICollection<OrganizationUser> owners,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
|
|
|
.GetManyByOrganizationAsync(organizationId, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(owners);
|
|
|
|
|
|
|
|
|
|
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organizationId, organizationUsersId);
|
|
|
|
|
|
|
|
|
|
Assert.True(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task HasConfirmedOwnersExceptAsync_WithConfirmedProviders_ReturnsTrue(
|
|
|
|
|
Guid organizationId,
|
|
|
|
|
IEnumerable<Guid> organizationUsersId,
|
|
|
|
|
ICollection<ProviderUser> providerUsers,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
|
|
|
.GetManyByOrganizationAsync(organizationId, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new List<OrganizationUser>());
|
|
|
|
|
|
|
|
|
|
sutProvider.GetDependency<IProviderUserRepository>()
|
|
|
|
|
.GetManyByOrganizationAsync(organizationId, ProviderUserStatusType.Confirmed)
|
|
|
|
|
.Returns(providerUsers);
|
|
|
|
|
|
|
|
|
|
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organizationId, organizationUsersId);
|
|
|
|
|
|
|
|
|
|
Assert.True(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task HasConfirmedOwnersExceptAsync_WithNoConfirmedOwnersOrProviders_ReturnsFalse(
|
|
|
|
|
Guid organizationId,
|
|
|
|
|
IEnumerable<Guid> organizationUsersId,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
|
|
|
|
.GetManyByOrganizationAsync(organizationId, OrganizationUserType.Owner)
|
|
|
|
|
.Returns(new List<OrganizationUser>());
|
|
|
|
|
|
|
|
|
|
sutProvider.GetDependency<IProviderUserRepository>()
|
|
|
|
|
.GetManyByOrganizationAsync(organizationId, ProviderUserStatusType.Confirmed)
|
|
|
|
|
.Returns(new List<ProviderUser>());
|
|
|
|
|
|
|
|
|
|
var result = await sutProvider.Sut.HasConfirmedOwnersExceptAsync(organizationId, organizationUsersId);
|
|
|
|
|
|
|
|
|
|
Assert.False(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[BitAutoData(OrganizationUserType.Owner)]
|
|
|
|
|
[BitAutoData(OrganizationUserType.Admin)]
|
|
|
|
|
[BitAutoData(OrganizationUserType.User)]
|
|
|
|
|
public async Task ValidateOrganizationCustomPermissionsEnabledAsync_WithNotCustomType_IsValid(
|
|
|
|
|
OrganizationUserType newType,
|
|
|
|
|
Guid organizationId,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
await sutProvider.Sut.ValidateOrganizationCustomPermissionsEnabledAsync(organizationId, newType);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task ValidateOrganizationCustomPermissionsEnabledAsync_NotExistingOrg_ThrowsNotFound(
|
|
|
|
|
Guid organizationId,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.ValidateOrganizationCustomPermissionsEnabledAsync(organizationId, OrganizationUserType.Custom));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task ValidateOrganizationCustomPermissionsEnabledAsync_WithUseCustomPermissionsDisabled_ThrowsBadRequest(
|
|
|
|
|
Organization organization,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
organization.UseCustomPermissions = false;
|
|
|
|
|
|
|
|
|
|
sutProvider.GetDependency<IOrganizationRepository>()
|
|
|
|
|
.GetByIdAsync(organization.Id)
|
|
|
|
|
.Returns(organization);
|
|
|
|
|
|
|
|
|
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
|
|
|
|
() => sutProvider.Sut.ValidateOrganizationCustomPermissionsEnabledAsync(organization.Id, OrganizationUserType.Custom));
|
|
|
|
|
|
|
|
|
|
Assert.Contains("to enable custom permissions", exception.Message.ToLowerInvariant());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Theory, BitAutoData]
|
|
|
|
|
public async Task ValidateOrganizationCustomPermissionsEnabledAsync_WithUseCustomPermissionsEnabled_IsValid(
|
|
|
|
|
Organization organization,
|
|
|
|
|
SutProvider<OrganizationService> sutProvider)
|
|
|
|
|
{
|
|
|
|
|
organization.UseCustomPermissions = true;
|
|
|
|
|
|
|
|
|
|
sutProvider.GetDependency<IOrganizationRepository>()
|
|
|
|
|
.GetByIdAsync(organization.Id)
|
|
|
|
|
.Returns(organization);
|
|
|
|
|
|
|
|
|
|
await sutProvider.Sut.ValidateOrganizationCustomPermissionsEnabledAsync(organization.Id, OrganizationUserType.Custom);
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-18 17:16:17 +01:00
|
|
|
|
// Must set real guids in order for dictionary of guids to not throw aggregate exceptions
|
|
|
|
|
private void SetupOrgUserRepositoryCreateManyAsyncMock(IOrganizationUserRepository organizationUserRepository)
|
|
|
|
|
{
|
|
|
|
|
organizationUserRepository.CreateManyAsync(Arg.Any<IEnumerable<OrganizationUser>>()).Returns(
|
|
|
|
|
info =>
|
|
|
|
|
{
|
|
|
|
|
var orgUsers = info.Arg<IEnumerable<OrganizationUser>>();
|
|
|
|
|
foreach (var orgUser in orgUsers)
|
|
|
|
|
{
|
|
|
|
|
orgUser.Id = Guid.NewGuid();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Task.FromResult<ICollection<Guid>>(orgUsers.Select(u => u.Id).ToList());
|
|
|
|
|
}
|
|
|
|
|
);
|
[AC-2646] Remove FC MVP dead code from Core (#4281)
* chore: remove fc refs in CreateGroup and UpdateGroup commands, refs AC-2646
* chore: remove fc refs and update interface to represent usage/get rid of double enumeration warnings, refs AC-2646
* chore: remove org/provider service fc callers, refs AC-2646
* chore: remove collection service fc callers, refs AC-2646
* chore: remove cipher service import ciphers fc callers, refs AC-2646
* fix: UpdateOrganizationUserCommandTests collections to list, refs AC-2646
* fix: update CreateGroupCommandTests, refs AC-2646
* fix: adjust UpdateGroupCommandTests, refs AC-2646
* fix: adjust UpdateOrganizationUserCommandTests for FC always true, refs AC-2646
* fix: update CollectionServiceTests, refs AC-2646
* fix: remove unnecessary test with fc disabled, refs AC-2646
* fix: update tests to account for AccessAll removal and Manager removal, refs AC-2646
* chore: remove dependence on FC flag for tests, refs AC-2646
2024-07-12 19:25:04 +02:00
|
|
|
|
|
|
|
|
|
organizationUserRepository.CreateAsync(Arg.Any<OrganizationUser>(), Arg.Any<IEnumerable<CollectionAccessSelection>>()).Returns(
|
|
|
|
|
info =>
|
|
|
|
|
{
|
|
|
|
|
var orgUser = info.Arg<OrganizationUser>();
|
|
|
|
|
orgUser.Id = Guid.NewGuid();
|
|
|
|
|
return Task.FromResult<Guid>(orgUser.Id);
|
|
|
|
|
}
|
|
|
|
|
);
|
2023-12-18 17:16:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Must set real guids in order for dictionary of guids to not throw aggregate exceptions
|
|
|
|
|
private void SetupOrgUserRepositoryCreateAsyncMock(IOrganizationUserRepository organizationUserRepository)
|
|
|
|
|
{
|
|
|
|
|
organizationUserRepository.CreateAsync(Arg.Any<OrganizationUser>(),
|
|
|
|
|
Arg.Any<IEnumerable<CollectionAccessSelection>>()).Returns(
|
|
|
|
|
info =>
|
|
|
|
|
{
|
|
|
|
|
var orgUser = info.Arg<OrganizationUser>();
|
|
|
|
|
orgUser.Id = Guid.NewGuid();
|
|
|
|
|
return Task.FromResult<Guid>(orgUser.Id);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
2019-02-19 22:27:05 +01:00
|
|
|
|
}
|