CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-apereo-cas--cas-server-core-authentication-api

Core authentication API module for Apereo CAS providing fundamental authentication interfaces, implementations, and components for the CAS server infrastructure

Pending
Overview
Eval results
Files

authentication-policies.mddocs/

Authentication Policies

Authentication policies define the rules and requirements that must be satisfied for an authentication attempt to be considered successful. The CAS authentication API provides a comprehensive set of configurable policies that can be combined to implement complex authentication requirements.

Core Policy Interface

package org.apereo.cas.authentication;

public interface AuthenticationPolicy {
    AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction) throws Exception;
    boolean shouldResumeOnFailure(Throwable failure);
    String getName();
    void setName(String name);
    int getOrder();
    void setOrder(int order);
}

Policy Execution Result

package org.apereo.cas.authentication;

import java.util.Optional;

public interface AuthenticationPolicyExecutionResult {
    boolean isSuccess();
    Optional<Throwable> getFailure();
    
    static AuthenticationPolicyExecutionResult success() {
        return new DefaultAuthenticationPolicyExecutionResult(true, null);
    }
    
    static AuthenticationPolicyExecutionResult failure(Throwable failure) {
        return new DefaultAuthenticationPolicyExecutionResult(false, failure);
    }
}

class DefaultAuthenticationPolicyExecutionResult implements AuthenticationPolicyExecutionResult {
    private final boolean success;
    private final Throwable failure;
    
    public DefaultAuthenticationPolicyExecutionResult(boolean success, Throwable failure) {
        this.success = success;
        this.failure = failure;
    }
    
    public boolean isSuccess() { return success; }
    public Optional<Throwable> getFailure() { return Optional.ofNullable(failure); }
}

Base Authentication Policy

package org.apereo.cas.authentication.policy;

import org.apereo.cas.authentication.AuthenticationPolicy;
import org.springframework.core.Ordered;

public abstract class BaseAuthenticationPolicy implements AuthenticationPolicy {
    private int order = Ordered.LOWEST_PRECEDENCE;
    private String name = getClass().getSimpleName();
    
    public int getOrder() { return order; }
    public void setOrder(int order) { this.order = order; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public boolean shouldResumeOnFailure(Throwable failure) {
        return false;
    }
}

Authentication Handler Policies

All Authentication Handlers Succeeded Policy

Requires all configured authentication handlers to succeed:

package org.apereo.cas.authentication.policy;

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;
import org.apereo.cas.authentication.AuthenticationTransaction;

public class AllAuthenticationHandlersSucceededAuthenticationPolicy 
    extends BaseAuthenticationPolicy {
    
    public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction) 
        throws Exception {
        
        Set<AuthenticationHandler> handlers = transaction.getAuthenticationHandlers();
        Map<String, Throwable> failures = transaction.getFailures();
        
        if (handlers.isEmpty()) {
            return AuthenticationPolicyExecutionResult.failure(
                new AuthenticationException("No authentication handlers configured"));
        }
        
        for (AuthenticationHandler handler : handlers) {
            if (failures.containsKey(handler.getName())) {
                return AuthenticationPolicyExecutionResult.failure(
                    new AuthenticationException("Handler " + handler.getName() + " failed"));
            }
        }
        
        return AuthenticationPolicyExecutionResult.success();
    }
}

Required Authentication Handler Policy

Requires specific authentication handlers to succeed:

package org.apereo.cas.authentication.policy;

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;
import org.apereo.cas.authentication.AuthenticationTransaction;
import java.util.Set;

public class RequiredAuthenticationHandlerAuthenticationPolicy 
    extends BaseAuthenticationHandlerAuthenticationPolicy {
    
    private final Set<String> requiredHandlerNames;
    private final boolean tryAll;
    
    public RequiredAuthenticationHandlerAuthenticationPolicy(Set<String> requiredHandlerNames) {
        this(requiredHandlerNames, false);
    }
    
    public RequiredAuthenticationHandlerAuthenticationPolicy(Set<String> requiredHandlerNames, 
                                                           boolean tryAll) {
        this.requiredHandlerNames = requiredHandlerNames;
        this.tryAll = tryAll;
    }
    
    public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction) 
        throws Exception {
        
        Map<String, AuthenticationHandlerExecutionResult> successes = transaction.getSuccesses();
        
        for (String requiredHandler : requiredHandlerNames) {
            if (!successes.containsKey(requiredHandler)) {
                return AuthenticationPolicyExecutionResult.failure(
                    new AuthenticationException("Required handler " + requiredHandler + " did not succeed"));
            }
        }
        
        return AuthenticationPolicyExecutionResult.success();
    }
    
    public boolean shouldResumeOnFailure(Throwable failure) {
        return tryAll;
    }
}

Excluded Authentication Handler Policy

Excludes specific authentication handlers from being required:

package org.apereo.cas.authentication.policy;

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;
import org.apereo.cas.authentication.AuthenticationTransaction;
import java.util.Set;

public class ExcludedAuthenticationHandlerAuthenticationPolicy 
    extends BaseAuthenticationHandlerAuthenticationPolicy {
    
    private final Set<String> excludedHandlerNames;
    
    public ExcludedAuthenticationHandlerAuthenticationPolicy(Set<String> excludedHandlerNames) {
        this.excludedHandlerNames = excludedHandlerNames;
    }
    
    public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction) 
        throws Exception {
        
        Map<String, AuthenticationHandlerExecutionResult> successes = transaction.getSuccesses();
        Map<String, Throwable> failures = transaction.getFailures();
        
        // Check if any non-excluded handlers succeeded
        boolean hasNonExcludedSuccess = successes.keySet().stream()
            .anyMatch(handlerName -> !excludedHandlerNames.contains(handlerName));
            
        if (hasNonExcludedSuccess) {
            return AuthenticationPolicyExecutionResult.success();
        }
        
        // Check if only excluded handlers failed
        boolean onlyExcludedFailures = failures.keySet().stream()
            .allMatch(excludedHandlerNames::contains);
            
        if (onlyExcludedFailures && !successes.isEmpty()) {
            return AuthenticationPolicyExecutionResult.success();
        }
        
        return AuthenticationPolicyExecutionResult.failure(
            new AuthenticationException("No non-excluded authentication handlers succeeded"));
    }
}

Credential Validation Policies

All Credentials Validated Policy

Requires all provided credentials to be validated successfully:

package org.apereo.cas.authentication.policy;

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;
import org.apereo.cas.authentication.AuthenticationTransaction;
import org.apereo.cas.authentication.Credential;

public class AllCredentialsValidatedAuthenticationPolicy extends BaseAuthenticationPolicy {
    
    public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction) 
        throws Exception {
        
        Collection<Credential> credentials = transaction.getCredentials();
        Map<String, AuthenticationHandlerExecutionResult> successes = transaction.getSuccesses();
        
        if (credentials.isEmpty()) {
            return AuthenticationPolicyExecutionResult.failure(
                new AuthenticationException("No credentials provided"));
        }
        
        for (Credential credential : credentials) {
            boolean credentialValidated = successes.values().stream()
                .anyMatch(result -> result.getCredential().equals(credential));
                
            if (!credentialValidated) {
                return AuthenticationPolicyExecutionResult.failure(
                    new AuthenticationException("Credential not validated: " + credential.getId()));
            }
        }
        
        return AuthenticationPolicyExecutionResult.success();
    }
}

At Least One Credential Validated Policy

Requires at least one credential to be validated successfully:

package org.apereo.cas.authentication.policy;

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;
import org.apereo.cas.authentication.AuthenticationTransaction;

public class AtLeastOneCredentialValidatedAuthenticationPolicy extends BaseAuthenticationPolicy {
    
    public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction) 
        throws Exception {
        
        Collection<Credential> credentials = transaction.getCredentials();
        Map<String, AuthenticationHandlerExecutionResult> successes = transaction.getSuccesses();
        
        if (credentials.isEmpty()) {
            return AuthenticationPolicyExecutionResult.failure(
                new AuthenticationException("No credentials provided"));
        }
        
        if (successes.isEmpty()) {
            return AuthenticationPolicyExecutionResult.failure(
                new AuthenticationException("No authentication handlers succeeded"));
        }
        
        // At least one credential was validated if we have successes
        return AuthenticationPolicyExecutionResult.success();
    }
}

Attribute-Based Policies

Required Attributes Policy

Requires specific attributes to be present in the authentication:

package org.apereo.cas.authentication.policy;

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;
import org.apereo.cas.authentication.AuthenticationTransaction;
import org.apereo.cas.authentication.principal.Principal;
import java.util.Map;
import java.util.Set;

public class RequiredAttributesAuthenticationPolicy extends BaseAuthenticationPolicy {
    
    private final Map<String, Set<Object>> requiredAttributes;
    private final boolean tryAll;
    
    public RequiredAttributesAuthenticationPolicy(Map<String, Set<Object>> requiredAttributes) {
        this(requiredAttributes, false);
    }
    
    public RequiredAttributesAuthenticationPolicy(Map<String, Set<Object>> requiredAttributes,
                                                boolean tryAll) {
        this.requiredAttributes = requiredAttributes;
        this.tryAll = tryAll;
    }
    
    public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction) 
        throws Exception {
        
        Map<String, AuthenticationHandlerExecutionResult> successes = transaction.getSuccesses();
        
        for (AuthenticationHandlerExecutionResult result : successes.values()) {
            Principal principal = result.getPrincipal();
            Map<String, List<Object>> principalAttributes = principal.getAttributes();
            
            for (Map.Entry<String, Set<Object>> requiredEntry : requiredAttributes.entrySet()) {
                String attributeName = requiredEntry.getKey();
                Set<Object> requiredValues = requiredEntry.getValue();
                
                List<Object> actualValues = principalAttributes.get(attributeName);
                if (actualValues == null || actualValues.isEmpty()) {
                    return AuthenticationPolicyExecutionResult.failure(
                        new AuthenticationException("Required attribute missing: " + attributeName));
                }
                
                if (!requiredValues.isEmpty()) {
                    boolean hasRequiredValue = actualValues.stream()
                        .anyMatch(requiredValues::contains);
                    if (!hasRequiredValue) {
                        return AuthenticationPolicyExecutionResult.failure(
                            new AuthenticationException("Required attribute value not found: " + attributeName));
                    }
                }
            }
        }
        
        return AuthenticationPolicyExecutionResult.success();
    }
    
    public boolean shouldResumeOnFailure(Throwable failure) {
        return tryAll;
    }
}

Prevention Policies

Not Prevented Policy

Ensures that no authentication attempts are marked as prevented:

package org.apereo.cas.authentication.policy;

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;
import org.apereo.cas.authentication.AuthenticationTransaction;
import org.apereo.cas.authentication.PreventedException;

public class NotPreventedAuthenticationPolicy extends BaseAuthenticationPolicy {
    
    public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction) 
        throws Exception {
        
        Map<String, Throwable> failures = transaction.getFailures();
        
        for (Throwable failure : failures.values()) {
            if (failure instanceof PreventedException) {
                return AuthenticationPolicyExecutionResult.failure(failure);
            }
        }
        
        return AuthenticationPolicyExecutionResult.success();
    }
}

Principal Uniqueness Policy

Unique Principal Policy

Ensures that all successful authentications resolve to the same principal:

package org.apereo.cas.authentication.policy;

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;
import org.apereo.cas.authentication.AuthenticationTransaction;
import org.apereo.cas.authentication.principal.Principal;
import java.util.Set;
import java.util.stream.Collectors;

public class UniquePrincipalAuthenticationPolicy extends BaseAuthenticationPolicy {
    
    public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction) 
        throws Exception {
        
        Map<String, AuthenticationHandlerExecutionResult> successes = transaction.getSuccesses();
        
        if (successes.size() <= 1) {
            return AuthenticationPolicyExecutionResult.success();
        }
        
        Set<String> principalIds = successes.values().stream()
            .map(AuthenticationHandlerExecutionResult::getPrincipal)
            .map(Principal::getId)
            .collect(Collectors.toSet());
            
        if (principalIds.size() > 1) {
            return AuthenticationPolicyExecutionResult.failure(
                new MixedPrincipalException("Multiple principals found: " + principalIds));
        }
        
        return AuthenticationPolicyExecutionResult.success();
    }
}

Scriptable Policies

Groovy Script Policy

Allows defining authentication policies using Groovy scripts:

package org.apereo.cas.authentication.policy;

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;
import org.apereo.cas.authentication.AuthenticationTransaction;
import org.apereo.cas.util.scripting.ExecutableCompiledGroovyScript;

public class GroovyScriptAuthenticationPolicy extends BaseAuthenticationPolicy {
    
    private final ExecutableCompiledGroovyScript watchableScript;
    
    public GroovyScriptAuthenticationPolicy(ExecutableCompiledGroovyScript watchableScript) {
        this.watchableScript = watchableScript;
    }
    
    public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction) 
        throws Exception {
        
        Object result = watchableScript.execute(transaction, 
                                              AuthenticationPolicyExecutionResult.class);
        
        if (result instanceof AuthenticationPolicyExecutionResult) {
            return (AuthenticationPolicyExecutionResult) result;
        } else if (result instanceof Boolean) {
            return (Boolean) result 
                ? AuthenticationPolicyExecutionResult.success()
                : AuthenticationPolicyExecutionResult.failure(
                    new AuthenticationException("Groovy script returned false"));
        }
        
        return AuthenticationPolicyExecutionResult.success();
    }
}

RESTful Policies

RESTful Authentication Policy

Delegates policy decisions to external REST services:

package org.apereo.cas.authentication.policy;

import org.apereo.cas.authentication.AuthenticationPolicyExecutionResult;
import org.apereo.cas.authentication.AuthenticationTransaction;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import java.util.Map;

public class RestfulAuthenticationPolicy extends BaseAuthenticationPolicy {
    
    private final RestTemplate restTemplate;
    private final String endpoint;
    
    public RestfulAuthenticationPolicy(RestTemplate restTemplate, String endpoint) {
        this.restTemplate = restTemplate;
        this.endpoint = endpoint;
    }
    
    public AuthenticationPolicyExecutionResult execute(AuthenticationTransaction transaction) 
        throws Exception {
        
        try {
            Map<String, Object> request = Map.of(
                "transaction", transaction,
                "credentials", transaction.getCredentials(),
                "successes", transaction.getSuccesses(),
                "failures", transaction.getFailures()
            );
            
            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(request);
            ResponseEntity<Map> response = restTemplate.exchange(
                endpoint, HttpMethod.POST, entity, Map.class);
                
            Map<String, Object> responseBody = response.getBody();
            if (responseBody != null) {
                Boolean success = (Boolean) responseBody.get("success");
                if (success != null && success) {
                    return AuthenticationPolicyExecutionResult.success();
                }
                
                String message = (String) responseBody.get("message");
                return AuthenticationPolicyExecutionResult.failure(
                    new AuthenticationException(message != null ? message : "REST policy failed"));
            }
            
        } catch (Exception e) {
            return AuthenticationPolicyExecutionResult.failure(
                new AuthenticationException("REST policy endpoint error", e));
        }
        
        return AuthenticationPolicyExecutionResult.failure(
            new AuthenticationException("REST policy evaluation failed"));
    }
}

Policy Resolvers

Policy resolvers determine which policies should be applied to authentication transactions:

package org.apereo.cas.authentication;

import java.util.Set;

public interface AuthenticationPolicyResolver {
    Set<AuthenticationPolicy> resolve(AuthenticationTransaction transaction);
    boolean supports(AuthenticationTransaction transaction);
}

Registered Service Policy Resolver

Resolves policies based on the registered service configuration:

package org.apereo.cas.authentication.policy;

import org.apereo.cas.authentication.AuthenticationPolicy;
import org.apereo.cas.authentication.AuthenticationPolicyResolver;
import org.apereo.cas.authentication.AuthenticationTransaction;
import org.apereo.cas.services.RegisteredService;
import org.apereo.cas.services.ServicesManager;
import java.util.Set;
import java.util.LinkedHashSet;

public class RegisteredServiceAuthenticationPolicyResolver 
    implements AuthenticationPolicyResolver {
    
    private final ServicesManager servicesManager;
    
    public RegisteredServiceAuthenticationPolicyResolver(ServicesManager servicesManager) {
        this.servicesManager = servicesManager;
    }
    
    public Set<AuthenticationPolicy> resolve(AuthenticationTransaction transaction) {
        Set<AuthenticationPolicy> policies = new LinkedHashSet<>();
        
        Service service = transaction.getService();
        if (service != null) {
            RegisteredService registeredService = servicesManager.findServiceBy(service);
            if (registeredService != null && registeredService.getAuthenticationPolicy() != null) {
                
                // Add required handler policy if specified
                Set<String> requiredHandlers = registeredService.getAuthenticationPolicy()
                    .getRequiredAuthenticationHandlers();
                if (!requiredHandlers.isEmpty()) {
                    policies.add(new RequiredAuthenticationHandlerAuthenticationPolicy(requiredHandlers));
                }
                
                // Add required attribute policy if specified
                Map<String, Set<Object>> requiredAttributes = registeredService.getAuthenticationPolicy()
                    .getRequiredAttributes();
                if (!requiredAttributes.isEmpty()) {
                    policies.add(new RequiredAttributesAuthenticationPolicy(requiredAttributes));
                }
            }
        }
        
        return policies;
    }
    
    public boolean supports(AuthenticationTransaction transaction) {
        return transaction.getService() != null;
    }
}

Configuration Examples

Spring Configuration

@Configuration
public class AuthenticationPolicyConfiguration {
    
    @Bean
    public AuthenticationPolicy allCredentialsValidatedAuthenticationPolicy() {
        return new AllCredentialsValidatedAuthenticationPolicy();
    }
    
    @Bean
    public AuthenticationPolicy requiredHandlersAuthenticationPolicy() {
        Set<String> requiredHandlers = Set.of("LdapAuthenticationHandler", "DatabaseAuthenticationHandler");
        return new RequiredAuthenticationHandlerAuthenticationPolicy(requiredHandlers);
    }
    
    @Bean
    public AuthenticationPolicy requiredAttributesAuthenticationPolicy() {
        Map<String, Set<Object>> requiredAttributes = Map.of(
            "memberOf", Set.of("CN=Users,DC=example,DC=com"),
            "accountEnabled", Set.of("true")
        );
        return new RequiredAttributesAuthenticationPolicy(requiredAttributes);
    }
    
    @Bean
    public AuthenticationPolicyResolver registeredServiceAuthenticationPolicyResolver(
        ServicesManager servicesManager) {
        return new RegisteredServiceAuthenticationPolicyResolver(servicesManager);
    }
}

Programmatic Configuration

// Create policy chain
List<AuthenticationPolicy> policies = List.of(
    new NotPreventedAuthenticationPolicy(),
    new AtLeastOneCredentialValidatedAuthenticationPolicy(),
    new UniquePrincipalAuthenticationPolicy()
);

// Register policies in execution plan
for (AuthenticationPolicy policy : policies) {
    authenticationEventExecutionPlan.registerAuthenticationPolicy(policy);
}

// Create Groovy-based policy
ExecutableCompiledGroovyScript script = // ... create script
GroovyScriptAuthenticationPolicy groovyPolicy = new GroovyScriptAuthenticationPolicy(script);
authenticationEventExecutionPlan.registerAuthenticationPolicy(groovyPolicy);

// Create REST-based policy
RestTemplate restTemplate = new RestTemplate();
RestfulAuthenticationPolicy restPolicy = new RestfulAuthenticationPolicy(
    restTemplate, "https://policy.example.com/validate");
authenticationEventExecutionPlan.registerAuthenticationPolicy(restPolicy);

Authentication policies provide fine-grained control over authentication requirements and enable complex security scenarios by composing multiple policy types to meet specific organizational needs.

Install with Tessl CLI

npx tessl i tessl/maven-org-apereo-cas--cas-server-core-authentication-api

docs

adaptive-authentication.md

authentication-handlers.md

authentication-policies.md

credential-handling.md

index.md

password-policies.md

principal-resolution.md

tile.json