421.6 Abp microserices and Keycloak - chempkovsky/CS82ANGULAR GitHub Wiki
- Only security aspects will be discussed and implemented.
- In this article we will rewrite
Add Tenant-page
- We are currently using:
- MS Visual studio 22 (Community Edition)
- Volo.Abp.Studio.Cli v.1.1.1 Community Edition
- Volo.Abp.Cli v.9.2.3 Community Edition
- ABP Studio v.1.1.1 Community Edition
- Keycloak v.26.3.0
- Redis v=7.0.15
- In the
rupbes.tstapp.Auth.Host.csproj-project createTenantManagement-folder- In the
TenantManagement-folder of therupbes.tstapp.Auth.Host.csproj-project createCustomTenantAppService-class as follows:-
User Idis getting fromExtraPropertiesof theTenantCreateDto-object
-
- In the
Click to show the code
using Microsoft.AspNetCore.Authorization;
using System;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.EventBus.Local;
using Volo.Abp.MultiTenancy;
using Volo.Abp.ObjectExtending;
using Volo.Abp.TenantManagement;
namespace rupbes.tstapp.TenantManagement
{
[Dependency(ReplaceServices = true)]
public class CustomTenantAppService : TenantAppService
{
public CustomTenantAppService(ITenantRepository tenantRepository, ITenantManager tenantManager, IDataSeeder dataSeeder, IDistributedEventBus distributedEventBus, ILocalEventBus localEventBus) : base(tenantRepository, tenantManager, dataSeeder, distributedEventBus, localEventBus)
{
}
[Authorize(TenantManagementPermissions.Tenants.Create)]
public override async Task<TenantDto> CreateAsync(TenantCreateDto input)
{
var tenant = await TenantManager.CreateAsync(input.Name);
input.MapExtraPropertiesTo(tenant);
await TenantRepository.InsertAsync(tenant);
await CurrentUnitOfWork.SaveChangesAsync();
Guid id = Guid.Empty;
if (input.ExtraProperties.TryGetValue("Id", out var idobj))
{
if (idobj != null)
{
id = new Guid((string)idobj);
}
}
if (id == Guid.Empty) id = GuidGenerator.Create();
await DistributedEventBus.PublishAsync(
new TenantCreatedEto
{
Id = tenant.Id,
Name = tenant.Name,
Properties =
{
{ "AdminId", id.ToString() },
{ "AdminEmail", input.AdminEmailAddress },
{ "AdminPassword", input.AdminPassword }
}
});
using (CurrentTenant.Change(tenant.Id, tenant.Name))
{
//TODO: Handle database creation?
// TODO: Seeder might be triggered via event handler.
await DataSeeder.SeedAsync(
new DataSeedContext(tenant.Id)
.WithProperty("AdminId", id)
.WithProperty("AdminEmail", input.AdminEmailAddress)
.WithProperty("AdminPassword", input.AdminPassword)
);
}
return ObjectMapper.Map<Tenant, TenantDto>(tenant);
}
}
}- In the code above we have
DataSeeder.SeedAsync( new DataSeedContext(tenant.Id).WithProperty("AdminId", id) ...- The description of the new
IdentityDataSeederimplementation is here
- The description of the new
- In the
rupbes.tstapp.Web.Host.csproj-project createPages/Identity/Users-folder- In the
Pages/TenantManagement/Tenants-folder of therupbes.tstapp.Auth.Host.csproj-project createCustomCreateModal-razor page - Modify
CustomCreateModal.cshtml.cs-file as follows
- In the
Click to show the code
using Microsoft.AspNetCore.Mvc;
using System;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Volo.Abp.TenantManagement;
using Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants;
namespace rupbes.tstapp.Pages.TenantManagement.Tenants
{
public class CustomCreateModalModel : CreateModalModel
{
[BindProperty]
public new CustomTenantInfoModel Tenant { get; set; } = null!;
public CustomCreateModalModel(ITenantAppService tenantAppService) : base(tenantAppService)
{
}
public override Task<IActionResult> OnGetAsync()
{
Tenant = new CustomTenantInfoModel();
return Task.FromResult<IActionResult>(Page());
}
public override async Task<IActionResult> OnPostAsync()
{
ValidateModel();
var input = ObjectMapper.Map<TenantInfoModel, TenantCreateDto>(Tenant);
input.ExtraProperties.Add("Id", Tenant.Id.ToString());
await TenantAppService.CreateAsync(input);
return NoContent();
}
public class CustomTenantInfoModel : TenantInfoModel
{
[Required]
public Guid Id { get; set; } = Guid.Empty;
}
}
}- Modify
CustomCreateModal.cshtml-file as follows:
Click to show the code
@page
@using Microsoft.AspNetCore.Mvc.Localization
@using Microsoft.Extensions.Localization
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@using Volo.Abp.Localization
@using Volo.Abp.ObjectExtending
@using Volo.Abp.TenantManagement.Localization
@using Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants
@using Volo.Abp.Data
@using Volo.Abp.TenantManagement;
@model rupbes.tstapp.Pages.TenantManagement.Tenants.CustomCreateModalModel
@inject IHtmlLocalizer<AbpTenantManagementResource> L
@inject IStringLocalizerFactory StringLocalizerFactory
@{
Layout = null;
}
<form method="post" asp-page="/TenantManagement/Tenants/CreateModal">
<abp-modal>
<abp-modal-header title="@L["NewTenant"].Value"></abp-modal-header>
<abp-modal-body>
<abp-input asp-for="Tenant.Name" />
<abp-input asp-for="Tenant.Id" />
<abp-input asp-for="Tenant.AdminEmailAddress" />
<div class="mb-3">
<label class="form-label">@L["DisplayName:AdminPassword"].Value</label>
<span> * </span>
<div class="input-group">
<input type="password" class="form-control" maxlength="@TenantConsts.MaxPasswordLength" asp-for="Tenant.AdminPassword" />
<button class="btn btn-secondary" type="button" id="PasswordVisibilityButton">
<i class="fa fa-eye-slash" aria-hidden="true"></i>
</button>
</div>
<span asp-validation-for="Tenant.AdminPassword"></span>
</div>
@foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties<CreateModalModel.TenantInfoModel>())
{
if (!propertyInfo.Name.EndsWith("_Text"))
{
if (propertyInfo.Type.IsEnum || !propertyInfo.Lookup.Url.IsNullOrEmpty())
{
if (propertyInfo.Type.IsEnum)
{
Model.Tenant.ExtraProperties.ToEnum(propertyInfo.Name, propertyInfo.Type);
}
<abp-select asp-for="Tenant.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-selected-item-name="@Model.Tenant.GetProperty(propertyInfo.Name+"_Text")"
autocomplete-selected-item-value="@Model.Tenant.GetProperty(propertyInfo.Name)"
autocomplete-filter-param-name="@propertyInfo.Lookup.FilterParamName"
autocomplete-items-property-name="@propertyInfo.Lookup.ResultListPropertyName"
autocomplete-display-property-name="@propertyInfo.Lookup.DisplayPropertyName"
autocomplete-value-property-name="@propertyInfo.Lookup.ValuePropertyName"></abp-select>
}
else
{
<abp-input type="@propertyInfo.GetInputType()"
asp-for="Tenant.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
asp-format="@propertyInfo.GetInputFormatOrNull()"
value="@propertyInfo.GetInputValueOrNull(Model.Tenant.GetProperty(propertyInfo.Name))" />
}
}
}
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer>
</abp-modal>
</form>- rename
CustomCreateModal-page. The new name isCreateModal. - Start
rupbes.tstapp.AuthServer.csproj-app andrupbes.tstapp.Web.Host.csproj-app- Login as admin
- Goto
Administration/TenantManagement/Tenants-page - Click
New Tenant-page.-
Id-field is avalable for editing
-