package org.apereo.cas.oidc.util; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.apereo.cas.CasProtocolConstants; import org.apereo.cas.authentication.Authentication; import org.apereo.cas.oidc.OidcConstants; import org.apereo.cas.ticket.registry.TicketRegistrySupport; import org.apereo.cas.web.support.CookieRetrievingCookieGenerator; import org.apereo.cas.web.support.WebUtils; import org.jasig.cas.client.util.URIBuilder; import org.pac4j.cas.client.CasClient; import org.pac4j.core.context.J2EContext; import org.pac4j.core.context.WebContext; import org.pac4j.core.profile.ProfileManager; import org.pac4j.core.profile.UserProfile; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.Assert; import java.time.ZonedDateTime; import java.util.Arrays; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; /** * This is {@link OidcAuthorizationRequestSupport}. * * @author Misagh Moayyed * @since 5.0.0 */ public class OidcAuthorizationRequestSupport { private static final Logger LOGGER = LoggerFactory.getLogger(OidcAuthorizationRequestSupport.class); private final CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator; private final TicketRegistrySupport ticketRegistrySupport; public OidcAuthorizationRequestSupport(final CookieRetrievingCookieGenerator tgtCookieGenerator, final TicketRegistrySupport ticketRegistrySupport) { this.ticketGrantingTicketCookieGenerator = tgtCookieGenerator; this.ticketRegistrySupport = ticketRegistrySupport; } /** * Gets oidc prompt from authorization request. * * @param url the url * @return the oidc prompt from authorization request */ public static Set<String> getOidcPromptFromAuthorizationRequest(final String url) { Assert.notNull(url, "URL cannot be null"); return new URIBuilder(url).getQueryParams().stream() .filter(p -> OidcConstants.PROMPT.equals(p.getName())) .map(param -> param.getValue().split(" ")) .flatMap(Arrays::stream) .collect(Collectors.toSet()); } /** * Gets oidc prompt from authorization request. * * @param context the context * @return the oidc prompt from authorization request */ public static Set<String> getOidcPromptFromAuthorizationRequest(final WebContext context) { return getOidcPromptFromAuthorizationRequest(context.getFullRequestURL()); } /** * Gets oidc max age from authorization request. * * @param context the context * @return the oidc max age from authorization request */ public static Optional<Long> getOidcMaxAgeFromAuthorizationRequest(final WebContext context) { final URIBuilder builderContext = new URIBuilder(context.getFullRequestURL()); final Optional<URIBuilder.BasicNameValuePair> parameter = builderContext.getQueryParams() .stream().filter(p -> OidcConstants.MAX_AGE.equals(p.getName())) .findFirst(); if (parameter.isPresent()) { final long maxAge = NumberUtils.toLong(parameter.get().getValue(), -1); return Optional.of(maxAge); } return Optional.empty(); } /** * Is authentication profile available?. * * @param context the context * @return the optional user profile */ public static Optional<UserProfile> isAuthenticationProfileAvailable(final WebContext context) { final ProfileManager manager = WebUtils.getPac4jProfileManager(context); return manager.get(true); } /** * Is cas authentication available? * * @param context the context * @return the optional authn */ public Optional<Authentication> isCasAuthenticationAvailable(final WebContext context) { final J2EContext j2EContext = (J2EContext) context; if (j2EContext != null) { final String tgtId = ticketGrantingTicketCookieGenerator.retrieveCookieValue(j2EContext.getRequest()); if (StringUtils.isNotBlank(tgtId)) { final Authentication authentication = ticketRegistrySupport.getAuthenticationFrom(tgtId); if (authentication != null) { return Optional.of(authentication); } } } return Optional.empty(); } /** * Is cas authentication old for max age authorization request boolean. * * @param context the context * @param authenticationDate the authentication date * @return true/false */ public boolean isCasAuthenticationOldForMaxAgeAuthorizationRequest(final WebContext context, final ZonedDateTime authenticationDate) { final Optional<Long> maxAge = getOidcMaxAgeFromAuthorizationRequest(context); if (maxAge.isPresent() && maxAge.get() > 0) { final long now = ZonedDateTime.now().toEpochSecond(); final long authTime = authenticationDate.toEpochSecond(); final long diffInSeconds = now - authTime; if (diffInSeconds > maxAge.get()) { LOGGER.info("Authentication is too old: [{}] and was created [{}] seconds ago.", authTime, diffInSeconds); return true; } } return false; } /** * Is cas authentication old for max age authorization request? * * @param context the context * @param authentication the authentication * @return true/false */ public boolean isCasAuthenticationOldForMaxAgeAuthorizationRequest(final WebContext context, final Authentication authentication) { return isCasAuthenticationOldForMaxAgeAuthorizationRequest(context, authentication.getAuthenticationDate()); } /** * Is cas authentication old for max age authorization request? * * @param context the context * @param profile the profile * @return true/false */ public boolean isCasAuthenticationOldForMaxAgeAuthorizationRequest(final WebContext context, final UserProfile profile) { final Object authTime = profile.getAttribute(CasProtocolConstants.VALIDATION_CAS_MODEL_ATTRIBUTE_NAME_AUTHENTICATION_DATE); if (authTime == null) { return false; } final ZonedDateTime dt = ZonedDateTime.parse(authTime.toString()); return isCasAuthenticationOldForMaxAgeAuthorizationRequest(context, dt); } /** * Configure client for max age authorization request. * Sets the CAS client to ask for renewed authentication if * the authn time is too old based on the requested max age. * * @param casClient the cas client * @param context the context * @param authentication the authentication */ public void configureClientForMaxAgeAuthorizationRequest(final CasClient casClient, final WebContext context, final Authentication authentication) { if (isCasAuthenticationOldForMaxAgeAuthorizationRequest(context, authentication)) { casClient.getConfiguration().setRenew(true); } } /** * Configure client for prompt login authorization request. * * @param casClient the cas client * @param context the context */ public static void configureClientForPromptLoginAuthorizationRequest(final CasClient casClient, final WebContext context) { final Set<String> prompts = getOidcPromptFromAuthorizationRequest(context); if (prompts.contains(OidcConstants.PROMPT_LOGIN)) { casClient.getConfiguration().setRenew(true); } } /** * Configure client for prompt none authorization request. * * @param casClient the cas client * @param context the context */ public static void configureClientForPromptNoneAuthorizationRequest(final CasClient casClient, final WebContext context) { final Set<String> prompts = getOidcPromptFromAuthorizationRequest(context); if (prompts.contains(OidcConstants.PROMPT_NONE)) { casClient.getConfiguration().setRenew(false); casClient.getConfiguration().setGateway(true); } } }