Authorization of Web Endpoints

Quarkusには、プラグイン可能なウェブセキュリティーレイヤーが統合されています。セキュリティーが有効になっている場合、すべてのHTTPリクエストは、処理の継続が許可されているかどうかを確認するためのパーミッションチェックが実行されます。

Configuration authorization checks are executed before any annotation-based authorization check is done, so both checks have to pass for a request to be allowed. This means you cannot use @PermitAll to open up a path if the path has been blocked using quarkus.http.auth. configuration. If you are using JAX-RS you may want to consider using the quarkus.security.jaxrs.deny-unannotated-endpoints or quarkus.security.jaxrs.default-roles-allowed to set default security requirements instead of HTTP path level matching, as these properties can be overridden by annotations on an individual endpoint.

認可は、セキュリティプロバイダが提供するユーザーロールに基づいて行われます。 これらのロールをカスタマイズするために、 SecurityIdentityAugmentor を作成することができます。Security Identity Customization を参照してください。

設定を利用した認可

デフォルトの実装では、 application.properties の 設定を使用してパーミッションを定義することができます。以下に config の例を示します。

quarkus.http.auth.policy.role-policy1.roles-allowed=user,admin                      (1)

quarkus.http.auth.permission.roles1.paths=/roles-secured/*,/other/*,/api/*          (2)
quarkus.http.auth.permission.roles1.policy=role-policy1

quarkus.http.auth.permission.permit1.paths=/public/*                                (3)
quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET

quarkus.http.auth.permission.deny1.paths=/forbidden                                 (4)
quarkus.http.auth.permission.deny1.policy=deny
1 これは、 user と admin のロールを持つユーザーを許可するロールベースのポリシーを定義します。これは後のルールで参照されます。
2 これは以前に定義されたポリシーを参照するパーミッションセットです。 roles1 は任意の名前ですので、好きなようにパーミッションセットを呼び出すことができます。
3 このパーミッションはデフォルトの permit ビルトインポリシーを参照して、 /public への GET メソッドを許可します。この例では、このリクエストはいずれにせよ許可されているはずなので、これは実際には no-op です。
4 このパーミッションは /forbidden に対する組み込みの deny ポリシーを参照します。これは * で終わらないので、完全なパスマッチとなります。

パーミッションは、パーミッションセットを使ってコンフィグで定義されます。これらは任意の名前のパーミッション・グルーピングです。各パーミッションセットには、アクセスを制御するためのポリシーを指定する必要があります。組み込みのポリシーには, deny , permit , authenticated の3種類があり,それぞれ,すべてを許可,すべてを拒否,認証されたユーザのみを許可 となります。

また、例に示すように、ロールベースのポリシーを定義することも可能です。これらのポリシーでは、指定されたロールを持つユーザーにのみ、リソースへのアクセスを許可します。

パス、メソッドでのマッチング

パーミッションセットでは、パスとメソッドをカンマ区切りのリストで指定することもできます。パスの末尾が * である場合はワイルドカードマッチとみなされ、すべてのサブパスにマッチしますが、そうでない場合は完全一致となり、特定のパスにのみマッチします。

quarkus.http.auth.permission.permit1.paths=/public/*,/css/*,/js/*,/robots.txt
quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET,HEAD

パスはマッチするがメソッドはマッチしない場合

リクエストがパスに基づいて1つ以上のパーミッションセットにマッチするが、 メソッドの要件によりマッチしない場合、そのリクエストは拒否されます。

Given the above permission set, GET /public/foo would match both the path and method and thus be allowed, whereas POST /public/foo would match the path but not the method and would thus be rejected.

複数のパスのマッチング:一番長いパスが勝ちます。

マッチングは常にロンゲストパス に基づいて行われ、より具体的なパーミッションセットがマッチした場合、それより具体的でないパーミッションセットは考慮されません。:

quarkus.http.auth.permission.permit1.paths=/public/*
quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET,HEAD

quarkus.http.auth.permission.deny1.paths=/public/forbidden-folder/*
quarkus.http.auth.permission.deny1.policy=deny
Given the above permission set, GET /public/forbidden-folder/foo would match both permission sets' paths, but because it matches the deny1 permission set’s path on a longer match, deny1 will be chosen and the request will be rejected.

サブパス パーミッションは、上述の deny1 対 permit1 パーミッションの例で説明したように、ルートパス パーミッションに対して常に勝利します。以下は、サブパス権限で公開リソースへのアクセスを許可し、ルートパス権限で認可を要求する例です。 :

quarkus.http.auth.policy.user-policy.roles-allowed=user
quarkus.http.auth.permission.roles.paths=/api/*
quarkus.http.auth.permission.roles.policy=user-policy

quarkus.http.auth.permission.public.paths=/api/noauth/*
quarkus.http.auth.permission.public.policy=permit

複数のパスのマッチング:一番具体的なパスが勝ちます

パスが複数の許可セットで登録されている場合、HTTPメソッドを指定する許可セットが優先され、メソッドを持たない許可セットは考慮されません(もちろん、メソッドが一致することを前提としています)。この例では、メソッドを持たないパーミッションセットは、 リクエストメソッドがメソッドパーミッションセットのいずれにもマッチしない場合にのみ 有効になります。

quarkus.http.auth.permission.permit1.paths=/public/*
quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET,HEAD

quarkus.http.auth.permission.deny1.paths=/public/*
quarkus.http.auth.permission.deny1.policy=deny
Given the above permission set, GET /public/foo would match both permission sets' paths, but because it matches the permit1 permission set’s explicit method, permit1 will be chosen and the request will be accepted. PUT /public/foo on the other hand, will not match the method permissions of permit1 and so deny1 will be activated and reject the request.

複数のパスのマッチング:一番長いパスが勝ちます

複数のパーミッションセットが同じパスとメソッドを指定している場合(または複数のパーミッションがメソッドを持たない場合)、リクエストを進めるためには両方のパーミッションがアクセスを許可しなければなりません。これを実現するためには、両方がメソッドを指定しているか、メソッドを持たないかのいずれかでなければならないことに注意してください、メソッド固有のマッチは上記のように優先されます。 :

quarkus.http.auth.policy.user-policy1.roles-allowed=user
quarkus.http.auth.policy.admin-policy1.roles-allowed=admin

quarkus.http.auth.permission.roles1.paths=/api/*,/restricted/*
quarkus.http.auth.permission.roles1.policy=user-policy1

quarkus.http.auth.permission.roles2.paths=/api/*,/admin/*
quarkus.http.auth.permission.roles2.policy=admin-policy1
Given the above permission set, GET /api/foo would match both permission sets' paths, so would require both the user and admin roles.

アクセスを拒否する設定プロパティー

RBACのDeny動作を変更する設定は3つあります。 :

quarkus.security.jaxrs.deny-unannotated-endpoints=true|false

この値をtrue に設定すると、デフォルトですべての JAX-RS エンドポイントに対してアクセスが拒否されます。したがって、JAX-RS エンドポイントがセキュリティアノテーションを持っていない場合、デフォルトで @DenyAll の動作になります。これは、セキュリティで保護されているはずのエンドポイントを誤って公開してしまわないようにするために有用です。デフォルトは false です。

quarkus.security.jaxrs.default-roles-allowed=role1,role2

アノテーションされていないエンドポイントのデフォルトのロール要件を定義します。ロール「**」は、認証された任意のユーザを意味する特別なロールです。これは、代わりにdenyが有効になるため、 deny-unannotated-endpoints と組み合わせることはできません。

quarkus.security.deny-unannotated-members=true|false
  • この値をtrue に設定すると、セキュリティアノテーションを持つメソッドを含むクラスで定義されているものの、セキュリティアノテーションを持たないすべての CDI メソッドと JAX-RS エンドポイントへのアクセスが拒否されるようになります。デフォルトは false です。

権限の無効化

パーミッションは、宣言されたパーミッションごとの enabled プロパティを使って、ビルド時に無効にすることができます。:

quarkus.http.auth.permission.permit1.enabled=false
quarkus.http.auth.permission.permit1.paths=/public/*,/css/*,/js/*,/robots.txt
quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET,HEAD

また、実行時にシステムプロパティや環境変数で有効にすることもできます。例えばこのような形です。 -Dquarkus.http.auth.permission.permit1.enabled=true.

パーミッションパスとhttp rootパス

quarkus.http.root-path 設定プロパティは、 http エンドポイントコンテキストパス を変更するために使用されます。

デフォルトでは、設定された権限のパスの前に自動的に quarkus.http.root-path 、フォワードスラッシュを使用しない場合などです。 :

quarkus.http.auth.permission.permit1.paths=public/*,css/*,js/*,robots.txt

この構成は以下に相当します。 :

quarkus.http.auth.permission.permit1.paths=${quarkus.http.root-path}/public/*,${quarkus.http.root-path}/css/*,${quarkus.http.root-path}/js/*,${quarkus.http.root-path}/robots.txt

先頭のスラッシュは、設定されたパーミッションのパスの解釈方法を変更します。設定されたURLはそのまま使用され、 quarkus.http.root-path の値が変更されても、パスは調整されません。例えば下記の形です。 :

quarkus.http.auth.permission.permit1.paths=/public/*,css/*,js/*,robots.txt

この設定は、固定/静的URL /public から提供されるリソースにのみ影響します。 quarkus.http.root-path が / 以外に設定されている場合、アプリケーションリソースと一致しない可能性があります。

詳細については、 Quarkusのパス解決 を参照してください。

アノテーションを使った認可

Quarkusには、共通のセキュリティーアノテーション( @RolesAllowed , @DenyAll , @PermitAll )に基づいたロールベースのアクセス制御 (RBAC )をRESTエンドポイントとCDI Bean上で可能にするためのビルトインセキュリティーが搭載されています。JAX-RSとCommon Securityアノテーションの両方を利用してエンドポイントを記述し、セキュリティーを確保するエンドポイントの例は、 [subject-example] に記載されています。Quarkusはまた、認証済みのユーザーがリソースにアクセスすることを許可する io.quarkus.security.Authenticated アノテーションも提供しています( @RolesAllowed("**") と同等です)。

SubjectExposingResource Example
import java.security.Principal;

import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.SecurityContext;

@Path("subject")
public class SubjectExposingResource {

    @GET
    @Path("secured")
    @RolesAllowed("Tester") (1)
    public String getSubjectSecured(@Context SecurityContext sec) {
        Principal user = sec.getUserPrincipal(); (2)
        String name = user != null ? user.getName() : "anonymous";
        return name;
    }

    @GET
    @Path("unsecured")
    @PermitAll (3)
    public String getSubjectUnsecured(@Context SecurityContext sec) {
        Principal user = sec.getUserPrincipal(); (4)
        String name = user != null ? user.getName() : "anonymous";
        return name;
    }

    @GET
    @Path("denied")
    @DenyAll (5)
    public String getSubjectDenied(@Context SecurityContext sec) {
        Principal user = sec.getUserPrincipal();
        String name = user != null ? user.getName() : "anonymous";
        return name;
    }
}
1 この /subject/secured エンドポイントは、 @RolesAllowed("Tester") アノテーションを使用して"Tester"というロールを付与された認証済みユーザーが必要です。
2 エンドポイントは、JAX-RS SecurityContext からユーザー・プリンシパルを取得します。これは、保護されたエンドポイントの場合は非 null になります。
3 /subject/unsecured エンドポイントでは、 @PermitAll アノテーションを指定することで、認証されていないアクセスが可能になります。
4 ユーザープリンシパルを取得するためのこの呼び出しは、呼び出し元が認証されていない場合はnullを返し、呼び出し元が認証されている場合は非nullを返します。
5 /subject/denied エンドポイントは、 @DenyAll アノテーションを指定することで、呼出が認証されているかどうかにかかわらず、あらゆるアクセスを禁止します。

リファレンス