Skip to content

Commit

Permalink
Adding temporary classes to resolve NoSuchMethodError for SAML2Config…
Browse files Browse the repository at this point in the history
…uration.setCertificateNameToAppend
  • Loading branch information
ilgrosso committed Jun 22, 2023
1 parent 302691d commit 5e3c844
Show file tree
Hide file tree
Showing 2 changed files with 267 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import io.swagger.v3.oas.models.security.SecurityScheme;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
Expand Down Expand Up @@ -59,6 +60,7 @@
import org.apache.syncope.wa.starter.mapping.TicketExpirationMapper;
import org.apache.syncope.wa.starter.mapping.TimeBasedAccessMapper;
import org.apache.syncope.wa.starter.oidc.WAOIDCJWKSGeneratorService;
import org.apache.syncope.wa.starter.pac4j.WADelegatedClientFactory;
import org.apache.syncope.wa.starter.pac4j.saml.WASAML2ClientCustomizer;
import org.apache.syncope.wa.starter.saml.idp.WASamlIdPCasEventListener;
import org.apache.syncope.wa.starter.saml.idp.WASamlIdPObjectSigner;
Expand All @@ -71,19 +73,24 @@
import org.apereo.cas.adaptors.u2f.storage.U2FDeviceRepository;
import org.apereo.cas.audit.AuditTrailExecutionPlanConfigurer;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
import org.apereo.cas.authentication.CasSSLContext;
import org.apereo.cas.authentication.MultifactorAuthenticationProvider;
import org.apereo.cas.authentication.surrogate.SurrogateAuthenticationService;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.configuration.model.support.mfa.gauth.LdapGoogleAuthenticatorMultifactorProperties;
import org.apereo.cas.configuration.model.support.mfa.u2f.U2FCoreMultifactorAuthenticationProperties;
import org.apereo.cas.configuration.model.support.pac4j.Pac4jDelegatedAuthenticationCoreProperties;
import org.apereo.cas.configuration.support.Beans;
import org.apereo.cas.gauth.credential.LdapGoogleAuthenticatorTokenCredentialRepository;
import org.apereo.cas.oidc.jwks.generator.OidcJsonWebKeystoreGeneratorService;
import org.apereo.cas.otp.repository.credentials.OneTimeTokenCredentialRepository;
import org.apereo.cas.services.ServiceRegistryExecutionPlanConfigurer;
import org.apereo.cas.services.ServiceRegistryListener;
import org.apereo.cas.support.events.CasEventRepository;
import org.apereo.cas.support.events.CasEventRepositoryFilter;
import org.apereo.cas.support.pac4j.authentication.clients.DelegatedClientFactory;
import org.apereo.cas.support.pac4j.authentication.clients.DelegatedClientFactoryCustomizer;
import org.apereo.cas.support.pac4j.authentication.clients.RestfulDelegatedClientFactory;
import org.apereo.cas.support.pac4j.authentication.handler.support.DelegatedClientAuthenticationHandler;
import org.apereo.cas.support.saml.idp.SamlIdPCasEventListener;
import org.apereo.cas.support.saml.idp.metadata.generator.SamlIdPMetadataGenerator;
Expand All @@ -98,6 +105,8 @@
import org.ldaptive.ConnectionFactory;
import org.opensaml.saml.metadata.resolver.MetadataResolver;
import org.pac4j.core.client.Client;
import org.pac4j.core.client.IndirectClient;
import org.pac4j.saml.store.SAMLMessageStoreFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
Expand All @@ -107,6 +116,7 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;

@Configuration(proxyBeanMethods = false)
public class WAContext {
Expand Down Expand Up @@ -196,6 +206,38 @@ public ClientAppMapper saml2SPClientAppTOMapper() {
return new SAML2SPClientAppTOMapper();
}

@SuppressWarnings("rawtypes")
@RefreshScope(proxyMode = ScopedProxyMode.DEFAULT)
@Bean
public DelegatedClientFactory pac4jDelegatedClientFactory(
@Qualifier(DelegatedClientFactory.BEAN_NAME_SAML2_CLIENT_MESSAGE_FACTORY)
final ObjectProvider<SAMLMessageStoreFactory> samlMessageStoreFactory,
final CasConfigurationProperties casProperties,
final ObjectProvider<List<DelegatedClientFactoryCustomizer>> customizerList,
@Qualifier(CasSSLContext.BEAN_NAME)
final CasSSLContext casSslContext) {

Pac4jDelegatedAuthenticationCoreProperties core = casProperties.getAuthn().getPac4j().getCore();
Cache<String, Collection<IndirectClient>> clientsCache = Caffeine.newBuilder().
maximumSize(core.getCacheSize()).
expireAfterAccess(Beans.newDuration(core.getCacheDuration())).
build();

List<DelegatedClientFactoryCustomizer> customizers = Optional.ofNullable(customizerList.getIfAvailable()).
map(result -> {
AnnotationAwareOrderComparator.sortIfNecessary(result);
return result;
}).orElseGet(() -> new ArrayList<>(0));

if (StringUtils.isNotBlank(casProperties.getAuthn().getPac4j().getRest().getUrl())) {
return new RestfulDelegatedClientFactory(
customizers, casSslContext, casProperties, samlMessageStoreFactory, clientsCache);
}

return new WADelegatedClientFactory(
casProperties, customizers, casSslContext, samlMessageStoreFactory, clientsCache);
}

@RefreshScope(proxyMode = ScopedProxyMode.DEFAULT)
@ConditionalOnMissingBean
@Bean
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.syncope.wa.starter.pac4j;

import org.apereo.cas.authentication.CasSSLContext;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.configuration.support.Beans;
import org.apereo.cas.util.CollectionUtils;
import org.apereo.cas.util.ResourceUtils;
import org.apereo.cas.util.function.FunctionUtils;
import org.apereo.cas.util.spring.SpringExpressionLanguageValueResolver;

import com.github.benmanes.caffeine.cache.Cache;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.pac4j.core.client.IndirectClient;
import org.pac4j.core.profile.converter.AttributeConverter;
import org.pac4j.saml.client.SAML2Client;
import org.pac4j.saml.config.SAML2Configuration;
import org.pac4j.saml.metadata.DefaultSAML2MetadataSigner;
import org.pac4j.saml.metadata.SAML2ServiceProviderRequestedAttribute;
import org.pac4j.saml.store.EmptyStoreFactory;
import org.pac4j.saml.store.HttpSessionStoreFactory;
import org.pac4j.saml.store.SAMLMessageStoreFactory;
import org.springframework.beans.factory.ObjectProvider;

import java.time.Period;
import java.util.Collection;
import java.util.Locale;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apereo.cas.support.pac4j.authentication.clients.DefaultDelegatedClientFactory;
import org.apereo.cas.support.pac4j.authentication.clients.DelegatedClientFactoryCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class WADelegatedClientFactory extends DefaultDelegatedClientFactory {

private static final Logger LOG = LoggerFactory.getLogger(WADelegatedClientFactory.class);

private final CasSSLContext casSSLContext;

private final ObjectProvider<SAMLMessageStoreFactory> samlMessageStoreFactory;

@SuppressWarnings("rawtypes")
public WADelegatedClientFactory(
final CasConfigurationProperties casProperties,
final Collection<DelegatedClientFactoryCustomizer> customizers,
final CasSSLContext casSSLContext,
final ObjectProvider<SAMLMessageStoreFactory> samlMessageStoreFactory,
final Cache<String, Collection<IndirectClient>> clientsCache) {

super(casProperties, customizers, casSSLContext, samlMessageStoreFactory, clientsCache);
this.casSSLContext = casSSLContext;
this.samlMessageStoreFactory = samlMessageStoreFactory;
}

@Override
protected Collection<IndirectClient> buildSaml2IdentityProviders(final CasConfigurationProperties casProperties) {
var pac4jProperties = casProperties.getAuthn().getPac4j();
return pac4jProperties
.getSaml()
.stream()
.filter(saml -> saml.isEnabled()
&& StringUtils.isNotBlank(saml.getKeystorePath())
&& StringUtils.isNotBlank(saml.getMetadata().getIdentityProviderMetadataPath())
&& StringUtils.isNotBlank(saml.getServiceProviderEntityId()))
.map(saml -> {
var keystorePath = SpringExpressionLanguageValueResolver.getInstance().
resolve(saml.getKeystorePath());
var identityProviderMetadataPath = SpringExpressionLanguageValueResolver.getInstance()
.resolve(saml.getMetadata().getIdentityProviderMetadataPath());

var cfg = new SAML2Configuration(keystorePath, saml.getKeystorePassword(),
saml.getPrivateKeyPassword(), identityProviderMetadataPath);
cfg.setForceKeystoreGeneration(saml.isForceKeystoreGeneration());

FunctionUtils.doIf(saml.getCertificateExpirationDays() > 0,
__ -> cfg.setCertificateExpirationPeriod(
Period.ofDays(saml.getCertificateExpirationDays()))).accept(saml);
FunctionUtils.doIfNotNull(saml.getResponseBindingType(), cfg::setResponseBindingType);
FunctionUtils.doIfNotNull(saml.getCertificateSignatureAlg(), cfg::setCertificateSignatureAlg);

cfg.setPartialLogoutTreatedAsSuccess(saml.isPartialLogoutAsSuccess());
cfg.setResponseDestinationAttributeMandatory(saml.isResponseDestinationMandatory());
cfg.setSupportedProtocols(saml.getSupportedProtocols());

FunctionUtils.doIfNotBlank(saml.getRequestInitiatorUrl(), __ -> cfg.setRequestInitiatorUrl(saml.
getRequestInitiatorUrl()));
FunctionUtils.doIfNotBlank(saml.getSingleLogoutServiceUrl(), __ -> cfg.setSingleSignOutServiceUrl(
saml.getSingleLogoutServiceUrl()));
FunctionUtils.doIfNotBlank(saml.getLogoutResponseBindingType(), __ -> cfg.
setSpLogoutResponseBindingType(saml.getLogoutResponseBindingType()));

cfg.setCertificateNameToAppend(
StringUtils.defaultIfBlank(saml.getCertificateNameToAppend(), saml.getClientName()));
cfg.setMaximumAuthenticationLifetime(
Beans.newDuration(saml.getMaximumAuthenticationLifetime()).toSeconds());
var serviceProviderEntityId = SpringExpressionLanguageValueResolver.getInstance().
resolve(saml.getServiceProviderEntityId());
cfg.setServiceProviderEntityId(serviceProviderEntityId);

FunctionUtils.doIfNotNull(saml.getMetadata().getServiceProvider().getFileSystem().getLocation(),
location -> {
var resource = ResourceUtils.getRawResourceFrom(location);
cfg.setServiceProviderMetadataResource(resource);
});

cfg.setAuthnRequestBindingType(saml.getDestinationBinding());
cfg.setSpLogoutRequestBindingType(saml.getLogoutRequestBinding());
cfg.setForceAuth(saml.isForceAuth());
cfg.setPassive(saml.isPassive());
cfg.setSignMetadata(saml.isSignServiceProviderMetadata());
cfg.setMetadataSigner(new DefaultSAML2MetadataSigner(cfg));
cfg.setAuthnRequestSigned(saml.isSignAuthnRequest());
cfg.setSpLogoutRequestSigned(saml.isSignServiceProviderLogoutRequest());
cfg.setAcceptedSkew(Beans.newDuration(saml.getAcceptedSkew()).toSeconds());
cfg.setSslSocketFactory(casSSLContext.getSslContext().getSocketFactory());
cfg.setHostnameVerifier(casSSLContext.getHostnameVerifier());

FunctionUtils.doIfNotBlank(saml.getPrincipalIdAttribute(), __ -> cfg.setAttributeAsId(saml.
getPrincipalIdAttribute()));
FunctionUtils.doIfNotBlank(saml.getNameIdAttribute(), __ -> cfg.setNameIdAttribute(saml.
getNameIdAttribute()));

cfg.setWantsAssertionsSigned(saml.isWantsAssertionsSigned());
cfg.setWantsResponsesSigned(saml.isWantsResponsesSigned());
cfg.setAllSignatureValidationDisabled(saml.isAllSignatureValidationDisabled());
cfg.setUseNameQualifier(saml.isUseNameQualifier());
cfg.setAttributeConsumingServiceIndex(saml.getAttributeConsumingServiceIndex());

Optional.ofNullable(samlMessageStoreFactory.getIfAvailable())
.ifPresentOrElse(cfg::setSamlMessageStoreFactory, () -> {
FunctionUtils.doIf("EMPTY".equalsIgnoreCase(saml.getMessageStoreFactory()),
ig -> cfg.setSamlMessageStoreFactory(new EmptyStoreFactory())).accept(saml);
FunctionUtils.doIf("SESSION".equalsIgnoreCase(saml.getMessageStoreFactory()),
ig -> cfg.setSamlMessageStoreFactory(new HttpSessionStoreFactory())).
accept(saml);
if (saml.getMessageStoreFactory().contains(".")) {
FunctionUtils.doAndHandle(__ -> {
var clazz = ClassUtils.getClass(
getClass().getClassLoader(),
saml.getMessageStoreFactory());
var factory = (SAMLMessageStoreFactory) clazz.getDeclaredConstructor().
newInstance();
cfg.setSamlMessageStoreFactory(factory);
});
}
});

FunctionUtils.doIf(saml.getAssertionConsumerServiceIndex() >= 0,
__ -> cfg.setAssertionConsumerServiceIndex(saml.getAssertionConsumerServiceIndex())).
accept(saml);

if (!saml.getAuthnContextClassRef().isEmpty()) {
cfg.setComparisonType(saml.getAuthnContextComparisonType().toUpperCase(Locale.ENGLISH));
cfg.setAuthnContextClassRefs(saml.getAuthnContextClassRef());
}

FunctionUtils.doIfNotBlank(saml.getNameIdPolicyFormat(), __ -> cfg.setNameIdPolicyFormat(saml.
getNameIdPolicyFormat()));

if (!saml.getRequestedAttributes().isEmpty()) {
saml.getRequestedAttributes().stream()
.map(attribute -> new SAML2ServiceProviderRequestedAttribute(attribute.getName(),
attribute.getFriendlyName(),
attribute.getNameFormat(), attribute.isRequired()))
.forEach(attribute -> cfg.getRequestedServiceProviderAttributes().add(attribute));
}

if (!saml.getBlockedSignatureSigningAlgorithms().isEmpty()) {
cfg.setBlackListedSignatureSigningAlgorithms(saml.getBlockedSignatureSigningAlgorithms());
}
if (!saml.getSignatureAlgorithms().isEmpty()) {
cfg.setSignatureAlgorithms(saml.getSignatureAlgorithms());
}
if (!saml.getSignatureReferenceDigestMethods().isEmpty()) {
cfg.setSignatureReferenceDigestMethods(saml.getSignatureReferenceDigestMethods());
}

FunctionUtils.doIfNotBlank(
saml.getSignatureCanonicalizationAlgorithm(),
__ -> cfg.setSignatureCanonicalizationAlgorithm(
saml.getSignatureCanonicalizationAlgorithm()));
cfg.setProviderName(saml.getProviderName());
cfg.setNameIdPolicyAllowCreate(saml.getNameIdPolicyAllowCreate().toBoolean());

if (StringUtils.isNotBlank(saml.getSaml2AttributeConverter())) {
FunctionUtils.doAndHandle(__ -> {
var clazz = ClassUtils.getClass(
getClass().getClassLoader(), saml.getSaml2AttributeConverter());
var converter = (AttributeConverter) clazz.getDeclaredConstructor().newInstance();
cfg.setSamlAttributeConverter(converter);
});
}

var mappedAttributes = saml.getMappedAttributes();
if (!mappedAttributes.isEmpty()) {
cfg.setMappedAttributes(CollectionUtils.convertDirectedListToMap(mappedAttributes));
}

var client = new SAML2Client(cfg);
configureClient(client, saml, casProperties);

LOG.debug("Created delegated client [{}]", client);
return client;
}).collect(Collectors.toList());
}
}

0 comments on commit 5e3c844

Please sign in to comment.