Skip to content

Commit

Permalink
Transform security annotations to be inherited; add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
manovotn committed Sep 18, 2024
1 parent 8e6e43d commit 2f9954b
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.quarkus.security.deployment;

import java.lang.annotation.Inherited;

import jakarta.annotation.security.DenyAll;
import jakarta.annotation.security.PermitAll;
import jakarta.annotation.security.RolesAllowed;
Expand All @@ -17,6 +19,9 @@ public final class DotNames {
public static final DotName DENY_ALL = DotName.createSimple(DenyAll.class.getName());
public static final DotName PERMIT_ALL = DotName.createSimple(PermitAll.class.getName());

// used to make the above annotations appear as @Inherited to Arc
public static final DotName INHERITED = DotName.createSimple(Inherited.class.getName());

private DotNames() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static io.quarkus.gizmo.MethodDescriptor.ofMethod;
import static io.quarkus.security.deployment.DotNames.AUTHENTICATED;
import static io.quarkus.security.deployment.DotNames.DENY_ALL;
import static io.quarkus.security.deployment.DotNames.INHERITED;
import static io.quarkus.security.deployment.DotNames.PERMISSIONS_ALLOWED;
import static io.quarkus.security.deployment.DotNames.PERMIT_ALL;
import static io.quarkus.security.deployment.DotNames.ROLES_ALLOWED;
Expand Down Expand Up @@ -559,6 +560,17 @@ void transformSecurityAnnotations(BuildProducer<AnnotationsTransformerBuildItem>
}
}

/*
* Transform all security annotations to be {@code @Inherited}
*/
@BuildStep
void makeSecurityAnnotationsInherited(BuildProducer<AnnotationsTransformerBuildItem> transformer) {
Set<DotName> securityAnnotationNames = Set.of(PERMIT_ALL, DENY_ALL, AUTHENTICATED, PERMISSIONS_ALLOWED, ROLES_ALLOWED);
transformer.produce(new AnnotationsTransformerBuildItem(AnnotationTransformation.forClasses()
.whenClass(c -> securityAnnotationNames.contains(c.name()))
.transform(c -> c.add(AnnotationInstance.builder(INHERITED).build()))));
}

@BuildStep
PermissionsAllowedMetaAnnotationBuildItem transformPermissionsAllowedMetaAnnotations(
BeanArchiveIndexBuildItem beanArchiveBuildItem,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package io.quarkus.security.test.cdi;

import static io.quarkus.security.test.utils.IdentityMock.ADMIN;
import static io.quarkus.security.test.utils.IdentityMock.ANONYMOUS;
import static io.quarkus.security.test.utils.IdentityMock.USER;
import static io.quarkus.security.test.utils.SecurityTestUtils.assertFailureFor;
import static io.quarkus.security.test.utils.SecurityTestUtils.assertSuccess;

import java.util.Collections;
import java.util.Set;

import jakarta.inject.Inject;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.security.ForbiddenException;
import io.quarkus.security.StringPermission;
import io.quarkus.security.UnauthorizedException;
import io.quarkus.security.test.cdi.app.TestException;
import io.quarkus.security.test.cdi.inheritance.AuthenticatedBean;
import io.quarkus.security.test.cdi.inheritance.DenyAllBean;
import io.quarkus.security.test.cdi.inheritance.PermissionsAllowedBean;
import io.quarkus.security.test.cdi.inheritance.RolesAllowedBean;
import io.quarkus.security.test.cdi.inheritance.SubclassAuthenticatedBean;
import io.quarkus.security.test.cdi.inheritance.SubclassDenyAllBean;
import io.quarkus.security.test.cdi.inheritance.SubclassPermissionsAllowedBean;
import io.quarkus.security.test.cdi.inheritance.SubclassRolesAllowedBean;
import io.quarkus.security.test.utils.AuthData;
import io.quarkus.security.test.utils.IdentityMock;
import io.quarkus.security.test.utils.SecurityTestUtils;
import io.quarkus.test.QuarkusUnitTest;

public class CdiClassLevelInheritanceTest {

@Inject
SubclassDenyAllBean subclassDenyAll;

@Inject
SubclassRolesAllowedBean subclassRolesAllowed;

@Inject
SubclassAuthenticatedBean subclassAuthenticated;

@Inject
SubclassPermissionsAllowedBean subclassPermissionsAllowed;

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(IdentityMock.class,
AuthData.class,
DenyAllBean.class,
RolesAllowedBean.class,
AuthenticatedBean.class,
PermissionsAllowedBean.class,
SubclassPermissionsAllowedBean.class,
SubclassDenyAllBean.class,
SubclassRolesAllowedBean.class,
SubclassAuthenticatedBean.class,
TestException.class,
SecurityTestUtils.class))
// Following configuration tells Arc to *not* automatically inherit all class level annotations
.overrideConfigKey("quarkus.arc.strict-compatibility", "true");

@Test
public void testDenyAllInherited() {
assertFailureFor(() -> subclassDenyAll.ping(), UnauthorizedException.class, ANONYMOUS);
assertFailureFor(() -> subclassDenyAll.ping(), ForbiddenException.class, USER, ADMIN);
}

@Test
public void testRolesAllowedInherited() {
assertFailureFor(() -> subclassRolesAllowed.ping(), UnauthorizedException.class, ANONYMOUS);
assertFailureFor(() -> subclassRolesAllowed.ping(), ForbiddenException.class, USER);
assertSuccess(() -> subclassRolesAllowed.ping(), RolesAllowedBean.class.getSimpleName(), ADMIN);
}

@Test
public void testAuthenticatedInherited() {
assertFailureFor(() -> subclassAuthenticated.ping(), UnauthorizedException.class, ANONYMOUS);
assertSuccess(() -> subclassAuthenticated.ping(), AuthenticatedBean.class.getSimpleName(), USER);
assertSuccess(() -> subclassAuthenticated.ping(), AuthenticatedBean.class.getSimpleName(), ADMIN);
}

@Test
public void testPermissionAllowedInherited() {
AuthData USER_READ = new AuthData(Collections.singleton("user_read"), false, "user_read",
Set.of(new StringPermission("read")));
AuthData USER_WRITE = new AuthData(Collections.singleton("user_write"), false, "user_write",
Set.of(new StringPermission("write")));
assertSuccess(() -> subclassPermissionsAllowed.ping(), PermissionsAllowedBean.class.getSimpleName(), USER_READ);
assertFailureFor(() -> subclassPermissionsAllowed.ping(), ForbiddenException.class, USER_WRITE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.quarkus.security.test.cdi.inheritance;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkus.security.Authenticated;

@ApplicationScoped
@Authenticated
public class AuthenticatedBean {

public String ping() {
return AuthenticatedBean.class.getSimpleName();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.quarkus.security.test.cdi.inheritance;

import jakarta.annotation.security.DenyAll;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
@DenyAll
public class DenyAllBean {

public String ping() {
return DenyAllBean.class.getSimpleName();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.quarkus.security.test.cdi.inheritance;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkus.security.PermissionsAllowed;

@ApplicationScoped
@PermissionsAllowed("read")
public class PermissionsAllowedBean {

public String ping() {
return PermissionsAllowedBean.class.getSimpleName();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.quarkus.security.test.cdi.inheritance;

import jakarta.annotation.security.RolesAllowed;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
@RolesAllowed("admin")
public class RolesAllowedBean {

public String ping() {
return RolesAllowedBean.class.getSimpleName();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.quarkus.security.test.cdi.inheritance;

import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class SubclassAuthenticatedBean extends AuthenticatedBean {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.quarkus.security.test.cdi.inheritance;

import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class SubclassDenyAllBean extends DenyAllBean {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.quarkus.security.test.cdi.inheritance;

import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class SubclassPermissionsAllowedBean extends PermissionsAllowedBean {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.quarkus.security.test.cdi.inheritance;

import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class SubclassRolesAllowedBean extends RolesAllowedBean {
}

0 comments on commit 2f9954b

Please sign in to comment.