<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns="http://www.springframework.org/schema/webflow"
      xsi:schemaLocation="http://www.springframework.org/schema/webflow
                          http://www.springframework.org/schema/webflow/spring-webflow.xsd">

    <var name="credential" class="org.jasig.cas.authentication.UsernamePasswordCredential"/>

    <!--
    <var name="credential" class="org.jasig.cas.authentication.RememberMeUsernamePasswordCredential" />
    -->
    <on-start>
        <evaluate expression="initialFlowSetupAction"/>
    </on-start>

    <action-state id="ticketGrantingTicketCheck">
        <evaluate expression="ticketGrantingTicketCheckAction"/>
        <transition on="notExists" to="gatewayRequestCheck"/>
        <transition on="invalid" to="terminateSession"/>
        <transition on="valid" to="hasServiceCheck"/>
    </action-state>

    <action-state id="terminateSession">
        <evaluate expression="terminateSessionAction.terminate(flowRequestContext)"/>
        <transition to="gatewayRequestCheck"/>
    </action-state>

    <decision-state id="gatewayRequestCheck">
        <if test="requestParameters.gateway != '' and requestParameters.gateway != null and flowScope.service != null"
            then="gatewayServicesManagementCheck" else="serviceAuthorizationCheck"/>
    </decision-state>

    <decision-state id="hasServiceCheck">
        <if test="flowScope.service != null" then="renewRequestCheck" else="viewGenericLoginSuccess"/>
    </decision-state>

    <decision-state id="renewRequestCheck">
        <if test="requestParameters.renew != '' and requestParameters.renew != null" then="serviceAuthorizationCheck"
            else="generateServiceTicket"/>
    </decision-state>

    <!-- Do a service authorization check early without the need to login first -->
    <action-state id="serviceAuthorizationCheck">
        <evaluate expression="serviceAuthorizationCheck"/>
        <transition to="initializeLogin"/>
    </action-state>

    <!--
        The "warn" action makes the determination of whether to redirect directly to the requested
        service or display the "confirmation" page to go back to the server.
    -->
    <decision-state id="warn">
        <if test="flowScope.warnCookieValue" then="showWarningView" else="redirect"/>
    </decision-state>

    <action-state id="initializeLogin">
        <evaluate expression="'success'"/>
        <transition on="success" to="viewLoginForm"/>
    </action-state>

    <view-state id="viewLoginForm" view="casLoginView" model="credential">
        <binder>
            <binding property="username" required="true"/>
            <binding property="password" required="true"/>

            <!--
            <binding property="rememberMe" />
            -->
        </binder>
        <on-entry>
            <set name="viewScope.commandName" value="'credential'"/>

            <!--
            <evaluate expression="samlMetadataUIParserAction" />
            -->
        </on-entry>
        <transition on="submit" bind="true" validate="true" to="realSubmit"/>
    </view-state>

    <action-state id="realSubmit">
        <evaluate
                expression="authenticationViaFormAction.submit(flowRequestContext, flowScope.credential, messageContext)"/>
        <transition on="warn" to="warn"/>
        <!--
        To enable AUP workflows, replace the 'success' transition with the following:
        <transition on="success" to="acceptableUsagePolicyCheck" />
        -->
        <transition on="success" to="sendTicketGrantingTicket"/>
        <transition on="successWithWarnings" to="showMessages"/>
        <transition on="authenticationFailure" to="handleAuthenticationFailure"/>
        <transition on="error" to="initializeLogin"/>
    </action-state>

    <view-state id="showMessages" view="casLoginMessageView">
        <on-entry>
            <evaluate expression="sendTicketGrantingTicketAction"/>
            <set name="requestScope.messages" value="messageContext.allMessages"/>
        </on-entry>
        <transition on="proceed" to="serviceCheck"/>
    </view-state>

    <action-state id="handleAuthenticationFailure">
        <evaluate expression="authenticationExceptionHandler.handle(currentEvent.attributes.error, messageContext)"/>
        <transition on="AccountDisabledException" to="casAccountDisabledView"/>
        <transition on="AccountLockedException" to="casAccountLockedView"/>
        <transition on="AccountPasswordMustChangeException" to="casMustChangePassView"/>
        <transition on="CredentialExpiredException" to="casExpiredPassView"/>
        <transition on="InvalidLoginLocationException" to="casBadWorkstationView"/>
        <transition on="InvalidLoginTimeException" to="casBadHoursView"/>
        <transition on="FailedLoginException" to="initializeLogin"/>
        <transition on="AccountNotFoundException" to="initializeLogin"/>
        <transition on="UNKNOWN" to="initializeLogin"/>
    </action-state>

    <action-state id="sendTicketGrantingTicket">
        <evaluate expression="sendTicketGrantingTicketAction"/>
        <transition to="serviceCheck"/>
    </action-state>

    <decision-state id="serviceCheck">
        <if test="flowScope.service != null" then="generateServiceTicket" else="viewGenericLoginSuccess"/>
    </decision-state>

    <action-state id="generateServiceTicket">
        <evaluate expression="generateServiceTicketAction"/>
        <transition on="success" to="warn"/>
        <transition on="authenticationFailure" to="handleAuthenticationFailure"/>
        <transition on="error" to="initializeLogin"/>
        <transition on="gateway" to="gatewayServicesManagementCheck"/>
    </action-state>

    <action-state id="gatewayServicesManagementCheck">
        <evaluate expression="gatewayServicesManagementCheck"/>
        <transition on="success" to="redirect"/>
    </action-state>

    <action-state id="redirect">
        <evaluate expression="flowScope.service.getResponse(requestScope.serviceTicketId)"
                  result-type="org.jasig.cas.authentication.principal.Response" result="requestScope.response"/>
        <transition to="postRedirectDecision"/>
    </action-state>

    <decision-state id="postRedirectDecision">
        <if test="requestScope.response.responseType.name() == 'POST'" then="postView" else="redirectView"/>
    </decision-state>

    <!--
        the "viewGenericLoginSuccess" is the end state for when a user attempts to login without coming directly from a service.
        They have only initialized their single-sign on session.
    -->

    <end-state id="viewGenericLoginSuccess" view="casGenericSuccessView">
        <on-entry>
            <evaluate expression="genericSuccessViewAction.getAuthenticationPrincipal(flowScope.ticketGrantingTicketId)"
                      result="requestScope.principal"
                      result-type="org.jasig.cas.authentication.principal.Principal"/>
        </on-entry>
    </end-state>


    <!--
    The "showWarningView" end state is the end state for when the user has requested privacy settings (to be "warned")
    to be turned on.  It delegates to a view defines in default_views.properties that display the
    "Please click here to go to the service." message.
    -->
    <end-state id="showWarningView" view="casConfirmView"/>

    <!-- Password policy failure states -->
    <end-state id="abstactPasswordChangeView">
        <on-entry>
            <set name="flowScope.passwordPolicyUrl" value="passwordPolicyConfiguration.passwordPolicyUrl"/>
        </on-entry>
    </end-state>
    <end-state id="casExpiredPassView" view="casExpiredPassView" parent="#abstactPasswordChangeView"/>
    <end-state id="casMustChangePassView" view="casMustChangePassView" parent="#abstactPasswordChangeView"/>
    <end-state id="casAccountDisabledView" view="casAccountDisabledView"/>
    <end-state id="casAccountLockedView" view="casAccountLockedView"/>
    <end-state id="casBadHoursView" view="casBadHoursView"/>
    <end-state id="casBadWorkstationView" view="casBadWorkstationView"/>

    <end-state id="postView" view="postResponseView">
        <on-entry>
            <set name="requestScope.parameters" value="requestScope.response.attributes"/>
            <set name="requestScope.originalUrl" value="flowScope.service.id"/>
        </on-entry>
    </end-state>

    <!--
        The "redirect" end state allows CAS to properly end the workflow while still redirecting
        the user back to the service required.
    -->
    <end-state id="redirectView" view="externalRedirect:#{requestScope.response.url}"/>

    <end-state id="viewServiceErrorView" view="serviceErrorView"/>

    <decision-state id="serviceUnauthorizedCheck">
        <if test="flowScope.unauthorizedRedirectUrl != null"
            then="viewRedirectToUnauthorizedUrlView"
            else="viewServiceErrorView"/>
    </decision-state>

    <end-state id="viewRedirectToUnauthorizedUrlView" view="externalRedirect:#{flowScope.unauthorizedRedirectUrl}"/>
    <end-state id="viewServiceSsoErrorView" view="serviceErrorSsoView" />

    <global-transitions>
        <transition to="viewLoginForm" on-exception="org.jasig.cas.services.UnauthorizedSsoServiceException"/>
        <transition to="viewServiceErrorView"
                    on-exception="org.springframework.webflow.execution.repository.NoSuchFlowExecutionException"/>
        <transition to="serviceUnauthorizedCheck" on-exception="org.jasig.cas.services.UnauthorizedServiceException"/>
        <transition to="serviceUnauthorizedCheck" on-exception="org.jasig.cas.services.UnauthorizedServiceForPrincipalException" />
    </global-transitions>
</flow>
