From e96f1dd02d2b8bf0e0664ef1fd702d05c297d172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Fri, 20 Sep 2024 11:48:01 +0200 Subject: [PATCH] Match Permission constructor parameters based on parameter names --- .../quarkus/security/PermissionsAllowed.java | 93 ++++++++++++++----- 1 file changed, 72 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/quarkus/security/PermissionsAllowed.java b/src/main/java/io/quarkus/security/PermissionsAllowed.java index 578f2bc..20ce78f 100644 --- a/src/main/java/io/quarkus/security/PermissionsAllowed.java +++ b/src/main/java/io/quarkus/security/PermissionsAllowed.java @@ -32,38 +32,50 @@ /** * Constant value for {@link #params()} indicating that the constructor parameters of the {@link #permission()} - * should be autodetected. That is, each constructor argument data type must exactly match a data type of at least - * one argument of the secured method. - * - * For example consider following permission: - * + * should be autodetected based on formal parameter names. For example, consider following method secured with this + * annotation: + *
+     * {@code
+     * @PermissionsAllowed(value = "resource:retrieve", permission = UserPermission.class)
+     * public Resource getResource(String param1, String param2, String param3) {
+     *      // business logic
+     * }
+     * }
+     * 
+ * The {@code getResource} method parameters {@code param1} and {@code param3} will be + * matched with the {@code UserPermission} constructor parameters {@code param1} and {@code param3}. *
+     * {@code
      * public class UserPermission extends Permission {
      *
-     *     private final User user;
-     *
-     *     public UserPermission(String name, User user) {
-     *         super(name);
-     *         this.user = user;
+     *     public UserPermission(String name, String param3, String param1) {
+     *         ...
      *     }
      *
      *     ...
      * }
-     * 
+ * } + * If no method parameter name matches the constructor parameter name, Quarkus checks names of fields and methods + * declared on the method parameter type. + * For example: + *
+     * {@code
+     * record BeanParam2(String param1, String param2) {}
+     * record BeanParam3(String param3) {}
+     * record BeanParam1(BeanParam2 beanParam2, BeanParam3 beanParam3) {
      *
-     * Constructor parameter {@code user} is in fact object passed to a secured method.
-     * In the example below, {@code user1} parameter of the 'getResource' method is passed to the constructor.
+     * }
      *
-     * 
-     * @PermissionsAllowed(permission = UserPermission.class, value = "resource")
-     * public Resource getResource(User user1) {
-     *     // business logic
+     * @PermissionsAllowed(value = "resource:retrieve", permission = UserPermission.class)
+     * public Resource getResource(BeanParam1 beanParam) {
+     *      // business logic
      * }
-     * 
* - * Constructor parameters are always selected as the first secured method parameter with exactly matching data type. - * There is no limit to a reasonable number of parameters passed to the permission constructor this way. - * Please see {@link #params()} for more complex matching. + * } + * } + *
+ * In this example, resolution of the {@code param1} and {@code param3} formal parameters is unambiguous. + * For more complex scenarios, we suggest to specify {@link #params()} explicitly. */ String AUTODETECTED = "<>"; @@ -204,6 +216,45 @@ * WARNING: "params" attribute is only supported in the scenarios explicitly named in the Quarkus documentation. *

* + * Method parameter fields or methods can be passed to a Permission constructor as well. + * Consider the following secured method and its parameters: + *
+     * {@code
+     * @PermissionsAllowed(permission = UserPermission.class, value = "resource", params = {"admin1.param1", "user1.param3"})
+     * public Resource getResource(User user, User user1, Admin admin, Admin admin1) {
+     *     // business logic
+     * }
+     * class ResourceIdentity {
+     *     private final String param1;
+     *
+     *     public String getParam1() {
+     *         return param1;
+     *     }
+     * }
+     * class User extends ResourceIdentity {
+     *     public String getParam3() {
+     *         return "param3";
+     *     }
+     * }
+     * class Admin extends ResourceIdentity { }
+     * }
+     * 
+ * + * The corresponding {@code UserPermission} constructor would look like this: + * + *
+     * public class UserPermission extends Permission {
+     *
+     *     public UserPermission(String name, String param1, String param3) {
+     *     }
+     *
+     *     ...
+     * }
+     * 
+ * + * Here, the constructor parameter {@code param1} refers to the {@code admin1#param1} secured method parameter + * and the constructor parameter {@code param3} to the {@code user1#getParam3} secured method parameter. + * * @see #AUTODETECTED * * @return constructor parameters passed to the {@link #permission()}