Skip to content

Commit

Permalink
Build SecurityEventHelper lazily due to static interceptors
Browse files Browse the repository at this point in the history
  • Loading branch information
michalvavrik authored and holly-cummins committed Jul 31, 2024
1 parent 28fb0a8 commit e3f514e
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import jakarta.inject.Inject;
import jakarta.inject.Singleton;

import org.eclipse.microprofile.config.ConfigProvider;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
Expand Down Expand Up @@ -47,7 +48,7 @@ public class RolesAllowedExpressionTest {
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(RolesAllowedBean.class, IdentityMock.class,
AuthData.class, SecurityTestUtils.class)
AuthData.class, SecurityTestUtils.class, SecuredUtils.class)
.addAsResource(new StringAsset(APP_PROPS), "application.properties"));

@Inject
Expand Down Expand Up @@ -97,6 +98,12 @@ public void shouldRestrictAccessToSpecificRole() {
new AuthData(Set.of("cn=Administrator,ou=Software,dc=Tester,dc=User"), false, "ldap"));
}

@Test
public void testStaticSecuredMethod() {
assertSuccess(SecuredUtils::staticSecuredMethod, "admin", ADMIN);
assertFailureFor(SecuredUtils::staticSecuredMethod, ForbiddenException.class, USER);
}

@Singleton
public static class RolesAllowedBean {

Expand Down Expand Up @@ -153,4 +160,17 @@ public final String ldap() {

}

public static class SecuredUtils {

private SecuredUtils() {
// UTIL CLASS
}

@RolesAllowed("${sudo}")
public static String staticSecuredMethod() {
return ConfigProvider.getConfig().getValue("sudo", String.class);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import jakarta.enterprise.event.Event;
import jakarta.enterprise.inject.spi.BeanManager;

import org.eclipse.microprofile.config.ConfigProvider;

import io.quarkus.security.identity.SecurityIdentity;

public class SecurityEventHelper<S extends SecurityEvent, F extends SecurityEvent> {
Expand Down Expand Up @@ -90,4 +92,61 @@ public Map<String, Object> getEventProperties() {
return false;
}
}

/**
* Creates {@link SecurityEventHelper} initialized on first request.
* This method should only be used when there is a risk the helper will be initialized during the static init phase.
* During the runtime init phase, prefer the constructor.
*/
public static <S extends SecurityEvent, F extends SecurityEvent> SecurityEventHelper<S, F> lazilyOf(Event<S> successEvent,
Event<F> failureEvent, S successInstance, F failureInstance, BeanManager beanManager) {
return new SecurityEventHelper<>(successEvent, failureEvent, successInstance, failureInstance, beanManager, true) {

private volatile Boolean eventsDisabled = null;

private boolean areEventsDisabled() {
if (eventsDisabled == null) {
synchronized (this) {
if (eventsDisabled == null) {
this.eventsDisabled = !ConfigProvider.getConfig().getValue("quarkus.security.events.enabled",
Boolean.class);
}
}
}
return eventsDisabled;
}

@Override
public void fireSuccessEvent(S successInstance) {
if (areEventsDisabled()) {
return;
}
super.fireSuccessEvent(successInstance);
}

@Override
public void fireFailureEvent(F failureInstance) {
if (areEventsDisabled()) {
return;
}
super.fireFailureEvent(failureInstance);
}

@Override
public boolean fireEventOnSuccess() {
if (areEventsDisabled()) {
return false;
}
return super.fireEventOnSuccess();
}

@Override
public boolean fireEventOnFailure() {
if (areEventsDisabled()) {
return false;
}
return super.fireEventOnFailure();
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

import io.quarkus.runtime.BlockingOperationNotAllowedException;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.security.runtime.SecurityConfig;
import io.quarkus.security.runtime.SecurityIdentityAssociation;
import io.quarkus.security.spi.runtime.AuthorizationFailureEvent;
import io.quarkus.security.spi.runtime.AuthorizationSuccessEvent;
Expand All @@ -36,11 +35,12 @@ public class SecurityConstrainer {
@Inject
SecurityIdentityAssociation identityAssociation;

SecurityConstrainer(SecurityCheckStorage storage, BeanManager beanManager, SecurityConfig securityConfig,
SecurityConstrainer(SecurityCheckStorage storage, BeanManager beanManager,
Event<AuthorizationFailureEvent> authZFailureEvent, Event<AuthorizationSuccessEvent> authZSuccessEvent) {
this.storage = storage;
this.securityEventHelper = new SecurityEventHelper<>(authZSuccessEvent, authZFailureEvent, AUTHORIZATION_SUCCESS,
AUTHORIZATION_FAILURE, beanManager, securityConfig.events().enabled());
// static interceptors are initialized during the static init, therefore we need to initialize the helper lazily
this.securityEventHelper = SecurityEventHelper.lazilyOf(authZSuccessEvent, authZFailureEvent,
AUTHORIZATION_SUCCESS, AUTHORIZATION_FAILURE, beanManager);
}

public void check(Method method, Object[] parameters) {
Expand Down

0 comments on commit e3f514e

Please sign in to comment.