Apereo CAS Surrogate Authentication API providing core interfaces and classes for surrogate authentication functionality
npx @tessl/cli install tessl/maven-org-apereo-cas--cas-server-support-surrogate-api@7.2.0A comprehensive API for implementing surrogate authentication functionality in the Apereo CAS (Central Authentication Service) ecosystem. This library provides the core interfaces, data classes, and service access strategies needed to enable authorized users to impersonate other users for administrative and support purposes.
import org.apereo.cas.authentication.surrogate.SurrogateAuthenticationService;
import org.apereo.cas.authentication.surrogate.SurrogateAuthenticationRequest;
import org.apereo.cas.authentication.surrogate.SurrogateCredentialTrait;
import org.apereo.cas.authentication.surrogate.SurrogateCredentialParser;
import org.apereo.cas.services.SurrogateRegisteredServiceAccessStrategy;
import org.apereo.cas.services.BaseSurrogateRegisteredServiceAccessStrategy;
import org.apereo.cas.services.GroovySurrogateRegisteredServiceAccessStrategy;
import org.apereo.cas.authentication.AuthenticationBuilder;
import org.apereo.cas.authentication.principal.Principal;
import org.apereo.cas.authentication.principal.Service;
import org.apereo.cas.authentication.MutableCredential;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import org.apereo.cas.authentication.surrogate.SurrogateAuthenticationService;
import org.apereo.cas.authentication.surrogate.SurrogateAuthenticationRequest;
import org.apereo.cas.authentication.principal.Principal;
import org.apereo.cas.authentication.principal.Service;
import java.util.Collection;
import java.util.Optional;
// Implement surrogate authentication service
public class CustomSurrogateAuthenticationService implements SurrogateAuthenticationService {
@Override
public Collection<String> getImpersonationAccounts(String username, Optional<? extends Service> service) throws Throwable {
// Return list of accounts this user can impersonate
return java.util.Arrays.asList("user1", "user2", "admin");
}
@Override
public boolean canImpersonate(String surrogate, Principal principal, Optional<? extends Service> service) throws Throwable {
// Check if principal can impersonate the surrogate user
Collection<String> accounts = getImpersonationAccounts(principal.getId(), service);
return accounts.contains(surrogate) || isWildcardedAccount(accounts, service);
}
}
// Create surrogate authentication request
SurrogateAuthenticationRequest request = SurrogateAuthenticationRequest.builder()
.credential(mutableCredential)
.username("admin")
.surrogateUsername("targetUser")
.selectable(true)
.build();
// Check if request has surrogate username
if (request.hasSurrogateUsername()) {
// Process surrogate authentication
}The surrogate authentication API follows a layered architecture:
SurrogateAuthenticationService defines core impersonation logicSurrogateAuthenticationRequest and parsing components handle authentication flowThis design enables flexible backend integration (LDAP, JDBC, REST APIs) and fine-grained access control through CAS's service management framework.
Primary interface for surrogate authentication operations including permission checking, account enumeration, and wildcard support for super-administrators.
@FunctionalInterface
public interface SurrogateAuthenticationService {
/**
* Logger instance.
*/
Logger LOGGER = LoggerFactory.getLogger(SurrogateAuthenticationService.class);
/**
* An authorized account may be tagged as a wildcard, meaning
* that the account has special permissions to impersonate anyone.
*/
String WILDCARD_ACCOUNT = "*";
/**
* Default bean name.
*/
String BEAN_NAME = "surrogateAuthenticationService";
/**
* Surrogate username attribute in the authentication payload.
*/
String AUTHENTICATION_ATTR_SURROGATE_USER = "surrogateUser";
/**
* Original credential attribute in the authentication payload.
*/
String AUTHENTICATION_ATTR_SURROGATE_PRINCIPAL = "surrogatePrincipal";
/**
* Indicates that surrogate authn is enabled and activated.
*/
String AUTHENTICATION_ATTR_SURROGATE_ENABLED = "surrogateEnabled";
Collection<String> getImpersonationAccounts(String username, Optional<? extends Service> service) throws Throwable;
default boolean canImpersonate(String surrogate, Principal principal, Optional<? extends Service> service) throws Throwable;
default boolean isWildcardedAccount(String surrogate, Principal principal, Optional<? extends Service> service) throws Throwable;
default boolean isWildcardedAccount(Collection<String> accounts, Optional<? extends Service> service);
default void collectSurrogateAttributes(AuthenticationBuilder builder, String surrogateUser, String principal);
}Data classes and parsers for managing surrogate authentication requests, credential traits, and integration with CAS authentication flow.
@ToString
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@Getter
@NoArgsConstructor(force = true)
@EqualsAndHashCode
@RequiredArgsConstructor
@SuperBuilder
public class SurrogateAuthenticationRequest implements Serializable {
private final MutableCredential credential;
private final String username;
private final String surrogateUsername;
private final boolean selectable;
boolean hasSurrogateUsername();
}
@ToString
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@Getter
@NoArgsConstructor(force = true)
@EqualsAndHashCode
@RequiredArgsConstructor
public class SurrogateCredentialTrait implements CredentialTrait {
private final String surrogateUsername;
}
@FunctionalInterface
public interface SurrogateCredentialParser {
String BEAN_NAME = "surrogateCredentialParser";
Optional<SurrogateAuthenticationRequest> parse(MutableCredential credential);
}Request and Credential Handling
Service-level access strategies for controlling surrogate authentication permissions at the individual service level, including attribute-based and script-based authorization.
public abstract class BaseSurrogateRegisteredServiceAccessStrategy extends BaseRegisteredServiceAccessStrategy {
protected boolean isSurrogateAuthenticationSession(RegisteredServiceAccessStrategyRequest request);
}
@Getter
@Setter
@EqualsAndHashCode(callSuper = true)
public class SurrogateRegisteredServiceAccessStrategy extends BaseSurrogateRegisteredServiceAccessStrategy {
protected boolean requireAllAttributes = true;
protected boolean caseInsensitive;
private Map<String, Set<String>> surrogateRequiredAttributes = new HashMap<>(0);
boolean authorizeRequest(RegisteredServiceAccessStrategyRequest request);
protected boolean doPrincipalAttributesAllowSurrogateServiceAccess(RegisteredServiceAccessStrategyRequest request);
}
@Slf4j
@Getter
@Setter
@EqualsAndHashCode(callSuper = true)
public class GroovySurrogateRegisteredServiceAccessStrategy extends BaseSurrogateRegisteredServiceAccessStrategy {
@ExpressionLanguageCapable
private String groovyScript;
boolean authorizeRequest(RegisteredServiceAccessStrategyRequest request) throws Throwable;
}