summaryrefslogblamecommitdiffstats
path: root/src/main/java/edu/kit/scc/dei/ecplean/ECPAuthenticator.java
blob: 702e67826714c8e5e4936a6ba90ea6cec916da71 (plain) (tree)
1
2
3
4
5
6
7
8


                                 
                     

                            

                                                       





                                                 

                                                
                                                          
                                            
                                                     
                                                        


                                         
                             



                                                             

                                                                                                                  
                               
                                                                                                 
          

                                                                                                   

                                                                                                 
          
 
                                                                               





                                                                     
 
                                                           
 
                                                                               
                                                                                    

                                                                                                                      

                                                                    



                                           
                                                                     
                                                                                       
                                         
                                                           


                                                                   
 


                                                                              
                                                                                        
                                                                    

                                                                 
 









                                                                                                                      

                                                                                                                          







                                                                                                     












                                                                                                 
 
                                                                       
 
                                           
                                                                                                 







                                                                                                            

                                             

                                                                                                                                  
                                        




                                                                                                  
                                                                         







                                                                                                                
                                                                                                                                   

                                                                                             
                                                                      

                                                                                              
                                                                 


                                                                             
 


                                                                       
                                                                     
                                          


                                                                        
                                     
          
 







                                                                                         
                             
                                                               


                                                                                                   

                                        
                  




                                        








                                                                             










                                                                                                              
                          
                  
                               
          
  
package edu.kit.scc.dei.ecplean;

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathException;

import org.apache.http.HttpResponse;
import org.apache.http.ParseException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class ECPAuthenticator extends ECPAuthenticatorBase {

	public ECPAuthenticator(CloseableHttpClient client, String username, String password, URI idpEcpEndpoint,
			URI spUrl) {
		super(client);
		authInfo = new ECPAuthenticationInfo(username, password, idpEcpEndpoint, spUrl);
	}

	public ECPAuthenticator(String username, String password, URI idpEcpEndpoint, URI spUrl) {
		super();
		authInfo = new ECPAuthenticationInfo(username, password, idpEcpEndpoint, spUrl);
	}

	public HttpResponse authenticate() throws ECPAuthenticationException {
		logger.info("Starting authentication");

		logger.info("Contacting SP " + authInfo.getSpUrl());
		authInfo.setAuthState(ECPAuthState.INITIAL_PAOS_SP);
		setChanged();
		notifyObservers(authInfo);

		logger.info("Sending initial SP Request");

		HttpGet httpGet = new HttpGet(authInfo.getSpUrl().toString());
		httpGet.setHeader("Accept", "text/html, application/vnd.paos+xml");
		httpGet.setHeader("PAOS",
				"ver=\"urn:liberty:paos:2003-08\";\"urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp\"");
		HttpClientContext ctx = HttpClientContext.create();
		ctx.setCookieStore(new BasicCookieStore());

		HttpResponse httpResponse;
		String responseBody;
		try {
			httpResponse = client.execute(httpGet, ctx);
			responseBody = EntityUtils.toString(httpResponse.getEntity());
			httpGet.reset();
		} catch (IOException | ParseException e) {
			logger.debug("Initial SP Request failed");
			throw new ECPAuthenticationException(e);
		}

		Document initResponse;
		try {
			initResponse = buildDocumentFromString(responseBody);
		} catch (IOException | SAXException | ParserConfigurationException e) {
			logger.debug("Parsing SP Response failed");
			throw new ECPAuthenticationException(e);
		}

		String relayState;
		try {
			relayState = (String) queryDocument(initResponse, "//ecp:RelayState", XPathConstants.STRING);
		} catch (XPathException e) {
			logger.debug("Could not find relay state in PAOS answer from SP");
			throw new ECPAuthenticationException(e);
		}
		logger.info("Got relayState: " + relayState);
		String responseConsumerUrl;
		try {
			responseConsumerUrl = (String) queryDocument(initResponse,
					"/S:Envelope/S:Header/paos:Request/@responseConsumerURL", XPathConstants.STRING);
		} catch (XPathException e) {
			logger.debug("Could not find response consumer url in PAOS answer from SP");
			throw new ECPAuthenticationException(e);
		}
		logger.info("Got responseConsumerUrl: " + responseConsumerUrl);

		Node firstChild = initResponse.getDocumentElement().getFirstChild();
		initResponse.getDocumentElement().removeChild(firstChild);

		Document idpResponse;
		try {
			idpResponse = authenticateIdP(initResponse);
		} catch (ECPAuthenticationException e) {
			logger.debug("Original SP response:\n" + responseBody);
			try {
				logger.debug("Sent to IdP:\n" + documentToString(initResponse));
			} catch (TransformerException e1) {
				logger.debug("Barf", e1);
			}
			throw e;
		}

		List<String> statusCodes = getStatusCode(idpResponse);

		if (statusCodes.isEmpty())
			throw new ECPAuthenticationException("IdP returned no status code!!!x");
		if (!statusCodes.get(0).endsWith(":Success") && !statusCodes.get(0).endsWith(":success")) {
			String allCodes = "";
			for (String sCode : statusCodes) {
				allCodes = allCodes + sCode + "\n";
			}
			throw new ECPAuthenticationException("IdP Returned StatusCodes:\n" + allCodes);
		}

		String assertionConsumerUrl;
		try {
			assertionConsumerUrl = (String) queryDocument(idpResponse,
					"/S:Envelope/S:Header/ecp:Response/@AssertionConsumerServiceURL", XPathConstants.STRING);
		} catch (Exception e) {
			logger.debug("Could not find assertion consumer url in answer from IdP");
			throw new ECPAuthenticationException(e);
		}
		logger.info("Got assertionConsumerUrl: " + assertionConsumerUrl);

		if (!assertionConsumerUrl.equals(responseConsumerUrl)) {
			throw new ECPAuthenticationException("Assertion- and ResponseConsumerURL don't match");
		}

		idpResponse.getDocumentElement().getFirstChild().getFirstChild().setTextContent(relayState);

		logger.info("Sending Assertion to SP");
		HttpPost httpPost = new HttpPost(assertionConsumerUrl);
		httpPost.setHeader("Content-Type", "application/vnd.paos+xml");
		//httpPost.setHeader("PAOS", "ver=\"urn:liberty:paos:2003-08\";\"urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp\"");
		try {
			httpPost.setEntity(new StringEntity(documentToString(idpResponse)));
			httpResponse = client.execute(httpPost, ctx);
			logger.info("Asserting resulted in " + httpResponse.getStatusLine());
			httpPost.reset();
		} catch (TransformerException | IOException e) {
			logger.debug("Could not post assertion back to SP");
			throw new ECPAuthenticationException(e);
		}

		logger.info("Requesting original URL");
		httpGet = new HttpGet(authInfo.getSpUrl().toString());
		try {
			httpResponse = client.execute(httpGet, ctx);
		} catch (IOException e) {
			logger.debug("Could not request original URL");
			throw new ECPAuthenticationException(e);
		}
		return httpResponse;
	}

	/**
	 * Parse the IDP Response for the status code.
	 * 
	 * @param idpResponse
	 * @return A list of strings of messages returned by the IDP or an empty list on
	 *         error.
	 */
	private List<String> getStatusCode(Document idpResponse) {
		NodeList nl;
		List<String> result = new ArrayList<String>();
		try {
			nl = (NodeList) queryDocument(idpResponse, "//*", XPathConstants.NODESET);
		} catch (XPathException e) {
			result.clear();
			return result;
		}
		if (nl == null) {
			result.clear();
			return result;
		}

		for (int i = 0; i < nl.getLength(); ++i) {
			Node ns = nl.item(i);
			if (!ns.getLocalName().endsWith("StatusCode"))
				continue;
			if (!ns.hasAttributes())
				continue;
			Node val = ns.getAttributes().getNamedItem("Value");
			if (val == null)
				continue;

			if (result.isEmpty() || result.get(0).endsWith(":Responder")) {
				result.clear();
				result.add(val.getNodeValue());
				logger.debug("Current idpResponse status code result: " + result);
				NodeList subnodes = ns.getChildNodes();
				if (subnodes.getLength() != 0) {
					Node subval = subnodes.item(0).getAttributes().getNamedItem("Value");
					if (subval != null)
						result.add(subval.getNodeValue());
				}
			}
		}
		return result;
	}
}