All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.beangle.security.cas.validation.AbstractTicketValidator Maven / Gradle / Ivy

There is a newer version: 4.0.7
Show newest version
/*
 * Beangle, Agile Java/Scala Development Scaffold and Toolkit
 *
 * Copyright (c) 2005-2012, Beangle Software.
 *
 * Beangle is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Beangle is distributed in the hope that it will be useful.
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Beangle.  If not, see .
 */
package org.beangle.security.cas.validation;

import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.net.ssl.HostnameVerifier;

import org.beangle.commons.web.util.HttpUtils;
import org.beangle.security.cas.CasConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

/**
 * @author chaostone
 * @version $Id: Cas20ServiceTicketValidator.java Dec 27, 2011 6:06:21 PM chaostone $
 */
public abstract class AbstractTicketValidator implements TicketValidator {

  protected final Logger logger = LoggerFactory.getLogger(getClass());

  /** Hostname verifier used when making an SSL request to the CAS server. */
  protected HostnameVerifier hostnameVerifier;

  private CasConfig config;

  /** A map containing custom parameters to pass to the validation url. */
  private Map customParameters;

  private String encoding;

  /**
   * Template method for ticket validators that need to provide additional parameters to the
   * validation url.
   * 
   * @param urlParameters
   *          the map containing the parameters.
   */
  protected void populateUrlAttributeMap(final Map urlParameters) {
    // nothing to do
  }

  /**
   * Constructs the URL to send the validation request to.
   * 
   * @param ticket
   *          the ticket to be validated.
   * @param serviceUrl
   *          the service identifier.
   * @return the fully constructed URL.
   */
  protected final String constructValidationUrl(final String ticket, final String serviceUrl) {
    final Map urlParameters = new HashMap();
    urlParameters.put("ticket", ticket);
    urlParameters.put("service", encodeUrl(serviceUrl));

    if (config.isRenew()) urlParameters.put("renew", "true");

    populateUrlAttributeMap(urlParameters);

    if (customParameters != null) urlParameters.putAll(customParameters);

    final String suffix = config.getValidateUri();
    final StringBuilder buffer = new StringBuilder(urlParameters.size() * 10 + config.getCasServer().length()
        + suffix.length() + 1);
    buffer.append(config.getCasServer()).append(suffix);

    int i = 0;
    for (Map.Entry entry : urlParameters.entrySet()) {
      final String key = entry.getKey();
      final String value = entry.getValue();
      if (value != null) {
        buffer.append(i++ == 0 ? "?" : "&");
        buffer.append(key);
        buffer.append("=");
        buffer.append(value);
      }
    }
    return buffer.toString();
  }

  /**
   * Encodes a URL using the URLEncoder format.
   * 
   * @param url the url to encode.
   * @return the encoded url, or the original url if "UTF-8" character encoding could not be
   *         found.
   */
  protected final String encodeUrl(final String url) {
    if (url == null) { return null; }

    try {
      return URLEncoder.encode(url, "UTF-8");
    } catch (final UnsupportedEncodingException e) {
      return url;
    }
  }

  /**
   * Parses the response from the server into a CAS Assertion.
   * 
   * @param response
   *          the response from the server, in any format.
   * @return the CAS assertion if one could be parsed from the response.
   * @throws TicketValidationException
   *           if an Assertion could not be created.
   */
  protected abstract Assertion parseResponseFromServer(final String ticket, final String response)
      throws TicketValidationException;

  /**
   * Contacts the CAS Server to retrieve the response for the ticket validation.
   * 
   * @param validationUrl
   *          the url to send the validation request to.
   * @param ticket
   *          the ticket to Assert.
   * @return the response from the CAS server.
   */

  protected String retrieveResponseFromServer(URL validationUrl, String ticket) {
    return HttpUtils.getResponseText(validationUrl, hostnameVerifier, getEncoding());
  }

  public Assertion validate(final String ticket, final String service) throws TicketValidationException {
    final String validationUrl = constructValidationUrl(ticket, service);
    logger.debug("Constructing validation url: " + validationUrl);
    try {
      logger.debug("Retrieving response from server.");
      final String serverResponse = retrieveResponseFromServer(new URL(validationUrl), ticket);

      if (serverResponse == null) { throw new TicketValidationException(
          "The CAS server returned no response."); }
      logger.debug("Server response: " + serverResponse);
      return parseResponseFromServer(ticket, serverResponse);
    } catch (final MalformedURLException e) {
      throw new TicketValidationException(e);
    }
  }

  /**
   * Get an instance of an XML reader from the XMLReaderFactory.
   * 
   * @return the XMLReader.
   */
  public static XMLReader getXmlReader() {
    try {
      return XMLReaderFactory.createXMLReader();
    } catch (final SAXException e) {
      throw new RuntimeException("Unable to create XMLReader", e);
    }
  }

  /**
   * Retrieve the text for a group of elements. Each text element is an entry
   * in a list.
   * 

* This method is currently optimized for the use case of two elements in a list. * * @param xmlAsString * the xml response * @param element * the element to look for * @return the list of text from the elements. */ public List getTextForElements(final String xmlAsString, final String element) { final List elements = new ArrayList(2); final XMLReader reader = getXmlReader(); final DefaultHandler handler = new DefaultHandler() { private boolean foundElement = false; private StringBuilder buffer = new StringBuilder(); public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { if (localName.equals(element)) { this.foundElement = true; } } public void endElement(final String uri, final String localName, final String qName) throws SAXException { if (localName.equals(element)) { this.foundElement = false; elements.add(this.buffer.toString()); this.buffer = new StringBuilder(); } } public void characters(char[] ch, int start, int length) throws SAXException { if (this.foundElement) { this.buffer.append(ch, start, length); } } }; reader.setContentHandler(handler); reader.setErrorHandler(handler); try { reader.parse(new InputSource(new StringReader(xmlAsString))); } catch (final Exception e) { logger.error("parse", e); return null; } return elements; } /** * Retrieve the text for a specific element (when we know there is only * one). * * @param xmlAsString * the xml response * @param element * the element to look for * @return the text value of the element. */ public String getTextForElement(final String xmlAsString, final String element) { final XMLReader reader = getXmlReader(); final StringBuilder builder = new StringBuilder(); final DefaultHandler handler = new DefaultHandler() { private boolean foundElement = false; public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { if (localName.equals(element)) { this.foundElement = true; } } public void endElement(final String uri, final String localName, final String qName) throws SAXException { if (localName.equals(element)) { this.foundElement = false; } } public void characters(char[] ch, int start, int length) throws SAXException { if (this.foundElement) { builder.append(ch, start, length); } } }; reader.setContentHandler(handler); reader.setErrorHandler(handler); try { reader.parse(new InputSource(new StringReader(xmlAsString))); } catch (final Exception e) { logger.error("parse error", e); return null; } return builder.toString(); } public final void setCustomParameters(final Map customParameters) { this.customParameters = customParameters; } public final void setHostnameVerifier(final HostnameVerifier verifier) { this.hostnameVerifier = verifier; } public final void setEncoding(final String encoding) { this.encoding = encoding; } protected final String getEncoding() { return this.encoding; } public void setConfig(CasConfig config) { this.config = config; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy