421.5 Abp microserices and Keycloak - chempkovsky/CS82ANGULAR GitHub Wiki
- Only security aspects will be discussed and implemented.
- In this article we will rewrite
Add User-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 createIdentity-folder- In the
Identity-folder of therupbes.tstapp.Auth.Host.csproj-project createCustomIdentityUserAppService-class as follows:-
User Idis getting fromExtraPropertiesof theIdentityUserCreateDto-object
-
- In the
Click to show the code
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using System;
using System.Threading.Tasks;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.ObjectExtending;
namespace rupbes.tstapp.Identity
{
[Dependency(ReplaceServices = true)]
public class CustomIdentityUserAppService : IdentityUserAppService
{
public CustomIdentityUserAppService(
IdentityUserManager userManager,
IIdentityUserRepository userRepository,
IIdentityRoleRepository roleRepository,
IOptions<IdentityOptions> identityOptions,
IPermissionChecker permissionChecker) : base(userManager, userRepository, roleRepository, identityOptions, permissionChecker) { }
[Authorize(IdentityPermissions.Users.Create)]
public override async Task<IdentityUserDto> CreateAsync(IdentityUserCreateDto input)
{
await IdentityOptions.SetAsync();
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();
var user = new Volo.Abp.Identity.IdentityUser(
id,
input.UserName,
input.Email,
CurrentTenant.Id
);
input.MapExtraPropertiesTo(user);
(await UserManager.CreateAsync(user, input.Password)).CheckErrors();
await UpdateUserByInput(user, input);
(await UserManager.UpdateAsync(user)).CheckErrors();
await CurrentUnitOfWork.SaveChangesAsync();
return ObjectMapper.Map<Volo.Abp.Identity.IdentityUser, IdentityUserDto>(user);
}
}
}- In the
rupbes.tstapp.Web.Host.csproj-project createPages/Identity/Users-folder- In the
Pages/Identity/Users-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.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.Identity.Web.Pages.Identity.Users;
namespace rupbes.tstapp.Pages.Identity.Users
{
[Dependency(ReplaceServices = true)]
// [ExposeServices(typeof(CreateModalModel))]
[ExposeServices(typeof(CustomCreateModalModel), typeof(CreateModalModel))]
public class CustomCreateModalModel : CreateModalModel
{
[BindProperty]
public new CustomUserInfoViewModel UserInfo { get; set; } = new CustomUserInfoViewModel();
public class CustomUserInfoViewModel : UserInfoViewModel
{
[Required]
public Guid Id { get; set; } = Guid.Empty;
}
public CustomCreateModalModel(IIdentityUserAppService identityUserAppService) : base(identityUserAppService)
{
}
public override async Task<IActionResult> OnGetAsync()
{
UserInfo = new CustomUserInfoViewModel();
var roleDtoList = (await IdentityUserAppService.GetAssignableRolesAsync()).Items;
Roles = ObjectMapper.Map<IReadOnlyList<IdentityRoleDto>, AssignedRoleViewModel[]>(roleDtoList);
foreach (var role in Roles)
{
role.IsAssigned = role.IsDefault;
}
return Page();
}
public override async Task<NoContentResult> OnPostAsync()
{
ValidateModel();
var input = ObjectMapper.Map<CustomUserInfoViewModel, IdentityUserCreateDto>(UserInfo);
input.RoleNames = Roles.Where(r => r.IsAssigned).Select(r => r.Name).ToArray();
input.ExtraProperties.Add("Id", UserInfo.Id.ToString());
await IdentityUserAppService.CreateAsync(input);
return NoContent();
}
}
}- 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.Identity.Localization
@using Volo.Abp.Identity.Web.Pages.Identity.Users
@using Volo.Abp.Localization
@using Volo.Abp.ObjectExtending
@using Volo.Abp.Data
@using Volo.Abp.Identity
@model rupbes.tstapp.Pages.Identity.Users.CustomCreateModalModel
@inject IHtmlLocalizer<IdentityResource> L
@inject IStringLocalizerFactory StringLocalizerFactory
@{
Layout = null;
}
<form method="post" asp-page="/Identity/Users/CreateModal">
<abp-modal>
<abp-modal-header title="@L["NewUser"].Value"></abp-modal-header>
<abp-modal-body>
<abp-tabs name="create-user-modal-tabs">
<abp-tab title="@L["UserInformations"].Value">
<div >
@* TODO: Can we use dynamic form? *@
<abp-input asp-for="UserInfo.Id" />
<abp-input asp-for="UserInfo.UserName" />
<abp-input asp-for="UserInfo.Name" />
<abp-input asp-for="UserInfo.Surname" />
<div class="mb-3">
<label asp-for="UserInfo.Password" class="form-label">@L["Password"] *</label>
<div class="input-group">
<input type="password" class="form-control" autocomplete="new-password" maxlength="@IdentityUserConsts.MaxPasswordLength" asp-for="UserInfo.Password" />
<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="UserInfo.Password"></span>
</div>
<abp-input asp-for="UserInfo.Email" />
<abp-input asp-for="UserInfo.PhoneNumber" />
<abp-input asp-for="UserInfo.IsActive" />
<abp-input asp-for="UserInfo.LockoutEnabled" label-tooltip="@L.GetString("Description:LockoutEnabled")" />
@foreach (var propertyInfo in await ObjectExtensionManager.Instance.GetPropertiesAndCheckPolicyAsync<CreateModalModel.UserInfoViewModel>(HttpContext.RequestServices))
{
if (!propertyInfo.Name.EndsWith("_Text"))
{
if (propertyInfo.Type.IsEnum || !propertyInfo.Lookup.Url.IsNullOrEmpty())
{
if (propertyInfo.Type.IsEnum)
{
Model.UserInfo.ExtraProperties.ToEnum(propertyInfo.Name, propertyInfo.Type);
}
<abp-select asp-for="UserInfo.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
autocomplete-api-url="@propertyInfo.Lookup.Url"
autocomplete-selected-item-name="@Model.UserInfo.GetProperty(propertyInfo.Name+"_Text")"
autocomplete-selected-item-value="@Model.UserInfo.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="UserInfo.ExtraProperties[propertyInfo.Name]"
label="@propertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)"
asp-format="@propertyInfo.GetInputFormatOrNull()"
value="@propertyInfo.GetInputValueOrNull(Model.UserInfo.GetProperty(propertyInfo.Name))" />
}
}
}
</div>
</abp-tab>
<abp-tab title="@L["Roles"].Value">
<div>
@for (var i = 0; i < Model.Roles.Length; i++)
{
var role = Model.Roles[i];
<abp-input abp-id-name="@Model.Roles[i].IsAssigned" asp-for="@role.IsAssigned" label="@role.Name" />
<input abp-id-name="@Model.Roles[i].Name" asp-for="@role.Name" />
}
</div>
</abp-tab>
</abp-tabs>
</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
- Click
Add New User-page.-
Id-field is avalable for editing
-