Upgrading to CAS 7, webflow no longer working

219 views
Skip to first unread message

Neil Bhadsavle

unread,
Sep 20, 2024, 2:18:17 PM9/20/24
to CAS Community
While upgrading to CAS 7, I am running into issues with the Action we are adding to the webflow saying it can no longer find the method:

Here are the errors I am getting:

casuseradmin-1  | 2024-09-20 15:03:15,051 DEBUG [org.springframework.webflow.execution.ActionExecutor] - <Finished executing org.apereo.cas.web.flow.login.InitialAuthenticationRequestValidationAction@7ac00160; result = success>
casuseradmin-1  | 2024-09-20 15:03:15,051 DEBUG [org.springframework.webflow.execution.ActionExecutor] - <Finished executing [EvaluateAction@45fe3bfc expression = initialAuthenticationRequestValidationAction, resultExpression = [null]]; result = success>
casuseradmin-1  | 2024-09-20 15:03:15,052 DEBUG [org.springframework.webflow.engine.Transition] - <Executing [Transition@6080e1b6 on = success, to = ticketGrantingTicketCheck]>
casuseradmin-1  | 2024-09-20 15:03:15,052 DEBUG [org.springframework.webflow.engine.Transition] - <Exiting state 'initialAuthenticationRequestValidationCheck'>
casuseradmin-1  | 2024-09-20 15:03:15,052 DEBUG [org.springframework.webflow.engine.ActionState] - <Entering state 'ticketGrantingTicketCheck' of flow 'login'>
casuseradmin-1  | 2024-09-20 15:03:15,052 DEBUG [org.springframework.webflow.execution.ActionExecutor] - <Executing [EvaluateAction@3a2eaea6 expression = ticketGrantingTicketCheckAction, resultExpression = [null]]>
casuseradmin-1  | 2024-09-20 15:03:15,058 DEBUG [org.springframework.webflow.execution.ActionExecutor] - <Executing org.apereo.cas.web.flow.login.TicketGrantingTicketCheckAction@68d8359a>
casuseradmin-1  | 2024-09-20 15:03:15,058 DEBUG [org.springframework.webflow.execution.ActionExecutor] - <Finished executing org.apereo.cas.web.flow.login.TicketGrantingTicketCheckAction@68d8359a; result = notExists>
casuseradmin-1  | 2024-09-20 15:03:15,058 DEBUG [org.springframework.webflow.execution.ActionExecutor] - <Finished executing [EvaluateAction@3a2eaea6 expression = ticketGrantingTicketCheckAction, resultExpression = [null]]; result = notExists>
casuseradmin-1  | 2024-09-20 15:03:15,059 DEBUG [org.springframework.webflow.engine.Transition] - <Executing [Transition@2a181fdd on = notExists, to = gatewayRequestCheck]>
casuseradmin-1  | 2024-09-20 15:03:15,059 DEBUG [org.springframework.webflow.engine.Transition] - <Exiting state 'ticketGrantingTicketCheck'>
casuseradmin-1  | 2024-09-20 15:03:15,059 DEBUG [org.springframework.webflow.engine.DecisionState] - <Entering state 'gatewayRequestCheck' of flow 'login'>
casuseradmin-1  | 2024-09-20 15:03:15,061 DEBUG [org.springframework.webflow.engine.Transition] - <Executing [Transition@e1476bb on = *, to = serviceAuthorizationCheck]>
casuseradmin-1  | 2024-09-20 15:03:15,062 DEBUG [org.springframework.webflow.engine.Transition] - <Exiting state 'gatewayRequestCheck'>
casuseradmin-1  | 2024-09-20 15:03:15,062 DEBUG [org.springframework.webflow.engine.ActionState] - <Entering state 'serviceAuthorizationCheck' of flow 'login'>
casuseradmin-1  | 2024-09-20 15:03:15,062 DEBUG [org.springframework.webflow.execution.ActionExecutor] - <Executing [EvaluateAction@716ea9c3 expression = serviceAuthorizationCheck, resultExpression = [null]]>
casuseradmin-1  | 2024-09-20 15:03:15,072 DEBUG [org.springframework.webflow.execution.ActionExecutor] - <Executing org.apereo.cas.web.flow.ServiceAuthorizationCheckAction@3934d4c0>
casuseradmin-1  | 2024-09-20 15:03:15,073 DEBUG [org.springframework.webflow.execution.ActionExecutor] - <Finished executing org.apereo.cas.web.flow.ServiceAuthorizationCheckAction@3934d4c0; result = success>
casuseradmin-1  | 2024-09-20 15:03:15,073 DEBUG [org.springframework.webflow.execution.ActionExecutor] - <Finished executing [EvaluateAction@716ea9c3 expression = serviceAuthorizationCheck, resultExpression = [null]]; result = success>
casuseradmin-1  | 2024-09-20 15:03:15,073 DEBUG [org.springframework.webflow.engine.Transition] - <Executing [Transition@470e78d4 on = *, to = initializeLoginForm]>
casuseradmin-1  | 2024-09-20 15:03:15,073 DEBUG [org.springframework.webflow.engine.Transition] - <Exiting state 'serviceAuthorizationCheck'>
casuseradmin-1  | 2024-09-20 15:03:15,073 DEBUG [org.springframework.webflow.engine.ActionState] - <Entering state 'initializeLoginForm' of flow 'login'>
casuseradmin-1  | 2024-09-20 15:03:15,073 DEBUG [org.springframework.webflow.execution.ActionExecutor] - <Executing [EvaluateAction@514d40e1 expression = initializeLoginAction, resultExpression = [null]]>
casuseradmin-1  | 2024-09-20 15:03:15,078 DEBUG [org.springframework.webflow.execution.ActionExecutor] - <Executing org.apereo.cas.web.flow.login.InitializeLoginAction@7f63fa0a>
casuseradmin-1  | 2024-09-20 15:03:15,079 DEBUG [org.springframework.webflow.execution.ActionExecutor] - <Finished executing org.apereo.cas.web.flow.login.InitializeLoginAction@7f63fa0a; result = success>
casuseradmin-1  | 2024-09-20 15:03:15,079 DEBUG [org.springframework.webflow.execution.ActionExecutor] - <Finished executing [EvaluateAction@514d40e1 expression = initializeLoginAction, resultExpression = [null]]; result = success>
casuseradmin-1  | 2024-09-20 15:03:15,079 DEBUG [org.springframework.webflow.engine.Transition] - <Executing [Transition@2a2c0e3f on = success, to = afterInitializeLoginForm]>
casuseradmin-1  | 2024-09-20 15:03:15,079 DEBUG [org.springframework.webflow.engine.Transition] - <Exiting state 'initializeLoginForm'>
casuseradmin-1  | 2024-09-20 15:03:15,079 DEBUG [org.springframework.webflow.engine.ActionState] - <Entering state 'afterInitializeLoginForm' of flow 'login'>
casuseradmin-1  | 2024-09-20 15:03:15,079 DEBUG [org.springframework.webflow.execution.ActionExecutor] - <Executing [SetAction@27e2ca6f name = requestScope.initialized, value = true]>
casuseradmin-1  | 2024-09-20 15:03:15,080 DEBUG [org.springframework.webflow.execution.ActionExecutor] - <Finished executing [SetAction@27e2ca6f name = requestScope.initialized, value = true]; result = success>
casuseradmin-1  | 2024-09-20 15:03:15,080 DEBUG [org.springframework.webflow.engine.Transition] - <Executing [Transition@3fdf7956 on = success, to = startTempAuthenticate]>
casuseradmin-1  | 2024-09-20 15:03:15,080 DEBUG [org.springframework.webflow.engine.Transition] - <Exiting state 'afterInitializeLoginForm'>
casuseradmin-1  | 2024-09-20 15:03:15,080 DEBUG [org.springframework.webflow.engine.ActionState] - <Entering state 'startTempAuthenticate' of flow 'login'>
casuseradmin-1  | 2024-09-20 15:03:15,081 DEBUG [org.springframework.webflow.execution.ActionExecutor] - <Executing [EvaluateAction@78ddce4 expression = tempTicketCheck, resultExpression = [null]]>
casuseradmin-1  | 2024-09-20 15:03:15,086 DEBUG [org.springframework.webflow.engine.impl.FlowExecutionImpl] - <Attempting to handle [org.springframework.webflow.execution.ActionExecutionException: Exception thrown executing [EvaluateAction@78ddce4 expression = tempTicketCheck, resultExpression = [null]] in state 'startTempAuthenticate' of flow 'login' -- action execution attributes were 'map[[empty]]'] with root cause [org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'tempTicketCheck' cannot be found on object of type 'org.springframework.webflow.engine.impl.RequestControlContextImpl' - maybe not public or not valid?]>
casuseradmin-1  | 2024-09-20 15:03:15,087 DEBUG [org.springframework.webflow.engine.impl.FlowExecutionImpl] - <Rethrowing unhandled flow execution exception>
casuseradmin-1  | 2024-09-20 15:03:15,088 DEBUG [org.apereo.cas.web.FlowExecutionExceptionResolver] - <Ignoring the received exception [org.springframework.webflow.execution.ActionExecutionException: Exception thrown executing [EvaluateAction@78ddce4 expression = tempTicketCheck, resultExpression = [null]] in state 'startTempAuthenticate' of flow 'login' -- action execution attributes were 'map[[empty]]'] due to a type mismatch with handler [[FlowHandlerMapping.DefaultFlowHandler@3d5f2f9f]]>

2024-09-20 15:03:15,092 ERROR [org.apereo.cas.web.support.filters.AbstractSecurityFilter] - <Request processing failed: org.springframework.webflow.execution.ActionExecutionException: Exception thrown executing [EvaluateAction@78ddce4 expression = tempTicketCheck, resultExpression = [null]] in state 'startTempAuthenticate' of flow 'login' -- action execution attributes were 'map[[empty]]'>

2024-09-20 15:03:15,094 ERROR [org.springframework.boot.web.servlet.support.ErrorPageFilter] - <Forwarding to error page from request [/login] due to exception [jakarta.servlet.ServletException: Request processing failed: org.springframework.webflow.execution.ActionExecutionException: Exception thrown executing [EvaluateAction@78ddce4 expression = tempTicketCheck, resultExpression = [null]] in state 'startTempAuthenticate' of flow 'login' -- action execution attributes were 'map[[empty]]']>

Here is the method that sets up startTempAuthenticate:

@Bean(name = "tempTicketCheck")
public Action tempTicketCheck() {
return new TemporaryTicketNonInteractiveAction(initialAuthenticationAttemptWebflowEventResolver.getIfAvailable(),
serviceTicketRequestWebflowEventResolver.getIfAvailable(),
adaptiveAuthenticationPolicy.getIfAvailable());
}


Here is the doInitialize setting up the webflow:

@Override
protected void doInitialize() {
val flow = getLoginFlow();
if (flow != null) {
val actionState = createActionState(flow, CasWebflowConstants.STATE_ID_X509_START,
CasWebflowConstants.ACTION_ID_X509_CHECK);
val transitionSet = actionState.getTransitionSet();

//Adding x509 webflow transition
transitionSet.add(createTransition(CasWebflowConstants.TRANSITION_ID_SUCCESS,
CasWebflowConstants.STATE_ID_CREATE_TICKET_GRANTING_TICKET));
transitionSet.add(createTransition(CasWebflowConstants.TRANSITION_ID_WARN,
CasWebflowConstants.TRANSITION_ID_WARN));
transitionSet.add(createTransition(CasWebflowConstants.TRANSITION_ID_ERROR, getStateIdOnX509Failure(flow)));
transitionSet.add(createTransition(CasWebflowConstants.TRANSITION_ID_AUTHENTICATION_FAILURE,
CasWebflowConstants.STATE_ID_HANDLE_AUTHN_FAILURE));
transitionSet.add(createTransition(CasWebflowConstants.TRANSITION_ID_SUCCESS_WITH_WARNINGS,
CasWebflowConstants.STATE_ID_SHOW_AUTHN_WARNING_MSGS));

actionState.getExitActionList().add(createEvaluateAction(CasWebflowConstants.ACTION_ID_CLEAR_WEBFLOW_CREDENTIALS));

//Adding Temp ticket webflow transition

val tempTicketActionState = createActionState(flow, EVENT_ID_START_TEMP_TICKET, createEvaluateAction("tempTicketCheck"));
val tempTicketTransitionSet = tempTicketActionState.getTransitionSet();

tempTicketTransitionSet.add(createTransition(CasWebflowConstants.TRANSITION_ID_SUCCESS, CasWebflowConstants.STATE_ID_CREATE_TICKET_GRANTING_TICKET));
tempTicketTransitionSet.add(createTransition(CasWebflowConstants.TRANSITION_ID_WARN, CasWebflowConstants.TRANSITION_ID_WARN));
tempTicketTransitionSet.add(createTransition(CasWebflowConstants.TRANSITION_ID_AUTHENTICATION_FAILURE,
CasWebflowConstants.STATE_ID_HANDLE_AUTHN_FAILURE));
tempTicketTransitionSet.add(createTransition(CasWebflowConstants.TRANSITION_ID_ERROR, getStateIdOnX509Failure(flow)));
tempTicketTransitionSet.add(createTransition(CasWebflowConstants.TRANSITION_ID_SUCCESS_WITH_WARNINGS,
CasWebflowConstants.STATE_ID_SHOW_AUTHN_WARNING_MSGS));

tempTicketActionState.getExitActionList().add(createEvaluateAction(CasWebflowConstants.ACTION_ID_CLEAR_WEBFLOW_CREDENTIALS));

//Injecting the temp ticket webflow before the x509 webflow and to transition to x509 when a temp ticket doesn't exist
val state = getState(flow, CasWebflowConstants.STATE_ID_AFTER_INIT_LOGIN_FORM, ActionState.class);
createTransitionForState(state, CasWebflowConstants.TRANSITION_ID_SUCCESS, EVENT_ID_START_TEMP_TICKET, true);

val initState = getState(flow, EVENT_ID_START_TEMP_TICKET, ActionState.class);
createTransitionForState(initState, CasWebflowConstants.TRANSITION_ID_ERROR,
CasWebflowConstants.STATE_ID_X509_START, true);
}
}

Neil Bhadsavle

unread,
Sep 20, 2024, 4:48:35 PM9/20/24
to CAS Community, Neil Bhadsavle
This is the TemporaryTicketNonInteractiveAction just in case that helps:

package edu.gatech.gtri.cas.web.flow;

import edu.gatech.gtri.cas.authentication.principal.AfwpTempTicketCredential;
import org.apereo.cas.authentication.Credential;
import org.apereo.cas.authentication.adaptive.AdaptiveAuthenticationPolicy;
import org.apereo.cas.web.flow.actions.AbstractNonInteractiveCredentialsAction;
import org.apereo.cas.web.flow.authentication.DefaultCasWebflowExceptionCatalog;
import org.apereo.cas.web.flow.authentication.CasWebflowExceptionHandler;
import org.apereo.cas.web.flow.resolver.CasDelegatingWebflowEventResolver;
import org.apereo.cas.web.flow.resolver.CasWebflowEventResolver;
import org.apereo.cas.web.support.WebUtils;

import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.webflow.execution.RequestContext;
import org.apache.commons.lang3.StringUtils;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.core.collection.LocalAttributeMap;
import org.springframework.webflow.execution.Event;

import org.springframework.webflow.execution.Action;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.apereo.cas.web.flow.actions.AuthenticationExceptionHandlerAction;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.configuration.model.core.web.MessageBundleProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;

import edu.gatech.gtri.cas.authentication.casuseradmin.InvalidTempTicketException;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;

/**
* Fully overlaid here from the core implementation package, so CAS config uses this class instead of its own
* version, as there is no conditional bean definition available for this class as of version 6.0.3.
* <p>
* This implementation uses custom <code>AfwpX509CertificateCredential</code>
*/
@Slf4j
public class TemporaryTicketNonInteractiveAction extends AbstractNonInteractiveCredentialsAction {

@Autowired
private ConfigurableApplicationContext applicationContext;

/**
* Attribute to indicate the x509 certificate.
*/
private static final long serialVersionUID = 4789797148634156909L;

public TemporaryTicketNonInteractiveAction(final CasDelegatingWebflowEventResolver initialAuthenticationAttemptWebflowEventResolver,
final CasWebflowEventResolver serviceTicketRequestWebflowEventResolver,
final AdaptiveAuthenticationPolicy adaptiveAuthenticationPolicy) {

super(initialAuthenticationAttemptWebflowEventResolver, serviceTicketRequestWebflowEventResolver,
adaptiveAuthenticationPolicy);
}

@Override
protected Credential constructCredentialsFromRequest(final RequestContext context) {
val request = WebUtils.getHttpServletRequestFromExternalWebflowContext(context);
final String loginTicketHash = context.getRequestParameters().get("onetimeTicket");

if (loginTicketHash != null && !loginTicketHash.isEmpty()) {
return new AfwpTempTicketCredential(loginTicketHash);
}
return null;
}

/*
@ConditionalOnMissingBean(name = "authenticationExceptionHandler2")
@Bean
public Action authenticationExceptionHandler() {
Look at org/apereo/cas/web/flow/config/CasCoreWebflowConfiguration.java for reference.
return new AuthenticationExceptionHandlerAction(handledAuthenticationExceptions(),
MessageBundleProperties.DEFAULT_BUNDLE_PREFIX_AUTHN_FAILURE);
}*/

@ConditionalOnMissingBean(name = "authenticationExceptionHandler2")
@Bean
public Action authenticationExceptionHandler() {
val beans = applicationContext.getBeansOfType(CasWebflowExceptionHandler.class, false, true);
val handlers = new ArrayList<>(beans.values());
AnnotationAwareOrderComparator.sort(handlers);
return new AuthenticationExceptionHandlerAction(handlers);
}

@RefreshScope
@Bean
public DefaultCasWebflowExceptionCatalog handledAuthenticationExceptions() {
System.out.println("--------- handleAuthenticationExceptions");
/*
* Order is important here; We want the account policy exceptions to be handled
* first before moving onto more generic errors. In the event that multiple handlers
* are defined, where one fails due to account policy restriction and one fails
* due to a bad password, we want the error associated with the account policy
* to be processed first, rather than presenting a more generic error associated
*/
val errors = new DefaultCasWebflowExceptionCatalog();;
errors.registerException(InvalidTempTicketException.class);
// errors.add(javax.security.auth.login.AccountLockedException.class);
// errors.add(javax.security.auth.login.CredentialExpiredException.class);
// errors.add(javax.security.auth.login.AccountExpiredException.class);
// errors.add(AccountDisabledException.class);
// errors.add(InvalidLoginLocationException.class);
// errors.add(AccountPasswordMustChangeException.class);
// errors.add(InvalidLoginTimeException.class);

// errors.add(javax.security.auth.login.AccountNotFoundException.class);
// errors.add(javax.security.auth.login.FailedLoginException.class);
// errors.add(UnauthorizedServiceForPrincipalException.class);
// errors.add(PrincipalException.class);
// errors.add(UnsatisfiedAuthenticationPolicyException.class);
// errors.add(UnauthorizedAuthenticationException.class);

// errors.addAll(casProperties.getAuthn().getExceptions().getExceptions());

return errors;
}
}

Neil Bhadsavle

unread,
Sep 24, 2024, 2:20:29 AM9/24/24
to CAS Community, Neil Bhadsavle
I was able to figure out the issue for any future people that may be running into this issue:

This was due to upgrading Spring Boot from 2.x to 3.x, the spring.factories file is no longer used so you need to move the contents of that file into the resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

then replacing @Configuration in the file that contains the bean to @AutoConfiguration along with adding import org.springframework.boot.autoconfigure.AutoConfiguration;
Reply all
Reply to author
Forward
0 new messages