Skip to content

Commit

Permalink
Merge branch 'dev' into deploy-to-ec2
Browse files Browse the repository at this point in the history
Conflicts:
	pom.xml
  • Loading branch information
landonreed committed Nov 30, 2018
2 parents b372303 + 3e47cec commit 7c92c1d
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 11 deletions.
5 changes: 4 additions & 1 deletion configurations/default/env.yml.tmp
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
AUTH0_CLIENT_ID: your-auth0-client-id
AUTH0_DOMAIN: your-auth0-domain
AUTH0_SECRET: your-auth0-secret
# Note: One of AUTH0_SECRET or AUTH0_PUBLIC_KEY should be used depending on the signing algorithm set on the client.
# It seems that newer Auth0 accounts (2017 and later) might default to RS256 (public key).
AUTH0_SECRET: your-auth0-secret # uses HS256 signing algorithm
# AUTH0_PUBLIC_KEY: /path/to/auth0.pem # uses RS256 signing algorithm
AUTH0_TOKEN: your-auth0-token
DISABLE_AUTH: false
OSM_VEX: http://localhost:1000
Expand Down
40 changes: 38 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,42 @@
<artifactId>datatools-server</artifactId>
<version>3.2.0-SNAPSHOT</version>

<licenses>
<license>
<name>MIT License</name>
<url>https://opensource.org/licenses/MIT</url>
</license>
</licenses>

<!-- Developer entries are provided for primary developers.
For other contributors, see https://github.com/catalogueglobal/datatools-server/graphs/contributors -->
<developers>
<developer>
<name>Landon Reed</name>
<email>[email protected]</email>
<organization>Conveyal</organization>
<organizationUrl>http://conveyal.com/</organizationUrl>
</developer>
<developer>
<name>Andrew Byrd</name>
<email>[email protected]</email>
<organization>Conveyal</organization>
<organizationUrl>http://conveyal.com/</organizationUrl>
</developer>
<developer>
<name>David Emory</name>
<email>[email protected]</email>
<organization>Conveyal</organization>
<organizationUrl>http://conveyal.com/</organizationUrl>
</developer>
<developer>
<name>Evan Siroky</name>
<email>[email protected]</email>
<organization>Conveyal</organization>
<organizationUrl>http://conveyal.com/</organizationUrl>
</developer>
</developers>

<!-- Define where the source code for project lives -->
<scm>
<connection>scm:git:https://github.com/catalogueglobal/datatools-server.git</connection>
Expand Down Expand Up @@ -222,7 +258,7 @@
<dependency>
<groupId>com.conveyal</groupId>
<artifactId>gtfs-lib</artifactId>
<version>4.0.1-SNAPSHOT</version>
<version>4.1.0</version>
</dependency>

<!-- Used for data-tools application database -->
Expand Down Expand Up @@ -307,7 +343,7 @@
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>2.1.0</version>
<version>2.3.0</version>
</dependency>

<!-- Rest Assured is an assertion library that makes testing web APIs easy. -->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.conveyal.datatools.manager.auth;

import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.internal.org.apache.commons.codec.binary.Base64;
import com.auth0.jwt.JWTVerifyException;
import com.auth0.jwt.pem.PemReader;
import com.conveyal.datatools.manager.DataManager;
import com.conveyal.datatools.manager.models.FeedSource;
import com.conveyal.datatools.manager.persistence.Persistence;
Expand All @@ -10,18 +11,34 @@
import org.slf4j.LoggerFactory;
import spark.Request;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.util.Map;

import static com.conveyal.datatools.common.utils.SparkUtils.haltWithMessage;
import static com.conveyal.datatools.manager.DataManager.getConfigPropertyAsText;
import static com.conveyal.datatools.manager.DataManager.hasConfigProperty;

/**
* This handles verifying the Auth0 token passed in the Auth header of Spark HTTP requests.
*
* Created by demory on 3/22/16.
*/

public class Auth0Connection {
public static final String APP_METADATA = "app_metadata";
public static final String USER_METADATA = "user_metadata";
public static final String SCOPE = "http://datatools";
public static final String SCOPED_APP_METADATA = String.join("/", SCOPE, APP_METADATA);
public static final String SCOPED_USER_METADATA = String.join("/", SCOPE, USER_METADATA);
private static final ObjectMapper MAPPER = new ObjectMapper();
private static final Logger LOG = LoggerFactory.getLogger(Auth0Connection.class);
private static JWTVerifier verifier;

/**
* Check the incoming API request for the user token (and verify it) and assign as the "user" attribute on the
Expand All @@ -48,20 +65,71 @@ public static void checkUser(Request req) {
if (token == null) {
haltWithMessage(req, 401, "Could not find authorization token");
}
// Handle getting the verifier outside of the below verification try/catch, which is intended to catch issues
// with the client request. (getVerifier has its own exception/halt handling).
verifier = getVerifier(req);
// Validate the JWT and cast into the user profile, which will be attached as an attribute on the request object
// for downstream controllers to check permissions.
try {
byte[] decodedSecret = new Base64().decode(getConfigPropertyAsText("AUTH0_SECRET"));
JWTVerifier verifier = new JWTVerifier(decodedSecret);
Map<String, Object> jwt = verifier.verify(token);
remapTokenValues(jwt);
Auth0UserProfile profile = MAPPER.convertValue(jwt, Auth0UserProfile.class);
// The user attribute is used on the server side to check user permissions and does not have all of the
// fields that the raw Auth0 profile string does.
req.attribute("user", profile);
} catch (Exception e) {
LOG.warn("Login failed to verify with our authorization provider.", e);
haltWithMessage(req, 401, "Could not verify user's token");
}
}

/**
* Choose the correct JWT verification algorithm (based on the values present in env.yml config) and get the
* respective verifier.
*/
private static JWTVerifier getVerifier(Request req) {
if (verifier == null) {
try {
if (hasConfigProperty("AUTH0_SECRET")) {
// Use HS256 algorithm to verify token (uses client secret).
byte[] decodedSecret = new org.apache.commons.codec.binary.Base64().decode(getConfigPropertyAsText("AUTH0_SECRET"));
verifier = new JWTVerifier(decodedSecret);
} else if (hasConfigProperty("AUTH0_PUBLIC_KEY")) {
// Use RS256 algorithm to verify token (uses public key/.pem file).
PublicKey publicKey = PemReader.readPublicKey(getConfigPropertyAsText("AUTH0_PUBLIC_KEY"));
verifier = new JWTVerifier(publicKey);
} else throw new IllegalStateException("Auth0 public key or secret token must be defined in config (env.yml).");
} catch (IllegalStateException | NullPointerException | NoSuchAlgorithmException | IOException | NoSuchProviderException | InvalidKeySpecException e) {
LOG.error("Auth0 verifier configured incorrectly.");
e.printStackTrace();
haltWithMessage(req, 500, "Server authentication configured incorrectly.", e);
}
}
return verifier;
}

/**
* Handle mapping token values to the expected keys. This accounts for app_metadata and user_metadata that have been
* scoped to conform with OIDC (i.e., how newer Auth0 accounts structure the user profile) as well as the user_id ->
* sub mapping.
*/
private static void remapTokenValues(Map<String, Object> jwt) {
// If token did not contain app_metadata or user_metadata, add the scoped values to the decoded token object.
if (!jwt.containsKey(APP_METADATA) && jwt.containsKey(SCOPED_APP_METADATA)) {
jwt.put(APP_METADATA, jwt.get(SCOPED_APP_METADATA));
}
if (!jwt.containsKey(USER_METADATA) && jwt.containsKey(SCOPED_USER_METADATA)) {
jwt.put(USER_METADATA, jwt.get(SCOPED_USER_METADATA));
}
// Do the same for user_id -> sub
if (!jwt.containsKey("user_id") && jwt.containsKey("sub")) {
jwt.put("user_id", jwt.get("sub"));
}
// Remove scoped metadata objects to clean up user profile object.
jwt.remove(SCOPED_APP_METADATA);
jwt.remove(SCOPED_USER_METADATA);
}

/**
* Check that the user has edit privileges for the feed ID specified. NOTE: the feed ID provided in the request will
* represent a feed source, not a specific SQL namespace that corresponds to a feed version or specific set of GTFS
Expand Down
5 changes: 0 additions & 5 deletions src/main/resources/logback.xml
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{MMM dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}:%L - %msg%n</Pattern>
</layout>
</appender>

<logger name="com.base22" level="TRACE"/>


<root level="info">
<appender-ref ref="STDOUT" />
</root>
Expand Down

0 comments on commit 7c92c1d

Please sign in to comment.