summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--pom.xml31
-rw-r--r--src/main/java/edu/kit/scc/dei/ecplean/ECPAuthState.java13
-rw-r--r--src/main/java/edu/kit/scc/dei/ecplean/ECPAuthenticationException.java22
-rw-r--r--src/main/java/edu/kit/scc/dei/ecplean/ECPAuthenticationInfo.java47
-rw-r--r--src/main/java/edu/kit/scc/dei/ecplean/ECPAuthenticator.java167
-rw-r--r--src/main/java/edu/kit/scc/dei/ecplean/ECPAuthenticatorBase.java150
-rw-r--r--src/main/java/edu/kit/scc/dei/ecplean/ECPIdPAuth.java82
-rw-r--r--src/main/java/edu/kit/scc/dei/ecplean/NamespaceResolver.java39
9 files changed, 557 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..00e038e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+/.settings
+/.project
+/.classpath
+/target
+*~
+*.swp
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..2c3d9b8
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,31 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>edu.kit.scc.dei</groupId>
+ <artifactId>ecp-client-lean</artifactId>
+ <version>0.0.2-SNAPSHOT</version>
+ <name>Lean ECP Client</name>
+ <description>ECP Client w/o OpenSAML Libs</description>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.3.2</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>4.1</version>
+ </dependency>
+ </dependencies>
+</project> \ No newline at end of file
diff --git a/src/main/java/edu/kit/scc/dei/ecplean/ECPAuthState.java b/src/main/java/edu/kit/scc/dei/ecplean/ECPAuthState.java
new file mode 100644
index 0000000..486af84
--- /dev/null
+++ b/src/main/java/edu/kit/scc/dei/ecplean/ECPAuthState.java
@@ -0,0 +1,13 @@
+package edu.kit.scc.dei.ecplean;
+
+public enum ECPAuthState {
+
+ NOT_STARTED,
+ INITIAL_PAOS_SP,
+ AUTH_IDP,
+ RESPONSE_TO_SP,
+ AUTH_COMPLETED,
+ AUTH_DENIED,
+ AUTH_ERROR
+
+}
diff --git a/src/main/java/edu/kit/scc/dei/ecplean/ECPAuthenticationException.java b/src/main/java/edu/kit/scc/dei/ecplean/ECPAuthenticationException.java
new file mode 100644
index 0000000..2fbfe96
--- /dev/null
+++ b/src/main/java/edu/kit/scc/dei/ecplean/ECPAuthenticationException.java
@@ -0,0 +1,22 @@
+package edu.kit.scc.dei.ecplean;
+
+public class ECPAuthenticationException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public ECPAuthenticationException() {
+ }
+
+ public ECPAuthenticationException(String message) {
+ super(message);
+ }
+
+ public ECPAuthenticationException(Throwable cause) {
+ super(cause);
+ }
+
+ public ECPAuthenticationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/src/main/java/edu/kit/scc/dei/ecplean/ECPAuthenticationInfo.java b/src/main/java/edu/kit/scc/dei/ecplean/ECPAuthenticationInfo.java
new file mode 100644
index 0000000..0fc8b90
--- /dev/null
+++ b/src/main/java/edu/kit/scc/dei/ecplean/ECPAuthenticationInfo.java
@@ -0,0 +1,47 @@
+package edu.kit.scc.dei.ecplean;
+
+import java.net.URI;
+
+public class ECPAuthenticationInfo {
+
+ private String username;
+ private String password;
+ private URI idpEcpEndpoint;
+ private URI spUrl;
+ private ECPAuthState authState;
+
+ public ECPAuthenticationInfo(String username, String password,
+ URI idpEcpEndpoint, URI spUrl) {
+ super();
+ this.username = username;
+ this.password = password;
+ this.idpEcpEndpoint = idpEcpEndpoint;
+ this.spUrl = spUrl;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public URI getIdpEcpEndpoint() {
+ return idpEcpEndpoint;
+ }
+
+ public URI getSpUrl() {
+ return spUrl;
+ }
+
+ public ECPAuthState getAuthState() {
+ return authState;
+ }
+
+ public void setAuthState(ECPAuthState authState) {
+ this.authState = authState;
+ }
+
+
+}
diff --git a/src/main/java/edu/kit/scc/dei/ecplean/ECPAuthenticator.java b/src/main/java/edu/kit/scc/dei/ecplean/ECPAuthenticator.java
new file mode 100644
index 0000000..f6d1bb7
--- /dev/null
+++ b/src/main/java/edu/kit/scc/dei/ecplean/ECPAuthenticator.java
@@ -0,0 +1,167 @@
+package edu.kit.scc.dei.ecplean;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerConfigurationException;
+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.ClientProtocolException;
+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.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 (ClientProtocolException e) {
+ logger.debug("Initial SP Request failed");
+ throw new ECPAuthenticationException(e);
+ } catch (ParseException e) {
+ logger.debug("Initial SP Request failed");
+ throw new ECPAuthenticationException(e);
+ } catch (IOException e) {
+ logger.debug("Initial SP Request failed");
+ throw new ECPAuthenticationException(e);
+ }
+
+ Document initResponse;
+ try {
+ initResponse = buildDocumentFromString(responseBody);
+ } catch (IOException e) {
+ logger.debug("Parsing SP Request failed");
+ throw new ECPAuthenticationException(e);
+ } catch (ParserConfigurationException e) {
+ logger.debug("Parsing SP Request failed");
+ throw new ECPAuthenticationException(e);
+ } catch (SAXException 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 assertionConsumerUrl;
+ try {
+ assertionConsumerUrl = (String) queryDocument(idpResponse, "/S:Envelope/S:Header/ecp:Response/@AssertionConsumerServiceURL", XPathConstants.STRING);
+ } catch (XPathException 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");
+ try {
+ httpPost.setEntity(new StringEntity(documentToString(idpResponse)));
+ httpResponse = client.execute(httpPost);
+ responseBody = EntityUtils.toString(httpResponse.getEntity());
+ } catch (UnsupportedEncodingException e) {
+ logger.debug("Could not post assertion back to SP");
+ throw new ECPAuthenticationException(e);
+ } catch (TransformerConfigurationException e) {
+ logger.debug("Could not post assertion back to SP");
+ throw new ECPAuthenticationException(e);
+ } catch (ClientProtocolException e) {
+ logger.debug("Could not post assertion back to SP");
+ throw new ECPAuthenticationException(e);
+ } catch (ParseException e) {
+ logger.debug("Could not post assertion back to SP");
+ throw new ECPAuthenticationException(e);
+ } catch (TransformerException e) {
+ logger.debug("Could not post assertion back to SP");
+ throw new ECPAuthenticationException(e);
+ } catch (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 (ClientProtocolException e) {
+ logger.debug("Could not request original URL");
+ throw new ECPAuthenticationException(e);
+ } catch (ParseException e) {
+ logger.debug("Could not request original URL");
+ throw new ECPAuthenticationException(e);
+ } catch (IOException e) {
+ logger.debug("Could not request original URL");
+ throw new ECPAuthenticationException(e);
+ }
+
+ }
+}
diff --git a/src/main/java/edu/kit/scc/dei/ecplean/ECPAuthenticatorBase.java b/src/main/java/edu/kit/scc/dei/ecplean/ECPAuthenticatorBase.java
new file mode 100644
index 0000000..7e080f6
--- /dev/null
+++ b/src/main/java/edu/kit/scc/dei/ecplean/ECPAuthenticatorBase.java
@@ -0,0 +1,150 @@
+package edu.kit.scc.dei.ecplean;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Observable;
+
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathException;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathFactory;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.ParseException;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.ClientProtocolException;
+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.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+public abstract class ECPAuthenticatorBase extends Observable {
+
+ protected static Log logger = LogFactory.getLog(ECPAuthenticatorBase.class);
+ protected ECPAuthenticationInfo authInfo;
+ protected DefaultHttpClient client;
+ protected DocumentBuilderFactory documentBuilderFactory;
+ protected XPathFactory xpathFactory;
+ protected NamespaceResolver namespaceResolver;
+ protected TransformerFactory transformerFactory;
+
+ public ECPAuthenticatorBase(DefaultHttpClient client) {
+ this.client = client;
+
+ documentBuilderFactory = DocumentBuilderFactory.newInstance();
+ documentBuilderFactory.setNamespaceAware(true);
+
+ xpathFactory = XPathFactory.newInstance();
+ namespaceResolver = new NamespaceResolver();
+ namespaceResolver.addNamespace("ecp", "urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp");
+ namespaceResolver.addNamespace("S", "http://schemas.xmlsoap.org/soap/envelope/");
+ namespaceResolver.addNamespace("paos", "urn:liberty:paos:2003-08");
+
+ transformerFactory = TransformerFactory.newInstance();
+ }
+
+ public ECPAuthenticatorBase() {
+ this(new DefaultHttpClient());
+ }
+
+ protected Document authenticateIdP(Document idpRequest)
+ throws ECPAuthenticationException {
+ logger.info("Sending initial IdP Request");
+ client.getCredentialsProvider().setCredentials(
+ new AuthScope(authInfo.getIdpEcpEndpoint().getHost(), authInfo.getIdpEcpEndpoint().getPort()),
+ new UsernamePasswordCredentials(authInfo.getUsername(), authInfo.getPassword()));
+ HttpPost httpPost = new HttpPost(authInfo.getIdpEcpEndpoint().toString());
+ HttpResponse httpResponse;
+
+ try {
+ httpPost.setEntity(new StringEntity(documentToString(idpRequest)));
+ httpResponse = client.execute(httpPost);
+
+ if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
+ throw new ECPAuthenticationException("User not authorized");
+ }
+ } catch (UnsupportedEncodingException e) {
+ logger.debug("Could not submit PAOS request to IdP");
+ throw new ECPAuthenticationException(e);
+ } catch (TransformerConfigurationException e) {
+ logger.debug("Could not submit PAOS request to IdP");
+ throw new ECPAuthenticationException(e);
+ } catch (ClientProtocolException e) {
+ logger.debug("Could not submit PAOS request to IdP");
+ throw new ECPAuthenticationException(e);
+ } catch (TransformerException e) {
+ logger.debug("Could not submit PAOS request to IdP");
+ throw new ECPAuthenticationException(e);
+ } catch (IOException e) {
+ logger.debug("Could not submit PAOS request to IdP");
+ throw new ECPAuthenticationException(e);
+ }
+
+ String responseBody;
+ try {
+ responseBody = EntityUtils.toString(httpResponse.getEntity());
+ return buildDocumentFromString(responseBody);
+ } catch (ParseException e) {
+ logger.debug("Could not read response from IdP");
+ throw new ECPAuthenticationException(e);
+ } catch (IOException e) {
+ logger.debug("Could not read response from IdP");
+ throw new ECPAuthenticationException(e);
+ } catch (SAXException e) {
+ logger.debug("Could not read response from IdP");
+ throw new ECPAuthenticationException(e);
+ } catch (ParserConfigurationException e) {
+ logger.debug("Could not read response from IdP");
+ throw new ECPAuthenticationException(e);
+ }
+ }
+
+ protected Document buildDocumentFromString(String input)
+ throws IOException, ParserConfigurationException, SAXException {
+ DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
+ return builder.parse(new InputSource(new StringReader(input)));
+ }
+
+ protected Object queryDocument(Document xmlDocument, String expression,
+ QName returnType) throws XPathException {
+ XPath xpath = xpathFactory.newXPath();
+ xpath.setNamespaceContext(namespaceResolver);
+ XPathExpression xPathExpression = xpath.compile(expression);
+ return xPathExpression.evaluate(xmlDocument, returnType);
+ }
+
+ protected String documentToString(Document xmlDocument)
+ throws TransformerConfigurationException, TransformerException {
+ Transformer transformer = transformerFactory.newTransformer();
+
+ StreamResult result = new StreamResult(new StringWriter());
+ DOMSource source = new DOMSource(xmlDocument);
+ transformer.transform(source, result);
+
+ return result.getWriter().toString();
+ }
+
+ public DefaultHttpClient getHttpClient() {
+ return client;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/edu/kit/scc/dei/ecplean/ECPIdPAuth.java b/src/main/java/edu/kit/scc/dei/ecplean/ECPIdPAuth.java
new file mode 100644
index 0000000..0eb035b
--- /dev/null
+++ b/src/main/java/edu/kit/scc/dei/ecplean/ECPIdPAuth.java
@@ -0,0 +1,82 @@
+package edu.kit.scc.dei.ecplean;
+
+import java.io.IOException;
+import java.net.URI;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathException;
+
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+public class ECPIdPAuth extends ECPAuthenticatorBase {
+
+ public ECPIdPAuth(String username, String password,
+ URI idpEcpEndpoint) {
+ this(new DefaultHttpClient(), username, password, idpEcpEndpoint);
+ }
+
+ public ECPIdPAuth(DefaultHttpClient client, String username, String password,
+ URI idpEcpEndpoint) {
+ super(client);
+
+ authInfo = new ECPAuthenticationInfo(username, password, idpEcpEndpoint, null);
+ authInfo.setAuthState(ECPAuthState.NOT_STARTED);
+ }
+
+ public String authenticate(String paosMessage) throws ECPAuthenticationException {
+
+ Document initResponse;
+ try {
+ initResponse = buildDocumentFromString(paosMessage);
+ } catch (IOException e) {
+ logger.debug("Parsing SP Request failed");
+ throw new ECPAuthenticationException(e);
+ } catch (ParserConfigurationException e) {
+ logger.debug("Parsing SP Request failed");
+ throw new ECPAuthenticationException(e);
+ } catch (SAXException 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);
+ idpResponse.getDocumentElement().getFirstChild().getFirstChild().setTextContent(relayState);
+
+ try {
+ return documentToString(idpResponse);
+ } catch (TransformerConfigurationException e) {
+ logger.debug("documentToString failed");
+ throw new ECPAuthenticationException(e);
+ } catch (TransformerException e) {
+ logger.debug("documentToString failed");
+ throw new ECPAuthenticationException(e);
+ }
+ }
+
+}
diff --git a/src/main/java/edu/kit/scc/dei/ecplean/NamespaceResolver.java b/src/main/java/edu/kit/scc/dei/ecplean/NamespaceResolver.java
new file mode 100644
index 0000000..455923e
--- /dev/null
+++ b/src/main/java/edu/kit/scc/dei/ecplean/NamespaceResolver.java
@@ -0,0 +1,39 @@
+package edu.kit.scc.dei.ecplean;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.xml.namespace.NamespaceContext;
+
+public class NamespaceResolver implements NamespaceContext {
+
+ private Map<String, String> prefixMap;
+ private Map<String, String> uriMap;
+
+ public NamespaceResolver() {
+ prefixMap = new HashMap<String, String>();
+ uriMap = new HashMap<String, String>();
+ }
+
+ public void addNamespace(String prefix, String uri) {
+ prefixMap.put(prefix, uri);
+ uriMap.put(uri, prefix);
+ }
+
+ @Override
+ public String getNamespaceURI(String prefix) {
+ return prefixMap.get(prefix);
+ }
+
+ @Override
+ public String getPrefix(String namespaceURI) {
+ return uriMap.get(namespaceURI);
+ }
+
+ @Override
+ public Iterator<String> getPrefixes(String namespaceURI) {
+ return prefixMap.keySet().iterator();
+ }
+
+}