package org.apereo.cas.config;
import org.apache.cxf.sts.STSPropertiesMBean;
import org.apache.cxf.sts.StaticSTSProperties;
import org.apache.cxf.sts.claims.ClaimsAttributeStatementProvider;
import org.apache.cxf.sts.claims.ClaimsManager;
import org.apache.cxf.sts.event.map.EventMapper;
import org.apache.cxf.sts.event.map.MapEventLogger;
import org.apache.cxf.sts.operation.TokenIssueOperation;
import org.apache.cxf.sts.operation.TokenValidateOperation;
import org.apache.cxf.sts.service.StaticService;
import org.apache.cxf.sts.token.delegation.SAMLDelegationHandler;
import org.apache.cxf.sts.token.delegation.TokenDelegationHandler;
import org.apache.cxf.sts.token.provider.DefaultConditionsProvider;
import org.apache.cxf.sts.token.provider.DefaultSubjectProvider;
import org.apache.cxf.sts.token.provider.SAMLTokenProvider;
import org.apache.cxf.sts.token.realm.RealmProperties;
import org.apache.cxf.sts.token.realm.Relationship;
import org.apache.cxf.sts.token.validator.SAMLTokenValidator;
import org.apache.cxf.sts.token.validator.TokenValidator;
import org.apache.cxf.sts.token.validator.X509TokenValidator;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.apache.cxf.ws.security.sts.provider.SecurityTokenServiceProvider;
import org.apache.cxf.ws.security.sts.provider.operation.IssueOperation;
import org.apache.cxf.ws.security.sts.provider.operation.ValidateOperation;
import org.apache.wss4j.dom.validate.Validator;
import org.apereo.cas.CipherExecutor;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer;
import org.apereo.cas.authentication.AuthenticationMetaDataPopulator;
import org.apereo.cas.authentication.AuthenticationServiceSelectionStrategy;
import org.apereo.cas.authentication.SecurityTokenServiceAuthenticationMetaDataPopulator;
import org.apereo.cas.authentication.SecurityTokenServiceClientBuilder;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.configuration.model.support.wsfed.WsFederationProperties;
import org.apereo.cas.services.ServicesManager;
import org.apereo.cas.support.claims.WrappingSecurityTokenServiceClaimsHandler;
import org.apereo.cas.support.realm.RealmPasswordVerificationCallbackHandler;
import org.apereo.cas.support.realm.UriRealmParser;
import org.apereo.cas.support.util.CryptoUtils;
import org.apereo.cas.support.validation.CipheredCredentialsValidator;
import org.apereo.cas.support.validation.SecurityTokenServiceCredentialCipherExecutor;
import org.apereo.cas.support.x509.X509TokenDelegationHandler;
import org.apereo.cas.ticket.DefaultSecurityTokenTicketFactory;
import org.apereo.cas.ticket.ExpirationPolicy;
import org.apereo.cas.ticket.SecurityTokenTicketFactory;
import org.apereo.cas.ticket.UniqueTicketIdGenerator;
import org.apereo.cas.util.DefaultUniqueTicketIdGenerator;
import org.apereo.cas.ws.idp.WSFederationConstants;
import org.opensaml.saml.saml2.core.NameID;
import org.springframework.beans.factory.BeanCreationException;
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.boot.web.servlet.ServletRegistrationBean;
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.ImportResource;
import javax.xml.ws.Provider;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* This is {@link CoreWsSecuritySecurityTokenServiceConfiguration}.
*
* @author Misagh Moayyed
* @since 5.1.0
*/
@Configuration("coreWsSecuritySecurityTokenServiceConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
@ImportResource(locations = {"classpath:jaxws-realms.xml", "classpath:META-INF/cxf/cxf.xml"})
public class CoreWsSecuritySecurityTokenServiceConfiguration implements AuthenticationEventExecutionPlanConfigurer {
@Autowired
@Qualifier("grantingTicketExpirationPolicy")
private ExpirationPolicy grantingTicketExpirationPolicy;
@Autowired
@Qualifier("wsFederationAuthenticationServiceSelectionStrategy")
private AuthenticationServiceSelectionStrategy wsFederationAuthenticationServiceSelectionStrategy;
@Autowired
@Qualifier("servicesManager")
private ServicesManager servicesManager;
@Autowired
private CasConfigurationProperties casProperties;
@Bean
public ServletRegistrationBean cxfServlet() {
final ServletRegistrationBean bean = new ServletRegistrationBean();
bean.setEnabled(true);
bean.setName("cxfServletSecurityTokenService");
bean.setServlet(new CXFServlet());
bean.setUrlMappings(Collections.singleton(WSFederationConstants.ENDPOINT_STS.concat("*")));
bean.setAsyncSupported(true);
return bean;
}
@Bean
public EventMapper loggerListener() {
return new EventMapper(new MapEventLogger());
}
@RefreshScope
@Bean
public List<TokenDelegationHandler> delegationHandlers() {
final List<TokenDelegationHandler> handlers = new ArrayList<>();
handlers.add(new SAMLDelegationHandler());
handlers.add(new X509TokenDelegationHandler());
return handlers;
}
@RefreshScope
@Bean
public Provider transportSTSProviderBean() {
try {
final SecurityTokenServiceProvider provider = new SecurityTokenServiceProvider();
provider.setIssueOperation(transportIssueDelegate());
provider.setValidateOperation(transportValidateDelegate());
return provider;
} catch (final Exception e) {
throw new BeanCreationException(e.getMessage(), e);
}
}
@RefreshScope
@Bean
public IssueOperation transportIssueDelegate() {
final WsFederationProperties.SecurityTokenService wsfed = casProperties.getAuthn().getWsfedIdP().getSts();
final WsFederationProperties.IdentityProvider idp = casProperties.getAuthn().getWsfedIdP().getIdp();
final ClaimsManager claimsManager = new ClaimsManager();
claimsManager.setClaimHandlers(Arrays.asList(new WrappingSecurityTokenServiceClaimsHandler(
idp.getRealmName(),
wsfed.getRealm().getIssuer())));
final TokenIssueOperation op = new TokenIssueOperation();
op.setTokenProviders(transportTokenProviders());
op.setServices(Arrays.asList(transportService()));
op.setStsProperties(transportSTSProperties());
op.setClaimsManager(claimsManager);
op.setTokenValidators(transportTokenValidators());
op.setEventListener(loggerListener());
op.setDelegationHandlers(delegationHandlers());
op.setEncryptIssuedToken(wsfed.isEncryptTokens());
return op;
}
@RefreshScope
@Bean
public ValidateOperation transportValidateDelegate() {
final TokenValidateOperation op = new TokenValidateOperation();
op.setTokenValidators(transportTokenValidators());
op.setStsProperties(transportSTSProperties());
op.setEventListener(loggerListener());
return op;
}
@RefreshScope
@Bean
public List transportTokenValidators() {
final List list = new ArrayList<>();
list.add(transportSamlTokenValidator());
list.add(new X509TokenValidator());
return list;
}
@RefreshScope
@Bean
public List transportTokenProviders() {
final List list = new ArrayList<>();
list.add(transportSamlTokenProvider());
return list;
}
@RefreshScope
@Bean
public RealmProperties casRealm() {
final WsFederationProperties.SecurityTokenService wsfed = casProperties.getAuthn().getWsfedIdP().getSts();
final WsFederationProperties.IdentityProvider idp = casProperties.getAuthn().getWsfedIdP().getIdp();
final RealmProperties realm = new RealmProperties();
realm.setIssuer(wsfed.getRealm().getIssuer());
final Properties p = CryptoUtils.getSecurityProperties(wsfed.getRealm().getKeystoreFile(),
wsfed.getRealm().getKeystorePassword(),
wsfed.getRealm().getKeystoreAlias());
realm.setSignatureCryptoProperties(p);
realm.setCallbackHandler(new RealmPasswordVerificationCallbackHandler(wsfed.getRealm().getKeyPassword()));
return realm;
}
@RefreshScope
@Bean
public Map<String, RealmProperties> realms() {
final WsFederationProperties.IdentityProvider idp = casProperties.getAuthn().getWsfedIdP().getIdp();
final Map<String, RealmProperties> realms = new HashMap<>();
realms.put(idp.getRealmName(), casRealm());
return realms;
}
@RefreshScope
@Bean
public SAMLTokenProvider transportSamlTokenProvider() {
final WsFederationProperties.SecurityTokenService wsfed = casProperties.getAuthn().getWsfedIdP().getSts();
final DefaultSubjectProvider s = new DefaultSubjectProvider();
switch (wsfed.getSubjectNameIdFormat().trim().toLowerCase()) {
case "email":
s.setSubjectNameIDFormat(NameID.EMAIL);
break;
case "entity":
s.setSubjectNameIDFormat(NameID.ENTITY);
break;
case "transient":
s.setSubjectNameIDFormat(NameID.TRANSIENT);
break;
case "unspecified":
default:
s.setSubjectNameIDFormat(NameID.UNSPECIFIED);
break;
}
final DefaultConditionsProvider c = new DefaultConditionsProvider();
c.setAcceptClientLifetime(true);
final SAMLTokenProvider provider = new SAMLTokenProvider();
provider.setAttributeStatementProviders(Arrays.asList(new ClaimsAttributeStatementProvider()));
provider.setRealmMap(realms());
provider.setConditionsProvider(c);
provider.setSubjectProvider(s);
return provider;
}
@RefreshScope
@Bean
public TokenValidator transportSamlTokenValidator() {
final WsFederationProperties.IdentityProvider idp = casProperties.getAuthn().getWsfedIdP().getIdp();
final SAMLTokenValidator v = new SAMLTokenValidator();
return v;
}
@RefreshScope
@Bean
public Validator transportUsernameTokenValidator() {
return new CipheredCredentialsValidator(securityTokenServiceCredentialCipherExecutor());
}
@RefreshScope
@Bean
public StaticService transportService() {
final StaticService s = new StaticService();
s.setEndpoints(Arrays.asList(".*"));
return s;
}
@RefreshScope
@ConditionalOnMissingBean(name = "transportSTSProperties")
@Bean
public STSPropertiesMBean transportSTSProperties() {
final WsFederationProperties.SecurityTokenService wsfed = casProperties.getAuthn().getWsfedIdP().getSts();
final WsFederationProperties.IdentityProvider idp = casProperties.getAuthn().getWsfedIdP().getIdp();
final StaticSTSProperties s = new StaticSTSProperties();
s.setIssuer(getClass().getSimpleName());
s.setRealmParser(new UriRealmParser(realms()));
s.setSignatureCryptoProperties(CryptoUtils.getSecurityProperties(wsfed.getSigningKeystoreFile(), wsfed.getSigningKeystorePassword()));
s.setEncryptionCryptoProperties(CryptoUtils.getSecurityProperties(wsfed.getEncryptionKeystoreFile(), wsfed.getEncryptionKeystorePassword()));
final Relationship rel = new Relationship();
rel.setType(Relationship.FED_TYPE_IDENTITY);
rel.setSourceRealm(idp.getRealmName());
rel.setTargetRealm(idp.getRealmName());
s.setRelationships(Arrays.asList(rel));
return s;
}
@Bean
@RefreshScope
public SecurityTokenServiceClientBuilder securityTokenServiceClientBuilder() {
return new SecurityTokenServiceClientBuilder(casProperties.getAuthn().getWsfedIdP(),
casProperties.getServer().getPrefix());
}
@ConditionalOnMissingBean(name = "securityTokenServiceAuthenticationMetaDataPopulator")
@Bean
@RefreshScope
public AuthenticationMetaDataPopulator securityTokenServiceAuthenticationMetaDataPopulator() {
return new SecurityTokenServiceAuthenticationMetaDataPopulator(servicesManager,
wsFederationAuthenticationServiceSelectionStrategy, securityTokenServiceCredentialCipherExecutor(),
securityTokenServiceClientBuilder());
}
@RefreshScope
@Bean
public CipherExecutor securityTokenServiceCredentialCipherExecutor() {
final WsFederationProperties.SecurityTokenService wsfed = casProperties.getAuthn().getWsfedIdP().getSts();
return new SecurityTokenServiceCredentialCipherExecutor(wsfed.getEncryptionKey(), wsfed.getSigningKey());
}
@Bean
@RefreshScope
public SecurityTokenTicketFactory securityTokenTicketFactory() {
return new DefaultSecurityTokenTicketFactory(securityTokenTicketIdGenerator(), grantingTicketExpirationPolicy);
}
@ConditionalOnMissingBean(name = "securityTokenTicketIdGenerator")
@Bean
@RefreshScope
public UniqueTicketIdGenerator securityTokenTicketIdGenerator() {
return new DefaultUniqueTicketIdGenerator();
}
@Override
public void configureAuthenticationExecutionPlan(final AuthenticationEventExecutionPlan plan) {
plan.registerMetadataPopulator(securityTokenServiceAuthenticationMetaDataPopulator());
}
}