/*
 * Licensed to Apereo under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Apereo licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License.  You may obtain a
 * copy of the License at the following location:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.jasig.cas.web.flow;

import org.jasig.cas.authentication.AuthenticationException;
import org.jasig.cas.authentication.OneTimePasswordFailedLoginException;
import org.jasig.cas.authentication.OneTimePasswordRequiredException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;

import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Performs two important error handling functions on an {@link AuthenticationException} raised from the authentication
 * layer:
 *
 * <ol>
 *     <li>Maps handler errors onto message bundle strings for display to user.</li>
 *     <li>Determines the next webflow state by comparing handler erors against {@link #errors}
 *     in list order. The first entry that matches determines the outcome state, which
 *     is the simple class name of the exception.</li>
 * </ol>
 *
 * @author Marvin S. Addison
 * @since 4.0.0
 */
public class OsfAuthenticationExceptionHandler {

    /** State name when no matching exception is found. */
    private static final String UNKNOWN = "UNKNOWN";

    /** Default message bundle prefix. */
    private static final String DEFAULT_MESSAGE_BUNDLE_PREFIX = "authenticationFailure.";

    /** Default list of errors this class knows how to handle. */
    private static final List<Class<? extends Exception>> DEFAULT_ERROR_LIST =
            new ArrayList<>();

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    static {
        DEFAULT_ERROR_LIST.add(javax.security.auth.login.AccountLockedException.class);
        DEFAULT_ERROR_LIST.add(javax.security.auth.login.FailedLoginException.class);
        DEFAULT_ERROR_LIST.add(javax.security.auth.login.CredentialExpiredException.class);
        DEFAULT_ERROR_LIST.add(javax.security.auth.login.AccountNotFoundException.class);
        DEFAULT_ERROR_LIST.add(org.jasig.cas.authentication.AccountDisabledException.class);
        DEFAULT_ERROR_LIST.add(org.jasig.cas.authentication.InvalidLoginLocationException.class);
        DEFAULT_ERROR_LIST.add(org.jasig.cas.authentication.AccountPasswordMustChangeException.class);
        DEFAULT_ERROR_LIST.add(org.jasig.cas.authentication.InvalidLoginTimeException.class);
        DEFAULT_ERROR_LIST.add(OneTimePasswordFailedLoginException.class);
        DEFAULT_ERROR_LIST.add(OneTimePasswordRequiredException.class);
    }

    /** Ordered list of error classes that this class knows how to handle. */
    @NotNull
    private List<Class<? extends Exception>> errors = DEFAULT_ERROR_LIST;

    /** String appended to exception class name to create a message bundle key for that particular error. */
    private String messageBundlePrefix = DEFAULT_MESSAGE_BUNDLE_PREFIX;

    /**
     * Sets the list of errors that this class knows how to handle.
     *
     * @param errors List of errors in order of descending precedence.
     */
    public void setErrors(final List<Class<? extends Exception>> errors) {
        this.errors = errors;
    }

    public final List<Class<? extends Exception>> getErrors() {
        return Collections.unmodifiableList(this.errors);
    }

    /**
     * Sets the message bundle prefix appended to exception class names to create a message bundle key for that
     * particular error.
     *
     * @param prefix Prefix appended to exception names.
     */
    public void setMessageBundlePrefix(final String prefix) {
        this.messageBundlePrefix = prefix;
    }

    /**
     * Maps an authentication exception onto a state name equal to the simple class name of the.
     *
     * @param e Authentication error to handle.
     * @param messageContext the spring message context
     * @return Name of next flow state to transition to or {@value #UNKNOWN}
     * {@link org.jasig.cas.authentication.AuthenticationException#getHandlerErrors()} with highest precedence.
     * Also sets an ERROR severity message in the message context of the form
     * <code>[messageBundlePrefix][exceptionClassSimpleName]</code> for each handler error that is
     * configured. If not match is found, {@value #UNKNOWN} is returned.
     */
    public String handle(final AuthenticationException e, final MessageContext messageContext) {
        if (e != null) {
            final MessageBuilder builder = new MessageBuilder();
            for (final Class<? extends Exception> kind : this.errors) {
                for (final Class<? extends Exception> handlerError : e.getHandlerErrors().values()) {
                    if (handlerError != null && handlerError.equals(kind)) {
                        final String handlerErrorName = handlerError.getSimpleName();
                        final String messageCode = this.messageBundlePrefix + handlerErrorName;
                        messageContext.addMessage(builder.error().code(messageCode).build());
                        return handlerErrorName;
                    }
                }

            }
        }
        final String messageCode = this.messageBundlePrefix + UNKNOWN;
        logger.trace("Unable to translate handler errors of the authentication exception {}. Returning {} by default...", e, messageCode);
        messageContext.addMessage(new MessageBuilder().error().code(messageCode).build());
        return UNKNOWN;
    }
}

@@ -1033,285 +1033,35 @@
 on;%0A
-import org.slf4j.Logger;%0Aimport org.slf4j.LoggerFactory;%0Aimport org.springframework.binding.message.MessageBuilder;%0Aimport org.springframework.binding.message.MessageContext;%0A%0Aimport javax.validation.constraints.NotNull;%0Aimport java.util.ArrayList;%0Aimport java.util.Collections
+%0Aimport java.util.ArrayList
 ;%0Aim
@@ -1380,16 +1380,17 @@
 ndler er
+r
 ors agai
@@ -1579,22 +1579,21 @@
 or M
-arvin S. Addis
+ichael Haselt
 on%0A 
@@ -1662,255 +1662,48 @@
 ler 
-%7B%0A%0A    /** State name when no matching exception is found. */%0A    private static final String UNKNOWN = %22UNKNOWN%22;%0A%0A    /** Default message bundle prefix. */%0A    private static final String DEFAULT_MESSAGE_BUNDLE_PREFIX = %22authenticationFailure.%22;
+extends AuthenticationExceptionHandler %7B
 %0A%0A  
@@ -1881,85 +1881,8 @@
 );%0A%0A
-    private final Logger logger = LoggerFactory.getLogger(this.getClass());%0A%0A
     
@@ -2790,2910 +2790,7 @@
    %7D
-%0A%0A    /** Ordered list of error classes that this class knows how to handle. */%0A    @NotNull%0A    private List%3CClass%3C? extends Exception%3E%3E errors = DEFAULT_ERROR_LIST;%0A%0A    /** String appended to exception class name to create a message bundle key for that particular error. */%0A    private String messageBundlePrefix = DEFAULT_MESSAGE_BUNDLE_PREFIX;%0A%0A    /**%0A     * Sets the list of errors that this class knows how to handle.%0A     *%0A     * @param errors List of errors in order of descending precedence.%0A     */%0A    public void setErrors(final List%3CClass%3C? extends Exception%3E%3E errors) %7B%0A        this.errors = errors;%0A    %7D%0A%0A    public final List%3CClass%3C? extends Exception%3E%3E getErrors() %7B%0A        return Collections.unmodifiableList(this.errors);%0A    %7D%0A%0A    /**%0A     * Sets the message bundle prefix appended to exception class names to create a message bundle key for that%0A     * particular error.%0A     *%0A     * @param prefix Prefix appended to exception names.%0A     */%0A    public void setMessageBundlePrefix(final String prefix) %7B%0A        this.messageBundlePrefix = prefix;%0A    %7D%0A%0A    /**%0A     * Maps an authentication exception onto a state name equal to the simple class name of the.%0A     *%0A     * @param e Authentication error to handle.%0A     * @param messageContext the spring message context%0A     * @return Name of next flow state to transition to or %7B@value #UNKNOWN%7D%0A     * %7B@link org.jasig.cas.authentication.AuthenticationException#getHandlerErrors()%7D with highest precedence.%0A     * Also sets an ERROR severity message in the message context of the form%0A     * %3Ccode%3E%5BmessageBundlePrefix%5D%5BexceptionClassSimpleName%5D%3C/code%3E for each handler error that is%0A     * configured. If not match is found, %7B@value #UNKNOWN%7D is returned.%0A     */%0A    public String handle(final AuthenticationException e, final MessageContext messageContext) %7B%0A        if (e != null) %7B%0A            final MessageBuilder builder = new MessageBuilder();%0A            for (final Class%3C? extends Exception%3E kind : this.errors) %7B%0A                for (final Class%3C? extends Exception%3E handlerError : e.getHandlerErrors().values()) %7B%0A                    if (handlerError != null && handlerError.equals(kind)) %7B%0A                        final String handlerErrorName = handlerError.getSimpleName();%0A                        final String messageCode = this.messageBundlePrefix + handlerErrorName;%0A                        messageContext.addMessage(builder.error().code(messageCode).build());%0A                        return handlerErrorName;%0A                    %7D%0A                %7D%0A%0A            %7D%0A        %7D%0A        final String messageCode = this.messageBundlePrefix + UNKNOWN;%0A        logger.trace(%22Unable to translate handler errors of the authentication exception %7B%7D. Returning %7B%7D by default...%22, e, messageCode);%0A        messageContext.addMessage(new MessageBuilder().error().code(messageCode).build());%0A        return UNKNOWN;%0A    %7D
 %0A%7D%0A

extend AuthenticationExceptionHandler class rather than reimplementation in maven overlay