package org.apereo.cas;

import org.apereo.cas.authentication.AuthenticationException;
import org.apereo.cas.authentication.AuthenticationResult;
import org.apereo.cas.authentication.principal.Service;
import org.apereo.cas.logout.LogoutRequest;
import org.apereo.cas.ticket.AbstractTicketException;
import org.apereo.cas.ticket.InvalidTicketException;
import org.apereo.cas.ticket.ServiceTicket;
import org.apereo.cas.ticket.Ticket;
import org.apereo.cas.ticket.TicketGrantingTicket;
import org.apereo.cas.ticket.proxy.ProxyGrantingTicket;
import org.apereo.cas.ticket.proxy.ProxyTicket;
import org.apereo.cas.validation.Assertion;

import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;

/**
 * CAS viewed as a set of services to generate and validate Tickets.
 * <p>
 * This is the interface between a Web HTML, Web Services, RMI, or any other
 * request processing layer and the CAS Service viewed as a mechanism to
 * generate, store, validate, and retrieve Tickets containing Authentication
 * information. The features of the request processing layer (the HttpXXX
 * Servlet objects) are not visible here or in any modules behind this layer. In
 * theory, a standalone application could call these methods directly as a
 * private authentication service.
 * </p>
 *
 * @author William G. Thompson, Jr.
 * @author Dmitry Kopylenko
 * @author Scott Battaglia
 * @author Marvin S. Addison
 * @author Misagh Moayyed
 * @since 3.0.0
 */
public interface CentralAuthenticationService {

    /**
     * Create a {@link TicketGrantingTicket} by authenticating credentials.
     * The details of the security policy around credential authentication and the definition
     * of authentication success are dependent on the implementation, but it SHOULD be safe to assume
     * that at least one credential MUST be authenticated for ticket creation to succeed.
     *
     * @param authenticationResult the current authentication result in order to create the ticket.
     * @return Non -null ticket-granting ticket identifier.
     * @throws AuthenticationException on errors authenticating the credentials
     * @throws AbstractTicketException if ticket cannot be created
     */
    TicketGrantingTicket createTicketGrantingTicket(AuthenticationResult authenticationResult)
        throws AuthenticationException, AbstractTicketException;


    /**
     * Updates the ticket instance in the underlying storage mechanism.
     * The properties of a given ticket, such as its authentication attributes
     * may have changed during various legs of the authentication flow.
     *
     * @param ticket the ticket
     * @return the updated ticket
     * @since 5.0.0
     */
    Ticket updateTicket(Ticket ticket);

    /**
     * Obtains the given ticket by its id
     * and returns the CAS-representative object. Implementations
     * need to check for the validity of the ticket by making sure
     * it exists and has not expired yet, etc. This method is specifically
     * designed to remove the need to access the ticket registry.
     *
     * @param <T>      the generic ticket type to return that extends {@link Ticket}
     * @param ticketId the ticket granting ticket id
     * @return the ticket object
     * @throws InvalidTicketException the invalid ticket exception
     * @since 5.0.0
     */
    <T extends Ticket> T getTicket(String ticketId) throws InvalidTicketException;

    /**
     * Obtains the given ticket by its id and type
     * and returns the CAS-representative object. Implementations
     * need to check for the validity of the ticket by making sure
     * it exists and has not expired yet, etc. This method is specifically
     * designed to remove the need to access the ticket registry.
     *
     * @param <T>      the generic ticket type to return that extends {@link Ticket}
     * @param ticketId the ticket granting ticket id
     * @param clazz    the ticket type that is reques to be found
     * @return the ticket object
     * @throws InvalidTicketException the invalid ticket exception
     * @since 4.1.0
     */
    <T extends Ticket> T getTicket(String ticketId, Class<T> clazz)
            throws InvalidTicketException;

    /**
     * Attempts to delete a ticket from the underlying store
     * and is allowed to run any number of processing on the ticket
     * and removal op before invoking it. The ticket id can be associated
     * with any ticket type that is valid and understood by CAS and the underlying
     * ticket store; however some special cases require that you invoke the appropriate
     * operation when destroying tickets, such {@link #destroyTicketGrantingTicket(String)}.
     *
     * @param ticketId the ticket id
     */
    default void deleteTicket(String ticketId) {}

    /**
     * Retrieve a collection of tickets from the underlying ticket registry.
     * The retrieval operation must pass the predicate check that is solely
     * used to filter the collection of tickets received. Implementations
     * can use the predicate to request a collection of expired tickets,
     * or tickets whose id matches a certain pattern, etc. The resulting
     * collection will include ticktes that have been evaluated by the predicate.
     *
     * @param predicate the predicate
     * @return the tickets
     * @since 4.1.0
     */
    Collection<Ticket> getTickets(Predicate<Ticket> predicate);

    /**
     * Grant a {@link ServiceTicket} that may be used to access the given service
     * by authenticating the given credentials.
     * The details of the security policy around credential authentication and the definition
     * of authentication success are dependent on the implementation, but it SHOULD be safe to assume
     * that at least one credential MUST be authenticated for ticket creation to succeed.
     * <p>
     * The principal that is resolved from the authenticated credentials MUST be the same as that to which
     * the given ticket-granting ticket was issued.
     * </p>
     *
     * @param ticketGrantingTicketId Proof of prior authentication.
     * @param service                The target service of the ServiceTicket.
     * @param authenticationResult   The authentication context established if credentials provided
     * @return Non -null service ticket identifier.
     * @throws AuthenticationException on errors authenticating the credentials
     * @throws AbstractTicketException if the ticket could not be created.
     */
    ServiceTicket grantServiceTicket(
            String ticketGrantingTicketId, Service service, AuthenticationResult authenticationResult)
            throws AuthenticationException, AbstractTicketException;

    /**
     * Grant a {@link ProxyTicket} that may be used to access the given service
     * by authenticating the given credentials.
     * The details of the security policy around credential authentication and the definition
     * of authentication success are dependent on the implementation, but it SHOULD be safe to assume
     * that at least one credential MUST be authenticated for ticket creation to succeed.
     * <p>
     * The principal that is resolved from the authenticated credentials MUST be the same as that to which
     * the given ticket-granting ticket was issued.
     * </p>
     *
     * @param proxyGrantingTicket Proof of prior authentication.
     * @param service             The target service of the ServiceTicket.
     * @return Non -null service ticket identifier.
     * @throws AbstractTicketException if the ticket could not be created.
     */
    ProxyTicket grantProxyTicket(
             String proxyGrantingTicket, Service service)
            throws AbstractTicketException;

    /**
     * Validate a ServiceTicket for a particular Service.
     *
     * @param serviceTicketId Proof of prior authentication.
     * @param service         Service wishing to validate a prior authentication.
     * @return Non -null ticket validation assertion.
     * @throws AbstractTicketException if there was an error validating the ticket.
     */
    Assertion validateServiceTicket(String serviceTicketId, Service service) throws AbstractTicketException;

    /**
     * Destroy a TicketGrantingTicket and perform back channel logout. This has the effect of invalidating any
     * Ticket that was derived from the TicketGrantingTicket being destroyed. May throw an
     * {@link IllegalArgumentException} if the TicketGrantingTicket ID is null.
     *
     * @param ticketGrantingTicketId the id of the ticket we want to destroy
     * @return the logout requests.
     */
    List<LogoutRequest> destroyTicketGrantingTicket(String ticketGrantingTicketId);

    /**
     * Delegate a TicketGrantingTicket to a Service for proxying authentication
     * to other Services.
     *
     * @param serviceTicketId      The service ticket identifier that will delegate to a {@link TicketGrantingTicket}.
     * @param authenticationResult The current authentication context before this ticket can be granted.
     * @return Non -null ticket-granting ticket identifier that can grant {@link ServiceTicket} that proxy authentication.
     * @throws AuthenticationException on errors authenticating the credentials
     * @throws AbstractTicketException if there was an error creating the ticket
     */
    ProxyGrantingTicket createProxyGrantingTicket(String serviceTicketId, AuthenticationResult authenticationResult)
            throws AuthenticationException, AbstractTicketException;
}
