Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ThreadLocal based Inspektr PrincipalResolver #1957

Merged
merged 3 commits into from Aug 18, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions cas-server-core-audit/build.gradle
Expand Up @@ -8,6 +8,8 @@ dependencies {
compile libraries.hibernate
compile project(":cas-server-core-web")
compile project(":cas-server-core-util")
compile project(":cas-server-core-api-events")
compile project(":cas-server-core-authentication")
testCompile project(":cas-server-core-logout")
testCompile project(":cas-server-core-monitor")
testCompile project(":cas-server-core-util")
Expand Down

This file was deleted.

Expand Up @@ -4,7 +4,7 @@

/**
* Strategy interface to provide principal id tokens from any given authentication event.
*
* <p>
* Useful for authentication scenarios where there is not only one primary principal id available, but additional authentication metadata
* in addition to custom requirement to compute and show more complex principal identifier for auditing purposes.
* An example would be compound ids resulted from multi-legged mfa authentications, 'surrogate' authentications, etc.
Expand All @@ -18,8 +18,9 @@ public interface PrincipalIdProvider {
* Return principal id from a given authentication event.
*
* @param authentication authentication event containing the data to computed the final principal id from
*
* @return computed principal id
*/
String getPrincipalIdFrom(Authentication authentication);
default String getPrincipalIdFrom(Authentication authentication) {
return authentication != null ? authentication.getPrincipal().getId() : null;
}
}
@@ -0,0 +1,50 @@
package org.apereo.cas.audit.spi;

import org.apereo.cas.authentication.CurrentCredentialsAndAuthentication;
import org.apereo.inspektr.common.spi.PrincipalResolver;
import org.aspectj.lang.JoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
* Inspektr PrincipalResolver that gets the value for principal id from Authentication object bound to a current thread of execution.
*
* @author Dmitriy Kopylenko
* @since 5.0.0
*/
public class ThreadLocalPrincipalResolver implements PrincipalResolver {

private static final Logger LOGGER = LoggerFactory.getLogger(ThreadLocalPrincipalResolver.class);

private PrincipalIdProvider principalIdProvider;

public ThreadLocalPrincipalResolver(final PrincipalIdProvider principalIdProvider) {
this.principalIdProvider = principalIdProvider;
}

@Override
public String resolveFrom(final JoinPoint auditTarget, final Object returnValue) {
LOGGER.debug("Resolving principal at audit point [{}]", auditTarget);
return getCurrentPrincipal();
}

@Override
public String resolveFrom(final JoinPoint auditTarget, final Exception exception) {
LOGGER.debug("Resolving principal at audit point [{}] with thrown exception [{}]", auditTarget, exception);
return getCurrentPrincipal();
}

@Override
public String resolve() {
return UNKNOWN_USER;
}

private String getCurrentPrincipal() {
String principal = this.principalIdProvider.getPrincipalIdFrom(CurrentCredentialsAndAuthentication.getCurrentAuthentication());
if (principal == null) {
principal = CurrentCredentialsAndAuthentication.getCurrentCredentialIdsAsString();
}
return (principal != null) ? principal : UNKNOWN_USER;
}
}
Expand Up @@ -24,7 +24,7 @@
*
* @author Scott Battaglia
* @since 3.1.2
*
* @deprecated
*/
public class TicketOrCredentialPrincipalResolver implements PrincipalResolver {

Expand All @@ -33,7 +33,7 @@ public class TicketOrCredentialPrincipalResolver implements PrincipalResolver {

private CentralAuthenticationService centralAuthenticationService;

private PrincipalIdProvider principalIdProvider = authentication -> authentication.getPrincipal().getId();
private PrincipalIdProvider principalIdProvider = new PrincipalIdProvider() {};

/**
* Instantiates a new Ticket or credential principal resolver.
Expand Down
@@ -1,14 +1,12 @@
package org.apereo.cas.audit.spi.config;

import com.google.common.collect.ImmutableList;
import org.apereo.cas.CentralAuthenticationService;
import org.apereo.cas.audit.spi.AssertionAsReturnValuePrincipalResolver;
import org.apereo.cas.audit.spi.CredentialsAsFirstParameterResourceResolver;
import org.apereo.cas.audit.spi.MessageBundleAwareResourceResolver;
import org.apereo.cas.audit.spi.PrincipalIdProvider;
import org.apereo.cas.audit.spi.ServiceResourceResolver;
import org.apereo.cas.audit.spi.ThreadLocalPrincipalResolver;
import org.apereo.cas.audit.spi.TicketAsFirstParameterResourceResolver;
import org.apereo.cas.audit.spi.TicketOrCredentialPrincipalResolver;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.inspektr.audit.AuditTrailManagementAspect;
import org.apereo.inspektr.audit.AuditTrailManager;
Expand Down Expand Up @@ -50,13 +48,12 @@ public class CasCoreAuditConfiguration {

@Bean
public AuditTrailManagementAspect auditTrailManagementAspect(
@Qualifier("centralAuthenticationService")
final CentralAuthenticationService centralAuthenticationService,
@Qualifier("auditTrailManager")
final AuditTrailManager auditTrailManager) {

final AuditTrailManagementAspect aspect = new AuditTrailManagementAspect(
casProperties.getAudit().getAppCode(),
auditablePrincipalResolver(centralAuthenticationService),
auditablePrincipalResolver(principalIdProvider()),
ImmutableList.of(auditTrailManager), auditActionResolverMap(),
auditResourceResolverMap());
aspect.setFailOnAuditFailures(!casProperties.getAudit().isIgnoreAuditFailures());
Expand Down Expand Up @@ -133,14 +130,8 @@ public Map auditResourceResolverMap() {
}

@Bean
public PrincipalResolver auditablePrincipalResolver(
@Qualifier("centralAuthenticationService")
final CentralAuthenticationService centralAuthenticationService
) {
final TicketOrCredentialPrincipalResolver r =
new TicketOrCredentialPrincipalResolver(centralAuthenticationService);
r.setPrincipalIdProvider(principalIdProvider());
return new AssertionAsReturnValuePrincipalResolver(r);
public PrincipalResolver auditablePrincipalResolver(@Qualifier("principalIdProvider") final PrincipalIdProvider principalIdProvider) {
return new ThreadLocalPrincipalResolver(principalIdProvider);
}

@Bean
Expand All @@ -153,8 +144,10 @@ public MessageBundleAwareResourceResolver messageBundleAwareResourceResolver() {
return new MessageBundleAwareResourceResolver();
}

@ConditionalOnMissingBean(name = "principalIdProvider")
@Bean
public PrincipalIdProvider principalIdProvider() {
return authentication -> authentication.getPrincipal().getId();
return new PrincipalIdProvider() {
};
}
}

This file was deleted.

@@ -0,0 +1,58 @@
package org.apereo.cas.audit.spi;

import org.apereo.cas.authentication.CurrentCredentialsAndAuthentication;
import org.apereo.inspektr.common.spi.PrincipalResolver;
import org.junit.After;
import org.junit.Test;

import static org.apereo.cas.authentication.TestUtils.CONST_USERNAME;
import static org.apereo.cas.authentication.TestUtils.getAuthentication;
import static org.apereo.cas.authentication.TestUtils.getCredentialsWithSameUsernameAndPassword;
import static org.junit.Assert.*;

/**
* Unit test for {@link ThreadLocalPrincipalResolver}.
*
* @author Dmitriy Kopylenko
* @since 5.0.0
*/
public class ThreadLocalPrincipalResolverTests {

private ThreadLocalPrincipalResolver theResolver =
new ThreadLocalPrincipalResolver(new PrincipalIdProvider() {});

@After
public void cleanup() {
CurrentCredentialsAndAuthentication.clear();
}

@Test
public void noAuthenticationOrCrendentialsAvailableInThreadLocal() {
assertResolvedPrincipal(PrincipalResolver.UNKNOWN_USER);
}

@Test
public void singleThreadSetsSingleCredential() {
CurrentCredentialsAndAuthentication.bindCurrent(getCredentialsWithSameUsernameAndPassword());
assertResolvedPrincipal(CONST_USERNAME);
}

@Test
public void singleThreadSetsMultipleCredentials() {
CurrentCredentialsAndAuthentication.bindCurrent(
getCredentialsWithSameUsernameAndPassword(),
getCredentialsWithSameUsernameAndPassword("test2"));

assertResolvedPrincipal(String.format("%s, %s", CONST_USERNAME, "test2"));
}

@Test
public void singleThreadSetsAuthentication() {
CurrentCredentialsAndAuthentication.bindCurrent(getAuthentication());
assertResolvedPrincipal(CONST_USERNAME);
}

private void assertResolvedPrincipal(final String principalId) {
assertEquals(principalId, theResolver.resolveFrom(null, (Object) null));
}
}
Expand Up @@ -169,6 +169,7 @@ protected Principal resolvePrincipal(
@Metered(name = "AUTHENTICATE_METER")
@Counted(name = "AUTHENTICATE_COUNT", monotonic = true)
public Authentication authenticate(final AuthenticationTransaction transaction) throws AuthenticationException {
CurrentCredentialsAndAuthentication.bindCurrent(transaction.getCredentials());
final AuthenticationBuilder builder = authenticateInternal(transaction);
final Authentication authentication = builder.build();
final Principal principal = authentication.getPrincipal();
Expand All @@ -183,7 +184,9 @@ public Authentication authenticate(final AuthenticationTransaction transaction)

populateAuthenticationMetadataAttributes(builder, transaction.getCredentials());

return builder.build();
final Authentication a = builder.build();
CurrentCredentialsAndAuthentication.bindCurrent(a);
return a;
}

/**
Expand Down