diff --git a/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityDefinition.java b/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityDefinition.java index e4f8df972ff..c64555a3c97 100644 --- a/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityDefinition.java +++ b/microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023 Oracle and/or its affiliates. + * Copyright (c) 2018, 2024 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -229,11 +229,13 @@ public void analyzerResponse(AnnotationAnalyzer analyzer, AnnotationAnalyzer.Ana default -> {} } - this.requiresAuthorization = switch (analyzerResponse.authorizationResponse()) { - case REQUIRED, OPTIONAL -> true; - case FORBIDDEN -> false; - default -> null; - }; + if (this.requiresAuthorization == null) { + this.requiresAuthorization = switch (analyzerResponse.authorizationResponse()) { + case REQUIRED, OPTIONAL -> true; + case FORBIDDEN -> false; + default -> null; + }; + } this.authenticator = analyzerResponse.authenticator().orElse(this.authenticator); this.authorizer = analyzerResponse.authorizer().orElse(this.authorizer); diff --git a/tests/integration/security/pom.xml b/tests/integration/security/pom.xml index 2221ff52f74..5599dd7c5e1 100644 --- a/tests/integration/security/pom.xml +++ b/tests/integration/security/pom.xml @@ -41,5 +41,6 @@ gh2772 path-params security-response-mapper + security-annotation diff --git a/tests/integration/security/security-annotation/pom.xml b/tests/integration/security/security-annotation/pom.xml new file mode 100644 index 00000000000..78961aeffea --- /dev/null +++ b/tests/integration/security/security-annotation/pom.xml @@ -0,0 +1,57 @@ + + + + 4.0.0 + + io.helidon.tests.integration + helidon-tests-integration-security + 4.0.0-SNAPSHOT + + + + helidon-tests-integration-security-annotation + Helidon Tests Integration Security Annotations + + + Integration test for security annotations + + + + + io.helidon.microprofile.bundles + helidon-microprofile + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-all + test + + + io.helidon.microprofile.testing + helidon-microprofile-testing-junit5 + test + + + + \ No newline at end of file diff --git a/tests/integration/security/security-annotation/src/main/java/io/helidon/tests/integration/security/annotation/AdminResource.java b/tests/integration/security/security-annotation/src/main/java/io/helidon/tests/integration/security/annotation/AdminResource.java new file mode 100644 index 00000000000..2e5375cb5f1 --- /dev/null +++ b/tests/integration/security/security-annotation/src/main/java/io/helidon/tests/integration/security/annotation/AdminResource.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.tests.integration.security.annotation; + +import io.helidon.security.annotations.Authenticated; +import io.helidon.security.annotations.Authorized; + +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +/** + * An admin resource that should not be accessible without proper credentials. + */ +@Path("/admin") +public class AdminResource { + + /** + * The resource. + * + * @return admin secret. + */ + @GET + @Authenticated + @Authorized + @RolesAllowed("admin") + public String admin() { + return "admin"; + } + + + /** + * The resource. + * + * @return admin secret. + */ + @GET + @Path("/unauthorized") + @Authenticated + @Authorized(false) + @RolesAllowed("admin") + public String unauthorizedAdmin() { + return "unauthorized admin"; + } +} diff --git a/tests/integration/security/security-annotation/src/main/resources/META-INF/beans.xml b/tests/integration/security/security-annotation/src/main/resources/META-INF/beans.xml new file mode 100644 index 00000000000..af19f065062 --- /dev/null +++ b/tests/integration/security/security-annotation/src/main/resources/META-INF/beans.xml @@ -0,0 +1,26 @@ + + + + +k diff --git a/tests/integration/security/security-annotation/src/main/resources/application.yaml b/tests/integration/security/security-annotation/src/main/resources/application.yaml new file mode 100644 index 00000000000..e00256e3eee --- /dev/null +++ b/tests/integration/security/security-annotation/src/main/resources/application.yaml @@ -0,0 +1,27 @@ +# +# Copyright (c) 2024 Oracle and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +server.port: 0 +security: + providers: + - abac: + - http-basic-auth: + realm: "helidon" + users: + - login: "success" + password: "password" + roles: ["admin"] + - login: "fail" + password: "password" \ No newline at end of file diff --git a/tests/integration/security/security-annotation/src/main/resources/logging.properties b/tests/integration/security/security-annotation/src/main/resources/logging.properties new file mode 100644 index 00000000000..5855f9793b4 --- /dev/null +++ b/tests/integration/security/security-annotation/src/main/resources/logging.properties @@ -0,0 +1,29 @@ +# +# Copyright (c) 2024 Oracle and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Example Logging Configuration File +# For more information see $JAVA_HOME/jre/lib/logging.properties + +# Send messages to the console +handlers=io.helidon.logging.jul.HelidonConsoleHandler + +# HelidonConsoleHandler uses a SimpleFormatter subclass that replaces "!thread!" with the current thread +java.util.logging.SimpleFormatter.format=%1$tY.%1$tm.%1$td %1$tH:%1$tM:%1$tS %4$s %3$s !thread!: %5$s%6$s%n + +# Global logging level. Can be overridden by specific loggers +.level=INFO +AUDIT.level=FINEST + diff --git a/tests/integration/security/security-annotation/src/test/java/io/helidon/tests/integration/security/pathparams/AdminTest.java b/tests/integration/security/security-annotation/src/test/java/io/helidon/tests/integration/security/pathparams/AdminTest.java new file mode 100644 index 00000000000..a1c5e9310b9 --- /dev/null +++ b/tests/integration/security/security-annotation/src/test/java/io/helidon/tests/integration/security/pathparams/AdminTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.tests.integration.security.pathparams; + +import java.util.Base64; + +import io.helidon.http.Status; +import io.helidon.microprofile.testing.junit5.HelidonTest; + +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.Response; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +@HelidonTest +class AdminTest { + + @Test + void testProtectedAdminEndpoint(WebTarget target) { + try (Response response = target.path("/admin") + .request() + .header("Authorization", basic("fail")) + .get()) { + assertThat(response.getStatus(), is(Status.FORBIDDEN_403.code())); + } + try (Response response = target.path("/admin") + .request() + .header("Authorization", basic("success")) + .get()) { + assertThat(response.getStatus(), is(Status.OK_200.code())); + assertThat(response.readEntity(String.class), is("admin")); + } + } + + @Test + void testUnauthorizedAdminEndpoint(WebTarget target) { + try (Response response = target.path("/admin/unauthorized") + .request() + .header("Authorization", basic("fail")) + .get()) { + assertThat(response.getStatus(), is(Status.OK_200.code())); + assertThat(response.readEntity(String.class), is("unauthorized admin")); + } + try (Response response = target.path("/admin/unauthorized") + .request() + .header("Authorization", basic("success")) + .get()) { + assertThat(response.getStatus(), is(Status.OK_200.code())); + assertThat(response.readEntity(String.class), is("unauthorized admin")); + } + } + + private String basic(String user) { + String uap = user + ":password"; + return "basic " + Base64.getEncoder().encodeToString(uap.getBytes()); + } +}