Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
a6d7952
story #15455 feat: upgrade CAS 6 to 7
Regzox Dec 5, 2025
ae02544
story #15455: setup beans and replace client
Regzox Jan 5, 2026
981a512
story #15455: presentation dlb vendredi
Regzox Jan 19, 2026
9145b92
story #15455: debug
Regzox Jan 19, 2026
edc892c
story #15455: replace getUser by getUserByEmail
Regzox Jan 19, 2026
5aab3f6
story #15455: undo xelians static resources
Regzox Jan 19, 2026
bc97cdb
story #15455: clean code
Regzox Jan 21, 2026
45505ae
story #15455 fix: user principal resolver
Regzox Jan 23, 2026
69ae66b
story #15455: bidouilles access token
Regzox Jan 23, 2026
bdc8a24
story #15455: xelians resources
Regzox Jan 26, 2026
b1d61a3
story #15455: wip to revert
Regzox Jan 26, 2026
d9da2e5
story #15455: wip update client oauth2 conf
Regzox Jan 27, 2026
2c5c486
story #15455: cfg, css, webflow bean
Regzox Jan 28, 2026
78cbc3f
story #15455: step: login works
Regzox Jan 28, 2026
5a5b6dc
story #15455 wip: logout
Regzox Jan 28, 2026
65ff479
story #15455: logout works
Regzox Jan 29, 2026
c3c9477
story #15455 style: enable scss compilation and update scss
Regzox Jan 30, 2026
2cc7bce
story #15455 config: update redirect_url for each missed oauth client
Regzox Jan 30, 2026
cc1b924
story #15455 lint: spotless apply
Regzox Jan 30, 2026
73e49c2
story #15455 feat(passwordless-unify): move subrogation check at webf…
Regzox Jan 30, 2026
532a2e2
story #15455 feat(passwordless-unify): unify passwordless and login-p…
Regzox Jan 30, 2026
5c22001
story #15455 test: fix broken tests
Regzox Feb 3, 2026
bcd7e41
story #15455 style: fix typo
Regzox Feb 3, 2026
7132dd4
story #15455 feat(template): setup our templates and styles
Regzox Feb 3, 2026
44c8ed0
story #15455 fix(subrogation): customer id pattern
Regzox Feb 3, 2026
fca1418
story #15455 conf: rename secret token key
Regzox Feb 4, 2026
7d137a7
story #15455 conf(cas): update deployment application conf
Regzox Feb 4, 2026
225dce2
story #15455 fix: secret token key
Regzox Feb 4, 2026
688bf0f
story #15455 conf(test): add hardcoded crypto settings
Regzox Feb 4, 2026
5f092b8
story #15455 conf: update client redirect uris
Regzox Feb 4, 2026
fc8c04c
story #15455 conf(test): add crypto params
Regzox Feb 4, 2026
dc953dd
story #15455 fix(password-mgt): change password args permutation
Regzox Feb 5, 2026
4dfbd16
story #15455 style: fix typo var
Regzox Feb 16, 2026
4a08b69
story #15455 style: fix i18n configs
Regzox Feb 16, 2026
cd5c3c2
story #15455 style: remove pom java 17 comment
Regzox Feb 16, 2026
ed8eca5
story #15455 style: code cleanup
Regzox Feb 16, 2026
540f09b
story #15455 style: remove OIDCProviderMetadata.findProviderMetadata …
Regzox Feb 16, 2026
4ef9437
story #15455 test: add pac4j client builder tests
Regzox Feb 16, 2026
fa773bb
story #15455 style: rename method
Regzox Feb 16, 2026
bc2861a
story #15455 style: rename method
Regzox Feb 16, 2026
2e7ae50
story #15455: spotless
Regzox Feb 16, 2026
14edd60
story #15455 style: remove TODOs
Regzox Feb 16, 2026
5e72c9a
story #15455 style: remove login z-index
Regzox Feb 16, 2026
24930f5
story #15455 fix: custom passwordless missing actions
Regzox Feb 16, 2026
fd9ab5d
story #15455 fix: clean flow scope when user find
Regzox Feb 16, 2026
7c0e756
story #15307 fix: truststore generation for devs
Regzox Feb 17, 2026
4eaf567
story #15455 deps: cleanup versions
Regzox Feb 19, 2026
a26b0a9
story #15455 clean: pom and annotations
Regzox Feb 19, 2026
d7008d3
story #15455: remove useless items
Regzox Feb 19, 2026
a3f13cb
story #15455 pom: upgrade cas version
Regzox Feb 19, 2026
5504598
story #15455 fix: iam crashes
Regzox Feb 20, 2026
caa3198
story #15455 fix: remove passwordless feature
Regzox Feb 25, 2026
f47b715
story #15455 fix: override token validator method
Regzox Feb 25, 2026
69d8311
story #15455 fix: trigger change password action
Regzox Feb 25, 2026
09743dd
story #15455 fix: trigger change password action
Regzox Feb 25, 2026
ac98e01
story #15455 fix: remove fixme
Regzox Feb 25, 2026
e268e20
story #15455 fix: remove passwordless templates
Regzox Feb 25, 2026
9c786d0
story #15455 fix: remove unused consent.js
Regzox Feb 25, 2026
1647add
story #15455 fix: remove unused js files
Regzox Feb 25, 2026
87f9327
story #15455 fix: remove geo feature from cas.js
Regzox Feb 25, 2026
7f86952
story #15455 fix: remove geo feature from cas.js
Regzox Feb 25, 2026
b0f9a62
story #15455 fix: service username magic var
Regzox Feb 25, 2026
dfd27e5
story #15455 fix: remove commented code
Regzox Feb 25, 2026
bac2468
story #15455 fix: client http context
Regzox Feb 26, 2026
ef51b12
story #15455: change fixme about vitam client null provider id problem
Regzox Feb 26, 2026
ce51785
story #15455: clean poms
Regzox Feb 26, 2026
13851a4
story #15455: clean cas-server pom
Regzox Feb 26, 2026
264615f
story #15455: clean deps
Regzox Feb 27, 2026
ed910c1
story #15455: remove commented code
Regzox Feb 27, 2026
dcb64f0
story #15455: fix javadoc spacings
Regzox Feb 27, 2026
8afceb4
story #15455: key parametrization
Regzox Feb 27, 2026
8199de4
story #15455 deploy: add parametrizable crypto configs
Regzox Feb 27, 2026
7b887fe
story #15455 deploy: fix cookie_name condition
Regzox Feb 27, 2026
ecec3f7
story #15455 deploy: fix key-size property presence
Regzox Feb 27, 2026
cc3e435
story #15455 fix: infinite redirect loop when crypto no fully enabled
Regzox Feb 27, 2026
c3988f2
story #15455 deploy: improve crypto blocks
Regzox Feb 27, 2026
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
9 changes: 3 additions & 6 deletions api/api-iam/iam-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,11 @@
</parent>

<properties>
<!-- make iam-client on jdk 17 temporarily -->
<!-- current Cas version use an incompatible Spring boot version 2.7.18 with java 21,
that's why we keep CAS in JAVA 17, and all dependencies as iam-client and commons that are compiled in Java 21 with target java 17-->
<java.version>21</java.version>
<java.release>17</java.release>
<java.release>21</java.release>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.release>17</maven.compiler.release>
<maven.compiler.target>21</maven.compiler.target>
<maven.compiler.release>21</maven.compiler.release>
</properties>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,22 @@

import fr.gouv.vitamui.commons.api.CommonConstants;
import fr.gouv.vitamui.commons.rest.client.HttpContext;
import fr.gouv.vitamui.commons.rest.client.HttpContextHolder;
import fr.gouv.vitamui.iam.openapiclient.invoker.ApiClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.Collections;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.Supplier;

@Slf4j
public class IamApiClient extends ApiClient {

public IamApiClient(RestTemplate restTemplate) {
Expand All @@ -52,48 +58,85 @@ protected void updateParamsForAuth(
HttpHeaders headerParams,
MultiValueMap<String, String> cookieParams
) {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
final HttpContext context;
if (authentication instanceof PreAuthenticatedAuthenticationToken) {
// Needed for the initial call to usersApi.getMe() during authentication
context = (HttpContext) authentication.getPrincipal();
} else {
// The other calls get normal authentication credentials
context = (HttpContext) authentication.getCredentials();
}
updateParamsForAuth(headerParams, context);
}
resolveContext()
.ifPresentOrElse(
context -> {
applyHeaders(context, headerParams);
log.debug("IAM headers applied. Context={}", context);
},
() -> log.warn("No HttpContext available for authNames={}", Arrays.toString(authNames))
);
}

private void updateParamsForAuth(HttpHeaders headerParams, HttpContext context) {
final Integer tenantIdentifier = context.getTenantIdentifier();
final String userToken = context.getUserToken();
final String applicationId = context.getApplicationId();
final String identity = context.getIdentity();
final String requestId = context.getRequestId();
final String accessContractId = context.getAccessContract();
headerParams.set(CommonConstants.X_ORIGIN_HEADER_NAME, CommonConstants.X_ORIGIN_HEADER_INTERNAL);
if (tenantIdentifier != null) {
headerParams.put(
CommonConstants.X_TENANT_ID_HEADER,
Collections.singletonList(String.valueOf(tenantIdentifier))
);
}
if (userToken != null) {
headerParams.put(CommonConstants.X_USER_TOKEN_HEADER, Collections.singletonList(userToken));
}
if (applicationId != null) {
headerParams.put(CommonConstants.X_APPLICATION_ID_HEADER, Collections.singletonList(applicationId));
private Optional<HttpContext> resolveContext() {
return resolveFromHolder().or(this::resolveFromSecurityContext);
}

/**
* First priority: context manually set in HttpContextHolder.
*/
private Optional<HttpContext> resolveFromHolder() {
return HttpContextHolder.get();
}

/**
* Fallback: try to resolve HttpContext from Spring Security.
*/
private Optional<HttpContext> resolveFromSecurityContext() {
Authentication authentication = Optional.ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.orElse(null);

if (authentication == null) {
return Optional.empty();
}
if (identity != null) {
headerParams.put(CommonConstants.X_IDENTITY_HEADER, Collections.singletonList(identity));

// Special case: initial usersApi.getMe() call during authentication
if (
authentication instanceof PreAuthenticatedAuthenticationToken &&
authentication.getPrincipal() instanceof HttpContext context
) {
return Optional.of(context);
}
if (requestId != null) {
headerParams.put(CommonConstants.X_REQUEST_ID_HEADER, Collections.singletonList(requestId));

// Standard case: HttpContext stored in credentials
if (authentication.getCredentials() instanceof HttpContext context) {
return Optional.of(context);
}
if (accessContractId != null) {
headerParams.put(CommonConstants.X_ACCESS_CONTRACT_ID_HEADER, Collections.singletonList(accessContractId));

return Optional.empty();
}

/**
* Apply IAM-related headers based on the resolved HttpContext.
*/
private void applyHeaders(HttpContext context, HttpHeaders headers) {
headers.set(CommonConstants.X_ORIGIN_HEADER_NAME, CommonConstants.X_ORIGIN_HEADER_INTERNAL);

putIfNotNull(
headers,
CommonConstants.X_TENANT_ID_HEADER,
() -> Optional.ofNullable(context.getTenantIdentifier()).map(String::valueOf).orElse(null)
);

putIfNotNull(headers, CommonConstants.X_USER_TOKEN_HEADER, context::getUserToken);

putIfNotNull(headers, CommonConstants.X_APPLICATION_ID_HEADER, context::getApplicationId);

putIfNotNull(headers, CommonConstants.X_IDENTITY_HEADER, context::getIdentity);

putIfNotNull(headers, CommonConstants.X_REQUEST_ID_HEADER, context::getRequestId);

putIfNotNull(headers, CommonConstants.X_ACCESS_CONTRACT_ID_HEADER, context::getAccessContract);
}

/**
* Add header only if the supplied value is not null.
*/
private void putIfNotNull(HttpHeaders headers, String headerName, Supplier<String> valueSupplier) {
String value = valueSupplier.get();
if (value != null) {
headers.set(headerName, value);
}
}
}
2 changes: 1 addition & 1 deletion api/api-iam/iam-client/src/main/resources/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2526,7 +2526,7 @@ paths:
content:
'*/*':
schema:
$ref: "#/components/schemas/UserDto"
$ref: "#/components/schemas/AuthUserDto"
example: null
/iam/v1/cas/subrogations:
get:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,28 @@
/**
* Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020)
* and the signatories of the "VITAM - Accord du Contributeur" agreement.
/*
* Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2015-2022)
*
* contact@programmevitam.fr
* contact.vitam@culture.gouv.fr
*
* This software is a computer program whose purpose is to implement
* implement a digital archiving front-office system for the secure and
* efficient high volumetry VITAM solution.
* This software is a computer program whose purpose is to implement a digital archiving back-office system managing
* high volumetry securely and efficiently.
*
* This software is governed by the CeCILL-C license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL-C
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
* This software is governed by the CeCILL 2.1 license under French law and abiding by the rules of distribution of free
* software. You can use, modify and/ or redistribute the software under the terms of the CeCILL 2.1 license as
* circulated by CEA, CNRS and INRIA at the following URL "https://cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited
* liability.
* As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license,
* users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the
* successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
* In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or
* developing or reproducing the software by the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it is reserved for developers and
* experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling the security of their systems and/or data
* to be ensured and, more generally, to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C license and that you accept its terms.
* The fact that you are presently reading this means that you have had knowledge of the CeCILL 2.1 license and that you
* accept its terms.
*/
package fr.gouv.vitamui.iam.common.dto;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package fr.gouv.vitamui.iam.common.utils;

import org.pac4j.oidc.config.OidcConfiguration;
import org.pac4j.oidc.metadata.OidcOpMetadataResolver;
import org.pac4j.oidc.profile.creator.TokenValidator;

/** Custom OIDC metadata resolver. */
public class CustomOidcOpMetadataResolver extends OidcOpMetadataResolver {

public CustomOidcOpMetadataResolver(final OidcConfiguration configuration) {
super(configuration);
}

@Override
protected TokenValidator createTokenValidator() {
return new CustomTokenValidator(configuration, this.loaded);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import com.nimbusds.jwt.proc.BadJWTException;
import com.nimbusds.openid.connect.sdk.Nonce;
import com.nimbusds.openid.connect.sdk.claims.IDTokenClaimsSet;
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
import org.pac4j.oidc.config.OidcConfiguration;
import org.pac4j.oidc.profile.creator.TokenValidator;

Expand All @@ -57,8 +58,11 @@ public class CustomTokenValidator extends TokenValidator {

private static final List<String> AGENTCONNECT_ACR_VALUES = Arrays.asList("eidas1", "eidas2", "eidas3");

public CustomTokenValidator(final OidcConfiguration configuration) {
super(configuration);
private final OidcConfiguration configuration;

public CustomTokenValidator(final OidcConfiguration configuration, final OIDCProviderMetadata metadata) {
super(configuration, metadata);
this.configuration = configuration;
}

public IDTokenClaimsSet validate(final JWT idToken, final Nonce expectedNonce)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,22 @@ public Optional<IdentityProviderDto> findByUserIdentifierAndCustomerId(
.findFirst();
}

public Optional<IdentityProviderDto> findAutoProvisioningProviderByEmail(
final List<IdentityProviderDto> providers,
final String email
) {
for (final IdentityProviderDto provider : providers) {
if (provider.isAutoProvisioningEnabled()) {
for (final String pattern : provider.getPatterns()) {
if (Pattern.compile(pattern, Pattern.CASE_INSENSITIVE).matcher(email).matches()) {
return Optional.of(provider);
}
}
}
}
return Optional.empty();
}

public boolean identifierMatchProviderPattern(
final List<IdentityProviderDto> providers,
final String userEmail,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.opensaml.saml.common.xml.SAMLConstants;
import org.pac4j.core.client.IndirectClient;
Expand All @@ -53,26 +54,19 @@
import org.pac4j.oidc.config.OidcConfiguration;
import org.pac4j.saml.client.SAML2Client;
import org.pac4j.saml.config.SAML2Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ByteArrayResource;

import java.util.Base64;
import java.util.Map;
import java.util.Optional;

/**
* A pac4j client builder.
*
*
*/
/** A pac4j client builder. */
@Getter
@Setter
@Slf4j
public class Pac4jClientBuilder {

private static final Logger LOGGER = LoggerFactory.getLogger(Pac4jClientBuilder.class);

@Value("${login.url}")
@NotNull
private String casLoginUrl;
Expand Down Expand Up @@ -153,13 +147,14 @@ public Optional<IndirectClient> buildClient(final IdentityProviderDto provider)
oidcConfiguration.setUseNonce(useNonce != null ? useNonce : true);
final Boolean usePkce = provider.getUsePkce();
oidcConfiguration.setDisablePkce(usePkce != null ? !usePkce : true);
oidcConfiguration.setStateGenerator((context, store) -> new Nonce().toString());
oidcConfiguration.setTokenValidator(new CustomTokenValidator(oidcConfiguration));
oidcConfiguration.setStateGenerator(ctx -> new Nonce().toString());
oidcConfiguration.setOpMetadataResolver(new CustomOidcOpMetadataResolver(oidcConfiguration));

final OidcClient oidcClient = new OidcClient(oidcConfiguration);
setCallbackUrl(oidcClient, technicalName);

oidcClient.init();
oidcClient.getConfiguration().getOpMetadataResolver().load();
return Optional.of(oidcClient);
}
}
Expand All @@ -172,7 +167,7 @@ public Optional<IndirectClient> buildClient(final IdentityProviderDto provider)
} else if (message.equals("Error parsing idp Metadata")) {
throw new InvalidFormatException(message, ErrorsConstants.ERRORS_VALID_IDP_METADATA);
}
LOGGER.error("Cannot build pac4j client with provider identifier: " + provider.getIdentifier(), e);
log.error("Cannot build pac4j client with provider identifier: " + provider.getIdentifier(), e);
}
return Optional.empty();
}
Expand Down
Loading
Loading