# HG changeset patch # User Andre Heinecke # Date 1424876615 -3600 # Node ID 04e254571b8a0bb147a66204fad92867b8038279 # Parent 56a2d43b4af506b10b00ba461d1ffff9aba0d4f3 First non-functional implementation of openid verification Uses the openid4j package to extract openid information provided by the client as a HTTP Header. Currently the statelessness of the services prevents the exchange of discovery information between client and server. It is only useful for evaluation and testing. diff -r 56a2d43b4af5 -r 04e254571b8a pom.xml --- a/pom.xml Thu Feb 19 15:21:44 2015 +0100 +++ b/pom.xml Wed Feb 25 16:03:35 2015 +0100 @@ -139,6 +139,13 @@ 3.0.10.Final test + + + + org.openid4java + openid4java + 0.9.7 + diff -r 56a2d43b4af5 -r 04e254571b8a src/main/java/de/intevation/lada/rest/ProbeService.java --- a/src/main/java/de/intevation/lada/rest/ProbeService.java Thu Feb 19 15:21:44 2015 +0100 +++ b/src/main/java/de/intevation/lada/rest/ProbeService.java Wed Feb 25 16:03:35 2015 +0100 @@ -67,7 +67,7 @@ /* The authentication module.*/ @Inject - @AuthenticationConfig(type=AuthenticationType.NONE) + @AuthenticationConfig(type=AuthenticationType.OPENID) private Authentication authentication; /* The authorization module.*/ diff -r 56a2d43b4af5 -r 04e254571b8a src/main/java/de/intevation/lada/util/auth/OpenIDAuthentication.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/intevation/lada/util/auth/OpenIDAuthentication.java Wed Feb 25 16:03:35 2015 +0100 @@ -0,0 +1,206 @@ +/* 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 java.util.Map; +import java.util.List; +import java.util.LinkedHashMap; +import java.net.URLDecoder; + +import javax.inject.Inject; +import javax.ejb.Stateless; +import javax.ws.rs.core.HttpHeaders; + +import de.intevation.lada.util.annotation.AuthenticationConfig; + +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.InMemoryNonceVerifier; +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.apache.log4j.Logger; + +public class OpenIDAuthentication implements Authentication { + + /** The name of the header field used to transport OpenID parameters.*/ + private static final String OID_HEADER_FIELD= "X-OPENID-PARAMS"; + + /** The identity provider we accept here. */ + private static final String IDENTITY_PROVIDER = + "http://localhost:8087/account"; + + /** This is currently a faked dummy */ + private static final String RETURN_URL = + "http://localhost:8086/consumer-servlet/consumer?is_return=true"; + + private static final Logger logger = + Logger.getLogger(OpenIDAuthentication.class); + + private ConsumerManager manager; + + private Map idParams; + + boolean discoveryDone = false; + + private DiscoveryInformation discovered; + + private boolean discoverServer() { + /* Perform discovery on the configured IDENTITY_PROVIDER */ + List discoveries = null; + try { + discoveries = manager.discover(IDENTITY_PROVIDER); + } 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); + + /* Validate the parameters. */ + logger.debug("After discovery."); + try { + AuthRequest authReq = manager.authenticate(discovered, RETURN_URL); + logger.debug("Authenticate with: " + authReq.getDestinationUrl(true)); + } catch (MessageException e) { + logger.debug("Failed to create the Authentication request: " + + e.getMessage()); + } catch (ConsumerException e) { + logger.debug("Error in consumer manager: " + + e.getMessage()); + } + logger.debug("After authenticate."); + return true; + } + + public OpenIDAuthentication() { + manager = new ConsumerManager(); + /* TODO: Check for alternative configs. */ + manager.setAssociations(new InMemoryConsumerAssociationStore()); + manager.setNonceVerifier(new InMemoryNonceVerifier(50000)); + manager.setMinAssocSessEnc(AssociationSessionType.DH_SHA256); + discoveryDone = discoverServer(); + } + + /** 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) { + Map queryMap = + new LinkedHashMap(); + 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("Invalid query. Duplicate key: " + key); + return null; + } + 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(HttpHeaders headers) { + /* First check if there are is anything provided */ + List oidParamString = headers.getRequestHeader( + OID_HEADER_FIELD); + if (oidParamString == null) { + logger.debug("Header " + OID_HEADER_FIELD + " not provided."); + return false; + } + if (oidParamString.size() != 1) { + logger.debug("Found " + oidParamString.size() + " openid headers."); + return false; + } + + /* Parse the parameters. Do it first to avoid a useless discovery. */ + ParameterList oidParams = splitParams(oidParamString.get(0)); + if (oidParams == null) { + return false; + } + + VerificationResult verification = null; + try { + verification = manager.verify(RETURN_URL, 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; + } + + logger.debug("Verified user: " + verified); + + return true; + } + + @Override + public boolean isAuthenticated(HttpHeaders headers) { + if (!discoveryDone) { + discoveryDone = discoverServer(); + } + if (!discoveryDone) { + return false; + } + if (checkOpenIDHeader(headers)) { + /** Successfully authenticated. */ + return true; + } else { + + return false; + } + } +}