package org.apereo.cas.trusted.config;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.apereo.cas.CipherExecutor;
import org.apereo.cas.authentication.PseudoPlatformTransactionManager;
import org.apereo.cas.config.CasCoreUtilConfiguration;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.trusted.authentication.MultifactorAuthenticationTrustCipherExecutor;
import org.apereo.cas.trusted.authentication.api.MultifactorAuthenticationTrustRecord;
import org.apereo.cas.trusted.authentication.api.MultifactorAuthenticationTrustStorage;
import org.apereo.cas.trusted.authentication.storage.InMemoryMultifactorAuthenticationTrustStorage;
import org.apereo.cas.trusted.authentication.storage.MultifactorAuthenticationTrustStorageCleaner;
import org.apereo.cas.trusted.web.MultifactorAuthenticationTrustController;
import org.apereo.cas.trusted.web.flow.MultifactorAuthenticationSetTrustAction;
import org.apereo.cas.trusted.web.flow.MultifactorAuthenticationVerifyTrustAction;
import org.apereo.cas.util.cipher.NoOpCipherExecutor;
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.AutoConfigureAfter;
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.context.annotation.Lazy;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.webflow.execution.Action;
import java.io.Serializable;
/**
* This is {@link MultifactorAuthnTrustConfiguration}.
*
* @author Misagh Moayyed
* @since 5.0.0
*/
@Configuration("multifactorAuthnTrustConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
@AutoConfigureAfter(CasCoreUtilConfiguration.class)
public class MultifactorAuthnTrustConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(MultifactorAuthnTrustConfiguration.class);
private static final int INITIAL_CACHE_SIZE = 50;
private static final long MAX_CACHE_SIZE = 1000;
@Autowired
private CasConfigurationProperties casProperties;
@Bean
@RefreshScope
public Action mfaSetTrustAction(@Qualifier("mfaTrustEngine") final MultifactorAuthenticationTrustStorage storage) {
return new MultifactorAuthenticationSetTrustAction(storage, casProperties.getAuthn().getMfa().getTrusted());
}
@Bean
public MultifactorAuthenticationTrustController mfaTrustController(@Qualifier("mfaTrustEngine") final MultifactorAuthenticationTrustStorage storage) {
return new MultifactorAuthenticationTrustController(storage, casProperties.getAuthn().getMfa().getTrusted());
}
@Bean
@RefreshScope
public Action mfaVerifyTrustAction(@Qualifier("mfaTrustEngine") final MultifactorAuthenticationTrustStorage storage) {
return new MultifactorAuthenticationVerifyTrustAction(storage, casProperties.getAuthn().getMfa().getTrusted());
}
@ConditionalOnMissingBean(name = "mfaTrustEngine")
@Bean
@RefreshScope
public MultifactorAuthenticationTrustStorage mfaTrustEngine() {
final LoadingCache<String, MultifactorAuthenticationTrustRecord> storage = CacheBuilder.newBuilder()
.initialCapacity(INITIAL_CACHE_SIZE)
.maximumSize(MAX_CACHE_SIZE)
.recordStats()
.expireAfterWrite(casProperties.getAuthn().getMfa().getTrusted().getExpiration(),
casProperties.getAuthn().getMfa().getTrusted().getTimeUnit())
.build(new CacheLoader<String, MultifactorAuthenticationTrustRecord>() {
@Override
public MultifactorAuthenticationTrustRecord load(final String s) throws Exception {
LOGGER.error("Load operation of the cache is not supported.");
return null;
}
});
final InMemoryMultifactorAuthenticationTrustStorage m = new InMemoryMultifactorAuthenticationTrustStorage(storage);
m.setCipherExecutor(mfaTrustCipherExecutor());
return m;
}
@ConditionalOnMissingBean(name = "transactionManagerMfaAuthnTrust")
@Bean
public PlatformTransactionManager transactionManagerMfaAuthnTrust() {
return new PseudoPlatformTransactionManager();
}
@Bean
@RefreshScope
public CipherExecutor<Serializable, String> mfaTrustCipherExecutor() {
if (casProperties.getAuthn().getMfa().getTrusted().isCipherEnabled()) {
return new MultifactorAuthenticationTrustCipherExecutor(
casProperties.getAuthn().getMfa().getTrusted().getEncryptionKey(),
casProperties.getAuthn().getMfa().getTrusted().getSigningKey());
}
LOGGER.info("Multifactor trusted authentication record encryption/signing is turned off and "
+ "MAY NOT be safe in a production environment. "
+ "Consider using other choices to handle encryption, signing and verification of "
+ "trusted authentication records for MFA");
return NoOpCipherExecutor.getInstance();
}
@ConditionalOnMissingBean(name = "mfaTrustStorageCleaner")
@Bean
@Lazy
public MultifactorAuthenticationTrustStorageCleaner mfaTrustStorageCleaner(
@Qualifier("mfaTrustEngine") final MultifactorAuthenticationTrustStorage storage) {
return new MultifactorAuthenticationTrustStorageCleaner(casProperties.getAuthn().getMfa().getTrusted(), storage);
}
}