論理積権限管理をLiferayに組み込む

# はじめに
Liferayに限らず、権限管理は論理和(disjunction)方式で動作するシステムが多いです。LiferayにはSubTypeという仕組みを利用して、日本でのコンテンツ表示権限管理などで必要になるケースが多い、論理積（conjunction)を標準機能を利用して追加していくことができます。このドキュメントでは論理積の拡張方法に関して説明します。

本記事の動作する[PoC、ソースコード](https://github.com/holatuwol/liferay-blogs-code/tree/master/example-role-conjunction#role-combinatorics)はここからダウンロードできます。

# 前提条件
- Windows 11 WSL2 (Ubuntu 18.04 LTS)
- Java 11
- Liferay 7.4 U92
- gradle 8.2
- Docker 4.25.2

# 論理和、論理積とは？
例えば、
- Role A
- Role B
- Role C
があると仮定した時に、以下のような動作をします。

|権限|詳細|
|:--|:--|
|論理和|Role A**または**Role B**または**Role Cの権限がある場合に、そのデータにアクセスできる。|
|論理積|Role A**かつ**Role B**かつ**Role Cの権限がある場合に、そのデータにアクセスできる。|

Liferayのロールの標準動作としては、上記でいう論理和権限の方式で動作します。

## 論理積権限が必要になるユースケース
:::note info
**ユースケース** : 場所、部門、役職の権限の任意の組み合わせを**すべて**満たす場合に、特定のアセットにアクセスできるようにしたい。(論理積権限での権限管理）ただし、Liferayのロールの標準動作(論理和権限）と共存させたい。
:::

上記のユースケースで、3つのサブタイプに分けられた18の標準ロールがあるとします：

|ロール|内容|
|:--|:--|
|場所|アメリカ合衆国、ブラジル、スペイン、ハンガリー、中国、日本、インド|
|部門|サブスクリプションサービス、カスタマーオペレーション、グローバルサービス、カスタマーサクセス、マーケティング、セールス、エンジニアリング|
|役職|最高責任者、副社長、ディレクター、フルタイム従業員、パートタイム従業員|

あるアセットへのアクセス権限を定義するために、複数の役割が必要な場合、すべての組み合わせをロールとして定義することはしたくないですよね。

現実的に利用するためには、役割の組み合わせ(論理積）で権限を設定できる必要があります。

これにアプローチする1つの方法は、以下の制限を適用することです：

- それぞれのサブタイプ（場所、部門、役職）内では、論理和として扱う。
- 各サブタイプ（場所、部門、役職）内では、論理積として扱う。

この単純化された仮定により、権限システムがどのように機能するかについて、次のように説明することができます：

1. 基本、指定されたサブタイプ以外のロールを使って、通常のLiferay権限チェック（論理和）を行います。
1. その後、場所、部門、役職が様々な組み合わせで指定されている論理積権限のチェックを行います。

# 注意点

## スコープの相互作用
Liferayでは通常、役割がより高いスコープで権限を持つ場合、その役割は適用可能なすべてのアセットにその権限を持ちます。ですが論理積権限管理が必要なシステムでは、_より高いスコープ_ の権限が何を意味するか、改めて検討する必要があります。

## 特別な役割
Liferayは通常、アセットの「所有者」に特別な権限を付与します。所有者権限は、論理積権限管理を有効にした場合でも適用されるよう考慮が必要です。また、管理者やサイト管理者などの役割についても、UIが権限を削除する機能を提供しない場合の扱いを考慮する必要があります。

## ユーザーインターフェース
論理積権限の組み合わせをわかりやすく表現した権限設定のUIが必要ですが、このサンプルでは実装していません。

従来通りの論理和権限で利用している標準の権限設定画面を応用しています。実際のプロジェクトで利用を検討する場合は、設定された組み合わせを簡単に確認できるユーザーインターフェース、および各役割サブタイプに新しいエントリを追加する画面の追加・変更が必要でしょう。

# コード解説
メインとなるのは以下のファイルです。

[RoleConjunctionResourcePermissionLocalServiceWrapper.java](https://github.com/holatuwol/liferay-blogs-code/blob/f679d11b834988e7d8bd430b0b964c7acf62b592/example-role-conjunction/role-conjunction-service/src/main/java/com/example/roleconjunction/service/RoleConjunctionResourcePermissionLocalServiceWrapper.java#L59-L88)

[29行目から43行目](https://github.com/holatuwol/liferay-blogs-code/blob/f679d11b834988e7d8bd430b0b964c7acf62b592/example-role-conjunction/role-conjunction-service/src/main/java/com/example/roleconjunction/service/RoleConjunctionResourcePermissionLocalServiceWrapper.java#L29)までで、論理和での権限チェックを行った後、[45行目から](https://github.com/holatuwol/liferay-blogs-code/blob/f679d11b834988e7d8bd430b0b964c7acf62b592/example-role-conjunction/role-conjunction-service/src/main/java/com/example/roleconjunction/service/RoleConjunctionResourcePermissionLocalServiceWrapper.java#L41-L53)ロールのサブタイプで構成された論理積権限に関してのチェックを行っています。

これにより、論理和と論理積権限の共存を可能にしています。

[RoleConjunctionHelper.java](https://github.com/holatuwol/liferay-blogs-code/blob/f679d11b834988e7d8bd430b0b964c7acf62b592/example-role-conjunction/role-conjunction-service/src/main/java/com/example/roleconjunction/service/util/RoleConjunctionHelper.java#L33C14-L33C36)
このファイル内で、ロールのサブタイプで定義されたロールを集約して、論理積権限用のリストを生成しています。

[RoleConjunctionConfiguration.java](https://github.com/holatuwol/liferay-blogs-code/blob/f679d11b834988e7d8bd430b0b964c7acf62b592/example-role-conjunction/role-conjunction-service/src/main/java/com/example/roleconjunction/service/configuration/RoleConjunctionConfiguration.java#L18)
このファイルでは、サブタイプを論理積として設定するための設定ファイル定義をしています。

# まとめ
いかがでしたでしょうか。日本の企業において、複数の権限を持つときにのみ権限を有効とする、といういった権限管理はよくあるもので、多大な労力をかけて実装しているケースもありますが、Liferayの場合はサンプルにあるような軽微なカスタマイズで実現することができます。

Liferayではこうした大企業向けエンタープライズ開発に興味があるコンサルタントを募集中です。ご興味あるかたは是非応募してみてください！