it.scoppelletti.programmerpower.web.security.CasClient.java Source code

Java tutorial

Introduction

Here is the source code for it.scoppelletti.programmerpower.web.security.CasClient.java

Source

/*
 * Copyright (C) 2011 Dario Scoppelletti, <http://www.scoppelletti.it/>.
 * 
 * Licensed 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
 * 
 *     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 it.scoppelletti.programmerpower.web.security;

import java.net.*;
import java.util.regex.*;
import javax.servlet.http.*;
import javax.servlet.http.Cookie;
import org.restlet.*;
import org.restlet.data.*;
import org.jasig.cas.client.session.*;
import org.slf4j.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.security.authentication.*;
import org.springframework.security.cas.*;
import org.springframework.web.util.*;
import org.springframework.web.util.WebUtils;
import it.scoppelletti.programmerpower.*;
import it.scoppelletti.programmerpower.reflect.*;
import it.scoppelletti.programmerpower.security.*;
import it.scoppelletti.programmerpower.types.*;
import it.scoppelletti.programmerpower.web.*;

/**
 * Client delle <ACRONYM TITLE="Application Programming Interface">API</ACRONYM>
 * <ACRONYM TITLE="REpresentional State Transfer">REST</ACRONYM> del server 
 * <ACRONYM TITLE="Central Authentication Service">CAS</ACRONYM>.
 *  
 * @since 1.0.0
 */
@Final
public class CasClient {

    /**
     * Nome della propriet&agrave; di ambiente sulla quale deve essere impostato
     * l&rsquo;indirizzo del server CAS (ad esempio,
     * {@code https://localhost:8443/cas}). Il valore della costante &egrave;
     * <CODE>{@value}</CODE>. 
     * 
     * @see <A HREF="{@docRoot}/../reference/setup/envprops.html"
     *      TARGET="_top">Propriet&agrave; di ambiente</A> 
     */
    public static final String PROP_URL = "it.scoppelletti.programmerpower.web.security.CasClient.url";

    /**
     * Nome della propriet&agrave; di ambiente sulla quale pu&ograve; essere
     * impostato il nome del cookie nel quale &egrave; memorizzato il ticket di
     * autenticazione. Il valore della costante &egrave; <CODE>{@value}</CODE>.
     * 
     * @it.scoppelletti.tag.default {@code it.scoppelletti.TGC} 
     * @see <A HREF="{@docRoot}/../reference/setup/envprops.html"
     *      TARGET="_top">Propriet&agrave; di ambiente</A> 
     */
    public static final String PROP_COOKIE = "it.scoppelletti.programmerpower.web.security.CasClient.cookie";

    private static final Pattern myTGTFormat = Pattern.compile(".*action=\".*/(.*?)\".*");
    private static final Logger myLogger = LoggerFactory.getLogger(CasClient.class);

    private String myServerUrl;
    private ServiceProperties myServiceProps;
    private CookieGenerator myTGTCookieGenerator;
    private SessionMappingStorage mySessionStorage;

    /**
     * Costruttore.
     */
    public CasClient() {
    }

    /**
     * Imposta l&rsquo;URL delle API RESTful del server CAS. 
     * 
     * @param value Valore.
     */
    @Required
    public void setServerUrl(String value) {
        myServerUrl = value;
    }

    /**
     * Imposta le propriet&agrave; del servizio CAS corrispondente
     * all&rsquo;applicazione.
     * 
     * @param obj Oggetto.
     */
    @Required
    public void setServiceProperties(ServiceProperties obj) {
        myServiceProps = obj;
    }

    /**
     * Imposta il generatore del cookie del ticket di autenticazione.
     * 
     * @param obj Oggetto.
     */
    @Required
    public void setTicketGrantingTicketCookieGenerator(CookieGenerator obj) {
        myTGTCookieGenerator = obj;
    }

    /**
     * Imposta la collezione delle sessioni autenticate.
     * 
     * @param obj Oggetto.
     */
    @Required
    public void setSessionMappingStorage(SessionMappingStorage obj) {
        mySessionStorage = obj;
    }

    /**
     * Richiede un ticket di autenticazione.
     * 
     * @param  userName Nome dell&rsquo;utente.
     * @param  pwd      Password.
     * @return          Ticket di autenticazione.
     */
    public String newTicketGrantingTicket(String userName, SecureString pwd) throws ProtocolException {
        String text;
        Matcher matcher;
        Client client;
        Request req;
        Response resp;
        Form form;
        Status status;
        WebResources res = new WebResources();
        SecurityResources secRes = new SecurityResources();

        if (Strings.isNullOrEmpty(userName)) {
            throw new ArgumentNullException("userName");
        }
        if (Values.isNullOrEmpty(pwd)) {
            throw new ArgumentNullException("pwd");
        }
        if (Strings.isNullOrEmpty(myServerUrl)) {
            throw new PropertyNotSetException(toString(), "serverUrl");
        }
        if (myServiceProps == null) {
            throw new PropertyNotSetException(toString(), "serviceProperties");
        }

        form = new Form();
        form.add("username", userName);
        form.add("password", pwd.toString());

        req = new Request(Method.POST, myServerUrl);
        req.setEntity(form.getWebRepresentation(CharacterSet.UTF_8));

        client = new Client(Protocol.HTTPS);
        resp = client.handle(req);

        status = resp.getStatus();
        if (status.equals(Status.CLIENT_ERROR_BAD_REQUEST)) {
            throw new BadCredentialsException(secRes.getFailedLoginException());
        }
        if (status.equals(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE)) {
            throw new ProtocolException(res.getUnsupportedMediaTypeException());
        }
        if (!status.equals(Status.SUCCESS_CREATED)) {
            throw new ProtocolException(
                    res.getUnexpectedStatusCodeException(status.getCode(), status.getDescription()));
        }

        text = resp.getEntityAsText();
        if (Strings.isNullOrEmpty(text)) {
            throw new ProtocolException(res.getEmptyResponseException());
        }

        matcher = myTGTFormat.matcher(text);
        if (!matcher.matches()) {
            throw new ProtocolException(res.getInvalidResponseException(text));
        }

        return matcher.group(1);
    }

    /**
     * Richiede un ticket di servizio.
     * 
     * @param  ticketGrantingTicket Ticket di autenticazione.
     * @return                      Ticket di servizio.
     */
    public String newServiceTicket(String ticketGrantingTicket) throws ProtocolException {
        String text, url;
        Client client;
        Request req;
        Response resp;
        Form form;
        Status status;
        WebResources res = new WebResources();
        SecurityResources secRes = new SecurityResources();

        if (Strings.isNullOrEmpty(ticketGrantingTicket)) {
            throw new ArgumentNullException("ticketGrantingTicket");
        }
        if (Strings.isNullOrEmpty(myServerUrl)) {
            throw new PropertyNotSetException(toString(), "serverUrl");
        }
        if (myServiceProps == null) {
            throw new PropertyNotSetException(toString(), "serviceProperties");
        }

        if (myServerUrl.endsWith("/")) {
            url = myServerUrl;
        } else {
            url = myServerUrl.concat("/");
        }
        url = url.concat(ticketGrantingTicket);

        form = new Form();
        form.add(myServiceProps.getServiceParameter(), myServiceProps.getService());

        req = new Request(Method.POST, url);
        req.setEntity(form.getWebRepresentation(CharacterSet.UTF_8));

        client = new Client(Protocol.HTTPS);
        resp = client.handle(req);

        status = resp.getStatus();
        if (status.equals(Status.CLIENT_ERROR_BAD_REQUEST)) {
            throw new BadCredentialsException(secRes.getFailedLoginException());
        }
        if (status.equals(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE)) {
            throw new ProtocolException(res.getUnsupportedMediaTypeException());
        }
        if (!status.equals(Status.SUCCESS_OK)) {
            throw new ProtocolException(
                    res.getUnexpectedStatusCodeException(status.getCode(), status.getDescription()));
        }

        text = resp.getEntityAsText();
        if (Strings.isNullOrEmpty(text)) {
            throw new ProtocolException(res.getEmptyResponseException());
        }

        return text;
    }

    /**
     * Rimuove tutte le autenticazioni eseguite con lo stesso ticket di
     * autenticazione registrato come cookie.
     * 
     * @param req    Richiesta.
     * @param resp   Risposta.
     * @param ticket Ticket di autenticazione.
     */
    public void destroyTicketGrantingTicket(HttpServletRequest req, HttpServletResponse resp, String ticket) {
        String url;
        Client client;
        Request restReq;
        Response restResp;
        Status status;

        if (req == null) {
            throw new ArgumentNullException("req");
        }
        if (resp == null) {
            throw new ArgumentNullException("resp");
        }
        if (Strings.isNullOrEmpty(ticket)) {
            throw new ArgumentNullException("ticket");
        }
        if (Strings.isNullOrEmpty(myServerUrl)) {
            throw new PropertyNotSetException(toString(), "serverUrl");
        }

        if (myServerUrl.endsWith("/")) {
            url = myServerUrl;
        } else {
            url = myServerUrl.concat("/");
        }
        url = url.concat(ticket);

        try {
            restReq = new Request(Method.DELETE, url);
            client = new Client(Protocol.HTTPS);
            restResp = client.handle(restReq);

            status = restResp.getStatus();
            if (!status.isSuccess()) {
                // CAS non documenta i possibili codici di risposta
                myLogger.error("destroyTicketGrantingTicket status: {} {}", status.getCode(),
                        status.getDescription());
            }
        } catch (Exception ex) {
            throw new AuthenticationServiceException(ApplicationException.toString(ex), ex);
        }
    }

    /**
     * Restituisce il ticket di autenticazione registrato come cookie.
     * 
     * @param  req  Richiesta.
     * @param  resp Risposta.
     * @return      Valore. Se il ticket non &egrave; stato registrato,
     *              restituisce {@code null}.
     */
    public String getTicketGrantingTicket(HttpServletRequest req, HttpServletResponse resp) {
        Cookie cookie;

        if (req == null) {
            throw new ArgumentNullException("req");
        }
        if (myTGTCookieGenerator == null) {
            throw new PropertyNotSetException(toString(), "ticketGrantingTicketCookieGenerator");
        }

        cookie = WebUtils.getCookie(req, myTGTCookieGenerator.getCookieName());
        if (cookie == null) {
            return null;
        }

        return cookie.getValue();
    }

    /**
     * Registra un ticket di autenticazione come cookie.
     * 
     * @param req    Richiesta.
     * @param resp   Risposta.
     * @param ticket Ticket di autenticazione.
     */
    public void addTicketGrantingTicket(HttpServletRequest req, HttpServletResponse resp, String ticket) {
        if (resp == null) {
            throw new ArgumentNullException("resp");
        }
        if (Strings.isNullOrEmpty(ticket)) {
            throw new ArgumentNullException("ticket");
        }
        if (myTGTCookieGenerator == null) {
            throw new PropertyNotSetException(toString(), "ticketGrantingTicketCookieGenerator");
        }

        try {
            myTGTCookieGenerator.addCookie(resp, ticket);
        } catch (Exception ex) {
            myLogger.error("Failed to store TGT cookie.", ex);
        }
    }

    /**
     * Rimuove il ticket di autenticazione registrato come cookie.
     * 
     * @param req    Richiesta.
     * @param resp   Risposta.
     */
    public void removeTicketGrantingTicket(HttpServletRequest req, HttpServletResponse resp) {
        if (resp == null) {
            throw new ArgumentNullException("resp");
        }
        if (myTGTCookieGenerator == null) {
            throw new PropertyNotSetException(toString(), "ticketGrantingTicketCookieGenerator");
        }

        try {
            myTGTCookieGenerator.removeCookie(resp);
        } catch (Exception ex) {
            myLogger.error("Failed to remove TGT cookie.", ex);
        }
    }

    /**
     * Colleziona una sessione autenticata.
     * 
     * @param serviceTicket Ticket di servizio.
     * @param session       Sessione.
     */
    public void addAuthenticatedSession(String serviceTicket, HttpSession session) {
        if (Strings.isNullOrEmpty(serviceTicket)) {
            throw new ArgumentNullException("serviceTicket");
        }
        if (session == null) {
            throw new ArgumentNullException("session");
        }
        if (mySessionStorage == null) {
            throw new PropertyNotSetException(toString(), "sessionMappingStorage");
        }

        myLogger.debug("Storing authenticated session {}.", session.getId());
        try {
            mySessionStorage.removeBySessionById(session.getId());
        } catch (Exception ex) {
            // NOP
        }
        mySessionStorage.addSessionById(serviceTicket, session);
    }

    /**
     * Rimuove una sessione autenticata.
     * 
     * @param serviceTicket Ticket di servizio.
     */
    public void removeAuthenticatedSession(String serviceTicket) {
        HttpSession session;

        if (Strings.isNullOrEmpty(serviceTicket)) {
            throw new ArgumentNullException("serviceTicket");
        }
        if (mySessionStorage == null) {
            throw new PropertyNotSetException(toString(), "sessionMappingStorage");
        }

        myLogger.debug("Removing service ticket {}.", serviceTicket);
        session = mySessionStorage.removeSessionByMappingId(serviceTicket);
        if (session == null) {
            return;
        }

        myLogger.debug("Invalidating session {}.", session.getId());
        try {
            session.invalidate();
        } catch (Exception ex) {
            myLogger.error("Failed to invalidate session.", ex);
        }
    }
}