Skip to content

Commit

Permalink
feat: gestion d'exception si l'attribut du principal est inconnu
Browse files Browse the repository at this point in the history
Modification du comportement par défaut qui remplaçait par le principal par défaut
  • Loading branch information
nathancailbourdin committed Oct 23, 2024
1 parent 65660b2 commit 8e62d54
Showing 1 changed file with 113 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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<String, List<Object>> 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()));
}
}

0 comments on commit 8e62d54

Please sign in to comment.