From 2134be74247ef3ebdbceb5869375e18b63154a21 Mon Sep 17 00:00:00 2001 From: Mike Conway Date: Tue, 6 Aug 2019 10:28:39 -0400 Subject: [PATCH] #9 add start at a jwt support service --- irodsext-jwt-service/pom.xml | 93 +++++++++++++++++++ .../src/main/java/.gitinclude | 0 .../irodsext/jwt/JwtIssueServiceImpl.java | 67 +++++++++++++ .../jargon/irodsext/jwt/JwtServiceConfig.java | 59 ++++++++++++ .../jargon/irodsext/jwt/package-info.java | 9 ++ .../src/main/resources/.gitinclude | 0 .../src/test/java/.gitinclude | 0 .../irodsext/jwt/JwtIssueServiceImplTest.java | 47 ++++++++++ .../irodsext/jwt/unittest/AllTests.java | 12 +++ .../irodsext/jwt/unittest/package-info.java | 10 ++ .../src/test/resources/log4j.properties | 11 +++ pom.xml | 1 + 12 files changed, 309 insertions(+) create mode 100755 irodsext-jwt-service/pom.xml create mode 100755 irodsext-jwt-service/src/main/java/.gitinclude create mode 100644 irodsext-jwt-service/src/main/java/org/irods/jargon/irodsext/jwt/JwtIssueServiceImpl.java create mode 100644 irodsext-jwt-service/src/main/java/org/irods/jargon/irodsext/jwt/JwtServiceConfig.java create mode 100644 irodsext-jwt-service/src/main/java/org/irods/jargon/irodsext/jwt/package-info.java create mode 100755 irodsext-jwt-service/src/main/resources/.gitinclude create mode 100755 irodsext-jwt-service/src/test/java/.gitinclude create mode 100644 irodsext-jwt-service/src/test/java/org/irods/jargon/irodsext/jwt/JwtIssueServiceImplTest.java create mode 100644 irodsext-jwt-service/src/test/java/org/irods/jargon/irodsext/jwt/unittest/AllTests.java create mode 100644 irodsext-jwt-service/src/test/java/org/irods/jargon/irodsext/jwt/unittest/package-info.java create mode 100755 irodsext-jwt-service/src/test/resources/log4j.properties diff --git a/irodsext-jwt-service/pom.xml b/irodsext-jwt-service/pom.xml new file mode 100755 index 0000000..155843c --- /dev/null +++ b/irodsext-jwt-service/pom.xml @@ -0,0 +1,93 @@ + + + 4.0.0 + + jargon-irods-ext + org.irods.jargon + 4.3.1.0-SNAPSHOT + + irodsext-jwt-service + irodsext-jwt-service + + + junit + junit + compile + + + io.jsonwebtoken + jjwt-api + + + io.jsonwebtoken + jjwt-impl + runtime + + + io.jsonwebtoken + jjwt-jackson + runtime + + + Tools for managing jwts used by associated microservices + + + + maven-antrun-plugin + + + 0 + validate + + + + + + test.confirm=${jargon.test.confirm} + test.data.directory=${jargon.test.data.directory} + test.irods.admin=${jargon.test.irods.admin} + test.irods.admin.password=${jargon.test.irods.admin.password} + test.irods.user=${jargon.test.irods.user} + test.irods.password=${jargon.test.irods.password} + test.irods.resource=${jargon.test.irods.resource} + test2.irods.user=${jargon.test.irods.user2} + test2.irods.password=${jargon.test.irods.password2} + test2.irods.resource=${jargon.test.irods.resource2} + test3.irods.user=${jargon.test.irods.user3} + test3.irods.password=${jargon.test.irods.password3} + test3.irods.resource=${jargon.test.irods.resource3} + test.irods.host=${jargon.test.irods.host} + test.irods.port=${jargon.test.irods.port} + test.irods.zone=${jargon.test.irods.zone} + test.resource.group=${jargon.test.resource.group} + test.irods.userDN=${jargon.test.irods.userDN} + test.irods.scratch.subdir=${jargon.test.irods.scratch.subdir} + + + + + run + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + true + + + + + + diff --git a/irodsext-jwt-service/src/main/java/.gitinclude b/irodsext-jwt-service/src/main/java/.gitinclude new file mode 100755 index 0000000..e69de29 diff --git a/irodsext-jwt-service/src/main/java/org/irods/jargon/irodsext/jwt/JwtIssueServiceImpl.java b/irodsext-jwt-service/src/main/java/org/irods/jargon/irodsext/jwt/JwtIssueServiceImpl.java new file mode 100644 index 0000000..36b0c05 --- /dev/null +++ b/irodsext-jwt-service/src/main/java/org/irods/jargon/irodsext/jwt/JwtIssueServiceImpl.java @@ -0,0 +1,67 @@ +/** + * + */ +package org.irods.jargon.irodsext.jwt; + +import java.security.Key; +import java.util.Date; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; + +/** + * @author Mike Conway - NIEHS + * + */ +public class JwtIssueServiceImpl { + + public static final Logger log = LoggerFactory.getLogger(JwtIssueServiceImpl.class); + + private final JwtServiceConfig jwtServiceConfig; + private final Key myKey; + + /** + * Constructor with configs + * + * @param jwtServiceConfig {@link JwtServiceConfig} + */ + public JwtIssueServiceImpl(final JwtServiceConfig jwtServiceConfig) { + if (jwtServiceConfig == null) { + throw new IllegalArgumentException("null jwtServiceConfig"); + } + + this.jwtServiceConfig = jwtServiceConfig; + myKey = Keys.hmacShaKeyFor(jwtServiceConfig.getSecret().getBytes()); + } + + public String issueJwtToken(final String subject) { + log.info("issueJwtToken()"); + + if (subject == null || subject.isEmpty()) { + throw new IllegalArgumentException("null or empty subject"); + } + + String signedJwt = Jwts.builder().setSubject(subject).setIssuer(jwtServiceConfig.getIssuer()) + .setIssuedAt(new Date()).signWith(myKey).compact(); + return signedJwt; + + } + + public Jws decodeJwtToken(final String token) { + log.info("decodeJwtToken()"); + + if (token == null || token.isEmpty()) { + throw new IllegalArgumentException("null or empty token"); + } + + Jws claims = Jwts.parser().setSigningKey(myKey).parseClaimsJws(token); + return claims; + + } + +} diff --git a/irodsext-jwt-service/src/main/java/org/irods/jargon/irodsext/jwt/JwtServiceConfig.java b/irodsext-jwt-service/src/main/java/org/irods/jargon/irodsext/jwt/JwtServiceConfig.java new file mode 100644 index 0000000..89f3b8b --- /dev/null +++ b/irodsext-jwt-service/src/main/java/org/irods/jargon/irodsext/jwt/JwtServiceConfig.java @@ -0,0 +1,59 @@ +/** + * + */ +package org.irods.jargon.irodsext.jwt; + +/** + * Basic configs for a service to issue and decode jwts used in iRODS + * microservices + * + * @author Mike Conway - NIEHS + * + */ +public class JwtServiceConfig { + + /** + * Issuer typically in reverse dns name format, used as "iss" in the JWT + */ + private String issuer = ""; + /** + * Secret used to sign tokens given the provided algo + */ + private String secret = ""; + /** + * Signing algo used in JWT + */ + private String algo = ""; + + public String getIssuer() { + return issuer; + } + + public void setIssuer(String issuer) { + this.issuer = issuer; + } + + public String getSecret() { + return secret; + } + + public void setSecret(String secret) { + this.secret = secret; + } + + public String getAlgo() { + return algo; + } + + public void setAlgo(String algo) { + this.algo = algo; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("JwtServiceConfig [issuer=").append(issuer).append(", algo=").append(algo).append("]"); + return builder.toString(); + } + +} diff --git a/irodsext-jwt-service/src/main/java/org/irods/jargon/irodsext/jwt/package-info.java b/irodsext-jwt-service/src/main/java/org/irods/jargon/irodsext/jwt/package-info.java new file mode 100644 index 0000000..211f488 --- /dev/null +++ b/irodsext-jwt-service/src/main/java/org/irods/jargon/irodsext/jwt/package-info.java @@ -0,0 +1,9 @@ + +/** + * Utils and services to support use of JWT tokens in Jargon extensions and + * microservices + * + * @author Mike Conway - NIEHS + * + */ +package org.irods.jargon.irodsext.jwt; \ No newline at end of file diff --git a/irodsext-jwt-service/src/main/resources/.gitinclude b/irodsext-jwt-service/src/main/resources/.gitinclude new file mode 100755 index 0000000..e69de29 diff --git a/irodsext-jwt-service/src/test/java/.gitinclude b/irodsext-jwt-service/src/test/java/.gitinclude new file mode 100755 index 0000000..e69de29 diff --git a/irodsext-jwt-service/src/test/java/org/irods/jargon/irodsext/jwt/JwtIssueServiceImplTest.java b/irodsext-jwt-service/src/test/java/org/irods/jargon/irodsext/jwt/JwtIssueServiceImplTest.java new file mode 100644 index 0000000..7b4d1b1 --- /dev/null +++ b/irodsext-jwt-service/src/test/java/org/irods/jargon/irodsext/jwt/JwtIssueServiceImplTest.java @@ -0,0 +1,47 @@ +package org.irods.jargon.irodsext.jwt; + +import org.junit.Assert; +import org.junit.Test; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.WeakKeyException; + +public class JwtIssueServiceImplTest { + + @Test(expected = WeakKeyException.class) + public void testIssueJwtWeakKey() { + JwtServiceConfig config = new JwtServiceConfig(); + config.setAlgo(SignatureAlgorithm.HS256.getValue()); + config.setIssuer("test"); + config.setSecret("thisisasecret"); + JwtIssueServiceImpl jwtIssueServiceImpl = new JwtIssueServiceImpl(config); + jwtIssueServiceImpl.issueJwtToken("subject"); + } + + @Test + public void testIssueJwt() { + JwtServiceConfig config = new JwtServiceConfig(); + config.setAlgo(SignatureAlgorithm.HS256.getValue()); + config.setIssuer("test"); + config.setSecret("thisisasecretthatisverysecretyouwillneverguessthiskey"); + JwtIssueServiceImpl jwtIssueServiceImpl = new JwtIssueServiceImpl(config); + String jwt = jwtIssueServiceImpl.issueJwtToken("subject"); + Assert.assertNotNull("no jwt issued", jwt); + } + + @Test + public void testIssueAndDecodeJwt() { + JwtServiceConfig config = new JwtServiceConfig(); + config.setAlgo(SignatureAlgorithm.HS256.getValue()); + config.setIssuer("test"); + config.setSecret("thisisasecretthatisverysecretyouwillneverguessthiskeyhurray"); + JwtIssueServiceImpl jwtIssueServiceImpl = new JwtIssueServiceImpl(config); + String jwt = jwtIssueServiceImpl.issueJwtToken("subject"); + Jws actual = jwtIssueServiceImpl.decodeJwtToken(jwt); + Assert.assertNotNull("claims not returned", actual); + + } + +} diff --git a/irodsext-jwt-service/src/test/java/org/irods/jargon/irodsext/jwt/unittest/AllTests.java b/irodsext-jwt-service/src/test/java/org/irods/jargon/irodsext/jwt/unittest/AllTests.java new file mode 100644 index 0000000..a1770f5 --- /dev/null +++ b/irodsext-jwt-service/src/test/java/org/irods/jargon/irodsext/jwt/unittest/AllTests.java @@ -0,0 +1,12 @@ +package org.irods.jargon.irodsext.jwt.unittest; + +import org.irods.jargon.irodsext.jwt.JwtIssueServiceImplTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +@RunWith(Suite.class) +@SuiteClasses({ JwtIssueServiceImplTest.class }) +public class AllTests { + +} diff --git a/irodsext-jwt-service/src/test/java/org/irods/jargon/irodsext/jwt/unittest/package-info.java b/irodsext-jwt-service/src/test/java/org/irods/jargon/irodsext/jwt/unittest/package-info.java new file mode 100644 index 0000000..ad82a96 --- /dev/null +++ b/irodsext-jwt-service/src/test/java/org/irods/jargon/irodsext/jwt/unittest/package-info.java @@ -0,0 +1,10 @@ +/** + * + */ +/** + * Suites and test utils + * + * @author Mike Conway - NIEHS + * + */ +package org.irods.jargon.irodsext.jwt.unittest; \ No newline at end of file diff --git a/irodsext-jwt-service/src/test/resources/log4j.properties b/irodsext-jwt-service/src/test/resources/log4j.properties new file mode 100755 index 0000000..eed7b65 --- /dev/null +++ b/irodsext-jwt-service/src/test/resources/log4j.properties @@ -0,0 +1,11 @@ +# Set root logger level to DEBUG and its only appender to A1. +#log4j.rootLogger=ERROR, A1 +log4j.category.org.irods.jargon.core=INFO, A1 +log4j.category.org.irods.jargon.datautils=INFO, A1 + +# A1 is set to be a ConsoleAppender. +log4j.appender.A1=org.apache.log4j.ConsoleAppender + +# A1 uses PatternLayout. +log4j.appender.A1.layout=org.apache.log4j.PatternLayout +log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n diff --git a/pom.xml b/pom.xml index 2ea0ce6..b58e271 100755 --- a/pom.xml +++ b/pom.xml @@ -214,5 +214,6 @@ irodsext-data-typer emc-metalnx-core emc-metalnx-services + irodsext-jwt-service