package org.apereo.cas.authentication;

import org.apereo.cas.authentication.principal.Principal;
import org.springframework.core.Ordered;

import java.security.GeneralSecurityException;

/**
 * An authentication handler authenticates a single credential. In many cases credentials are authenticated by
 * comparison with data in a system of record such as LDAP directory or database.
 *
 * @author Marvin S. Addison
 * @since 4.0.0
 */
public interface AuthenticationHandler extends Ordered {

    /** Attribute name containing collection of handler names that successfully authenticated credential. */
    String SUCCESSFUL_AUTHENTICATION_HANDLERS = "successfulAuthenticationHandlers";

    /**
     * Authenticates the given credential. There are three possible outcomes of this process, and implementers
     * MUST adhere to the following contract:
     *
     * <ol>
     *     <li>Success -- return {@link HandlerResult}</li>
     *     <li>Failure -- throw {@link GeneralSecurityException}</li>
     *     <li>Indeterminate -- throw {@link PreventedException}</li>
     * </ol>
     *
     * @param credential The credential to authenticate.
     *
     * @return A result object containing metadata about a successful authentication event that includes at
     * a minimum the name of the handler that authenticated the credential and some credential metadata.
     * The following data is optional:
     * <ul>
     *     <li>{@link Principal}</li>
     *     <li>Messages issued by the handler about the credential (e.g. impending password expiration warning)</li>
     * </ul>
     *
     * @throws GeneralSecurityException On authentication failures where the root cause is security related,
     * e.g. invalid credential. Implementing classes SHOULD be as specific as possible in communicating the reason for
     * authentication failure. Recommendations for common cases:
     * <ul>
     *     <li>Bad password: {@code javax.security.auth.login.FailedLoginException}</li>
     *     <li>Expired password: {@code javax.security.auth.login.CredentialExpiredException}</li>
     *     <li>User account expired: {@code javax.security.auth.login.AccountExpiredException}</li>
     *     <li>User account locked: {@code javax.security.auth.login.AccountLockedException}</li>
     *     <li>User account not found: {@code javax.security.auth.login.AccountNotFoundException}</li>
     *     <li>Time of authentication not allowed: {@code org.apereo.cas.authentication.InvalidLoginTimeException}</li>
     *     <li>Location of authentication not allowed: {@code org.apereo.cas.authentication.InvalidLoginLocationException}</li>
     *     <li>Expired X.509 certificate: {@code java.security.cert.CertificateExpiredException}</li>
     * </ul>
     * @throws PreventedException On errors that prevented authentication from occurring. Implementing classes SHOULD
     * take care to populate the cause, where applicable, with the error that prevented authentication.
     */
    HandlerResult authenticate(Credential credential) throws GeneralSecurityException, PreventedException;


    /**
     * Determines whether the handler has the capability to authenticate the given credential. In practical terms,
     * the {@link #authenticate(Credential)} method MUST be capable of processing a given credential if
     * {@code supports} returns true on the same credential.
     *
     * @param credential The credential to check.
     *
     * @return True if the handler supports the Credential, false otherwise.
     */
    boolean supports(Credential credential);


    /**
     * Gets a unique name for this authentication handler within the Spring context that contains it.
     * For implementations that allow setting a unique name, deployers MUST take care to ensure that every
     * handler instance has a unique name.
     *
     * @return Unique name within a Spring context.
     */
    String getName();
}
