Skip to content

ポリシー

Lase が提供するポリシークラスの一覧と、実装の約束事です。すべて CodebaseJp\Lase\Policies 名前空間にあります。

Laravel の規約による自動解決が効くため、LaseServiceProvider への登録は不要です(CodebaseJp\Lase\Models\TenantUserCodebaseJp\Lase\Policies\TenantUserPolicy)。

権限の確認は2層

対象担当
レコード単位の判定が必要なものポリシー($this->authorize())。ルート側の tenant.can は粗い下限として重ねる
判定対象のレコードがないもの(Senter連携など)tenant.can ミドルウェア

ポリシーはレコードに紐づく判定を持ちます。一覧や登録のようにレコードを取らない操作は、権限の確認をミドルウェアに任せます(既存のポリシーに viewAny / create が定義されている場合は、定義と実装が乖離しないよう呼び出します)。

実装の約束事

戻り値

  • 拒否理由が「権限がない」の一つだけなら bool
  • 拒否理由が複数あるなら Illuminate\Auth\Access\Response を返し、Response::deny('理由') でメッセージを添える

テナント境界

テナントに属するレコード(tenant_id を持つモデル)を扱うメソッドは、必ず操作者のテナントと一致することを確認します。

一次防御は BelongsToTenant のグローバルスコープで、テナントのコンテキスト配下ではルートモデルバインディングが他テナントのレコードを解決しません。ポリシー側の確認は二次防御です。スコープを外した経路(withoutGlobalScope('tenant')、明示的な forTenant()、リレーション経由の取得、アプリ側がトレイトを付けていないモデル)でも境界が守られるようにします。

境界を跨いだ場合は Response::denyAsNotFound() を返します。403 を返すと、存在しない ID(ルートモデルバインディングで 404)と区別がついてしまい、他テナントのレコードの存在が分かるためです。

php
public function delete(User $user, McpServiceToken $mcpServiceToken): Response
{
    if (! $user->isTenantUser($mcpServiceToken->tenant_id)) {
        return Response::denyAsNotFound('トークンが見つかりません。');
    }

    if (! $user->hasTenantPermission(TenantPermission::MCP_SERVICE_TOKEN_MANAGE)) {
        return Response::deny('MCP Service Token の管理権限が必要です。');
    }

    return Response::allow();
}

isTenantUser() で境界を確認した後は、hasTenantPermission() の第2引数(リソースのテナントID)は同じ比較の繰り返しになるため渡しません。

TenantUserPolicy

メソッド戻り値必要な権限追加の条件
viewAnybooluser:read-
viewResponseuser:readテナント境界
createbooluser:write-
updateResponseuser:writeテナント境界/自分自身は不可/オーナーはオーナーのみ
changeRolesResponseuser:writeupdate と同じ
deleteResponseuser:writeupdate と同じ
  • 自分自身には行えません。 権限を失って設定を戻せなくなるのを防ぐためです。画面側では自分の行の編集・削除を出さないようにしてください。
  • オーナー(* を持つ役割が割り当てられたユーザー)に対して行えるのはオーナーだけです。 ステータスの SUSPENDED は実質の利用停止なので、降格・削除と同じ保護をかけています。
  • update(ステータス)と changeRoles(役割)は現在同じ条件ですが、アプリ側が別々の条件に上書きできるようメソッドを分けています。

TenantRolePolicy

tenant_roles はテナントを持たない共通テーブルのため、テナント境界の判定はありません。

メソッド戻り値必要な権限追加の条件
viewAny / viewbooluser:read-
create / update / deletebooluser:write-
assignResponseuser:writeオーナーの役割を付与できるのはオーナーのみ

assign は「その役割をユーザーに付与してよいか」を判定します。招待時と役割変更時の両方で呼んでください。

TenantUserInvitationPolicy

メソッド戻り値必要な権限追加の条件
viewAnybooluser:read-
viewResponseuser:readテナント境界
createbooluser:write-
update / deleteResponseuser:writeテナント境界

SubscriptionPolicy

メソッド戻り値必要な権限追加の条件
updateResponsecontract:writeテナント境界

一覧・登録は現在のテナントに対してのみ行われるため、レコード単位の判定を持ちません。

McpServiceTokenPolicy

メソッド戻り値必要な権限追加の条件
updateResponsemcp.service-tokens:manageテナント境界/無効化済みは対象外(404)
deleteResponsemcp.service-tokens:manageテナント境界

update はトークンのローテートに対応します。一覧・発行はレコードを取らないため、権限の確認は tenant.can:mcp.service-tokens:manage が行います。

アプリ側での拡張

ポリシーを継承して条件を追加できます。Gate::policy() で上書き登録してください。

php
class MyTenantUserPolicy extends TenantUserPolicy
{
    public function changeRoles(User $user, TenantUser $tenantUser): Response
    {
        if (! $user->hasTenantPermission(MyTenantPermission::ROLE_ASSIGN)) {
            return Response::deny('役割の割り当て権限が必要です。');
        }

        return parent::changeRoles($user, $tenantUser);
    }
}
php
// AuthServiceProvider
Gate::policy(TenantUser::class, MyTenantUserPolicy::class);