Skip to content

Commit

Permalink
Add a new static token type to Syn (#6)
Browse files Browse the repository at this point in the history
This commit adds the ability to set a static token in the Syn configuration file
so that you can easily use a token for testing, connecting to fedora etc.
  • Loading branch information
jonathangreen authored and ruebot committed Apr 13, 2017
1 parent 80ad06d commit 274595d
Show file tree
Hide file tree
Showing 9 changed files with 281 additions and 3 deletions.
8 changes: 8 additions & 0 deletions conf/syn-settings.example.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,12 @@ my secret key
-->
<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'>
my super secret token
</token>

</sites>
37 changes: 34 additions & 3 deletions src/main/java/ca/islandora/syn/settings/SettingsParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ private static Digester getDigester() {
digester.addSetProperties("sites/site");
digester.addCallMethod("sites/site", "setKey", 0);
digester.addSetNext("sites/site", "addSite", "ca.islandora.syn.settings.Site");
digester.addObjectCreate("sites/token", "ca.islandora.syn.settings.Token");
digester.addSetProperties("sites/token");
digester.addCallMethod("sites/token", "setToken", 0);
digester.addSetNext("sites/token", "addToken", "ca.islandora.syn.settings.Token");
}
return digester;
}
Expand Down Expand Up @@ -168,19 +172,28 @@ private static Algorithm getHmacAlgorithm(final Site site) {
}
}

public static Map<String, Algorithm> getSiteAlgorithms(final InputStream settings) {
final Map<String, Algorithm> algorithms = new HashMap<>();
private static Sites getSites(final InputStream settings) {
Sites sites;

try {
sites = getSitesObject(settings);
} catch (Exception e) {
log.error("Error loading settings file.", e);
return algorithms;
return null;
}

if (sites.getVersion() != 1) {
log.error("Incorrect XML version. Aborting.");
return null;
}

return sites;
}

public static Map<String, Algorithm> getSiteAlgorithms(final InputStream settings) {
final Map<String, Algorithm> algorithms = new HashMap<>();
final Sites sites = getSites(settings);
if (sites == null) {
return algorithms;
}

Expand Down Expand Up @@ -236,6 +249,24 @@ public static Map<String, Algorithm> getSiteAlgorithms(final InputStream setting
return algorithms;
}

public static Map<String, Token> getSiteStaticTokens(final InputStream settings) {
final Map<String, Token> tokens = new HashMap<>();
final Sites sites = getSites(settings);
if (sites == null) {
return tokens;
}

for (Token token : sites.getTokens()) {
if (token.getToken().isEmpty()) {
log.error("Static token is empty ignoring.");
} else {
tokens.put(token.getToken(), token);
}
}

return tokens;
}

static Sites getSitesObject(final InputStream settings)
throws IOException, SAXException {
return (Sites) getDigester().parse(settings);
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/ca/islandora/syn/settings/Sites.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
public class Sites {
private int version = -1;
private List<Site> sites = new ArrayList<>();
private List<Token> tokens = new ArrayList<>();

public void addSite(final Site site) {
sites.add(site);
Expand All @@ -20,4 +21,11 @@ public int getVersion() {
public void setVersion(final int version) {
this.version = version;
}

public void addToken(final Token token) {
tokens.add(token);
}
public List<Token> getTokens() {
return tokens;
}
}
39 changes: 39 additions & 0 deletions src/main/java/ca/islandora/syn/settings/Token.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package ca.islandora.syn.settings;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Token {
private String user = "islandoraAdmin";
private List<String> roles = new ArrayList<>();
private String token = "";

public String getUser() {
return user;
}

public void setUser(final String user) {
this.user = user;
}

public List<String> getRoles() {
return roles;
}

public void setRoles(final String roles) {
this.roles.clear();
if (!roles.isEmpty()) {
final String[] parts = roles.split(",");
Collections.addAll(this.roles,parts);
}
}

public String getToken() {
return token;
}

public void setToken(final String token) {
this.token = token.trim();
}
}
20 changes: 20 additions & 0 deletions src/main/java/ca/islandora/syn/valve/SynValve.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ca.islandora.syn.valve;

import ca.islandora.syn.settings.SettingsParser;
import ca.islandora.syn.settings.Token;
import ca.islandora.syn.token.Verifier;
import java.io.File;
import java.io.FileInputStream;
Expand All @@ -24,6 +25,7 @@ public class SynValve extends ValveBase {
private String pathname = "conf/syn-settings.xml";
private static final Log log = LogFactory.getLog(SynValve.class);
private Map<String, Algorithm> algorithmMap = null;
private Map<String, Token> staticTokenMap = null;

@Override
public void invoke(final Request request, final Response response)
Expand Down Expand Up @@ -64,6 +66,15 @@ private boolean doAuthentication(final Request request) {

// 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.");
Expand Down Expand Up @@ -94,6 +105,14 @@ private boolean doAuthentication(final Request request) {
}
}

private void setUserRolesFromStaticToken(final Request request, final Token token) {
final List<String> 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<String> roles = verifier.getRoles();
roles.add("islandora");
Expand Down Expand Up @@ -135,6 +154,7 @@ public synchronized void startInternal() throws LifecycleException {
// Load the contents of the database file
try {
this.algorithmMap = SettingsParser.getSiteAlgorithms(new FileInputStream(file));
this.staticTokenMap = SettingsParser.getSiteStaticTokens(new FileInputStream(file));
} catch (Exception e) {
throw new LifecycleException("Error parsing XML Configuration", e);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package ca.islandora.syn.settings;

import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class SettingsParserTokenTest {
@Test
public void testInvalidVersion() throws Exception {
final String testXml = String.join("\n"
, "<sites version='2'>"
, " <token>"
, " c00lpazzward"
, " </token>"
, "</sites>"
);

final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
final Map<String,Token> tokens = SettingsParser.getSiteStaticTokens(stream);
assertEquals(0, tokens.size());
}

@Test
public void testTokenNoParams() throws Exception {
final String testXml = String.join("\n"
, "<sites version='1'>"
, " <token>"
, " c00lpazzward"
, " </token>"
, "</sites>"
);

final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
final Map<String,Token> 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 testXml = String.join("\n"
, "<sites version='1'>"
, " <token user='denis'>"
, " c00lpazzward"
, " </token>"
, "</sites>"
);

final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
final Map<String,Token> 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 testXml = String.join("\n"
, "<sites version='1'>"
, " <token roles='role1,role2,role3'>"
, " c00lpazzward"
, " </token>"
, "</sites>"
);

final InputStream stream = new ByteArrayInputStream(testXml.getBytes());
final Map<String,Token> 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"));
}
}
10 changes: 10 additions & 0 deletions src/test/java/ca/islandora/syn/settings/SitesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,14 @@ public void TestJwtSites() {
assertEquals(1, sites.getSites().size());
assertEquals(site, sites.getSites().get(0));
}

@Test
public void TestToken() {
final Sites sites = new Sites();
final Token token = new Token();
assertEquals(0, sites.getTokens().size());
sites.addToken(token);
assertEquals(1, sites.getTokens().size());
assertEquals(token, sites.getTokens().get(0));
}
}
50 changes: 50 additions & 0 deletions src/test/java/ca/islandora/syn/settings/TokenTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package ca.islandora.syn.settings;

import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class TokenTest {
Token token;

@Before
public void initializeSite() {
this.token = new Token();
}

@Test
public void testTokenUser() {
assertEquals("islandoraAdmin", this.token.getUser());
final String testVal = "test";
this.token.setUser(testVal);
assertEquals(testVal, this.token.getUser());
}

@Test
public void testTokenRoles() {
assertEquals(0, this.token.getRoles().size());

this.token.setRoles("test");
assertEquals(1, this.token.getRoles().size());
assertEquals("test", this.token.getRoles().get(0));

this.token.setRoles("this,is,a,test");
assertEquals(4, this.token.getRoles().size());
assertEquals("this", this.token.getRoles().get(0));
assertEquals("is", this.token.getRoles().get(1));
assertEquals("a", this.token.getRoles().get(2));
assertEquals("test", this.token.getRoles().get(3));
}

@Test
public void testTokenToken() {
assertTrue(this.token.getToken().isEmpty());
final String testVal = "test";
this.token.setToken(testVal);
assertEquals(testVal, this.token.getToken());
this.token.setToken(" " + testVal);
assertEquals(testVal, this.token.getToken());
}
}
30 changes: 30 additions & 0 deletions src/test/java/ca/islandora/syn/valves/SynValveTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,33 @@ public void shouldPassAuth() throws Exception {
assertNull(argument.getValue().getPassword());
}

@Test
public void shouldPassAuthToken() throws Exception {
final ArgumentCaptor<GenericPrincipal> 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<String> 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();
Expand Down Expand Up @@ -281,6 +308,9 @@ private void createSettings(final File settingsFile) throws Exception {
, " <site algorithm='HS256' encoding='plain' default='true'>"
, "secret2"
, " </site>"
, " <token>"
, "1337"
, " </token>"
, "</sites>"
);
Files.write(Paths.get(settingsFile.getAbsolutePath()), testXml.getBytes());
Expand Down

0 comments on commit 274595d

Please sign in to comment.