From 8e62d547c68d4e029fc73b5401b79573724d2eee Mon Sep 17 00:00:00 2001 From: cailbourdin Date: Wed, 23 Oct 2024 16:30:30 +0200 Subject: [PATCH] feat: gestion d'exception si l'attribut du principal est inconnu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modification du comportement par défaut qui remplaçait par le principal par défaut --- ...buteRegisteredServiceUsernameProvider.java | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 src/main/java/org/apereo/cas/services/PrincipalAttributeRegisteredServiceUsernameProvider.java diff --git a/src/main/java/org/apereo/cas/services/PrincipalAttributeRegisteredServiceUsernameProvider.java b/src/main/java/org/apereo/cas/services/PrincipalAttributeRegisteredServiceUsernameProvider.java new file mode 100644 index 0000000..894bced --- /dev/null +++ b/src/main/java/org/apereo/cas/services/PrincipalAttributeRegisteredServiceUsernameProvider.java @@ -0,0 +1,113 @@ +package org.apereo.cas.services; + +import org.apereo.cas.ticket.UnsatisfiedAuthenticationContextTicketValidationException; +import org.apereo.cas.util.CollectionUtils; +import org.apereo.cas.util.function.FunctionUtils; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serial; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * Determines the username for this registered service based on a principal attribute. + * If the attribute is not found, default principal id is returned. + * + * @author Misagh Moayyed + * @since 4.1.0 + */ +@Slf4j +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class PrincipalAttributeRegisteredServiceUsernameProvider extends BaseRegisteredServiceUsernameAttributeProvider { + + @Serial + private static final long serialVersionUID = -3546719400741715137L; + + private String usernameAttribute; + + @Override + public String resolveUsernameInternal(final RegisteredServiceUsernameProviderContext context) { + var principalId = context.getPrincipal().getId(); + val originalPrincipalAttributes = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + originalPrincipalAttributes.putAll(context.getPrincipal().getAttributes()); + LOGGER.debug("Original principal attributes available for selection of username attribute [{}] are [{}].", + usernameAttribute, originalPrincipalAttributes); + val releasePolicyAttributes = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + + FunctionUtils.doIfNull(context.getReleasingAttributes(), + __ -> releasePolicyAttributes.putAll(getPrincipalAttributesFromReleasePolicy(context)), + releasePolicyAttributes::putAll); + + LOGGER.debug("Attributes resolved by the release policy available for selection of username attribute [{}] are [{}].", + usernameAttribute, releasePolicyAttributes); + + if (StringUtils.isBlank(usernameAttribute)) { + LOGGER.warn("No username attribute is defined for service [{}]. CAS will fall back onto using the default principal id. " + + "This is likely a mistake in the configuration of the registered service definition.", context.getRegisteredService().getName()); + } else if (releasePolicyAttributes.containsKey(usernameAttribute)) { + LOGGER.debug("Attribute release policy for registered service [{}] contains an attribute for [{}]", + context.getRegisteredService().getServiceId(), usernameAttribute); + val value = releasePolicyAttributes.get(usernameAttribute); + principalId = CollectionUtils.wrap(value).getFirst().toString(); + } else if (originalPrincipalAttributes.containsKey(usernameAttribute)) { + LOGGER.debug("The selected username attribute [{}] was retrieved as a direct " + + "principal attribute and not through the attribute release policy for service [{}]. " + + "CAS is unable to detect new attribute values for [{}] after authentication unless the attribute " + + "is explicitly authorized for release via the service attribute release policy.", + usernameAttribute, context.getRegisteredService(), usernameAttribute); + val value = originalPrincipalAttributes.get(usernameAttribute); + principalId = CollectionUtils.wrap(value).getFirst().toString(); + } else { + // Customization to throw an exception if the principal attribute is not found + LOGGER.info("Principal [{}] does not have an attribute [{}] among attributes [{}] so CAS cannot " + + "provide the user attribute the service expects. " + + "Ensure the attribute selected as the username " + + "is allowed to be released by the service attribute release policy.", principalId, usernameAttribute, releasePolicyAttributes); + throw new UnsatisfiedAuthenticationContextTicketValidationException(context.getService()); + } + LOGGER.debug("Principal id to return for [{}] is [{}]. The default principal id is [{}].", context.getService().getId(), + principalId, context.getPrincipal().getId()); + return principalId.trim(); + } + + protected Map> getPrincipalAttributesFromReleasePolicy( + final RegisteredServiceUsernameProviderContext context) throws Throwable { + if (context.getRegisteredService() != null && context.getRegisteredService().getAccessStrategy() + .isServiceAccessAllowed(context.getRegisteredService(), context.getService())) { + LOGGER.debug("Located service [{}] in the registry. Attempting to resolve attributes for [{}]", + context.getRegisteredService(), context.getPrincipal().getId()); + if (context.getRegisteredService().getAttributeReleasePolicy() == null) { + LOGGER.debug("No attribute release policy is defined for [{}]. Returning default principal attributes", context.getService().getId()); + return context.getPrincipal().getAttributes(); + } + val releasePolicyContext = RegisteredServiceAttributeReleasePolicyContext.builder() + .registeredService(context.getRegisteredService()) + .service(context.getService()) + .principal(context.getPrincipal()) + .applicationContext(context.getApplicationContext()) + .build(); + return context.getRegisteredService().getAttributeReleasePolicy().getAttributes(releasePolicyContext); + } + LOGGER.debug("Could not locate service [{}] in the registry.", context.getService().getId()); + throw UnauthorizedServiceException.denied("Rejected: %s".formatted(context.getService().getId())); + } +} \ No newline at end of file