LDAP Password Policy Enforcement 3.4.10 Upgrade Notes

Starting with the patched cas-server-support-ldap-pwd-expiration-3.4.8, I pulled in all the changes from CAS Server 3.4.10.  These are the merge notes.

Resources

The LPPE module currently has files under /src/main/resources/ that map to cas-server-webapp/src/main/webapp/. These files could be applied via Maven Overlay if they were in the right directory.

resources/classes/ -> webapp/WEB-INF/classes/

default_views.properties

  • added additional views for LPPE

protocol_views.properties

  • added postWarnPassResponseView class and url for casWarnPassPostResponseView.jsp (what is this for?)

message.properties (renamed to message_en.properties)

  • added messages for LPPE

resources/spring-configuration/ -> webapp/WEB-INF/spring-configuration/

applicationContext.xml

  • LPPE depends on a modified CASImpl.
    <bean id="centralAuthenticationService" class="org.jasig.cas.LdapPwdCentralAuthenticationServiceImpl"...
    

passwordWarningCheck.xml

  • new file defining beans for LPPE

resources/view/jsp/default/ui -> webapp/WEB-INF/view/jsp/default/ui

New UX files for LPPE

  • casAccountDisabledView.jsp
  • casAccountLockedView.jsp
  • casBadWorkstationView.jsp
  • casExpiredPassView.jsp
  • casMustChangePassword.jsp
  • casWarnPassView.jsp

resources/view/jsp/protocol -> webapp/WEB-INF/view/jsp/protocol

casWarnPassPostResponseView.jsp

  • new file

resources/cas-servlet.xml -> webapp/WEB-INF/cas-servlet.xml

Changes:

<bean id="authenticationViaFormAction" class="org.jasig.cas.web.flow.LdapPwdAuthenticationViaFormAction"
      p:centralAuthenticationService-ref="centralAuthenticationService"
      p:warnCookieGenerator-ref="warnCookieGenerator"
      p:errorProcessor-ref="firstErrorProcessor" />

resources/deployerConfigContext.xml -> webapp/WEB-INF/deployerConfigContext.xml

Changes:

<bean class="org.jasig.cas.adaptors.ldappwd.BindLdapAuthenticationHandler">
  <property name="filter" value="uid=%u" />
  <property name="searchBase" value="ou=people,dc=rutgers,dc=edu" />
  <property name="contextSource" ref="contextSource" />
  <property name="errorProcessor"  ref="firstErrorProcessor" />
</bean>

Additions:

<bean id="firstErrorProcessor" class="org.jasig.cas.adaptors.ldappwd.util.ExpiredPasswordErrorProcessor">
        <property name="nextItem">
        <bean class="org.jasig.cas.adaptors.ldappwd.util.AccountLockedErrorProcessor">
            <property name="nextItem">
            <bean class="org.jasig.cas.adaptors.ldappwd.util.MustChangePasswordErrorProcessor">
                <property name="nextItem">
                <bean class="org.jasig.cas.adaptors.ldappwd.util.BadHoursErrorProcessor">
                    <property name="nextItem">
                    <bean class="org.jasig.cas.adaptors.ldappwd.util.BadWorkstationErrorProcessor">
                        <property name="nextItem">
                            <bean class="org.jasig.cas.adaptors.ldappwd.util.AccountDisabledErrorProcessor" />
                        </property>
                    </bean>
                    </property>
                </bean>
                </property>
            </bean>
            </property>
        </bean>
        </property>
    </bean>

    <bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
        <property name="anonymousReadOnly" value="false" />
        <property name="password" value="{password_goes_here}" />
        <property name="pooled" value="true" />
        <property name="urls">
            <list>
                <value>ldap://ldap.rutgers.edu:636/</value>
                <value>ldap://ldap2.rutgers.edu:636/</value>
            </list>
        </property>
        <property name="userDn" value="{username_goes_here}" />
        <property name="baseEnvironmentProperties">
            <map>
                <entry>
                        <key><value>java.naming.security.protocol</value></key>
                        <value>ssl</value>
                    </entry>
                <entry>
                        <key><value>java.naming.security.authentication</value></key>
                        <value>simple</value>
                    </entry>
                </map>
        </property>
    </bean>

resources/login-webflow.xml -> webapp/WEB-INF/login-webflow.xml

Change:

change:
    <decision-state id="warn">
        <if test="flowScope.warnCookieValue" then="showWarningView" else="PasswordWarningCheck" />
    </decision-state>

Additions:

<!-- this checks the status of a password -->
    <action-state id="PasswordWarningCheck">
        <evaluate expression="PasswordWarningCheckAction" />
        <transition on="showWarning" to="warnPassRedirect" />
        <transition on="success" to="redirect" />
        <transition on="error" to="viewLoginForm" />
    </action-state>

        <!--
        The "warnPassRedirect" end state is the end state for when the user's password is close to expiring.  They are not required
        to change their password to reach the requested service.
        -->
    <action-state id="warnPassRedirect" >
        <evaluate expression="flowScope.service.getResponse(requestScope.serviceTicketId)" result-type="org.jasig.cas.authentication.principal.Response" result="requestScope.response" />
        <transition on="requestScope.response" to="pwdPostView" />
        <transition to="pwdRedirectView" />
    </action-state>

   <end-state id="pwdPostView" view="postWarnPassResponseView">
        <output name="viewScope.parameters" value="requestScope.response.attributes" />
        <output name="viewScope.expireDays" value="flowScope.expireDays" />
        <output name="viewScope.originalUrl" value="flowScope.service.id" />
    </end-state>

    <!--
        The "showExpiredPassView" end state is the end state for when the user's password has expired and they must be sent
        to the account management page to change their password.
        -->
    <end-state id="showExpiredPassView" view="casExpiredPassView" />

        <!--
        The "showAccountLockedView" end state is the end state for when the user's account has been locked out due to
        password failures.  They are told to try again in 15 minutes.
        -->
    <end-state id="showAccountLockedView" view="casAccountLockedView" />

    <!--
        The "showAccountDisabledView" end state is the end state for when the user's account has been disabled.  They are not
        allowed to reach any services and must call the Help Desk to re-enable service
        -->
    <end-state id="showAccountDisabledView" view="casAccountDisabledView" />

    <!--
        The "showMustChangePassView" end state is the end state for when the user must change his password and then must be sent
        to the account management page.
        -->
    <end-state id="showMustChangePassView" view="casMustChangePassView" />

        <!--
        The "showBadHoursView" end state is the end state for when the user cannot log in at this time.
        -->
    <end-state id="showBadhoursView" view="casBadHoursView" />

        <!--
        The "showBadWorkstation" end state is the end state when the user cannot log in from this worstation (hum, the server indeed)
        -->
    <end-state id="showBadWorkstationView" view="casBadWorkstationView" />

    <end-state id="pwdRedirectView" view="casWarnPassView">
        <output name="viewScope.parameters" value="requestScope.response.attributes" />
        <output name="viewScope.serviceTicketId" value="requestScope.serviceTicketId" />
        <output name="viewScope.expireDays" value="flowScope.expireDays" />
        <output name="viewScope.originalUrl" value="flowScope.service.id" />
    </end-state>

Source

org.jasig.cas.adapators.ldappwd

Bunch of new files for dealing with UX and LDAP Error translation plus a fork of org.jasig.cas.adoptors.ldap.BindLdapAuthenticationHandler in order to plugin the new behavior.

org.jasig.cas.adoptors.ldappwd.BindLdapAuthenticationHandler
Additions:

/**
     * Chaine de traitement pour les erreurs LDAP
     */
    @NotNull
    private AbstractLdapErrorDetailProcessor errorProcessor = new NoOpErrorProcessor();

    /** Log instance for logging events, info, warnings, errors, etc. */
    private final Logger log = LoggerFactory.getLogger(this.getClass());

Changes:

line 114: in catch in authenticateUsernamePasswordInternal
                String details = e.getMessage();
                this.log.debug("LDAP server returned exception message: " + details);

                // Call Treatment chain
                errorProcessor.processErrorDetail(details);

                // if we catch an exception, just try the next cn


    /**
     * @param errorProcessor Processor chain for ldap error details
     */
    public final void setErrorProcessor(final AbstractLdapErrorDetailProcessor errorProcessor) {
        this.errorProcessor = errorProcessor;
    }

org.jasig.cas.authentication

New files:

  • AbstractPasswordWarningCheck
  • PasswordWarningCheck

org.jasig.cas.web.flow

New files:

  • LdapPwdAuthenticationViaFormAction
  • PasswordWarningCheckAction

org.jasig.cas

New files:

  • LdapPwdCentralAuthenticationService
  • LdapPwdCentralAuthenticationServiceImpl (copy and fork of CASImpl)
    Additions:
    public final class LdapPwdCentralAuthenticationServiceImpl implements LdapPwdCentralAuthenticationService {
    
    
    public Principal getPrincipal(String id) {
        Principal principal = ((TicketGrantingTicket) ticketRegistry.getTicket(id)).getAuthentication().getPrincipal();
    
        return principal;
    }