Apereo CAS Core Multitenancy library providing tenant management capabilities for Central Authentication Service
—
Auto-configuration classes and beans for seamless Spring Boot integration with proper conditional configuration. The multitenancy module provides complete Spring Boot auto-configuration support with feature toggles and security endpoint configuration.
Main auto-configuration class that sets up all multitenancy beans with proper Spring Boot integration patterns.
/**
* Spring Boot auto-configuration for CAS multitenancy functionality
* Conditionally enabled based on feature configuration
*/
@EnableConfigurationProperties(CasConfigurationProperties.class)
@ConditionalOnFeatureEnabled(feature = CasFeatureModule.FeatureCatalog.Multitenancy)
@AutoConfiguration
public class CasCoreMultitenancyAutoConfiguration {
/**
* Create TenantsManager bean with JSON-based configuration
* Conditionally created only if no existing bean with the same name exists
* @param casProperties CAS configuration properties containing multitenancy settings
* @return DefaultTenantsManager instance configured with JSON location
* @throws Exception if resource location cannot be resolved
*/
@Bean
@RefreshScope(proxyMode = ScopedProxyMode.DEFAULT)
@ConditionalOnMissingBean(name = TenantsManager.BEAN_NAME)
public TenantsManager tenantsManager(CasConfigurationProperties casProperties) throws Exception;
/**
* Create TenantExtractor bean for request-based tenant extraction
* Conditionally created only if no existing bean with the same name exists
* @param casProperties CAS configuration properties
* @param tenantsManager TenantsManager bean for tenant lookups
* @return DefaultTenantExtractor instance
*/
@Bean
@RefreshScope(proxyMode = ScopedProxyMode.DEFAULT)
@ConditionalOnMissingBean(name = TenantExtractor.BEAN_NAME)
public TenantExtractor tenantExtractor(
CasConfigurationProperties casProperties,
@Qualifier(TenantsManager.BEAN_NAME) TenantsManager tenantsManager);
/**
* Create web security configurer for multitenancy endpoints
* Configures security rules for tenant-related HTTP endpoints
* @return CasWebSecurityConfigurer for HTTP security configuration
*/
@Bean
@ConditionalOnMissingBean(name = "casMultitenancyEndpointConfigurer")
@RefreshScope(proxyMode = ScopedProxyMode.DEFAULT)
public CasWebSecurityConfigurer<HttpSecurity> casMultitenancyEndpointConfigurer();
}Usage in Spring Boot Application:
// Auto-configuration is automatically applied when module is on classpath
// No manual configuration needed - beans are created automatically
@SpringBootApplication
public class CasApplication {
public static void main(String[] args) {
SpringApplication.run(CasApplication.class, args);
// TenantsManager and TenantExtractor beans are automatically available
}
}
// Inject beans in your components
@Component
public class MultitenancyService {
@Autowired
private TenantsManager tenantsManager;
@Autowired
private TenantExtractor tenantExtractor;
public void processTenantRequest(HttpServletRequest request) {
Optional<TenantDefinition> tenant = tenantExtractor.extract(request);
if (tenant.isPresent()) {
// Process with tenant context
System.out.println("Processing for tenant: " + tenant.get().getId());
}
}
}The auto-configuration uses CAS configuration properties to control multitenancy behavior:
# application.yml configuration example
cas:
multitenancy:
core:
enabled: true # Enable/disable multitenancy
json:
location: "classpath:tenants.json" # Location of tenant definitions file// Configuration properties are accessed within auto-configuration
CasConfigurationProperties casProperties = ...;
// Get JSON location for tenant definitions
Resource location = casProperties.getMultitenancy().getJson().getLocation();
// Check if multitenancy core is enabled
boolean enabled = casProperties.getMultitenancy().getCore().isEnabled();All multitenancy beans are configured with @RefreshScope to support dynamic configuration updates:
@Bean
@RefreshScope(proxyMode = ScopedProxyMode.DEFAULT)
public TenantsManager tenantsManager(CasConfigurationProperties casProperties) {
// Bean will be recreated when configuration changes
Resource location = casProperties.getMultitenancy().getJson().getLocation();
return new DefaultTenantsManager(location);
}Refresh Scope Benefits:
Beans are created conditionally to allow for custom implementations:
@ConditionalOnMissingBean(name = TenantsManager.BEAN_NAME)
public TenantsManager tenantsManager(CasConfigurationProperties casProperties) {
// Only created if no existing bean with name "tenantsManager"
}Custom Bean Override Example:
@Configuration
public class CustomMultitenancyConfiguration {
// Override default TenantsManager with custom implementation
@Bean(name = TenantsManager.BEAN_NAME)
@Primary
public TenantsManager customTenantsManager() {
return new CustomTenantsManager();
}
}The auto-configuration includes a security configurer for multitenancy endpoints:
/**
* Web security configurer for multitenancy endpoints
* Configures access rules for tenant-related HTTP endpoints
*/
@Bean
@ConditionalOnMissingBean(name = "casMultitenancyEndpointConfigurer")
@RefreshScope(proxyMode = ScopedProxyMode.DEFAULT)
public CasWebSecurityConfigurer<HttpSecurity> casMultitenancyEndpointConfigurer() {
return new CasWebSecurityConfigurer<>() {
@Override
@CanIgnoreReturnValue
public CasWebSecurityConfigurer<HttpSecurity> configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(customizer -> {
AntPathRequestMatcher authEndpoints = new AntPathRequestMatcher("/tenants/**");
customizer.requestMatchers(authEndpoints).permitAll();
});
return this;
}
};
}Security Configuration Effects:
/tenants/** endpoints are configured to permit all accessThe entire auto-configuration is conditionally enabled based on CAS feature configuration:
@ConditionalOnFeatureEnabled(feature = CasFeatureModule.FeatureCatalog.Multitenancy)Feature Control:
# Enable multitenancy feature
cas:
features:
multitenancy:
enabled: trueThe multitenancy feature is part of the CAS feature catalog system:
// Feature reference
CasFeatureModule.FeatureCatalog.MultitenancyBeans use proper qualification to avoid naming conflicts:
@Qualifier(TenantsManager.BEAN_NAME)
TenantsManager tenantsManager@Component
public class TenantAwareService {
private final TenantsManager tenantsManager;
private final TenantExtractor tenantExtractor;
// Constructor injection - recommended approach
public TenantAwareService(TenantsManager tenantsManager, TenantExtractor tenantExtractor) {
this.tenantsManager = tenantsManager;
this.tenantExtractor = tenantExtractor;
}
public void handleTenantOperation(String tenantId) {
Optional<TenantDefinition> tenant = tenantsManager.findTenant(tenantId);
// Process tenant operation
}
}@Service
public class MultitenancyController {
@Autowired
@Qualifier(TenantsManager.BEAN_NAME)
private TenantsManager tenantsManager;
@Autowired
@Qualifier(TenantExtractor.BEAN_NAME)
private TenantExtractor tenantExtractor;
@GetMapping("/admin/tenants")
public List<TenantDefinition> getAllTenants() {
return tenantsManager.findTenants();
}
}# Development profile
---
spring:
config:
activate:
on-profile: development
cas:
multitenancy:
json:
location: "file:/dev/cas/tenants-dev.json"
# Production profile
---
spring:
config:
activate:
on-profile: production
cas:
multitenancy:
json:
location: "file:/etc/cas/tenants-prod.json"@TestConfiguration
public class MultitenancyTestConfiguration {
@Bean
@Primary
public TenantsManager testTenantsManager() {
// Test-specific implementation
return new TestTenantsManager();
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-apereo-cas--cas-server-core-multitenancy