421.5 Abp microserices and Keycloak - chempkovsky/CS82ANGULAR GitHub Wiki

Notes

  • Only security aspects will be discussed and implemented.
  • In this article we will rewrite Add User-page

Tools

CustomIdentityUserAppService

  • In the rupbes.tstapp.Auth.Host.csproj-project create Identity-folder
    • In the Identity-folder of the rupbes.tstapp.Auth.Host.csproj-project create CustomIdentityUserAppService-class as follows:
      • User Id is getting from ExtraProperties of the IdentityUserCreateDto-object
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);
        }

    }
}

CustomCreateModal page

  • In the rupbes.tstapp.Web.Host.csproj-project create Pages/Identity/Users-folder
    • In the Pages/Identity/Users-folder of the rupbes.tstapp.Auth.Host.csproj-project create CustomCreateModal-razor page
    • Modify CustomCreateModal.cshtml.cs-file as follows
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 is CreateModal.
  • Start rupbes.tstapp.AuthServer.csproj-app and rupbes.tstapp.Web.Host.csproj-app
    • Login as admin
    • Click Add New User-page.
      • Id-field is avalable for editing
⚠️ **GitHub.com Fallback** ⚠️