You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2017/03/15 10:01:55 UTC
[09/22] james-project git commit: JAMES-1959 Extract JWT to an other
maven project
JAMES-1959 Extract JWT to an other maven project
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/f1a087fa
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/f1a087fa
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/f1a087fa
Branch: refs/heads/master
Commit: f1a087fa60217383969f2a281703f8cf0e0d8efb
Parents: 3ac2368
Author: benwa <bt...@linagora.com>
Authored: Wed Mar 8 17:25:54 2017 +0700
Committer: benwa <bt...@linagora.com>
Committed: Wed Mar 15 09:01:53 2017 +0700
----------------------------------------------------------------------
.../java/org/apache/james/jmap/JMAPModule.java | 7 +
server/pom.xml | 6 +
server/protocols/jmap/pom.xml | 4 +
.../james/jmap/JWTAuthenticationStrategy.java | 2 +-
.../james/jmap/crypto/JwtTokenVerifier.java | 63 -----
.../crypto/MissingOrInvalidKeyException.java | 22 --
.../james/jmap/crypto/PublicKeyProvider.java | 44 ----
.../james/jmap/crypto/PublicKeyReader.java | 56 -----
.../jmap/JWTAuthenticationStrategyTest.java | 2 +-
.../james/jmap/crypto/JwtTokenVerifierTest.java | 124 ----------
.../jmap/crypto/PublicKeyProviderTest.java | 74 ------
.../james/jmap/crypto/PublicKeyReaderTest.java | 62 -----
server/protocols/jwt/pom.xml | 245 +++++++++++++++++++
.../org/apache/james/jwt/JwtConfiguration.java | 34 +++
.../org/apache/james/jwt/JwtTokenVerifier.java | 63 +++++
.../james/jwt/MissingOrInvalidKeyException.java | 22 ++
.../org/apache/james/jwt/PublicKeyProvider.java | 44 ++++
.../org/apache/james/jwt/PublicKeyReader.java | 56 +++++
.../apache/james/jwt/JwtTokenVerifierTest.java | 119 +++++++++
.../apache/james/jwt/PublicKeyProviderTest.java | 67 +++++
.../apache/james/jwt/PublicKeyReaderTest.java | 62 +++++
21 files changed, 731 insertions(+), 447 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java
----------------------------------------------------------------------
diff --git a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java
index 7269cb1..eca11aa 100644
--- a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java
+++ b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java
@@ -34,6 +34,7 @@ import org.apache.james.jmap.utils.HtmlTextExtractor;
import org.apache.james.jmap.utils.MailboxBasedHtmlTextExtractor;
import org.apache.james.jmap.utils.SystemMailboxesProvider;
import org.apache.james.jmap.utils.SystemMailboxesProviderImpl;
+import org.apache.james.jwt.JwtConfiguration;
import org.apache.james.lifecycle.api.Configurable;
import org.apache.james.mailbox.MailboxManager;
import org.apache.james.mailbox.MailboxManager.SearchCapabilities;
@@ -89,6 +90,12 @@ public class JMAPModule extends AbstractModule {
.build();
}
+ @Provides
+ @Singleton
+ JwtConfiguration providesJwtConfiguration(JMAPConfiguration jmapConfiguration) {
+ return new JwtConfiguration(jmapConfiguration.getJwtPublicKeyPem());
+ }
+
private Optional<String> loadPublicKey(FileSystem fileSystem, Optional<String> jwtPublickeyPemUrl) {
return jwtPublickeyPemUrl.map(Throwing.function(url -> FileUtils.readFileToString(fileSystem.getFile(url))));
}
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/pom.xml
----------------------------------------------------------------------
diff --git a/server/pom.xml b/server/pom.xml
index 4ceaa15..4b7d969 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -109,6 +109,7 @@
<module>protocols/webadmin-integration-test</module>
<module>testing</module>
+ <module>protocols/jwt</module>
</modules>
<distributionManagement>
@@ -807,6 +808,11 @@
</dependency>
<dependency>
<groupId>org.apache.james</groupId>
+ <artifactId>james-server-jwt</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.james</groupId>
<artifactId>james-server-protocols-lmtp</artifactId>
<version>${project.version}</version>
</dependency>
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jmap/pom.xml
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/pom.xml b/server/protocols/jmap/pom.xml
index 152dc79..287e428 100644
--- a/server/protocols/jmap/pom.xml
+++ b/server/protocols/jmap/pom.xml
@@ -217,6 +217,10 @@
</dependency>
<dependency>
<groupId>org.apache.james</groupId>
+ <artifactId>james-server-jwt</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.james</groupId>
<artifactId>james-server-lifecycle-api</artifactId>
</dependency>
<dependency>
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jmap/src/main/java/org/apache/james/jmap/JWTAuthenticationStrategy.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/JWTAuthenticationStrategy.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/JWTAuthenticationStrategy.java
index a7e91be..9f4f609 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/JWTAuthenticationStrategy.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/JWTAuthenticationStrategy.java
@@ -23,10 +23,10 @@ import java.util.stream.Stream;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
-import org.apache.james.jmap.crypto.JwtTokenVerifier;
import org.apache.james.jmap.exceptions.MailboxSessionCreationException;
import org.apache.james.jmap.exceptions.NoValidAuthHeaderException;
import org.apache.james.jmap.utils.HeadersAuthenticationExtractor;
+import org.apache.james.jwt.JwtTokenVerifier;
import org.apache.james.mailbox.MailboxManager;
import org.apache.james.mailbox.MailboxSession;
import org.apache.james.mailbox.exception.MailboxException;
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/JwtTokenVerifier.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/JwtTokenVerifier.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/JwtTokenVerifier.java
deleted file mode 100644
index dea68af..0000000
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/JwtTokenVerifier.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one *
- * or more contributor license agreements. See the NOTICE file *
- * distributed with this work for additional information *
- * regarding copyright ownership. The ASF licenses this file *
- * to you under the Apache License, Version 2.0 (the *
- * "License"); you may not use this file except in compliance *
- * with the License. You may obtain a copy of the License at *
- * *
- * http://www.apache.org/licenses/LICENSE-2.0 *
- * *
- * Unless required by applicable law or agreed to in writing, *
- * software distributed under the License is distributed on an *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
- * KIND, either express or implied. See the License for the *
- * specific language governing permissions and limitations *
- * under the License. *
- ****************************************************************/
-package org.apache.james.jmap.crypto;
-
-import javax.inject.Inject;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Strings;
-
-import io.jsonwebtoken.Claims;
-import io.jsonwebtoken.Jws;
-import io.jsonwebtoken.JwtException;
-import io.jsonwebtoken.Jwts;
-import io.jsonwebtoken.MalformedJwtException;
-
-public class JwtTokenVerifier {
-
- private final PublicKeyProvider pubKeyProvider;
-
- @Inject
- @VisibleForTesting
- JwtTokenVerifier(PublicKeyProvider pubKeyProvider) {
- this.pubKeyProvider = pubKeyProvider;
- }
-
- public boolean verify(String token) throws JwtException {
- String subject = extractLogin(token);
- if (Strings.isNullOrEmpty(subject)) {
- throw new MalformedJwtException("'subject' field in token is mandatory");
- }
- return true;
- }
-
- public String extractLogin(String token) throws JwtException {
- Jws<Claims> jws = parseToken(token);
- return jws
- .getBody()
- .getSubject();
- }
-
- private Jws<Claims> parseToken(String token) throws JwtException {
- return Jwts
- .parser()
- .setSigningKey(pubKeyProvider.get())
- .parseClaimsJws(token);
- }
-}
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/MissingOrInvalidKeyException.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/MissingOrInvalidKeyException.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/MissingOrInvalidKeyException.java
deleted file mode 100644
index a98b7e3..0000000
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/MissingOrInvalidKeyException.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one *
- * or more contributor license agreements. See the NOTICE file *
- * distributed with this work for additional information *
- * regarding copyright ownership. The ASF licenses this file *
- * to you under the Apache License, Version 2.0 (the *
- * "License"); you may not use this file except in compliance *
- * with the License. You may obtain a copy of the License at *
- * *
- * http://www.apache.org/licenses/LICENSE-2.0 *
- * *
- * Unless required by applicable law or agreed to in writing, *
- * software distributed under the License is distributed on an *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
- * KIND, either express or implied. See the License for the *
- * specific language governing permissions and limitations *
- * under the License. *
- ****************************************************************/
-package org.apache.james.jmap.crypto;
-
-public class MissingOrInvalidKeyException extends RuntimeException {
-}
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/PublicKeyProvider.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/PublicKeyProvider.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/PublicKeyProvider.java
deleted file mode 100644
index e88ffba..0000000
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/PublicKeyProvider.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one *
- * or more contributor license agreements. See the NOTICE file *
- * distributed with this work for additional information *
- * regarding copyright ownership. The ASF licenses this file *
- * to you under the Apache License, Version 2.0 (the *
- * "License"); you may not use this file except in compliance *
- * with the License. You may obtain a copy of the License at *
- * *
- * http://www.apache.org/licenses/LICENSE-2.0 *
- * *
- * Unless required by applicable law or agreed to in writing, *
- * software distributed under the License is distributed on an *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
- * KIND, either express or implied. See the License for the *
- * specific language governing permissions and limitations *
- * under the License. *
- ****************************************************************/
-package org.apache.james.jmap.crypto;
-
-import com.google.common.annotations.VisibleForTesting;
-import org.apache.james.jmap.JMAPConfiguration;
-
-import javax.inject.Inject;
-import java.security.PublicKey;
-
-public class PublicKeyProvider {
-
- private final JMAPConfiguration config;
- private final PublicKeyReader reader;
-
- @Inject
- @VisibleForTesting
- PublicKeyProvider(JMAPConfiguration config, PublicKeyReader reader) {
- this.config = config;
- this.reader = reader;
- }
-
- public PublicKey get() throws MissingOrInvalidKeyException {
- return reader.fromPEM(config.getJwtPublicKeyPem())
- .orElseThrow(() -> new MissingOrInvalidKeyException());
- }
-
-}
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/PublicKeyReader.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/PublicKeyReader.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/PublicKeyReader.java
deleted file mode 100644
index b695c26..0000000
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/crypto/PublicKeyReader.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one *
- * or more contributor license agreements. See the NOTICE file *
- * distributed with this work for additional information *
- * regarding copyright ownership. The ASF licenses this file *
- * to you under the Apache License, Version 2.0 (the *
- * "License"); you may not use this file except in compliance *
- * with the License. You may obtain a copy of the License at *
- * *
- * http://www.apache.org/licenses/LICENSE-2.0 *
- * *
- * Unless required by applicable law or agreed to in writing, *
- * software distributed under the License is distributed on an *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
- * KIND, either express or implied. See the License for the *
- * specific language governing permissions and limitations *
- * under the License. *
- ****************************************************************/
-package org.apache.james.jmap.crypto;
-
-import java.io.IOException;
-import java.io.StringReader;
-import java.security.PublicKey;
-import java.util.Optional;
-
-import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.openssl.PEMParser;
-import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
-import org.bouncycastle.util.io.pem.PemReader;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class PublicKeyReader {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(PublicKeyReader.class);
-
- Optional<PublicKey> fromPEM(Optional<String> pemKey) {
- return pemKey
- .map(k -> new PEMParser(new PemReader(new StringReader(k))))
- .flatMap(this::publicKeyFrom);
- }
-
- private Optional<PublicKey> publicKeyFrom(PEMParser reader) {
- try {
- Object readPEM = reader.readObject();
- if (readPEM instanceof SubjectPublicKeyInfo) {
- return Optional.of(new JcaPEMKeyConverter().getPublicKey((SubjectPublicKeyInfo) readPEM));
- }
- LOGGER.warn("Key is not an instance of SubjectPublicKeyInfo but of " + readPEM);
- return Optional.empty();
- } catch (IOException e) {
- LOGGER.warn("Error when reading the PEM file", e);
- return Optional.empty();
- }
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jmap/src/test/java/org/apache/james/jmap/JWTAuthenticationStrategyTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/JWTAuthenticationStrategyTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/JWTAuthenticationStrategyTest.java
index 4a60578..530fac4 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/JWTAuthenticationStrategyTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/JWTAuthenticationStrategyTest.java
@@ -29,10 +29,10 @@ import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest;
-import org.apache.james.jmap.crypto.JwtTokenVerifier;
import org.apache.james.jmap.exceptions.MailboxSessionCreationException;
import org.apache.james.jmap.exceptions.NoValidAuthHeaderException;
import org.apache.james.jmap.utils.HeadersAuthenticationExtractor;
+import org.apache.james.jwt.JwtTokenVerifier;
import org.apache.james.mailbox.MailboxManager;
import org.apache.james.mailbox.MailboxSession;
import org.apache.james.mailbox.exception.MailboxException;
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/JwtTokenVerifierTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/JwtTokenVerifierTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/JwtTokenVerifierTest.java
deleted file mode 100644
index e3fa5bd..0000000
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/JwtTokenVerifierTest.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one *
- * or more contributor license agreements. See the NOTICE file *
- * distributed with this work for additional information *
- * regarding copyright ownership. The ASF licenses this file *
- * to you under the Apache License, Version 2.0 (the *
- * "License"); you may not use this file except in compliance *
- * with the License. You may obtain a copy of the License at *
- * *
- * http://www.apache.org/licenses/LICENSE-2.0 *
- * *
- * Unless required by applicable law or agreed to in writing, *
- * software distributed under the License is distributed on an *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
- * KIND, either express or implied. See the License for the *
- * specific language governing permissions and limitations *
- * under the License. *
- ****************************************************************/
-package org.apache.james.jmap.crypto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.security.Security;
-import java.util.Optional;
-
-import org.apache.james.jmap.JMAPConfiguration;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import io.jsonwebtoken.MalformedJwtException;
-import io.jsonwebtoken.SignatureException;
-
-public class JwtTokenVerifierTest {
-
- private static final String PUBLIC_PEM_KEY = "-----BEGIN PUBLIC KEY-----\n" +
- "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtlChO/nlVP27MpdkG0Bh\n" +
- "16XrMRf6M4NeyGa7j5+1UKm42IKUf3lM28oe82MqIIRyvskPc11NuzSor8HmvH8H\n" +
- "lhDs5DyJtx2qp35AT0zCqfwlaDnlDc/QDlZv1CoRZGpQk1Inyh6SbZwYpxxwh0fi\n" +
- "+d/4RpE3LBVo8wgOaXPylOlHxsDizfkL8QwXItyakBfMO6jWQRrj7/9WDhGf4Hi+\n" +
- "GQur1tPGZDl9mvCoRHjFrD5M/yypIPlfMGWFVEvV5jClNMLAQ9bYFuOc7H1fEWw6\n" +
- "U1LZUUbJW9/CH45YXz82CYqkrfbnQxqRb2iVbVjs/sHopHd1NTiCfUtwvcYJiBVj\n" +
- "kwIDAQAB\n" +
- "-----END PUBLIC KEY-----";
-
- private static final String VALID_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.T04BTk" +
- "LXkJj24coSZkK13RfG25lpvmSl2MJ7N10KpBk9_-95EGYZdog-BDAn3PJzqVw52z-Bwjh4VOj1-j7cURu0cT4jXehhUrlCxS4n7QHZD" +
- "N_bsEYGu7KzjWTpTsUiHe-rN7izXVFxDGG1TGwlmBCBnPW-EFCf9ylUsJi0r2BKNdaaPRfMIrHptH1zJBkkUziWpBN1RNLjmvlAUf49" +
- "t1Tbv21ZqYM5Ht2vrhJWczFbuC-TD-8zJkXhjTmA1GVgomIX5dx1cH-dZX1wANNmshUJGHgepWlPU-5VIYxPEhb219RMLJIELMY2qN" +
- "OR8Q31ydinyqzXvCSzVJOf6T60-w";
-
- private JwtTokenVerifier sut;
-
- @BeforeClass
- public static void init() {
- Security.addProvider(new BouncyCastleProvider());
- }
-
- @Before
- public void setup() {
- PublicKeyProvider pubKeyProvider = new PublicKeyProvider(getJWTConfiguration(), new PublicKeyReader());
- sut = new JwtTokenVerifier(pubKeyProvider);
- }
-
- private JMAPConfiguration getJWTConfiguration() {
-
- return JMAPConfiguration.builder()
- .keystore(".")
- .secret(".")
- .jwtPublicKeyPem(Optional.ofNullable(PUBLIC_PEM_KEY))
- .build();
- }
-
- @Test
- public void shouldReturnTrueOnValidSignature() {
-
- assertThat(sut.verify(VALID_TOKEN)).isTrue();
- }
-
- @Test
- public void shouldThrowOnMismatchingSigningKey() {
- String invalidToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.Pd6t82" +
- "tPL3EZdkeYxw_DV2KimE1U2FvuLHmfR_mimJ5US3JFU4J2Gd94O7rwpSTGN1B9h-_lsTebo4ua4xHsTtmczZ9xa8a_kWKaSkqFjNFa" +
- "Fp6zcoD6ivCu03SlRqsQzSRHXo6TKbnqOt9D6Y2rNa3C4igSwoS0jUE4BgpXbc0";
-
- assertThatThrownBy(() -> sut.verify(invalidToken))
- .isInstanceOf(SignatureException.class);
- }
-
- @Test
- public void verifyShouldThrowWhenSubjectIsNull() {
- String tokenWithNullSubject = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOm51bGwsIm5hbWUiOiJKb2huIERvZSJ9.EB" +
- "_1grWDy_kFelXs3AQeiP13ay4eG_134dWB9XPRSeWsuPs8Mz2UY-VHDxLGD-fAqv-xKXr4QFEnS7iZkdpe0tPLNSwIjqeqkC6KqQln" +
- "oC1okqWVWBDOcf7Acp1Jzp_cFTUhL5LkHvZDsyCdq5T9OOVVkzO4A9RrzIUsTrYPtRCBuYJ3ggR33cKpw191PulPGNH70rZqpUfDXe" +
- "VPY3q15vWzZH9O9IJzB2KdHRMPxl2luRjzDbh4DLp56NhZuLX_2a9UAlmbV8MQX4Z_04ybhAYrcBfxR3MgJyr0jlxSibqSbXrkXuo-" +
- "PyybfZCIhK_qXUlO5OS6sO7AQhKZO9p0MQ";
-
- assertThatThrownBy(() -> sut.verify(tokenWithNullSubject))
- .isInstanceOf(MalformedJwtException.class)
- .hasMessage("'subject' field in token is mandatory");
- }
-
- @Test
- public void verifyShouldThrowWhenEmptySubject() {
- String tokenWithEmptySubject = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIiLCJuYW1lIjoiSm9obiBEb2UifQ.UdY" +
- "s2PPzFCegUYspoDCnlJR_bJm8_z1InOv4v3tq8SJETQUarOXlhb_n6y6ujVvmJiSx9dI24Hc3Czi3RGUOXbnBDj1WPfd0aVSiUSqZr" +
- "MCHBt5vjCYqAseDaP3w4aiiFb6EV3tteJFeBLZx8XlKPYxlzRLLUADDyDSQvrFBBPxfsvCETZovKdo9ofIN64o-yx23ss63yE6oIOd" +
- "zJZ1Id40KSR2d7l3kIQJPLKUWJDnro5RAh4DOGOWNSq0JSbMhk7Zn3cXIBUpv3R8p79tui1UQpzwHMC0e6OSuWEDNQHtq-Cz85u8GG" +
- "sUSbogmgObA_BimNtUq_Q1p0SGtIYBXmQ";
-
- assertThatThrownBy(() -> sut.verify(tokenWithEmptySubject))
- .isInstanceOf(MalformedJwtException.class)
- .hasMessage("'subject' field in token is mandatory");
- }
-
- @Test
- public void shouldReturnUserLoginFromValidToken() {
-
- assertThat(sut.extractLogin(VALID_TOKEN)).isEqualTo("1234567890");
- }
-
-}
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/PublicKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/PublicKeyProviderTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/PublicKeyProviderTest.java
deleted file mode 100644
index 5db7d7b..0000000
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/PublicKeyProviderTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one *
- * or more contributor license agreements. See the NOTICE file *
- * distributed with this work for additional information *
- * regarding copyright ownership. The ASF licenses this file *
- * to you under the Apache License, Version 2.0 (the *
- * "License"); you may not use this file except in compliance *
- * with the License. You may obtain a copy of the License at *
- * *
- * http://www.apache.org/licenses/LICENSE-2.0 *
- * *
- * Unless required by applicable law or agreed to in writing, *
- * software distributed under the License is distributed on an *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
- * KIND, either express or implied. See the License for the *
- * specific language governing permissions and limitations *
- * under the License. *
- ****************************************************************/
-package org.apache.james.jmap.crypto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-import java.security.Security;
-import java.security.interfaces.RSAPublicKey;
-import java.util.Optional;
-
-import org.apache.james.jmap.JMAPConfiguration;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-public class PublicKeyProviderTest {
-
- private static final String PUBLIC_PEM_KEY = "-----BEGIN PUBLIC KEY-----\n" +
- "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtlChO/nlVP27MpdkG0Bh\n" +
- "16XrMRf6M4NeyGa7j5+1UKm42IKUf3lM28oe82MqIIRyvskPc11NuzSor8HmvH8H\n" +
- "lhDs5DyJtx2qp35AT0zCqfwlaDnlDc/QDlZv1CoRZGpQk1Inyh6SbZwYpxxwh0fi\n" +
- "+d/4RpE3LBVo8wgOaXPylOlHxsDizfkL8QwXItyakBfMO6jWQRrj7/9WDhGf4Hi+\n" +
- "GQur1tPGZDl9mvCoRHjFrD5M/yypIPlfMGWFVEvV5jClNMLAQ9bYFuOc7H1fEWw6\n" +
- "U1LZUUbJW9/CH45YXz82CYqkrfbnQxqRb2iVbVjs/sHopHd1NTiCfUtwvcYJiBVj\n" +
- "kwIDAQAB\n" +
- "-----END PUBLIC KEY-----";
-
- @BeforeClass
- public static void init() {
- Security.addProvider(new BouncyCastleProvider());
- }
-
- @Test
- public void getShouldNotThrowWhenPEMKeyProvided() {
-
- JMAPConfiguration configWithPEMKey = JMAPConfiguration.builder()
- .jwtPublicKeyPem(Optional.ofNullable(PUBLIC_PEM_KEY))
- .keystore(".").secret(".")
- .build();
-
- PublicKeyProvider sut = new PublicKeyProvider(configWithPEMKey, new PublicKeyReader());
-
- assertThat(sut.get()).isInstanceOf(RSAPublicKey.class);
- }
-
- @Test
- public void getShouldThrowWhenPEMKeyNotProvided() {
- JMAPConfiguration configWithPEMKey = JMAPConfiguration.builder()
- .jwtPublicKeyPem(Optional.ofNullable(""))
- .keystore(" ").secret(" ")
- .build();
-
- PublicKeyProvider sut = new PublicKeyProvider(configWithPEMKey, new PublicKeyReader());
-
- assertThatThrownBy(() -> sut.get()).isExactlyInstanceOf(MissingOrInvalidKeyException.class);
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/PublicKeyReaderTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/PublicKeyReaderTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/PublicKeyReaderTest.java
deleted file mode 100644
index 2765282..0000000
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/crypto/PublicKeyReaderTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one *
- * or more contributor license agreements. See the NOTICE file *
- * distributed with this work for additional information *
- * regarding copyright ownership. The ASF licenses this file *
- * to you under the Apache License, Version 2.0 (the *
- * "License"); you may not use this file except in compliance *
- * with the License. You may obtain a copy of the License at *
- * *
- * http://www.apache.org/licenses/LICENSE-2.0 *
- * *
- * Unless required by applicable law or agreed to in writing, *
- * software distributed under the License is distributed on an *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
- * KIND, either express or implied. See the License for the *
- * specific language governing permissions and limitations *
- * under the License. *
- ****************************************************************/
-
-package org.apache.james.jmap.crypto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.security.Security;
-import java.util.Optional;
-
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-public class PublicKeyReaderTest {
-
- private static final String PUBLIC_PEM_KEY = "-----BEGIN PUBLIC KEY-----\n" +
- "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtlChO/nlVP27MpdkG0Bh\n" +
- "16XrMRf6M4NeyGa7j5+1UKm42IKUf3lM28oe82MqIIRyvskPc11NuzSor8HmvH8H\n" +
- "lhDs5DyJtx2qp35AT0zCqfwlaDnlDc/QDlZv1CoRZGpQk1Inyh6SbZwYpxxwh0fi\n" +
- "+d/4RpE3LBVo8wgOaXPylOlHxsDizfkL8QwXItyakBfMO6jWQRrj7/9WDhGf4Hi+\n" +
- "GQur1tPGZDl9mvCoRHjFrD5M/yypIPlfMGWFVEvV5jClNMLAQ9bYFuOc7H1fEWw6\n" +
- "U1LZUUbJW9/CH45YXz82CYqkrfbnQxqRb2iVbVjs/sHopHd1NTiCfUtwvcYJiBVj\n" +
- "kwIDAQAB\n" +
- "-----END PUBLIC KEY-----";
-
- @BeforeClass
- public static void init() {
- Security.addProvider(new BouncyCastleProvider());
- }
-
- @Test
- public void fromPEMShouldReturnEmptyWhenEmptyProvided() {
- assertThat(new PublicKeyReader().fromPEM(Optional.empty())).isEmpty();
- }
-
- @Test
- public void fromPEMShouldReturnEmptyWhenInvalidPEMKey() {
- assertThat(new PublicKeyReader().fromPEM(Optional.of("blabla"))).isEmpty();
- }
-
- @Test
- public void fromPEMShouldReturnRSAPublicKeyWhenValidPEMKey() {
- assertThat(new PublicKeyReader().fromPEM(Optional.of(PUBLIC_PEM_KEY))).isPresent();
- }
-}
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jwt/pom.xml
----------------------------------------------------------------------
diff --git a/server/protocols/jwt/pom.xml b/server/protocols/jwt/pom.xml
new file mode 100644
index 0000000..85d17b9
--- /dev/null
+++ b/server/protocols/jwt/pom.xml
@@ -0,0 +1,245 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>james-server</artifactId>
+ <groupId>org.apache.james</groupId>
+ <version>3.0.0-beta6-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>james-server-jwt</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Apache James :: Server :: JWT</name>
+
+ <profiles>
+ <profile>
+ <id>noTest</id>
+ <activation>
+ <os>
+ <family>windows</family>
+ </os>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <skipTests>true</skipTests>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>disable-build-for-older-jdk</id>
+ <activation>
+ <jdk>(,1.8)</jdk>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>default-jar</id>
+ <phase>none</phase>
+ </execution>
+ <execution>
+ <id>jar</id>
+ <phase>none</phase>
+ </execution>
+ <execution>
+ <id>test-jar</id>
+ <phase>none</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>default-compile</id>
+ <phase>none</phase>
+ </execution>
+ <execution>
+ <id>default-testCompile</id>
+ <phase>none</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>default-test</id>
+ <phase>none</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-source-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <phase>none</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-install-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>default-install</id>
+ <phase>none</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>default-resources</id>
+ <phase>none</phase>
+ </execution>
+ <execution>
+ <id>default-testResources</id>
+ <phase>none</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-site-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-descriptor</id>
+ <phase>none</phase>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>build-for-jdk-8</id>
+ <activation>
+ <jdk>[1.8,)</jdk>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>javax.inject</groupId>
+ <artifactId>javax.inject</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.jsonwebtoken</groupId>
+ <artifactId>jjwt</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>${assertj-3.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifest>
+ <mainClass>fully.qualified.MainClass</mainClass>
+ </manifest>
+ </archive>
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>animal-sniffer-java-8</id>
+ <activation>
+ <jdk>[1.8,)</jdk>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>animal-sniffer-maven-plugin</artifactId>
+ <configuration>
+ <signature>
+ <groupId>org.codehaus.mojo.signature</groupId>
+ <artifactId>java18</artifactId>
+ <version>1.0</version>
+ </signature>
+ </configuration>
+ <executions>
+ <execution>
+ <id>check_java_8</id>
+ <phase>test</phase>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jwt/src/main/java/org/apache/james/jwt/JwtConfiguration.java
----------------------------------------------------------------------
diff --git a/server/protocols/jwt/src/main/java/org/apache/james/jwt/JwtConfiguration.java b/server/protocols/jwt/src/main/java/org/apache/james/jwt/JwtConfiguration.java
new file mode 100644
index 0000000..3cfcfef
--- /dev/null
+++ b/server/protocols/jwt/src/main/java/org/apache/james/jwt/JwtConfiguration.java
@@ -0,0 +1,34 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.jwt;
+
+import java.util.Optional;
+
+public class JwtConfiguration {
+ private final Optional<String> jwtPublicKeyPem;
+
+ public JwtConfiguration(Optional<String> jwtPublicKeyPem) {
+ this.jwtPublicKeyPem = jwtPublicKeyPem;
+ }
+
+ public Optional<String> getJwtPublicKeyPem() {
+ return jwtPublicKeyPem;
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jwt/src/main/java/org/apache/james/jwt/JwtTokenVerifier.java
----------------------------------------------------------------------
diff --git a/server/protocols/jwt/src/main/java/org/apache/james/jwt/JwtTokenVerifier.java b/server/protocols/jwt/src/main/java/org/apache/james/jwt/JwtTokenVerifier.java
new file mode 100644
index 0000000..661302f
--- /dev/null
+++ b/server/protocols/jwt/src/main/java/org/apache/james/jwt/JwtTokenVerifier.java
@@ -0,0 +1,63 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+package org.apache.james.jwt;
+
+import javax.inject.Inject;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Strings;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.MalformedJwtException;
+
+public class JwtTokenVerifier {
+
+ private final PublicKeyProvider pubKeyProvider;
+
+ @Inject
+ @VisibleForTesting
+ JwtTokenVerifier(PublicKeyProvider pubKeyProvider) {
+ this.pubKeyProvider = pubKeyProvider;
+ }
+
+ public boolean verify(String token) throws JwtException {
+ String subject = extractLogin(token);
+ if (Strings.isNullOrEmpty(subject)) {
+ throw new MalformedJwtException("'subject' field in token is mandatory");
+ }
+ return true;
+ }
+
+ public String extractLogin(String token) throws JwtException {
+ Jws<Claims> jws = parseToken(token);
+ return jws
+ .getBody()
+ .getSubject();
+ }
+
+ private Jws<Claims> parseToken(String token) throws JwtException {
+ return Jwts
+ .parser()
+ .setSigningKey(pubKeyProvider.get())
+ .parseClaimsJws(token);
+ }
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jwt/src/main/java/org/apache/james/jwt/MissingOrInvalidKeyException.java
----------------------------------------------------------------------
diff --git a/server/protocols/jwt/src/main/java/org/apache/james/jwt/MissingOrInvalidKeyException.java b/server/protocols/jwt/src/main/java/org/apache/james/jwt/MissingOrInvalidKeyException.java
new file mode 100644
index 0000000..b61de91
--- /dev/null
+++ b/server/protocols/jwt/src/main/java/org/apache/james/jwt/MissingOrInvalidKeyException.java
@@ -0,0 +1,22 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+package org.apache.james.jwt;
+
+public class MissingOrInvalidKeyException extends RuntimeException {
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jwt/src/main/java/org/apache/james/jwt/PublicKeyProvider.java
----------------------------------------------------------------------
diff --git a/server/protocols/jwt/src/main/java/org/apache/james/jwt/PublicKeyProvider.java b/server/protocols/jwt/src/main/java/org/apache/james/jwt/PublicKeyProvider.java
new file mode 100644
index 0000000..9ce7792
--- /dev/null
+++ b/server/protocols/jwt/src/main/java/org/apache/james/jwt/PublicKeyProvider.java
@@ -0,0 +1,44 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+package org.apache.james.jwt;
+
+import java.security.PublicKey;
+
+import javax.inject.Inject;
+
+import com.google.common.annotations.VisibleForTesting;
+
+public class PublicKeyProvider {
+
+ private final JwtConfiguration jwtConfiguration;
+ private final PublicKeyReader reader;
+
+ @Inject
+ @VisibleForTesting
+ PublicKeyProvider(JwtConfiguration jwtConfiguration, PublicKeyReader reader) {
+ this.jwtConfiguration = jwtConfiguration;
+ this.reader = reader;
+ }
+
+ public PublicKey get() throws MissingOrInvalidKeyException {
+ return reader.fromPEM(jwtConfiguration.getJwtPublicKeyPem())
+ .orElseThrow(MissingOrInvalidKeyException::new);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jwt/src/main/java/org/apache/james/jwt/PublicKeyReader.java
----------------------------------------------------------------------
diff --git a/server/protocols/jwt/src/main/java/org/apache/james/jwt/PublicKeyReader.java b/server/protocols/jwt/src/main/java/org/apache/james/jwt/PublicKeyReader.java
new file mode 100644
index 0000000..779fa1a
--- /dev/null
+++ b/server/protocols/jwt/src/main/java/org/apache/james/jwt/PublicKeyReader.java
@@ -0,0 +1,56 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+package org.apache.james.jwt;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.security.PublicKey;
+import java.util.Optional;
+
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.util.io.pem.PemReader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PublicKeyReader {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(PublicKeyReader.class);
+
+ Optional<PublicKey> fromPEM(Optional<String> pemKey) {
+ return pemKey
+ .map(k -> new PEMParser(new PemReader(new StringReader(k))))
+ .flatMap(this::publicKeyFrom);
+ }
+
+ private Optional<PublicKey> publicKeyFrom(PEMParser reader) {
+ try {
+ Object readPEM = reader.readObject();
+ if (readPEM instanceof SubjectPublicKeyInfo) {
+ return Optional.of(new JcaPEMKeyConverter().getPublicKey((SubjectPublicKeyInfo) readPEM));
+ }
+ LOGGER.warn("Key is not an instance of SubjectPublicKeyInfo but of " + readPEM);
+ return Optional.empty();
+ } catch (IOException e) {
+ LOGGER.warn("Error when reading the PEM file", e);
+ return Optional.empty();
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jwt/src/test/java/org/apache/james/jwt/JwtTokenVerifierTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jwt/src/test/java/org/apache/james/jwt/JwtTokenVerifierTest.java b/server/protocols/jwt/src/test/java/org/apache/james/jwt/JwtTokenVerifierTest.java
new file mode 100644
index 0000000..ac5cc94
--- /dev/null
+++ b/server/protocols/jwt/src/test/java/org/apache/james/jwt/JwtTokenVerifierTest.java
@@ -0,0 +1,119 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+package org.apache.james.jwt;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.security.Security;
+import java.util.Optional;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import io.jsonwebtoken.MalformedJwtException;
+import io.jsonwebtoken.SignatureException;
+
+public class JwtTokenVerifierTest {
+
+ private static final String PUBLIC_PEM_KEY = "-----BEGIN PUBLIC KEY-----\n" +
+ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtlChO/nlVP27MpdkG0Bh\n" +
+ "16XrMRf6M4NeyGa7j5+1UKm42IKUf3lM28oe82MqIIRyvskPc11NuzSor8HmvH8H\n" +
+ "lhDs5DyJtx2qp35AT0zCqfwlaDnlDc/QDlZv1CoRZGpQk1Inyh6SbZwYpxxwh0fi\n" +
+ "+d/4RpE3LBVo8wgOaXPylOlHxsDizfkL8QwXItyakBfMO6jWQRrj7/9WDhGf4Hi+\n" +
+ "GQur1tPGZDl9mvCoRHjFrD5M/yypIPlfMGWFVEvV5jClNMLAQ9bYFuOc7H1fEWw6\n" +
+ "U1LZUUbJW9/CH45YXz82CYqkrfbnQxqRb2iVbVjs/sHopHd1NTiCfUtwvcYJiBVj\n" +
+ "kwIDAQAB\n" +
+ "-----END PUBLIC KEY-----";
+
+ private static final String VALID_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.T04BTk" +
+ "LXkJj24coSZkK13RfG25lpvmSl2MJ7N10KpBk9_-95EGYZdog-BDAn3PJzqVw52z-Bwjh4VOj1-j7cURu0cT4jXehhUrlCxS4n7QHZD" +
+ "N_bsEYGu7KzjWTpTsUiHe-rN7izXVFxDGG1TGwlmBCBnPW-EFCf9ylUsJi0r2BKNdaaPRfMIrHptH1zJBkkUziWpBN1RNLjmvlAUf49" +
+ "t1Tbv21ZqYM5Ht2vrhJWczFbuC-TD-8zJkXhjTmA1GVgomIX5dx1cH-dZX1wANNmshUJGHgepWlPU-5VIYxPEhb219RMLJIELMY2qN" +
+ "OR8Q31ydinyqzXvCSzVJOf6T60-w";
+
+ private JwtTokenVerifier sut;
+
+ @BeforeClass
+ public static void init() {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ @Before
+ public void setup() {
+ PublicKeyProvider pubKeyProvider = new PublicKeyProvider(getJWTConfiguration(), new PublicKeyReader());
+ sut = new JwtTokenVerifier(pubKeyProvider);
+ }
+
+ private JwtConfiguration getJWTConfiguration() {
+
+ return new JwtConfiguration(Optional.of(PUBLIC_PEM_KEY));
+ }
+
+ @Test
+ public void shouldReturnTrueOnValidSignature() {
+
+ assertThat(sut.verify(VALID_TOKEN)).isTrue();
+ }
+
+ @Test
+ public void shouldThrowOnMismatchingSigningKey() {
+ String invalidToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.Pd6t82" +
+ "tPL3EZdkeYxw_DV2KimE1U2FvuLHmfR_mimJ5US3JFU4J2Gd94O7rwpSTGN1B9h-_lsTebo4ua4xHsTtmczZ9xa8a_kWKaSkqFjNFa" +
+ "Fp6zcoD6ivCu03SlRqsQzSRHXo6TKbnqOt9D6Y2rNa3C4igSwoS0jUE4BgpXbc0";
+
+ assertThatThrownBy(() -> sut.verify(invalidToken))
+ .isInstanceOf(SignatureException.class);
+ }
+
+ @Test
+ public void verifyShouldThrowWhenSubjectIsNull() {
+ String tokenWithNullSubject = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOm51bGwsIm5hbWUiOiJKb2huIERvZSJ9.EB" +
+ "_1grWDy_kFelXs3AQeiP13ay4eG_134dWB9XPRSeWsuPs8Mz2UY-VHDxLGD-fAqv-xKXr4QFEnS7iZkdpe0tPLNSwIjqeqkC6KqQln" +
+ "oC1okqWVWBDOcf7Acp1Jzp_cFTUhL5LkHvZDsyCdq5T9OOVVkzO4A9RrzIUsTrYPtRCBuYJ3ggR33cKpw191PulPGNH70rZqpUfDXe" +
+ "VPY3q15vWzZH9O9IJzB2KdHRMPxl2luRjzDbh4DLp56NhZuLX_2a9UAlmbV8MQX4Z_04ybhAYrcBfxR3MgJyr0jlxSibqSbXrkXuo-" +
+ "PyybfZCIhK_qXUlO5OS6sO7AQhKZO9p0MQ";
+
+ assertThatThrownBy(() -> sut.verify(tokenWithNullSubject))
+ .isInstanceOf(MalformedJwtException.class)
+ .hasMessage("'subject' field in token is mandatory");
+ }
+
+ @Test
+ public void verifyShouldThrowWhenEmptySubject() {
+ String tokenWithEmptySubject = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIiLCJuYW1lIjoiSm9obiBEb2UifQ.UdY" +
+ "s2PPzFCegUYspoDCnlJR_bJm8_z1InOv4v3tq8SJETQUarOXlhb_n6y6ujVvmJiSx9dI24Hc3Czi3RGUOXbnBDj1WPfd0aVSiUSqZr" +
+ "MCHBt5vjCYqAseDaP3w4aiiFb6EV3tteJFeBLZx8XlKPYxlzRLLUADDyDSQvrFBBPxfsvCETZovKdo9ofIN64o-yx23ss63yE6oIOd" +
+ "zJZ1Id40KSR2d7l3kIQJPLKUWJDnro5RAh4DOGOWNSq0JSbMhk7Zn3cXIBUpv3R8p79tui1UQpzwHMC0e6OSuWEDNQHtq-Cz85u8GG" +
+ "sUSbogmgObA_BimNtUq_Q1p0SGtIYBXmQ";
+
+ assertThatThrownBy(() -> sut.verify(tokenWithEmptySubject))
+ .isInstanceOf(MalformedJwtException.class)
+ .hasMessage("'subject' field in token is mandatory");
+ }
+
+ @Test
+ public void shouldReturnUserLoginFromValidToken() {
+
+ assertThat(sut.extractLogin(VALID_TOKEN)).isEqualTo("1234567890");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jwt/src/test/java/org/apache/james/jwt/PublicKeyProviderTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jwt/src/test/java/org/apache/james/jwt/PublicKeyProviderTest.java b/server/protocols/jwt/src/test/java/org/apache/james/jwt/PublicKeyProviderTest.java
new file mode 100644
index 0000000..feb4b66
--- /dev/null
+++ b/server/protocols/jwt/src/test/java/org/apache/james/jwt/PublicKeyProviderTest.java
@@ -0,0 +1,67 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+package org.apache.james.jwt;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.security.Security;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Optional;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class PublicKeyProviderTest {
+
+ private static final String PUBLIC_PEM_KEY = "-----BEGIN PUBLIC KEY-----\n" +
+ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtlChO/nlVP27MpdkG0Bh\n" +
+ "16XrMRf6M4NeyGa7j5+1UKm42IKUf3lM28oe82MqIIRyvskPc11NuzSor8HmvH8H\n" +
+ "lhDs5DyJtx2qp35AT0zCqfwlaDnlDc/QDlZv1CoRZGpQk1Inyh6SbZwYpxxwh0fi\n" +
+ "+d/4RpE3LBVo8wgOaXPylOlHxsDizfkL8QwXItyakBfMO6jWQRrj7/9WDhGf4Hi+\n" +
+ "GQur1tPGZDl9mvCoRHjFrD5M/yypIPlfMGWFVEvV5jClNMLAQ9bYFuOc7H1fEWw6\n" +
+ "U1LZUUbJW9/CH45YXz82CYqkrfbnQxqRb2iVbVjs/sHopHd1NTiCfUtwvcYJiBVj\n" +
+ "kwIDAQAB\n" +
+ "-----END PUBLIC KEY-----";
+
+ @BeforeClass
+ public static void init() {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ @Test
+ public void getShouldNotThrowWhenPEMKeyProvided() {
+
+ JwtConfiguration configWithPEMKey = new JwtConfiguration(Optional.of(PUBLIC_PEM_KEY));
+
+ PublicKeyProvider sut = new PublicKeyProvider(configWithPEMKey, new PublicKeyReader());
+
+ assertThat(sut.get()).isInstanceOf(RSAPublicKey.class);
+ }
+
+ @Test
+ public void getShouldThrowWhenPEMKeyNotProvided() {
+ JwtConfiguration configWithPEMKey = new JwtConfiguration(Optional.of(""));
+
+ PublicKeyProvider sut = new PublicKeyProvider(configWithPEMKey, new PublicKeyReader());
+
+ assertThatThrownBy(() -> sut.get()).isExactlyInstanceOf(MissingOrInvalidKeyException.class);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/james-project/blob/f1a087fa/server/protocols/jwt/src/test/java/org/apache/james/jwt/PublicKeyReaderTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jwt/src/test/java/org/apache/james/jwt/PublicKeyReaderTest.java b/server/protocols/jwt/src/test/java/org/apache/james/jwt/PublicKeyReaderTest.java
new file mode 100644
index 0000000..1b850d0
--- /dev/null
+++ b/server/protocols/jwt/src/test/java/org/apache/james/jwt/PublicKeyReaderTest.java
@@ -0,0 +1,62 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.jwt;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.security.Security;
+import java.util.Optional;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class PublicKeyReaderTest {
+
+ private static final String PUBLIC_PEM_KEY = "-----BEGIN PUBLIC KEY-----\n" +
+ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtlChO/nlVP27MpdkG0Bh\n" +
+ "16XrMRf6M4NeyGa7j5+1UKm42IKUf3lM28oe82MqIIRyvskPc11NuzSor8HmvH8H\n" +
+ "lhDs5DyJtx2qp35AT0zCqfwlaDnlDc/QDlZv1CoRZGpQk1Inyh6SbZwYpxxwh0fi\n" +
+ "+d/4RpE3LBVo8wgOaXPylOlHxsDizfkL8QwXItyakBfMO6jWQRrj7/9WDhGf4Hi+\n" +
+ "GQur1tPGZDl9mvCoRHjFrD5M/yypIPlfMGWFVEvV5jClNMLAQ9bYFuOc7H1fEWw6\n" +
+ "U1LZUUbJW9/CH45YXz82CYqkrfbnQxqRb2iVbVjs/sHopHd1NTiCfUtwvcYJiBVj\n" +
+ "kwIDAQAB\n" +
+ "-----END PUBLIC KEY-----";
+
+ @BeforeClass
+ public static void init() {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ @Test
+ public void fromPEMShouldReturnEmptyWhenEmptyProvided() {
+ assertThat(new PublicKeyReader().fromPEM(Optional.empty())).isEmpty();
+ }
+
+ @Test
+ public void fromPEMShouldReturnEmptyWhenInvalidPEMKey() {
+ assertThat(new PublicKeyReader().fromPEM(Optional.of("blabla"))).isEmpty();
+ }
+
+ @Test
+ public void fromPEMShouldReturnRSAPublicKeyWhenValidPEMKey() {
+ assertThat(new PublicKeyReader().fromPEM(Optional.of(PUBLIC_PEM_KEY))).isPresent();
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org