From 0fc40ecea135bb5d125b799457ce43883a9d9c8d Mon Sep 17 00:00:00 2001 From: dannylamb Date: Wed, 28 Nov 2018 09:39:16 -0400 Subject: [PATCH 1/2] Revert "Change Tomcat Valve to servlet Filter (#11)" This reverts commit 9482e9547e68cea3f6a52ec0330b29146321c380. --- README.md | 55 +- build.gradle | 52 +- conf/syn-settings.example.xml | 36 + conf/syn-settings.example.yaml | 43 -- .../ca/islandora/syn/settings/Config.java | 20 +- .../syn/settings/SettingsParser.java | 214 +++--- .../syn/settings/SettingsParserException.java | 35 - .../java/ca/islandora/syn/settings/Site.java | 9 +- .../java/ca/islandora/syn/settings/Token.java | 14 +- .../syn/token/InvalidTokenException.java | 36 - .../java/ca/islandora/syn/token/Verifier.java | 53 +- .../ca/islandora/syn/valve/SynFilter.java | 242 ------- .../syn/valve/SynRequestWrapper.java | 52 -- .../java/ca/islandora/syn/valve/SynValve.java | 213 ++++++ .../SettingsParserAlgorithmsTest.java | 462 ++++++------- .../settings/SettingsParserAnonymousTest.java | 120 ++-- .../settings/SettingsParserDigestTest.java | 217 +++--- .../syn/settings/SettingsParserTokenTest.java | 111 +-- .../ca/islandora/syn/settings/SitesTest.java | 5 +- .../ca/islandora/syn/settings/TokenTest.java | 16 +- .../ca/islandora/syn/token/VerifierTest.java | 44 +- .../islandora/syn/valves/SynFilterTest.java | 623 ----------------- .../ca/islandora/syn/valves/SynValveTest.java | 643 ++++++++++++++++++ src/test/resources/exampleSettings.yaml | 4 - 24 files changed, 1514 insertions(+), 1805 deletions(-) create mode 100644 conf/syn-settings.example.xml delete mode 100644 conf/syn-settings.example.yaml delete mode 100644 src/main/java/ca/islandora/syn/settings/SettingsParserException.java delete mode 100644 src/main/java/ca/islandora/syn/token/InvalidTokenException.java delete mode 100644 src/main/java/ca/islandora/syn/valve/SynFilter.java delete mode 100644 src/main/java/ca/islandora/syn/valve/SynRequestWrapper.java create mode 100644 src/main/java/ca/islandora/syn/valve/SynValve.java delete mode 100644 src/test/java/ca/islandora/syn/valves/SynFilterTest.java create mode 100644 src/test/java/ca/islandora/syn/valves/SynValveTest.java delete mode 100644 src/test/resources/exampleSettings.yaml diff --git a/README.md b/README.md index c048898..d22750c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ## Description -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)). +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)). ## Building @@ -15,39 +15,54 @@ 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 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. +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. -### Register Filter -Now register the filter in web applications' `web.xml` file by adding something like. +### Register Valve +Now register the valve in Tomcat configuration file. +In Ubuntu this file is located at: `/var/lib/tomcat8/conf/context.xml` ```xml - - SynFilter - ca.islandora.syn.valve.SynFilter - - settings-path - /var/lib/tomcat8/conf/syn-settings.yml - - - - - SynFilter - /* - + ``` -Where the **settings-path** `param-value` is the the location of the settings file. +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 + +``` 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.yaml) and move it to: `$CATALINA_BASE/conf/syn-settings.xml`. Then use this path when configuring the application's filter `init-param`s. +Modify the [example configuration](./conf/syn-settings.example.xml) and move it to: `$CATALINA_BASE/conf/syn-settings.xml`. ## Maintainers * [Jonathan Green](https://github.com/jonathangreen/) -* [Jared Whiklo](https://github.com/whikloj) ## Development diff --git a/build.gradle b/build.gradle index d99d80d..7b29848 100644 --- a/build.gradle +++ b/build.gradle @@ -14,20 +14,6 @@ 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') @@ -38,17 +24,15 @@ repositories { } dependencies { - 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 + 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 } jacocoTestReport { @@ -70,13 +54,6 @@ 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 { @@ -85,14 +62,3 @@ 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 new file mode 100644 index 0000000..3cbd328 --- /dev/null +++ b/conf/syn-settings.example.xml @@ -0,0 +1,36 @@ + + + + + +my secret key + + + + + + + + + + + + + + my super secret token + + + diff --git a/conf/syn-settings.example.yaml b/conf/syn-settings.example.yaml deleted file mode 100644 index a1ea934..0000000 --- a/conf/syn-settings.example.yaml +++ /dev/null @@ -1,43 +0,0 @@ -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 b96a38b..de74243 100644 --- a/src/main/java/ca/islandora/syn/settings/Config.java +++ b/src/main/java/ca/islandora/syn/settings/Config.java @@ -3,37 +3,29 @@ import java.util.ArrayList; import java.util.List; -import com.fasterxml.jackson.annotation.JsonSetter; - public class Config { private int version = -1; - private final List sites = new ArrayList<>(); - private final List tokens = new ArrayList<>(); + private List sites = new ArrayList<>(); + private List tokens = new ArrayList<>(); - @JsonSetter("site") public void addSite(final Site site) { - this.sites.add(site); + sites.add(site); } - public List getSites() { - return this.sites; + return sites; } public int getVersion() { return this.version; } - public void setVersion(final int version) { this.version = version; } - @JsonSetter("token") public void addToken(final Token token) { - this.tokens.add(token); + tokens.add(token); } - public List getTokens() { - return this.tokens; + return tokens; } - } diff --git a/src/main/java/ca/islandora/syn/settings/SettingsParser.java b/src/main/java/ca/islandora/syn/settings/SettingsParser.java index fa28ba8..92e25b5 100644 --- a/src/main/java/ca/islandora/syn/settings/SettingsParser.java +++ b/src/main/java/ca/islandora/syn/settings/SettingsParser.java @@ -1,11 +1,10 @@ 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; @@ -18,88 +17,66 @@ 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.slf4j.Logger; +import org.xml.sax.SAXException; 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 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."); + 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"); } + 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.toUpperCase().startsWith("RS")) { + if (algorithm.equalsIgnoreCase("RS256")) { + return AlgorithmType.RSA; + } else if (algorithm.equalsIgnoreCase("RS384")) { return AlgorithmType.RSA; - } else if (algorithm.toUpperCase().startsWith("HS")) { + } 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")) { return AlgorithmType.HMAC; } else { return AlgorithmType.INVALID; } } - /** - * 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()); + 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()); + } if (!file.exists() || !file.canRead()) { log.error("Path does not exist:" + site.getPath() + ". Site ignored."); return false; @@ -108,23 +85,16 @@ private static boolean validatePath(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() != null) { + if (!site.getKey().equalsIgnoreCase("")) { publicKeyReader = new StringReader(site.getKey()); } else if (site.getPath() != null) { try { publicKeyReader = new FileReader(site.getPath()); - } catch (final FileNotFoundException e) { + } catch (FileNotFoundException e) { log.error("Private key file not found."); } } @@ -142,7 +112,7 @@ private static Algorithm getRsaAlgorithm(final Site site) { publicKey = (RSAPublicKey) factory.generatePublic(pubKeySpec); pemReader.close(); publicKeyReader.close(); - } catch (final Exception e) { + } catch (Exception e) { log.error("Error loading public key."); return null; } @@ -163,23 +133,16 @@ 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) { - final byte[] secret; + byte[] secret; byte[] secretRaw = null; - if (site.getKey() != null) { + if (!site.getKey().equalsIgnoreCase("")) { secretRaw = site.getKey().trim().getBytes(); } else if (site.getPath() != null) { try { secretRaw = Files.readAllBytes(Paths.get(site.getPath())); - } catch (final IOException e) { + } catch (IOException e) { log.error("Unable to get secret from file.", e); } } @@ -191,7 +154,7 @@ private static Algorithm getHmacAlgorithm(final Site site) { if (site.getEncoding().equalsIgnoreCase("base64")) { try { secret = Base64.getDecoder().decode(secretRaw); - } catch (final Exception e) { + } catch (Exception e) { log.error("Base64 decode error. Skipping site.", e); return null; } @@ -212,20 +175,34 @@ private static Algorithm getHmacAlgorithm(final Site site) { } } - /** - * Get site keys from loaded Config. - * - * @return Map of URLs (or null) and parsed keys for verification. - */ - public Map getSiteAlgorithms() { + 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) { final Map algorithms = new HashMap<>(); - if (loadedSites == null) { + final Config sites = getSites(settings); + if (sites == null) { return algorithms; } boolean defaultSet = false; - for (final Site site : loadedSites.getSites()) { + for (Site site : sites.getSites()) { final boolean pathDefined = site.getPath() != null && !site.getPath().equalsIgnoreCase(""); final boolean keyDefined = site.getKey() != null && !site.getKey().equalsIgnoreCase(""); @@ -236,20 +213,20 @@ public Map getSiteAlgorithms() { } if (site.getPath() != null) { - if (!validatePath(site)) { + if (!validateExpandPath(site)) { continue; } } // Check that the algorithm type is valid. final AlgorithmType algorithmType = getSiteAlgorithmType(site.getAlgorithm()); - final Algorithm algorithm; + 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; } @@ -275,18 +252,14 @@ public Map getSiteAlgorithms() { return algorithms; } - /** - * Get static tokens from the loaded Config. - * - * @return Map of token value and Token object. - */ - public Map getSiteStaticTokens() { - if (loadedSites == null) { + public static Map getSiteStaticTokens(final InputStream settings) { + final Config sites = getSites(settings); + if (sites == null) { return new HashMap(); } - final Map tokens = loadedSites.getTokens().stream().filter(x -> !x.getValue().isEmpty()) - .collect(Collectors.toMap(Token::getValue, t -> t)); + final Map tokens = sites.getTokens().stream().filter(x -> !x.getToken().isEmpty()) + .collect(Collectors.toMap(Token::getToken, t -> t)); return tokens; } @@ -294,28 +267,25 @@ public Map getSiteStaticTokens() { /** * Build a list of site urls that allow anonymous GET requests. * - * @return Map of all URLs (or null for default) and boolean if they allow - * anonymous. + * @param settings the path to the syn-settings file + * @return list of site urls. */ - public Map getSiteAllowAnonymous() { - if (loadedSites == null) { + public static Map getSiteAllowAnonymous(final InputStream settings) { + final Config sites = getSites(settings); + if (sites == null) { return new HashMap(); } - 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())); + 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())); return anonymousAllowed; } - /** - * Getter for loaded Config object. - * - * @return - */ - public Config getConfig() { - return loadedSites; + static Config getSitesObject(final InputStream settings) + throws IOException, SAXException { + return (Config) getDigester().parse(settings); } } diff --git a/src/main/java/ca/islandora/syn/settings/SettingsParserException.java b/src/main/java/ca/islandora/syn/settings/SettingsParserException.java deleted file mode 100644 index a61556c..0000000 --- a/src/main/java/ca/islandora/syn/settings/SettingsParserException.java +++ /dev/null @@ -1,35 +0,0 @@ -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 4cf7f19..1524282 100644 --- a/src/main/java/ca/islandora/syn/settings/Site.java +++ b/src/main/java/ca/islandora/syn/settings/Site.java @@ -12,7 +12,6 @@ public class Site { public String getUrl() { return this.url; } - public void setUrl(final String url) { this.url = url; } @@ -20,7 +19,6 @@ public void setUrl(final String url) { public String getAlgorithm() { return this.algorithm; } - public void setAlgorithm(final String algorithm) { this.algorithm = algorithm; } @@ -28,7 +26,6 @@ public void setAlgorithm(final String algorithm) { public String getKey() { return this.key; } - public void setKey(final String key) { this.key = key; } @@ -36,7 +33,6 @@ public void setKey(final String key) { public String getPath() { return this.path; } - public void setPath(final String path) { this.path = path; } @@ -44,7 +40,6 @@ public void setPath(final String path) { public String getEncoding() { return this.encoding; } - public void setEncoding(final String encoding) { this.encoding = encoding; } @@ -52,7 +47,6 @@ public void setEncoding(final String encoding) { public boolean getDefault() { return this.defaultItem; } - public void setDefault(final boolean defaultItem) { this.defaultItem = defaultItem; } @@ -69,8 +63,7 @@ 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 630c6d1..3548d72 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 final List roles = new ArrayList<>(); - private String value = ""; + private List roles = new ArrayList<>(); + private String token = ""; 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 getValue() { - return value; + public String getToken() { + return token; } - public void setValue(final String token) { - this.value = token.trim(); + public void setToken(final String token) { + this.token = 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 deleted file mode 100644 index bdbde41..0000000 --- a/src/main/java/ca/islandora/syn/token/InvalidTokenException.java +++ /dev/null @@ -1,36 +0,0 @@ -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 f4d5d5e..97fe054 100644 --- a/src/main/java/ca/islandora/syn/token/Verifier.java +++ b/src/main/java/ca/islandora/syn/token/Verifier.java @@ -1,60 +1,62 @@ 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 com.auth0.jwt.interfaces.Claim; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; public class Verifier { - private static final Logger log = getLogger(Verifier.class); + private static final Log log = LogFactory.getLog(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); - 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("uid").isNull()) { + return null; + } + if (verifier.jwt.getClaim("url").isNull()) { + return null; } - } catch (final JWTDecodeException exception) { + 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) { log.error("Error decoding token: " + token, exception); - throw new InvalidTokenException("Error decoding token: " + token, exception); + return null; } return verifier; } public int getUid() { - return this.jwt.getClaim("webid").asInt(); + return this.jwt.getClaim("uid").asInt(); } public String getUrl() { - return this.jwt.getClaim("iss").asString(); + return this.jwt.getClaim("url").asString(); } public String getName() { - return this.jwt.getClaim("sub").asString(); + return this.jwt.getClaim("name").asString(); } public List getRoles() { @@ -65,11 +67,10 @@ public boolean verify(final Algorithm algorithm) { final JWTVerifier verifier = JWT.require(algorithm).build(); try { verifier.verify(this.token); - } catch (final JWTVerificationException exception) { + } catch (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 deleted file mode 100644 index a5dd0d5..0000000 --- a/src/main/java/ca/islandora/syn/valve/SynFilter.java +++ /dev/null @@ -1,242 +0,0 @@ -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 deleted file mode 100644 index 2fbf16b..0000000 --- a/src/main/java/ca/islandora/syn/valve/SynRequestWrapper.java +++ /dev/null @@ -1,52 +0,0 @@ -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 new file mode 100644 index 0000000..6d0158a --- /dev/null +++ b/src/main/java/ca/islandora/syn/valve/SynValve.java @@ -0,0 +1,213 @@ +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 a6f37e5..d33d112 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.StringReader; +import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Map; @@ -20,21 +20,19 @@ public class SettingsParserAlgorithmsTest { public TemporaryFolder temporaryFolder = new TemporaryFolder(); private void testOneSiteHmacInlineKey(final String algorithm) throws Exception { - 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()); - } + 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()); } @Test @@ -45,109 +43,80 @@ 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 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(); - } + 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()); } @Test public void testOneSiteHmacBase64() throws Exception { - 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()); - } + final String testXml = String.join("\n" + , "" + , " " + , " am9uYXRoYW4gaXMgYXdlc29tZQ==" + , " " + , "" + ); + + final InputStream stream = new ByteArrayInputStream(testXml.getBytes()); + final Map algorithms = SettingsParser.getSiteAlgorithms(stream); + assertEquals(1, algorithms.size()); } @Test public void testOneSiteHmacInvalidBase64() throws Exception { - 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()); - } + 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()); } @Test public void testOneSiteHmacInvalidEncoding() throws Exception { - 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()); - } + 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()); } private void testOneSiteHmacFileKey(final String algorithm) throws Exception { final File key = temporaryFolder.newFile(); final String path = key.getAbsolutePath(); - 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()); - } + 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()); } @Test @@ -159,110 +128,94 @@ public void testOneSiteAllHmacFileKey() throws Exception { @Test public void testSiteBothInlineAndPath() throws Exception { - 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()); - } + 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()); } @Test public void testSiteNeitherInlineAndPath() throws Exception { - 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()); - } + final String testXml = String.join("\n" + , "" + , " " + , "" + ); + + final InputStream stream = new ByteArrayInputStream(testXml.getBytes()); + final Map algorithms = SettingsParser.getSiteAlgorithms(stream); + assertEquals(0, algorithms.size()); } @Test public void testSiteInvalidPath() throws Exception { - 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()); - } + final String testXml = String.join("\n" + , "" + , " " + , "" + ); + + final InputStream stream = new ByteArrayInputStream(testXml.getBytes()); + final Map algorithms = SettingsParser.getSiteAlgorithms(stream); + assertEquals(0, algorithms.size()); } @Test public void testSiteNoUrlDefault() throws Exception { - 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()); - } + 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()); } @Test public void testSiteNoUrl() throws Exception { - 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()); - } + 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()); } private void testOneSiteRsaInlineKey(final String algorithm) throws Exception { - 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()); - } + 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()); } @Test @@ -276,29 +229,28 @@ 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 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()); - } + 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()); } @Test @@ -310,52 +262,45 @@ public void testOneSiteAllRsaFileKey() throws Exception { @Test public void testOneSiteAllRsaInvalidEncoding() throws Exception { - 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()); - } + 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()); } + @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 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()); - } + final String testXml = String.join("\n" + , "" + , " " + , " " + , "" + ); + final InputStream stream = new ByteArrayInputStream(testXml.getBytes()); + final Map algorithms = SettingsParser.getSiteAlgorithms(stream); + assertEquals(1, algorithms.size()); } @Test @@ -363,25 +308,24 @@ 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 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()); - } + final String testXml = String.join("\n" + , "" + , " " + , "" + ); + final InputStream stream = new ByteArrayInputStream(testXml.getBytes()); + final Map algorithms = SettingsParser.getSiteAlgorithms(stream); + 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 917beb0..9c7d321 100644 --- a/src/test/java/ca/islandora/syn/settings/SettingsParserAnonymousTest.java +++ b/src/test/java/ca/islandora/syn/settings/SettingsParserAnonymousTest.java @@ -2,7 +2,8 @@ import static org.junit.Assert.assertEquals; -import java.io.StringReader; +import java.io.ByteArrayInputStream; +import java.io.InputStream; import java.util.Map; import org.junit.Test; @@ -11,84 +12,65 @@ public class SettingsParserAnonymousTest { @Test public void testSiteAnonymousOn() throws Exception { - final String testYaml = String.join("\n", - "---", - "version: 1", - "site:", - " url: http://test.com", - " algorithm: RS256", - " encoding: plain", - " anonymous: true", - " key: test data"); + final String testXml = String.join("\n" + , "" + , " " + , " test data" + , " " + , "" + ); - 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")); - } + 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")); } @Test public void testSiteMultipleAnonymousTest() throws Exception { - 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 String testXml = String.join("\n" + , "" + , " " + , " test data" + , " " + , " " + , " test data" + , " " + , "" + ); - 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")); - } + 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")); } @Test public void testDefaultMultipleAnonymousTest() throws Exception { - 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 String testXml = String.join("\n" + , "" + , " " + , " test data" + , " " + , " " + , " test data" + , " " + , " " + , "" + ); - 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")); - } + 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")); } } diff --git a/src/test/java/ca/islandora/syn/settings/SettingsParserDigestTest.java b/src/test/java/ca/islandora/syn/settings/SettingsParserDigestTest.java index cfc3486..77daec8 100644 --- a/src/test/java/ca/islandora/syn/settings/SettingsParserDigestTest.java +++ b/src/test/java/ca/islandora/syn/settings/SettingsParserDigestTest.java @@ -5,7 +5,8 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import java.io.StringReader; +import java.io.ByteArrayInputStream; +import java.io.InputStream; import org.junit.Test; @@ -13,141 +14,127 @@ public class SettingsParserDigestTest { @Test public void testOneSitePath() throws Exception { - 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()); - } + 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()); } @Test public void testOneSiteKey() throws Exception { - 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()); - } + 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()); } @Test public void testTwoSites() throws Exception { - 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()); - } + final String testXml = String.join("\n" + , "" + , " " + , " " + , "" + ); + + final InputStream stream = new ByteArrayInputStream(testXml.getBytes()); + final Config settings = SettingsParser.getSitesObject(stream); + assertEquals(2, settings.getSites().size()); } - @Test(expected = SettingsParserException.class) + @Test public void testOneSiteUnexpectedAttribute() throws Exception { - 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()); - } + final String testXml = String.join("\n" + , "" + , " " + , "" + ); + + final InputStream stream = new ByteArrayInputStream(testXml.getBytes()); + final Config settings = SettingsParser.getSitesObject(stream); + 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 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()); - } + 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()); } @Test public void testValidAnonymousFalse() throws Exception { - 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()); - } + 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()); } - @Test(expected = SettingsParserException.class) + @Test public void testInvalidAnonymous() throws Exception { - 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()); - } + 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()); } } diff --git a/src/test/java/ca/islandora/syn/settings/SettingsParserTokenTest.java b/src/test/java/ca/islandora/syn/settings/SettingsParserTokenTest.java index 3c75e6a..85f1513 100644 --- a/src/test/java/ca/islandora/syn/settings/SettingsParserTokenTest.java +++ b/src/test/java/ca/islandora/syn/settings/SettingsParserTokenTest.java @@ -1,79 +1,82 @@ package ca.islandora.syn.settings; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.io.StringReader; +import org.junit.Test; +import java.io.ByteArrayInputStream; +import java.io.InputStream; import java.util.Map; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class SettingsParserTokenTest { - @Test(expected = SettingsParserException.class) + @Test public void testInvalidVersion() throws Exception { - final String testYaml = String.join("\n", - "---", - "version: 2", - "token:", - " value: c00lpazzward"); + final String testXml = String.join("\n" + , "" + , " " + , " c00lpazzward" + , " " + , "" + ); - try (final StringReader stream = new StringReader(testYaml)) { - SettingsParser.create(stream).getSiteStaticTokens(); - } + final InputStream stream = new ByteArrayInputStream(testXml.getBytes()); + final Map tokens = SettingsParser.getSiteStaticTokens(stream); + assertEquals(0, tokens.size()); } @Test public void testTokenNoParams() throws Exception { - final String testYaml = String.join("\n", - "---", - "version: 1", - "token:", - " value: c00lpazzward"); + final String testXml = String.join("\n" + , "" + , " " + , " c00lpazzward" + , " " + , "" + ); - 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()); - } + 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()); } @Test public void testTokenUser() throws Exception { - final String testYaml = String.join("\n", - "---", - "version: 1", - "token:", - " user: denis", - " value: c00lpazzward"); + final String testXml = String.join("\n" + , "" + , " " + , " c00lpazzward" + , " " + , "" + ); - 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()); - } + 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()); } @Test public void testTokenRole() throws Exception { - final String testYaml = String.join("\n", - "---", - "version: 1", - "token:", - " roles: role1,role2,role3", - " value: c00lpazzward"); + final String testXml = String.join("\n" + , "" + , " " + , " c00lpazzward" + , " " + , "" + ); - 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")); - } + 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")); } } diff --git a/src/test/java/ca/islandora/syn/settings/SitesTest.java b/src/test/java/ca/islandora/syn/settings/SitesTest.java index e102e06..22ca6da 100644 --- a/src/test/java/ca/islandora/syn/settings/SitesTest.java +++ b/src/test/java/ca/islandora/syn/settings/SitesTest.java @@ -1,8 +1,7 @@ package ca.islandora.syn.settings; -import static org.junit.Assert.assertEquals; - import org.junit.Test; +import static org.junit.Assert.assertEquals; public class SitesTest { @@ -11,7 +10,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 53c3604..5f6647a 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 static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import org.junit.Before; import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + public class TokenTest { Token token; @@ -40,11 +40,11 @@ public void testTokenRoles() { @Test public void testTokenToken() { - assertTrue(this.token.getValue().isEmpty()); + assertTrue(this.token.getToken().isEmpty()); final String testVal = "test"; - this.token.setValue(testVal); - assertEquals(testVal, this.token.getValue()); - this.token.setValue(" " + testVal); - assertEquals(testVal, this.token.getValue()); + this.token.setToken(testVal); + assertEquals(testVal, this.token.getToken()); + this.token.setToken(" " + testVal); + assertEquals(testVal, this.token.getToken()); } } diff --git a/src/test/java/ca/islandora/syn/token/VerifierTest.java b/src/test/java/ca/islandora/syn/token/VerifierTest.java index 7d519b7..9933556 100644 --- a/src/test/java/ca/islandora/syn/token/VerifierTest.java +++ b/src/test/java/ca/islandora/syn/token/VerifierTest.java @@ -35,14 +35,13 @@ public void setUp() { @Test public void testClaimsWithoutVerify() { token = JWT.create() - .withArrayClaim("roles", new String[] { "Role1", "Role2" }) - .withClaim("webid", 1) - .withClaim("sub", "admin") - .withClaim("iss", "http://test.com") + .withArrayClaim("roles", new String[]{"Role1", "Role2"}) + .withClaim("uid", 1) + .withClaim("name", "admin") + .withClaim("url", "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()); @@ -53,16 +52,17 @@ public void testClaimsWithoutVerify() { assertEquals("Role2", roles.get(1)); } - @Test(expected = InvalidTokenException.class) + @Test public void testClaimsMissing() { token = JWT.create() - .withClaim("sub", "admin") - .withClaim("iss", "http://test.com") + .withClaim("name", "admin") + .withClaim("url", "http://test.com") .sign(Algorithm.none()); - Verifier.create(token); + final Verifier verifier = Verifier.create(token); + assertNull(verifier); } - @Test(expected = InvalidTokenException.class) + @Test 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("webid", 1) - .withClaim("sub", "admin") - .withClaim("iss", "http://test.com") + .withArrayClaim("roles", new String[]{"Role1", "Role2"}) + .withClaim("uid", 1) + .withClaim("name", "admin") + .withClaim("url", "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("webid", 1) - .withClaim("sub", "admin") - .withClaim("iss", "http://test.com") + .withArrayClaim("roles", new String[]{"Role1", "Role2"}) + .withClaim("uid", 1) + .withClaim("name", "admin") + .withClaim("url", "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("webid", 1) - .withClaim("sub", "admin") - .withClaim("iss", "http://test.com") + .withArrayClaim("roles", new String[]{"Role1", "Role2"}) + .withClaim("uid", 1) + .withClaim("name", "admin") + .withClaim("url", "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 deleted file mode 100644 index 52db131..0000000 --- a/src/test/java/ca/islandora/syn/valves/SynFilterTest.java +++ /dev/null @@ -1,623 +0,0 @@ -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 new file mode 100644 index 0000000..c94d5f4 --- /dev/null +++ b/src/test/java/ca/islandora/syn/valves/SynValveTest.java @@ -0,0 +1,643 @@ +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 deleted file mode 100644 index f014962..0000000 --- a/src/test/resources/exampleSettings.yaml +++ /dev/null @@ -1,4 +0,0 @@ -version: 1 -token: - user: bob - value: bobsPassword \ No newline at end of file From e5f235e307741a777998692303ff09538e3f6c1d Mon Sep 17 00:00:00 2001 From: dannylamb Date: Wed, 28 Nov 2018 09:45:21 -0400 Subject: [PATCH 2/2] Version bump so it gets the right number this time :man_facepalming: --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 7b29848..3cfd8e1 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ apply plugin: 'jacoco' apply plugin: 'com.github.johnrengelman.shadow' def projectName = 'islandora-syn' -def projectVersion = '0.1.0' +def projectVersion = '0.3.0' sourceCompatibility = 1.8 targetCompatibility = 1.8