From fc9a4b6edcff8660595450f2d68f2050ddece1f4 Mon Sep 17 00:00:00 2001 From: Josef Smrcka Date: Thu, 12 Jan 2023 16:35:18 +0100 Subject: [PATCH] Add test for security annotations in rest-data-panache Quarkus extensions based on `rest-data-panache` support propagation of security annotations into generated JAX-RS resources. These tests provide coverage of this feature for extensions: - `quarkus-hibernate-orm-rest-data-panache` - `quarkus-spring-data-rest` See also related test plan: - https://github.com/quarkus-qe/quarkus-test-plans/blob/main/QUARKUS-2788.md --- spring/spring-data/pom.xml | 4 + .../data/rest/secured/DenyAllRepository.java | 27 +++ .../rest/secured/PermitAllRepository.java | 21 +++ .../data/rest/secured/PublicRepository.java | 19 ++ .../rest/secured/RolesAllowedRepository.java | 19 ++ .../src/main/resources/application.properties | 9 + ...ShiftSecuredRepositoryRestResourcesIT.java | 7 + .../SecuredRepositoryRestResourcesIT.java | 160 ++++++++++++++++ sql-db/panache-flyway/pom.xml | 4 + .../secured/EntityDenyAllResource.java | 30 +++ .../secured/EntityPermitAllResource.java | 16 ++ .../secured/EntityPublicResource.java | 14 ++ ...esourcePropertiesRolesAllowedResource.java | 9 + .../secured/EntityRolesAllowedResource.java | 12 ++ .../secured/RepositoryDenyAllResource.java | 31 +++ .../secured/RepositoryPermitAllResource.java | 17 ++ .../secured/RepositoryPublicResource.java | 15 ++ ...esourcePropertiesRolesAllowedResource.java | 11 ++ .../RepositoryRolesAllowedResource.java | 13 ++ .../src/main/resources/application.properties | 9 + .../AbstractSecuredPanacheResourceIT.java | 176 ++++++++++++++++++ ...enShiftSecuredPanacheEntityResourceIT.java | 7 + ...iftSecuredPanacheRepositoryResourceIT.java | 7 + .../SecuredPanacheEntityResourceIT.java | 16 ++ .../SecuredPanacheRepositoryResourceIT.java | 16 ++ 25 files changed, 669 insertions(+) create mode 100644 spring/spring-data/src/main/java/io/quarkus/ts/spring/data/rest/secured/DenyAllRepository.java create mode 100644 spring/spring-data/src/main/java/io/quarkus/ts/spring/data/rest/secured/PermitAllRepository.java create mode 100644 spring/spring-data/src/main/java/io/quarkus/ts/spring/data/rest/secured/PublicRepository.java create mode 100644 spring/spring-data/src/main/java/io/quarkus/ts/spring/data/rest/secured/RolesAllowedRepository.java create mode 100644 spring/spring-data/src/test/java/io/quarkus/ts/spring/data/rest/secured/OpenShiftSecuredRepositoryRestResourcesIT.java create mode 100644 spring/spring-data/src/test/java/io/quarkus/ts/spring/data/rest/secured/SecuredRepositoryRestResourcesIT.java create mode 100644 sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityDenyAllResource.java create mode 100644 sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityPermitAllResource.java create mode 100644 sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityPublicResource.java create mode 100644 sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityResourcePropertiesRolesAllowedResource.java create mode 100644 sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityRolesAllowedResource.java create mode 100644 sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryDenyAllResource.java create mode 100644 sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryPermitAllResource.java create mode 100644 sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryPublicResource.java create mode 100644 sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryResourcePropertiesRolesAllowedResource.java create mode 100644 sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryRolesAllowedResource.java create mode 100644 sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/AbstractSecuredPanacheResourceIT.java create mode 100644 sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/OpenShiftSecuredPanacheEntityResourceIT.java create mode 100644 sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/OpenShiftSecuredPanacheRepositoryResourceIT.java create mode 100644 sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/SecuredPanacheEntityResourceIT.java create mode 100644 sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/SecuredPanacheRepositoryResourceIT.java diff --git a/spring/spring-data/pom.xml b/spring/spring-data/pom.xml index 897c14c3d..43bc8b8c9 100644 --- a/spring/spring-data/pom.xml +++ b/spring/spring-data/pom.xml @@ -35,6 +35,10 @@ io.quarkus quarkus-hibernate-validator + + io.quarkus + quarkus-elytron-security-properties-file + io.quarkus.qe quarkus-test-service-database diff --git a/spring/spring-data/src/main/java/io/quarkus/ts/spring/data/rest/secured/DenyAllRepository.java b/spring/spring-data/src/main/java/io/quarkus/ts/spring/data/rest/secured/DenyAllRepository.java new file mode 100644 index 000000000..d8a17af99 --- /dev/null +++ b/spring/spring-data/src/main/java/io/quarkus/ts/spring/data/rest/secured/DenyAllRepository.java @@ -0,0 +1,27 @@ +package io.quarkus.ts.spring.data.rest.secured; + +import java.util.Optional; + +import javax.annotation.security.DenyAll; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.rest.core.annotation.RepositoryRestResource; +import org.springframework.data.rest.core.annotation.RestResource; + +import io.quarkus.ts.spring.data.rest.Library; + +@RepositoryRestResource(path = "/secured/deny-all") +@DenyAll +public interface DenyAllRepository extends CrudRepository { + @Override + @RestResource + @PermitAll + Iterable findAll(); + + @Override + @RestResource + @RolesAllowed("admin") + Optional findById(Long aLong); +} diff --git a/spring/spring-data/src/main/java/io/quarkus/ts/spring/data/rest/secured/PermitAllRepository.java b/spring/spring-data/src/main/java/io/quarkus/ts/spring/data/rest/secured/PermitAllRepository.java new file mode 100644 index 000000000..c5cfdac3a --- /dev/null +++ b/spring/spring-data/src/main/java/io/quarkus/ts/spring/data/rest/secured/PermitAllRepository.java @@ -0,0 +1,21 @@ +package io.quarkus.ts.spring.data.rest.secured; + +import javax.annotation.security.DenyAll; +import javax.annotation.security.PermitAll; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.rest.core.annotation.RepositoryRestResource; +import org.springframework.data.rest.core.annotation.RestResource; + +import io.quarkus.ts.spring.data.rest.Library; + +@RepositoryRestResource(path = "/secured/permit-all") +@PermitAll +public interface PermitAllRepository extends JpaRepository { + @Override + @RestResource + @DenyAll + Page findAll(Pageable pageable); +} diff --git a/spring/spring-data/src/main/java/io/quarkus/ts/spring/data/rest/secured/PublicRepository.java b/spring/spring-data/src/main/java/io/quarkus/ts/spring/data/rest/secured/PublicRepository.java new file mode 100644 index 000000000..d6235a84b --- /dev/null +++ b/spring/spring-data/src/main/java/io/quarkus/ts/spring/data/rest/secured/PublicRepository.java @@ -0,0 +1,19 @@ +package io.quarkus.ts.spring.data.rest.secured; + +import javax.annotation.security.DenyAll; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.rest.core.annotation.RepositoryRestResource; +import org.springframework.data.rest.core.annotation.RestResource; + +import io.quarkus.ts.spring.data.rest.Library; + +@RepositoryRestResource(path = "/secured/public") +public interface PublicRepository extends PagingAndSortingRepository { + @Override + @RestResource + @DenyAll + Page findAll(Pageable pageable); +} diff --git a/spring/spring-data/src/main/java/io/quarkus/ts/spring/data/rest/secured/RolesAllowedRepository.java b/spring/spring-data/src/main/java/io/quarkus/ts/spring/data/rest/secured/RolesAllowedRepository.java new file mode 100644 index 000000000..487ca3d05 --- /dev/null +++ b/spring/spring-data/src/main/java/io/quarkus/ts/spring/data/rest/secured/RolesAllowedRepository.java @@ -0,0 +1,19 @@ +package io.quarkus.ts.spring.data.rest.secured; + +import java.util.Optional; + +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.rest.core.annotation.RepositoryRestResource; + +import io.quarkus.ts.spring.data.rest.Library; + +@RepositoryRestResource(path = "/secured/roles-allowed") +@RolesAllowed("admin") +public interface RolesAllowedRepository extends CrudRepository { + @Override + @PermitAll + Optional findById(Long aLong); +} diff --git a/spring/spring-data/src/main/resources/application.properties b/spring/spring-data/src/main/resources/application.properties index 1a9b6af43..5ca59acbd 100644 --- a/spring/spring-data/src/main/resources/application.properties +++ b/spring/spring-data/src/main/resources/application.properties @@ -1,3 +1,12 @@ quarkus.datasource.db-kind=postgresql quarkus.hibernate-orm.database.generation=drop-and-create quarkus.hibernate-orm.sql-load-script=import.sql + +# Basic security setup +quarkus.http.auth.basic=true +quarkus.security.users.embedded.enabled=true +quarkus.security.users.embedded.plain-text=true +quarkus.security.users.embedded.users.admin=admin +quarkus.security.users.embedded.users.user=user +quarkus.security.users.embedded.roles.admin=admin +quarkus.security.users.embedded.roles.user=user diff --git a/spring/spring-data/src/test/java/io/quarkus/ts/spring/data/rest/secured/OpenShiftSecuredRepositoryRestResourcesIT.java b/spring/spring-data/src/test/java/io/quarkus/ts/spring/data/rest/secured/OpenShiftSecuredRepositoryRestResourcesIT.java new file mode 100644 index 000000000..e36f53ca0 --- /dev/null +++ b/spring/spring-data/src/test/java/io/quarkus/ts/spring/data/rest/secured/OpenShiftSecuredRepositoryRestResourcesIT.java @@ -0,0 +1,7 @@ +package io.quarkus.ts.spring.data.rest.secured; + +import io.quarkus.test.scenarios.OpenShiftScenario; + +@OpenShiftScenario +public class OpenShiftSecuredRepositoryRestResourcesIT extends SecuredRepositoryRestResourcesIT { +} diff --git a/spring/spring-data/src/test/java/io/quarkus/ts/spring/data/rest/secured/SecuredRepositoryRestResourcesIT.java b/spring/spring-data/src/test/java/io/quarkus/ts/spring/data/rest/secured/SecuredRepositoryRestResourcesIT.java new file mode 100644 index 000000000..b1f2dd242 --- /dev/null +++ b/spring/spring-data/src/test/java/io/quarkus/ts/spring/data/rest/secured/SecuredRepositoryRestResourcesIT.java @@ -0,0 +1,160 @@ +package io.quarkus.ts.spring.data.rest.secured; + +import static io.restassured.RestAssured.given; + +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.scenarios.QuarkusScenario; +import io.quarkus.test.scenarios.annotations.DisabledOnQuarkusVersion; +import io.quarkus.ts.spring.data.AbstractDbIT; +import io.restassured.http.ContentType; + +@Tag("QUARKUS-2788") +@QuarkusScenario +public class SecuredRepositoryRestResourcesIT extends AbstractDbIT { + private static final long NONEXISTENT_ENTITY_ID = 999; + private static final String BASE_URL = "/secured"; + + private String getUrl(String path) { + return BASE_URL + path; + } + + @Test + void publicResourceNoAuth() { + app.given() + .accept(ContentType.JSON) + .get(getUrl("/public/" + NONEXISTENT_ENTITY_ID)) + .then().statusCode(HttpStatus.SC_NOT_FOUND); + } + + @Test + void publicResourceDenyAllMethodNoAuth() { + app.given() + .accept(ContentType.JSON) + .get(getUrl("/public")) + .then().statusCode(HttpStatus.SC_UNAUTHORIZED); + } + + @Test + void publicResourceDenyAllMethodAuth() { + app.given() + .accept(ContentType.JSON) + .auth().basic("admin", "admin") + .get(getUrl("/public")) + .then().statusCode(HttpStatus.SC_FORBIDDEN); + } + + @Test + void denyAllResourceNoAuth() { + app.given() + .accept(ContentType.JSON) + .delete(getUrl("/deny-all/" + NONEXISTENT_ENTITY_ID)) + .then().statusCode(HttpStatus.SC_UNAUTHORIZED); + } + + @Test + void denyAllResourceAuth() { + app.given() + .accept(ContentType.JSON) + .auth().basic("admin", "admin") + .delete(getUrl("/deny-all/" + NONEXISTENT_ENTITY_ID)) + .then().statusCode(HttpStatus.SC_FORBIDDEN); + } + + @Test + void denyAllResourcePermitAllMethodNoAuth() { + given() + .accept(ContentType.JSON) + .get(getUrl("/deny-all")) + .then().statusCode(HttpStatus.SC_OK); + } + + @Test + void denyAllResourceRolesAllowedMethodNoAuth() { + app.given() + .accept(ContentType.JSON) + .get(getUrl("/deny-all/" + NONEXISTENT_ENTITY_ID)) + .then().statusCode(HttpStatus.SC_UNAUTHORIZED); + } + + @Test + void denyAllResourceRolesAllowedMethodAuthForbidden() { + app.given() + .accept(ContentType.JSON) + .auth().basic("user", "user") + .get(getUrl("/deny-all/" + NONEXISTENT_ENTITY_ID)) + .then().statusCode(HttpStatus.SC_FORBIDDEN); + } + + @Test + void denyAllResourceRolesAllowedMethodAuthPermitted() { + app.given() + .accept(ContentType.JSON) + .auth().basic("admin", "admin") + .get(getUrl("/deny-all/" + NONEXISTENT_ENTITY_ID)) + .then().statusCode(HttpStatus.SC_NOT_FOUND); + } + + @Test + void permitAllResourceNoAuth() { + app.given() + .accept(ContentType.JSON) + .get(getUrl("/permit-all/" + NONEXISTENT_ENTITY_ID)) + .then().statusCode(HttpStatus.SC_NOT_FOUND); + } + + @Test + void permitAllResourceDenyAllMethodNoAuth() { + app.given() + .accept(ContentType.JSON) + .get(getUrl("/permit-all")) + .then().statusCode(HttpStatus.SC_UNAUTHORIZED); + } + + @Test + void permitAllResourceDenyAllMethodAuth() { + app.given() + .accept(ContentType.JSON) + .auth().basic("admin", "admin") + .get(getUrl("/permit-all")) + .then().statusCode(HttpStatus.SC_FORBIDDEN); + } + + @Test + void rolesAllowedResourceNoAuth() { + app.given() + .accept(ContentType.JSON) + .get(getUrl("/roles-allowed")) + .then().statusCode(HttpStatus.SC_UNAUTHORIZED); + } + + @Test + void rolesAllowedResourceAuthForbidden() { + app.given() + .accept(ContentType.JSON) + .auth().basic("user", "user") + .get(getUrl("/roles-allowed")) + .then().statusCode(HttpStatus.SC_FORBIDDEN); + } + + @Test + void rolesAllowedResourceAuthPermitted() { + app.given() + .accept(ContentType.JSON) + .auth().basic("admin", "admin") + .get(getUrl("/roles-allowed")) + .then().statusCode(HttpStatus.SC_OK); + } + + // Fix for https://github.com/quarkusio/quarkus/issues/30358 has been backported to Quarkus 2.13 and 2.16, but not to 2.14 and 2.15. + @DisabledOnQuarkusVersion(version = "(2\\.14\\..*)|(2\\.15\\..*)", reason = "https://github.com/quarkusio/quarkus/issues/30358") + @Test + void rolesAllowedResourcePermitAllMethodWithoutRestResourceAnnotation() { + app.given() + .accept(ContentType.JSON) + .get(getUrl("/roles-allowed/" + NONEXISTENT_ENTITY_ID)) + .then().statusCode(HttpStatus.SC_NOT_FOUND); + } +} diff --git a/sql-db/panache-flyway/pom.xml b/sql-db/panache-flyway/pom.xml index 757a4c5fa..069dec6cd 100644 --- a/sql-db/panache-flyway/pom.xml +++ b/sql-db/panache-flyway/pom.xml @@ -35,6 +35,10 @@ io.quarkus quarkus-hibernate-validator + + io.quarkus + quarkus-elytron-security-properties-file + org.testcontainers mysql diff --git a/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityDenyAllResource.java b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityDenyAllResource.java new file mode 100644 index 000000000..2e9becb26 --- /dev/null +++ b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityDenyAllResource.java @@ -0,0 +1,30 @@ +package io.quarkus.ts.sqldb.panacheflyway.secured; + +import java.util.List; + +import javax.annotation.security.DenyAll; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; + +import io.quarkus.hibernate.orm.rest.data.panache.PanacheEntityResource; +import io.quarkus.panache.common.Page; +import io.quarkus.panache.common.Sort; +import io.quarkus.rest.data.panache.MethodProperties; +import io.quarkus.rest.data.panache.ResourceProperties; +import io.quarkus.ts.sqldb.panacheflyway.ApplicationEntity; + +@ResourceProperties(path = "/secured/entity/deny-all") +@DenyAll +public interface EntityDenyAllResource extends PanacheEntityResource { + @Override + @PermitAll + long count(); + + @Override + @RolesAllowed("admin") + List list(Page page, Sort sort); + + @Override + @MethodProperties(rolesAllowed = "admin") + ApplicationEntity get(Long aLong); +} diff --git a/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityPermitAllResource.java b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityPermitAllResource.java new file mode 100644 index 000000000..3806acd61 --- /dev/null +++ b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityPermitAllResource.java @@ -0,0 +1,16 @@ +package io.quarkus.ts.sqldb.panacheflyway.secured; + +import javax.annotation.security.DenyAll; +import javax.annotation.security.PermitAll; + +import io.quarkus.hibernate.orm.rest.data.panache.PanacheEntityResource; +import io.quarkus.rest.data.panache.ResourceProperties; +import io.quarkus.ts.sqldb.panacheflyway.ApplicationEntity; + +@ResourceProperties(path = "/secured/entity/permit-all") +@PermitAll +public interface EntityPermitAllResource extends PanacheEntityResource { + @Override + @DenyAll + long count(); +} diff --git a/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityPublicResource.java b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityPublicResource.java new file mode 100644 index 000000000..9dbf39641 --- /dev/null +++ b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityPublicResource.java @@ -0,0 +1,14 @@ +package io.quarkus.ts.sqldb.panacheflyway.secured; + +import javax.annotation.security.DenyAll; + +import io.quarkus.hibernate.orm.rest.data.panache.PanacheEntityResource; +import io.quarkus.rest.data.panache.ResourceProperties; +import io.quarkus.ts.sqldb.panacheflyway.ApplicationEntity; + +@ResourceProperties(path = "/secured/entity/public") +public interface EntityPublicResource extends PanacheEntityResource { + @Override + @DenyAll + long count(); +} diff --git a/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityResourcePropertiesRolesAllowedResource.java b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityResourcePropertiesRolesAllowedResource.java new file mode 100644 index 000000000..5f6ec25b8 --- /dev/null +++ b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityResourcePropertiesRolesAllowedResource.java @@ -0,0 +1,9 @@ +package io.quarkus.ts.sqldb.panacheflyway.secured; + +import io.quarkus.hibernate.orm.rest.data.panache.PanacheEntityResource; +import io.quarkus.rest.data.panache.ResourceProperties; +import io.quarkus.ts.sqldb.panacheflyway.ApplicationEntity; + +@ResourceProperties(path = "/secured/entity/resource-properties-roles-allowed", rolesAllowed = "admin") +public interface EntityResourcePropertiesRolesAllowedResource extends PanacheEntityResource { +} diff --git a/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityRolesAllowedResource.java b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityRolesAllowedResource.java new file mode 100644 index 000000000..d0141681f --- /dev/null +++ b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/EntityRolesAllowedResource.java @@ -0,0 +1,12 @@ +package io.quarkus.ts.sqldb.panacheflyway.secured; + +import javax.annotation.security.RolesAllowed; + +import io.quarkus.hibernate.orm.rest.data.panache.PanacheEntityResource; +import io.quarkus.rest.data.panache.ResourceProperties; +import io.quarkus.ts.sqldb.panacheflyway.ApplicationEntity; + +@ResourceProperties(path = "/secured/entity/roles-allowed") +@RolesAllowed("admin") +public interface EntityRolesAllowedResource extends PanacheEntityResource { +} diff --git a/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryDenyAllResource.java b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryDenyAllResource.java new file mode 100644 index 000000000..ee2256d19 --- /dev/null +++ b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryDenyAllResource.java @@ -0,0 +1,31 @@ +package io.quarkus.ts.sqldb.panacheflyway.secured; + +import java.util.List; + +import javax.annotation.security.DenyAll; +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; + +import io.quarkus.hibernate.orm.rest.data.panache.PanacheRepositoryResource; +import io.quarkus.panache.common.Page; +import io.quarkus.panache.common.Sort; +import io.quarkus.rest.data.panache.MethodProperties; +import io.quarkus.rest.data.panache.ResourceProperties; +import io.quarkus.ts.sqldb.panacheflyway.UserEntity; +import io.quarkus.ts.sqldb.panacheflyway.UserRepository; + +@ResourceProperties(path = "/secured/repository/deny-all") +@DenyAll +public interface RepositoryDenyAllResource extends PanacheRepositoryResource { + @Override + @PermitAll + long count(); + + @Override + @RolesAllowed("admin") + List list(Page page, Sort sort); + + @Override + @MethodProperties(rolesAllowed = "admin") + UserEntity get(Long aLong); +} diff --git a/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryPermitAllResource.java b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryPermitAllResource.java new file mode 100644 index 000000000..6cd3dbd93 --- /dev/null +++ b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryPermitAllResource.java @@ -0,0 +1,17 @@ +package io.quarkus.ts.sqldb.panacheflyway.secured; + +import javax.annotation.security.DenyAll; +import javax.annotation.security.PermitAll; + +import io.quarkus.hibernate.orm.rest.data.panache.PanacheRepositoryResource; +import io.quarkus.rest.data.panache.ResourceProperties; +import io.quarkus.ts.sqldb.panacheflyway.UserEntity; +import io.quarkus.ts.sqldb.panacheflyway.UserRepository; + +@ResourceProperties(path = "/secured/repository/permit-all") +@PermitAll +public interface RepositoryPermitAllResource extends PanacheRepositoryResource { + @Override + @DenyAll + long count(); +} diff --git a/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryPublicResource.java b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryPublicResource.java new file mode 100644 index 000000000..c88bd054d --- /dev/null +++ b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryPublicResource.java @@ -0,0 +1,15 @@ +package io.quarkus.ts.sqldb.panacheflyway.secured; + +import javax.annotation.security.DenyAll; + +import io.quarkus.hibernate.orm.rest.data.panache.PanacheRepositoryResource; +import io.quarkus.rest.data.panache.ResourceProperties; +import io.quarkus.ts.sqldb.panacheflyway.UserEntity; +import io.quarkus.ts.sqldb.panacheflyway.UserRepository; + +@ResourceProperties(path = "/secured/repository/public") +public interface RepositoryPublicResource extends PanacheRepositoryResource { + @Override + @DenyAll + long count(); +} diff --git a/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryResourcePropertiesRolesAllowedResource.java b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryResourcePropertiesRolesAllowedResource.java new file mode 100644 index 000000000..a0a8b5a5b --- /dev/null +++ b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryResourcePropertiesRolesAllowedResource.java @@ -0,0 +1,11 @@ +package io.quarkus.ts.sqldb.panacheflyway.secured; + +import io.quarkus.hibernate.orm.rest.data.panache.PanacheRepositoryResource; +import io.quarkus.rest.data.panache.ResourceProperties; +import io.quarkus.ts.sqldb.panacheflyway.UserEntity; +import io.quarkus.ts.sqldb.panacheflyway.UserRepository; + +@ResourceProperties(path = "/secured/repository/resource-properties-roles-allowed", rolesAllowed = "admin") +public interface RepositoryResourcePropertiesRolesAllowedResource + extends PanacheRepositoryResource { +} diff --git a/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryRolesAllowedResource.java b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryRolesAllowedResource.java new file mode 100644 index 000000000..a2c0da618 --- /dev/null +++ b/sql-db/panache-flyway/src/main/java/io/quarkus/ts/sqldb/panacheflyway/secured/RepositoryRolesAllowedResource.java @@ -0,0 +1,13 @@ +package io.quarkus.ts.sqldb.panacheflyway.secured; + +import javax.annotation.security.RolesAllowed; + +import io.quarkus.hibernate.orm.rest.data.panache.PanacheRepositoryResource; +import io.quarkus.rest.data.panache.ResourceProperties; +import io.quarkus.ts.sqldb.panacheflyway.UserEntity; +import io.quarkus.ts.sqldb.panacheflyway.UserRepository; + +@ResourceProperties(path = "/secured/repository/roles-allowed") +@RolesAllowed("admin") +public interface RepositoryRolesAllowedResource extends PanacheRepositoryResource { +} diff --git a/sql-db/panache-flyway/src/main/resources/application.properties b/sql-db/panache-flyway/src/main/resources/application.properties index 3164cab32..3409020b5 100644 --- a/sql-db/panache-flyway/src/main/resources/application.properties +++ b/sql-db/panache-flyway/src/main/resources/application.properties @@ -30,3 +30,12 @@ quarkus.flyway.schemas=test %agroal_pool_test.quarkus.datasource.jdbc.acquisition-timeout=60S %agroal_pool_test.quarkus.datasource.jdbc.validation-query-sql=SELECT CURRENT_TIMESTAMP %agroal_pool_test.quarkus.datasource.jdbc.new-connection-sql=SELECT CURRENT_TIMESTAMP + +# Basic security setup +quarkus.http.auth.basic=true +quarkus.security.users.embedded.enabled=true +quarkus.security.users.embedded.plain-text=true +quarkus.security.users.embedded.users.admin=admin +quarkus.security.users.embedded.users.user=user +quarkus.security.users.embedded.roles.admin=admin +quarkus.security.users.embedded.roles.user=user diff --git a/sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/AbstractSecuredPanacheResourceIT.java b/sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/AbstractSecuredPanacheResourceIT.java new file mode 100644 index 000000000..660617010 --- /dev/null +++ b/sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/AbstractSecuredPanacheResourceIT.java @@ -0,0 +1,176 @@ +package io.quarkus.ts.sqldb.panacheflyway.secured; + +import static io.restassured.RestAssured.given; + +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.Test; + +import io.quarkus.ts.sqldb.panacheflyway.PanacheWithFlywayBaseIT; + +public abstract class AbstractSecuredPanacheResourceIT extends PanacheWithFlywayBaseIT { + private static final long NONEXISTENT_ENTITY_ID = 999; + + protected abstract String getBaseUrl(); + + private String getUrl(String path) { + return getBaseUrl() + path; + } + + @Test + void publicResourceNoAuth() { + given() + .get(getUrl("/public")) + .then().statusCode(HttpStatus.SC_OK); + } + + @Test + void publicResourceDenyAllMethodNoAuth() { + given() + .get(getUrl("/public/count")) + .then().statusCode(HttpStatus.SC_UNAUTHORIZED); + } + + @Test + void publicResourceDenyAllMethodAuth() { + given() + .auth().basic("admin", "admin") + .get(getUrl("/public/count")) + .then().statusCode(HttpStatus.SC_FORBIDDEN); + } + + @Test + void denyAllResourceNoAuth() { + given() + .delete(getUrl("/deny-all/" + NONEXISTENT_ENTITY_ID)) + .then().statusCode(HttpStatus.SC_UNAUTHORIZED); + } + + @Test + void denyAllResourceAuth() { + given() + .auth().basic("admin", "admin") + .delete(getUrl("/deny-all/" + NONEXISTENT_ENTITY_ID)) + .then().statusCode(HttpStatus.SC_FORBIDDEN); + } + + @Test + void denyAllResourcePermitAllMethodNoAuth() { + given() + .get(getUrl("/deny-all/count")) + .then().statusCode(HttpStatus.SC_OK); + } + + @Test + void denyAllResourceRolesAllowedMethodNoAuth() { + given() + .get(getUrl("/deny-all")) + .then().statusCode(HttpStatus.SC_UNAUTHORIZED); + } + + @Test + void denyAllResourceRolesAllowedMethodAuthForbidden() { + given() + .auth().basic("user", "user") + .get(getUrl("/deny-all")) + .then().statusCode(HttpStatus.SC_FORBIDDEN); + } + + @Test + void denyAllResourceRolesAllowedMethodAuthPermitted() { + given() + .auth().basic("admin", "admin") + .get(getUrl("/deny-all")) + .then().statusCode(HttpStatus.SC_OK); + } + + @Test + void denyAllResourcePropertiesRolesAllowedMethodNoAuth() { + given() + .get(getUrl("/deny-all/" + NONEXISTENT_ENTITY_ID)) + .then().statusCode(HttpStatus.SC_UNAUTHORIZED); + } + + @Test + void denyAllResourcePropertiesRolesAllowedMethodAuthForbidden() { + given() + .auth().basic("user", "user") + .get(getUrl("/deny-all/" + NONEXISTENT_ENTITY_ID)) + .then().statusCode(HttpStatus.SC_FORBIDDEN); + } + + @Test + void denyAllResourcePropertiesRolesAllowedMethodAuthPermitted() { + given() + .auth().basic("admin", "admin") + .get(getUrl("/deny-all/" + NONEXISTENT_ENTITY_ID)) + .then().statusCode(HttpStatus.SC_NOT_FOUND); + } + + @Test + void permitAllResourceNoAuth() { + given() + .get(getUrl("/permit-all")) + .then().statusCode(HttpStatus.SC_OK); + } + + @Test + void permitAllResourceDenyAllMethodNoAuth() { + given() + .get(getUrl("/permit-all/count")) + .then().statusCode(HttpStatus.SC_UNAUTHORIZED); + } + + @Test + void permitAllResourceDenyAllMethodAuth() { + given() + .auth().basic("admin", "admin") + .get(getUrl("/permit-all/count")) + .then().statusCode(HttpStatus.SC_FORBIDDEN); + } + + @Test + void propertiesRolesAllowedResourceNoAuth() { + given() + .get(getUrl("/resource-properties-roles-allowed")) + .then().statusCode(HttpStatus.SC_UNAUTHORIZED); + } + + @Test + void propertiesRolesAllowedResourceAuthForbidden() { + given() + .auth().basic("user", "user") + .get(getUrl("/resource-properties-roles-allowed")) + .then().statusCode(HttpStatus.SC_FORBIDDEN); + } + + @Test + void propertiesRolesAllowedResourceAuthPermitted() { + given() + .auth().basic("admin", "admin") + .get(getUrl("/resource-properties-roles-allowed")) + .then().statusCode(HttpStatus.SC_OK); + } + + @Test + void rolesAllowedResourceNoAuth() { + given() + .get(getUrl("/roles-allowed")) + .then().statusCode(HttpStatus.SC_UNAUTHORIZED); + } + + @Test + void rolesAllowedResourceAuthForbidden() { + given() + .auth().basic("user", "user") + .get(getUrl("/roles-allowed")) + .then().statusCode(HttpStatus.SC_FORBIDDEN); + } + + @Test + void rolesAllowedResourceAuthPermitted() { + given() + .auth().basic("admin", "admin") + .get(getUrl("/roles-allowed")) + .then().statusCode(HttpStatus.SC_OK); + } +} diff --git a/sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/OpenShiftSecuredPanacheEntityResourceIT.java b/sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/OpenShiftSecuredPanacheEntityResourceIT.java new file mode 100644 index 000000000..fc555a7a9 --- /dev/null +++ b/sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/OpenShiftSecuredPanacheEntityResourceIT.java @@ -0,0 +1,7 @@ +package io.quarkus.ts.sqldb.panacheflyway.secured; + +import io.quarkus.test.scenarios.OpenShiftScenario; + +@OpenShiftScenario +public class OpenShiftSecuredPanacheEntityResourceIT extends SecuredPanacheEntityResourceIT { +} diff --git a/sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/OpenShiftSecuredPanacheRepositoryResourceIT.java b/sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/OpenShiftSecuredPanacheRepositoryResourceIT.java new file mode 100644 index 000000000..1ce44a269 --- /dev/null +++ b/sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/OpenShiftSecuredPanacheRepositoryResourceIT.java @@ -0,0 +1,7 @@ +package io.quarkus.ts.sqldb.panacheflyway.secured; + +import io.quarkus.test.scenarios.OpenShiftScenario; + +@OpenShiftScenario +public class OpenShiftSecuredPanacheRepositoryResourceIT extends SecuredPanacheRepositoryResourceIT { +} diff --git a/sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/SecuredPanacheEntityResourceIT.java b/sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/SecuredPanacheEntityResourceIT.java new file mode 100644 index 000000000..d588da07c --- /dev/null +++ b/sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/SecuredPanacheEntityResourceIT.java @@ -0,0 +1,16 @@ +package io.quarkus.ts.sqldb.panacheflyway.secured; + +import org.junit.jupiter.api.Tag; + +import io.quarkus.test.scenarios.QuarkusScenario; + +@Tag("QUARKUS-2788") +@QuarkusScenario +public class SecuredPanacheEntityResourceIT extends AbstractSecuredPanacheResourceIT { + private static final String BASE_URL = "/secured/entity"; + + @Override + protected String getBaseUrl() { + return BASE_URL; + } +} diff --git a/sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/SecuredPanacheRepositoryResourceIT.java b/sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/SecuredPanacheRepositoryResourceIT.java new file mode 100644 index 000000000..16f2bb543 --- /dev/null +++ b/sql-db/panache-flyway/src/test/java/io/quarkus/ts/sqldb/panacheflyway/secured/SecuredPanacheRepositoryResourceIT.java @@ -0,0 +1,16 @@ +package io.quarkus.ts.sqldb.panacheflyway.secured; + +import org.junit.jupiter.api.Tag; + +import io.quarkus.test.scenarios.QuarkusScenario; + +@Tag("QUARKUS-2788") +@QuarkusScenario +public class SecuredPanacheRepositoryResourceIT extends AbstractSecuredPanacheResourceIT { + private static final String BASE_URL = "/secured/repository"; + + @Override + protected String getBaseUrl() { + return BASE_URL; + } +}