diff --git a/README.md b/README.md
index d22750c..c048898 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
## Description
-A valve for Tomcat8 that authenticates the JWT tokens created by Islandora in order to provide sessionless Authentication for Fedora4. Named after the Norse goddess [Syn](https://en.wikipedia.org/wiki/Syn_(goddess)).
+A ServletFilter that authenticates the JWT tokens created by Islandora in order to provide sessionless Authentication for Fedora4. Named after the Norse goddess [Syn](https://en.wikipedia.org/wiki/Syn_(goddess)).
## Building
@@ -15,54 +15,39 @@ This project requires Java 8 and can be built with [Gradle](https://gradle.org).
## Installing
### Copy Syn JAR
-Copy the JAR that was built above from `build/libs/islandora-syn-X.X.X-all.jar` and place into `$TOMCAT_HOME/lib` directory. Can be found in Ubuntu at: `/var/lib/tomcat8/lib/`. Note that this JAR is built to contain all the dependancies.
+Copy the JAR that was built above from `build/libs/islandora-syn-X.X.X-all.jar` and place into `$TOMCAT_HOME/lib` directory or the individual webapps `WEB-INF/lib` directory. Can be found in Ubuntu at: `/var/lib/tomcat8/lib/`. Note that this JAR is built to contain all the dependancies.
-### Register Valve
-Now register the valve in Tomcat configuration file.
-In Ubuntu this file is located at: `/var/lib/tomcat8/conf/context.xml`
+### Register Filter
+Now register the filter in web applications' `web.xml` file by adding something like.
```xml
-
+
+ SynFilter
+ ca.islandora.syn.valve.SynFilter
+
+ settings-path
+ /var/lib/tomcat8/conf/syn-settings.yml
+
+
+
+
+ SynFilter
+ /*
+
```
-where:
-* ***pathname***: The location of the settings file. Defaults to `$CATALINA_BASE/conf/syn-settings.xml`.
-
-### Enable `security-contraint`
-The valve checks if requested url is under **security contraints**. So, valve will activate only if the Fedora4 *web.xml* file contains something like:
-
-```xml
-
-
- Fedora4
- /*
-
-
- *
-
-
- NONE
-
-
-
- islandora
-
-
- BASIC
- fcrepo
-
-```
+Where the **settings-path** `param-value` is the the location of the settings file.
On ubuntu this file can be found at:
`/var/lib/tomcat8/webapps/fcrepo/WEB-INF/web.xml`
### Setup Syn Configuration
-Modify the [example configuration](./conf/syn-settings.example.xml) and move it to: `$CATALINA_BASE/conf/syn-settings.xml`.
+Modify the [example configuration](./conf/syn-settings.example.yaml) and move it to: `$CATALINA_BASE/conf/syn-settings.xml`. Then use this path when configuring the application's filter `init-param`s.
## Maintainers
* [Jonathan Green](https://github.com/jonathangreen/)
+* [Jared Whiklo](https://github.com/whikloj)
## Development
diff --git a/build.gradle b/build.gradle
index 7b29848..d99d80d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -14,6 +14,20 @@ targetCompatibility = 1.8
def tomcatVersion = '8.0.28'
+ext {
+
+ versions = [
+ bcprov : '1.56',
+ jackson : '2.9.2',
+ javaJwt : '3.1.0',
+ junit : '4.12',
+ logback : '1.0.13',
+ mockito : '2.7.14',
+ servlet : '3.0.1',
+ slf4j : '1.7.12'
+ ]
+}
+
checkstyle {
configFile = rootProject.file('gradle/checkstyle/checkstyle.xml')
configProperties.checkstyleConfigDir = rootProject.file('gradle/checkstyle')
@@ -24,15 +38,17 @@ repositories {
}
dependencies {
- compile group: 'com.auth0', name: 'java-jwt', version:'3.1.0'
- compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version:'1.56'
- compileOnly group: 'org.apache.tomcat', name: 'tomcat-catalina', version:tomcatVersion
- compileOnly group: 'org.apache.tomcat', name: 'tomcat-coyote', version:tomcatVersion
-
- testCompile group: 'junit', name: 'junit', version:'4.12'
- testCompile group: 'org.mockito', name: 'mockito-core', version:'2.7.14'
- testCompile group: 'org.apache.tomcat', name: 'tomcat-catalina', version:tomcatVersion
- testCompile group: 'org.apache.tomcat', name: 'tomcat-coyote', version:tomcatVersion
+ compile group: 'com.auth0', name: 'java-jwt', version: versions.javaJwt
+ compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: versions.bcprov
+ compile group: 'org.slf4j', name: 'slf4j-api', version: versions.slf4j
+ compile group: 'javax.servlet', name: 'javax.servlet-api', version: versions.servlet
+ compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: versions.jackson
+ compile group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: versions.jackson
+
+ testRuntime group: 'ch.qos.logback', name: 'logback-classic', version: versions.logback
+
+ testCompile group: 'junit', name: 'junit', version: versions.junit
+ testCompile group: 'org.mockito', name: 'mockito-core', version: versions.mockito
}
jacocoTestReport {
@@ -54,6 +70,13 @@ buildscript {
jar {
baseName = projectName
version = projectVersion
+ manifest {
+ attributes("Implementation-Title": baseName,
+ "Implementation-Version": version,
+ "Implementation-Vendor": "Islandora",
+ "Class-Path": configurations.compile.collect { it.getName() }.join(' '))
+ }
+
}
shadowJar {
@@ -62,3 +85,14 @@ shadowJar {
}
assemble.dependsOn(shadowJar);
+
+test {
+ testLogging {
+ // Make sure output from
+ // standard out or error is shown
+ // in Gradle output.
+ // showStandardStreams = true
+ //events 'standard_out'
+ exceptionFormat = 'full'
+ }
+}
\ No newline at end of file
diff --git a/conf/syn-settings.example.xml b/conf/syn-settings.example.xml
deleted file mode 100644
index 3cbd328..0000000
--- a/conf/syn-settings.example.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
-my secret key
-
-
-
-
-
-
-
-
-
-
-
-
-
- my super secret token
-
-
-
diff --git a/conf/syn-settings.example.yaml b/conf/syn-settings.example.yaml
new file mode 100644
index 0000000..a1ea934
--- /dev/null
+++ b/conf/syn-settings.example.yaml
@@ -0,0 +1,43 @@
+version: 1
+# Sites can be specified with a Key inline, or with a reference to a key
+# stored in a file. Both are shown in examples below.
+#
+# The encoding parameter depends on what algorithm is chosen.
+# HS256, HS384, HS512 support: plain and base64.
+# RS256, RS384, RS512 support: PEM.
+
+# A site with an inline key
+site:
+ url: http://test.com
+ algorithm: HS256
+ encoding: plain
+ key: my secret key
+
+# A site with a key stored in a file
+site:
+ url: http://test2.com
+ algorithm: HS256
+ encoding: base64
+ path: /somewhere/on/filesystem.key
+
+# A site that allows all GET/HEAD requests
+site:
+ url: http://test3.com
+ algorithm: HS256
+ encoding: plain
+ anonymous: true
+
+# This is how you specify a default site, which will be chosen if no
+# other site matches the JWT url claim
+site:
+ algorithm: RS256
+ encoding: PEM
+ path: /somewhere/on/filesystem.key
+ default: true
+
+# This lets you specify a master token for testing. This should be used with care, as it gives anyone
+# with this token unlimited access to your repository.
+token:
+ user: test
+ roles: role1,role2,role3
+ value: my super secret token
diff --git a/src/main/java/ca/islandora/syn/settings/Config.java b/src/main/java/ca/islandora/syn/settings/Config.java
index de74243..b96a38b 100644
--- a/src/main/java/ca/islandora/syn/settings/Config.java
+++ b/src/main/java/ca/islandora/syn/settings/Config.java
@@ -3,29 +3,37 @@
import java.util.ArrayList;
import java.util.List;
+import com.fasterxml.jackson.annotation.JsonSetter;
+
public class Config {
private int version = -1;
- private List sites = new ArrayList<>();
- private List tokens = new ArrayList<>();
+ private final List sites = new ArrayList<>();
+ private final List tokens = new ArrayList<>();
+ @JsonSetter("site")
public void addSite(final Site site) {
- sites.add(site);
+ this.sites.add(site);
}
+
public List getSites() {
- return sites;
+ return this.sites;
}
public int getVersion() {
return this.version;
}
+
public void setVersion(final int version) {
this.version = version;
}
+ @JsonSetter("token")
public void addToken(final Token token) {
- tokens.add(token);
+ this.tokens.add(token);
}
+
public List getTokens() {
- return tokens;
+ return this.tokens;
}
+
}
diff --git a/src/main/java/ca/islandora/syn/settings/SettingsParser.java b/src/main/java/ca/islandora/syn/settings/SettingsParser.java
index 92e25b5..fa28ba8 100644
--- a/src/main/java/ca/islandora/syn/settings/SettingsParser.java
+++ b/src/main/java/ca/islandora/syn/settings/SettingsParser.java
@@ -1,10 +1,11 @@
package ca.islandora.syn.settings;
+import static org.slf4j.LoggerFactory.getLogger;
+
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
-import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.nio.file.Files;
@@ -17,66 +18,88 @@
import java.util.Map;
import java.util.stream.Collectors;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.digester.Digester;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
-import org.xml.sax.SAXException;
+import org.slf4j.Logger;
import com.auth0.jwt.algorithms.Algorithm;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
public final class SettingsParser {
- private static Digester digester = null;
- private static Log log = LogFactory.getLog(Site.class);
- private enum AlgorithmType {INVALID, RSA, HMAC}
-
- private SettingsParser() { }
-
- private static Digester getDigester() {
- if (digester == null) {
- digester = new Digester();
- digester.setValidating(false);
- digester.addObjectCreate("config", "ca.islandora.syn.settings.Config");
- digester.addSetProperties("config");
- digester.addObjectCreate("config/site", "ca.islandora.syn.settings.Site");
- digester.addSetProperties("config/site");
- digester.addCallMethod("config/site", "setKey", 0);
- digester.addSetNext("config/site", "addSite", "ca.islandora.syn.settings.Site");
- digester.addObjectCreate("config/token", "ca.islandora.syn.settings.Token");
- digester.addSetProperties("config/token");
- digester.addCallMethod("config/token", "setToken", 0);
- digester.addSetNext("config/token", "addToken", "ca.islandora.syn.settings.Token");
+ private static Logger log = getLogger(Site.class);
+
+ private enum AlgorithmType {
+ INVALID, RSA, HMAC
+ }
+
+ private static final int VALID_VERSION = 1;
+
+ private Config loadedSites;
+
+ private final static ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
+
+ /**
+ * Constructor.
+ *
+ * @param settings
+ * A Reader object with the configuration in it.
+ * @throws Exception
+ * On loading/parsing or version of configuration.
+ */
+ public SettingsParser(final Reader settings) {
+ try {
+ loadedSites = mapper.readValue(settings, Config.class);
+ } catch (final Exception e) {
+ log.error("Error loading settings file.", e);
+ throw new SettingsParserException("Error parsing settings file.", e);
+ }
+
+ if (loadedSites.getVersion() != VALID_VERSION) {
+ log.error("Incorrect config version. Aborting.");
+ throw new SettingsParserException("Incorrect config version. Aborting.");
}
- return digester;
}
+ /**
+ * Static creator.
+ *
+ * @param settings
+ * A Reader object with the configuration on it.
+ * @return The SettingsParser.
+ * @throws Exception
+ * On loading/parsing or version of configuration.
+ */
+ public static SettingsParser create(final Reader settings) {
+ return new SettingsParser(settings);
+ }
+ /**
+ * Determine the type of key algorithm.
+ *
+ * @param algorithm
+ * The algorithm name.
+ * @return an algorithm.
+ */
private static AlgorithmType getSiteAlgorithmType(final String algorithm) {
- if (algorithm.equalsIgnoreCase("RS256")) {
- return AlgorithmType.RSA;
- } else if (algorithm.equalsIgnoreCase("RS384")) {
+ if (algorithm.toUpperCase().startsWith("RS")) {
return AlgorithmType.RSA;
- } else if (algorithm.equalsIgnoreCase("RS512")) {
- return AlgorithmType.RSA;
- }
-
- if (algorithm.equalsIgnoreCase("HS256")) {
- return AlgorithmType.HMAC;
- } else if (algorithm.equalsIgnoreCase("HS384")) {
- return AlgorithmType.HMAC;
- } else if (algorithm.equalsIgnoreCase("HS512")) {
+ } else if (algorithm.toUpperCase().startsWith("HS")) {
return AlgorithmType.HMAC;
} else {
return AlgorithmType.INVALID;
}
}
- private static boolean validateExpandPath(final Site site) {
- File file = new File(site.getPath());
- if (!file.isAbsolute()) {
- file = new File(System.getProperty("catalina.base"), site.getPath());
- }
+ /**
+ * Validate the site's key path.
+ *
+ * @param site
+ * The site to act on.
+ * @return true if key exists.
+ */
+ private static boolean validatePath(final Site site) {
+ final File file = new File(site.getPath());
if (!file.exists() || !file.canRead()) {
log.error("Path does not exist:" + site.getPath() + ". Site ignored.");
return false;
@@ -85,16 +108,23 @@ private static boolean validateExpandPath(final Site site) {
return true;
}
+ /**
+ * Parse a RSA encoded key and return the algorithm for verifying.
+ *
+ * @param site
+ * The site to get the key for.
+ * @return A RSA algorithm for the site's key.
+ */
private static Algorithm getRsaAlgorithm(final Site site) {
Reader publicKeyReader = null;
RSAPublicKey publicKey = null;
- if (!site.getKey().equalsIgnoreCase("")) {
+ if (site.getKey() != null) {
publicKeyReader = new StringReader(site.getKey());
} else if (site.getPath() != null) {
try {
publicKeyReader = new FileReader(site.getPath());
- } catch (FileNotFoundException e) {
+ } catch (final FileNotFoundException e) {
log.error("Private key file not found.");
}
}
@@ -112,7 +142,7 @@ private static Algorithm getRsaAlgorithm(final Site site) {
publicKey = (RSAPublicKey) factory.generatePublic(pubKeySpec);
pemReader.close();
publicKeyReader.close();
- } catch (Exception e) {
+ } catch (final Exception e) {
log.error("Error loading public key.");
return null;
}
@@ -133,16 +163,23 @@ private static Algorithm getRsaAlgorithm(final Site site) {
}
}
+ /**
+ * Parse a HMAC encoded key and return the algorithm for verifying.
+ *
+ * @param site
+ * The site to get the key for.
+ * @return A HMAC algorithm for the site's key.
+ */
private static Algorithm getHmacAlgorithm(final Site site) {
- byte[] secret;
+ final byte[] secret;
byte[] secretRaw = null;
- if (!site.getKey().equalsIgnoreCase("")) {
+ if (site.getKey() != null) {
secretRaw = site.getKey().trim().getBytes();
} else if (site.getPath() != null) {
try {
secretRaw = Files.readAllBytes(Paths.get(site.getPath()));
- } catch (IOException e) {
+ } catch (final IOException e) {
log.error("Unable to get secret from file.", e);
}
}
@@ -154,7 +191,7 @@ private static Algorithm getHmacAlgorithm(final Site site) {
if (site.getEncoding().equalsIgnoreCase("base64")) {
try {
secret = Base64.getDecoder().decode(secretRaw);
- } catch (Exception e) {
+ } catch (final Exception e) {
log.error("Base64 decode error. Skipping site.", e);
return null;
}
@@ -175,34 +212,20 @@ private static Algorithm getHmacAlgorithm(final Site site) {
}
}
- private static Config getSites(final InputStream settings) {
- Config sites;
-
- try {
- sites = getSitesObject(settings);
- } catch (Exception e) {
- log.error("Error loading settings file.", e);
- return null;
- }
-
- if (sites.getVersion() != 1) {
- log.error("Incorrect XML version. Aborting.");
- return null;
- }
-
- return sites;
- }
-
- public static Map getSiteAlgorithms(final InputStream settings) {
+ /**
+ * Get site keys from loaded Config.
+ *
+ * @return Map of URLs (or null) and parsed keys for verification.
+ */
+ public Map getSiteAlgorithms() {
final Map algorithms = new HashMap<>();
- final Config sites = getSites(settings);
- if (sites == null) {
+ if (loadedSites == null) {
return algorithms;
}
boolean defaultSet = false;
- for (Site site : sites.getSites()) {
+ for (final Site site : loadedSites.getSites()) {
final boolean pathDefined = site.getPath() != null && !site.getPath().equalsIgnoreCase("");
final boolean keyDefined = site.getKey() != null && !site.getKey().equalsIgnoreCase("");
@@ -213,20 +236,20 @@ public static Map getSiteAlgorithms(final InputStream setting
}
if (site.getPath() != null) {
- if (!validateExpandPath(site)) {
+ if (!validatePath(site)) {
continue;
}
}
// Check that the algorithm type is valid.
final AlgorithmType algorithmType = getSiteAlgorithmType(site.getAlgorithm());
- Algorithm algorithm;
+ final Algorithm algorithm;
if (algorithmType == AlgorithmType.HMAC) {
algorithm = getHmacAlgorithm(site);
} else if (algorithmType == AlgorithmType.RSA) {
algorithm = getRsaAlgorithm(site);
} else {
- log.error("Invalid algorithm selection: " + site.getAlgorithm() + ". Site ignored." );
+ log.error("Invalid algorithm selection: " + site.getAlgorithm() + ". Site ignored.");
continue;
}
@@ -252,14 +275,18 @@ public static Map getSiteAlgorithms(final InputStream setting
return algorithms;
}
- public static Map getSiteStaticTokens(final InputStream settings) {
- final Config sites = getSites(settings);
- if (sites == null) {
+ /**
+ * Get static tokens from the loaded Config.
+ *
+ * @return Map of token value and Token object.
+ */
+ public Map getSiteStaticTokens() {
+ if (loadedSites == null) {
return new HashMap();
}
- final Map tokens = sites.getTokens().stream().filter(x -> !x.getToken().isEmpty())
- .collect(Collectors.toMap(Token::getToken, t -> t));
+ final Map tokens = loadedSites.getTokens().stream().filter(x -> !x.getValue().isEmpty())
+ .collect(Collectors.toMap(Token::getValue, t -> t));
return tokens;
}
@@ -267,25 +294,28 @@ public static Map getSiteStaticTokens(final InputStream settings)
/**
* Build a list of site urls that allow anonymous GET requests.
*
- * @param settings the path to the syn-settings file
- * @return list of site urls.
+ * @return Map of all URLs (or null for default) and boolean if they allow
+ * anonymous.
*/
- public static Map getSiteAllowAnonymous(final InputStream settings) {
- final Config sites = getSites(settings);
- if (sites == null) {
+ public Map getSiteAllowAnonymous() {
+ if (loadedSites == null) {
return new HashMap();
}
- final Map anonymousAllowed = sites.getSites().stream().filter(s -> !s.getDefault())
- .collect(Collectors.toMap(Site::getUrl, Site::getAnonymous));
- sites.getSites().stream().filter(Site::getDefault).findFirst()
- .ifPresent(s -> anonymousAllowed.put("default", s.getAnonymous()));
+ final Map anonymousAllowed = loadedSites.getSites().stream().filter(s -> !s.getDefault())
+ .collect(Collectors.toMap(Site::getUrl, Site::getAnonymous));
+ loadedSites.getSites().stream().filter(Site::getDefault).findFirst()
+ .ifPresent(s -> anonymousAllowed.put("default", s.getAnonymous()));
return anonymousAllowed;
}
- static Config getSitesObject(final InputStream settings)
- throws IOException, SAXException {
- return (Config) getDigester().parse(settings);
+ /**
+ * Getter for loaded Config object.
+ *
+ * @return
+ */
+ public Config getConfig() {
+ return loadedSites;
}
}
diff --git a/src/main/java/ca/islandora/syn/settings/SettingsParserException.java b/src/main/java/ca/islandora/syn/settings/SettingsParserException.java
new file mode 100644
index 0000000..a61556c
--- /dev/null
+++ b/src/main/java/ca/islandora/syn/settings/SettingsParserException.java
@@ -0,0 +1,35 @@
+package ca.islandora.syn.settings;
+
+/**
+ * Exception while parsing the settings YAML file.
+ *
+ * @author whikloj
+ * @since 2018-01-17
+ */
+public class SettingsParserException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor
+ *
+ * @param message
+ * Exception message
+ */
+ public SettingsParserException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param message
+ * Exception message
+ * @param e
+ * Wrapped Exception
+ */
+ public SettingsParserException(final String message, final Throwable e) {
+ super(message, e);
+ }
+
+}
diff --git a/src/main/java/ca/islandora/syn/settings/Site.java b/src/main/java/ca/islandora/syn/settings/Site.java
index 1524282..4cf7f19 100644
--- a/src/main/java/ca/islandora/syn/settings/Site.java
+++ b/src/main/java/ca/islandora/syn/settings/Site.java
@@ -12,6 +12,7 @@ public class Site {
public String getUrl() {
return this.url;
}
+
public void setUrl(final String url) {
this.url = url;
}
@@ -19,6 +20,7 @@ public void setUrl(final String url) {
public String getAlgorithm() {
return this.algorithm;
}
+
public void setAlgorithm(final String algorithm) {
this.algorithm = algorithm;
}
@@ -26,6 +28,7 @@ public void setAlgorithm(final String algorithm) {
public String getKey() {
return this.key;
}
+
public void setKey(final String key) {
this.key = key;
}
@@ -33,6 +36,7 @@ public void setKey(final String key) {
public String getPath() {
return this.path;
}
+
public void setPath(final String path) {
this.path = path;
}
@@ -40,6 +44,7 @@ public void setPath(final String path) {
public String getEncoding() {
return this.encoding;
}
+
public void setEncoding(final String encoding) {
this.encoding = encoding;
}
@@ -47,6 +52,7 @@ public void setEncoding(final String encoding) {
public boolean getDefault() {
return this.defaultItem;
}
+
public void setDefault(final boolean defaultItem) {
this.defaultItem = defaultItem;
}
@@ -63,7 +69,8 @@ public boolean getAnonymous() {
/**
* Set allow GET requests with a token that match this site.
*
- * @param allowAnonGet boolean whether to allow these requests.
+ * @param allowAnonGet
+ * boolean whether to allow these requests.
*/
public void setAnonymous(final boolean allowAnonGet) {
this.allowAnonymous = allowAnonGet;
diff --git a/src/main/java/ca/islandora/syn/settings/Token.java b/src/main/java/ca/islandora/syn/settings/Token.java
index 3548d72..630c6d1 100644
--- a/src/main/java/ca/islandora/syn/settings/Token.java
+++ b/src/main/java/ca/islandora/syn/settings/Token.java
@@ -6,8 +6,8 @@
public class Token {
private String user = "islandoraAdmin";
- private List roles = new ArrayList<>();
- private String token = "";
+ private final List roles = new ArrayList<>();
+ private String value = "";
public String getUser() {
return user;
@@ -25,15 +25,15 @@ public void setRoles(final String roles) {
this.roles.clear();
if (!roles.isEmpty()) {
final String[] parts = roles.split(",");
- Collections.addAll(this.roles,parts);
+ Collections.addAll(this.roles, parts);
}
}
- public String getToken() {
- return token;
+ public String getValue() {
+ return value;
}
- public void setToken(final String token) {
- this.token = token.trim();
+ public void setValue(final String token) {
+ this.value = token.trim();
}
}
\ No newline at end of file
diff --git a/src/main/java/ca/islandora/syn/token/InvalidTokenException.java b/src/main/java/ca/islandora/syn/token/InvalidTokenException.java
new file mode 100644
index 0000000..bdbde41
--- /dev/null
+++ b/src/main/java/ca/islandora/syn/token/InvalidTokenException.java
@@ -0,0 +1,36 @@
+package ca.islandora.syn.token;
+
+/**
+ *
+ * @author whikloj
+ *
+ */
+public class InvalidTokenException extends RuntimeException {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor
+ *
+ * @param message
+ * Exception message
+ */
+ public InvalidTokenException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param message
+ * Exception message
+ * @param e
+ * Wrapped Exception
+ */
+ public InvalidTokenException(final String message, final Throwable e) {
+ super(message, e);
+ }
+}
diff --git a/src/main/java/ca/islandora/syn/token/Verifier.java b/src/main/java/ca/islandora/syn/token/Verifier.java
index 97fe054..f4d5d5e 100644
--- a/src/main/java/ca/islandora/syn/token/Verifier.java
+++ b/src/main/java/ca/islandora/syn/token/Verifier.java
@@ -1,62 +1,60 @@
package ca.islandora.syn.token;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.Arrays;
import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
+import com.auth0.jwt.interfaces.Claim;
public class Verifier {
- private static final Log log = LogFactory.getLog(Verifier.class);
+ private static final Logger log = getLogger(Verifier.class);
private String token;
private JWT jwt;
- private Verifier() { }
+ private Verifier() {
+ }
public static Verifier create(final String token) {
final Verifier verifier = new Verifier();
+ final List requiredClaims = Arrays.asList("sub", "iss", "webid", "roles", "exp", "iat");
verifier.token = token;
try {
verifier.jwt = JWT.decode(token);
- if (verifier.jwt.getClaim("uid").isNull()) {
- return null;
- }
- if (verifier.jwt.getClaim("url").isNull()) {
- return null;
+ final Map claims = verifier.jwt.getClaims();
+ for (final String claim : requiredClaims) {
+ if (claims.get(claim) == null) {
+ log.info("Token missing required claim ({})", claim);
+ throw new InvalidTokenException(
+ String.format("Token missing required claim ({})", claim));
+ }
}
- if (verifier.jwt.getClaim("name").isNull()) {
- return null;
- }
- if (verifier.jwt.getClaim("roles").isNull()) {
- return null;
- }
- if (verifier.jwt.getExpiresAt() == null) {
- return null;
- }
- if (verifier.jwt.getIssuedAt() == null) {
- return null;
- }
- } catch (JWTDecodeException exception) {
+ } catch (final JWTDecodeException exception) {
log.error("Error decoding token: " + token, exception);
- return null;
+ throw new InvalidTokenException("Error decoding token: " + token, exception);
}
return verifier;
}
public int getUid() {
- return this.jwt.getClaim("uid").asInt();
+ return this.jwt.getClaim("webid").asInt();
}
public String getUrl() {
- return this.jwt.getClaim("url").asString();
+ return this.jwt.getClaim("iss").asString();
}
public String getName() {
- return this.jwt.getClaim("name").asString();
+ return this.jwt.getClaim("sub").asString();
}
public List getRoles() {
@@ -67,10 +65,11 @@ public boolean verify(final Algorithm algorithm) {
final JWTVerifier verifier = JWT.require(algorithm).build();
try {
verifier.verify(this.token);
- } catch (JWTVerificationException exception) {
+ } catch (final JWTVerificationException exception) {
return false;
}
return true;
}
+
}
diff --git a/src/main/java/ca/islandora/syn/valve/SynFilter.java b/src/main/java/ca/islandora/syn/valve/SynFilter.java
new file mode 100644
index 0000000..a5dd0d5
--- /dev/null
+++ b/src/main/java/ca/islandora/syn/valve/SynFilter.java
@@ -0,0 +1,242 @@
+package ca.islandora.syn.valve;
+
+import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
+import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+
+import com.auth0.jwt.algorithms.Algorithm;
+
+import ca.islandora.syn.settings.SettingsParser;
+import ca.islandora.syn.settings.Token;
+import ca.islandora.syn.token.InvalidTokenException;
+import ca.islandora.syn.token.Verifier;
+
+/**
+ * The JWT testing filter
+ *
+ * @author jonathangreen
+ * @author whikloj
+ *
+ */
+public class SynFilter implements Filter {
+
+ private static final Logger LOGGER = getLogger(SynFilter.class);
+
+ public static final String UNAUTHORIZED_MSG = "Token authentication failed.";
+ public static final String FORBIDDEN_MSG = "Token authentication failed.";
+
+ private static String settingsPath;
+ private static Map algorithmMap;
+ private static Map staticTokenMap;
+ private static Map anonymousMap;
+
+ /**
+ * Constructor
+ */
+ public SynFilter() {
+
+ }
+
+ @Override
+ public void init(final FilterConfig config) throws ServletException {
+ settingsPath = config.getInitParameter("settings-path");
+ if (settingsPath == null) {
+ throw new ServletException("settings-path init parameter must have location of syn-settings.yml file.");
+ }
+ // Validate the existence of our database file
+ final File file = new File(settingsPath);
+ if (!file.exists() || !file.canRead()) {
+ LOGGER.error("Unable to load Syn configuration from path: " + settingsPath);
+ throw new ServletException("Unable to load Syn configuration from path: " + settingsPath);
+ }
+
+ // Load the contents of the database file
+ try (final FileReader settings = new FileReader(file)) {
+ final SettingsParser parser = SettingsParser.create(settings);
+ algorithmMap = parser.getSiteAlgorithms();
+ staticTokenMap = parser.getSiteStaticTokens();
+ anonymousMap = parser.getSiteAllowAnonymous();
+ } catch (final Exception e) {
+ throw new ServletException("Error parsing Syn configuration", e);
+ }
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+ @Override
+ public void doFilter(final ServletRequest servRequest, final ServletResponse servResponse, final FilterChain chain)
+ throws IOException, ServletException {
+ final HttpServletRequest request = (HttpServletRequest) servRequest;
+ final HttpServletResponse response = (HttpServletResponse) servResponse;
+
+ String token = request.getHeader("Authorization");
+ if (token == null) {
+ LOGGER.info("Request did not contain any token.");
+ // Only check for anonymous access if there is no token.
+ if ((request.getMethod().equalsIgnoreCase("GET") ||
+ request.getMethod().equals("HEAD")) &&
+ allowGetRequests(buildUrlRegex(request))) {
+ chain.doFilter(setAnonymousRoles(request), response);
+ return;
+ }
+ response.sendError(SC_UNAUTHORIZED, UNAUTHORIZED_MSG);
+ return;
+ }
+
+ final String[] tokenParts = token.split(" ");
+ if (tokenParts.length != 2 || !tokenParts[0].equalsIgnoreCase("bearer")) {
+ LOGGER.info("Token was malformed. Token: " + token);
+ response.sendError(SC_UNAUTHORIZED, UNAUTHORIZED_MSG);
+ return;
+ }
+
+ // strip bearer off of the token
+ token = tokenParts[1];
+
+ // check if we have a static token that matches
+ if (staticTokenMap.containsKey(token)) {
+ LOGGER.info("Site verified using static token.");
+ chain.doFilter(setUserRolesFromStaticToken(request, staticTokenMap.get(token)), response);
+ return;
+ }
+
+ final Verifier verifier;
+ try {
+ verifier = Verifier.create(token);
+ } catch (final InvalidTokenException e) {
+ response.sendError(SC_UNAUTHORIZED, FORBIDDEN_MSG);
+ return;
+ }
+
+ final String url = verifier.getUrl();
+ Algorithm algorithm = null;
+ if (algorithmMap.containsKey(url)) {
+ algorithm = algorithmMap.get(url);
+ } else if (algorithmMap.containsKey(null)) {
+ algorithm = algorithmMap.get(null);
+ }
+
+ if (algorithm == null) {
+ LOGGER.info("No key found for site: " + url + ".");
+ response.sendError(SC_UNAUTHORIZED, UNAUTHORIZED_MSG);
+ return;
+ }
+
+ if (verifier.verify(algorithm)) {
+ LOGGER.info("Site verified: " + url);
+ chain.doFilter(setUserRolesFromToken(request, verifier), response);
+ return;
+ } else {
+ LOGGER.info("Token failed signature verification: " + url);
+ response.sendError(SC_FORBIDDEN, FORBIDDEN_MSG);
+ return;
+ }
+
+ }
+
+ /**
+ * Create fake principal with anonymous credentials.
+ *
+ * @param request
+ * The original incoming request.
+ * @return A wrapper with created credentials.
+ */
+ private HttpServletRequestWrapper setAnonymousRoles(final HttpServletRequest request) {
+ final String host = request.getScheme() + "://" + request.getServerName()
+ + (request.getServerPort() != 80 ? ":" + request.getServerPort() : "");
+ final List roles = Arrays.asList("anonymous", "islandora", host);
+ final String name = "anonymous";
+ return new SynRequestWrapper(name, roles, request);
+ }
+
+ /**
+ * Create fake principal with credentials from static token.
+ *
+ * @param request
+ * The original incoming request.
+ * @return A wrapper with created credentials.
+ */
+ private HttpServletRequestWrapper setUserRolesFromStaticToken(final HttpServletRequest request, final Token token) {
+ final List roles = token.getRoles();
+ roles.add("islandora");
+ final String name = token.getUser();
+ return new SynRequestWrapper(name, roles, request);
+ }
+
+ /**
+ * Create fake principal with credentials from JWT.
+ *
+ * @param request
+ * The original incoming request.
+ * @return A wrapper with created credentials.
+ */
+ private HttpServletRequestWrapper setUserRolesFromToken(final HttpServletRequest request, final Verifier verifier) {
+ final List roles = verifier.getRoles();
+ roles.add("islandora");
+ roles.add(verifier.getUrl());
+ final String name = verifier.getName();
+ return new SynRequestWrapper(name, roles, request);
+ }
+
+ /**
+ * Do the logic of allowing GET/HEAD requests.
+ *
+ * @param hostRegex
+ * A regular expression built of the hostname.
+ * @return whether to allow GET requests without authentication.
+ */
+ private boolean allowGetRequests(final String hostRegex) {
+ // If there is a matching site URI, return its value
+ for (final Entry site : anonymousMap.entrySet()) {
+ if (Pattern.matches(hostRegex, site.getKey())) {
+ return site.getValue();
+ }
+ }
+ // Else if there is a default, return its value.
+ if (anonymousMap.containsKey("default")) {
+ LOGGER.debug(
+ String.format(
+ "Using default anonymous ({}) for GET/HEAD requests",
+ anonymousMap.get("default")));
+ return anonymousMap.get("default");
+ }
+ // Else disallow anonymous.
+ return false;
+ }
+
+ /**
+ * Build a regular expression from the current scheme, hostname and port for
+ * matching against the site urls.
+ *
+ * @param request
+ * The current request.
+ * @return The regular expression.
+ */
+ private static String buildUrlRegex(final HttpServletRequest request) {
+ return "^" + request.getScheme() + "://" + Pattern.quote(request.getServerName()) +
+ "(?::" + request.getServerPort() + ")" + (request.getServerPort() == 80 ? "?" : "") + "/?$";
+ }
+}
diff --git a/src/main/java/ca/islandora/syn/valve/SynRequestWrapper.java b/src/main/java/ca/islandora/syn/valve/SynRequestWrapper.java
new file mode 100644
index 0000000..2fbf16b
--- /dev/null
+++ b/src/main/java/ca/islandora/syn/valve/SynRequestWrapper.java
@@ -0,0 +1,52 @@
+package ca.islandora.syn.valve;
+
+import java.security.Principal;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+/**
+ * Request wrapper to use JWT provided name and roles.
+ *
+ * @author whikloj
+ * @since 2018-01-16
+ */
+public class SynRequestWrapper extends HttpServletRequestWrapper {
+ private final String user;
+ private List roles;
+ private final HttpServletRequest realRequest;
+
+ /**
+ * Constructor
+ *
+ * @param user
+ * The username for the Principal.
+ * @param roles
+ * List of roles to include the Principal in.
+ * @param request
+ * The original request.
+ */
+ public SynRequestWrapper(final String user, final List roles, final HttpServletRequest request) {
+ super(request);
+ this.user = user;
+ this.realRequest = request;
+ this.roles = roles;
+ }
+
+ @Override
+ public boolean isUserInRole(final String role) {
+ return roles.contains(role);
+ }
+
+ @Override
+ public Principal getUserPrincipal() {
+ // make an anonymous implementation to just return our user
+ return new Principal() {
+ @Override
+ public String getName() {
+ return user;
+ }
+ };
+ }
+}
diff --git a/src/main/java/ca/islandora/syn/valve/SynValve.java b/src/main/java/ca/islandora/syn/valve/SynValve.java
deleted file mode 100644
index 6d0158a..0000000
--- a/src/main/java/ca/islandora/syn/valve/SynValve.java
+++ /dev/null
@@ -1,213 +0,0 @@
-package ca.islandora.syn.valve;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.catalina.LifecycleException;
-import org.apache.catalina.connector.Request;
-import org.apache.catalina.connector.Response;
-import org.apache.catalina.realm.GenericPrincipal;
-import org.apache.catalina.valves.ValveBase;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
-
-import com.auth0.jwt.algorithms.Algorithm;
-
-import ca.islandora.syn.settings.SettingsParser;
-import ca.islandora.syn.settings.Token;
-import ca.islandora.syn.token.Verifier;
-
-public class SynValve extends ValveBase {
-
- private String pathname = "conf/syn-settings.xml";
- private static final Log log = LogFactory.getLog(SynValve.class);
- private Map algorithmMap = null;
- private Map staticTokenMap = null;
-
- private Map anonymousGetMap = null;
-
- @Override
- public void invoke(final Request request, final Response response)
- throws IOException, ServletException {
-
- final SecurityConstraint[] constraints = this.container.getRealm()
- .findSecurityConstraints(request, request.getContext());
-
- if ((constraints == null
- && !request.getContext().getPreemptiveAuthentication())
- || !hasAuthConstraint(constraints)) {
- this.getNext().invoke(request, response);
- } else {
- handleAuthentication(request, response);
- }
- }
-
- private boolean hasAuthConstraint(final SecurityConstraint[] constraints) {
- boolean authConstraint = true;
- for (SecurityConstraint securityConstraint : constraints) {
- authConstraint &= securityConstraint.getAuthConstraint();
- }
- return authConstraint;
- }
-
- private boolean doAuthentication(final Request request) {
- String token = request.getHeader("Authorization");
- if (token == null) {
- log.info("Request did not contain any token.");
- return false;
- }
-
- final String[] tokenParts = token.split(" ");
- if (tokenParts.length != 2 || !tokenParts[0].equalsIgnoreCase("bearer")) {
- log.info("Token was malformed. Token: " + token);
- return false;
- }
-
- // strip bearer off of the token
- token = tokenParts[1];
-
- // check if we have a static token that matches
- if (this.staticTokenMap.containsKey(token)) {
- log.info("Site verified using static token.");
- setUserRolesFromStaticToken(request, this.staticTokenMap.get(token));
- request.setAuthType("SYN");
- return true;
- }
-
- final Verifier verifier = Verifier.create(token);
- if (verifier == null) {
- log.info("Token rejected for not containing correct claims.");
- return false;
- }
-
- final String url = verifier.getUrl();
- Algorithm algorithm = null;
- if (algorithmMap.containsKey(url)) {
- algorithm = algorithmMap.get(url);
- } else if (algorithmMap.containsKey(null)) {
- algorithm = algorithmMap.get(null);
- }
-
- if (algorithm == null) {
- log.info("No key found for site: " + url + ".");
- return false;
- }
-
- if (verifier.verify(algorithm)) {
- log.info("Site verified: " + url);
- setUserRolesFromToken(request, verifier);
- request.setAuthType("SYN");
- return true;
- } else {
- log.info("Token failed signature verification: " + url);
- return false;
- }
- }
-
- private void setAnonymousRoles(final Request request) {
- final List roles = new ArrayList();
- roles.add("anonymous");
- roles.add("islandora");
- final String name = "anonymous";
- final GenericPrincipal principal = new GenericPrincipal(name, null, roles);
- request.setUserPrincipal(principal);
- }
-
- private void setUserRolesFromStaticToken(final Request request, final Token token) {
- final List roles = token.getRoles();
- roles.add("islandora");
- final String name = token.getUser();
- final GenericPrincipal principal = new GenericPrincipal(name, null, roles);
- request.setUserPrincipal(principal);
- }
-
- private void setUserRolesFromToken(final Request request, final Verifier verifier) {
- final List roles = verifier.getRoles();
- roles.add("islandora");
- roles.add(verifier.getUrl());
- final String name = verifier.getName();
- final GenericPrincipal principal = new GenericPrincipal(name, null, roles);
- request.setUserPrincipal(principal);
- }
-
- private void handleAuthentication(final Request request, final Response response)
- throws IOException, ServletException {
- if ((request.getMethod().equalsIgnoreCase("GET") ||
- request.getMethod().equals("HEAD")) &&
- allowGetRequests(request.getHost().toString())) {
- // Skip authentication
- setAnonymousRoles(request);
- this.getNext().invoke(request, response);
- } else if (doAuthentication(request)) {
- this.getNext().invoke(request, response);
- } else {
- response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token authentication failed.");
- }
- }
-
- /**
- * Do the logic of allowing GET/HEAD requests.
- *
- * @param requestURI the site being requested
- * @return whether to allow GET requests without authentication.
- */
- private boolean allowGetRequests(final String requestURI) {
- // If there is a matching site URI, return its value
- if (anonymousGetMap.containsKey(requestURI)) {
- log.debug(
- String.format(
- "Using site anonymous ({}) for GET/HEAD requests, site {}",
- anonymousGetMap.get(requestURI),
- requestURI));
- return anonymousGetMap.get(requestURI);
- // Else if there is a default, return its value.
- } else if (anonymousGetMap.containsKey("default")) {
- log.debug(
- String.format(
- "Using default anonymous ({}) for GET/HEAD requests, host {}",
- anonymousGetMap.get("default"),
- requestURI));
- return anonymousGetMap.get("default");
- }
- // Else disallow anonymous.
- return false;
- }
-
- public String getPathname() {
- return pathname;
- }
- public void setPathname(final String pathname) {
- this.pathname = pathname;
- }
-
- @Override
- public synchronized void startInternal() throws LifecycleException {
- // Perform normal superclass initialization
- super.startInternal();
- // Validate the existence of our database file
- File file = new File(pathname);
- if (!file.isAbsolute()) {
- file = new File(System.getProperty("catalina.base"), pathname);
- }
- if (!file.exists() || !file.canRead()) {
- throw new LifecycleException("Unable to load XML Configuration from Path: " + pathname);
- }
-
- // Load the contents of the database file
- try {
- this.algorithmMap = SettingsParser.getSiteAlgorithms(new FileInputStream(file));
- this.staticTokenMap = SettingsParser.getSiteStaticTokens(new FileInputStream(file));
- this.anonymousGetMap = SettingsParser.getSiteAllowAnonymous(new FileInputStream(file));
- } catch (Exception e) {
- throw new LifecycleException("Error parsing XML Configuration", e);
- }
- }
-}
diff --git a/src/test/java/ca/islandora/syn/settings/SettingsParserAlgorithmsTest.java b/src/test/java/ca/islandora/syn/settings/SettingsParserAlgorithmsTest.java
index d33d112..a6f37e5 100644
--- a/src/test/java/ca/islandora/syn/settings/SettingsParserAlgorithmsTest.java
+++ b/src/test/java/ca/islandora/syn/settings/SettingsParserAlgorithmsTest.java
@@ -1,10 +1,10 @@
package ca.islandora.syn.settings;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
-import java.io.ByteArrayInputStream;
import java.io.File;
-import java.io.InputStream;
+import java.io.StringReader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Map;
@@ -20,19 +20,21 @@ public class SettingsParserAlgorithmsTest {
public TemporaryFolder temporaryFolder = new TemporaryFolder();
private void testOneSiteHmacInlineKey(final String algorithm) throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , " test data"
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map algorithms = SettingsParser.getSiteAlgorithms(stream);
- assertEquals(1, algorithms.size());
- assertEquals(true, algorithms.containsKey("http://test.com"));
- assertEquals(algorithm, algorithms.get("http://test.com").getName());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: " + algorithm,
+ " encoding: plain",
+ " key: test data");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map algorithms = SettingsParser.create(stream).getSiteAlgorithms();
+ assertEquals(1, algorithms.size());
+ assertEquals(true, algorithms.containsKey("http://test.com"));
+ assertEquals(algorithm, algorithms.get("http://test.com").getName());
+ }
}
@Test
@@ -43,80 +45,109 @@ public void testOneSiteAllHmacInlineKey() throws Exception {
}
@Test
+ public void testUnsupportedAlgorithm() throws Exception {
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: AES128",
+ " encoding: plain",
+ " key: test data");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map algorithms = SettingsParser.create(stream).getSiteAlgorithms();
+ assertEquals(0, algorithms.size());
+ assertFalse(algorithms.containsKey("http://test.com"));
+ }
+ }
+
+ @Test(expected = SettingsParserException.class)
public void testInvalidSitesVersion() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , " test data"
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map algorithms = SettingsParser.getSiteAlgorithms(stream);
- assertEquals(0, algorithms.size());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 2",
+ "site:",
+ " url: http://test.com",
+ " algorithm: HS384",
+ " encoding: plain",
+ " key: test data");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ SettingsParser.create(stream).getSiteAlgorithms();
+ }
}
@Test
public void testOneSiteHmacBase64() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , " am9uYXRoYW4gaXMgYXdlc29tZQ=="
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map algorithms = SettingsParser.getSiteAlgorithms(stream);
- assertEquals(1, algorithms.size());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: HS256",
+ " encoding: base64",
+ " key: am9uYXRoYW4gaXMgYXdlc29tZQ==");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map algorithms = SettingsParser.create(stream).getSiteAlgorithms();
+ assertEquals(1, algorithms.size());
+ }
}
@Test
public void testOneSiteHmacInvalidBase64() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , " this is invalid base64"
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map algorithms = SettingsParser.getSiteAlgorithms(stream);
- assertEquals(0, algorithms.size());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: HS256",
+ " encoding: base64",
+ " key: this is invalid base64");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map algorithms = SettingsParser.create(stream).getSiteAlgorithms();
+ assertEquals(0, algorithms.size());
+ }
}
@Test
public void testOneSiteHmacInvalidEncoding() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , " this is invalid base64"
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map algorithms = SettingsParser.getSiteAlgorithms(stream);
- assertEquals(0, algorithms.size());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: HS256",
+ " encoding: badalgorithm",
+ " key: this is invalid base64");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map algorithms = SettingsParser.create(stream).getSiteAlgorithms();
+ assertEquals(0, algorithms.size());
+ }
}
private void testOneSiteHmacFileKey(final String algorithm) throws Exception {
final File key = temporaryFolder.newFile();
final String path = key.getAbsolutePath();
- final String testXml = String.join("\n"
- , ""
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map algorithms = SettingsParser.getSiteAlgorithms(stream);
- assertEquals(1, algorithms.size());
- assertEquals(true, algorithms.containsKey("http://test.com"));
- assertEquals(algorithm, algorithms.get("http://test.com").getName());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: " + algorithm,
+ " encoding: plain",
+ " path: " + path);
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map algorithms = SettingsParser.create(stream).getSiteAlgorithms();
+ assertEquals(1, algorithms.size());
+ assertEquals(true, algorithms.containsKey("http://test.com"));
+ assertEquals(algorithm, algorithms.get("http://test.com").getName());
+ }
}
@Test
@@ -128,94 +159,110 @@ public void testOneSiteAllHmacFileKey() throws Exception {
@Test
public void testSiteBothInlineAndPath() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , " test data"
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map algorithms = SettingsParser.getSiteAlgorithms(stream);
- assertEquals(0, algorithms.size());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: HS384",
+ " encoding: plain",
+ " path: foo",
+ " key: test data");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map algorithms = SettingsParser.create(stream).getSiteAlgorithms();
+ assertEquals(0, algorithms.size());
+ }
}
@Test
public void testSiteNeitherInlineAndPath() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map algorithms = SettingsParser.getSiteAlgorithms(stream);
- assertEquals(0, algorithms.size());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: HS384",
+ " encoding: plain");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map algorithms = SettingsParser.create(stream).getSiteAlgorithms();
+ assertEquals(0, algorithms.size());
+ }
}
@Test
public void testSiteInvalidPath() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map algorithms = SettingsParser.getSiteAlgorithms(stream);
- assertEquals(0, algorithms.size());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: HS384",
+ " encoding: plain",
+ " path: foo");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map algorithms = SettingsParser.create(stream).getSiteAlgorithms();
+ assertEquals(0, algorithms.size());
+ }
}
@Test
public void testSiteNoUrlDefault() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , " test data"
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map algorithms = SettingsParser.getSiteAlgorithms(stream);
- assertEquals(1, algorithms.size());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " algorithm: HS256",
+ " encoding: plain",
+ " default: true",
+ " key: test data");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map algorithms = SettingsParser.create(stream).getSiteAlgorithms();
+ assertEquals(1, algorithms.size());
+ }
}
@Test
public void testSiteNoUrl() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , " test data"
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map algorithms = SettingsParser.getSiteAlgorithms(stream);
- assertEquals(0, algorithms.size());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " algorithm: HS256",
+ " encoding: plain",
+ " key: test data");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map algorithms = SettingsParser.create(stream).getSiteAlgorithms();
+ assertEquals(0, algorithms.size());
+ }
}
private void testOneSiteRsaInlineKey(final String algorithm) throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , "-----BEGIN PUBLIC KEY-----"
- , "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEVO4MNlZG+iGYhoJd/cBpfMd9"
- , "YnKsntF+zhQs8lCbBabgY8kNoXVIEeOm4WPJ+W53gLDAIg6BNrZqxk9z1TLD6Dmz"
- , "t176OLYkNoTI9LNf6z4wuBenrlQ/H5UnYl6h5QoOdVpNAgEjkDcdTSOE1lqFLIle"
- , "KOT4nEF7MBGyOSP3KQIDAQAB"
- , "-----END PUBLIC KEY-----"
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map algorithms = SettingsParser.getSiteAlgorithms(stream);
- assertEquals(1, algorithms.size());
- assertEquals(true, algorithms.containsKey("http://test.com"));
- assertEquals(algorithm, algorithms.get("http://test.com").getName());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: " + algorithm,
+ " encoding: PEM",
+ " key: |",
+ " -----BEGIN PUBLIC KEY-----",
+ " MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEVO4MNlZG+iGYhoJd/cBpfMd9",
+ " YnKsntF+zhQs8lCbBabgY8kNoXVIEeOm4WPJ+W53gLDAIg6BNrZqxk9z1TLD6Dmz",
+ " t176OLYkNoTI9LNf6z4wuBenrlQ/H5UnYl6h5QoOdVpNAgEjkDcdTSOE1lqFLIle",
+ " KOT4nEF7MBGyOSP3KQIDAQAB",
+ " -----END PUBLIC KEY-----");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map algorithms = SettingsParser.create(stream).getSiteAlgorithms();
+ assertEquals(1, algorithms.size());
+ assertEquals(true, algorithms.containsKey("http://test.com"));
+ assertEquals(algorithm, algorithms.get("http://test.com").getName());
+ }
}
@Test
@@ -229,28 +276,29 @@ private void testOneSiteRsaFileKey(final String algorithm) throws Exception {
final File keyFile = temporaryFolder.newFile();
final String path = keyFile.getAbsolutePath();
- final String pemPublicKey = String.join("\n"
- , "-----BEGIN PUBLIC KEY-----"
- , "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEVO4MNlZG+iGYhoJd/cBpfMd9"
- , "YnKsntF+zhQs8lCbBabgY8kNoXVIEeOm4WPJ+W53gLDAIg6BNrZqxk9z1TLD6Dmz"
- , "t176OLYkNoTI9LNf6z4wuBenrlQ/H5UnYl6h5QoOdVpNAgEjkDcdTSOE1lqFLIle"
- , "KOT4nEF7MBGyOSP3KQIDAQAB"
- , "-----END PUBLIC KEY-----"
- );
+ final String pemPublicKey = String.join("\n", "-----BEGIN PUBLIC KEY-----",
+ "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEVO4MNlZG+iGYhoJd/cBpfMd9",
+ "YnKsntF+zhQs8lCbBabgY8kNoXVIEeOm4WPJ+W53gLDAIg6BNrZqxk9z1TLD6Dmz",
+ "t176OLYkNoTI9LNf6z4wuBenrlQ/H5UnYl6h5QoOdVpNAgEjkDcdTSOE1lqFLIle", "KOT4nEF7MBGyOSP3KQIDAQAB",
+ "-----END PUBLIC KEY-----");
Files.write(Paths.get(path), pemPublicKey.getBytes());
- final String testXml = String.join("\n"
- , ""
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map algorithms = SettingsParser.getSiteAlgorithms(stream);
- assertEquals(1, algorithms.size());
- assertEquals(true, algorithms.containsKey("http://test.com"));
- assertEquals(algorithm, algorithms.get("http://test.com").getName());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: " + algorithm,
+ " encoding: PEM",
+ " path: " + path);
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map algorithms = SettingsParser.create(stream).getSiteAlgorithms();
+ assertEquals(1, algorithms.size());
+ assertEquals(true, algorithms.containsKey("http://test.com"));
+ assertEquals(algorithm, algorithms.get("http://test.com").getName());
+ }
}
@Test
@@ -262,45 +310,52 @@ public void testOneSiteAllRsaFileKey() throws Exception {
@Test
public void testOneSiteAllRsaInvalidEncoding() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , "-----BEGIN PUBLIC KEY-----"
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map algorithms = SettingsParser.getSiteAlgorithms(stream);
- assertEquals(0, algorithms.size());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: RS256",
+ " encoding: PEM",
+ " key: |",
+ " -----BEGIN PUBLIC KEY-----");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map algorithms = SettingsParser.create(stream).getSiteAlgorithms();
+ assertEquals(0, algorithms.size());
+ }
}
-
@Test
public void testMultipleDefaults() throws Exception {
final File keyFile = temporaryFolder.newFile();
final String path = keyFile.getAbsolutePath();
- final String pemPublicKey = String.join("\n"
- , "-----BEGIN PUBLIC KEY-----"
- , "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEVO4MNlZG+iGYhoJd/cBpfMd9"
- , "YnKsntF+zhQs8lCbBabgY8kNoXVIEeOm4WPJ+W53gLDAIg6BNrZqxk9z1TLD6Dmz"
- , "t176OLYkNoTI9LNf6z4wuBenrlQ/H5UnYl6h5QoOdVpNAgEjkDcdTSOE1lqFLIle"
- , "KOT4nEF7MBGyOSP3KQIDAQAB"
- , "-----END PUBLIC KEY-----"
- );
+ final String pemPublicKey = String.join("\n", "-----BEGIN PUBLIC KEY-----",
+ "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEVO4MNlZG+iGYhoJd/cBpfMd9",
+ "YnKsntF+zhQs8lCbBabgY8kNoXVIEeOm4WPJ+W53gLDAIg6BNrZqxk9z1TLD6Dmz",
+ "t176OLYkNoTI9LNf6z4wuBenrlQ/H5UnYl6h5QoOdVpNAgEjkDcdTSOE1lqFLIle", "KOT4nEF7MBGyOSP3KQIDAQAB",
+ "-----END PUBLIC KEY-----");
Files.write(Paths.get(path), pemPublicKey.getBytes());
- final String testXml = String.join("\n"
- , ""
- , " "
- , " "
- , ""
- );
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map algorithms = SettingsParser.getSiteAlgorithms(stream);
- assertEquals(1, algorithms.size());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " algorithm: RS384",
+ " path: " + path,
+ " encoding: PEM",
+ " default: true",
+ "site:",
+ " algorithm: HS256",
+ " path: " + path,
+ " encoding: plain",
+ " default: true");
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map algorithms = SettingsParser.create(stream).getSiteAlgorithms();
+ assertEquals(1, algorithms.size());
+ }
}
@Test
@@ -308,24 +363,25 @@ public void testInvalidAlgorithm() throws Exception {
final File keyFile = temporaryFolder.newFile();
final String path = keyFile.getAbsolutePath();
- final String pemPublicKey = String.join("\n"
- , "-----BEGIN PUBLIC KEY-----"
- , "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEVO4MNlZG+iGYhoJd/cBpfMd9"
- , "YnKsntF+zhQs8lCbBabgY8kNoXVIEeOm4WPJ+W53gLDAIg6BNrZqxk9z1TLD6Dmz"
- , "t176OLYkNoTI9LNf6z4wuBenrlQ/H5UnYl6h5QoOdVpNAgEjkDcdTSOE1lqFLIle"
- , "KOT4nEF7MBGyOSP3KQIDAQAB"
- , "-----END PUBLIC KEY-----"
- );
+ final String pemPublicKey = String.join("\n", "-----BEGIN PUBLIC KEY-----",
+ "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEVO4MNlZG+iGYhoJd/cBpfMd9",
+ "YnKsntF+zhQs8lCbBabgY8kNoXVIEeOm4WPJ+W53gLDAIg6BNrZqxk9z1TLD6Dmz",
+ "t176OLYkNoTI9LNf6z4wuBenrlQ/H5UnYl6h5QoOdVpNAgEjkDcdTSOE1lqFLIle", "KOT4nEF7MBGyOSP3KQIDAQAB",
+ "-----END PUBLIC KEY-----");
Files.write(Paths.get(path), pemPublicKey.getBytes());
- final String testXml = String.join("\n"
- , ""
- , " "
- , ""
- );
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map algorithms = SettingsParser.getSiteAlgorithms(stream);
- assertEquals(0, algorithms.size());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " algorithm: RSA384",
+ " path: " + path,
+ " encoding: PEM",
+ " default: true");
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map algorithms = SettingsParser.create(stream).getSiteAlgorithms();
+ assertEquals(0, algorithms.size());
+ }
}
}
diff --git a/src/test/java/ca/islandora/syn/settings/SettingsParserAnonymousTest.java b/src/test/java/ca/islandora/syn/settings/SettingsParserAnonymousTest.java
index 9c7d321..917beb0 100644
--- a/src/test/java/ca/islandora/syn/settings/SettingsParserAnonymousTest.java
+++ b/src/test/java/ca/islandora/syn/settings/SettingsParserAnonymousTest.java
@@ -2,8 +2,7 @@
import static org.junit.Assert.assertEquals;
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
+import java.io.StringReader;
import java.util.Map;
import org.junit.Test;
@@ -12,65 +11,84 @@ public class SettingsParserAnonymousTest {
@Test
public void testSiteAnonymousOn() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , " test data"
- , " "
- , ""
- );
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: RS256",
+ " encoding: plain",
+ " anonymous: true",
+ " key: test data");
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map anonymous = SettingsParser.getSiteAllowAnonymous(stream);
- assertEquals(1, anonymous.size());
- assertEquals(true, anonymous.containsKey("http://test.com"));
- assertEquals(true, anonymous.get("http://test.com"));
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map anonymous = SettingsParser.create(stream).getSiteAllowAnonymous();
+ assertEquals(1, anonymous.size());
+ assertEquals(true, anonymous.containsKey("http://test.com"));
+ assertEquals(true, anonymous.get("http://test.com"));
+ }
}
@Test
public void testSiteMultipleAnonymousTest() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , " test data"
- , " "
- , " "
- , " test data"
- , " "
- , ""
- );
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: RS256",
+ " encoding: plain",
+ " anonymous: true",
+ " key: test data",
+ "site:",
+ " url: http://test2.com",
+ " algorithm: RS256",
+ " encoding: plain",
+ " anonymous: false",
+ " key: test data");
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map anonymous = SettingsParser.getSiteAllowAnonymous(stream);
- assertEquals(2, anonymous.size());
- assertEquals(true, anonymous.containsKey("http://test.com"));
- assertEquals(true, anonymous.get("http://test.com"));
- assertEquals(true, anonymous.containsKey("http://test2.com"));
- assertEquals(false, anonymous.get("http://test2.com"));
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map anonymous = SettingsParser.create(stream).getSiteAllowAnonymous();
+ assertEquals(2, anonymous.size());
+ assertEquals(true, anonymous.containsKey("http://test.com"));
+ assertEquals(true, anonymous.get("http://test.com"));
+ assertEquals(true, anonymous.containsKey("http://test2.com"));
+ assertEquals(false, anonymous.get("http://test2.com"));
+ }
}
@Test
public void testDefaultMultipleAnonymousTest() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , " test data"
- , " "
- , " "
- , " test data"
- , " "
- , " "
- , ""
- );
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: RS256",
+ " encoding: plain",
+ " anonymous: true",
+ " key: test data",
+ "site:",
+ " url: http://test2.com",
+ " algorithm: RS256",
+ " encoding: plain",
+ " anonymous: false",
+ " key: test data",
+ "site:",
+ " algorithm: RS256",
+ " encoding: plain",
+ " anonymous: true",
+ " default: true");
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map anonymous = SettingsParser.getSiteAllowAnonymous(stream);
- assertEquals(3, anonymous.size());
- assertEquals(true, anonymous.containsKey("http://test.com"));
- assertEquals(true, anonymous.get("http://test.com"));
- assertEquals(true, anonymous.containsKey("http://test2.com"));
- assertEquals(false, anonymous.get("http://test2.com"));
- assertEquals(true, anonymous.containsKey("default"));
- assertEquals(true, anonymous.get("default"));
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map anonymous = SettingsParser.create(stream).getSiteAllowAnonymous();
+ assertEquals(3, anonymous.size());
+ assertEquals(true, anonymous.containsKey("http://test.com"));
+ assertEquals(true, anonymous.get("http://test.com"));
+ assertEquals(true, anonymous.containsKey("http://test2.com"));
+ assertEquals(false, anonymous.get("http://test2.com"));
+ assertEquals(true, anonymous.containsKey("default"));
+ assertEquals(true, anonymous.get("default"));
+ }
}
}
diff --git a/src/test/java/ca/islandora/syn/settings/SettingsParserDigestTest.java b/src/test/java/ca/islandora/syn/settings/SettingsParserDigestTest.java
index 77daec8..cfc3486 100644
--- a/src/test/java/ca/islandora/syn/settings/SettingsParserDigestTest.java
+++ b/src/test/java/ca/islandora/syn/settings/SettingsParserDigestTest.java
@@ -5,8 +5,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
+import java.io.StringReader;
import org.junit.Test;
@@ -14,127 +13,141 @@ public class SettingsParserDigestTest {
@Test
public void testOneSitePath() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Config settings = SettingsParser.getSitesObject(stream);
- assertEquals(12, settings.getVersion());
- assertEquals(1, settings.getSites().size());
-
- final Site site = settings.getSites().get(0);
- assertEquals("RS384", site.getAlgorithm());
- assertEquals("http://test.com", site.getUrl());
- assertEquals("test/path.key", site.getPath());
- assertEquals("PEM", site.getEncoding());
- assertEquals("", site.getKey());
- assertFalse(site.getDefault());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: RS384",
+ " path: test/path.key",
+ " encoding: PEM");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Config settings = SettingsParser.create(stream).getConfig();
+ assertEquals(1, settings.getVersion());
+ assertEquals(1, settings.getSites().size());
+
+ final Site site = settings.getSites().get(0);
+ assertEquals("RS384", site.getAlgorithm());
+ assertEquals("http://test.com", site.getUrl());
+ assertEquals("test/path.key", site.getPath());
+ assertEquals("PEM", site.getEncoding());
+ assertNull(site.getKey());
+ assertFalse(site.getDefault());
+ }
}
@Test
public void testOneSiteKey() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , "multiline"
- , "key"
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Config settings = SettingsParser.getSitesObject(stream);
- assertEquals(-1, settings.getVersion());
- assertEquals(1, settings.getSites().size());
-
- final Site site = settings.getSites().get(0);
- assertEquals("RS384", site.getAlgorithm());
- assertEquals("http://test.com", site.getUrl());
- assertNull(site.getPath());
- assertEquals("PEM", site.getEncoding());
- assertEquals("multiline\nkey", site.getKey());
- assertTrue(site.getDefault());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: RS384",
+ " encoding: PEM",
+ " default: true",
+ " key: |",
+ " multiline",
+ " key");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Config settings = SettingsParser.create(stream).getConfig();
+ assertEquals(1, settings.getVersion());
+ assertEquals(1, settings.getSites().size());
+
+ final Site site = settings.getSites().get(0);
+ assertEquals("RS384", site.getAlgorithm());
+ assertEquals("http://test.com", site.getUrl());
+ assertNull(site.getPath());
+ assertEquals("PEM", site.getEncoding());
+ assertEquals("multiline\nkey", site.getKey());
+ assertTrue(site.getDefault());
+ }
}
@Test
public void testTwoSites() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Config settings = SettingsParser.getSitesObject(stream);
- assertEquals(2, settings.getSites().size());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ "site:");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Config settings = SettingsParser.create(stream).getConfig();
+ assertEquals(2, settings.getSites().size());
+ }
}
- @Test
+ @Test(expected = SettingsParserException.class)
public void testOneSiteUnexpectedAttribute() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Config settings = SettingsParser.getSitesObject(stream);
- assertEquals(1, settings.getSites().size());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " unexpected: woh");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Config settings = SettingsParser.create(stream).getConfig();
+ assertEquals(1, settings.getSites().size());
+ }
}
- @Test
- public void testOneSiteUnexpectedTag() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , ""
- );
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Config settings = SettingsParser.getSitesObject(stream);
- assertEquals(0, settings.getSites().size())
-; }
-
@Test
public void testValidAnonymousTrue() throws Exception {
- final String testXml = "\n" +
- " \n" +
- "";
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Config settings = SettingsParser.getSitesObject(stream);
- final Site sites = settings.getSites().get(0);
- assertTrue("Did not set anonymous property", sites.getAnonymous());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: RS384",
+ " encoding: PEM",
+ " default: true",
+ " anonymous: true");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Config settings = SettingsParser.create(stream).getConfig();
+ final Site sites = settings.getSites().get(0);
+ assertTrue("Did not set anonymous property", sites.getAnonymous());
+ }
}
@Test
public void testValidAnonymousFalse() throws Exception {
- final String testXml = "\n" +
- " \n" +
- "";
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Config settings = SettingsParser.getSitesObject(stream);
- final Site sites = settings.getSites().get(0);
- assertFalse("Did not set anonymous property", sites.getAnonymous());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: RS384",
+ " encoding: PEM",
+ " default: true",
+ " anonymous: false");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Config settings = SettingsParser.create(stream).getConfig();
+ final Site sites = settings.getSites().get(0);
+ assertFalse("Did not set anonymous property", sites.getAnonymous());
+ }
}
- @Test
+ @Test(expected = SettingsParserException.class)
public void testInvalidAnonymous() throws Exception {
- final String testXml = "\n" +
- " \n" +
- "";
-
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Config settings = SettingsParser.getSitesObject(stream);
- final Site sites = settings.getSites().get(0);
- assertFalse("Did not set anonymous property", sites.getAnonymous());
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: RS384",
+ " encoding: PEM",
+ " default: true",
+ " anonymous: whatever");
+
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Config settings = SettingsParser.create(stream).getConfig();
+ final Site sites = settings.getSites().get(0);
+ assertFalse("Did not set anonymous property", sites.getAnonymous());
+ }
}
}
diff --git a/src/test/java/ca/islandora/syn/settings/SettingsParserTokenTest.java b/src/test/java/ca/islandora/syn/settings/SettingsParserTokenTest.java
index 85f1513..3c75e6a 100644
--- a/src/test/java/ca/islandora/syn/settings/SettingsParserTokenTest.java
+++ b/src/test/java/ca/islandora/syn/settings/SettingsParserTokenTest.java
@@ -1,82 +1,79 @@
package ca.islandora.syn.settings;
-import org.junit.Test;
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.util.Map;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import java.io.StringReader;
+import java.util.Map;
+
+import org.junit.Test;
+
public class SettingsParserTokenTest {
- @Test
+ @Test(expected = SettingsParserException.class)
public void testInvalidVersion() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , " c00lpazzward"
- , " "
- , ""
- );
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 2",
+ "token:",
+ " value: c00lpazzward");
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map tokens = SettingsParser.getSiteStaticTokens(stream);
- assertEquals(0, tokens.size());
+ try (final StringReader stream = new StringReader(testYaml)) {
+ SettingsParser.create(stream).getSiteStaticTokens();
+ }
}
@Test
public void testTokenNoParams() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , " c00lpazzward"
- , " "
- , ""
- );
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "token:",
+ " value: c00lpazzward");
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map tokens = SettingsParser.getSiteStaticTokens(stream);
- final Token token = tokens.get("c00lpazzward");
- assertEquals(1, tokens.size());
- assertEquals("c00lpazzward", token.getToken());
- assertEquals("islandoraAdmin", token.getUser());
- assertEquals(0, token.getRoles().size());
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map tokens = SettingsParser.create(stream).getSiteStaticTokens();
+ final Token token = tokens.get("c00lpazzward");
+ assertEquals(1, tokens.size());
+ assertEquals("c00lpazzward", token.getValue());
+ assertEquals("islandoraAdmin", token.getUser());
+ assertEquals(0, token.getRoles().size());
+ }
}
@Test
public void testTokenUser() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , " c00lpazzward"
- , " "
- , ""
- );
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "token:",
+ " user: denis",
+ " value: c00lpazzward");
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map tokens = SettingsParser.getSiteStaticTokens(stream);
- final Token token = tokens.get("c00lpazzward");
- assertEquals(1, tokens.size());
- assertEquals("denis", token.getUser());
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map tokens = SettingsParser.create(stream).getSiteStaticTokens();
+ final Token token = tokens.get("c00lpazzward");
+ assertEquals(1, tokens.size());
+ assertEquals("denis", token.getUser());
+ }
}
@Test
public void testTokenRole() throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , " c00lpazzward"
- , " "
- , ""
- );
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "token:",
+ " roles: role1,role2,role3",
+ " value: c00lpazzward");
- final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
- final Map tokens = SettingsParser.getSiteStaticTokens(stream);
- final Token token = tokens.get("c00lpazzward");
- assertEquals(1, tokens.size());
- assertEquals(3, token.getRoles().size());
- assertTrue(token.getRoles().contains("role1"));
- assertTrue(token.getRoles().contains("role2"));
- assertTrue(token.getRoles().contains("role3"));
+ try (final StringReader stream = new StringReader(testYaml)) {
+ final Map tokens = SettingsParser.create(stream).getSiteStaticTokens();
+ final Token token = tokens.get("c00lpazzward");
+ assertEquals(1, tokens.size());
+ assertEquals(3, token.getRoles().size());
+ assertTrue(token.getRoles().contains("role1"));
+ assertTrue(token.getRoles().contains("role2"));
+ assertTrue(token.getRoles().contains("role3"));
+ }
}
}
diff --git a/src/test/java/ca/islandora/syn/settings/SitesTest.java b/src/test/java/ca/islandora/syn/settings/SitesTest.java
index 22ca6da..e102e06 100644
--- a/src/test/java/ca/islandora/syn/settings/SitesTest.java
+++ b/src/test/java/ca/islandora/syn/settings/SitesTest.java
@@ -1,8 +1,9 @@
package ca.islandora.syn.settings;
-import org.junit.Test;
import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
public class SitesTest {
@Test
@@ -10,7 +11,7 @@ public void TestJwtSites() {
final Config sites = new Config();
assertEquals(-1, sites.getVersion());
sites.setVersion(2);
- assertEquals(2,sites.getVersion());
+ assertEquals(2, sites.getVersion());
assertEquals(0, sites.getSites().size());
final Site site = new Site();
diff --git a/src/test/java/ca/islandora/syn/settings/TokenTest.java b/src/test/java/ca/islandora/syn/settings/TokenTest.java
index 5f6647a..53c3604 100644
--- a/src/test/java/ca/islandora/syn/settings/TokenTest.java
+++ b/src/test/java/ca/islandora/syn/settings/TokenTest.java
@@ -1,11 +1,11 @@
package ca.islandora.syn.settings;
-import org.junit.Before;
-import org.junit.Test;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import org.junit.Before;
+import org.junit.Test;
+
public class TokenTest {
Token token;
@@ -40,11 +40,11 @@ public void testTokenRoles() {
@Test
public void testTokenToken() {
- assertTrue(this.token.getToken().isEmpty());
+ assertTrue(this.token.getValue().isEmpty());
final String testVal = "test";
- this.token.setToken(testVal);
- assertEquals(testVal, this.token.getToken());
- this.token.setToken(" " + testVal);
- assertEquals(testVal, this.token.getToken());
+ this.token.setValue(testVal);
+ assertEquals(testVal, this.token.getValue());
+ this.token.setValue(" " + testVal);
+ assertEquals(testVal, this.token.getValue());
}
}
diff --git a/src/test/java/ca/islandora/syn/token/VerifierTest.java b/src/test/java/ca/islandora/syn/token/VerifierTest.java
index 9933556..7d519b7 100644
--- a/src/test/java/ca/islandora/syn/token/VerifierTest.java
+++ b/src/test/java/ca/islandora/syn/token/VerifierTest.java
@@ -35,13 +35,14 @@ public void setUp() {
@Test
public void testClaimsWithoutVerify() {
token = JWT.create()
- .withArrayClaim("roles", new String[]{"Role1", "Role2"})
- .withClaim("uid", 1)
- .withClaim("name", "admin")
- .withClaim("url", "http://test.com")
+ .withArrayClaim("roles", new String[] { "Role1", "Role2" })
+ .withClaim("webid", 1)
+ .withClaim("sub", "admin")
+ .withClaim("iss", "http://test.com")
.withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
.withExpiresAt(Date.from(LocalDateTime.now().plusHours(2).toInstant(offset)))
.sign(Algorithm.none());
+
final Verifier verifier = Verifier.create(token);
assertEquals(1, verifier.getUid());
assertEquals("admin", verifier.getName());
@@ -52,17 +53,16 @@ public void testClaimsWithoutVerify() {
assertEquals("Role2", roles.get(1));
}
- @Test
+ @Test(expected = InvalidTokenException.class)
public void testClaimsMissing() {
token = JWT.create()
- .withClaim("name", "admin")
- .withClaim("url", "http://test.com")
+ .withClaim("sub", "admin")
+ .withClaim("iss", "http://test.com")
.sign(Algorithm.none());
- final Verifier verifier = Verifier.create(token);
- assertNull(verifier);
+ Verifier.create(token);
}
- @Test
+ @Test(expected = InvalidTokenException.class)
public void testClaimsBad() {
token = "gibberish";
final Verifier verifier = Verifier.create(token);
@@ -72,10 +72,10 @@ public void testClaimsBad() {
@Test
public void testClaimsAndVerifyHmac() throws Exception {
token = JWT.create()
- .withArrayClaim("roles", new String[]{"Role1", "Role2"})
- .withClaim("uid", 1)
- .withClaim("name", "admin")
- .withClaim("url", "http://test.com")
+ .withArrayClaim("roles", new String[] { "Role1", "Role2" })
+ .withClaim("webid", 1)
+ .withClaim("sub", "admin")
+ .withClaim("iss", "http://test.com")
.withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
.withExpiresAt(Date.from(LocalDateTime.now().plusHours(2).toInstant(offset)))
.sign(Algorithm.HMAC256("secret"));
@@ -101,10 +101,10 @@ public void testClaimsAndVerifyRsa() throws Exception {
final RSAKey publicKey = (RSAKey) pair.getPublic();
token = JWT.create()
- .withArrayClaim("roles", new String[]{"Role1", "Role2"})
- .withClaim("uid", 1)
- .withClaim("name", "admin")
- .withClaim("url", "http://test.com")
+ .withArrayClaim("roles", new String[] { "Role1", "Role2" })
+ .withClaim("webid", 1)
+ .withClaim("sub", "admin")
+ .withClaim("iss", "http://test.com")
.withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
.withExpiresAt(Date.from(LocalDateTime.now().plusHours(2).toInstant(offset)))
.sign(Algorithm.RSA512(privateKey));
@@ -125,10 +125,10 @@ public void testClaimsAndVerifyRsa() throws Exception {
@Test
public void testClaimsAndVerifyHmacBadIssueDate() throws Exception {
token = JWT.create()
- .withArrayClaim("roles", new String[]{"Role1", "Role2"})
- .withClaim("uid", 1)
- .withClaim("name", "admin")
- .withClaim("url", "http://test.com")
+ .withArrayClaim("roles", new String[] { "Role1", "Role2" })
+ .withClaim("webid", 1)
+ .withClaim("sub", "admin")
+ .withClaim("iss", "http://test.com")
.withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
.withExpiresAt(Date.from(LocalDateTime.now().minusHours(2).toInstant(offset)))
.sign(Algorithm.HMAC256("secret"));
diff --git a/src/test/java/ca/islandora/syn/valves/SynFilterTest.java b/src/test/java/ca/islandora/syn/valves/SynFilterTest.java
new file mode 100644
index 0000000..52db131
--- /dev/null
+++ b/src/test/java/ca/islandora/syn/valves/SynFilterTest.java
@@ -0,0 +1,623 @@
+package ca.islandora.syn.valves;
+
+import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
+import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
+import static junit.framework.TestCase.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.algorithms.Algorithm;
+
+import ca.islandora.syn.valve.SynFilter;
+
+@RunWith(MockitoJUnitRunner.class)
+public class SynFilterTest {
+
+ private SynFilter synFilter;
+
+ private File settings;
+
+ @Mock
+ private FilterChain chain;
+
+ @Mock
+ private FilterConfig config;
+
+ @Mock
+ private HttpServletRequest request;
+
+ @Mock
+ private HttpServletResponse response;
+
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private static ZoneOffset offset;
+
+ private ArgumentCaptor requestCaptor;
+ private ArgumentCaptor responseCaptor;
+
+ @Before
+ public void setUp() throws Exception {
+ settings = temporaryFolder.newFile();
+ createSettings(settings);
+
+ when(config.getInitParameter("settings-path")).thenReturn(settings.getAbsolutePath());
+
+ synFilter = createFilter();
+
+ when(request.getScheme()).thenReturn("http");
+ when(request.getServerPort()).thenReturn(80);
+
+ offset = ZoneId.systemDefault().getRules().getOffset(Instant.now());
+
+ requestCaptor = ArgumentCaptor.forClass(HttpServletRequest.class);
+ responseCaptor = ArgumentCaptor.forClass(HttpServletResponse.class);
+ }
+
+ private SynFilter createFilter() throws ServletException {
+ final SynFilter synFilter = new SynFilter();
+ synFilter.init(config);
+ return synFilter;
+ }
+
+ @Test(expected = ServletException.class)
+ public void missingInitParameter() throws Exception {
+ when(config.getInitParameter("settings-path")).thenReturn(null);
+ synFilter = createFilter();
+ }
+
+ @Test(expected = ServletException.class)
+ public void absoluteSettingsDoesNotExist() throws Exception {
+ when(config.getInitParameter("settings-path")).thenReturn("/tmp/fileIsFake");
+ synFilter = createFilter();
+ }
+
+ @Test(expected = ServletException.class)
+ public void relativeSettingsDoesNotExist() throws Exception {
+ when(config.getInitParameter("settings-path")).thenReturn("fileIsFake");
+ synFilter = createFilter();
+ }
+
+ @Test(expected = ServletException.class)
+ public void settingsParseFail() throws Exception {
+ final String testYaml = String.join("\n",
+ "---",
+ "version: bad",
+ "site:",
+ " url: http://test.com",
+ " algorithm: HS256",
+ " encoding: plain",
+ " anonymous: false",
+ " key: secret");
+ Files.write(Paths.get(this.settings.getAbsolutePath()), testYaml.getBytes());
+
+ synFilter = createFilter();
+ }
+
+ @Test
+ public void shouldPassAuth() throws Exception {
+ final String host = "http://test.com";
+ final String username = "adminuser";
+ final String[] roles = new String[] { "role1", "role2", "role3" };
+ final ArrayList finalRoles = new ArrayList(Arrays.asList(roles));
+ finalRoles.add("islandora");
+ finalRoles.add(host);
+
+ final String token = "Bearer " + JWT
+ .create()
+ .withClaim("webid", 1)
+ .withClaim("sub", username)
+ .withClaim("iss", host)
+ .withArrayClaim("roles", roles)
+ .withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
+ .withExpiresAt(Date.from(LocalDateTime.now().plusHours(2).toInstant(offset)))
+ .sign(Algorithm.HMAC256("secret"));
+
+ when(request.getHeader("Authorization")).thenReturn(token);
+
+ synFilter.doFilter(request, response, chain);
+
+ verify(chain).doFilter(requestCaptor.capture(), responseCaptor.capture());
+
+ assertEquals(username, requestCaptor.getValue().getUserPrincipal().getName());
+
+ finalRoles.forEach(role -> assertTrue(requestCaptor.getValue().isUserInRole(role)));
+
+ }
+
+ @Test
+ public void shouldPassAuthToken() throws Exception {
+ final String defaultUser = "islandoraAdmin";
+ final String token = "Bearer 1337";
+
+ when(request.getHeader("Authorization"))
+ .thenReturn(token);
+
+ synFilter.doFilter(request, response, chain);
+
+ verify(chain).doFilter(requestCaptor.capture(), responseCaptor.capture());
+
+ assertEquals(defaultUser, requestCaptor.getValue().getUserPrincipal().getName());
+ assertTrue(requestCaptor.getValue().isUserInRole("islandora"));
+ }
+
+ @Test
+ public void shouldFailAuthBecauseOfTokenNotSet() throws Exception {
+ when(request.getMethod()).thenReturn("GET");
+ when(request.getServerName()).thenReturn("test.com");
+
+ synFilter.doFilter(request, response, chain);
+
+ verify(request).getHeader("Authorization");
+ verify(response).sendError(SC_UNAUTHORIZED, SynFilter.UNAUTHORIZED_MSG);
+ }
+
+ @Test
+ public void shouldFailAuthBecauseOfTokenInvalid1() throws Exception {
+ when(request.getHeader("Authorization"))
+ .thenReturn("garbage");
+
+ synFilter.doFilter(request, response, chain);
+
+ verify(request).getHeader("Authorization");
+ verify(response).sendError(SC_UNAUTHORIZED, SynFilter.UNAUTHORIZED_MSG);
+ }
+
+ @Test
+ public void shouldFailAuthBecauseOfTokenInvalid2() throws Exception {
+ when(request.getHeader("Authorization"))
+ .thenReturn("killer bandit foo");
+
+ synFilter.doFilter(request, response, chain);
+
+ verify(request).getHeader("Authorization");
+ verify(response).sendError(SC_UNAUTHORIZED, SynFilter.UNAUTHORIZED_MSG);
+ }
+
+ @Test
+ public void shouldFailTokenMissingUid() throws Exception {
+ final String host = "http://test.com";
+ final String username = "adminuser";
+ final String[] roles = new String[] { "role1", "role2", "role3" };
+ final ArrayList finalRoles = new ArrayList(Arrays.asList(roles));
+ finalRoles.add("islandora");
+ finalRoles.add(host);
+
+ final String token = JWT
+ .create()
+ .withClaim("sub", username)
+ .withClaim("iss", host)
+ .withArrayClaim("roles", roles)
+ .withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
+ .withExpiresAt(Date.from(LocalDateTime.now().plusHours(2).toInstant(offset)))
+ .sign(Algorithm.HMAC256("secret"));
+
+ when(request.getHeader("Authorization"))
+ .thenReturn("Bearer " + token);
+
+ synFilter.doFilter(request, response, chain);
+
+ verify(request).getHeader("Authorization");
+ verify(response).sendError(SC_UNAUTHORIZED, SynFilter.UNAUTHORIZED_MSG);
+ }
+
+ @Test
+ public void shouldPassAuthDefaultSite() throws Exception {
+ final String host = "http://test2.com";
+ final String username = "normalUser";
+ final List finalRoles = Arrays.asList("islandora", host);
+
+ final String token = JWT
+ .create()
+ .withClaim("webid", 1)
+ .withClaim("sub", username)
+ .withClaim("iss", host)
+ .withArrayClaim("roles", new String[] {})
+ .withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
+ .withExpiresAt(Date.from(LocalDateTime.now().plusHours(2).toInstant(offset)))
+ .sign(Algorithm.HMAC256("secret2"));
+
+ when(request.getHeader("Authorization"))
+ .thenReturn("Bearer " + token);
+
+ synFilter.doFilter(request, response, chain);
+ verify(chain).doFilter(requestCaptor.capture(), responseCaptor.capture());
+
+ verify(request).getHeader("Authorization");
+ assertEquals(username, requestCaptor.getValue().getUserPrincipal().getName());
+
+ finalRoles.forEach(role -> assertTrue(requestCaptor.getValue().isUserInRole(role)));
+ }
+
+ @Test
+ public void shouldFailAuthBecauseNoSiteMatch() throws Exception {
+ final String host = "http://test-no-match.com";
+ final String username = "normalUser";
+ final String token = JWT
+ .create()
+ .withClaim("webid", 1)
+ .withClaim("sub", username)
+ .withClaim("iss", host)
+ .withArrayClaim("roles", new String[] {})
+ .withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
+ .withExpiresAt(Date.from(LocalDateTime.now().plusHours(2).toInstant(offset)))
+ .sign(Algorithm.HMAC256("secret"));
+
+ when(request.getHeader("Authorization"))
+ .thenReturn("Bearer " + token);
+
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: HS256",
+ " encoding: plain",
+ " anonymous: false",
+ " key: secret");
+ Files.write(Paths.get(this.settings.getAbsolutePath()), testYaml.getBytes());
+
+ // Recreate because the settings file has changed so we need to run init again.
+ synFilter = createFilter();
+ synFilter.doFilter(request, response, chain);
+
+ verify(request).getHeader("Authorization");
+ verify(response).sendError(SC_UNAUTHORIZED, SynFilter.UNAUTHORIZED_MSG);
+ }
+
+ @Test
+ public void allowAuthWithToken() throws Exception {
+ final String host = "http://anon-test.com";
+ final String username = "Bob";
+ final List finalRoles = Arrays.asList("islandora", host);
+ final String token = JWT
+ .create()
+ .withClaim("webid", 1)
+ .withClaim("sub", username)
+ .withClaim("iss", host)
+ .withArrayClaim("roles", new String[] {})
+ .withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
+ .withExpiresAt(Date.from(LocalDateTime.now().plusHours(2).toInstant(offset)))
+ .sign(Algorithm.HMAC256("secretFool"));
+
+ when(request.getHeader("Authorization"))
+ .thenReturn("Bearer " + token);
+
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: " + host,
+ " algorithm: HS256",
+ " encoding: plain",
+ " anonymous: false",
+ " key: secretFool");
+ Files.write(Paths.get(this.settings.getAbsolutePath()), testYaml.getBytes());
+
+ synFilter = createFilter();
+
+ synFilter.doFilter(request, response, chain);
+ verify(chain).doFilter(requestCaptor.capture(), responseCaptor.capture());
+
+ assertEquals(username, requestCaptor.getValue().getUserPrincipal().getName());
+ verify(request).getHeader("Authorization");
+
+ finalRoles.forEach(role -> assertTrue(requestCaptor.getValue().isUserInRole(role)));
+ }
+
+ @Test
+ public void allowGetWithoutToken() throws Exception {
+ final String servername = "anon-test.com";
+ final String host = "http://" + servername;
+ final String username = "anonymous";
+ final List finalRoles = Arrays.asList("islandora", "anonymous", host);
+
+ when(request.getMethod()).thenReturn("GET");
+ when(request.getServerName()).thenReturn(servername);
+
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: " + host,
+ " algorithm: HS256",
+ " encoding: plain",
+ " anonymous: true",
+ " key: secretFool");
+ Files.write(Paths.get(this.settings.getAbsolutePath()), testYaml.getBytes());
+
+ synFilter = createFilter();
+ synFilter.doFilter(request, response, chain);
+
+ verify(chain).doFilter(requestCaptor.capture(), responseCaptor.capture());
+
+ assertEquals(username, requestCaptor.getValue().getUserPrincipal().getName());
+ finalRoles.forEach(role -> assertTrue(requestCaptor.getValue().isUserInRole(role)));
+ }
+
+ @Test
+ public void allowHeadWithoutToken() throws Exception {
+ final String servername = "anon-test.com";
+ final String host = "http://" + servername;
+ final String username = "anonymous";
+ final List finalRoles = Arrays.asList("islandora", "anonymous", host);
+
+ when(request.getMethod()).thenReturn("HEAD");
+ when(request.getServerName()).thenReturn(servername);
+
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: " + host,
+ " algorithm: HS256",
+ " encoding: plain",
+ " anonymous: true",
+ " key: secretFool");
+ Files.write(Paths.get(this.settings.getAbsolutePath()), testYaml.getBytes());
+
+ synFilter = createFilter();
+ synFilter.doFilter(request, response, chain);
+
+ verify(chain).doFilter(requestCaptor.capture(), responseCaptor.capture());
+
+ assertEquals(username, requestCaptor.getValue().getUserPrincipal().getName());
+ finalRoles.forEach(role -> assertTrue(requestCaptor.getValue().isUserInRole(role)));
+ }
+
+ @Test
+ public void disallowGetWithoutToken() throws Exception {
+ final String servername = "anon-test.com";
+ final String nohost = "http://other-site.com";
+
+ when(request.getMethod()).thenReturn("GET");
+ when(request.getServerName()).thenReturn(servername);
+
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: " + nohost,
+ " algorithm: HS256",
+ " encoding: plain",
+ " key: secretFool");
+ Files.write(Paths.get(this.settings.getAbsolutePath()), testYaml.getBytes());
+
+ synFilter = createFilter();
+ synFilter.doFilter(request, response, chain);
+
+ verify(request).getHeader("Authorization");
+ verify(response).sendError(SC_UNAUTHORIZED, SynFilter.UNAUTHORIZED_MSG);
+ }
+
+ @Test
+ public void overrideDefaultAllow() throws Exception {
+ final String servername = "anon-test.com";
+ final String host = "http://" + servername;
+ final String username = "normalUser123";
+ final List finalRoles = Arrays.asList("islandora", host);
+ final String token = JWT
+ .create()
+ .withClaim("webid", 1)
+ .withClaim("sub", username)
+ .withClaim("iss", host)
+ .withArrayClaim("roles", new String[] {})
+ .withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
+ .withExpiresAt(Date.from(LocalDateTime.now().plusHours(2).toInstant(offset)))
+ .sign(Algorithm.HMAC256("secretFool"));
+
+ when(request.getHeader("Authorization"))
+ .thenReturn("Bearer " + token);
+
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: " + host,
+ " algorithm: HS256",
+ " encoding: plain",
+ " anonymous: false",
+ " key: secretFool",
+ "site:",
+ " algorithm: RS256",
+ " encoding: plain",
+ " anonymous: true",
+ " default: true");
+ Files.write(Paths.get(this.settings.getAbsolutePath()), testYaml.getBytes());
+
+ synFilter = createFilter();
+ synFilter.doFilter(request, response, chain);
+
+ verify(chain).doFilter(requestCaptor.capture(), responseCaptor.capture());
+
+ assertEquals(username, requestCaptor.getValue().getUserPrincipal().getName());
+ finalRoles.forEach(role -> assertTrue(requestCaptor.getValue().isUserInRole(role)));
+ }
+
+ @Test
+ public void overrideDefaultAllowAndFail() throws Exception {
+ final String servername = "anon-test.com";
+ final String host = "http://" + servername;
+ final String username = "normalUser123";
+ final String token = JWT
+ .create()
+ .withClaim("webid", 1)
+ .withClaim("sub", username)
+ .withClaim("iss", host)
+ .withArrayClaim("roles", new String[] {})
+ .withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
+ .withExpiresAt(Date.from(LocalDateTime.now().plusHours(2).toInstant(offset)))
+ .sign(Algorithm.HMAC256("whatIsIt"));
+
+ when(request.getHeader("Authorization"))
+ .thenReturn("Bearer " + token);
+
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: " + host,
+ " algorithm: HS256",
+ " encoding: plain",
+ " anonymous: false",
+ " key: secretFool",
+ "site:",
+ " algorithm: RS256",
+ " encoding: plain",
+ " anonymous: true",
+ " default: true");
+ Files.write(Paths.get(this.settings.getAbsolutePath()), testYaml.getBytes());
+
+ synFilter = createFilter();
+ synFilter.doFilter(request, response, chain);
+
+ verify(request).getHeader("Authorization");
+ verify(response).sendError(SC_FORBIDDEN, SynFilter.FORBIDDEN_MSG);
+ }
+
+ @Test
+ public void overrideDefaultDeny() throws Exception {
+ final String servername = "anon-test.com";
+ final String host = "http://" + servername;
+ final String username = "anonymous";
+ final List finalRoles = Arrays.asList("islandora", "anonymous", host);
+
+ when(request.getMethod()).thenReturn("GET");
+ when(request.getServerName()).thenReturn(servername);
+
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: " + host,
+ " algorithm: HS256",
+ " encoding: plain",
+ " anonymous: true",
+ " key: secretFool",
+ "site:",
+ " algorithm: RS256",
+ " encoding: plain",
+ " anonymous: false",
+ " default: true");
+ Files.write(Paths.get(this.settings.getAbsolutePath()), testYaml.getBytes());
+
+ synFilter = createFilter();
+ synFilter.doFilter(request, response, chain);
+
+ verify(chain).doFilter(requestCaptor.capture(), responseCaptor.capture());
+
+ assertEquals(username, requestCaptor.getValue().getUserPrincipal().getName());
+ finalRoles.forEach(role -> assertTrue(requestCaptor.getValue().isUserInRole(role)));
+ }
+
+ @Test
+ public void defaultAndSiteAllowed() throws Exception {
+ final String servername = "anon-test.com";
+ final String host = "http://" + servername;
+ final String username = "anonymous";
+ final List finalRoles = Arrays.asList("islandora", "anonymous", host);
+
+ when(request.getMethod()).thenReturn("GET");
+ when(request.getServerName()).thenReturn(servername);
+
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " algorithm: RS256",
+ " encoding: plain",
+ " anonymous: true",
+ " default: true");
+ Files.write(Paths.get(this.settings.getAbsolutePath()), testYaml.getBytes());
+
+ synFilter = createFilter();
+ synFilter.doFilter(request, response, chain);
+
+ verify(chain).doFilter(requestCaptor.capture(), responseCaptor.capture());
+
+ assertEquals(username, requestCaptor.getValue().getUserPrincipal().getName());
+ finalRoles.forEach(role -> assertTrue(requestCaptor.getValue().isUserInRole(role)));
+ }
+
+ @Test
+ public void shouldFailSignatureVerification() throws Exception {
+ final String host = "http://test.com";
+ final String token = JWT
+ .create()
+ .withClaim("webid", 1)
+ .withClaim("sub", "normalUser")
+ .withClaim("iss", host)
+ .withArrayClaim("roles", new String[] {})
+ .withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
+ .withExpiresAt(Date.from(LocalDateTime.now().plusHours(2).toInstant(offset)))
+ .sign(Algorithm.HMAC256("secret"));
+
+ when(request.getHeader("Authorization"))
+ .thenReturn("Bearer " + token + "s");
+
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: " + host,
+ " algorithm: HS256",
+ " encoding: plain");
+ Files.write(Paths.get(this.settings.getAbsolutePath()), testYaml.getBytes());
+
+ synFilter = createFilter();
+ synFilter.doFilter(request, response, chain);
+
+ verify(request).getHeader("Authorization");
+ verify(response).sendError(SC_UNAUTHORIZED, SynFilter.UNAUTHORIZED_MSG);
+ }
+
+ private void createSettings(final File settingsFile) throws Exception {
+ final String testYaml = String.join("\n",
+ "---",
+ "version: 1",
+ "site:",
+ " url: http://test.com",
+ " algorithm: HS256",
+ " encoding: plain",
+ " key: secret",
+ "site:",
+ " algorithm: HS256",
+ " encoding: plain",
+ " default: true",
+ " key: secret2",
+ "token:",
+ " value: 1337");
+ Files.write(Paths.get(settingsFile.getAbsolutePath()), testYaml.getBytes());
+ }
+
+}
diff --git a/src/test/java/ca/islandora/syn/valves/SynValveTest.java b/src/test/java/ca/islandora/syn/valves/SynValveTest.java
deleted file mode 100644
index c94d5f4..0000000
--- a/src/test/java/ca/islandora/syn/valves/SynValveTest.java
+++ /dev/null
@@ -1,643 +0,0 @@
-package ca.islandora.syn.valves;
-
-import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.io.File;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.time.ZoneOffset;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-
-import org.apache.catalina.Container;
-import org.apache.catalina.Context;
-import org.apache.catalina.Host;
-import org.apache.catalina.Realm;
-import org.apache.catalina.Valve;
-import org.apache.catalina.connector.Request;
-import org.apache.catalina.connector.Response;
-import org.apache.catalina.realm.GenericPrincipal;
-import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-import com.auth0.jwt.JWT;
-import com.auth0.jwt.algorithms.Algorithm;
-
-import ca.islandora.syn.valve.SynValve;
-
-@RunWith(MockitoJUnitRunner.class)
-public class SynValveTest {
- private SynValve synValve;
- private File settings;
-
- @Mock
- private Container container;
-
- @Mock
- private Realm realm;
-
- @Mock
- private Context context;
-
- @Mock
- private Request request;
-
- @Mock
- private Response response;
-
- @Mock
- private Valve nextValve;
-
- @Rule
- public TemporaryFolder temporaryFolder = new TemporaryFolder();
-
- @Mock
- private Host mockHost;
-
- private static ZoneOffset offset;
-
- @Before
- public void setUp() throws Exception {
- settings = temporaryFolder.newFile();
- createSettings(settings);
-
- synValve = new SynValve();
- synValve.setPathname(settings.getAbsolutePath());
- synValve.setContainer(container);
- synValve.setNext(nextValve);
-
- when(container.getRealm()).thenReturn(realm);
- when(request.getContext()).thenReturn(context);
- when(request.getMethod()).thenReturn("POST");
- offset = ZoneId.systemDefault().getRules().getOffset(Instant.now());
- }
-
- @Test
- public void shouldInvokeNextValveWithoutAuth() throws Exception {
- when(realm.findSecurityConstraints(request, request.getContext()))
- .thenReturn(null);
-
- synValve.start();
- synValve.invoke(request, response);
-
- verify(nextValve).invoke(request, response);
- }
-
- @Test
- public void shouldPassAuth() throws Exception {
- final ArgumentCaptor argument = ArgumentCaptor.forClass(GenericPrincipal.class);
- final String host = "http://test.com";
-
- final String token = "Bearer " + JWT
- .create()
- .withClaim("uid", 1)
- .withClaim("name", "adminuser")
- .withClaim("url", host)
- .withArrayClaim("roles", new String[] {"role1", "role2", "role3"})
- .withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
- .withExpiresAt(Date.from(LocalDateTime.now().plusHours(2).toInstant(offset)))
- .sign(Algorithm.HMAC256("secret"));
-
- final SecurityConstraint securityConstraint = new SecurityConstraint();
- securityConstraint.setAuthConstraint(true);
- when(realm.findSecurityConstraints(request, request.getContext()))
- .thenReturn(new SecurityConstraint[] { securityConstraint });
- when(request.getHeader("Authorization"))
- .thenReturn(token);
-
- synValve.start();
- synValve.invoke(request, response);
-
- final InOrder inOrder = inOrder(request, nextValve);
- inOrder.verify(request).getHeader("Authorization");
- inOrder.verify(request).setUserPrincipal(argument.capture());
- inOrder.verify(request).setAuthType("SYN");
- inOrder.verify(nextValve).invoke(request, response);
-
- assertEquals("adminuser", argument.getValue().getName());
- final List roles = Arrays.asList(argument.getValue().getRoles());
- assertEquals(5, roles.size());
- assertTrue(roles.contains("role1"));
- assertTrue(roles.contains("role2"));
- assertTrue(roles.contains("role3"));
- assertTrue(roles.contains("islandora"));
- assertTrue(roles.contains("http://test.com"));
- assertNull(argument.getValue().getPassword());
- }
-
- @Test
- public void shouldPassAuthToken() throws Exception {
- final ArgumentCaptor argument = ArgumentCaptor.forClass(GenericPrincipal.class);
- final String token = "Bearer 1337";
- final SecurityConstraint securityConstraint = new SecurityConstraint();
- securityConstraint.setAuthConstraint(true);
- when(realm.findSecurityConstraints(request, request.getContext()))
- .thenReturn(new SecurityConstraint[] { securityConstraint });
- when(request.getHeader("Authorization"))
- .thenReturn(token);
-
- synValve.start();
- synValve.invoke(request, response);
-
- final InOrder inOrder = inOrder(request, nextValve);
- inOrder.verify(request).getHeader("Authorization");
- inOrder.verify(request).setUserPrincipal(argument.capture());
- inOrder.verify(request).setAuthType("SYN");
- inOrder.verify(nextValve).invoke(request, response);
-
- assertEquals("islandoraAdmin", argument.getValue().getName());
- final List roles = Arrays.asList(argument.getValue().getRoles());
- assertEquals(1, roles.size());
- assertTrue(roles.contains("islandora"));
- assertNull(argument.getValue().getPassword());
- }
-
- @Test
- public void shouldFailAuthBecauseOfTokenNotSet() throws Exception {
- final SecurityConstraint securityConstraint = new SecurityConstraint();
- securityConstraint.setAuthConstraint(true);
- when(realm.findSecurityConstraints(request, request.getContext()))
- .thenReturn(new SecurityConstraint[] { securityConstraint });
-
- synValve.start();
- synValve.invoke(request, response);
-
- verify(request).getHeader("Authorization");
- verify(response).sendError(401, "Token authentication failed.");
- }
-
- @Test
- public void shouldFailAuthBecauseOfTokenInvalid1() throws Exception {
- final SecurityConstraint securityConstraint = new SecurityConstraint();
- securityConstraint.setAuthConstraint(true);
- when(realm.findSecurityConstraints(request, request.getContext()))
- .thenReturn(new SecurityConstraint[] { securityConstraint });
- when(request.getHeader("Authorization"))
- .thenReturn("garbage");
-
- synValve.start();
- synValve.invoke(request, response);
-
- verify(request).getHeader("Authorization");
- verify(response).sendError(401, "Token authentication failed.");
- }
-
- @Test
- public void shouldFailAuthBecauseOfTokenInvalid2() throws Exception {
- final SecurityConstraint securityConstraint = new SecurityConstraint();
- securityConstraint.setAuthConstraint(true);
- when(realm.findSecurityConstraints(request, request.getContext()))
- .thenReturn(new SecurityConstraint[] { securityConstraint });
- when(request.getHeader("Authorization"))
- .thenReturn("killer bandit foo");
-
- synValve.start();
- synValve.invoke(request, response);
-
- verify(request).getHeader("Authorization");
- verify(response).sendError(401, "Token authentication failed.");
- }
-
- @Test
- public void shouldFailTokenMissingUid() throws Exception {
- final String host = "http://test.com";
- final String token = JWT
- .create()
- .withClaim("name", "adminuser")
- .withClaim("url", host)
- .withArrayClaim("roles", new String[] {"role1", "role2", "role3"})
- .withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
- .withExpiresAt(Date.from(LocalDateTime.now().plusHours(2).toInstant(offset)))
- .sign(Algorithm.HMAC256("secret"));
-
- final SecurityConstraint securityConstraint = new SecurityConstraint();
- securityConstraint.setAuthConstraint(true);
- when(realm.findSecurityConstraints(request, request.getContext()))
- .thenReturn(new SecurityConstraint[] { securityConstraint });
- when(request.getHeader("Authorization"))
- .thenReturn("Bearer " + token);
-
- synValve.start();
- synValve.invoke(request, response);
-
- verify(request).getHeader("Authorization");
- verify(response).sendError(401, "Token authentication failed.");
- }
-
- @Test
- public void shouldPassAuthDefaultSite() throws Exception {
- final String host = "http://test2.com";
- final String token = JWT
- .create()
- .withClaim("uid", 1)
- .withClaim("name", "normalUser")
- .withClaim("url", host)
- .withArrayClaim("roles", new String[] {})
- .withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
- .withExpiresAt(Date.from(LocalDateTime.now().plusHours(2).toInstant(offset)))
- .sign(Algorithm.HMAC256("secret2"));
-
- final ArgumentCaptor argument = ArgumentCaptor.forClass(GenericPrincipal.class);
-
- final SecurityConstraint securityConstraint = new SecurityConstraint();
- securityConstraint.setAuthConstraint(true);
- when(realm.findSecurityConstraints(request, request.getContext()))
- .thenReturn(new SecurityConstraint[] { securityConstraint });
- when(request.getHeader("Authorization"))
- .thenReturn("Bearer " + token);
-
- synValve.start();
- synValve.invoke(request, response);
-
- final InOrder inOrder = inOrder(request, nextValve);
- inOrder.verify(request).getHeader("Authorization");
- inOrder.verify(request).setUserPrincipal(argument.capture());
- inOrder.verify(request).setAuthType("SYN");
- inOrder.verify(nextValve).invoke(request, response);
-
- assertEquals("normalUser", argument.getValue().getName());
- final List roles = Arrays.asList(argument.getValue().getRoles());
- assertEquals(2, roles.size());
- assertTrue(roles.contains("islandora"));
- assertTrue(roles.contains("http://test2.com"));
- assertNull(argument.getValue().getPassword());
- }
-
- @Test
- public void shouldFailAuthBecauseNoSiteMatch() throws Exception {
- final String host = "http://test-no-match.com";
- final String token = JWT
- .create()
- .withClaim("uid", 1)
- .withClaim("name", "normalUser")
- .withClaim("url", host)
- .withArrayClaim("roles", new String[] {})
- .withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
- .withExpiresAt(Date.from(LocalDateTime.now().plusHours(2).toInstant(offset)))
- .sign(Algorithm.HMAC256("secret"));
-
- final SecurityConstraint securityConstraint = new SecurityConstraint();
- securityConstraint.setAuthConstraint(true);
- when(realm.findSecurityConstraints(request, request.getContext()))
- .thenReturn(new SecurityConstraint[] { securityConstraint });
- when(request.getHeader("Authorization"))
- .thenReturn("Bearer " + token);
-
- final String testXml = String.join("\n"
- , ""
- , " "
- , "secret"
- , " "
- , ""
- );
- Files.write(Paths.get(this.settings.getAbsolutePath()), testXml.getBytes());
-
- synValve.start();
- synValve.invoke(request, response);
-
- verify(request).getHeader("Authorization");
- verify(response).sendError(401, "Token authentication failed.");
- }
-
- @Test
- public void allowAuthWithToken() throws Exception {
- final String host = "http://anon-test.com";
- final String token = JWT
- .create()
- .withClaim("uid", 1)
- .withClaim("name", "normalUser")
- .withClaim("url", host)
- .withArrayClaim("roles", new String[] {})
- .withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
- .withExpiresAt(Date.from(LocalDateTime.now().plusHours(2).toInstant(offset)))
- .sign(Algorithm.HMAC256("secretFool"));
-
- final SecurityConstraint securityConstraint = new SecurityConstraint();
- securityConstraint.setAuthConstraint(true);
- when(realm.findSecurityConstraints(request, request.getContext()))
- .thenReturn(new SecurityConstraint[] { securityConstraint });
- when(request.getHeader("Authorization"))
- .thenReturn("Bearer " + token);
-
- final ArgumentCaptor argument = ArgumentCaptor.forClass(GenericPrincipal.class);
-
- final String testXml = String.join("\n"
- , ""
- , " "
- , "secretFool"
- , " "
- , ""
- );
- Files.write(Paths.get(this.settings.getAbsolutePath()), testXml.getBytes());
-
- synValve.start();
- synValve.invoke(request, response);
-
- final InOrder inOrder = inOrder(request, nextValve);
- inOrder.verify(request).getHeader("Authorization");
- inOrder.verify(request).setUserPrincipal(argument.capture());
- inOrder.verify(request).setAuthType("SYN");
- inOrder.verify(nextValve).invoke(request, response);
-
- assertEquals("normalUser", argument.getValue().getName());
- final List roles = Arrays.asList(argument.getValue().getRoles());
- assertEquals(2, roles.size());
- assertTrue(roles.contains("islandora"));
- assertTrue(roles.contains(host));
- assertNull(argument.getValue().getPassword());
- }
-
- @Test
- public void allowGetWithoutToken() throws Exception {
- final String host = "http://anon-test.com";
- final SecurityConstraint securityConstraint = new SecurityConstraint();
- securityConstraint.setAuthConstraint(true);
- when(realm.findSecurityConstraints(request, request.getContext()))
- .thenReturn(new SecurityConstraint[] { securityConstraint });
- when(request.getMethod()).thenReturn("GET");
- when(mockHost.toString()).thenReturn(host);
- when(request.getHost()).thenReturn(mockHost);
-
- final ArgumentCaptor argument = ArgumentCaptor.forClass(GenericPrincipal.class);
-
- final String testXml = String.join("\n"
- , ""
- , " "
- , "secretFool"
- , " "
- , ""
- );
- Files.write(Paths.get(this.settings.getAbsolutePath()), testXml.getBytes());
-
- synValve.start();
- synValve.invoke(request, response);
-
- final InOrder inOrder = inOrder(request, nextValve);
- inOrder.verify(request).setUserPrincipal(argument.capture());
- inOrder.verify(nextValve).invoke(request, response);
-
- assertEquals("anonymous", argument.getValue().getName());
- final List roles = Arrays.asList(argument.getValue().getRoles());
- assertEquals(2, roles.size());
- assertTrue(roles.contains("anonymous"));
- assertTrue(roles.contains("islandora"));
- assertNull(argument.getValue().getPassword());
- }
-
- @Test
- public void allowHeadWithoutToken() throws Exception {
- final String host = "http://anon-test.com";
- final SecurityConstraint securityConstraint = new SecurityConstraint();
- securityConstraint.setAuthConstraint(true);
- when(realm.findSecurityConstraints(request, request.getContext()))
- .thenReturn(new SecurityConstraint[] { securityConstraint });
- when(request.getMethod()).thenReturn("HEAD");
- when(mockHost.toString()).thenReturn(host);
- when(request.getHost()).thenReturn(mockHost);
-
- final ArgumentCaptor argument = ArgumentCaptor.forClass(GenericPrincipal.class);
-
- final String testXml = String.join("\n"
- , ""
- , " "
- , "secretFool"
- , " "
- , ""
- );
- Files.write(Paths.get(this.settings.getAbsolutePath()), testXml.getBytes());
-
- synValve.start();
- synValve.invoke(request, response);
-
- final InOrder inOrder = inOrder(request, nextValve);
- inOrder.verify(request).setUserPrincipal(argument.capture());
- inOrder.verify(nextValve).invoke(request, response);
-
- assertEquals("anonymous", argument.getValue().getName());
- final List roles = Arrays.asList(argument.getValue().getRoles());
- assertEquals(2, roles.size());
- assertTrue(roles.contains("anonymous"));
- assertTrue(roles.contains("islandora"));
- assertNull(argument.getValue().getPassword());
- }
-
- @Test
- public void disallowGetWithoutToken() throws Exception {
- final String host = "http://anon-test.com";
- final String nohost = "http://other-site.com";
- final SecurityConstraint securityConstraint = new SecurityConstraint();
- securityConstraint.setAuthConstraint(true);
- when(realm.findSecurityConstraints(request, request.getContext()))
- .thenReturn(new SecurityConstraint[] { securityConstraint });
- when(request.getMethod()).thenReturn("GET");
- when(mockHost.toString()).thenReturn(host);
- when(request.getHost()).thenReturn(mockHost);
-
- final String testXml = String.join("\n"
- , ""
- , " "
- , "secretFool"
- , " "
- , ""
- );
- Files.write(Paths.get(this.settings.getAbsolutePath()), testXml.getBytes());
-
- synValve.start();
- synValve.invoke(request, response);
-
- verify(request).getHeader("Authorization");
- verify(response).sendError(401, "Token authentication failed.");
- }
-
- @Test
- public void overrideDefaultAllow() throws Exception {
- final String host = "http://anon-test.com";
- final String token = JWT
- .create()
- .withClaim("uid", 1)
- .withClaim("name", "normalUser")
- .withClaim("url", host)
- .withArrayClaim("roles", new String[] {})
- .withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
- .withExpiresAt(Date.from(LocalDateTime.now().plusHours(2).toInstant(offset)))
- .sign(Algorithm.HMAC256("secretFool"));
- final SecurityConstraint securityConstraint = new SecurityConstraint();
- securityConstraint.setAuthConstraint(true);
- when(realm.findSecurityConstraints(request, request.getContext()))
- .thenReturn(new SecurityConstraint[] { securityConstraint });
- when(request.getHeader("Authorization"))
- .thenReturn("Bearer " + token);
- when(request.getMethod()).thenReturn("GET");
- when(mockHost.toString()).thenReturn(host);
- when(request.getHost()).thenReturn(mockHost);
-
- final ArgumentCaptor argument = ArgumentCaptor.forClass(GenericPrincipal.class);
-
- final String testXml = String.join("\n"
- , ""
- , " "
- , "secretFool"
- , " "
- , " "
- , ""
- );
- Files.write(Paths.get(this.settings.getAbsolutePath()), testXml.getBytes());
-
- synValve.start();
- synValve.invoke(request, response);
-
- final InOrder inOrder = inOrder(request, nextValve);
- inOrder.verify(request).setUserPrincipal(argument.capture());
- inOrder.verify(nextValve).invoke(request, response);
-
- assertEquals("normalUser", argument.getValue().getName());
- final List roles = Arrays.asList(argument.getValue().getRoles());
- assertEquals(2, roles.size());
- assertTrue(roles.contains("islandora"));
- assertTrue(roles.contains(host));
- assertNull(argument.getValue().getPassword());
- }
-
- @Test
- public void overrideDefaultDeny() throws Exception {
- final String host = "http://anon-test.com";
- final SecurityConstraint securityConstraint = new SecurityConstraint();
- securityConstraint.setAuthConstraint(true);
- when(realm.findSecurityConstraints(request, request.getContext()))
- .thenReturn(new SecurityConstraint[] { securityConstraint });
- when(request.getMethod()).thenReturn("GET");
- when(mockHost.toString()).thenReturn(host);
- when(request.getHost()).thenReturn(mockHost);
-
- final ArgumentCaptor argument = ArgumentCaptor.forClass(GenericPrincipal.class);
-
- final String testXml = String.join("\n"
- , ""
- , " "
- , "secretFool"
- , " "
- , " "
- , ""
- );
- Files.write(Paths.get(this.settings.getAbsolutePath()), testXml.getBytes());
-
- synValve.start();
- synValve.invoke(request, response);
-
- final InOrder inOrder = inOrder(request, nextValve);
- inOrder.verify(request).setUserPrincipal(argument.capture());
- inOrder.verify(nextValve).invoke(request, response);
-
- assertEquals("anonymous", argument.getValue().getName());
- final List roles = Arrays.asList(argument.getValue().getRoles());
- assertEquals(2, roles.size());
- assertTrue(roles.contains("anonymous"));
- assertTrue(roles.contains("islandora"));
- assertNull(argument.getValue().getPassword());
- }
-
- @Test
- public void defaultAndSiteAllowed() throws Exception {
- final String host = "http://anon-test.com";
- final SecurityConstraint securityConstraint = new SecurityConstraint();
- securityConstraint.setAuthConstraint(true);
- when(realm.findSecurityConstraints(request, request.getContext()))
- .thenReturn(new SecurityConstraint[] { securityConstraint });
- when(request.getMethod()).thenReturn("GET");
- when(mockHost.toString()).thenReturn(host);
- when(request.getHost()).thenReturn(mockHost);
-
- final ArgumentCaptor argument = ArgumentCaptor.forClass(GenericPrincipal.class);
-
- final String testXml = String.join("\n"
- , ""
- , " "
- , ""
- );
- Files.write(Paths.get(this.settings.getAbsolutePath()), testXml.getBytes());
-
- synValve.start();
- synValve.invoke(request, response);
-
- final InOrder inOrder = inOrder(request, nextValve);
- inOrder.verify(request).setUserPrincipal(argument.capture());
- inOrder.verify(nextValve).invoke(request, response);
-
- assertEquals("anonymous", argument.getValue().getName());
- final List roles = Arrays.asList(argument.getValue().getRoles());
- assertEquals(2, roles.size());
- assertTrue(roles.contains("anonymous"));
- assertTrue(roles.contains("islandora"));
- assertNull(argument.getValue().getPassword());
- }
-
- @Test
- public void shouldFailSignatureVerification() throws Exception {
- final String host = "http://test.com";
- final String token = JWT
- .create()
- .withClaim("uid", 1)
- .withClaim("name", "normalUser")
- .withClaim("url", host)
- .withArrayClaim("roles", new String[] {})
- .withIssuedAt(Date.from(LocalDateTime.now().toInstant(offset)))
- .withExpiresAt(Date.from(LocalDateTime.now().plusHours(2).toInstant(offset)))
- .sign(Algorithm.HMAC256("secret"));
-
- final SecurityConstraint securityConstraint = new SecurityConstraint();
- securityConstraint.setAuthConstraint(true);
- when(realm.findSecurityConstraints(request, request.getContext()))
- .thenReturn(new SecurityConstraint[] { securityConstraint });
- when(request.getHeader("Authorization"))
- .thenReturn("Bearer " + token + "s");
-
- final String testXml = String.join("\n"
- , ""
- , " "
- , "secret"
- , " "
- , ""
- );
- Files.write(Paths.get(this.settings.getAbsolutePath()), testXml.getBytes());
-
- synValve.start();
- synValve.invoke(request, response);
-
- verify(request).getHeader("Authorization");
- verify(response).sendError(401, "Token authentication failed.");
- }
-
- private void createSettings(final File settingsFile) throws Exception {
- final String testXml = String.join("\n"
- , ""
- , " "
- , "secret"
- , " "
- , " "
- , "secret2"
- , " "
- , " "
- , "1337"
- , " "
- , ""
- );
- Files.write(Paths.get(settingsFile.getAbsolutePath()), testXml.getBytes());
- }
-}
diff --git a/src/test/resources/exampleSettings.yaml b/src/test/resources/exampleSettings.yaml
new file mode 100644
index 0000000..f014962
--- /dev/null
+++ b/src/test/resources/exampleSettings.yaml
@@ -0,0 +1,4 @@
+version: 1
+token:
+ user: bob
+ value: bobsPassword
\ No newline at end of file