Skip to content

Commit

Permalink
Improve PermissionsAllowed user experience with shortcuts
Browse files Browse the repository at this point in the history
  • Loading branch information
michalvavrik committed Oct 1, 2024
1 parent 8e9154b commit f7912f0
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 1 deletion.
66 changes: 66 additions & 0 deletions src/main/java/io/quarkus/security/PermissionChecker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package io.quarkus.security;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Used to annotate CDI bean methods that check if a user holds a permission specified by the {@link #value()}.
* Quarkus Security will augment every {@link io.quarkus.security.identity.SecurityIdentity} with this permission checker.
* Such a permission checker grants access to methods secured with the {@link PermissionsAllowed} security annotation.
* Following example shows how it works:
* <pre>
* {@code
* &#64;Path("hello")
* public class HelloResource {
*
* &#64;PermissionsAllowed("speak")
* &#64;GET
* public String sayHello() {
* return "Hello World!";
* }
*
* &#64;PermissionChecker("speak")
* public boolean canSpeak(SecurityIdentity identity) {
* return "speaker".equals(identity.getPrincipal().getName());
* }
* }
* }
* </pre>
* Parameters of permission checker methods can include any of secured method parameters.
* Consider following secured method:
* <pre>
* {@code
* &#64;PermissionsAllowed("update")
* public String updateString(String a, String b, String c, String d) {
* ...
* }
* }
* </pre>
* Permission checker that grants access to the {@code updateString} method can inject
* any arguments it requires and optionally even {@link io.quarkus.security.identity.SecurityIdentity}:
* <pre>
* {@code
* &#64;PermissionChecker("update")
* public boolean canUpdate(String c, String a, SecurityIdentity identity) {
* ...
* }
* }
* </pre>
* Permission checker method parameters are matched with the secured method parameters in exactly same fashion
* as are constructor parameters of a custom permission. Please see {@link PermissionsAllowed#params()} for more information.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionChecker {

/**
* Specifies a permission this checker grants.
*
* @see PermissionsAllowed#value()
* @return name of the permission this checker grants
*/
String value();

}
7 changes: 6 additions & 1 deletion src/main/java/io/quarkus/security/PermissionsAllowed.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,15 @@
* </pre>
*
* To see how the example above is evaluated, please see "implies" method of your {@link #permission()}.
* <p>
* This attribute can be empty when {@link #permission()} is set to the {@link QuarkusPermission} as that permission
* does not require any name.
*
* @see StringPermission#implies(Permission) for more details on how above-mentioned example is evaluated
*
* @return permissions linked to respective actions
*/
String[] value();
String[] value() default {};

/**
* Choose a relation between permissions specified via {@link #value()}. By default, at least one of permissions
Expand Down Expand Up @@ -280,6 +283,8 @@
* }
* </pre>
*
*
* {@code name} may only be omitted when {@link #permission()} is set to the {@link QuarkusPermission}
* {@code actions} parameter is optional and may be omitted.
*
* @return permission class
Expand Down
72 changes: 72 additions & 0 deletions src/main/java/io/quarkus/security/QuarkusPermission.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package io.quarkus.security;

import io.quarkus.security.identity.SecurityIdentity;

import java.security.Permission;
import java.util.Objects;

/**
* Special type of the {@link PermissionsAllowed#permission()} that does not require {@link SecurityIdentity}
* augmentation. When this permission is set to the {@link PermissionsAllowed#permission()}, Quarkus Security augments
* all the {@link SecurityIdentity} with a permission checker for you.
* This way, access to methods secured with the {@link PermissionsAllowed} annotation will only be granted
* when {@link #isGranted(SecurityIdentity)} returns true.
*/
public abstract class QuarkusPermission extends Permission {

private static final String QUARKUS_PERMISSION = "io.quarkus.security.permission#name";

/**
* Permission name is set to the {@link #QUARKUS_PERMISSION}.
* Subclasses can declare constructors that accept permission name and/or arguments of a secured method.
*
* @see PermissionsAllowed#params() for more information about additional Permission arguments
*/
protected QuarkusPermission() {
this(QUARKUS_PERMISSION);
}

/**
* Subclasses can declare constructors that accept permission name and/or arguments of a secured method.
*
* @see PermissionsAllowed#params() for more information about additional Permission arguments
*/
protected QuarkusPermission(String permissionName) {
super(permissionName);
}

/**
* Determines whether access to secured resource should be granted.
*
* @param securityIdentity {@link SecurityIdentity}
* @return true if access should be granted and false otherwise
*/
public abstract boolean isGranted(SecurityIdentity securityIdentity);

/**
* @throws IllegalStateException for this permission can only be set to the {@link PermissionsAllowed#permission()}
*/
@Override
public final boolean implies(Permission requiredPermission) {
// possessed permission implies required permission
// this is required permission, not the possessed one
throw new IllegalStateException("QuarkusPermission should never be assigned to a SecurityIdentity. "
+ "This permission can only be set to the @PermissionsAllowed#permission attribute.");
}

@Override
public String getActions() {
return "";
}

@Override
public boolean equals(Object object) {
return this == object;
}

@Override
public int hashCode() {
return Objects.hash(toString());
}

}

0 comments on commit f7912f0

Please sign in to comment.