[Lada-commits] [PATCH 03 of 15] Merged branch openid back to default
Wald Commits
scm-commit at wald.intevation.org
Mon Mar 23 17:59:55 CET 2015
# HG changeset patch
# User Raimund Renkert <raimund.renkert at intevation.de>
# Date 1426771724 -3600
# Node ID b0d674240c2998c74e50986be8c3f050c4c1602a
# Parent 08084d754073ed880f46e2aab1d4c796cebb6a2e
# Parent 0e46adb8fcc5972c560c963267facac00cd653e8
Merged branch openid back to default.
diff -r 08084d754073 -r b0d674240c29 pom.xml
--- a/pom.xml Thu Mar 19 09:28:18 2015 +0100
+++ b/pom.xml Thu Mar 19 14:28:44 2015 +0100
@@ -60,7 +60,11 @@
<groupId>org.jboss.spec.javax.json</groupId>
<artifactId>jboss-json-api_1.0_spec</artifactId>
</dependency>
-
+ <dependency>
+ <groupId>org.jboss.spec.javax.servlet</groupId>
+ <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+ <version>1.0.2.Final</version>
+ </dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
@@ -139,6 +143,13 @@
<version>3.0.10.Final</version>
<scope>test</scope>
</dependency>
+
+ <!-- OpenID -->
+ <dependency>
+ <groupId>org.openid4java</groupId>
+ <artifactId>openid4java</artifactId>
+ <version>0.9.7</version>
+ </dependency>
</dependencies>
<profiles>
diff -r 08084d754073 -r b0d674240c29 src/main/java/de/intevation/lada/rest/LoginService.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/intevation/lada/rest/LoginService.java Thu Mar 19 14:28:44 2015 +0100
@@ -0,0 +1,49 @@
+/* Copyright (C) 2015 by Bundesamt fuer Strahlenschutz
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU GPL (v>=3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out
+ * the documentation coming with IMIS-Labordaten-Application for details.
+ */
+
+import javax.enterprise.context.RequestScoped;
+
+import javax.ws.rs.Path;
+import javax.ws.rs.GET;
+import javax.inject.Inject;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.Produces;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.lada.util.rest.Response;
+/**
+ * This class serves as a login check service
+ */
+ at Path("login")
+ at RequestScoped
+public class LoginService {
+
+ /* The logger used in this class.*/
+ @Inject
+ private Logger logger;
+
+ /**
+ * Get all probe objects.
+ *
+ * @return Response object containing all probe objects.
+ */
+ @SuppressWarnings("unchecked")
+ @GET
+ @Path("/")
+ @Produces("application/json")
+ public Response get(
+ @Context HttpHeaders headers,
+ @Context UriInfo info
+ ) {
+ /* This should probably contain the users name and roles. */
+ return new Response(true, 200, "Success");
+ }
+}
diff -r 08084d754073 -r b0d674240c29 src/main/java/de/intevation/lada/util/auth/OpenIDFilter.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/intevation/lada/util/auth/OpenIDFilter.java Thu Mar 19 14:28:44 2015 +0100
@@ -0,0 +1,360 @@
+/* Copyright (C) 2015 by Bundesamt fuer Strahlenschutz
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU GPL (v>=3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out
+ * the documentation coming with IMIS-Labordaten-Application for details.
+ */
+
+package de.intevation.lada.util.auth;
+
+import org.apache.log4j.Logger;
+
+import java.util.Map;
+import java.util.List;
+import java.util.LinkedHashMap;
+import java.net.URLDecoder;
+import java.util.Date;
+import java.util.Properties;
+import java.util.Enumeration;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.annotation.WebFilter;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpSession;
+
+import org.openid4java.association.AssociationSessionType;
+import org.openid4java.association.AssociationException;
+import org.openid4java.consumer.ConsumerManager;
+import org.openid4java.consumer.ConsumerException;
+import org.openid4java.consumer.InMemoryConsumerAssociationStore;
+import org.openid4java.consumer.AbstractNonceVerifier;
+import org.openid4java.message.ParameterList;
+import org.openid4java.consumer.VerificationResult;
+import org.openid4java.discovery.DiscoveryInformation;
+import org.openid4java.discovery.Identifier;
+import org.openid4java.discovery.DiscoveryException;
+import org.openid4java.message.MessageException;
+import org.openid4java.message.AuthRequest;
+import org.openid4java.message.AuthSuccess;
+import org.openid4java.message.ax.AxMessage;
+import org.openid4java.message.ax.FetchRequest;
+import org.openid4java.message.ax.FetchResponse;
+
+/** ServletFilter used for OpenID authentification. */
+ at WebFilter("/*")
+public class OpenIDFilter implements Filter {
+
+ private static final String CONFIG_FILE = "/openid.properties";
+
+ /** The name of the header field used to transport OpenID parameters.*/
+ private static final String OID_HEADER_DEFAULT = "X-OPENID-PARAMS";
+ private String oidHeader;
+
+ /** The identity provider we accept here. */
+ private static final String IDENTITY_PROVIDER_DEFAULT =
+ "https://localhost/openid/";
+ private String providerUrl;
+
+ private static final int SESSION_TIMEOUT_DEFAULT_MINUTES = 60;
+ private int sessionTimeout;
+
+ private boolean enabled;
+
+ private static Logger logger = Logger.getLogger(OpenIDFilter.class);
+
+ /** Nonce verifier to allow a session based on openid information.
+ *
+ * Usually one would create a session for the user but this would not
+ * be an advantage here as we want to transport the session in a header
+ * anyway.
+ *
+ * A nonce will be valid as long as as the maxAge is not reached.
+ * This is implemented by the basis verifier.
+ * We only implement seed no mark that we accept nonce's multiple
+ * times.
+ */
+ private class SessionNonceVerifier extends AbstractNonceVerifier {
+ public SessionNonceVerifier(int maxAge) {
+ super(maxAge);
+ }
+
+ @Override
+ protected int seen(Date now, String opUrl, String nonce) {
+ return OK;
+ }
+ };
+
+ private ConsumerManager manager;
+
+ /* This should be moved into a map <server->discovered>
+ * as we currently only supporting one server this is static. */
+ boolean discoveryDone = false;
+ private DiscoveryInformation discovered;
+
+ private boolean discoverServer() {
+ /* Perform discovery on the configured providerUrl */
+ List discoveries = null;
+ try {
+ discoveries = manager.discover(providerUrl);
+ } catch (DiscoveryException e) {
+ logger.debug("Discovery failed: " + e.getMessage());
+ return false;
+ }
+
+ if (discoveries == null || discoveries.isEmpty()) {
+ logger.error(
+ "Failed discovery step. OpenID provider unavailable?");
+ return false;
+ }
+
+ /* Add association for the discovered information */
+ discovered = manager.associate(discoveries);
+
+ return true;
+ }
+
+ /** Split up the OpenID response query provided in the header.
+ *
+ * @param responseQuery The query provided in the header field.
+ * @return The query as ParameterList or null on error.
+ */
+ private ParameterList splitParams(String responseQuery) {
+ if (responseQuery == null) {
+ return null;
+ }
+ Map<String, String> queryMap =
+ new LinkedHashMap<String, String>();
+ final String[] pairs = responseQuery.split("&");
+ for (String pair : pairs) {
+ final int idx = pair.indexOf("=");
+ if (idx <= 0) {
+ logger.debug("Invalid query.");
+ return null;
+ }
+ try {
+ final String key = URLDecoder.decode(
+ pair.substring(0, idx), "UTF-8");
+
+ if (queryMap.containsKey(key)) {
+ logger.debug("Duplicate key: " + key + " ignored.");
+ continue;
+ }
+ final String value = URLDecoder.decode(
+ pair.substring(idx + 1), "UTF-8");
+ queryMap.put(key, value);
+ } catch (java.io.UnsupportedEncodingException e) {
+ logger.error("UTF-8 unkown?!");
+ return null;
+ }
+ }
+ if (queryMap.isEmpty()) {
+ logger.debug("Empty query.");
+ return null;
+ }
+ return new ParameterList(queryMap);
+ }
+
+ private boolean checkOpenIDHeader(ServletRequest req) {
+
+ HttpServletRequest hReq = (HttpServletRequest) req;
+
+ /* Debug code to dump headers
+ Enumeration<String> headerNames = hReq.getHeaderNames();
+ while (headerNames.hasMoreElements()) {
+ String headerName = headerNames.nextElement();
+ logger.debug("Header: " + headerName);
+ Enumeration<String> headers = hReq.getHeaders(headerName);
+ while (headers.hasMoreElements()) {
+ String headerValue = headers.nextElement();
+ logger.debug("Value: " + headerValue);
+ }
+ }
+ */
+ /* First check if the header is provided at all */
+ String oidParamString = hReq.getHeader(oidHeader);
+
+ if (oidParamString == null) {
+ logger.debug("Header " + oidHeader + " not provided. Trying params.");
+ oidParamString = hReq.getQueryString();
+ }
+
+ /* Parse the parameters to a map for openid4j */
+ ParameterList oidParams = splitParams(oidParamString);
+ if (oidParams == null) {
+ return false;
+ }
+
+ /* Verify against the discovered server. */
+ VerificationResult verification = null;
+ String receivingURL = oidParams.getParameterValue("openid.return_to");
+
+ try {
+ verification = manager.verify(receivingURL, oidParams,
+ discovered);
+ } catch (MessageException e) {
+ logger.debug("Verification failed: " + e.getMessage());
+ return false;
+ } catch (DiscoveryException e) {
+ logger.debug("Verification discovery exception: " + e.getMessage());
+ return false;
+ } catch (AssociationException e) {
+ logger.debug("Verification assoc exception: " + e.getMessage());
+ return false;
+ }
+
+ /* See what could be verified */
+ Identifier verified = verification.getVerifiedId();
+ if (verified == null) {
+ logger.debug("Failed to verify Identity information: " +
+ verification.getStatusMsg());
+ return false;
+ }
+
+ AuthSuccess authSuccess =
+ (AuthSuccess) verification.getAuthResponse();
+ String rolesValue;
+ if (authSuccess.hasExtension(AxMessage.OPENID_NS_AX)) {
+ FetchResponse fetchResp = null;
+ try {
+ fetchResp = (FetchResponse) authSuccess.getExtension(
+ AxMessage.OPENID_NS_AX);
+ } catch (MessageException e) {
+ logger.debug("Failed to fetch extended result: " +
+ e.getMessage());
+ return false;
+ }
+ String roles = fetchResp.getAttributeValue("attr1");
+ logger.debug("Roles are: " + roles);
+ } else {
+ logger.debug("No such extension.");
+ }
+
+ logger.debug("Verified user: " + verified);
+
+ return true;
+ }
+
+ @Override
+ public void init(FilterConfig config)
+ throws ServletException
+ {
+ /* Read config and initialize configuration variables */
+ Properties properties = new Properties();
+ InputStream stream = null;
+ try {
+ stream = getClass().getResourceAsStream(CONFIG_FILE);
+ properties.load(stream);
+ stream.close();
+ } catch (java.io.FileNotFoundException e) {
+ logger.error ("Failed to find config file: " + CONFIG_FILE);
+ } catch (java.io.IOException e) {
+ logger.error ("Failed to read config file: " + CONFIG_FILE);
+ }
+ try {
+ sessionTimeout = Integer.parseInt(
+ properties.getProperty("session_timeout_minutes"));
+ } catch (NumberFormatException e) {
+ sessionTimeout = SESSION_TIMEOUT_DEFAULT_MINUTES;
+ }
+ oidHeader = properties.getProperty("oidHeader", OID_HEADER_DEFAULT);
+ providerUrl = properties.getProperty("identity_provider",
+ IDENTITY_PROVIDER_DEFAULT);
+ enabled = !properties.getProperty("enabled",
+ "true").toLowerCase().equals("false");
+
+ manager = new ConsumerManager();
+ /* We probably want to implement our own association store to keep
+ * associations persistent. */
+ manager.setAssociations(new InMemoryConsumerAssociationStore());
+ manager.setNonceVerifier(new SessionNonceVerifier(sessionTimeout * 60));
+ manager.setMinAssocSessEnc(AssociationSessionType.DH_SHA256);
+ discoveryDone = discoverServer();
+ }
+
+ @Override
+ public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
+ throws IOException, ServletException
+ {
+ if (!enabled) {
+ /* If we are not enabled we pass everything through */
+ logger.debug("OpenID filter disabled. Passing through.");
+ chain.doFilter(req, resp);
+ return;
+ }
+
+ HttpServletRequest hReq = (HttpServletRequest) req;
+ HttpServletResponse hResp = (HttpServletResponse) resp;
+ if (!discoveryDone) {
+ discoveryDone = discoverServer();
+ }
+ if (discoveryDone && checkOpenIDHeader(req)) {
+ /** Successfully authenticated. */
+ hResp.addHeader(oidHeader, hReq.getQueryString().replace(
+ "is_return=true",""));
+ chain.doFilter(req, resp);
+ return;
+ }
+ String authRequestURL = "Error communicating with openid server";
+ int errorCode = 698;
+ if (discoveryDone) {
+ /* Parse the parameters to a map for openid4j */
+ ParameterList params = splitParams(hReq.getQueryString());
+ String returnToUrl;
+ if (params == null) {
+ logger.debug("Failed to get any parameters from url.");
+ hResp.reset();
+ hResp.setStatus(401);
+ hResp.getOutputStream().print("{\"success\":false,\"message\":\"" + errorCode + "\",\"data\":" +
+ "\"No return url provided!\",\"errors\":{},\"warnings\":{}," +
+ "\"readonly\":false,\"totalCount\":0}");
+ hResp.getOutputStream().flush();
+ return;
+ } else {
+ returnToUrl = params.getParameterValue("return_to");
+ }
+ try {
+ AuthRequest authReq = manager.authenticate(discovered,
+ returnToUrl);
+ // Fetch the role attribute
+ FetchRequest fetch = FetchRequest.createFetchRequest();
+
+ fetch.addAttribute("attr1",
+ "http://axschema.org/person/role",
+ true, 0);
+ // attach the extension to the authentication request
+ authReq.addExtension(fetch);
+
+ authRequestURL = authReq.getDestinationUrl(true);
+ errorCode = 699;
+ } catch (MessageException e) {
+ logger.debug("Failed to create the Authentication request: " +
+ e.getMessage());
+ } catch (ConsumerException e) {
+ logger.debug("Error in consumer manager: " +
+ e.getMessage());
+ }
+ }
+ hResp.reset();
+ hResp.setStatus(401);
+ hResp.getOutputStream().print("{\"success\":false,\"message\":\"" + errorCode + "\",\"data\":" +
+ "\"" + authRequestURL + "\",\"errors\":{},\"warnings\":{}," +
+ "\"readonly\":false,\"totalCount\":0}");
+ hResp.getOutputStream().flush();
+ }
+ @Override
+ public void destroy()
+ {
+ }
+};
diff -r 08084d754073 -r b0d674240c29 src/main/resources/openid.properties
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/resources/openid.properties Thu Mar 19 14:28:44 2015 +0100
@@ -0,0 +1,14 @@
+# The name of the header field used to transport read OpenID
+# parameters from the client
+oid_header=X-OPENID-PARAMS
+
+# The URL of the identity provder
+identity_provider=https://bfs-identity.intevation.de/openid/
+
+# Session timeout in minutes
+session_timeout_minutes=60
+
+# Set this to false to disable the openID filter altogether
+# doing this will disable authentication and authorization
+# completely. Use this only for testing!
+enabled=true
More information about the Lada-commits
mailing list