CtrlK
BlogDocsLog inGet started
Tessl Logo

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

Apereo CAS Core Utilities - A comprehensive utility library providing functional programming constructs, encryption utilities, configuration helpers, and core infrastructure components for the Central Authentication Service framework

Pending
Overview
Eval results
Files

text-processing.mddocs/

Text Processing

Message sanitization and text processing utilities for secure handling of user input, system messages, and principal name transformations in CAS applications.

Message Sanitization

MessageSanitizer Interface

Core interface for message sanitization providing secure handling of potentially malicious or sensitive content.

public interface MessageSanitizer {
    
    // Primary sanitization method
    String sanitize(String message);
    
    // Batch sanitization
    Collection<String> sanitize(Collection<String> messages);
    Map<String, String> sanitize(Map<String, String> messageMap);
    
    // Configuration methods
    boolean isEnabled();
    Set<String> getSupportedPatterns();
}

DefaultMessageSanitizer

Default implementation providing comprehensive message sanitization with configurable rules and patterns.

public class DefaultMessageSanitizer implements MessageSanitizer {
    
    // Configuration
    private final boolean enabled;
    private final Set<Pattern> sanitizationPatterns;
    private final Map<String, String> replacementMap;
    
    // Constructors
    public DefaultMessageSanitizer();
    public DefaultMessageSanitizer(boolean enabled);
    public DefaultMessageSanitizer(Set<Pattern> patterns, Map<String, String> replacements);
    
    // MessageSanitizer implementation
    @Override
    public String sanitize(String message);
    
    @Override
    public Collection<String> sanitize(Collection<String> messages);
    
    @Override
    public Map<String, String> sanitize(Map<String, String> messageMap);
    
    @Override
    public boolean isEnabled();
    
    @Override
    public Set<String> getSupportedPatterns();
    
    // Configuration methods
    public void addSanitizationPattern(Pattern pattern, String replacement);
    public void addSanitizationRule(String regex, String replacement);
    public void removeSanitizationPattern(Pattern pattern);
}

Usage Examples

Basic message sanitization:

@Service
public class UserInputService {
    
    private final MessageSanitizer messageSanitizer;
    
    public UserInputService(MessageSanitizer messageSanitizer) {
        this.messageSanitizer = messageSanitizer;
    }
    
    public String processUserComment(String rawComment) {
        if (StringUtils.isBlank(rawComment)) {
            return rawComment;
        }
        
        // Sanitize potentially malicious content
        String sanitized = messageSanitizer.sanitize(rawComment);
        
        // Additional validation
        if (sanitized.length() > 1000) {
            throw new IllegalArgumentException("Comment too long after sanitization");
        }
        
        return sanitized;
    }
    
    public Map<String, String> processFormData(Map<String, String> formData) {
        // Sanitize all form field values
        Map<String, String> sanitized = messageSanitizer.sanitize(formData);
        
        // Log sanitization if content was changed
        formData.forEach((key, original) -> {
            String cleaned = sanitized.get(key);
            if (!Objects.equals(original, cleaned)) {
                log.info("Sanitized field '{}': '{}' -> '{}'", key, original, cleaned);
            }
        });
        
        return sanitized;
    }
    
    public List<String> processErrorMessages(List<String> errorMessages) {
        // Sanitize error messages before display
        Collection<String> sanitized = messageSanitizer.sanitize(errorMessages);
        return new ArrayList<>(sanitized);
    }
}

Custom sanitizer configuration:

@Configuration
public class SanitizationConfiguration {
    
    @Bean
    public MessageSanitizer customMessageSanitizer() {
        DefaultMessageSanitizer sanitizer = new DefaultMessageSanitizer(true);
        
        // Remove script tags
        sanitizer.addSanitizationRule("<script[^>]*>.*?</script>", "");
        
        // Remove potentially dangerous HTML attributes
        sanitizer.addSanitizationRule("\\s*javascript\\s*:", "");
        sanitizer.addSanitizationRule("\\s*vbscript\\s*:", "");
        sanitizer.addSanitizationRule("\\s*onload\\s*=", "");
        sanitizer.addSanitizationRule("\\s*onerror\\s*=", "");
        
        // Sanitize SQL injection attempts
        sanitizer.addSanitizationRule("(?i)(union|select|insert|update|delete|drop)\\s", "");
        
        // Remove excessive whitespace
        sanitizer.addSanitizationRule("\\s+", " ");
        
        // Replace sensitive patterns
        sanitizer.addSanitizationRule("password\\s*[=:]\\s*\\S+", "password=[REDACTED]");
        sanitizer.addSanitizationRule("token\\s*[=:]\\s*\\S+", "token=[REDACTED]");
        
        return sanitizer;
    }
    
    @Bean
    @ConditionalOnProperty(name = "cas.message.sanitization.strict", havingValue = "true")
    public MessageSanitizer strictMessageSanitizer() {
        DefaultMessageSanitizer sanitizer = new DefaultMessageSanitizer(true);
        
        // Strict HTML removal
        sanitizer.addSanitizationRule("<[^>]+>", "");
        
        // Allow only alphanumeric and basic punctuation
        sanitizer.addSanitizationRule("[^a-zA-Z0-9\\s.,!?-]", "");
        
        return sanitizer;
    }
}

MessageSanitationContributor Interface

Interface for contributing custom sanitization rules and patterns to the message sanitization process.

public interface MessageSanitationContributor {
    
    // Contribution methods
    Collection<Pattern> getPatterns();
    Map<String, String> getReplacements();
    
    // Priority and ordering
    int getOrder();
    
    // Conditional application
    boolean supports(String messageType);
    boolean isEnabled();
}

TicketCatalogMessageSanitationContributor

Specialized contributor for ticket-related message sanitization.

public class TicketCatalogMessageSanitationContributor implements MessageSanitationContributor {
    
    // Constructor
    public TicketCatalogMessageSanitationContributor();
    
    @Override
    public Collection<Pattern> getPatterns();
    
    @Override
    public Map<String, String> getReplacements();
    
    @Override
    public int getOrder();
    
    @Override
    public boolean supports(String messageType);
    
    @Override
    public boolean isEnabled();
}

Usage Examples

Custom sanitization contributor:

@Component
@Order(100)
public class AuthenticationMessageSanitationContributor implements MessageSanitationContributor {
    
    private final Collection<Pattern> patterns;
    private final Map<String, String> replacements;
    
    public AuthenticationMessageSanitationContributor() {
        this.patterns = Arrays.asList(
            Pattern.compile("(?i)credential\\s*[=:]\\s*\\S+"),
            Pattern.compile("(?i)password\\s*[=:]\\s*\\S+"),
            Pattern.compile("(?i)secret\\s*[=:]\\s*\\S+"),
            Pattern.compile("TGT-\\d+-\\S+"), // Ticket Granting Tickets
            Pattern.compile("ST-\\d+-\\S+")   // Service Tickets
        );
        
        this.replacements = Map.of(
            "(?i)credential\\s*[=:]\\s*\\S+", "credential=[PROTECTED]",
            "(?i)password\\s*[=:]\\s*\\S+", "password=[PROTECTED]", 
            "(?i)secret\\s*[=:]\\s*\\S+", "secret=[PROTECTED]",
            "TGT-\\d+-\\S+", "TGT-***",
            "ST-\\d+-\\S+", "ST-***"
        );
    }
    
    @Override
    public Collection<Pattern> getPatterns() {
        return patterns;
    }
    
    @Override
    public Map<String, String> getReplacements() {
        return replacements;
    }
    
    @Override
    public int getOrder() {
        return 100;
    }
    
    @Override
    public boolean supports(String messageType) {
        return messageType != null && (
            messageType.contains("authentication") || 
            messageType.contains("login") ||
            messageType.contains("ticket")
        );
    }
    
    @Override
    public boolean isEnabled() {
        return true;
    }
}

@Component
@Order(200)
public class SessionMessageSanitationContributor implements MessageSanitationContributor {
    
    @Override
    public Collection<Pattern> getPatterns() {
        return Arrays.asList(
            Pattern.compile("JSESSIONID=[^;\\s]+"),
            Pattern.compile("sessionId=[^;\\s]+"),
            Pattern.compile("sid=[^;\\s]+")
        );
    }
    
    @Override
    public Map<String, String> getReplacements() {
        return Map.of(
            "JSESSIONID=[^;\\s]+", "JSESSIONID=[HIDDEN]",
            "sessionId=[^;\\s]+", "sessionId=[HIDDEN]",
            "sid=[^;\\s]+", "sid=[HIDDEN]"
        );
    }
    
    @Override
    public int getOrder() {
        return 200;
    }
    
    @Override
    public boolean supports(String messageType) {
        return true; // Apply to all message types
    }
    
    @Override
    public boolean isEnabled() {
        return true;
    }
}

Composite sanitizer with contributors:

@Service
public class CompositeMessageSanitizationService {
    
    private final List<MessageSanitationContributor> contributors;
    private final MessageSanitizer baseSanitizer;
    
    public CompositeMessageSanitizationService(
            List<MessageSanitationContributor> contributors,
            MessageSanitizer baseSanitizer) {
        
        // Sort contributors by order
        this.contributors = contributors.stream()
            .filter(MessageSanitationContributor::isEnabled)
            .sorted(Comparator.comparingInt(MessageSanitationContributor::getOrder))
            .collect(Collectors.toList());
            
        this.baseSanitizer = baseSanitizer;
    }
    
    public String sanitizeMessage(String message, String messageType) {
        if (StringUtils.isBlank(message)) {
            return message;
        }
        
        String sanitized = message;
        
        // Apply base sanitization first
        if (baseSanitizer.isEnabled()) {
            sanitized = baseSanitizer.sanitize(sanitized);
        }
        
        // Apply contributor-specific sanitization
        for (MessageSanitationContributor contributor : contributors) {
            if (contributor.supports(messageType)) {
                sanitized = applyContributorSanitization(sanitized, contributor);
            }
        }
        
        return sanitized;
    }
    
    private String applyContributorSanitization(String message, MessageSanitationContributor contributor) {
        String sanitized = message;
        
        // Apply replacement patterns
        Map<String, String> replacements = contributor.getReplacements();
        for (Map.Entry<String, String> entry : replacements.entrySet()) {
            sanitized = sanitized.replaceAll(entry.getKey(), entry.getValue());
        }
        
        // Apply additional patterns
        Collection<Pattern> patterns = contributor.getPatterns();
        for (Pattern pattern : patterns) {
            sanitized = pattern.matcher(sanitized).replaceAll("[SANITIZED]");
        }
        
        return sanitized;
    }
}

Principal Name Transformers

PrincipalNameTransformer Interface

Interface for transforming principal names during authentication processing.

public interface PrincipalNameTransformer {
    
    // Primary transformation method
    String transform(String formUserId);
    
    // Configuration methods
    String getName();
    boolean isEnabled();
}

Core Transformer Implementations

NoOpPrincipalNameTransformer

No-operation transformer that returns the input unchanged.

public class NoOpPrincipalNameTransformer implements PrincipalNameTransformer {
    
    @Override
    public String transform(String formUserId);
    
    @Override
    public String getName();
    
    @Override
    public boolean isEnabled();
}

ConvertCasePrincipalNameTransformer

Transformer for case conversion (uppercase/lowercase).

public class ConvertCasePrincipalNameTransformer implements PrincipalNameTransformer {
    
    // Case conversion modes
    public enum CaseConversion {
        UPPERCASE, LOWERCASE, NONE
    }
    
    // Constructor
    public ConvertCasePrincipalNameTransformer(CaseConversion conversion);
    
    @Override
    public String transform(String formUserId);
}

PrefixSuffixPrincipalNameTransformer

Transformer for adding prefixes and/or suffixes to principal names.

public class PrefixSuffixPrincipalNameTransformer implements PrincipalNameTransformer {
    
    // Constructor
    public PrefixSuffixPrincipalNameTransformer(String prefix, String suffix);
    
    @Override
    public String transform(String formUserId);
}

RegexPrincipalNameTransformer

Transformer using regular expressions for complex name transformations.

public class RegexPrincipalNameTransformer implements PrincipalNameTransformer {
    
    // Constructor
    public RegexPrincipalNameTransformer(String pattern, String replacement);
    public RegexPrincipalNameTransformer(Pattern compiledPattern, String replacement);
    
    @Override
    public String transform(String formUserId);
}

BlockingPrincipalNameTransformer

Transformer that blocks/rejects certain principal names.

public class BlockingPrincipalNameTransformer implements PrincipalNameTransformer {
    
    // Constructor
    public BlockingPrincipalNameTransformer(Set<String> blockedNames);
    public BlockingPrincipalNameTransformer(Pattern blockingPattern);
    
    @Override
    public String transform(String formUserId);
}

ChainingPrincipalNameTransformer

Transformer that chains multiple transformers in sequence.

public class ChainingPrincipalNameTransformer implements PrincipalNameTransformer {
    
    // Constructor
    public ChainingPrincipalNameTransformer(List<PrincipalNameTransformer> transformers);
    
    @Override
    public String transform(String formUserId);
    
    // Chain management
    public void addTransformer(PrincipalNameTransformer transformer);
    public List<PrincipalNameTransformer> getTransformers();
}

GroovyPrincipalNameTransformer

Transformer using Groovy scripts for dynamic transformations.

public class GroovyPrincipalNameTransformer implements PrincipalNameTransformer {
    
    // Constructor
    public GroovyPrincipalNameTransformer(String groovyScript);
    public GroovyPrincipalNameTransformer(Resource groovyScriptResource);
    
    @Override
    public String transform(String formUserId);
}

Usage Examples

Basic transformers:

@Configuration
public class PrincipalTransformerConfiguration {
    
    @Bean
    @ConditionalOnProperty(name = "cas.principal.transform.case", havingValue = "lowercase")
    public PrincipalNameTransformer lowercaseTransformer() {
        return new ConvertCasePrincipalNameTransformer(CaseConversion.LOWERCASE);
    }
    
    @Bean
    @ConditionalOnProperty(name = "cas.principal.transform.domain.enabled", havingValue = "true")
    public PrincipalNameTransformer domainTransformer(
            @Value("${cas.principal.transform.domain.suffix:@example.com}") String suffix) {
        
        return new PrefixSuffixPrincipalNameTransformer("", suffix);
    }
    
    @Bean
    public PrincipalNameTransformer emailToUsernameTransformer() {
        // Transform email addresses to usernames
        return new RegexPrincipalNameTransformer("(.+)@.+", "$1");
    }
    
    @Bean
    public PrincipalNameTransformer blockingTransformer() {
        // Block administrative accounts
        Set<String> blockedNames = Set.of("admin", "root", "administrator", "guest");
        return new BlockingPrincipalNameTransformer(blockedNames);
    }
}

Complex chained transformations:

@Configuration
public class ComplexPrincipalTransformation {
    
    @Bean
    public PrincipalNameTransformer chainedPrincipalTransformer() {
        List<PrincipalNameTransformer> transformers = Arrays.asList(
            // 1. Block dangerous usernames first
            new BlockingPrincipalNameTransformer(Set.of("admin", "root")),
            
            // 2. Extract username from email if present
            new RegexPrincipalNameTransformer("(.+)@.+", "$1"),
            
            // 3. Convert to lowercase
            new ConvertCasePrincipalNameTransformer(CaseConversion.LOWERCASE),
            
            // 4. Remove special characters
            new RegexPrincipalNameTransformer("[^a-z0-9._-]", ""),
            
            // 5. Add domain if not present
            new RegexPrincipalNameTransformer("^([^@]+)$", "$1@company.com")
        );
        
        return new ChainingPrincipalNameTransformer(transformers);
    }
}

Groovy-based dynamic transformation:

@Bean
@ConditionalOnProperty(name = "cas.principal.transform.groovy.enabled", havingValue = "true")
public PrincipalNameTransformer groovyPrincipalTransformer() {
    String groovyScript = """
        // Dynamic principal transformation script
        
        // Input: formUserId (String)
        // Output: transformed principal name (String)
        
        if (formUserId == null || formUserId.isEmpty()) {
            return formUserId
        }
        
        def transformed = formUserId.toLowerCase()
        
        // Handle email addresses
        if (transformed.contains('@')) {
            def parts = transformed.split('@')
            def username = parts[0]
            def domain = parts[1]
            
            // Map domains to internal format
            def domainMappings = [
                'gmail.com': 'external',
                'company.com': 'internal',
                'contractor.com': 'contractor'
            ]
            
            def mappedDomain = domainMappings[domain] ?: 'unknown'
            return username + '.' + mappedDomain
        }
        
        // Handle employee ID format (EMP123456)
        if (transformed.startsWith('emp')) {
            return transformed.substring(3)
        }
        
        // Default transformation
        return transformed.replaceAll('[^a-z0-9._-]', '')
    """;
    
    return new GroovyPrincipalNameTransformer(groovyScript);
}

Usage in authentication handler:

@Component
public class CustomAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler {
    
    private final PrincipalNameTransformer principalTransformer;
    private final UserRepository userRepository;
    
    public CustomAuthenticationHandler(
            PrincipalNameTransformer principalTransformer,
            UserRepository userRepository) {
        this.principalTransformer = principalTransformer;
        this.userRepository = userRepository;
    }
    
    @Override
    protected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(
            UsernamePasswordCredential credential,
            String originalPassword) throws GeneralSecurityException {
        
        try {
            // Transform the principal name
            String originalUsername = credential.getUsername();
            String transformedUsername = principalTransformer.transform(originalUsername);
            
            log.debug("Transformed principal: '{}' -> '{}'", originalUsername, transformedUsername);
            
            // Authenticate with transformed username
            User user = userRepository.findByUsername(transformedUsername);
            if (user == null) {
                throw new AccountNotFoundException("User not found: " + transformedUsername);
            }
            
            if (!passwordEncoder.matches(originalPassword, user.getPasswordHash())) {
                throw new FailedLoginException("Invalid credentials");
            }
            
            // Create principal with transformed name
            Principal principal = principalFactory.createPrincipal(
                transformedUsername,
                user.getAttributes()
            );
            
            return createHandlerResult(credential, principal);
            
        } catch (Exception e) {
            log.error("Authentication failed for user: {}", credential.getUsername(), e);
            throw new FailedLoginException("Authentication failed", e);
        }
    }
}

This text processing library provides comprehensive capabilities for secure message handling and flexible principal name transformations, essential for production CAS deployments with security and compliance requirements.

Install with Tessl CLI

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

docs

core-utilities.md

cryptography.md

functional-programming.md

generators.md

http-clients.md

index.md

jwt-utilities.md

serialization.md

specialized-utilities.md

spring-integration.md

text-processing.md

tile.json