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;
+ }
+}