Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ RUN ./gradlew --no-daemon dependencies
COPY ./app/src /cas-overlay/src/
RUN ./gradlew clean build --parallel --no-daemon

FROM registry.cloudogu.com/official/base:3.23.3-4 AS tomcat
FROM registry.cloudogu.com/official/base:3.23.4-1 AS tomcat

ARG TOMCAT_MAJOR_VERSION
ARG TOMCAT_VERSION
Expand All @@ -38,9 +38,9 @@ RUN apk update && apk add wget && wget -O "apache-tomcat-${TOMCAT_VERSION}.tar.
&& rm "apache-tomcat-${TOMCAT_VERSION}.tar"

# registry.cloudogu.com/official/cas
FROM registry.cloudogu.com/official/java:21.0.10-4 AS cas
FROM registry.cloudogu.com/official/java:21.0.10-7 AS cas
LABEL NAME="official/cas" \
VERSION="7.2.7-16" \
VERSION="7.3.6-0" \
maintainer="hello@cloudogu.com"

ARG TOMCAT_VERSION
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
MAKEFILES_VERSION=10.7.0
MAKEFILES_VERSION=10.9.0

.DEFAULT_GOAL:=dogu-release

Expand Down
2 changes: 1 addition & 1 deletion app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Generic CAS WAR overlay to exercise the latest versions of CAS. This overlay cou

# Versions

- CAS `7.2.7`
- CAS `7.3.6`
- JDK `21`

# Overview
Expand Down
1 change: 0 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -221,4 +221,3 @@ configurations.all {
exclude group: "com.sun.xml.ws", module: "jaxws-rt"
exclude group: "org.apache.tomcat.embed"
}

6 changes: 4 additions & 2 deletions app/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# CAS server version
cas.version=7.2.7
cas.version=7.3.6

###############################
# Spring versions
Expand All @@ -11,6 +11,8 @@ springBootVersion=3.4.6
###############################
tomcatVersion=10.1.43

commons-lang3.version=3.19.0

# Use -jetty, -undertow to other containers
# Or blank if you want to deploy to an external container
appServer=-tomcat
Expand All @@ -21,7 +23,7 @@ gradleFreeFairPluginVersion=8.6
gradleDependencyManagementPluginVersion=1.1.5

# The version of this overlay project
version=7.2.7
version=7.3.6
group=org.apereo.cas
artifactId=cas-overlay
sourceCompatibility=21
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import org.apereo.cas.authentication.LdapAuthenticationHandler;
import org.apereo.cas.authentication.principal.Principal;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.services.ServicesManager;
import org.ldaptive.LdapEntry;
import org.ldaptive.auth.Authenticator;

Expand All @@ -28,18 +27,17 @@ public class CesGroupAwareLdapAuthenticationHandler extends LdapAuthenticationHa
* Creates a new authentication handler that delegates to the given authenticator.
*
* @param name the name
* @param servicesManager the services manager
* @param principalFactory the principal factory
* @param authenticator Ldaptive authenticator component.
* @param strategy the strategy
* @param groupResolver the resolver for resolving groups
*/
public CesGroupAwareLdapAuthenticationHandler(String name, ServicesManager servicesManager,
public CesGroupAwareLdapAuthenticationHandler(String name,
PrincipalFactory principalFactory,
Authenticator authenticator,
AuthenticationPasswordPolicyHandlingStrategy strategy,
GroupResolver groupResolver) {
super(name, servicesManager, principalFactory, 0, authenticator, strategy);
super(name, principalFactory, 0, authenticator, strategy);

this.groupResolver = groupResolver;
LOGGER.trace("{} created with group attribute {} and group resolver {}",
Expand Down
8 changes: 2 additions & 6 deletions app/src/main/java/de/triology/cas/ldap/LdapConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.configuration.model.support.ldap.LdapAuthenticationProperties;
import org.apereo.cas.configuration.model.support.ldap.LdapPasswordPolicyProperties;
import org.apereo.cas.services.ServicesManager;
import org.apereo.cas.util.CollectionUtils;
import org.apereo.cas.util.LdapUtils;
import org.ldaptive.ConnectionFactory;
Expand Down Expand Up @@ -74,15 +73,13 @@ ConnectionFactory searchPooledLdapConnectionFactory(CasConfigurationProperties p
@Bean
public AuthenticationHandler cesGroupAwareLdapAuthenticationHandler(CasConfigurationProperties casProperties,
ConfigurableApplicationContext applicationContext,
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
ServicesManager servicesManager,
CombinedGroupResolver combinedGroupResolver) {
LdapAuthenticationProperties ldapProperties = casProperties.getAuthn().getLdap().getFirst();

Multimap<String, Object> multiMapAttributes = createPrincipalAttributes(ldapProperties);
Authenticator authenticator = createAuthenticator(ldapProperties, multiMapAttributes);

LdapAuthenticationHandler handler = createCesLDAPAuthenticationHandler(ldapProperties, authenticator, applicationContext, servicesManager, combinedGroupResolver);
LdapAuthenticationHandler handler = createCesLDAPAuthenticationHandler(ldapProperties, authenticator, applicationContext, combinedGroupResolver);
configureLDAPAuthenticationHandler(handler, ldapProperties, multiMapAttributes, authenticator, applicationContext);

handler.initialize();
Expand All @@ -109,11 +106,10 @@ private Authenticator createAuthenticator(LdapAuthenticationProperties ldapPrope
private LdapAuthenticationHandler createCesLDAPAuthenticationHandler(LdapAuthenticationProperties ldapProperties,
Authenticator authenticator,
ConfigurableApplicationContext applicationContext,
ServicesManager servicesManager,
CombinedGroupResolver combinedGroupResolver) {
AuthenticationPasswordPolicyHandlingStrategy<AuthenticationResponse, PasswordPolicyContext> strategy = LdapUtils.createLdapPasswordPolicyHandlingStrategy(ldapProperties, applicationContext);

return new CesGroupAwareLdapAuthenticationHandler(ldapProperties.getName(), servicesManager, PrincipalFactoryUtils.newPrincipalFactory(),
return new CesGroupAwareLdapAuthenticationHandler(ldapProperties.getName(), PrincipalFactoryUtils.newPrincipalFactory(),
authenticator, strategy, combinedGroupResolver);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

import com.github.benmanes.caffeine.cache.Cache;

Expand Down Expand Up @@ -178,7 +177,7 @@ public DelegatedIdentityProviderFactory customDelegatedClientFactory(

private volatile List<BaseClient> cached;

public List<BaseClient> buildOnce() {
public List<BaseClient> buildOnce(CasConfigurationProperties properties) {
LOGGER.debug("Creating delegated clients from ces.delegation.oidc.clients...");

List<BaseClient> clients = new ArrayList<>();
Expand Down Expand Up @@ -206,7 +205,7 @@ public List<BaseClient> buildOnce() {
var client = new OidcClient(config);
client.setName(clientProps.getClientName());

String callbackUrl = casProperties.getServer().getPrefix() + "/login";
String callbackUrl = properties.getServer().getPrefix() + "/login";
client.setCallbackUrl(callbackUrl);

LOGGER.debug("Registered delegated OIDC client [{}] with discovery [{}]", client.getName(), clientProps.getDiscoveryUri());
Expand All @@ -218,21 +217,44 @@ public List<BaseClient> buildOnce() {

@Override
public List<BaseClient> build() {
if (cached == null) {
synchronized (this) {
if (cached == null) cached = buildOnce();
if (cached == null) {
synchronized (this) {
if (cached == null) {
cached = buildOnce(casProperties);
}
}
}
}
return cached;
return cached;
}

@Override
public Collection<BaseClient> rebuild() {
public List<BaseClient> rebuild() {
synchronized (this) {
cached = buildOnce();
cached = buildOnce(casProperties);
return cached;
}
}

@Override
public List<BaseClient> buildFrom(CasConfigurationProperties properties) {
return buildOnce(properties);
}

@Override
public void store(String key, List<BaseClient> currentClients) {
pac4jDelegatedClientFactoryCache.put(key, currentClients);
}

@Override
public List<BaseClient> retrieve(String key) {
Collection<BaseClient> clients = pac4jDelegatedClientFactoryCache.getIfPresent(key);
return clients == null ? List.of() : new ArrayList<>(clients);
}

@Override
public void destroy() {
cached = null;
}
};
}

Expand All @@ -255,25 +277,21 @@ public DelegatedIdentityProviders delegatedIdentityProviders(
CasConfigurationProperties casProperties,
DelegatedIdentityProviderFactory customFactory
) {
LOGGER.debug("Setting up custom delegated identity providers...");
// build once (initialized clients)
final List<BaseClient> initialized = new ArrayList<>(customFactory.build());

return new DelegatedIdentityProviders() {
@Override public List<Client> findAllClients() { return new ArrayList<>(initialized); }
@Override public List<Client> findAllClients(Service s, WebContext c) { return findAllClients(); }
@Override public Optional<Client> findClient(String name) {
return initialized.stream().filter(c -> c.getName().equalsIgnoreCase(name)).map(Client.class::cast).findFirst();
}
};
LOGGER.debug("Setting up custom delegated identity providers...");
final List<BaseClient> initialized = new ArrayList<>(customFactory.build());

return (Service service, WebContext webContext) -> new ArrayList<>(initialized);
}


// fixes No qualifying bean of type 'org.pac4j.core.client.Clients' available at logging out
@Bean
@RefreshScope
public Clients builtClients(DelegatedIdentityProviders delegatedIdentityProviders) {
var allClients = delegatedIdentityProviders.findAllClients();
List<Client> allClients = delegatedIdentityProviders.findAllClients((WebContext) null)
.stream()
.map(Client.class::cast)
.toList();
return new Clients(allClients);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,27 @@
package de.triology.cas.oidc.config;

import org.apereo.cas.configuration.model.support.ldap.LdapAuthenticationProperties;
import de.triology.cas.oidc.beans.delegation.CesDelegatedOidcClientProperties;
import de.triology.cas.oidc.beans.delegation.CesDelegatedOidcClientsProperties;
import org.apereo.cas.authentication.principal.Service;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.configuration.model.core.CasServerProperties;
import org.apereo.cas.pac4j.client.DelegatedIdentityProviderFactory;
import org.apereo.cas.pac4j.client.DelegatedIdentityProviders;
import org.apereo.cas.util.LdapUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.pac4j.core.client.BaseClient;
import org.pac4j.core.client.Clients;
import org.pac4j.core.context.WebContext;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.ConfigurableApplicationContext;
import java.lang.reflect.Field;

import java.util.Collection;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.mockStatic;
import org.apereo.cas.util.LdapUtils;



import org.apereo.cas.configuration.model.core.CasServerProperties;

import java.util.Collection;
import java.util.List;

class CesOidcConfigurationTests {

Expand Down Expand Up @@ -66,30 +58,32 @@ void shouldBuildCustomDelegatedClientsFactory() {
var factory = configuration.customDelegatedClientFactory(casProperties, cache, applicationContext, clientsProps);

assertNotNull(factory);
assertTrue(factory.build() instanceof Collection);
assertNotNull(factory.build());
}

@Test
void shouldReturnDelegatedIdentityProviders() {
var casProperties = mock(CasConfigurationProperties.class);
DelegatedIdentityProviderFactory factory = mock(DelegatedIdentityProviderFactory.class);
when(factory.build()).thenReturn(Collections.emptyList());
var client = delegatedClient("oidc-client");
var providers = delegatedIdentityProvidersFor(client);
var service = mock(Service.class);
var webContext = mock(WebContext.class);

var providers = configuration.delegatedIdentityProviders(casProperties, factory);
assertNotNull(providers);
assertTrue(providers.findAllClients().isEmpty());
assertTrue(providers.findClient("unknown").isEmpty());
assertEquals(java.util.List.of(client), providers.findAllClients(service, webContext));
assertEquals(java.util.List.of(client), providers.findAllClients(webContext));
assertSame(client, providers.findClient("OIDC-CLIENT", webContext).orElseThrow());
assertTrue(providers.findClient("unknown", webContext).isEmpty());
}

@Test
void shouldBuildClients() {
var providers = mock(DelegatedIdentityProviders.class);
when(providers.findAllClients()).thenReturn(Collections.emptyList());
var client = delegatedClient("oidc-client");
var providers = delegatedIdentityProvidersFor(client);

var clients = configuration.builtClients(providers);

assertNotNull(clients);
assertTrue(clients.getClients().isEmpty());
assertEquals(java.util.List.of(client), clients.getClients());
}

@Test
Expand Down Expand Up @@ -117,7 +111,7 @@ void shouldCreateClientUserProfileProvisioner() {
var ldapProps = mock(org.apereo.cas.configuration.model.support.ldap.LdapAuthenticationProperties.class);

when(casProperties.getAuthn()).thenReturn(authnProps);
when(authnProps.getLdap()).thenReturn(List.of(ldapProps));
when(authnProps.getLdap()).thenReturn(java.util.List.of(ldapProps));

when(ldapProps.getBaseDn()).thenReturn("dc=example,dc=org");
when(ldapProps.getLdapUrl()).thenReturn("ldap://localhost");
Expand Down Expand Up @@ -173,7 +167,7 @@ void shouldLogWhenNoDelegatedClientsAvailable() {
when(factory.build()).thenReturn(Collections.emptyList());

var providers = configuration.delegatedIdentityProviders(casProperties, factory);
var clients = providers.findAllClients();
var clients = providers.findAllClients(mock(WebContext.class));

assertTrue(clients.isEmpty(), "Clients should be empty, triggering log output");
}
Expand Down Expand Up @@ -203,7 +197,7 @@ void shouldSkipInvalidOidcClientProperties() {
invalidClientProps.setClientName("invalid-client"); // nur Name, Rest fehlt

var clientsProps = new CesDelegatedOidcClientsProperties();
clientsProps.setClients(List.of(invalidClientProps));
clientsProps.setClients(java.util.List.of(invalidClientProps));

var factory = configuration.customDelegatedClientFactory(casProperties, cache, applicationContext, clientsProps);
var clients = factory.build();
Expand All @@ -223,6 +217,20 @@ private void setField(Object target, String fieldName, Object value) throws Exce
field.set(target, value);
}

private BaseClient delegatedClient(String name) {
var client = mock(BaseClient.class);
when(client.getName()).thenReturn(name);
return client;
}

private DelegatedIdentityProviders delegatedIdentityProvidersFor(BaseClient... clients) {
var casProperties = mock(CasConfigurationProperties.class);
DelegatedIdentityProviderFactory factory = mock(DelegatedIdentityProviderFactory.class);
when(factory.build()).thenReturn(java.util.List.of(clients));

return configuration.delegatedIdentityProviders(casProperties, factory);
}

@Test
void shouldBuildValidOidcClientFromProperties() {
//var configuration = new CesOidcConfiguration(); // wird schon von @BeforeEach bereitgestellt
Expand All @@ -239,14 +247,14 @@ void shouldBuildValidOidcClientFromProperties() {
clientProps.setPreferredJwsAlgorithm("RS256");

var clientsProps = new CesDelegatedOidcClientsProperties();
clientsProps.setClients(List.of(clientProps));
clientsProps.setClients(java.util.List.of(clientProps));

var serverProps = mock(CasServerProperties.class);
when(serverProps.getPrefix()).thenReturn("https://cas.example.org");
when(casProperties.getServer()).thenReturn(serverProps);

var factory = configuration.customDelegatedClientFactory(casProperties, cache, applicationContext, clientsProps);
Collection<org.pac4j.core.client.BaseClient> clients = factory.build();
var clients = factory.build();

assertEquals(1, clients.size());
var client = (org.pac4j.oidc.client.OidcClient) clients.iterator().next();
Expand Down
Loading