package edu.kit.scc.dei.ecplean; import java.io.IOException; import java.net.URI; 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.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; 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(DefaultHttpClient client, String username, String password, URI idpEcpEndpoint, URI spUrl) { super(client); authInfo = new ECPAuthenticationInfo(username, password, idpEcpEndpoint, spUrl); authInfo.setAuthState(ECPAuthState.NOT_STARTED); } public ECPAuthenticator(String username, String password, URI idpEcpEndpoint, URI spUrl) { this(new DefaultHttpClient(), username, password, idpEcpEndpoint, spUrl); } public void 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'"); HttpResponse httpResponse; String responseBody; try { httpResponse = client.execute(httpGet); responseBody = EntityUtils.toString(httpResponse.getEntity()); } 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 Request 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 = authenticateIdP(initResponse); String statusCode = getStatusCode(idpResponse); if (statusCode == null) throw new ECPAuthenticationException("IdP returned no status code!!!x"); if (!statusCode.endsWith(":Success") && !statusCode.endsWith(":success")) throw new ECPAuthenticationException("IdP Returned StatusCode " + statusCode); 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); responseBody = EntityUtils.toString(httpResponse.getEntity()); } 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); responseBody = EntityUtils.toString(httpResponse.getEntity()); logger.info(responseBody); } catch (IOException | ParseException e) { logger.debug("Could not request original URL"); throw new ECPAuthenticationException(e); } } private String getStatusCode(Document idpResponse) { NodeList nl; try { nl = (NodeList) queryDocument(idpResponse, "//*", XPathConstants.NODESET); } catch (XPathException e) { return null; } if (nl == null) return null; 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; return val.getNodeValue(); } return null; } }