/** * Compare the flow's custom principal names to the string values of the attribute. * * @param flow flow to examine * * @return a match between the flow's principal names and the attribute's string values, or null */ @Nullable private String getMatch(@Nonnull final AuthenticationFlowDescriptor flow) { log.debug("{} Looking for match for flow {} against values for attribute {}", getLogPrefix(), flow.getId(), attribute.getId()); for (final Principal p : flow.getSupportedPrincipals()) { log.debug("{} Comparing principal {} against attribute values {}", getLogPrefix(), p.getName(), attribute.getValues()); for (final IdPAttributeValue val : attribute.getValues()) { if (val instanceof StringAttributeValue && Objects.equals(val.getValue(), p.getName())) { return p.getName(); } } } return null; }
/** * Check if a result generated by this flow is still active. * * @param result {@link AuthenticationResult} to check * * @return true iff the result remains valid */ public boolean isResultActive(@Nonnull final AuthenticationResult result) { Constraint.isNotNull(result, "AuthenticationResult cannot be null"); Constraint.isTrue(result.getAuthenticationFlowId().equals(getId()), "AuthenticationResult was not produced by this flow"); final long now = System.currentTimeMillis(); if (getLifetime() > 0 && result.getAuthenticationInstant() + getLifetime() <= now) { return false; } else if (getInactivityTimeout() > 0 && result.getLastActivityInstant() + getInactivityTimeout() <= now) { return false; } return true; }
/** {@inheritDoc} */ @Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext, @Nonnull final AuthenticationContext authenticationContext) { final Map<String, AuthenticationFlowDescriptor> potentialFlows = authenticationContext.getPotentialFlows(); final Iterator<Entry<String, AuthenticationFlowDescriptor>> descriptorItr = potentialFlows.entrySet().iterator(); while (descriptorItr.hasNext()) { final AuthenticationFlowDescriptor descriptor = descriptorItr.next().getValue(); if (descriptor.isNonBrowserSupported()) { log.debug("{} Retaining flow {}, it supports non-browser authentication", getLogPrefix(), descriptor.getId()); } else { log.debug("{} Removing flow {}, it does not support non-browser authentication", getLogPrefix(), descriptor.getId()); descriptorItr.remove(); } } if (potentialFlows.size() == 0) { log.info("{} No potential authentication flows remain after filtering", getLogPrefix()); } else { log.debug("{} Potential authentication flows left after filtering: {}", getLogPrefix(), potentialFlows); } }
/** {@inheritDoc} */ @Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext, @Nonnull final AuthenticationContext authenticationContext) { final Map<String, AuthenticationFlowDescriptor> potentialFlows = authenticationContext.getPotentialFlows(); final Iterator<Entry<String, AuthenticationFlowDescriptor>> descriptorItr = potentialFlows.entrySet().iterator(); while (descriptorItr.hasNext()) { final AuthenticationFlowDescriptor descriptor = descriptorItr.next().getValue(); if (descriptor.isForcedAuthenticationSupported()) { log.debug("{} Retaining flow {}, it supports forced authentication", getLogPrefix(), descriptor.getId()); } else { log.debug("{} Removing flow {}, it does not support forced authentication", getLogPrefix(), descriptor.getId()); descriptorItr.remove(); } } if (potentialFlows.size() == 0) { log.info("{} No potential authentication flows remain after filtering", getLogPrefix()); } else { log.debug("{} Potential authentication flows left after filtering: {}", getLogPrefix(), potentialFlows); } }
for (final AuthenticationResult result : activeResults.values()) { final AuthenticationFlowDescriptor flow = availableFlows.get(result.getAuthenticationFlowId()); if (flow != null && flow.getReuseCondition().apply(profileRequestContext) && predicate.apply(result)) { selectActiveResult(profileRequestContext, authenticationContext, result); if (predicate != null) { for (final AuthenticationFlowDescriptor descriptor : potentialFlows.values()) { if (!authenticationContext.getIntermediateFlows().containsKey(descriptor.getId()) && predicate.apply(descriptor) && descriptor.apply(profileRequestContext)) { final AuthenticationResult result = activeResults.get(descriptor.getId()); if (result == null || !descriptor.getReuseCondition().apply(profileRequestContext) || !predicate.apply(result)) { if (!authenticationContext.isPassive() || descriptor.isPassiveAuthenticationSupported()) { selectInactiveFlow(profileRequestContext, authenticationContext, descriptor); return;
candidate.getAuthenticationFlowId()); if (descriptor != null) { if (descriptor.apply(profileRequestContext)) { if (descriptor.isResultActive(candidate)) { if (authenticationContext.getMaxAge() > 0 && candidate.getAuthenticationInstant() + authenticationContext.getMaxAge() log.debug("{} Result from login flow {} has expired", getLogPrefix(), descriptor.getId());
/** * Return the first inactive potential flow not found in the intermediate flows collection that applies * to the request. * * @param profileRequestContext the current profile request context * @param authenticationContext the current authentication context * @return an eligible flow, or null */ @Nullable private AuthenticationFlowDescriptor getUnattemptedInactiveFlow( @Nonnull final ProfileRequestContext profileRequestContext, @Nonnull final AuthenticationContext authenticationContext) { AuthenticationFlowDescriptor selectedFlow = null; for (final AuthenticationFlowDescriptor flow : authenticationContext.getPotentialFlows().values()) { if (!authenticationContext.getIntermediateFlows().containsKey(flow.getId())) { if (!authenticationContext.isPassive() || flow.isPassiveAuthenticationSupported()) { if (flow.apply(profileRequestContext)) { selectedFlow = flow; if (preferredPrincipalCtx == null || preferredPrincipalCtx.isAcceptable(flow)) { break; } } } } } return selectedFlow; }
/** {@inheritDoc} */ @Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext, @Nonnull final AuthenticationContext authenticationContext) { final Map<String, AuthenticationFlowDescriptor> potentialFlows = authenticationContext.getPotentialFlows(); final Iterator<Entry<String, AuthenticationFlowDescriptor>> descriptorItr = potentialFlows.entrySet().iterator(); while (descriptorItr.hasNext()) { final AuthenticationFlowDescriptor descriptor = descriptorItr.next().getValue(); if (descriptor.isPassiveAuthenticationSupported()) { log.debug("{} Retaining flow {}, it supports passive authentication", getLogPrefix(), descriptor.getId()); } else { log.debug("{} Removing flow {}, it does not support passive authentication", getLogPrefix(), descriptor.getId()); descriptorItr.remove(); } } if (potentialFlows.size() == 0) { log.info("{} No potential authentication flows remain after filtering", getLogPrefix()); } else { log.debug("{} Potential authentication flows left after filtering: {}", getLogPrefix(), potentialFlows); } } }
/** * Locate a custom {@link Principal} matching a string, supported by the flow descriptor. * * @param descriptor flow descriptor * @param method method string * * @return a custom {@link Principal} or null */ @Nullable public Principal getPrincipal(@Nonnull final AuthenticationFlowDescriptor descriptor, @Nonnull @NotEmpty final String method) { for (final Principal p :descriptor.getSupportedPrincipals()) { if (p.getName().equals(method)) { return p; } } return null; }
authenticationContext.getAvailableFlows().put(desc.getId(), desc); final String flowId = desc.getId().substring(desc.getId().indexOf('/') + 1); if (activeFlows.contains(flowId)) { if (authenticationContext.getAvailableFlows().containsKey(desc.getId()) && desc.apply(profileRequestContext)) { authenticationContext.getPotentialFlows().put(desc.getId(), desc); } else { log.debug("{} Filtered out authentication flow {}", getLogPrefix(), desc.getId()); desc.getId()); if (authenticationContext.getAvailableFlows().containsKey(desc.getId()) && desc.apply(profileRequestContext)) { authenticationContext.getPotentialFlows().put(desc.getId(), desc); } else { log.debug("{} Filtered out authentication flow {}", getLogPrefix(), desc.getId());
if (flow.getReuseCondition().apply(profileRequestContext)) { log.debug("{} Reusing active result for '{}' flow", getLogPrefix(), flowId); activeResult.setLastActivityInstantToNow(); if (authenticationContext.isPassive() && !flow.isPassiveAuthenticationSupported()) { log.error("{} Targeted login flow '{}' does not support passive authentication", getLogPrefix(), flowId); return; } else if ((authenticationContext.isForceAuthn() || authenticationContext.getMaxAge() > 0) && !flow.isForcedAuthenticationSupported()) { log.error("{} Targeted login flow '{}' does not support forced re-authentication", getLogPrefix(), flowId); ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.REQUEST_UNSUPPORTED); return; } else if (!profileRequestContext.isBrowserProfile() && !flow.isNonBrowserSupported()) { log.error("{} Targeted login flow '{}' does not support non-browser authentication", getLogPrefix(), flowId);
do { success = sessionManager.getStorageService().create(getId(), flowId, result, flow, result.getLastActivityInstant() + flow.getInactivityTimeout() + AuthenticationFlowDescriptor.STORAGE_EXPIRATION_OFFSET); if (!success) { result.getLastActivityInstant() + flow.getInactivityTimeout() + AuthenticationFlowDescriptor.STORAGE_EXPIRATION_OFFSET);
if (descriptor.isResultActive(result)) { if (maxAge > 0 && result.getAuthenticationInstant() + maxAge < now) { log.debug("{} Authentication result {} exceeds maxAge setting, skiping it", getLogPrefix(),
final AuthenticationFlowDescriptor flow = authenticationContext.getPotentialFlows().get( activeResult.getAuthenticationFlowId()); if (flow != null && flow.getReuseCondition().apply(profileRequestContext)) { resultToSelect = activeResult; if (preferredPrincipalCtx == null || preferredPrincipalCtx.isAcceptable(activeResult)) {
log.debug("{} Attempting to honor signaled flow {}", getLogPrefix(), flow.getId()); if (!authenticationContext.isForceAuthn() && flow.getReuseCondition().apply(profileRequestContext)) { activeResult = authenticationContext.getActiveResults().get(flow.getId()); } else if (authenticationContext.getInitialAuthenticationResult() != null && authenticationContext.getInitialAuthenticationResult().getAuthenticationFlowId().equals( flow.getId())) { activeResult = authenticationContext.getInitialAuthenticationResult(); } else { if (authenticationContext.isPassive() && !flow.isPassiveAuthenticationSupported()) { log.error("{} Signaled flow {} does not support passive authentication", getLogPrefix(), flow.getId()); ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_PASSIVE); return; final PrincipalEvalPredicate predicate = requestedPrincipalCtx.getPredicate(p); if (predicate != null) { if (predicate.apply(flow) && flow.apply(profileRequestContext)) { selectInactiveFlow(profileRequestContext, authenticationContext, flow); return; } else if (flow.apply(profileRequestContext)) { selectInactiveFlow(profileRequestContext, authenticationContext, flow); return; log.error("{} Signaled flow {} was not applicable to request", getLogPrefix(), flow.getId()); ActionSupport.buildEvent(profileRequestContext, authenticationContext.isPassive() ? AuthnEventIds.NO_PASSIVE : AuthnEventIds.NO_POTENTIAL_FLOW);
if (predicate != null) { for (final AuthenticationFlowDescriptor descriptor : potentialFlows.values()) { if (!authenticationContext.getIntermediateFlows().containsKey(descriptor.getId()) && predicate.apply(descriptor) && descriptor.apply(profileRequestContext)) { if (!authenticationContext.isPassive() || descriptor.isPassiveAuthenticationSupported()) { selectInactiveFlow(profileRequestContext, authenticationContext, descriptor); return;
/** * Process requested acr values if any. * * @param authorizationRequest the authorization request * @param principals the principals */ private void processRequestedAcrValuesIfAny(final AuthorizationRequest authorizationRequest, final List<Principal> principals) { if (authorizationRequest.getExtensions().containsKey(OIDCConstants.ACR_VALUES)) { final String[] acrValues = authorizationRequest.getExtensions() .get(OIDCConstants.ACR_VALUES).toString().split(" "); for (final String acrValue : acrValues) { final AuthnContextClassRefPrincipal requestedPrincipal = new AuthnContextClassRefPrincipal(acrValue.trim()); for (final AuthenticationFlowDescriptor flow : this.availableAuthenticationFlows) { if (!principals.contains(requestedPrincipal) && flow.getSupportedPrincipals().contains(requestedPrincipal)) { principals.add(requestedPrincipal); } } } } }
/** {@inheritDoc} */ @Override public void updateAuthenticationResultActivity(@Nonnull final AuthenticationResult result) throws SessionException { final String flowId = result.getAuthenticationFlowId(); final AuthenticationFlowDescriptor flow = sessionManager.getAuthenticationFlowDescriptor(flowId); if (flow != null) { try { if (!sessionManager.getStorageService().updateExpiration(getId(), result.getAuthenticationFlowId(), result.getLastActivityInstant() + flow.getInactivityTimeout() + AuthenticationFlowDescriptor.STORAGE_EXPIRATION_OFFSET)) { log.warn("Skipping update, AuthenticationResult for flow {} in session {} not found in storage", flowId, getId()); } } catch (final IOException e) { log.error("Exception updating AuthenticationResult expiration for session {} and flow {}", getId(), flowId, e); if (!sessionManager.isMaskStorageFailure()) { throw new SessionException("Exception updating AuthenticationResult expiration in storage", e); } } } else { log.warn("No flow descriptor installed for ID {}, unable to update result in storage", flowId); } }
/** {@inheritDoc} */ @Override public int hashCode() { return getId().hashCode(); }