(26/30)みんなで学ぶBlazor:ユーザーにロールを割り当てる

(26/30)みんなで学ぶBlazor:ユーザーにロールを割り当てる

昨日ロールの`CRUD`機能がすべて完了しました。次はユーザーにロールを割り当てる作業です。

最終更新 2021/12/24 23:20
StrayaWorker
読了目安 4 分
カテゴリ
Blazor
テーマ
一緒に学ぶBlazorシリーズ
タグ
.NET C# ASP.NET Core Blazor

昨日、ロールのCRUD機能がすべて完了したので、次にロールをユーザーに割り当てます。まずViewModel CustomUserRoleViewModelを作成します。これはロールに属するユーザーを表示するためのViewModelです。

namespace BlazorServer.ViewModels;

public class CustomUserRoleViewModel
{
	public string? UserId { get; set; }

	public string? UserName { get; set; }

	public bool IsSelected { get; set; }
}

次にIRolesRepositoryRolesRepositoryに、ロール内のユーザーを編集する機能を追加しました。ここではオーバーロードを使用しており、最初のメソッドはGet(ページの初期データを取得)に相当し、2番目のメソッドはPost(変更後のデータを送信)に相当します。

IRolesRepository.cs

…
    Task<List<CustomUserRoleViewModel>> EditUsersInRoleAsync(string roleId);
	Task<ResultViewModel> EditUsersInRoleAsync(List<CustomUserRoleViewModel> model, string roleId);

RolesRepository.cs

…
    public async Task<List<CustomUserRoleViewModel>> EditUsersInRoleAsync(string roleId)
	{
		var role = await _roleManager.FindByIdAsync(roleId);
		var model = new List<CustomUserRoleViewModel>();

        // ここで注意:_userManager.Users.ToList()の後には必ず.ToList()を付けてください。そうしないと例外がスローされます:https://stackoverflow.com/questions/60727080/helping-solving-there-is-already-an-open-datareader-associated-with-this-comman
		foreach (var user in _userManager.Users.ToList())
		{
			var userRoleViewModel = new CustomUserRoleViewModel
			{
				UserId = user.Id,
				UserName = user.UserName
			};

			if (await _userManager.IsInRoleAsync(user, role.Name))
				userRoleViewModel.IsSelected = true;
			else
				userRoleViewModel.IsSelected = false;
			model.Add(userRoleViewModel);
		}

		return model;
	}

	public async Task<ResultViewModel> EditUsersInRoleAsync(List<CustomUserRoleViewModel> model, string roleId)
	{
		var role = await _roleManager.FindByIdAsync(roleId);
		foreach (var m in model)
		{
			var user = await _userManager.FindByIdAsync(m.UserId);
			IdentityResult result;
			if (m.IsSelected && !await _userManager.IsInRoleAsync(user, role.Name))
				result = await _userManager.AddToRoleAsync(user, role.Name);
			else if (!m.IsSelected && await _userManager.IsInRoleAsync(user, role.Name))
				result = await _userManager.RemoveFromRoleAsync(user, role.Name);
			else
				continue;
			if (result.Succeeded)
			{
				if (model.Count > 0)
					continue;
				return new ResultViewModel
				{
					Message = roleId,
					IsSuccess = true
				};
			}

			return new ResultViewModel
			{
				Message = roleId,
				IsSuccess = false
			};
		}

		return new ResultViewModel
		{
			Message = roleId,
			IsSuccess = true
		};
	}

次にページを追加します。

EditUsersInRole.razor.cs

using BlazorServer.Repository;
using BlazorServer.Shared;
using BlazorServer.ViewModels;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;

namespace BlazorServer.Pages.RolesManagement;

public partial class EditUsersInRole
{
	[Inject] protected IRolesRepository? RolesRepository { get; set; }
	[Inject] protected NavigationManager? NavigationManager { get; set; }
	[Inject] protected IJSRuntime? Js { get; set; }
	private JsInteropClasses? _jsClass;
	[Parameter] public string? Id { get; set; }
	public List<CustomUserRoleViewModel> UserRoleViewModel { get; set; } = new List<CustomUserRoleViewModel>();

	protected override async Task OnInitializedAsync()
	{
		await LoadData();
		_jsClass = new JsInteropClasses(Js!);
	}

	private async Task LoadData()
	{
		UserRoleViewModel = (await RolesRepository!.EditUsersInRoleAsync(Id!)).ToList();
	}


	public async Task HandleValidSubmit()
	{
		var result = await RolesRepository!.EditUsersInRoleAsync(UserRoleViewModel, Id!);

		if (result.IsSuccess)
		{
			NavigationManager!.NavigateTo($"/RolesManagement/EditRole/{Id}");
		}
		else
		{
			await _jsClass!.Alert(result.Message!);
		}
	}

	public void Cancel()
	{
		NavigationManager!.NavigateTo($"/RolesManagement/EditRole/{Id}");
	}
}

EditUsersInRole.razor

@page "/RolesManagement/EditUsersInRole/{Id}" @attribute [Authorize]

<EditForm Model="UserRoleViewModel" OnValidSubmit="HandleValidSubmit">
  <DataAnnotationsValidator />
  <ValidationSummary />
  <div class="card">
    <div class="card-header">
      <h2>ロールにユーザーを追加または削除する</h2>
    </div>
    <div class="card-body">
      @foreach (var user in UserRoleViewModel) {
      <div class="form-check m-1">
        <label class="form-check-label">
          <InputCheckbox @bind-Value="@user.IsSelected"></InputCheckbox>
          @user.UserName
        </label>
      </div>
      }
    </div>
    <div class="card-footer">
      <button type="submit" class="btn btn-primary">更新</button>
      <button type="button" class="btn btn-danger" @onclick="@Cancel">
        キャンセル
      </button>
    </div>
  </div>
</EditForm>

EditRole.razor.csにメソッドを追加し、EditUsersInRole.razorへ遷移することで、ロールに属するユーザーを編集できるようにします。

public void EditUsersInRole()
{
    NavigationManager!.NavigateTo($"/RolesManagement/EditUsersInRole/{Id}");
}

EditRole.razorにボタンを追加し、このメソッドを呼び出せるようにします。

<button type="button" class="btn btn-info" @onclick="EditUsersInRole">
  このロールに属するユーザーを追加または削除する
</button>

Webアプリを起動し、ロール編集ユーザーページに移動すると、現在1人のユーザーのみが表示されます。このユーザーにAdminロールを割り当てます。これでAdmintest@gmail.comの割り当てが完了しました。ただし、成功したかどうかをテストするには、ページに制限を追加し、さらにテスト用のユーザーを追加する必要があります。

まず、各razor component@attribute [Authorize]@attribute [Authorize(Roles = "Admin")]に変更します。これにより、このページを表示できるのはAdminロールを持つユーザーのみとなります。

次にNavMenu.razorに移動し、既存の<AuthorizeView>の上に以下のコードを追加します。同様に、Rolesリンクを表示できるのはAdminロールを持つユーザーのみとし、元の<AuthorizeView>内のRolesリンクは削除します。

<AuthorizeView Roles="Admin">
  <Authorized>
    <li class="nav-item px-3">
      <NavLink
        class="nav-link"
        href="RolesManagement/RolesList"
        Match="NavLinkMatch.All"
      >
        <span class="bi bi-kanban-fill h4 p-2 mb-0" aria-hidden="true"></span>
        Roles
      </NavLink>
    </li>
  </Authorized>
</AuthorizeView>

最後に、user@gmail.comという名前のユーザーを新規作成します。このユーザーでログインすると、左側のRolesが表示されなくなり、URLを直接入力してもアクセス権がないというエラーが表示されます。これが最も基本的なロール(Role)認可です。システムが非常にシンプルで、ロールだけで権限を分割するだけで十分であれば、この方法で要件を満たせます。

参照:

  1. Add or remove users from role in asp net core

注:本記事のコードは .NET 6 + Visual Studio 2022 でリファクタリングされています。原文リンクをクリックしてリファクタリング後のコードと比較しながら学習できます。ご清読ありがとうございます。原作者をサポートしてください。

さらに探索

関連読書

その他の記事
同じカテゴリ / 同じタグ 2021/12/25

(29/30)みんなで学ぶBlazor:Blazor単体テスト

システム開発において最も退屈なプロセスは、おそらくバグ修正です。特に、null オブジェクトにアクセスしようとするエラー(`Object reference not set to an instance of an object.`)は、多くの初心者が最初に直面する問題です。退屈なバグ修正から解放されるために、この記事では「単体テスト」を紹介します。

続きを読む
同じカテゴリ / 同じタグ 2021/12/25

(28/30)みんなで学ぶBlazor:ポリシーベースの認可

以前に「ASP.NET Core Identity」は「Claim」ベースの検証を使用すると述べましたが、実は「ASP.NET Core Identity」には異なる種類の認可方法があります。最も簡単な「ログイン認可」「ロール認可」「Claim認可」ですが、これらはすべて同じ方法で実現されています:原則認可(ポリシーベースの認可)です。

続きを読む