package org.apereo.cas.web.flow;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.binding.mapping.Mapper;
import org.springframework.binding.mapping.impl.DefaultMapping;
import org.springframework.webflow.definition.FlowDefinition;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.engine.ActionState;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.SubflowAttributeMapper;
import org.springframework.webflow.engine.SubflowState;
import org.springframework.webflow.engine.TransitionableState;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* The {@link AbstractCasMultifactorWebflowConfigurer} is responsible for
* providing an entry point into the CAS webflow.
*
* @author Misagh Moayyed
* @since 4.2
*/
public abstract class AbstractCasMultifactorWebflowConfigurer extends AbstractCasWebflowConfigurer {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractCasMultifactorWebflowConfigurer.class);
public AbstractCasMultifactorWebflowConfigurer(final FlowBuilderServices flowBuilderServices,
final FlowDefinitionRegistry loginFlowDefinitionRegistry) {
super(flowBuilderServices, loginFlowDefinitionRegistry);
}
/**
* Register flow definition into login flow registry.
*
* @param sourceRegistry the source registry
*/
protected void registerMultifactorFlowDefinitionIntoLoginFlowRegistry(final FlowDefinitionRegistry sourceRegistry) {
final String[] flowIds = sourceRegistry.getFlowDefinitionIds();
for (final String flowId : flowIds) {
final FlowDefinition definition = sourceRegistry.getFlowDefinition(flowId);
LOGGER.debug("Registering flow definition [{}]", flowId);
this.loginFlowDefinitionRegistry.registerFlowDefinition(definition);
}
}
private void ensureEndStateTransitionExists(final TransitionableState state, final Flow mfaProviderFlow,
final String transId, final String stateId) {
if (!containsTransition(state, transId)) {
createTransitionForState(state, transId, stateId);
if (!containsFlowState(mfaProviderFlow, stateId)) {
createEndState(mfaProviderFlow, stateId);
}
}
}
/**
* Augment mfa provider flow registry.
*
* @param mfaProviderFlowRegistry the mfa provider flow registry
*/
protected void augmentMfaProviderFlowRegistry(final FlowDefinitionRegistry mfaProviderFlowRegistry) {
final String[] flowIds = mfaProviderFlowRegistry.getFlowDefinitionIds();
Arrays.stream(flowIds).forEach(id -> {
final Flow flow = Flow.class.cast(mfaProviderFlowRegistry.getFlowDefinition(id));
if (containsFlowState(flow, CasWebflowConstants.TRANSITION_ID_REAL_SUBMIT)) {
final ActionState submit = (ActionState) flow.getState(CasWebflowConstants.TRANSITION_ID_REAL_SUBMIT);
ensureEndStateTransitionExists(submit, flow,
CasWebflowConstants.TRANSITION_ID_SUCCESS, CasWebflowConstants.STATE_ID_SUCCESS);
ensureEndStateTransitionExists(submit, flow,
CasWebflowConstants.TRANSITION_ID_SUCCESS_WITH_WARNINGS, CasWebflowConstants.TRANSITION_ID_SUCCESS_WITH_WARNINGS);
}
});
}
/**
* Register multifactor provider authentication webflow.
*
* @param flow the flow
* @param subflowId the subflow id
* @param mfaProviderFlowRegistry the registry
*/
protected void registerMultifactorProviderAuthenticationWebflow(final Flow flow, final String subflowId,
final FlowDefinitionRegistry mfaProviderFlowRegistry) {
final SubflowState subflowState = createSubflowState(flow, subflowId, subflowId);
final ActionState actionState = (ActionState) flow.getState(CasWebflowConstants.TRANSITION_ID_REAL_SUBMIT);
final String targetSuccessId = actionState.getTransition(CasWebflowConstants.TRANSITION_ID_SUCCESS).getTargetStateId();
final String targetWarningsId = actionState.getTransition(CasWebflowConstants.TRANSITION_ID_SUCCESS_WITH_WARNINGS).getTargetStateId();
final List<DefaultMapping> mappings = new ArrayList<>();
final Mapper inputMapper = createMapperToSubflowState(mappings);
final SubflowAttributeMapper subflowMapper = createSubflowAttributeMapper(inputMapper, null);
subflowState.setAttributeMapper(subflowMapper);
subflowState.getTransitionSet().add(createTransition(CasWebflowConstants.TRANSITION_ID_SUCCESS, targetSuccessId));
subflowState.getTransitionSet().add(createTransition(CasWebflowConstants.TRANSITION_ID_SUCCESS_WITH_WARNINGS, targetWarningsId));
LOGGER.debug("Retrieved action state [{}]", actionState.getId());
createTransitionForState(actionState, subflowId, subflowId);
registerMultifactorFlowDefinitionIntoLoginFlowRegistry(mfaProviderFlowRegistry);
augmentMfaProviderFlowRegistry(mfaProviderFlowRegistry);
final TransitionableState state = flow.getTransitionableState(CasWebflowConstants.TRANSITION_ID_INITIAL_AUTHN_REQUEST_VALIDATION_CHECK);
createTransitionForState(state, subflowId, subflowId);
}
}