セキュリティのテスト
このドキュメントでは、Quarkus Securityのテスト方法について説明します。
ユーザー情報の設定
セキュリティのテストには quarkus-elytron-security-properties-file が使えます。これは、 application.properties
にユーザー情報を埋め込むことと、スタンドアロンのプロパティファイルの両方をサポートしています。
例えば、以下の設定では、 設定プロファイル を使って、OAuth2が必要な本番モードと開発モードの両方でユーザーを設定することができます。
# Configure embedded authentication
%dev.quarkus.security.users.embedded.enabled=true
%dev.quarkus.security.users.embedded.plain-text=true
%dev.quarkus.security.users.embedded.users.scott=reader
%dev.quarkus.security.users.embedded.users.stuart=writer
%dev.quarkus.security.users.embedded.roles.scott=READER
%dev.quarkus.security.users.embedded.roles.stuart=READER,WRITER
# Configure OAuth2
quarkus.oauth2.enabled=true
%dev.quarkus.oauth2.enabled=false
quarkus.oauth2.client-id=client-id
quarkus.oauth2.client-secret=client-secret
quarkus.oauth2.introspection-url=http://host:port/introspect
セキュリティーエクステンションのテスト
Quarkusは、異なるユーザーでのテストや、セキュリティーサブシステムを無効にした状態でのテストを明示的にサポートしています。これを使用するには、 quarkus-test-security
のアーティファクトを含める必要があります。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-security</artifactId>
<scope>test</scope>
</dependency>
testImplementation("io.quarkus:quarkus-test-security")
このアーティファクトは io.quarkus.test.security.TestSecurity
アノテーションを提供します。これをテストメソッドやテストクラスに適用することで、テストが実行されるセキュリティーコンテキストを制御することができます。認証を必要とせずに安全なエンドポイントにテストがアクセスできるように認証を無効にすることができ、テストを実行する際の ID を指定することができます。
認可を無効にして実行されるテストは、enabled プロパティーを false に設定するだけです。
@Test
@TestSecurity(authorizationEnabled = false)
void someTestMethod() {
...
}
これにより、すべてのアクセスチェックが無効になり、認証を必要とせずに保護されたエンドポイントにアクセスできるようになります。
これを使用して、テストを実行する現在のユーザーを設定することもできます:
@Test
@TestSecurity(user = "testUser", roles = {"admin", "user"})
void someTestMethod() {
...
}
これは、指定されたユーザー名とロールを持つ ID でテストを実行します。これらを組み合わせることができるので、認証を無効にしながらテストを実行するための ID を提供することができることに注意してください。
注入された JsonWebToken
に依存するエンドポイントコードのテストの詳細は、OpenID Connect ベアラートークン結合テスト, OpenID Connect 認可コードフロー結合テスト and SmallRye JWT 結合テスト を参照してください。
さらに、アイデンティティーの属性、たとえばアイデンティティー拡張で追加されたカスタム項目を指定することもできます。
@Inject
SecurityIdentity identity;
@Test
@TestSecurity(
user = "testUser",
roles = {"admin", "user"},
attributes = { @SecurityAttribute(key = "answer", value = "42", type = AttributeType.LONG) })
void someTestMethod() {
Long answer = identity.<Long>getAttribute("answer");
...
}
これにより、 answer
という名前の Long
型の属性を持つアイデンティティーを使用してテストが実行されます。
この機能は |
これは、同じセキュリティ設定のセットを複数のテストメソッドで使用する必要がある場合に特に便利です。 |
@TestSecurity
アノテーションは @PermissionsAllowed
セキュリティーアノテーションでも機能します。
次の例を見てみましょう。
@Test
@TestSecurity(user = "testUser", permissions = "see:detail")
void someTestMethod() {
...
}
これにより、権限が see
とアクションが detail
のアイデンティティーを使用してテストが実行されます。
その結果、以下の例で宣言されている getDetail
メソッドの呼び出しは成功します。
@PermissionsAllowed("see:detail")
public String getDetail() {
return "detail";
}
以下の例のようにカスタム権限を設定することもできます。
@PermissionsAllowed("see", permission = CustomPermission.class)
public String getDetail() {
return "detail";
}
SecurityIdentityAugmentor
CDI Bean を使用した @TestSecurity
アノテーションによって
作成された`SecurityIdentity` に CustomPermission
を付与する必要があります。
@ApplicationScoped
public class CustomSecurityIdentityAugmentor implements SecurityIdentityAugmentor {
@Override
public Uni<SecurityIdentity> augment(SecurityIdentity securityIdentity,
AuthenticationRequestContext authenticationRequestContext) {
final SecurityIdentity augmentedIdentity;
if (shouldGrantCustomPermission(securityIdentity) {
augmentedIdentity = QuarkusSecurityIdentity.builder(securityIdentity)
.addPermission(new CustomPermission("see")).build();
} else {
augmentedIdentity = securityIdentity;
}
return Uni.createFrom().item(augmentedIdentity);
}
}
Quarkus は、 @TestSecurity`アノテーションで作成された`SecurityIdentity
を、次のように設定した場合にのみ拡張します。
@TestSecurity#augmentors
アノテーション属性を CustomSecurityIdentityAugmentor.class
に次のように追加します。
@Test
@TestSecurity(user = "testUser", permissions = "see:detail", augmentors = CustomSecurityIdentityAugmentor.class)
void someTestMethod() {
...
}
セキュリティーテストの混合
@TestSecurity
と Basic Auth の両方を使ってセキュリティー機能をテストする必要が出てきた場合
(何も定義されていない場合のフォールバック認証メカニズムです)、明示的に有効にする必要があり、
たとえば quarkus.http.auth.basic=true
や`%test.quarkus.http.auth.basic=true` と設定します。
同様に、 `@TestSecurity`とベアラートークン認証の両方を使用してセキュリティー機能をテストする必要がある場合、 以下の例のように両方を活用できます。
@Test
@TestSecurity(user = "Bob")
public void testSecurityMetaAnnotation {
RestAssured.given()
.auth().oauth2(getTokenForUser("Alice")) (1)
.get("hello")
.then()
.statusCode(200)
.body(Matchers.is("Hello Alice"));
RestAssured.given()
.get("hello")
.then()
.statusCode(200)
.body(Matchers.is("Hello Bob")); (2)
}
@Path("hello")
public static class HelloResource {
@Inject
SecurityIdentity identity;
@Authenticated
@GET
public String sayHello() {
return "Hello " + identity.getPrincipal().getName();
}
}
1 | ベアラーアクセストークンが HTTP リクエストとともに送信されるため、ベアラートークン認証メカニズムが使用されます。 |
2 | 認証ヘッダーが存在しないため、テストセキュリティーエクステンションによってユーザー Bob が作成されます。 |
パスベースの認証
@TestSecurity
は、認証メカニズムを組み合わせる必要がある 場合にも使用できます。
以下の例は、パスベースの認証が有効な場合に認証メカニズムを選択する方法を示しています。
@Test
@TestSecurity(user = "testUser", roles = {"admin", "user"}, authMechanism = "basic") (1)
void basicTestMethod() {
...
}
@Test
@TestSecurity(user = "testUser", roles = {"admin", "user"}, authMechanism = "form") (2)
void formTestMethod() {
...
}
1 | authMechanism 属性は Basic 認証を選択します。 |
2 | authMechanism 属性はフォームベースの認証を選択します。 |
Quarkus アプリケーションでは、アノテーションを使用して、各 Jakarta REST エンドポイントに固有の認証メカニズムを選択できます。
package org.acme.security.testing;
import io.quarkus.vertx.http.runtime.security.annotation.BasicAuthentication;
import io.quarkus.vertx.http.runtime.security.annotation.FormAuthentication;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@Path("/")
public class TestSecurityResource {
@BasicAuthentication (1)
@GET
@Path("basic-only")
public String basicOnly() {
return "basic-only";
}
@FormAuthentication (2)
@GET
@Path("form-only")
public String formOnly() {
return "form-only";
}
}
1 | basicTestMethod テストからの /basic-only パスへのすべての HTTP リクエストが正常に認証されます。 |
2 | Basic 認証が必要なため、 formTestMethod テストから呼び出された場合も同じ HTTP リクエストが失敗します。 |
または、HTTP セキュリティーポリシーを使用してパス固有の認証メカニズムを選択することもできます。
# require basic authentication for the '/basic-only' path
quarkus.http.auth.permission.basic.paths=/basic-only
quarkus.http.auth.permission.basic.policy=authenticated
quarkus.http.auth.permission.basic.auth-mechanism=basic
# require form-based authentication for the '/form-only' path
quarkus.http.auth.permission.form.paths=/form-only
quarkus.http.auth.permission.form.policy=authenticated
quarkus.http.auth.permission.form.auth-mechanism=form
結合テストに Wiremock を使用する
Wiremockを使用して認可OAuth2とOIDCサービスをモックすることもできます。 詳細は OAuth2 結合テスト、OpenID Connect ベアラートークン結合テスト, OpenID Connect 認可コードフロー結合テスト and SmallRye JWT 結合テスト を参照してください。