Skip to content

Commit

Permalink
Add test for security annotations in rest-data-panache
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jsmrcka committed Jan 13, 2023
1 parent 2fad57f commit ac96b8b
Show file tree
Hide file tree
Showing 25 changed files with 670 additions and 0 deletions.
4 changes: 4 additions & 0 deletions spring/spring-data/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-elytron-security-properties-file</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus.qe</groupId>
<artifactId>quarkus-test-service-database</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Library, Long> {
@Override
@RestResource
@PermitAll
Iterable<Library> findAll();

@Override
@RestResource
@RolesAllowed("admin")
Optional<Library> findById(Long aLong);
}
Original file line number Diff line number Diff line change
@@ -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<Library, Long> {
@Override
@RestResource
@DenyAll
Page<Library> findAll(Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -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<Library, Long> {
@Override
@RestResource
@DenyAll
Page<Library> findAll(Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.quarkus.ts.spring.data.rest.secured;

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<Library, Long> {
@Override
@PermitAll
Iterable<Library> findAll();
}
9 changes: 9 additions & 0 deletions spring/spring-data/src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.quarkus.ts.spring.data.rest.secured;

import io.quarkus.test.scenarios.OpenShiftScenario;

@OpenShiftScenario
public class OpenShiftSecuredRepositoryRestResourcesIT extends SecuredRepositoryRestResourcesIT {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
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.DisabledOnQuarkusSnapshot;
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);
}

// TODO: Enable for Quarkus SNAPSHOT after a fix for https://github.com/quarkusio/quarkus/issues/30358 is merged.
@DisabledOnQuarkusSnapshot(reason = "https://github.com/quarkusio/quarkus/issues/30358")
// TODO: Enable after a fix for https://github.com/quarkusio/quarkus/issues/30358 is backported to Quarkus 2.13, 2.14, 2.15
@DisabledOnQuarkusVersion(version = "(2\\.13\\..*)|(2\\.14\\..*)|(2\\.15\\..*)", reason = "https://github.com/quarkusio/quarkus/issues/30358")
@Test
void rolesAllowedResourcePermitAllMethodWithoutRestResourceAnnotation() {
app.given()
.accept(ContentType.JSON)
.get(getUrl("/roles-allowed"))
.then().statusCode(HttpStatus.SC_OK);
}
}
4 changes: 4 additions & 0 deletions sql-db/panache-flyway/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-elytron-security-properties-file</artifactId>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<ApplicationEntity, Long> {
@Override
@PermitAll
long count();

@Override
@RolesAllowed("admin")
List<ApplicationEntity> list(Page page, Sort sort);

@Override
@MethodProperties(rolesAllowed = "admin")
ApplicationEntity get(Long aLong);
}
Original file line number Diff line number Diff line change
@@ -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<ApplicationEntity, Long> {
@Override
@DenyAll
long count();
}
Original file line number Diff line number Diff line change
@@ -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<ApplicationEntity, Long> {
@Override
@DenyAll
long count();
}
Original file line number Diff line number Diff line change
@@ -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<ApplicationEntity, Long> {
}
Original file line number Diff line number Diff line change
@@ -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<ApplicationEntity, Long> {
}
Loading

0 comments on commit ac96b8b

Please sign in to comment.