セキュリティのテスト
このドキュメントでは、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 を提供することができることに注意してください。
See OpenID Connect Bearer Token Integration testing, OpenID Connect Authorization Code Flow Integration testing and SmallRye JWT Integration testing for more details about testing the endpoint code which depends on the injected JsonWebToken
.
Additionally, you can specify attributes for the identity, perhaps custom items that were added with identity augmentation:
@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");
...
}
This will run the test with an identity with an attribute of type Long
named answer
.
この機能は |
これは、同じセキュリティ設定のセットを複数のテストメソッドで使用する必要がある場合に特に便利です。 |
セキュリティーテストの混合
@TestSecurity
と Basic Auth の両方を使ってセキュリティー機能をテストする必要が出てきた場合 (何も定義されていない場合のフォールバック認証メカニズムです)、明示的に有効にする必要があり、例えば quarkus.http.auth.basic=true
や`%test.quarkus.http.auth.basic=true` と設定します。
Similarly, if it becomes necessary to test security features using both @TestSecurity
and Bearer token authentication,
you can leverage both like in the example below:
@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 | Bearer token authentication mechanism is used, because a Bearer access token is sent with the HTTP request. |
2 | No authorization header is present, therefore the Test Security Extension creates user Bob . |
Path-based authentication
@TestSecurity
can also be used when authentication mechanisms must be combined.
Example below shows how to select authentication mechanism when path-based authentication is enabled.
@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 | The 'authMechanism' attribute selects Basic authentication. |
2 | The 'authMechanism' attribute selects Form-based authentication. |
In your Quarkus application, it is possible to use annotations to select an authentication mechanism specific to each Jakarta REST endpoint:
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 | All HTTP requests to the /basic-only path from the basicTestMethod test are authenticated successfully. |
2 | Same HTTP requests will fail when invoked from the formTestMethod test as Basic authentication is required. |
Alternatively, it is possible to select path-specific authentication mechanism with HTTP Security Policy:
# 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を使用する
You can also use Wiremock to mock the authorization OAuth2 and OIDC services: See OAuth2 Integration testing, OpenID Connect Bearer Token Integration testing, OpenID Connect Authorization Code Flow Integration testing and SmallRye JWT Integration testing for more details.