diff --git a/docs/index.md b/docs/index.md index 2e4cedc9..f0450613 100644 --- a/docs/index.md +++ b/docs/index.md @@ -490,8 +490,20 @@ mutation requestRegistration (command: InputRequestRegistrationType!) { } ``` -> The mutation registers a company when all argumets have been provided, and registers a customer only when the company value is null. If a company is created, the customer becomes its member and owner. +> The mutation registers a company when all argumets have been provided, and registers a customer only when the company value is null. If a company is created, the customer becomes its member and owner. In this case customer gets the **Organization maintainer** role whose name or ID must be provided in **appsettings.json**. > > The user that creates a company and/or customer is always displayed as *frontend*. > > The company status is determined by the *Company default status* store setting, while contact and account statuses come from the *Contact default status* setting. Both settings must be provided in advance. +> +> Example of the role name settings provided below. + +``` +{ +... + "FrontendSecurity": { + "OrganizationMaintainerRole": "Organization maintainer" + }, +... +} +``` diff --git a/src/VirtoCommerce.ProfileExperienceApiModule.Data/Commands/RegisterRequestCommandHandler.cs b/src/VirtoCommerce.ProfileExperienceApiModule.Data/Commands/RegisterRequestCommandHandler.cs index 10f84d6e..04206649 100644 --- a/src/VirtoCommerce.ProfileExperienceApiModule.Data/Commands/RegisterRequestCommandHandler.cs +++ b/src/VirtoCommerce.ProfileExperienceApiModule.Data/Commands/RegisterRequestCommandHandler.cs @@ -26,6 +26,9 @@ using VirtoCommerce.NotificationsModule.Core.Model; using VirtoCommerce.Platform.Security; using VirtoCommerce.ProfileExperienceApiModule.Data.Models.RegisterOrganization; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; +using VirtoCommerce.ProfileExperienceApiModule.Data.Configuration; namespace VirtoCommerce.ProfileExperienceApiModule.Data.Commands { @@ -41,10 +44,10 @@ public class RegisterRequestCommandHandler : IRequestHandler _securityOptions; private const string Creator = "frontend"; private const string UserType = "Manager"; - private const string MaintainerRoleId = "org-maintainer"; #pragma warning disable S107 public RegisterRequestCommandHandler(IMapper mapper, IDynamicPropertyUpdaterService dynamicPropertyUpdater, @@ -55,7 +58,8 @@ public RegisterRequestCommandHandler(IMapper mapper, IAccountService accountService, NewContactValidator contactValidator, AccountValidator accountValidator, - OrganizationValidator organizationValidator) + OrganizationValidator organizationValidator, + IOptions securityOptions) #pragma warning restore S107 { _mapper = mapper; @@ -68,6 +72,7 @@ public RegisterRequestCommandHandler(IMapper mapper, _contactValidator = contactValidator; _accountValidator = accountValidator; _organizationValidator = organizationValidator; + _securityOptions = securityOptions; } public virtual async Task Handle(RegisterRequestCommand request, CancellationToken cancellationToken) @@ -128,10 +133,9 @@ private async Task ProcessRequestAsync(RegisterReque if (organization != null) { - var maintainerRole = await _accountService.FindRoleById(MaintainerRoleId); + var maintainerRole = await GetMaintainerRole(result, tokenSource); if (maintainerRole == null) { - SetErrorResult(result, "Role not found",$"Organization maintainer role with id {MaintainerRoleId} not found", tokenSource); return result; } @@ -181,6 +185,24 @@ private async Task ProcessRequestAsync(RegisterReque return result; } + private async Task GetMaintainerRole(RegisterOrganizationResult result, CancellationTokenSource tokenSource) + { + var maintainerRoleId = _securityOptions.Value.OrganizationMaintainerRole; + if (maintainerRoleId == null) + { + SetErrorResult(result, "Role not configured", "Organization maintainer role configuration is not found in the app settings", tokenSource); + return null; + } + + var role = await _accountService.FindRoleByName(maintainerRoleId) ?? await _accountService.FindRoleById(maintainerRoleId); + if (role == null) + { + SetErrorResult(result, "Role not found", $"Organization maintainer role {maintainerRoleId} not found", tokenSource); + } + + return role; + } + private static AccountCreationResult GetAccountCreationResult(IdentityResult identityResult, ApplicationUser account) { return new AccountCreationResult diff --git a/src/VirtoCommerce.ProfileExperienceApiModule.Data/Configuration/FrontendSecurityOptions.cs b/src/VirtoCommerce.ProfileExperienceApiModule.Data/Configuration/FrontendSecurityOptions.cs new file mode 100644 index 00000000..dd405502 --- /dev/null +++ b/src/VirtoCommerce.ProfileExperienceApiModule.Data/Configuration/FrontendSecurityOptions.cs @@ -0,0 +1,7 @@ +namespace VirtoCommerce.ProfileExperienceApiModule.Data.Configuration +{ + public class FrontendSecurityOptions + { + public string OrganizationMaintainerRole { get; set; } + } +} diff --git a/src/VirtoCommerce.ProfileExperienceApiModule.Data/Services/AccountsService.cs b/src/VirtoCommerce.ProfileExperienceApiModule.Data/Services/AccountsService.cs index 7085382b..b29028d5 100644 --- a/src/VirtoCommerce.ProfileExperienceApiModule.Data/Services/AccountsService.cs +++ b/src/VirtoCommerce.ProfileExperienceApiModule.Data/Services/AccountsService.cs @@ -59,5 +59,11 @@ public async Task FindRoleById(string roleId) using var roleManager = _roleManagerFactory(); return await roleManager.FindByIdAsync(roleId); } + + public async Task FindRoleByName(string roleName) + { + using var roleManager = _roleManagerFactory(); + return await roleManager.FindByNameAsync(roleName); + } } } diff --git a/src/VirtoCommerce.ProfileExperienceApiModule.Data/Services/IAccountService.cs b/src/VirtoCommerce.ProfileExperienceApiModule.Data/Services/IAccountService.cs index 9feca889..97a463a6 100644 --- a/src/VirtoCommerce.ProfileExperienceApiModule.Data/Services/IAccountService.cs +++ b/src/VirtoCommerce.ProfileExperienceApiModule.Data/Services/IAccountService.cs @@ -9,5 +9,6 @@ public interface IAccountService public Task CreateAccountAsync(ApplicationUser account); public Task GetAccountAsync(string userName); public Task FindRoleById(string roleId); + public Task FindRoleByName(string roleName); } } diff --git a/src/VirtoCommerce.ProfileExperienceApiModule.Web/Module.cs b/src/VirtoCommerce.ProfileExperienceApiModule.Web/Module.cs index e942d53d..96296c75 100644 --- a/src/VirtoCommerce.ProfileExperienceApiModule.Web/Module.cs +++ b/src/VirtoCommerce.ProfileExperienceApiModule.Web/Module.cs @@ -3,6 +3,7 @@ using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using VirtoCommerce.ExperienceApiModule.Core.Extensions; using VirtoCommerce.ExperienceApiModule.Core.Infrastructure; @@ -15,6 +16,7 @@ using VirtoCommerce.ProfileExperienceApiModule.Data.Aggregates.Contact; using VirtoCommerce.ProfileExperienceApiModule.Data.Aggregates.Organization; using VirtoCommerce.ProfileExperienceApiModule.Data.Authorization; +using VirtoCommerce.ProfileExperienceApiModule.Data.Configuration; using VirtoCommerce.ProfileExperienceApiModule.Data.Middlewares; using VirtoCommerce.ProfileExperienceApiModule.Data.Schemas; using VirtoCommerce.ProfileExperienceApiModule.Data.Services; @@ -23,9 +25,10 @@ namespace VirtoCommerce.CusomersExperienceApi.Web { - public class Module : IModule + public class Module : IModule, IHasConfiguration { public ManifestModuleInfo ModuleInfo { get; set; } + public IConfiguration Configuration { get; set; } public void Initialize(IServiceCollection serviceCollection) { @@ -44,6 +47,7 @@ public void Initialize(IServiceCollection serviceCollection) serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddSingleton(); + serviceCollection.AddOptions().Bind(Configuration.GetSection("FrontendSecurity")).ValidateDataAnnotations(); serviceCollection.AddAutoMapper(typeof(XProfileAnchor));