LdaptiveAuthenticationAutoConfiguration.java
/*
* Copyright 2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bremersee.spring.boot.autoconfigure.security.authentication;
import static java.util.Objects.nonNull;
import static org.springframework.util.ObjectUtils.isEmpty;
import lombok.extern.slf4j.Slf4j;
import org.bremersee.ldaptive.LdaptiveTemplate;
import org.bremersee.spring.boot.autoconfigure.ldaptive.LdaptiveAutoConfiguration;
import org.bremersee.spring.security.core.EmailToUsernameResolver;
import org.bremersee.spring.security.core.authority.mapping.NormalizedGrantedAuthoritiesMapper;
import org.bremersee.spring.security.ldaptive.authentication.AccountControlEvaluator;
import org.bremersee.spring.security.ldaptive.authentication.LdaptiveAuthentication;
import org.bremersee.spring.security.ldaptive.authentication.LdaptiveAuthenticationManager;
import org.bremersee.spring.security.ldaptive.authentication.LdaptiveAuthenticationProperties;
import org.bremersee.spring.security.ldaptive.authentication.ReactiveLdaptiveAuthenticationManager;
import org.bremersee.spring.security.ldaptive.authentication.UsernameToBindDnConverter;
import org.bremersee.spring.security.ldaptive.userdetails.LdaptiveEvaluatedRememberMeTokenProvider;
import org.bremersee.spring.security.ldaptive.userdetails.LdaptivePwdLastSetRememberMeTokenProvider;
import org.bremersee.spring.security.ldaptive.userdetails.LdaptiveRememberMeTokenProvider;
import org.bremersee.spring.security.ldaptive.userdetails.LdaptiveUserDetails;
import org.ldaptive.ConnectionConfig;
import org.ldaptive.ConnectionFactory;
import org.ldaptive.DefaultConnectionFactory;
import org.mapstruct.Mapper;
import org.mapstruct.NullValueCheckStrategy;
import org.mapstruct.factory.Mappers;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.util.ClassUtils;
/**
* The ldaptive authentication auto-configuration.
*
* @author Christian Bremer
*/
@AutoConfiguration
@ConditionalOnClass(name = {
"org.ldaptive.ConnectionFactory",
"org.bremersee.ldaptive.LdaptiveTemplate",
"org.bremersee.spring.security.ldaptive.authentication.LdaptiveAuthenticationManager"
})
@ConditionalOnBean({ConnectionConfig.class})
@ConditionalOnProperty(prefix = "bremersee.authentication.ldaptive", name = "user-base-dn")
@AutoConfigureAfter({LdaptiveAutoConfiguration.class})
@EnableConfigurationProperties(AuthenticationProperties.class)
@Slf4j
public class LdaptiveAuthenticationAutoConfiguration {
private final LdaptiveAuthenticationProperties properties;
private final String rememberMeKey;
/**
* Instantiates a new ldaptive authentication autoconfiguration.
*
* @param properties the properties
*/
public LdaptiveAuthenticationAutoConfiguration(AuthenticationProperties properties) {
this.properties = LdaptivePropertiesMapper.map(properties);
this.rememberMeKey = properties.getRememberMe().getKey();
}
/**
* Init.
*/
@EventListener(ApplicationReadyEvent.class)
public void init() {
log.info("""
*********************************************************************************
* {}
* properties = {}
*********************************************************************************""",
ClassUtils.getUserClass(getClass()).getSimpleName(),
properties);
}
/**
* Creates ldaptive password encoder provider.
*
* @return the ldaptive password encoder provider
*/
@ConditionalOnMissingBean
@Bean
public LdaptivePasswordEncoderProvider ldaptivePasswordEncoderProvider() {
return LdaptivePasswordEncoderProvider.defaultProvider();
}
/**
* The ldaptive remember-me token provider.
*
* @param accountControlEvaluator the account control evaluator
* @return the ldaptive remember-me token provider
*/
@ConditionalOnMissingBean
@Bean
public LdaptiveRememberMeTokenProvider ldaptiveRememberMeTokenProvider(
ObjectProvider<AccountControlEvaluator> accountControlEvaluator) {
AccountControlEvaluator evaluator = accountControlEvaluator
.getIfAvailable(() -> properties.getAccountControlEvaluator().get());
if (!isEmpty(properties.getPasswordLastSetAttribute())) {
return new LdaptivePwdLastSetRememberMeTokenProvider(
evaluator, properties.getPasswordLastSetAttribute());
}
return new LdaptiveEvaluatedRememberMeTokenProvider(evaluator);
}
/**
* Creates ldaptive authentication manager.
*
* @param connectionConfig the connection config
* @param connectionFactoryProvider the connection factory provider
* @param ldaptiveTemplateProvider the ldaptive template provider
* @param ldaptivePasswordEncoderProvider the ldaptive password encoder provider
* @param ldaptiveRememberMeTokenProvider the ldaptive remember-me token provider
* @param emailToUsernameResolver the email to username resolver
* @param usernameToBindDnConverter the username to bind dn provider
* @param accountControlEvaluator the account control evaluator
* @param grantedAuthoritiesMapper the granted authorities mapper
* @param tokenConverter the token converter
* @return the ldaptive authentication manager
*/
@ConditionalOnWebApplication(type = Type.SERVLET)
@Bean(initMethod = "init")
public LdaptiveAuthenticationManager ldaptiveAuthenticationManager(
ConnectionConfig connectionConfig,
ObjectProvider<ConnectionFactory> connectionFactoryProvider,
ObjectProvider<LdaptiveTemplate> ldaptiveTemplateProvider,
LdaptivePasswordEncoderProvider ldaptivePasswordEncoderProvider,
LdaptiveRememberMeTokenProvider ldaptiveRememberMeTokenProvider,
ObjectProvider<EmailToUsernameResolver> emailToUsernameResolver,
ObjectProvider<UsernameToBindDnConverter> usernameToBindDnConverter,
ObjectProvider<AccountControlEvaluator> accountControlEvaluator,
ObjectProvider<GrantedAuthoritiesMapper> grantedAuthoritiesMapper,
ObjectProvider<Converter<LdaptiveUserDetails, LdaptiveAuthentication>> tokenConverter) {
LdaptiveAuthenticationManager manager = new LdaptiveAuthenticationManager(
getLdaptiveTemplate(connectionConfig, connectionFactoryProvider, ldaptiveTemplateProvider),
properties,
rememberMeKey);
manager.setPasswordEncoder(ldaptivePasswordEncoderProvider.get());
manager.setPasswordProvider(ldaptiveRememberMeTokenProvider);
emailToUsernameResolver.ifAvailable(manager::setEmailToUsernameResolver);
usernameToBindDnConverter.ifAvailable(manager::setUsernameToBindDnConverter);
accountControlEvaluator.ifAvailable(manager::setAccountControlEvaluator);
manager.setGrantedAuthoritiesMapper(getGrantedAuthoritiesMapper(grantedAuthoritiesMapper));
tokenConverter.ifAvailable(manager::setTokenConverter);
return manager;
}
/**
* Creates reactive ldaptive authentication manager.
*
* @param connectionConfig the connection config
* @param connectionFactoryProvider the connection factory provider
* @param ldaptiveTemplateProvider the ldaptive template provider
* @param ldaptivePasswordEncoderProvider the ldaptive password encoder provider
* @param ldaptiveRememberMeTokenProvider the ldaptive remember-me token provider
* @param emailToUsernameResolver the email to username resolver
* @param usernameToBindDnConverter the username to bind dn converter
* @param accountControlEvaluator the account control evaluator
* @param grantedAuthoritiesMapper the granted authorities mapper
* @param tokenConverter the token converter
* @return the reactive ldaptive authentication manager
*/
@ConditionalOnWebApplication(type = Type.REACTIVE)
@Bean
public ReactiveLdaptiveAuthenticationManager reactiveLdaptiveAuthenticationManager(
ConnectionConfig connectionConfig,
ObjectProvider<ConnectionFactory> connectionFactoryProvider,
ObjectProvider<LdaptiveTemplate> ldaptiveTemplateProvider,
LdaptivePasswordEncoderProvider ldaptivePasswordEncoderProvider,
LdaptiveRememberMeTokenProvider ldaptiveRememberMeTokenProvider,
ObjectProvider<EmailToUsernameResolver> emailToUsernameResolver,
ObjectProvider<UsernameToBindDnConverter> usernameToBindDnConverter,
ObjectProvider<AccountControlEvaluator> accountControlEvaluator,
ObjectProvider<GrantedAuthoritiesMapper> grantedAuthoritiesMapper,
ObjectProvider<Converter<LdaptiveUserDetails, LdaptiveAuthentication>> tokenConverter) {
return new ReactiveLdaptiveAuthenticationManager(
ldaptiveAuthenticationManager(
connectionConfig,
connectionFactoryProvider,
ldaptiveTemplateProvider,
ldaptivePasswordEncoderProvider,
ldaptiveRememberMeTokenProvider,
emailToUsernameResolver,
usernameToBindDnConverter,
accountControlEvaluator,
grantedAuthoritiesMapper,
tokenConverter));
}
private LdaptiveTemplate getLdaptiveTemplate(
ConnectionConfig connectionConfig,
ObjectProvider<ConnectionFactory> connectionFactoryProvider,
ObjectProvider<LdaptiveTemplate> ldaptiveTemplateProvider) {
LdaptiveTemplate ldaptiveTemplate = ldaptiveTemplateProvider.getIfAvailable();
if (nonNull(ldaptiveTemplate)) {
return ldaptiveTemplate;
}
ConnectionFactory connectionFactory = connectionFactoryProvider.getIfAvailable();
if (nonNull(connectionFactory)) {
return new LdaptiveTemplate(connectionFactory);
}
return new LdaptiveTemplate(new DefaultConnectionFactory(connectionConfig));
}
private GrantedAuthoritiesMapper getGrantedAuthoritiesMapper(
ObjectProvider<GrantedAuthoritiesMapper> grantedAuthoritiesMapper) {
return grantedAuthoritiesMapper.getIfAvailable(() -> new NormalizedGrantedAuthoritiesMapper(
properties.getDefaultRoles(),
properties.toRoleMappings(),
properties.getRolePrefix(),
properties.getRoleCaseTransformation(),
properties.toRoleStringReplacements()));
}
/**
* The interface Properties mapper.
*/
@Mapper(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
interface PropertiesMapper {
/**
* The constant INSTANCE.
*/
PropertiesMapper INSTANCE = Mappers.getMapper(PropertiesMapper.class);
/**
* Map ldaptive authentication properties.
*
* @param source the source
* @return the ldaptive authentication properties
*/
LdaptiveAuthenticationProperties map(AuthenticationProperties.LdaptiveProperties source);
}
}