From e1ae1d8eb47fb37184ad58119184a344df898849 Mon Sep 17 00:00:00 2001 From: Jose Date: Wed, 2 Nov 2022 16:22:33 +0100 Subject: [PATCH] Propagate the javax.annotation.security annotations in REST Data With these changes, the REST Data with Panache extension will propagate the Security annotations within the package `javax.annotation.security` that are defined on your resource interfaces: ```java import javax.annotation.security.DenyAll; import javax.annotation.security.RolesAllowed; @DenyAll @ResourceProperties public interface PeopleResource extends PanacheEntityResource { @RolesAllowed("superuser") boolean delete(Long id); } ``` Additionally, if you are only interested in specifying the roles that are allowed to use the resources, the `@ResourceProperties` and `@MethodProperties` annotations have the field `rolesAllowed` to list the security roles permitted to access the resource or operation. Fix https://github.com/quarkusio/quarkus/issues/28995 --- docs/src/main/asciidoc/rest-data-panache.adoc | 20 ++++++ docs/src/main/asciidoc/security.adoc | 5 ++ .../deployment/openapi/AbstractEntity.java | 17 +++++ .../deployment/openapi/AbstractItem.java | 25 +++++++ .../deployment/openapi/Collection.java | 45 +++++++++++++ .../openapi/CollectionsRepository.java | 9 +++ .../openapi/CollectionsResource.java | 16 +++++ .../deployment/openapi/EmptyListItem.java | 8 +++ .../openapi/EmptyListItemsRepository.java | 9 +++ .../openapi/EmptyListItemsResource.java | 8 +++ .../data/panache/deployment/openapi/Item.java | 8 +++ .../deployment/openapi/ItemsRepository.java | 9 +++ .../deployment/openapi/ItemsResource.java | 8 +++ .../openapi/OpenApiIntegrationTest.java | 13 +--- .../repository/CollectionsResource.java | 5 +- .../deployment/JaxRsResourceImplementor.java | 6 ++ .../methods/AddMethodImplementor.java | 1 + .../methods/CountMethodImplementor.java | 1 + .../methods/DeleteMethodImplementor.java | 1 + .../methods/GetMethodImplementor.java | 1 + .../methods/ListMethodImplementor.java | 2 + .../methods/StandardMethodImplementor.java | 11 ++++ .../methods/UpdateMethodImplementor.java | 1 + .../methods/hal/ListHalMethodImplementor.java | 2 + .../properties/MethodProperties.java | 14 +++- .../properties/ResourceProperties.java | 22 ++++++- .../ResourcePropertiesProvider.java | 61 +++++++++++++---- .../ResourcePropertiesProvider.java | 65 ++++++++++++++++--- 28 files changed, 355 insertions(+), 38 deletions(-) create mode 100644 extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/AbstractEntity.java create mode 100644 extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/AbstractItem.java create mode 100644 extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/Collection.java create mode 100644 extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/CollectionsRepository.java create mode 100644 extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/CollectionsResource.java create mode 100644 extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/EmptyListItem.java create mode 100644 extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/EmptyListItemsRepository.java create mode 100644 extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/EmptyListItemsResource.java create mode 100644 extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/Item.java create mode 100644 extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/ItemsRepository.java create mode 100644 extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/ItemsResource.java diff --git a/docs/src/main/asciidoc/rest-data-panache.adoc b/docs/src/main/asciidoc/rest-data-panache.adoc index 3d66a770412c1..6dc0b32d028d3 100644 --- a/docs/src/main/asciidoc/rest-data-panache.adoc +++ b/docs/src/main/asciidoc/rest-data-panache.adoc @@ -322,6 +322,26 @@ Default is `false`. * `path` - operation path (this is appended to the resource base path). Default is an empty string. * `rolesAllowed` - List of the security roles permitted to access this operation. It needs a Quarkus security extension to be present, otherwise it will be ignored. Default is empty. +== Securing endpoints + +REST Data with Panache will use the Security annotations within the package `javax.annotation.security` that are defined on your resource interfaces: + +[source,java] +---- + +import javax.annotation.security.DenyAll; +import javax.annotation.security.RolesAllowed; + +@DenyAll +@ResourceProperties +public interface PeopleResource extends PanacheEntityResource { + @RolesAllowed("superuser") + boolean delete(Long id); +} +---- + +Additionally, if you are only interested in specifying the roles that are allowed to use the resources, the `@ResourceProperties` and `@MethodProperties` annotations have the field `rolesAllowed` to list the security roles permitted to access the resource or operation. + == Query parameters REST Data with Panache supports the following query parameters with the generated resources. diff --git a/docs/src/main/asciidoc/security.adoc b/docs/src/main/asciidoc/security.adoc index 6e6815d721c03..358f0b0c7cf72 100644 --- a/docs/src/main/asciidoc/security.adoc +++ b/docs/src/main/asciidoc/security.adoc @@ -318,6 +318,11 @@ For more information, see the link:{vault-guide}[Quarkus and HashiCorp Vault] do If your Quarkus Security architecture includes RESTEasy Reactive and Jackson, Quarkus can limit the fields that are included in JSON serialization based on the configured security. For more information, see xref:resteasy-reactive.adoc#secure-serialization[Writing REST services with RESTEasy Reactive]. +== Secure auto-generated resources by REST Data with Panache + +If you're using the REST Data with Panache extension to auto-generate your resources, you can still use the Security annotations within the package `javax.annotation.security`. +For more information, see xref:rest-data-panache.adoc#securing-endpoints[Securing auto-generated resources]. + == National Vulnerability Database Most of the Quarkus tags are registered in the US link:https://nvd.nist.gov[National Vulnerability Database] (NVD) in Common Platform Enumeration (CPE) name format. diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/AbstractEntity.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/AbstractEntity.java new file mode 100644 index 0000000000000..12164b9b77b98 --- /dev/null +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/AbstractEntity.java @@ -0,0 +1,17 @@ +package io.quarkus.hibernate.orm.rest.data.panache.deployment.openapi; + +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; + +@MappedSuperclass +public abstract class AbstractEntity { + + @Id + @GeneratedValue + private IdType id; + + public IdType getId() { + return id; + } +} diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/AbstractItem.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/AbstractItem.java new file mode 100644 index 0000000000000..9cc7828fa24d4 --- /dev/null +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/AbstractItem.java @@ -0,0 +1,25 @@ +package io.quarkus.hibernate.orm.rest.data.panache.deployment.openapi; + +import javax.persistence.ManyToOne; +import javax.persistence.MappedSuperclass; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonProperty.Access; + +@MappedSuperclass +public abstract class AbstractItem extends AbstractEntity { + + private String name; + + @ManyToOne(optional = false) + @JsonProperty(access = Access.WRITE_ONLY) + private Collection collection; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/Collection.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/Collection.java new file mode 100644 index 0000000000000..0722edf007579 --- /dev/null +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/Collection.java @@ -0,0 +1,45 @@ +package io.quarkus.hibernate.orm.rest.data.panache.deployment.openapi; + +import java.util.LinkedList; +import java.util.List; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.OneToMany; + +@Entity +public class Collection { + + @Id + private String id; + + private String name; + + @OneToMany(fetch = FetchType.EAGER, mappedBy = "collection") + private List items = new LinkedList<>(); + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } +} diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/CollectionsRepository.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/CollectionsRepository.java new file mode 100644 index 0000000000000..2423c9b6ce78c --- /dev/null +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/CollectionsRepository.java @@ -0,0 +1,9 @@ +package io.quarkus.hibernate.orm.rest.data.panache.deployment.openapi; + +import javax.enterprise.context.ApplicationScoped; + +import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase; + +@ApplicationScoped +public class CollectionsRepository implements PanacheRepositoryBase { +} diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/CollectionsResource.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/CollectionsResource.java new file mode 100644 index 0000000000000..323e85f727b4b --- /dev/null +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/CollectionsResource.java @@ -0,0 +1,16 @@ +package io.quarkus.hibernate.orm.rest.data.panache.deployment.openapi; + +import javax.annotation.security.RolesAllowed; + +import io.quarkus.hibernate.orm.rest.data.panache.PanacheRepositoryResource; +import io.quarkus.rest.data.panache.MethodProperties; +import io.quarkus.rest.data.panache.ResourceProperties; + +@ResourceProperties(hal = true, paged = false, halCollectionName = "item-collections", rolesAllowed = "user") +public interface CollectionsResource extends PanacheRepositoryResource { + @RolesAllowed("superuser") + Collection update(String id, Collection entity); + + @MethodProperties(rolesAllowed = "admin") + boolean delete(String name); +} diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/EmptyListItem.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/EmptyListItem.java new file mode 100644 index 0000000000000..b233ea1166bec --- /dev/null +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/EmptyListItem.java @@ -0,0 +1,8 @@ +package io.quarkus.hibernate.orm.rest.data.panache.deployment.openapi; + +import javax.persistence.Entity; + +@Entity +public class EmptyListItem extends AbstractItem { + +} diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/EmptyListItemsRepository.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/EmptyListItemsRepository.java new file mode 100644 index 0000000000000..134faddb349e7 --- /dev/null +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/EmptyListItemsRepository.java @@ -0,0 +1,9 @@ +package io.quarkus.hibernate.orm.rest.data.panache.deployment.openapi; + +import javax.enterprise.context.ApplicationScoped; + +import io.quarkus.hibernate.orm.panache.PanacheRepository; + +@ApplicationScoped +public class EmptyListItemsRepository implements PanacheRepository { +} diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/EmptyListItemsResource.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/EmptyListItemsResource.java new file mode 100644 index 0000000000000..26b41e8545e15 --- /dev/null +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/EmptyListItemsResource.java @@ -0,0 +1,8 @@ +package io.quarkus.hibernate.orm.rest.data.panache.deployment.openapi; + +import io.quarkus.hibernate.orm.rest.data.panache.PanacheRepositoryResource; +import io.quarkus.rest.data.panache.ResourceProperties; + +@ResourceProperties(hal = true) +public interface EmptyListItemsResource extends PanacheRepositoryResource { +} diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/Item.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/Item.java new file mode 100644 index 0000000000000..e448245ee8509 --- /dev/null +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/Item.java @@ -0,0 +1,8 @@ +package io.quarkus.hibernate.orm.rest.data.panache.deployment.openapi; + +import javax.persistence.Entity; + +@Entity +public class Item extends AbstractItem { + +} diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/ItemsRepository.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/ItemsRepository.java new file mode 100644 index 0000000000000..6367d59d99d30 --- /dev/null +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/ItemsRepository.java @@ -0,0 +1,9 @@ +package io.quarkus.hibernate.orm.rest.data.panache.deployment.openapi; + +import javax.enterprise.context.ApplicationScoped; + +import io.quarkus.hibernate.orm.panache.PanacheRepository; + +@ApplicationScoped +public class ItemsRepository implements PanacheRepository { +} diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/ItemsResource.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/ItemsResource.java new file mode 100644 index 0000000000000..9d6325e5719e8 --- /dev/null +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/ItemsResource.java @@ -0,0 +1,8 @@ +package io.quarkus.hibernate.orm.rest.data.panache.deployment.openapi; + +import io.quarkus.hibernate.orm.rest.data.panache.PanacheRepositoryResource; +import io.quarkus.rest.data.panache.ResourceProperties; + +@ResourceProperties(hal = true) +public interface ItemsResource extends PanacheRepositoryResource { +} diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/OpenApiIntegrationTest.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/OpenApiIntegrationTest.java index 8c1e6cabd5800..aca92b5b95199 100644 --- a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/OpenApiIntegrationTest.java +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/openapi/OpenApiIntegrationTest.java @@ -9,17 +9,6 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.builder.Version; -import io.quarkus.hibernate.orm.rest.data.panache.deployment.repository.AbstractEntity; -import io.quarkus.hibernate.orm.rest.data.panache.deployment.repository.AbstractItem; -import io.quarkus.hibernate.orm.rest.data.panache.deployment.repository.Collection; -import io.quarkus.hibernate.orm.rest.data.panache.deployment.repository.CollectionsRepository; -import io.quarkus.hibernate.orm.rest.data.panache.deployment.repository.CollectionsResource; -import io.quarkus.hibernate.orm.rest.data.panache.deployment.repository.EmptyListItem; -import io.quarkus.hibernate.orm.rest.data.panache.deployment.repository.EmptyListItemsRepository; -import io.quarkus.hibernate.orm.rest.data.panache.deployment.repository.EmptyListItemsResource; -import io.quarkus.hibernate.orm.rest.data.panache.deployment.repository.Item; -import io.quarkus.hibernate.orm.rest.data.panache.deployment.repository.ItemsRepository; -import io.quarkus.hibernate.orm.rest.data.panache.deployment.repository.ItemsResource; import io.quarkus.maven.dependency.Dependency; import io.quarkus.test.QuarkusProdModeTest; import io.restassured.RestAssured; @@ -73,7 +62,7 @@ public void testOpenApiForGeneratedResources() { is(COLLECTIONS_SCHEMA_REF)) .body("paths.'/collections/{id}'.put.responses.'201'.content.'application/json'.schema.$ref", is(COLLECTIONS_SCHEMA_REF)) - .body("paths.'/collections/{id}'.put.security[0].SecurityScheme", Matchers.hasItem("user")) + .body("paths.'/collections/{id}'.put.security[0].SecurityScheme", Matchers.hasItem("superuser")) .body("paths.'/collections/{id}'", Matchers.hasKey("delete")) .body("paths.'/collections/{id}'.delete.responses", Matchers.hasKey("204")) .body("paths.'/collections/{id}'.delete.security[0].SecurityScheme", Matchers.hasItem("admin")) diff --git a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/repository/CollectionsResource.java b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/repository/CollectionsResource.java index 55379c59b2f56..428e6a5be54fb 100644 --- a/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/repository/CollectionsResource.java +++ b/extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/orm/rest/data/panache/deployment/repository/CollectionsResource.java @@ -1,11 +1,8 @@ package io.quarkus.hibernate.orm.rest.data.panache.deployment.repository; import io.quarkus.hibernate.orm.rest.data.panache.PanacheRepositoryResource; -import io.quarkus.rest.data.panache.MethodProperties; import io.quarkus.rest.data.panache.ResourceProperties; -@ResourceProperties(hal = true, paged = false, halCollectionName = "item-collections", rolesAllowed = "user") +@ResourceProperties(hal = true, paged = false, halCollectionName = "item-collections") public interface CollectionsResource extends PanacheRepositoryResource { - @MethodProperties(rolesAllowed = "admin") - boolean delete(String name); } diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/JaxRsResourceImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/JaxRsResourceImplementor.java index 8a52d03eae3f0..aa6d680123d33 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/JaxRsResourceImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/JaxRsResourceImplementor.java @@ -8,6 +8,7 @@ import javax.ws.rs.Path; import org.apache.commons.lang3.StringUtils; +import org.jboss.jandex.AnnotationInstance; import org.jboss.logging.Logger; import io.quarkus.deployment.Capabilities; @@ -88,6 +89,11 @@ private void implementClassAnnotations(ClassCreator classCreator, ResourceMetada String className = StringUtils.substringAfterLast(resourceMetadata.getResourceInterface(), "."); classCreator.addAnnotation(OPENAPI_TAG_ANNOTATION).add("name", className); } + if (resourceProperties.getClassAnnotations() != null) { + for (AnnotationInstance classAnnotation : resourceProperties.getClassAnnotations()) { + classCreator.addAnnotation(classAnnotation); + } + } } private FieldDescriptor implementResourceField(ClassCreator classCreator, ResourceMetadata resourceMetadata) { diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/AddMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/AddMethodImplementor.java index ab1d6a59245fd..99a750d24f751 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/AddMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/AddMethodImplementor.java @@ -106,6 +106,7 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res // Add method annotations addPathAnnotation(methodCreator, resourceProperties.getPath(RESOURCE_METHOD_NAME)); + addMethodAnnotations(methodCreator, resourceProperties.getMethodAnnotations(RESOURCE_METHOD_NAME)); addPostAnnotation(methodCreator); addConsumesAnnotation(methodCreator, APPLICATION_JSON); addProducesJsonAnnotation(methodCreator, resourceProperties); diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/CountMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/CountMethodImplementor.java index de41bdd36fd91..efc45733a161a 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/CountMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/CountMethodImplementor.java @@ -80,6 +80,7 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res addGetAnnotation(methodCreator); addProducesAnnotation(methodCreator, APPLICATION_JSON); addPathAnnotation(methodCreator, appendToPath(resourceProperties.getPath(RESOURCE_METHOD_NAME), RESOURCE_METHOD_NAME)); + addMethodAnnotations(methodCreator, resourceProperties.getMethodAnnotations(RESOURCE_METHOD_NAME)); addOpenApiResponseAnnotation(methodCreator, Response.Status.OK, Long.class, false); addSecurityAnnotations(methodCreator, resourceProperties); if (!isResteasyClassic()) { diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/DeleteMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/DeleteMethodImplementor.java index 7bd2f25f85f48..37f1165300512 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/DeleteMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/DeleteMethodImplementor.java @@ -90,6 +90,7 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res addDeleteAnnotation(methodCreator); addPathParamAnnotation(methodCreator.getParameterAnnotations(0), "id"); addLinksAnnotation(methodCreator, resourceMetadata.getEntityType(), REL); + addMethodAnnotations(methodCreator, resourceProperties.getMethodAnnotations(RESOURCE_METHOD_NAME)); addOpenApiResponseAnnotation(methodCreator, Response.Status.NO_CONTENT); addSecurityAnnotations(methodCreator, resourceProperties); diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/GetMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/GetMethodImplementor.java index ad1ebc72adeb1..0b3b4039f2d1b 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/GetMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/GetMethodImplementor.java @@ -91,6 +91,7 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res addPathAnnotation(methodCreator, appendToPath(resourceProperties.getPath(RESOURCE_METHOD_NAME), "{id}")); addGetAnnotation(methodCreator); addProducesJsonAnnotation(methodCreator, resourceProperties); + addMethodAnnotations(methodCreator, resourceProperties.getMethodAnnotations(RESOURCE_METHOD_NAME)); addOpenApiResponseAnnotation(methodCreator, Response.Status.OK, resourceMetadata.getEntityType()); addSecurityAnnotations(methodCreator, resourceProperties); diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java index 5180ffa6afc31..03edb26b98cb0 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/ListMethodImplementor.java @@ -143,6 +143,7 @@ private void implementPaged(ClassCreator classCreator, ResourceMetadata resource addPathAnnotation(methodCreator, resourceProperties.getPath(RESOURCE_METHOD_NAME)); addProducesAnnotation(methodCreator, APPLICATION_JSON); addLinksAnnotation(methodCreator, resourceMetadata.getEntityType(), REL); + addMethodAnnotations(methodCreator, resourceProperties.getMethodAnnotations(RESOURCE_METHOD_NAME)); addOpenApiResponseAnnotation(methodCreator, Response.Status.OK, resourceMetadata.getEntityType(), true); addSecurityAnnotations(methodCreator, resourceProperties); addSortQueryParamValidatorAnnotation(methodCreator); @@ -210,6 +211,7 @@ private void implementNotPaged(ClassCreator classCreator, ResourceMetadata resou addPathAnnotation(methodCreator, resourceProperties.getPath(RESOURCE_METHOD_NAME)); addProducesAnnotation(methodCreator, APPLICATION_JSON); addLinksAnnotation(methodCreator, resourceMetadata.getEntityType(), REL); + addMethodAnnotations(methodCreator, resourceProperties.getMethodAnnotations(RESOURCE_METHOD_NAME)); addOpenApiResponseAnnotation(methodCreator, Response.Status.OK, resourceMetadata.getEntityType(), true); addSecurityAnnotations(methodCreator, resourceProperties); addQueryParamAnnotation(methodCreator.getParameterAnnotations(0), "sort"); diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/StandardMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/StandardMethodImplementor.java index aa74c43a1a369..1af632b140993 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/StandardMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/StandardMethodImplementor.java @@ -1,5 +1,7 @@ package io.quarkus.rest.data.panache.deployment.methods; +import java.util.Collection; + import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; @@ -13,6 +15,7 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; +import org.jboss.jandex.AnnotationInstance; import org.jboss.logging.Logger; import io.quarkus.deployment.Capabilities; @@ -154,6 +157,14 @@ protected void addSortQueryParamValidatorAnnotation(AnnotatedElement element) { element.addAnnotation(SortQueryParamValidator.class); } + protected void addMethodAnnotations(AnnotatedElement element, Collection methodAnnotations) { + if (methodAnnotations != null) { + for (AnnotationInstance methodAnnotation : methodAnnotations) { + element.addAnnotation(methodAnnotation); + } + } + } + protected void addSecurityAnnotations(AnnotatedElement element, ResourceProperties resourceProperties) { String[] rolesAllowed = resourceProperties.getRolesAllowed(getResourceMethodName()); if (rolesAllowed.length > 0 && hasSecurityCapability()) { diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java index e74b77a908a79..e05fa639072df 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/UpdateMethodImplementor.java @@ -143,6 +143,7 @@ protected void implementInternal(ClassCreator classCreator, ResourceMetadata res addConsumesAnnotation(methodCreator, APPLICATION_JSON); addProducesJsonAnnotation(methodCreator, resourceProperties); addLinksAnnotation(methodCreator, resourceMetadata.getEntityType(), REL); + addMethodAnnotations(methodCreator, resourceProperties.getMethodAnnotations(RESOURCE_UPDATE_METHOD_NAME)); addOpenApiResponseAnnotation(methodCreator, Response.Status.CREATED, resourceMetadata.getEntityType()); addSecurityAnnotations(methodCreator, resourceProperties); // Add parameter annotations diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/ListHalMethodImplementor.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/ListHalMethodImplementor.java index 09da8fbfe7e73..df974df30777d 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/ListHalMethodImplementor.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/methods/hal/ListHalMethodImplementor.java @@ -134,6 +134,7 @@ private void implementPaged(ClassCreator classCreator, ResourceMetadata resource addPathAnnotation(methodCreator, resourceProperties.getPath(RESOURCE_METHOD_NAME)); addGetAnnotation(methodCreator); addProducesAnnotation(methodCreator, APPLICATION_HAL_JSON); + addMethodAnnotations(methodCreator, resourceProperties.getMethodAnnotations(RESOURCE_METHOD_NAME)); addSecurityAnnotations(methodCreator, resourceProperties); addSortQueryParamValidatorAnnotation(methodCreator); addQueryParamAnnotation(methodCreator.getParameterAnnotations(0), "sort"); @@ -209,6 +210,7 @@ private void implementNotPaged(ClassCreator classCreator, ResourceMetadata resou addPathAnnotation(methodCreator, resourceProperties.getPath(RESOURCE_METHOD_NAME)); addGetAnnotation(methodCreator); addProducesAnnotation(methodCreator, APPLICATION_HAL_JSON); + addMethodAnnotations(methodCreator, resourceProperties.getMethodAnnotations(RESOURCE_METHOD_NAME)); addSecurityAnnotations(methodCreator, resourceProperties); addQueryParamAnnotation(methodCreator.getParameterAnnotations(0), "sort"); diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/MethodProperties.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/MethodProperties.java index e9f6b48e05928..827ef91a36ef4 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/MethodProperties.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/MethodProperties.java @@ -1,5 +1,9 @@ package io.quarkus.rest.data.panache.deployment.properties; +import java.util.Collection; + +import org.jboss.jandex.AnnotationInstance; + public class MethodProperties { private final boolean exposed; @@ -8,10 +12,14 @@ public class MethodProperties { private final String[] rolesAllowed; - public MethodProperties(boolean exposed, String path, String[] rolesAllowed) { + private final Collection methodAnnotations; + + public MethodProperties(boolean exposed, String path, String[] rolesAllowed, + Collection methodAnnotations) { this.exposed = exposed; this.path = path; this.rolesAllowed = rolesAllowed; + this.methodAnnotations = methodAnnotations; } public boolean isExposed() { @@ -25,4 +33,8 @@ public String getPath() { public String[] getRolesAllowed() { return rolesAllowed; } + + public Collection getMethodAnnotations() { + return methodAnnotations; + } } diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/ResourceProperties.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/ResourceProperties.java index 9208b8ef049c0..20002de2a496e 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/ResourceProperties.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/ResourceProperties.java @@ -1,7 +1,11 @@ package io.quarkus.rest.data.panache.deployment.properties; +import java.util.Collection; +import java.util.Collections; import java.util.Map; +import org.jboss.jandex.AnnotationInstance; + public class ResourceProperties { private final boolean exposed; @@ -16,16 +20,20 @@ public class ResourceProperties { private final String[] rolesAllowed; + private final Collection classAnnotations; + private final Map methodProperties; public ResourceProperties(boolean exposed, String path, boolean paged, boolean hal, String halCollectionName, - String[] rolesAllowed, Map methodProperties) { + String[] rolesAllowed, Collection classAnnotations, + Map methodProperties) { this.exposed = exposed; this.path = path; this.paged = paged; this.hal = hal; this.halCollectionName = halCollectionName; this.rolesAllowed = rolesAllowed; + this.classAnnotations = classAnnotations; this.methodProperties = methodProperties; } @@ -79,4 +87,16 @@ public String[] getRolesAllowed(String methodName) { return rolesAllowed; } + + public Collection getClassAnnotations() { + return classAnnotations; + } + + public Collection getMethodAnnotations(String methodName) { + if (methodProperties.containsKey(methodName)) { + return methodProperties.get(methodName).getMethodAnnotations(); + } + + return Collections.emptyList(); + } } diff --git a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/ResourcePropertiesProvider.java b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/ResourcePropertiesProvider.java index c2c3e72f025b2..feb4b4976bdb4 100644 --- a/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/ResourcePropertiesProvider.java +++ b/extensions/panache/rest-data-panache/deployment/src/main/java/io/quarkus/rest/data/panache/deployment/properties/ResourcePropertiesProvider.java @@ -1,7 +1,13 @@ package io.quarkus.rest.data.panache.deployment.properties; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; + +import javax.annotation.security.RolesAllowed; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.ClassInfo; @@ -19,6 +25,8 @@ public class ResourcePropertiesProvider { private static final DotName METHOD_PROPERTIES_ANNOTATION = DotName.createSimple( io.quarkus.rest.data.panache.MethodProperties.class.getName()); + private static final List ANNOTATIONS_TO_COPY = List.of(RolesAllowed.class.getPackageName()); + private final IndexView index; public ResourcePropertiesProvider(IndexView index) { @@ -32,9 +40,6 @@ public ResourcePropertiesProvider(IndexView index) { public ResourceProperties getForInterface(String resourceInterface) { DotName resourceInterfaceName = DotName.createSimple(resourceInterface); AnnotationInstance annotation = findResourcePropertiesAnnotation(resourceInterfaceName); - Map methodProperties = new HashMap<>(); - collectMethodProperties(resourceInterfaceName, methodProperties); - return new ResourceProperties( isExposed(annotation), getPath(annotation, resourceInterface), @@ -42,7 +47,28 @@ public ResourceProperties getForInterface(String resourceInterface) { isHal(annotation), getHalCollectionName(annotation, resourceInterface), getRolesAllowed(annotation), - methodProperties); + collectAnnotationsToCopy(resourceInterfaceName), + collectMethodProperties(resourceInterfaceName)); + } + + private Collection collectAnnotationsToCopy(DotName className) { + Set annotations = new HashSet<>(); + ClassInfo classInfo = index.getClassByName(className); + if (classInfo == null) { + return annotations; + } + + for (AnnotationInstance annotation : classInfo.classAnnotations()) { + if (ANNOTATIONS_TO_COPY.stream().anyMatch(annotation.name().toString()::startsWith)) { + annotations.add(annotation); + } + } + + if (classInfo.superName() != null) { + annotations.addAll(collectAnnotationsToCopy(classInfo.superName())); + } + + return annotations; } private AnnotationInstance findResourcePropertiesAnnotation(DotName className) { @@ -59,23 +85,36 @@ private AnnotationInstance findResourcePropertiesAnnotation(DotName className) { return null; } - private void collectMethodProperties(DotName className, Map properties) { + private Map collectMethodProperties(DotName className) { + Map methodProperties = new HashMap<>(); ClassInfo classInfo = index.getClassByName(className); if (classInfo == null) { - return; + return methodProperties; } + for (MethodInfo method : classInfo.methods()) { - if (!properties.containsKey(method.name()) && method.hasAnnotation(METHOD_PROPERTIES_ANNOTATION)) { - properties.put(method.name(), getMethodProperties(method.annotation(METHOD_PROPERTIES_ANNOTATION))); + AnnotationInstance annotation = method.annotation(METHOD_PROPERTIES_ANNOTATION); + Set annotationsToCopy = new HashSet<>(); + for (AnnotationInstance ann : method.annotations()) { + if (ANNOTATIONS_TO_COPY.stream().anyMatch(ann.name().toString()::startsWith)) { + annotationsToCopy.add(ann); + } + } + + if (!methodProperties.containsKey(method.name()) + && (annotation != null || !annotationsToCopy.isEmpty())) { + methodProperties.put(method.name(), getMethodProperties(annotation, annotationsToCopy)); } } if (classInfo.superName() != null) { - collectMethodProperties(classInfo.superName(), properties); + methodProperties.putAll(collectMethodProperties(classInfo.superName())); } + + return methodProperties; } - private MethodProperties getMethodProperties(AnnotationInstance annotation) { - return new MethodProperties(isExposed(annotation), getPath(annotation), getRolesAllowed(annotation)); + private MethodProperties getMethodProperties(AnnotationInstance annotation, Set annotationsToCopy) { + return new MethodProperties(isExposed(annotation), getPath(annotation), getRolesAllowed(annotation), annotationsToCopy); } private boolean isHal(AnnotationInstance annotation) { diff --git a/extensions/spring-data-rest/deployment/src/main/java/io/quarkus/spring/data/rest/deployment/ResourcePropertiesProvider.java b/extensions/spring-data-rest/deployment/src/main/java/io/quarkus/spring/data/rest/deployment/ResourcePropertiesProvider.java index 4613e4e22150e..85ce58d1a4d2f 100644 --- a/extensions/spring-data-rest/deployment/src/main/java/io/quarkus/spring/data/rest/deployment/ResourcePropertiesProvider.java +++ b/extensions/spring-data-rest/deployment/src/main/java/io/quarkus/spring/data/rest/deployment/ResourcePropertiesProvider.java @@ -1,9 +1,15 @@ package io.quarkus.spring.data.rest.deployment; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Predicate; +import javax.annotation.security.RolesAllowed; + import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; @@ -23,6 +29,8 @@ public abstract class ResourcePropertiesProvider { private static final DotName REPOSITORY_REST_RESOURCE_ANNOTATION = DotName .createSimple(RepositoryRestResource.class.getName()); + private static final List ANNOTATIONS_TO_COPY = List.of(RolesAllowed.class.getPackageName()); + private final IndexView index; private final boolean paged; @@ -41,22 +49,50 @@ public ResourceProperties getResourceProperties(String interfaceName) { String halCollectionName = getHalCollectionName(annotation, ResourceName.fromClass(interfaceName)); return new ResourceProperties(isExposed(annotation), resourcePath, paged, true, halCollectionName, - new String[0], getMethodProperties(repositoryInterfaceName)); + new String[0], collectAnnotationsToCopy(repositoryInterfaceName), getMethodProperties(repositoryInterfaceName)); } private Map getMethodProperties(DotName interfaceName) { Map methodPropertiesMap = new HashMap<>(); for (Map.Entry> method : getMethodPredicates().entrySet()) { - AnnotationInstance annotation = findMethodAnnotation(interfaceName, method.getValue()); - if (annotation != null) { - methodPropertiesMap.putIfAbsent(method.getKey(), getMethodProperties(annotation)); + MethodWithAnnotation methodWithAnnotation = findMethodAnnotation(interfaceName, method.getValue()); + if (methodWithAnnotation != null) { + Set annotationsToCopy = new HashSet<>(); + for (AnnotationInstance ann : methodWithAnnotation.method.annotations()) { + if (ANNOTATIONS_TO_COPY.stream().anyMatch(ann.name().toString()::startsWith)) { + annotationsToCopy.add(ann); + } + } + + methodPropertiesMap.putIfAbsent(method.getKey(), + getMethodProperties(methodWithAnnotation.annotation, annotationsToCopy)); } } return methodPropertiesMap; } - private MethodProperties getMethodProperties(AnnotationInstance annotation) { - return new MethodProperties(isExposed(annotation), getPath(annotation, ""), new String[0]); + private MethodProperties getMethodProperties(AnnotationInstance annotation, Set annotationsToCopy) { + return new MethodProperties(isExposed(annotation), getPath(annotation, ""), new String[0], annotationsToCopy); + } + + private Collection collectAnnotationsToCopy(DotName className) { + Set annotations = new HashSet<>(); + ClassInfo classInfo = index.getClassByName(className); + if (classInfo == null) { + return annotations; + } + + for (AnnotationInstance annotation : classInfo.classAnnotations()) { + if (ANNOTATIONS_TO_COPY.stream().anyMatch(annotation.name().toString()::startsWith)) { + annotations.add(annotation); + } + } + + if (classInfo.superName() != null) { + annotations.addAll(collectAnnotationsToCopy(classInfo.superName())); + } + + return annotations; } private AnnotationInstance findClassAnnotation(DotName interfaceName) { @@ -76,7 +112,7 @@ private AnnotationInstance findClassAnnotation(DotName interfaceName) { return null; } - private AnnotationInstance findMethodAnnotation(DotName interfaceName, Predicate methodPredicate) { + private MethodWithAnnotation findMethodAnnotation(DotName interfaceName, Predicate methodPredicate) { ClassInfo classInfo = index.getClassByName(interfaceName); if (classInfo == null) { return null; @@ -84,9 +120,15 @@ private AnnotationInstance findMethodAnnotation(DotName interfaceName, Predicate for (MethodInfo method : classInfo.methods()) { if (methodPredicate.test(method)) { if (method.hasAnnotation(REPOSITORY_REST_RESOURCE_ANNOTATION)) { - return method.annotation(REPOSITORY_REST_RESOURCE_ANNOTATION); + MethodWithAnnotation found = new MethodWithAnnotation(); + found.method = method; + found.annotation = method.annotation(REPOSITORY_REST_RESOURCE_ANNOTATION); + return found; } else if (method.hasAnnotation(REST_RESOURCE_ANNOTATION)) { - return method.annotation(REST_RESOURCE_ANNOTATION); + MethodWithAnnotation found = new MethodWithAnnotation(); + found.method = method; + found.annotation = method.annotation(REST_RESOURCE_ANNOTATION); + return found; } } } @@ -115,4 +157,9 @@ private String getHalCollectionName(AnnotationInstance annotation, String defaul } return defaultValue; } + + class MethodWithAnnotation { + MethodInfo method; + AnnotationInstance annotation; + } }