前回、Liferayのリソース権限のDBレコードについて考察しました。今回は権限チェックの実行について考察したいと思います。
Liferayは開発者が直接にResourcePermissionを利用しないように設計されています。開発者フォーラムを見ているとit is inadvisable to manipulate the Liferay database directly、do not ever change it or write to the database(Liferayデータベースを直接操作することは推奨されていません。なので、直接変更したり、データベースに書き込んだりしないでください。)
のようなコメントがしばしばあります。そのため、Liferayはユーザ権限操作機能を提供しています。では、今回は権限操作機能中に一番基本のPermissionCheckerから考察しましょう。
まとめると、以下の情報をPermissionCheckerに提供すれば、Liferayはユーザの権限を判断することができます。
Liferayでは、companyIdはgroup、layoutなどエンティティーから取得できるため、権限チェックが必要なメソッドは以下となります。
ここまで考察すると、ようやく第一回の課題「Liferayが提供しているPermission関連クラスのシグネーチャが異なった理由」を解決しました。
ポートレットリソースの権限チェック
ブログポートレットの設定などボタンの表示はcom_liferay_blogs_web_portlet_BlogsPortletポートレットリソースの権限チェックにおいて判定しています。ポートレット権限の判定はLiferayの組み込み機能のため、公式ドキュメントの説明の通りresource-actionsにアクション定義を入れたらLiferayは自動的にブログポートレット権限チェックを行います。
ポートレットの権限判定はモデルリソースのようなOSGI構造を持っていません。具体的にはPortletPermissionImpl.javaを参考してください。
Liferayは開発者が直接にResourcePermissionを利用しないように設計されています。開発者フォーラムを見ているとit is inadvisable to manipulate the Liferay database directly、do not ever change it or write to the database(Liferayデータベースを直接操作することは推奨されていません。なので、直接変更したり、データベースに書き込んだりしないでください。)
のようなコメントがしばしばあります。そのため、Liferayはユーザ権限操作機能を提供しています。では、今回は権限操作機能中に一番基本のPermissionCheckerから考察しましょう。
PermissionCheckerのメソッド
Liferayでは、ユーザごとにPermissionCheckerのインスタンスを持っています。PermissionCheckerはResourceLocalServiceを介してResourcePermissionテーブルレコードを確認する形でユーザの権限をチェックしてるため、権限チェックのメソッドhasPermission()のメソッドシグネーチャはResourcePermissionテーブルの要素になります。
boolean hasPermission(group, name, primKey, actionId);
- LiferayのcompanyIdはgroupから取得できるため、PermissionCheckerではcompanyIdに代わりにgroupをパラメータとして利用している
- ユーザロールはPermissionCheckerのプロパティーのためメソッドに含まれていない
- リソースのグループID
- リソース名
- リソースのPrimeKey
- アクションID
Liferayにおいての権限チェック
あるユーザが、あるリソースに対する操作の権限をチェックする際の手順を考察しましょう。ResourcePermissionテーブルのレコードはロール毎に記録されているため、権限チェックの際、ユーザのロールを全部取得し、PermissionCheckerにおいて以下の判断を行うことが必要です。
※ 共通パラメータactionIdを除外し、roleIdはPermissionCheckerが読み出せるため除外する
リソース種別 | scope | 必要パラメータ | チェック内容 |
---|---|---|---|
ポートレット | 1 | portletId companyId | companyIdのLiferayインスタンス中 portletIdで特定されるポートレットに対する操作権限 primKey=companyId |
2 | portletId groupId | groupIdのサイト中 portletIdで特定されるポートレットに対する操作権限 primKey=groupId | |
3 | portletId | サイトロールが割り当てられる際のみ portletIdで特定されるポートレットに対する操作権限 primKey=0 | |
4 | portletId layoutId | primKey=layoutId + _LAYOUT_ + portletId で特定するポートレットに対する操作権限 | |
インスタンス化 できない仮想 モデル | 1 | モデル名 companyId | companyIdのLiferayインスタンス中 モデル名で特定されるモデルに対する操作権限 primKey=companyId |
2 | モデル名 groupId | groupIdのサイト中 モデル名で特定されるモデルに対する操作権限 primKey=groupId | |
3 | モデル名 | サイトロールが割り当てられる際のみ モデル名で特定されるモデルに対する操作権限 primKey=0 | |
4 | - | 仮想モデルはインスタンスを持っていない | |
インスタンス化 可能なモデル | 1 | モデル名 companyId | 当該ロールはポータル範囲の操作権限を持つこと primKey=companyId |
2 | モデル名 groupId | groupIdのサイト中 モデル名で特定されるモデルに対する操作権限 primKey=groupId | |
3 | モデル名 | サイトロールが割り当てられる際のみ モデル名で特定されるモデルに対する操作権限 primKey=0 | |
4 | モデル名 primaryKey | primaryKeyで特定されるモデルリソースに対する操作権限 |
リソース種別 | scope | 権限チェックメソッド | Liferayのインターフェース |
---|---|---|---|
ポートレット | 1 | check(permissionChecker, group, portlet, action) 3の場合、groupId=0 | PortletPermission |
2 | |||
3 | |||
4 | check(permissionChecker, group, layout, portletId, actionId) | ||
インスタンス化 できない仮想 モデル | 1 | check(permisisonChecker, group, name, action) | PortletResourcePermission nameはプロパティとして実装クラスに登録する |
2 | |||
3 | |||
4 | - | - | |
インスタンス化 可能なモデル | 1 | check(permissionChecker, primaryKey, actionId) check(permissionChecker, model, actionId) | ModelResourcePermission nameはプロパティとして実装クラスに登録する groupIdはLiferaymodelにおいてgetGroupIdで取得する |
2 | |||
3 | |||
4 |
※ ただし、まだ解決していない課題があります。現在のLiferayにおいて、モデルリソースとして定義される仮想モデルに対する権限チェックを行うクラス名はPortletResourcePermissionです。
- 例として、ブログポートレットのcom.liferay.blogs仮想モデルに対するADD_ENTRY権限の判定は BlogsPermission.contains(permissionChecker, scopeGroupId, ActionKeys.ADD_ENTRY)が行います。BlogsEntryPermission.javaのソースコードを確認すると、PortletResourcePermissionをOSGIサービスとして参照してることが分かります。
正直、理由が分かりません。
権限チェックに追加されたクラス
※ 以下の内容はLiferay-CE 7.1に基づいています。7.0は一部ヘルパークラスを持っていません。
Liferay7.0の権限カスタマイズに詳しい方はもう気づいたかもしれませんか、Liferay7.1からの権限設定手順で、権限を登録するに、ModelResourcePermissionLogicとPortletResourcePermissionLogicなどクラスは新規追加されます。では、Liferayの標準モジュールBlogsを考察し、新規されたクラスを整理しましょう。
ブログポートレットのUIにおいて
- ブログポートレットの設定はリソースcom_liferay_blogs_web_portlet_BlogsPortletの権限CONFIGURATIONで決まります。
- 新しいエントリーボタンの表示可否はモデルリソースcom.liferay.blogsの権限ADD_ENTRYで決まります。
- permission-blogの編集可否はモデルリソースcom.liferay.blogs.model.BlogsEntryの権限UPDATEで決まります。
インスタンス化可能なモデルリソースの権限チェック
ブログエンティティの更新ボタンの表示の判定はblogs-web/src/main/resources/META-INF/resources/blogs/entry_action.jspにおいてBlogsEntryPermission.contains(permissionChecker, entry, ActionKeys.UPDATE)が行なっています。このクラスから考察すると、Liferay7.1のモデルリソースは以下のような構造のことが分かります。
ブログポートレットは、DefaultModelResourcePermissionを介して、ModelResourcePermissionLogicとPermissionCheckerを用いてあるブログエンティティーに対してユーザがもっている権限をチェックしています。では、DefaultModelResourcePermissionの流れを確認しましょう。
では、フローグラフ中のModelResourcePermisisonLogic[]はどこから登録されますか?その答えはBlogsEntryModelResourcePermissionDefinition.javaにあります。
@Override続いてStagedModelPermissionLogic.javaとWorkflowedModelPermissionLogic.javaを考察しましょう(StagingについてはStaging 公式ドキュメントを参考してください)。
public void registerModelResourcePermissionLogics(
ModelResourcePermission modelResourcePermission,
Consumer>
modelResourcePermissionLogicConsumer) {
modelResourcePermissionLogicConsumer.accept(
new StagedModelPermissionLogic<>(
_stagingPermission, BlogsPortletKeys.BLOGS,
BlogsEntry::getEntryId));
modelResourcePermissionLogicConsumer.accept(
new WorkflowedModelPermissionLogic<>(
_workflowPermission, modelResourcePermission,
_groupLocalService, BlogsEntry::getEntryId));
}
- StagedModelPermissionLogicは、StagingPermissionImpl.javaを介して、サイトページ状態がStageの場合、一定操作(例:ACCESS, VIEWなど)以外を禁止する
- WorkflowedModelPermissionLogicは、WorkflowPermissionImpl.javaを介して、ユーザの公開前コンテントに対するアクセス権限を判定する
すなわち、登録したのは本来の権限判定対象であるブログエンティティのResourcePermissionテーブルレコードに関係ないロジックです。
ここまで考察すると、Liferay 7.1の権限構造をようやく理解できます。DefaultModelResourcePermissionはModelResourcePermisisonLogicとPermissionChecker両方を参照する理由は、ModelResourcePermisisonLogicとPermissionCheckerの役割が違うのです:
ここまで考察すると、Liferay 7.1の権限構造をようやく理解できます。DefaultModelResourcePermissionはModelResourcePermisisonLogicとPermissionChecker両方を参照する理由は、ModelResourcePermisisonLogicとPermissionCheckerの役割が違うのです:
- 開発者はModelResourcePermissionLogicを介してModelResourcePermissionに登録したLiferayのResourcePermissionテーブルに関係ないビジネスロジック
- 登録方式は公式ドキュメント方式とLiferay 7.1 ソースコード方式があります
- どの方式でもOSGIサービス登録になります
- 登録された権限ビジネスロジックは全てnullを返す場合、PermissionCheckerを利用し、ResourcePermissionテーブルからユーザ権限をチェックします。
Liferay 7.0のBlogsEntryPermission.javaと比べてみると、7.1の権限チェッククラスの各機能は切り離されていることが分かります。
Liferay 7.0 | Liferay 7.1 |
---|---|
BlogsEntryPermissionは直接的に権限判定を行う | BlogsEntryPermissionはModelResourcePermission を介して権限判定を行う |
StagingPermissionUtilとWorkflowPermissionUtil を直接参照する | OSGIサービスのxxxPermissionLogic方式で ResourcePermissionテーブルに関係ない権限ロジック を登録する |
PermissionCheckerを直接参照する | PermissionCheckerは直接参照しない |
ポートレットリソースの権限チェック
ブログポートレットの設定などボタンの表示はcom_liferay_blogs_web_portlet_BlogsPortletポートレットリソースの権限チェックにおいて判定しています。ポートレット権限の判定はLiferayの組み込み機能のため、公式ドキュメントの説明の通りresource-actionsにアクション定義を入れたらLiferayは自動的にブログポートレット権限チェックを行います。
ポートレットの権限判定はモデルリソースのようなOSGI構造を持っていません。具体的にはPortletPermissionImpl.javaを参考してください。
仮想モデルリソースの権限チェック
- BlogsPermission.contains(permissionChecker, scopeGroupId, ActionKeys.ADD_ENTRY)が行います。
前述の通り、BlogsPermission.javaはPortletResourcePermissionを参照し権限チェックを行なっています。PortletResourcePermissionの構造はModelResourcePermissionとほぼ同じのため、考察を省略します。
PermissionCheckerの正体
ここまで考察すると、Liferayでは、PortletPermission、PortletResourcePermission、ModelResourcePermissionなどヘルパークラスを介してPermissionCheckerインタフェースを利用し、違うリソースに対する権限チェックを行っていることが分かりました。
では、最後にPermisisonCheckerの正体を確認しましょう。ポートレットJSPでPermissionCheckerのクラス名を出力すると、StagingPermissionChecker.javaのことが確認できますが、ソースコードを見ると、StagingPermissionCheckerはもう一つ_permissionCheckerをラップしています。続いてPermissionCheckerFactoryImpl.javaを確認すると、PermissionCheckerの正体はPropsValues、すなわちportal.propertiesで設定されたクラス名です。
public PermissionCheckerFactoryImpl() throws Exception {皆さまは多分すでに存知ですが、LiferayのシステムPermissionCheckerは、portal-ext.propertiesで設定できます。
Classclazz =
(Class)Class.forName(
PropsValues.PERMISSIONS_CHECKER);
_permissionChecker = clazz.newInstance();
}
# Set the default permission checker class used byそして、Liferayのデフォルトの状態のPermissionCheckerの正体はAdvancedPermissionChecker.javaであることが分かりました。さらに、portal-ext.propertiesにカスタマイズクラスを登録するとLiferayのデフォルトPermissionCheckerを変更することもできます。
# com.liferay.portal.security.permission.PermissionCheckerFactory to check
# permissions for actions on objects. This class can be overriden with a
# custom class that implements
# com.liferay.portal.security.permission.PermissionChecker.
#
#permissions.checker=com.liferay.portal.security.permission.SimplePermissionChecker
permissions.checker=com.liferay.portal.security.permission.AdvancedPermissionChecker