package org.apereo.cas.pm.config;
import org.apache.commons.lang3.StringUtils;
import org.apereo.cas.CipherExecutor;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.configuration.model.support.pm.PasswordManagementProperties;
import org.apereo.cas.configuration.support.Beans;
import org.apereo.cas.pm.NoOpPasswordManagementService;
import org.apereo.cas.pm.PasswordManagementService;
import org.apereo.cas.pm.PasswordResetTokenCipherExecutor;
import org.apereo.cas.pm.PasswordValidator;
import org.apereo.cas.pm.jdbc.JdbcPasswordManagementService;
import org.apereo.cas.pm.ldap.LdapPasswordManagementService;
import org.apereo.cas.pm.rest.RestPasswordManagementService;
import org.apereo.cas.pm.web.flow.InitPasswordChangeAction;
import org.apereo.cas.pm.web.flow.InitPasswordResetAction;
import org.apereo.cas.pm.web.flow.PasswordChangeAction;
import org.apereo.cas.pm.web.flow.PasswordManagementWebflowConfigurer;
import org.apereo.cas.pm.web.flow.SendPasswordResetInstructionsAction;
import org.apereo.cas.pm.web.flow.VerifyPasswordResetRequestAction;
import org.apereo.cas.pm.web.flow.VerifySecurityQuestionsAction;
import org.apereo.cas.util.cipher.NoOpCipherExecutor;
import org.apereo.cas.util.io.CommunicationsManager;
import org.apereo.cas.web.flow.CasWebflowConfigurer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
import org.springframework.webflow.execution.Action;
import org.springframework.webflow.executor.FlowExecutor;
import org.springframework.webflow.mvc.servlet.FlowHandler;
import org.springframework.webflow.mvc.servlet.FlowHandlerAdapter;
import javax.annotation.PostConstruct;
import java.io.Serializable;
/**
* This is {@link PasswordManagementConfiguration}.
*
* @author Misagh Moayyed
* @since 5.0.0
*/
@Configuration("passwordManagementConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
public class PasswordManagementConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(PasswordManagementConfiguration.class);
@Autowired
private CasConfigurationProperties casProperties;
@Autowired
@Qualifier("communicationsManager")
private CommunicationsManager communicationsManager;
@Autowired
private FlowBuilderServices flowBuilderServices;
@Autowired
@Qualifier("loginFlowRegistry")
private FlowDefinitionRegistry loginFlowDefinitionRegistry;
@Autowired
@Qualifier("loginFlowExecutor")
private FlowExecutor loginFlowExecutor;
@RefreshScope
@Bean
public HandlerAdapter passwordResetHandlerAdapter() {
final FlowHandlerAdapter handler = new FlowHandlerAdapter() {
@Override
public boolean supports(final Object handler) {
return super.supports(handler) && ((FlowHandler) handler)
.getFlowId().equals(PasswordManagementWebflowConfigurer.FLOW_ID_PASSWORD_RESET);
}
};
handler.setFlowExecutor(loginFlowExecutor);
return handler;
}
@RefreshScope
@Bean
public Action initPasswordChangeAction() {
return new InitPasswordChangeAction();
}
@Autowired
@RefreshScope
@Bean
public Action initPasswordResetAction(@Qualifier("passwordChangeService")
final PasswordManagementService passwordManagementService) {
return new InitPasswordResetAction(passwordManagementService);
}
@RefreshScope
@Bean
public Action passwordChangeAction() {
return new PasswordChangeAction(passwordChangeService());
}
@RefreshScope
@Bean
public CipherExecutor<Serializable, String> passwordManagementCipherExecutor() {
final PasswordManagementProperties pm = casProperties.getAuthn().getPm();
if (pm.isEnabled()) {
return new PasswordResetTokenCipherExecutor(
pm.getReset().getSecurity().getEncryptionKey(),
pm.getReset().getSecurity().getSigningKey());
}
return NoOpCipherExecutor.getInstance();
}
@ConditionalOnMissingBean(name = "passwordChangeService")
@RefreshScope
@Bean
public PasswordManagementService passwordChangeService() {
final PasswordManagementProperties pm = casProperties.getAuthn().getPm();
if (casProperties.getAuthn().getPm().isEnabled()) {
if (StringUtils.isNotBlank(pm.getLdap().getLdapUrl())
&& StringUtils.isNotBlank(pm.getLdap().getBaseDn())
&& StringUtils.isNotBlank(pm.getLdap().getUserFilter())) {
return new LdapPasswordManagementService(passwordManagementCipherExecutor(),
casProperties.getServer().getPrefix(),
casProperties.getAuthn().getPm());
}
if (StringUtils.isNotBlank(pm.getJdbc().getSqlChangePassword())
&& StringUtils.isNotBlank(pm.getJdbc().getSqlFindEmail())
&& StringUtils.isNotBlank(pm.getJdbc().getUrl())
&& StringUtils.isNotBlank(pm.getJdbc().getUser())) {
return new JdbcPasswordManagementService(passwordManagementCipherExecutor(),
casProperties.getServer().getPrefix(),
casProperties.getAuthn().getPm(),
Beans.newDataSource(casProperties.getAuthn().getPm().getJdbc()));
}
if (StringUtils.isNotBlank(pm.getRest().getEndpointUrlChange())
&& StringUtils.isNotBlank(pm.getRest().getEndpointUrlEmail())) {
return new RestPasswordManagementService(passwordManagementCipherExecutor(),
casProperties.getServer().getPrefix(),
new RestTemplate(),
casProperties.getAuthn().getPm());
}
}
if (pm.isEnabled()) {
LOGGER.warn("No backend is configured to handle the account update and password service operations. Verify your settings");
}
return new NoOpPasswordManagementService(passwordManagementCipherExecutor(),
casProperties.getServer().getPrefix(),
casProperties.getAuthn().getPm());
}
@Autowired
@Bean
public Action sendPasswordResetInstructionsAction(@Qualifier("passwordChangeService")
final PasswordManagementService passwordManagementService) {
return new SendPasswordResetInstructionsAction(communicationsManager, passwordManagementService);
}
@Bean
public Action verifyPasswordResetRequestAction(@Qualifier("passwordChangeService")
final PasswordManagementService passwordManagementService) {
return new VerifyPasswordResetRequestAction(passwordManagementService);
}
@Bean
public Action verifySecurityQuestionsAction(@Qualifier("passwordChangeService")
final PasswordManagementService passwordManagementService) {
return new VerifySecurityQuestionsAction(passwordManagementService);
}
@ConditionalOnMissingBean(name = "passwordManagementWebflowConfigurer")
@RefreshScope
@Bean
public CasWebflowConfigurer passwordManagementWebflowConfigurer() {
return new PasswordManagementWebflowConfigurer(flowBuilderServices, loginFlowDefinitionRegistry);
}
@RefreshScope
@ConditionalOnMissingBean(name = "passwordValidator")
@Bean
public PasswordValidator passwordValidator() {
return new PasswordValidator();
}
@PostConstruct
public void init() {
final PasswordManagementProperties pm = casProperties.getAuthn().getPm();
if (pm.isEnabled()) {
if (!communicationsManager.isMailSenderDefined()) {
LOGGER.warn("CAS is unable to send password-reset emails given no settings are defined to account for email servers, etc");
}
if (!communicationsManager.isSmsSenderDefined()) {
LOGGER.warn("CAS is unable to send password-reset sms messages given no settings are defined to account for sms providers, etc");
}
}
}
}