mirror of https://github.com/jwtk/jjwt.git
Reduce scope of Service Loader work to CompressionCodecs and JsonSerializers
Fixes: #458
This commit is contained in:
parent
ef32a1386d
commit
7037d64d24
|
@ -1,28 +0,0 @@
|
|||
package io.jsonwebtoken;
|
||||
|
||||
/**
|
||||
* Factory for {@link CompressionCodec} implementations. Backs the {@link io.jsonwebtoken.CompressionCodecs} constants
|
||||
* class. Implementations of jjwt-api should provide their own implementation and make it available via a provider
|
||||
* configuration file in META-INF/services.
|
||||
*/
|
||||
public interface CompressionCodecFactory {
|
||||
|
||||
/**
|
||||
* Creates a new instance of a CompressionCodec implementing the <a href="https://tools.ietf.org/html/rfc7518">JWA</a>
|
||||
* standard <a href="https://en.wikipedia.org/wiki/DEFLATE">deflate</a> compression algorithm
|
||||
*
|
||||
* @return A new instance of a deflate CompressionCodec
|
||||
*/
|
||||
CompressionCodec deflateCodec();
|
||||
|
||||
/**
|
||||
* Creates a new instance of a CompressionCodec implementing the <a href="https://en.wikipedia.org/wiki/Gzip">gzip</a>
|
||||
* compression algorithm. * <h3>Compatibility Warning</h3> * <p><b>This is not a standard JWA compression
|
||||
* algorithm</b>. Be sure to use this only when you are confident * that all parties accessing the token support
|
||||
* the gzip algorithm.</p> * <p>If you're concerned about compatibility, the {@link #deflateCodec()} code is JWA
|
||||
* standards-compliant.</p>
|
||||
*
|
||||
* @return A new instance of a gzip CompressionCodec
|
||||
*/
|
||||
CompressionCodec gzipCodec();
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package io.jsonwebtoken;
|
||||
|
||||
import io.jsonwebtoken.lang.Services;
|
||||
import io.jsonwebtoken.lang.Classes;
|
||||
|
||||
/**
|
||||
* Provides default implementations of the {@link CompressionCodec} interface.
|
||||
|
@ -26,8 +26,6 @@ import io.jsonwebtoken.lang.Services;
|
|||
*/
|
||||
public final class CompressionCodecs {
|
||||
|
||||
private static final CompressionCodecFactory FACTORY = Services.loadFirst(CompressionCodecFactory.class);
|
||||
|
||||
private CompressionCodecs() {
|
||||
} //prevent external instantiation
|
||||
|
||||
|
@ -35,7 +33,8 @@ public final class CompressionCodecs {
|
|||
* Codec implementing the <a href="https://tools.ietf.org/html/rfc7518">JWA</a> standard
|
||||
* <a href="https://en.wikipedia.org/wiki/DEFLATE">deflate</a> compression algorithm
|
||||
*/
|
||||
public static final CompressionCodec DEFLATE = FACTORY.deflateCodec();
|
||||
public static final CompressionCodec DEFLATE =
|
||||
Classes.newInstance("io.jsonwebtoken.impl.compression.DeflateCompressionCodec");
|
||||
|
||||
/**
|
||||
* Codec implementing the <a href="https://en.wikipedia.org/wiki/Gzip">gzip</a> compression algorithm.
|
||||
|
@ -44,6 +43,7 @@ public final class CompressionCodecs {
|
|||
* that all parties accessing the token support the gzip algorithm.</p>
|
||||
* <p>If you're concerned about compatibility, the {@link #DEFLATE DEFLATE} code is JWA standards-compliant.</p>
|
||||
*/
|
||||
public static final CompressionCodec GZIP = FACTORY.gzipCodec();
|
||||
public static final CompressionCodec GZIP =
|
||||
Classes.newInstance("io.jsonwebtoken.impl.compression.GzipCompressionCodec");
|
||||
|
||||
}
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
package io.jsonwebtoken;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Factory for creating instances of JWT interfaces. Backs the {@link io.jsonwebtoken.Jwts} factory class.
|
||||
* Implementations of jjwt-api should provide their own implementation and make it available via a provider
|
||||
* configuration file in META-INF/services.
|
||||
*
|
||||
* @since 0.1
|
||||
*/
|
||||
public interface JwtFactory {
|
||||
|
||||
/**
|
||||
* Creates a new {@link Header} instance suitable for <em>plaintext</em> (not digitally signed) JWTs. As this
|
||||
* is a less common use of JWTs, consider using the {@link #jwsHeader()} factory method instead if you will later
|
||||
* digitally sign the JWT.
|
||||
*
|
||||
* @return a new {@link Header} instance suitable for <em>plaintext</em> (not digitally signed) JWTs.
|
||||
*/
|
||||
Header header();
|
||||
|
||||
/**
|
||||
* Creates a new {@link Header} instance suitable for <em>plaintext</em> (not digitally signed) JWTs, populated
|
||||
* with the specified name/value pairs. As this is a less common use of JWTs, consider using the
|
||||
* {@link #jwsHeader(java.util.Map)} factory method instead if you will later digitally sign the JWT.
|
||||
*
|
||||
* @return a new {@link Header} instance suitable for <em>plaintext</em> (not digitally signed) JWTs.
|
||||
*/
|
||||
Header header(Map<String, Object> header);
|
||||
|
||||
/**
|
||||
* Returns a new {@link JwsHeader} instance suitable for digitally signed JWTs (aka 'JWS's).
|
||||
*
|
||||
* @return a new {@link JwsHeader} instance suitable for digitally signed JWTs (aka 'JWS's).
|
||||
* @see JwtBuilder#setHeader(Header)
|
||||
*/
|
||||
JwsHeader jwsHeader();
|
||||
|
||||
/**
|
||||
* Returns a new {@link JwsHeader} instance suitable for digitally signed JWTs (aka 'JWS's), populated with the
|
||||
* specified name/value pairs.
|
||||
*
|
||||
* @return a new {@link JwsHeader} instance suitable for digitally signed JWTs (aka 'JWS's), populated with the
|
||||
* specified name/value pairs.
|
||||
* @see JwtBuilder#setHeader(Header)
|
||||
*/
|
||||
JwsHeader jwsHeader(Map<String, Object> header);
|
||||
|
||||
/**
|
||||
* Returns a new {@link Claims} instance to be used as a JWT body.
|
||||
*
|
||||
* @return a new {@link Claims} instance to be used as a JWT body.
|
||||
*/
|
||||
Claims claims();
|
||||
|
||||
/**
|
||||
* Returns a new {@link Claims} instance populated with the specified name/value pairs.
|
||||
*
|
||||
* @param claims the name/value pairs to populate the new Claims instance.
|
||||
* @return a new {@link Claims} instance populated with the specified name/value pairs.
|
||||
*/
|
||||
Claims claims(Map<String, Object> claims);
|
||||
|
||||
/**
|
||||
* Returns a new {@link JwtParser} instance that can be configured and then used to parse JWT strings.
|
||||
*
|
||||
* @return a new {@link JwtParser} instance that can be configured and then used to parse JWT strings.
|
||||
*/
|
||||
JwtParser parser();
|
||||
|
||||
/**
|
||||
* Returns a new {@link JwtBuilder} instance that can be configured and then used to create JWT compact serialized
|
||||
* strings.
|
||||
*
|
||||
* @return a new {@link JwtBuilder} instance that can be configured and then used to create JWT compact serialized
|
||||
* strings.
|
||||
*/
|
||||
JwtBuilder builder();
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package io.jsonwebtoken;
|
||||
|
||||
import io.jsonwebtoken.lang.Services;
|
||||
import io.jsonwebtoken.lang.Classes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -23,11 +23,11 @@ import java.util.Map;
|
|||
* Factory class useful for creating instances of JWT interfaces. Using this factory class can be a good
|
||||
* alternative to tightly coupling your code to implementation classes.
|
||||
*
|
||||
* @since 0.11
|
||||
* @since 0.1
|
||||
*/
|
||||
public final class Jwts {
|
||||
|
||||
private static final JwtFactory FACTORY = Services.loadFirst(JwtFactory.class);
|
||||
private static final Class[] MAP_ARG = new Class[]{Map.class};
|
||||
|
||||
private Jwts() {
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ public final class Jwts {
|
|||
* @return a new {@link Header} instance suitable for <em>plaintext</em> (not digitally signed) JWTs.
|
||||
*/
|
||||
public static Header header() {
|
||||
return FACTORY.header();
|
||||
return Classes.newInstance("io.jsonwebtoken.impl.DefaultHeader");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,7 +51,7 @@ public final class Jwts {
|
|||
* @return a new {@link Header} instance suitable for <em>plaintext</em> (not digitally signed) JWTs.
|
||||
*/
|
||||
public static Header header(Map<String, Object> header) {
|
||||
return FACTORY.header(header);
|
||||
return Classes.newInstance("io.jsonwebtoken.impl.DefaultHeader", MAP_ARG, header);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,7 +61,7 @@ public final class Jwts {
|
|||
* @see JwtBuilder#setHeader(Header)
|
||||
*/
|
||||
public static JwsHeader jwsHeader() {
|
||||
return FACTORY.jwsHeader();
|
||||
return Classes.newInstance("io.jsonwebtoken.impl.DefaultJwsHeader");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,7 +73,7 @@ public final class Jwts {
|
|||
* @see JwtBuilder#setHeader(Header)
|
||||
*/
|
||||
public static JwsHeader jwsHeader(Map<String, Object> header) {
|
||||
return FACTORY.jwsHeader(header);
|
||||
return Classes.newInstance("io.jsonwebtoken.impl.DefaultJwsHeader", MAP_ARG, header);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -82,7 +82,7 @@ public final class Jwts {
|
|||
* @return a new {@link Claims} instance to be used as a JWT body.
|
||||
*/
|
||||
public static Claims claims() {
|
||||
return FACTORY.claims();
|
||||
return Classes.newInstance("io.jsonwebtoken.impl.DefaultClaims");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,7 +92,7 @@ public final class Jwts {
|
|||
* @return a new {@link Claims} instance populated with the specified name/value pairs.
|
||||
*/
|
||||
public static Claims claims(Map<String, Object> claims) {
|
||||
return FACTORY.claims(claims);
|
||||
return Classes.newInstance("io.jsonwebtoken.impl.DefaultClaims", MAP_ARG, claims);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -118,7 +118,7 @@ public final class Jwts {
|
|||
*/
|
||||
@Deprecated
|
||||
public static JwtParser parser() {
|
||||
return FACTORY.parser();
|
||||
return Classes.newInstance("io.jsonwebtoken.impl.DefaultJwtParser");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -138,6 +138,6 @@ public final class Jwts {
|
|||
* strings.
|
||||
*/
|
||||
public static JwtBuilder builder() {
|
||||
return FACTORY.builder();
|
||||
return Classes.newInstance("io.jsonwebtoken.impl.DefaultJwtBuilder");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
package io.jsonwebtoken.lang;
|
||||
|
||||
import io.jsonwebtoken.JwtException;
|
||||
|
||||
/**
|
||||
* Exception indicating that no implementation of an jjwt-api SPI was found on the classpath.
|
||||
*/
|
||||
public class ImplementationNotFoundException extends JwtException {
|
||||
|
||||
ImplementationNotFoundException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* Copyright (C) 2019 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.lang;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -11,6 +26,8 @@ import java.util.ServiceLoader;
|
|||
*/
|
||||
public final class Services {
|
||||
|
||||
private Services() {}
|
||||
|
||||
/**
|
||||
* Loads and instantiates all service implementation of the given SPI class and returns them as a List.
|
||||
*
|
||||
|
@ -19,14 +36,21 @@ public final class Services {
|
|||
* @return An unmodifiable list with an instance of all available implementations of the SPI. No guarantee is given
|
||||
* on the order of implementations, if more than one.
|
||||
*/
|
||||
public static <T> List<T> loadAllAvailableImplementations(Class<T> spi) {
|
||||
public static <T> List<T> loadAll(Class<T> spi) {
|
||||
Assert.notNull(spi, "Parameter 'spi' must not be null.");
|
||||
ServiceLoader<T> serviceLoader = ServiceLoader.load(spi);
|
||||
|
||||
List<T> implementations = new ArrayList<>();
|
||||
|
||||
for (T implementation : serviceLoader) {
|
||||
implementations.add(implementation);
|
||||
}
|
||||
|
||||
// fail if no implementations were found
|
||||
if (implementations.isEmpty()) {
|
||||
throw new UnavailableImplementationException(spi);
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(implementations);
|
||||
}
|
||||
|
||||
|
@ -38,14 +62,15 @@ public final class Services {
|
|||
* @param spi The class of the Service Provider Interface
|
||||
* @param <T> The type of the SPI
|
||||
* @return A new instance of the service.
|
||||
* @throws ImplementationNotFoundException When no implementation the SPI is available on the classpath.
|
||||
* @throws UnavailableImplementationException When no implementation the SPI is available on the classpath.
|
||||
*/
|
||||
public static <T> T loadFirst(Class<T> spi) {
|
||||
Assert.notNull(spi, "Parameter 'spi' must not be null.");
|
||||
ServiceLoader<T> serviceLoader = ServiceLoader.load(spi);
|
||||
if (serviceLoader.iterator().hasNext()) {
|
||||
return serviceLoader.iterator().next();
|
||||
} else {
|
||||
throw new ImplementationNotFoundException("No implementation of " + spi.getName() + " found on the classpath. Make sure to include an implementation of jjwt-api.");
|
||||
throw new UnavailableImplementationException(spi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (C) 2019 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.lang;
|
||||
|
||||
/**
|
||||
* Exception indicating that no implementation of an jjwt-api SPI was found on the classpath.
|
||||
* @since 0.11.0
|
||||
*/
|
||||
public final class UnavailableImplementationException extends RuntimeException {
|
||||
|
||||
private static final String DEFAULT_NOT_FOUND_MESSAGE = "Unable to find an implementation for %s using java.util.ServiceLoader. Ensure you include a backing implementation .jar in the classpath, for example jjwt-impl.jar, or your own .jar for custom implementations.";
|
||||
|
||||
UnavailableImplementationException(final Class klass) {
|
||||
super(String.format(DEFAULT_NOT_FOUND_MESSAGE, klass));
|
||||
}
|
||||
}
|
|
@ -48,16 +48,17 @@ public class UnknownClassException extends RuntimeException {
|
|||
public UnknownClassException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Constructs a new UnknownClassException.
|
||||
*
|
||||
* @param message the reason for the exception
|
||||
* @param cause the underlying Throwable that caused this exception to be thrown.
|
||||
*
|
||||
*/
|
||||
public UnknownClassException(String message, Throwable cause) {
|
||||
// TODO: remove in v1.0, this constructor is only exposed to allow for backward compatible behavior
|
||||
super(message, cause);
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package io.jsonwebtoken.security;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
public interface KeyGenerator {
|
||||
boolean supports(SignatureAlgorithm alg);
|
||||
|
||||
/**
|
||||
* Generates a new secure-random secret key of a length suitable for creating and verifying HMAC signatures
|
||||
* according to the specified {@code SignatureAlgorithm}.
|
||||
*
|
||||
* @param alg the desired signature algorithm
|
||||
* @return a new secure-random secret key of a length suitable for creating and verifying HMAC signatures according
|
||||
* to the specified {@code SignatureAlgorithm}.
|
||||
*/
|
||||
SecretKey generateKey(SignatureAlgorithm alg);
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package io.jsonwebtoken.security;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
|
||||
import java.security.KeyPair;
|
||||
|
||||
public interface KeyPairGenerator {
|
||||
boolean supports(SignatureAlgorithm alg);
|
||||
|
||||
/**
|
||||
* Generates a new secure-random key pair of sufficient strength for the specified {@link SignatureAlgorithm} using
|
||||
* JJWT's default SecureRandom instance.
|
||||
*
|
||||
* @param alg the algorithm indicating strength
|
||||
* @return a new secure-randomly generated key pair of sufficient strength for the specified {@link
|
||||
* SignatureAlgorithm}.
|
||||
*/
|
||||
KeyPair generateKeyPair(SignatureAlgorithm alg);
|
||||
}
|
|
@ -17,7 +17,7 @@ package io.jsonwebtoken.security;
|
|||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Services;
|
||||
import io.jsonwebtoken.lang.Classes;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
@ -32,8 +32,12 @@ import java.util.List;
|
|||
* @since 0.10.0
|
||||
*/
|
||||
public final class Keys {
|
||||
private static final List<KeyGenerator> KEY_GENERATORS = Services.loadAllAvailableImplementations(KeyGenerator.class);
|
||||
private static final List<KeyPairGenerator> KEY_PAIR_GENERATORS = Services.loadAllAvailableImplementations(KeyPairGenerator.class);
|
||||
|
||||
private static final String MAC = "io.jsonwebtoken.impl.crypto.MacProvider";
|
||||
private static final String RSA = "io.jsonwebtoken.impl.crypto.RsaProvider";
|
||||
private static final String EC = "io.jsonwebtoken.impl.crypto.EllipticCurveProvider";
|
||||
|
||||
private static final Class[] SIG_ARG_TYPES = new Class[]{SignatureAlgorithm.class};
|
||||
|
||||
//purposefully ordered higher to lower:
|
||||
private static final List<SignatureAlgorithm> PREFERRED_HMAC_ALGS = Collections.unmodifiableList(Arrays.asList(
|
||||
|
@ -125,14 +129,15 @@ public final class Keys {
|
|||
*/
|
||||
public static SecretKey secretKeyFor(SignatureAlgorithm alg) throws IllegalArgumentException {
|
||||
Assert.notNull(alg, "SignatureAlgorithm cannot be null.");
|
||||
for (KeyGenerator keyGenerator : KEY_GENERATORS) {
|
||||
if (keyGenerator.supports(alg)) {
|
||||
return keyGenerator.generateKey(alg);
|
||||
}
|
||||
switch (alg) {
|
||||
case HS256:
|
||||
case HS384:
|
||||
case HS512:
|
||||
return Classes.invokeStatic(MAC, "generateKey", SIG_ARG_TYPES, alg);
|
||||
default:
|
||||
String msg = "The " + alg.name() + " algorithm does not support shared secret keys.";
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
|
||||
String msg = "The " + alg.name() + " algorithm does not support shared secret keys.";
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -206,14 +211,21 @@ public final class Keys {
|
|||
*/
|
||||
public static KeyPair keyPairFor(SignatureAlgorithm alg) throws IllegalArgumentException {
|
||||
Assert.notNull(alg, "SignatureAlgorithm cannot be null.");
|
||||
|
||||
for (KeyPairGenerator keyPairGenerator : KEY_PAIR_GENERATORS) {
|
||||
if (keyPairGenerator.supports(alg)) {
|
||||
return keyPairGenerator.generateKeyPair(alg);
|
||||
}
|
||||
switch (alg) {
|
||||
case RS256:
|
||||
case PS256:
|
||||
case RS384:
|
||||
case PS384:
|
||||
case RS512:
|
||||
case PS512:
|
||||
return Classes.invokeStatic(RSA, "generateKeyPair", SIG_ARG_TYPES, alg);
|
||||
case ES256:
|
||||
case ES384:
|
||||
case ES512:
|
||||
return Classes.invokeStatic(EC, "generateKeyPair", SIG_ARG_TYPES, alg);
|
||||
default:
|
||||
String msg = "The " + alg.name() + " algorithm does not support Key Pairs.";
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
|
||||
String msg = "The " + alg.name() + " algorithm does not support Key Pairs.";
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,41 +15,41 @@
|
|||
*/
|
||||
package io.jsonwebtoken
|
||||
|
||||
import io.jsonwebtoken.lang.Services
|
||||
import io.jsonwebtoken.lang.Classes
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest
|
||||
import org.powermock.modules.junit4.PowerMockRunner
|
||||
|
||||
import static org.easymock.EasyMock.createMock
|
||||
import static org.easymock.EasyMock.eq
|
||||
import static org.easymock.EasyMock.expect
|
||||
import static org.junit.Assert.assertSame
|
||||
import static org.powermock.api.easymock.PowerMock.*
|
||||
import static org.powermock.api.easymock.PowerMock.mockStatic
|
||||
import static org.powermock.api.easymock.PowerMock.replay
|
||||
import static org.powermock.api.easymock.PowerMock.verify
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest([Services, CompressionCodecs])
|
||||
@PrepareForTest([Classes, CompressionCodecs])
|
||||
class CompressionCodecsTest {
|
||||
|
||||
@Test
|
||||
void testStatics() {
|
||||
|
||||
mockStatic(Services)
|
||||
|
||||
def factory = createMock(CompressionCodecFactory)
|
||||
|
||||
expect(Services.loadFirst(CompressionCodecFactory)).andReturn(factory)
|
||||
mockStatic(Classes)
|
||||
|
||||
def deflate = createMock(CompressionCodec)
|
||||
def gzip = createMock(CompressionCodec)
|
||||
|
||||
expect(factory.deflateCodec()).andReturn(deflate)
|
||||
expect(factory.gzipCodec()).andReturn(gzip)
|
||||
expect(Classes.newInstance(eq("io.jsonwebtoken.impl.compression.DeflateCompressionCodec"))).andReturn(deflate)
|
||||
expect(Classes.newInstance(eq("io.jsonwebtoken.impl.compression.GzipCompressionCodec"))).andReturn(gzip)
|
||||
|
||||
replay Services, factory, deflate, gzip
|
||||
replay Classes, deflate, gzip
|
||||
|
||||
assertSame deflate, CompressionCodecs.DEFLATE
|
||||
assertSame gzip, CompressionCodecs.GZIP
|
||||
|
||||
verify Services, factory, deflate, gzip
|
||||
verify Classes, deflate, gzip
|
||||
|
||||
//test coverage for private constructor:
|
||||
new CompressionCodecs()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2014 jsonwebtoken.io
|
||||
* Copyright (C) 2019 jsonwebtoken.io
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -13,12 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.jsonwebtoken.impl.io;
|
||||
package io.jsonwebtoken
|
||||
|
||||
/**
|
||||
* @since 0.10.0
|
||||
*/
|
||||
public interface InstanceLocator<T> {
|
||||
|
||||
T getInstance();
|
||||
class DefaultStubService implements StubService {
|
||||
}
|
|
@ -15,44 +15,26 @@
|
|||
*/
|
||||
package io.jsonwebtoken
|
||||
|
||||
import io.jsonwebtoken.lang.Services
|
||||
import org.junit.Before
|
||||
import org.junit.BeforeClass
|
||||
import io.jsonwebtoken.lang.Classes
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest
|
||||
import org.powermock.modules.junit4.PowerMockRunner
|
||||
|
||||
import static org.easymock.EasyMock.createMock
|
||||
import static org.easymock.EasyMock.eq
|
||||
import static org.easymock.EasyMock.expect
|
||||
import static org.easymock.EasyMock.mock
|
||||
import static org.easymock.EasyMock.reset
|
||||
import static org.easymock.EasyMock.same
|
||||
import static org.junit.Assert.assertSame
|
||||
import static org.powermock.api.easymock.PowerMock.createMock
|
||||
import static org.powermock.api.easymock.PowerMock.mockStatic
|
||||
import static org.powermock.api.easymock.PowerMock.replay
|
||||
import static org.powermock.api.easymock.PowerMock.reset
|
||||
import static org.powermock.api.easymock.PowerMock.verify
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest([Services])
|
||||
@PrepareForTest([Classes, Jwts])
|
||||
class JwtsTest {
|
||||
|
||||
static JwtFactory factory = mock(JwtFactory)
|
||||
|
||||
@BeforeClass
|
||||
static void prepareFactory() {
|
||||
mockStatic(Services)
|
||||
|
||||
expect(Services.loadFirst(JwtFactory)).andReturn(factory).anyTimes()
|
||||
|
||||
replay Services
|
||||
}
|
||||
|
||||
@Before
|
||||
void resetFactoryMock() {
|
||||
reset(factory)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPrivateCtor() { //for code coverage only
|
||||
new Jwts()
|
||||
|
@ -61,118 +43,146 @@ class JwtsTest {
|
|||
@Test
|
||||
void testHeader() {
|
||||
|
||||
mockStatic(Classes)
|
||||
|
||||
def instance = createMock(Header)
|
||||
|
||||
expect(factory.header()).andReturn(instance)
|
||||
expect(Classes.newInstance(eq("io.jsonwebtoken.impl.DefaultHeader"))).andReturn(instance)
|
||||
|
||||
replay factory, instance
|
||||
replay Classes, instance
|
||||
|
||||
assertSame instance, Jwts.header()
|
||||
|
||||
verify factory, instance
|
||||
verify Classes, instance
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHeaderFromMap() {
|
||||
|
||||
mockStatic(Classes)
|
||||
|
||||
def map = [:]
|
||||
|
||||
def instance = createMock(Header)
|
||||
|
||||
expect(factory.header(same(map) as Map<String, Object>)).andReturn(instance)
|
||||
expect(Classes.newInstance(
|
||||
eq("io.jsonwebtoken.impl.DefaultHeader"),
|
||||
same(Jwts.MAP_ARG),
|
||||
same(map))
|
||||
).andReturn(instance)
|
||||
|
||||
replay factory, instance
|
||||
replay Classes, instance
|
||||
|
||||
assertSame instance, Jwts.header(map)
|
||||
|
||||
verify factory, instance
|
||||
verify Classes, instance
|
||||
}
|
||||
|
||||
@Test
|
||||
void testJwsHeader() {
|
||||
|
||||
mockStatic(Classes)
|
||||
|
||||
def instance = createMock(JwsHeader)
|
||||
|
||||
expect(factory.jwsHeader()).andReturn(instance)
|
||||
expect(Classes.newInstance(eq("io.jsonwebtoken.impl.DefaultJwsHeader"))).andReturn(instance)
|
||||
|
||||
replay factory, instance
|
||||
replay Classes, instance
|
||||
|
||||
assertSame instance, Jwts.jwsHeader()
|
||||
|
||||
verify factory, instance
|
||||
verify Classes, instance
|
||||
}
|
||||
|
||||
@Test
|
||||
void testJwsHeaderFromMap() {
|
||||
|
||||
mockStatic(Classes)
|
||||
|
||||
def map = [:]
|
||||
|
||||
def instance = createMock(JwsHeader)
|
||||
|
||||
expect(factory.jwsHeader(same(map) as Map<String, Object>)).andReturn(instance)
|
||||
expect(Classes.newInstance(
|
||||
eq("io.jsonwebtoken.impl.DefaultJwsHeader"),
|
||||
same(Jwts.MAP_ARG),
|
||||
same(map))
|
||||
).andReturn(instance)
|
||||
|
||||
replay factory, instance
|
||||
replay Classes, instance
|
||||
|
||||
assertSame instance, Jwts.jwsHeader(map)
|
||||
|
||||
verify factory, instance
|
||||
verify Classes, instance
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClaims() {
|
||||
|
||||
mockStatic(Classes)
|
||||
|
||||
def instance = createMock(Claims)
|
||||
|
||||
expect(factory.claims()).andReturn(instance)
|
||||
expect(Classes.newInstance(eq("io.jsonwebtoken.impl.DefaultClaims"))).andReturn(instance)
|
||||
|
||||
replay factory, instance
|
||||
replay Classes, instance
|
||||
|
||||
assertSame instance, Jwts.claims()
|
||||
|
||||
verify factory, instance
|
||||
verify Classes, instance
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClaimsFromMap() {
|
||||
|
||||
mockStatic(Classes)
|
||||
|
||||
def map = [:]
|
||||
|
||||
def instance = createMock(Claims)
|
||||
|
||||
expect(factory.claims(same(map) as Map<String, Object>)).andReturn(instance)
|
||||
expect(Classes.newInstance(
|
||||
eq("io.jsonwebtoken.impl.DefaultClaims"),
|
||||
same(Jwts.MAP_ARG),
|
||||
same(map))
|
||||
).andReturn(instance)
|
||||
|
||||
replay factory, instance
|
||||
replay Classes, instance
|
||||
|
||||
assertSame instance, Jwts.claims(map)
|
||||
|
||||
verify factory, instance
|
||||
verify Classes, instance
|
||||
}
|
||||
|
||||
@Test
|
||||
void testParser() {
|
||||
|
||||
mockStatic(Classes)
|
||||
|
||||
def instance = createMock(JwtParser)
|
||||
|
||||
expect(factory.parser()).andReturn(instance)
|
||||
expect(Classes.newInstance(eq("io.jsonwebtoken.impl.DefaultJwtParser"))).andReturn(instance)
|
||||
|
||||
replay factory, instance
|
||||
replay Classes, instance
|
||||
|
||||
assertSame instance, Jwts.parser()
|
||||
|
||||
verify factory, instance
|
||||
verify Classes, instance
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBuilder() {
|
||||
|
||||
mockStatic(Classes)
|
||||
|
||||
def instance = createMock(JwtBuilder)
|
||||
|
||||
expect(factory.builder()).andReturn(instance)
|
||||
expect(Classes.newInstance(eq("io.jsonwebtoken.impl.DefaultJwtBuilder"))).andReturn(instance)
|
||||
|
||||
replay factory, instance
|
||||
replay Classes, instance
|
||||
|
||||
assertSame instance, Jwts.builder()
|
||||
|
||||
verify factory, instance
|
||||
verify Classes, instance
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright (C) 2019 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken
|
||||
|
||||
interface StubService {
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
package io.jsonwebtoken
|
||||
|
||||
class TestJwtFactory implements JwtFactory {
|
||||
@Override
|
||||
Header header() {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
Header header(final Map<String, Object> header) {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
JwsHeader jwsHeader() {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
JwsHeader jwsHeader(final Map<String, Object> header) {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
Claims claims() {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
Claims claims(final Map<String, Object> claim) {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
JwtParser parser() {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
JwtBuilder builder() {
|
||||
return null
|
||||
}
|
||||
}
|
|
@ -1,12 +1,28 @@
|
|||
/*
|
||||
* Copyright (C) 2019 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.lang
|
||||
|
||||
import io.jsonwebtoken.JwtFactory
|
||||
import io.jsonwebtoken.TestJwtFactory
|
||||
import io.jsonwebtoken.DefaultStubService
|
||||
import io.jsonwebtoken.StubService
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest
|
||||
import org.powermock.modules.junit4.PowerMockRunner
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.assertNotNull
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
|
@ -15,20 +31,23 @@ class ServicesTest {
|
|||
|
||||
@Test
|
||||
void testSuccessfulLoading() {
|
||||
def factory = Services.loadFirst(JwtFactory.class)
|
||||
|
||||
def factory = Services.loadFirst(StubService)
|
||||
assertNotNull factory
|
||||
|
||||
org.junit.Assert.assertEquals(TestJwtFactory, factory.class)
|
||||
assertEquals(DefaultStubService, factory.class)
|
||||
}
|
||||
|
||||
@Test(expected = ImplementationNotFoundException)
|
||||
void testFailedLoading() {
|
||||
ClassLoader cl = Thread.currentThread().getContextClassLoader()
|
||||
@Test(expected = UnavailableImplementationException)
|
||||
void testLoadFirstUnavailable() {
|
||||
NoServicesClassLoader.runWith {
|
||||
Services.loadFirst(StubService.class)
|
||||
}
|
||||
}
|
||||
|
||||
Thread.currentThread().setContextClassLoader(new NoServicesClassLoader(cl))
|
||||
|
||||
Services.loadFirst(JwtFactory.class)
|
||||
@Test(expected = UnavailableImplementationException)
|
||||
void testLoadAllUnavailable() {
|
||||
NoServicesClassLoader.runWith {
|
||||
Services.loadAll(StubService.class)
|
||||
}
|
||||
}
|
||||
|
||||
static class NoServicesClassLoader extends ClassLoader {
|
||||
|
@ -44,5 +63,17 @@ class ServicesTest {
|
|||
return super.getResources(name)
|
||||
}
|
||||
}
|
||||
|
||||
static void runWith(Closure closure) {
|
||||
ClassLoader originalClassloader = Thread.currentThread().getContextClassLoader()
|
||||
try {
|
||||
Thread.currentThread().setContextClassLoader(new NoServicesClassLoader(originalClassloader))
|
||||
closure.run()
|
||||
} finally {
|
||||
if (originalClassloader != null) {
|
||||
Thread.currentThread().setContextClassLoader(originalClassloader)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,7 @@
|
|||
package io.jsonwebtoken.security
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm
|
||||
import io.jsonwebtoken.lang.Services
|
||||
import org.junit.Before
|
||||
import org.junit.BeforeClass
|
||||
import io.jsonwebtoken.lang.Classes
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest
|
||||
|
@ -27,8 +25,8 @@ import org.powermock.modules.junit4.PowerMockRunner
|
|||
import javax.crypto.SecretKey
|
||||
import java.security.KeyPair
|
||||
|
||||
import static org.easymock.EasyMock.eq
|
||||
import static org.easymock.EasyMock.expect
|
||||
import static org.easymock.EasyMock.mock
|
||||
import static org.easymock.EasyMock.same
|
||||
import static org.junit.Assert.*
|
||||
import static org.powermock.api.easymock.PowerMock.*
|
||||
|
@ -39,28 +37,9 @@ import static org.powermock.api.easymock.PowerMock.*
|
|||
* The actual implementation assertions are done in KeysImplTest in the impl module.
|
||||
*/
|
||||
@RunWith(PowerMockRunner)
|
||||
@PrepareForTest([Keys, Services])
|
||||
@PrepareForTest([Classes, Keys])
|
||||
class KeysTest {
|
||||
|
||||
static KeyGenerator keyGenerator = mock(KeyGenerator)
|
||||
static KeyPairGenerator keyPairGenerator = mock(KeyPairGenerator)
|
||||
|
||||
@BeforeClass
|
||||
static void prepareServices() {
|
||||
mockStatic(Services)
|
||||
|
||||
expect(Services.loadAllAvailableImplementations(KeyGenerator)).andReturn([keyGenerator]).anyTimes()
|
||||
expect(Services.loadAllAvailableImplementations(KeyPairGenerator)).andReturn([keyPairGenerator]).anyTimes()
|
||||
|
||||
replay Services
|
||||
}
|
||||
|
||||
@Before
|
||||
void reset() {
|
||||
reset keyGenerator
|
||||
reset keyPairGenerator
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPrivateCtor() { //for code coverage only
|
||||
new Keys()
|
||||
|
@ -101,30 +80,27 @@ class KeysTest {
|
|||
|
||||
if (name.startsWith('H')) {
|
||||
|
||||
def key = createMock(SecretKey)
|
||||
expect(keyGenerator.supports(same(alg))).andReturn(true)
|
||||
expect(keyGenerator.generateKey(same(alg))).andReturn(key)
|
||||
mockStatic(Classes)
|
||||
|
||||
replay keyGenerator, key
|
||||
def key = createMock(SecretKey)
|
||||
expect(Classes.invokeStatic(eq(Keys.MAC), eq("generateKey"), same(Keys.SIG_ARG_TYPES), same(alg))).andReturn(key)
|
||||
|
||||
replay Classes, key
|
||||
|
||||
assertSame key, Keys.secretKeyFor(alg)
|
||||
|
||||
verify keyGenerator, key
|
||||
verify Classes, key
|
||||
|
||||
reset keyGenerator, key
|
||||
reset Classes, key
|
||||
|
||||
} else {
|
||||
expect(keyGenerator.supports(same(alg))).andReturn(false)
|
||||
|
||||
replay(keyGenerator)
|
||||
|
||||
try {
|
||||
Keys.secretKeyFor(alg)
|
||||
fail()
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertEquals "The $name algorithm does not support shared secret keys." as String, expected.message
|
||||
reset keyGenerator
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,29 +114,27 @@ class KeysTest {
|
|||
String name = alg.name()
|
||||
|
||||
if (name.equals('NONE') || name.startsWith('H')) {
|
||||
expect(keyPairGenerator.supports(alg)).andReturn(false)
|
||||
|
||||
replay keyPairGenerator
|
||||
|
||||
try {
|
||||
Keys.keyPairFor(alg)
|
||||
fail()
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertEquals "The $name algorithm does not support Key Pairs." as String, expected.message
|
||||
reset keyPairGenerator
|
||||
}
|
||||
} else {
|
||||
def pair = createMock(KeyPair)
|
||||
expect(keyPairGenerator.supports(same(alg))).andReturn(true)
|
||||
expect(keyPairGenerator.generateKeyPair(same(alg))).andReturn(pair)
|
||||
String fqcn = name.startsWith('E') ? Keys.EC : Keys.RSA
|
||||
|
||||
replay keyPairGenerator, pair
|
||||
mockStatic Classes
|
||||
|
||||
def pair = createMock(KeyPair)
|
||||
expect(Classes.invokeStatic(eq(fqcn), eq("generateKeyPair"), same(Keys.SIG_ARG_TYPES), same(alg))).andReturn(pair)
|
||||
|
||||
replay Classes, pair
|
||||
|
||||
assertSame pair, Keys.keyPairFor(alg)
|
||||
|
||||
verify keyPairGenerator, pair
|
||||
verify Classes, pair
|
||||
|
||||
reset keyPairGenerator, pair
|
||||
reset Classes, pair
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
io.jsonwebtoken.TestJwtFactory
|
|
@ -0,0 +1 @@
|
|||
io.jsonwebtoken.DefaultStubService
|
|
@ -0,0 +1 @@
|
|||
io.jsonwebtoken.gson.io.GsonDeserializer
|
|
@ -0,0 +1 @@
|
|||
io.jsonwebtoken.gson.io.GsonSerializer
|
|
@ -1 +1 @@
|
|||
io.jsonwebtoken.io.JacksonDeserializer
|
||||
io.jsonwebtoken.jackson.io.JacksonDeserializer
|
|
@ -1 +1 @@
|
|||
io.jsonwebtoken.io.JacksonSerializer
|
||||
io.jsonwebtoken.jackson.io.JacksonSerializer
|
|
@ -1 +1 @@
|
|||
io.jsonwebtoken.io.OrgJsonDeserializer
|
||||
io.jsonwebtoken.orgjson.io.OrgJsonDeserializer
|
|
@ -1 +1 @@
|
|||
io.jsonwebtoken.io.OrgJsonSerializer
|
||||
io.jsonwebtoken.orgjson.io.OrgJsonSerializer
|
|
@ -1,18 +0,0 @@
|
|||
package io.jsonwebtoken.impl;
|
||||
|
||||
import io.jsonwebtoken.CompressionCodec;
|
||||
import io.jsonwebtoken.CompressionCodecFactory;
|
||||
import io.jsonwebtoken.impl.compression.DeflateCompressionCodec;
|
||||
import io.jsonwebtoken.impl.compression.GzipCompressionCodec;
|
||||
|
||||
public class DefaultCompressionCodecFactory implements CompressionCodecFactory {
|
||||
@Override
|
||||
public CompressionCodec deflateCodec() {
|
||||
return new DeflateCompressionCodec();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompressionCodec gzipCodec() {
|
||||
return new GzipCompressionCodec();
|
||||
}
|
||||
}
|
|
@ -24,14 +24,13 @@ import io.jsonwebtoken.JwtParser;
|
|||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.impl.crypto.DefaultJwtSigner;
|
||||
import io.jsonwebtoken.impl.crypto.JwtSigner;
|
||||
import io.jsonwebtoken.impl.io.InstanceLocator;
|
||||
import io.jsonwebtoken.impl.lang.LegacyServices;
|
||||
import io.jsonwebtoken.io.Decoders;
|
||||
import io.jsonwebtoken.io.Encoder;
|
||||
import io.jsonwebtoken.io.Encoders;
|
||||
import io.jsonwebtoken.io.SerializationException;
|
||||
import io.jsonwebtoken.io.Serializer;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Classes;
|
||||
import io.jsonwebtoken.lang.Collections;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
import io.jsonwebtoken.security.InvalidKeyException;
|
||||
|
@ -295,10 +294,10 @@ public class DefaultJwtBuilder implements JwtBuilder {
|
|||
public String compact() {
|
||||
|
||||
if (this.serializer == null) {
|
||||
//try to find one based on the runtime environment:
|
||||
InstanceLocator<Serializer<Map<String,?>>> locator =
|
||||
Classes.newInstance("io.jsonwebtoken.impl.io.RuntimeClasspathSerializerLocator");
|
||||
this.serializer = locator.getInstance();
|
||||
// try to find one based on the services available
|
||||
// TODO: This util class will throw a UnavailableImplementationException here to retain behavior of previous version, remove in v1.0
|
||||
// use the previous commented out line instead
|
||||
this.serializer = LegacyServices.loadFirst(Serializer.class);
|
||||
}
|
||||
|
||||
if (payload == null && Collections.isEmpty(claims)) {
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
package io.jsonwebtoken.impl;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Header;
|
||||
import io.jsonwebtoken.JwsHeader;
|
||||
import io.jsonwebtoken.JwtBuilder;
|
||||
import io.jsonwebtoken.JwtFactory;
|
||||
import io.jsonwebtoken.JwtParser;
|
||||
import io.jsonwebtoken.impl.DefaultClaims;
|
||||
import io.jsonwebtoken.impl.DefaultHeader;
|
||||
import io.jsonwebtoken.impl.DefaultJwsHeader;
|
||||
import io.jsonwebtoken.impl.DefaultJwtBuilder;
|
||||
import io.jsonwebtoken.impl.DefaultJwtParser;
|
||||
|
||||
public class DefaultJwtFactory implements JwtFactory {
|
||||
|
||||
@Override
|
||||
public Header header() {
|
||||
return new DefaultHeader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Header header(final Map<String, Object> map) {
|
||||
return new DefaultHeader(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JwsHeader jwsHeader() {
|
||||
return new DefaultJwsHeader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JwsHeader jwsHeader(final Map<String, Object> header) {
|
||||
return new DefaultJwsHeader(header);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Claims claims() {
|
||||
return new DefaultClaims();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Claims claims(final Map<String, Object> claims) {
|
||||
return new DefaultClaims(claims);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JwtBuilder builder() {
|
||||
return new DefaultJwtBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JwtParser parser() {
|
||||
return new DefaultJwtParser();
|
||||
}
|
||||
}
|
|
@ -39,13 +39,12 @@ import io.jsonwebtoken.UnsupportedJwtException;
|
|||
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver;
|
||||
import io.jsonwebtoken.impl.crypto.DefaultJwtSignatureValidator;
|
||||
import io.jsonwebtoken.impl.crypto.JwtSignatureValidator;
|
||||
import io.jsonwebtoken.impl.io.InstanceLocator;
|
||||
import io.jsonwebtoken.impl.lang.LegacyServices;
|
||||
import io.jsonwebtoken.io.Decoder;
|
||||
import io.jsonwebtoken.io.Decoders;
|
||||
import io.jsonwebtoken.io.DeserializationException;
|
||||
import io.jsonwebtoken.io.Deserializer;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Classes;
|
||||
import io.jsonwebtoken.lang.DateFormats;
|
||||
import io.jsonwebtoken.lang.Objects;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
|
@ -107,13 +106,6 @@ public class DefaultJwtParser implements JwtParser {
|
|||
this.base64UrlDecoder = base64UrlDecoder;
|
||||
this.deserializer = deserializer;
|
||||
this.compressionCodecResolver = compressionCodecResolver;
|
||||
|
||||
if (this.deserializer == null) {
|
||||
//try to find one based on the runtime environment:
|
||||
InstanceLocator<Deserializer<Map<String, ?>>> locator =
|
||||
Classes.newInstance("io.jsonwebtoken.impl.io.RuntimeClasspathDeserializerLocator");
|
||||
this.deserializer = locator.getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -255,12 +247,12 @@ public class DefaultJwtParser implements JwtParser {
|
|||
@Override
|
||||
public Jwt parse(String jwt) throws ExpiredJwtException, MalformedJwtException, SignatureException {
|
||||
|
||||
// TODO move this to constructor before 1.0
|
||||
// TODO, this logic is only need for a now deprecated code path
|
||||
// remove this block in v1.0 (the equivalent is already in DefaultJwtParserBuilder)
|
||||
if (this.deserializer == null) {
|
||||
//try to find one based on the runtime environment:
|
||||
InstanceLocator<Deserializer<Map<String, ?>>> locator =
|
||||
Classes.newInstance("io.jsonwebtoken.impl.io.RuntimeClasspathDeserializerLocator");
|
||||
this.deserializer = locator.getInstance();
|
||||
// try to find one based on the services available
|
||||
// TODO: This util class will throw a UnavailableImplementationException here to retain behavior of previous version, remove in v1.0
|
||||
this.deserializer = LegacyServices.loadFirst(Deserializer.class);
|
||||
}
|
||||
|
||||
Assert.hasText(jwt, "JWT String argument cannot be null or empty.");
|
||||
|
|
|
@ -26,6 +26,8 @@ import io.jsonwebtoken.io.Decoder;
|
|||
import io.jsonwebtoken.io.Decoders;
|
||||
import io.jsonwebtoken.io.Deserializer;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Services;
|
||||
|
||||
import java.security.Key;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
@ -170,6 +172,14 @@ public class DefaultJwtParserBuilder implements JwtParserBuilder {
|
|||
|
||||
@Override
|
||||
public JwtParser build() {
|
||||
|
||||
// Only lookup the deserializer IF it is null. It is possible a Deserializer implementation was set
|
||||
// that is NOT exposed as a service and no other implementations are available for lookup.
|
||||
if (this.deserializer == null) {
|
||||
// try to find one based on the services available:
|
||||
this.deserializer = Services.loadFirst(Deserializer.class);
|
||||
}
|
||||
|
||||
return new ImmutableJwtParser(
|
||||
new DefaultJwtParser(signingKeyResolver,
|
||||
key,
|
||||
|
|
|
@ -17,11 +17,17 @@ package io.jsonwebtoken.impl.compression;
|
|||
|
||||
import io.jsonwebtoken.CompressionCodec;
|
||||
import io.jsonwebtoken.CompressionCodecResolver;
|
||||
import io.jsonwebtoken.CompressionCodecs;
|
||||
import io.jsonwebtoken.CompressionException;
|
||||
import io.jsonwebtoken.Header;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import io.jsonwebtoken.lang.Services;
|
||||
import io.jsonwebtoken.lang.Strings;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link CompressionCodecResolver} that supports the following:
|
||||
* <p>
|
||||
|
@ -45,6 +51,22 @@ import io.jsonwebtoken.lang.Strings;
|
|||
*/
|
||||
public class DefaultCompressionCodecResolver implements CompressionCodecResolver {
|
||||
|
||||
private static final String MISSING_COMPRESSION_MESSAGE = "Unable to find an implementation for compression algorithm [%s] using java.util.ServiceLoader. Ensure you include a backing implementation .jar in the classpath, for example jjwt-impl.jar, or your own .jar for custom implementations.";
|
||||
|
||||
private final Map<String, CompressionCodec> codecs;
|
||||
|
||||
public DefaultCompressionCodecResolver() {
|
||||
Map<String, CompressionCodec> codecMap = new HashMap<>();
|
||||
for (CompressionCodec codec : Services.loadAll(CompressionCodec.class)) {
|
||||
codecMap.put(codec.getAlgorithmName().toUpperCase(), codec);
|
||||
}
|
||||
|
||||
codecMap.put(CompressionCodecs.DEFLATE.getAlgorithmName().toUpperCase(), CompressionCodecs.DEFLATE);
|
||||
codecMap.put(CompressionCodecs.GZIP.getAlgorithmName().toUpperCase(), CompressionCodecs.GZIP);
|
||||
|
||||
codecs = Collections.unmodifiableMap(codecMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompressionCodec resolveCompressionCodec(Header header) {
|
||||
String cmpAlg = getAlgorithmFromHeader(header);
|
||||
|
@ -54,14 +76,7 @@ public class DefaultCompressionCodecResolver implements CompressionCodecResolver
|
|||
if (!hasCompressionAlgorithm) {
|
||||
return null;
|
||||
}
|
||||
if (io.jsonwebtoken.CompressionCodecs.DEFLATE.getAlgorithmName().equalsIgnoreCase(cmpAlg)) {
|
||||
return io.jsonwebtoken.CompressionCodecs.DEFLATE;
|
||||
}
|
||||
if (io.jsonwebtoken.CompressionCodecs.GZIP.getAlgorithmName().equalsIgnoreCase(cmpAlg)) {
|
||||
return io.jsonwebtoken.CompressionCodecs.GZIP;
|
||||
}
|
||||
|
||||
throw new CompressionException("Unsupported compression algorithm '" + cmpAlg + "'");
|
||||
return byName(cmpAlg);
|
||||
}
|
||||
|
||||
private String getAlgorithmFromHeader(Header header) {
|
||||
|
@ -69,4 +84,15 @@ public class DefaultCompressionCodecResolver implements CompressionCodecResolver
|
|||
|
||||
return header.getCompressionAlgorithm();
|
||||
}
|
||||
|
||||
private CompressionCodec byName(String name) {
|
||||
Assert.hasText(name, "'name' must not be empty");
|
||||
|
||||
CompressionCodec codec = codecs.get(name.toUpperCase());
|
||||
if (codec == null) {
|
||||
throw new CompressionException(String.format(MISSING_COMPRESSION_MESSAGE, name));
|
||||
}
|
||||
|
||||
return codec;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
package io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.security.KeyPairGenerator;
|
||||
|
||||
import java.security.KeyPair;
|
||||
|
||||
public class EllipticCurveKeyPairGenerator implements KeyPairGenerator {
|
||||
@Override
|
||||
public boolean supports(SignatureAlgorithm alg) {
|
||||
return alg.isEllipticCurve();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyPair generateKeyPair(SignatureAlgorithm alg) {
|
||||
return EllipticCurveProvider.generateKeyPair(alg);
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.security.KeyGenerator;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
public final class MacKeyGenerator implements KeyGenerator {
|
||||
|
||||
@Override
|
||||
public boolean supports(SignatureAlgorithm alg) {
|
||||
return alg.isHmac();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey generateKey(SignatureAlgorithm alg) {
|
||||
return MacProvider.generateKey(alg);
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package io.jsonwebtoken.impl.crypto;
|
||||
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.security.KeyPairGenerator;
|
||||
|
||||
import java.security.KeyPair;
|
||||
|
||||
public class RsaKeyPairGenerator implements KeyPairGenerator {
|
||||
@Override
|
||||
public boolean supports(SignatureAlgorithm alg) {
|
||||
return alg.isRsa();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyPair generateKeyPair(SignatureAlgorithm alg) {
|
||||
return RsaProvider.generateKeyPair(alg);
|
||||
}
|
||||
}
|
|
@ -132,6 +132,7 @@ public abstract class RsaProvider extends SignatureProvider {
|
|||
* @see #generateKeyPair(String, int, SecureRandom)
|
||||
* @since 0.10.0
|
||||
*/
|
||||
@SuppressWarnings("unused") //used by io.jsonwebtoken.security.Keys
|
||||
public static KeyPair generateKeyPair(SignatureAlgorithm alg) {
|
||||
Assert.isTrue(alg.isRsa(), "Only RSA algorithms are supported by this method.");
|
||||
int keySizeInBits = 4096;
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.io;
|
||||
|
||||
import io.jsonwebtoken.io.Deserializer;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* @since 0.10.0
|
||||
*/
|
||||
public class RuntimeClasspathDeserializerLocator<T> implements InstanceLocator<Deserializer<T>> {
|
||||
|
||||
private static final AtomicReference<Deserializer> DESERIALIZER = new AtomicReference<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Deserializer<T> getInstance() {
|
||||
Deserializer<T> deserializer = DESERIALIZER.get();
|
||||
if (deserializer == null) {
|
||||
deserializer = locate();
|
||||
Assert.state(deserializer != null, "locate() cannot return null.");
|
||||
if (!compareAndSet(deserializer)) {
|
||||
deserializer = DESERIALIZER.get();
|
||||
}
|
||||
}
|
||||
Assert.state(deserializer != null, "deserializer cannot be null.");
|
||||
return deserializer;
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") //to allow testing override
|
||||
protected Deserializer<T> locate() {
|
||||
ServiceLoader<Deserializer> serviceLoader = ServiceLoader.load(Deserializer.class);
|
||||
Iterator<Deserializer> iterator = serviceLoader.iterator();
|
||||
if(iterator.hasNext()) {
|
||||
return (Deserializer<T>)iterator.next();
|
||||
} else {
|
||||
throw new IllegalStateException("Unable to discover any JSON Deserializer implementations on the classpath.");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") //to allow testing override
|
||||
protected boolean compareAndSet(Deserializer<T> d) {
|
||||
return DESERIALIZER.compareAndSet(null, d);
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.io;
|
||||
|
||||
import io.jsonwebtoken.io.Serializer;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* @since 0.10.0
|
||||
*/
|
||||
public class RuntimeClasspathSerializerLocator implements InstanceLocator<Serializer> {
|
||||
|
||||
private static final AtomicReference<Serializer<Object>> SERIALIZER = new AtomicReference<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Serializer<Object> getInstance() {
|
||||
Serializer<Object> serializer = SERIALIZER.get();
|
||||
if (serializer == null) {
|
||||
serializer = locate();
|
||||
Assert.state(serializer != null, "locate() cannot return null.");
|
||||
if (!compareAndSet(serializer)) {
|
||||
serializer = SERIALIZER.get();
|
||||
}
|
||||
}
|
||||
Assert.state(serializer != null, "serializer cannot be null.");
|
||||
return serializer;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "WeakerAccess"}) //to allow testing override
|
||||
protected Serializer<Object> locate() {
|
||||
ServiceLoader<Serializer> serviceLoader = ServiceLoader.load(Serializer.class);
|
||||
Iterator<Serializer> iterator = serviceLoader.iterator();
|
||||
if(iterator.hasNext()) {
|
||||
return (Serializer<Object>)iterator.next();
|
||||
} else {
|
||||
throw new IllegalStateException("Unable to discover any JSON Serializer implementations on the classpath.");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") //to allow testing override
|
||||
protected boolean compareAndSet(Serializer<Object> s) {
|
||||
return SERIALIZER.compareAndSet(null, s);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package io.jsonwebtoken.impl.lang;
|
||||
|
||||
import io.jsonwebtoken.lang.Classes;
|
||||
import io.jsonwebtoken.lang.Services;
|
||||
import io.jsonwebtoken.lang.UnavailableImplementationException;
|
||||
import io.jsonwebtoken.lang.UnknownClassException;
|
||||
|
||||
/**
|
||||
* A backward compatibility {@link Services} utility to help migrate away from {@link Classes#newInstance(String)}.
|
||||
* TODO: remove before v1.0
|
||||
* @deprecated use {@link Services} directly
|
||||
*/
|
||||
@Deprecated
|
||||
public final class LegacyServices {
|
||||
|
||||
/**
|
||||
* Wraps {@code Services.loadFirst} and throws a {@link UnknownClassException} instead of a
|
||||
* {@link UnavailableImplementationException} to retain the previous behavior. This method should be used when
|
||||
* to retain the previous behavior of methods that throw an unchecked UnknownClassException.
|
||||
*/
|
||||
public static <T> T loadFirst(Class<T> spi) {
|
||||
try {
|
||||
return Services.loadFirst(spi);
|
||||
} catch (UnavailableImplementationException e) {
|
||||
throw new UnknownClassException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
io.jsonwebtoken.impl.compression.DeflateCompressionCodec
|
||||
io.jsonwebtoken.impl.compression.GzipCompressionCodec
|
|
@ -1 +0,0 @@
|
|||
io.jsonwebtoken.impl.DefaultCompressionCodecFactory
|
|
@ -1 +0,0 @@
|
|||
io.jsonwebtoken.impl.DefaultJwtFactory
|
|
@ -1 +0,0 @@
|
|||
io.jsonwebtoken.impl.crypto.MacKeyGenerator
|
|
@ -1,2 +0,0 @@
|
|||
io.jsonwebtoken.impl.crypto.RsaKeyPairGenerator
|
||||
io.jsonwebtoken.impl.crypto.EllipticCurveKeyPairGenerator
|
|
@ -19,8 +19,9 @@ import io.jsonwebtoken.impl.DefaultHeader
|
|||
import io.jsonwebtoken.impl.DefaultJwsHeader
|
||||
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver
|
||||
import io.jsonwebtoken.impl.compression.GzipCompressionCodec
|
||||
import io.jsonwebtoken.impl.io.RuntimeClasspathSerializerLocator
|
||||
import io.jsonwebtoken.io.Encoders
|
||||
import io.jsonwebtoken.io.Serializer
|
||||
import io.jsonwebtoken.lang.Services
|
||||
import io.jsonwebtoken.lang.Strings
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import io.jsonwebtoken.security.WeakKeyException
|
||||
|
@ -43,7 +44,7 @@ class DeprecatedJwtsTest {
|
|||
}
|
||||
|
||||
protected static String toJson(o) {
|
||||
def serializer = new RuntimeClasspathSerializerLocator().getInstance()
|
||||
def serializer = Services.loadFirst(Serializer)
|
||||
byte[] bytes = serializer.serialize(o)
|
||||
return new String(bytes, Strings.UTF_8)
|
||||
}
|
||||
|
|
|
@ -19,8 +19,9 @@ import io.jsonwebtoken.impl.DefaultHeader
|
|||
import io.jsonwebtoken.impl.DefaultJwsHeader
|
||||
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver
|
||||
import io.jsonwebtoken.impl.compression.GzipCompressionCodec
|
||||
import io.jsonwebtoken.impl.io.RuntimeClasspathSerializerLocator
|
||||
import io.jsonwebtoken.io.Encoders
|
||||
import io.jsonwebtoken.io.Serializer
|
||||
import io.jsonwebtoken.lang.Services
|
||||
import io.jsonwebtoken.lang.Strings
|
||||
import io.jsonwebtoken.security.Keys
|
||||
import io.jsonwebtoken.security.WeakKeyException
|
||||
|
@ -43,7 +44,7 @@ class JwtsTest {
|
|||
}
|
||||
|
||||
protected static String toJson(o) {
|
||||
def serializer = new RuntimeClasspathSerializerLocator().getInstance()
|
||||
def serializer = Services.loadFirst(Serializer)
|
||||
byte[] bytes = serializer.serialize(o)
|
||||
return new String(bytes, Strings.UTF_8)
|
||||
}
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
package io.jsonwebtoken.impl
|
||||
|
||||
import io.jsonwebtoken.impl.DefaultCompressionCodecFactory
|
||||
import io.jsonwebtoken.impl.compression.DeflateCompressionCodec
|
||||
import io.jsonwebtoken.impl.compression.GzipCompressionCodec
|
||||
import org.junit.Test
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
|
||||
class DefaultCompressionCodecFactoryTest {
|
||||
@Test
|
||||
void testCreateDeflateCodec() {
|
||||
|
||||
def deflate = new DefaultCompressionCodecFactory().deflateCodec()
|
||||
|
||||
assertEquals(DeflateCompressionCodec, deflate.class)
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateGzipCodec() {
|
||||
|
||||
def gzipCodec = new DefaultCompressionCodecFactory().gzipCodec()
|
||||
|
||||
assertEquals(GzipCompressionCodec, gzipCodec.class)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (C) 2019 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.compression
|
||||
|
||||
import io.jsonwebtoken.CompressionCodec
|
||||
import io.jsonwebtoken.CompressionException
|
||||
import io.jsonwebtoken.impl.DefaultHeader
|
||||
import io.jsonwebtoken.impl.io.FakeServiceDescriptorClassLoader
|
||||
import io.jsonwebtoken.lang.Services
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
import io.jsonwebtoken.CompressionCodecs
|
||||
|
||||
import static org.hamcrest.CoreMatchers.hasItem
|
||||
import static org.hamcrest.CoreMatchers.instanceOf
|
||||
import static org.hamcrest.CoreMatchers.is
|
||||
import static org.hamcrest.CoreMatchers.nullValue
|
||||
import static org.hamcrest.MatcherAssert.assertThat
|
||||
|
||||
class DefaultCompressionCodecResolverTest {
|
||||
|
||||
@Test
|
||||
void resolveHeaderTest() {
|
||||
assertThat new DefaultCompressionCodecResolver().resolveCompressionCodec(
|
||||
new DefaultHeader()), nullValue()
|
||||
assertThat new DefaultCompressionCodecResolver().resolveCompressionCodec(
|
||||
new DefaultHeader().setCompressionAlgorithm("def")), is(CompressionCodecs.DEFLATE)
|
||||
assertThat new DefaultCompressionCodecResolver().resolveCompressionCodec(
|
||||
new DefaultHeader().setCompressionAlgorithm("gzip")), is(CompressionCodecs.GZIP)
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidCompressionNameTest() {
|
||||
try {
|
||||
new DefaultCompressionCodecResolver().resolveCompressionCodec(
|
||||
new DefaultHeader().setCompressionAlgorithm("expected-missing"))
|
||||
Assert.fail("Expected CompressionException to be thrown")
|
||||
} catch (CompressionException e) {
|
||||
assertThat e.message, is(String.format(DefaultCompressionCodecResolver.MISSING_COMPRESSION_MESSAGE, "expected-missing"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void overrideDefaultCompressionImplTest() {
|
||||
FakeServiceDescriptorClassLoader.runWithFake "io.jsonwebtoken.io.compression.CompressionCodec.test.override", {
|
||||
|
||||
// first make sure the service loader actually resolves the test class
|
||||
assertThat Services.loadAll(CompressionCodec), hasItem(instanceOf(YagCompressionCodec))
|
||||
|
||||
// now we know the class is loadable, make sure we ALWAYS return the GZIP impl
|
||||
assertThat new DefaultCompressionCodecResolver().byName("gzip"), instanceOf(GzipCompressionCodec)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyCompressionAlgInHeaderTest() {
|
||||
try {
|
||||
new DefaultCompressionCodecResolver().byName("")
|
||||
Assert.fail("Expected IllegalArgumentException to be thrown")
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (C) 2019 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.compression
|
||||
|
||||
import io.jsonwebtoken.CompressionCodec
|
||||
import io.jsonwebtoken.CompressionException
|
||||
|
||||
/**
|
||||
* Yet Another GZIP CompressionCodec. This codec has the same name as the Official GZIP impl. The DefaultCompressionCodecResolver will NOT resolve this class.
|
||||
*/
|
||||
class YagCompressionCodec implements CompressionCodec {
|
||||
|
||||
@Override
|
||||
String getAlgorithmName() {
|
||||
return new GzipCompressionCodec().getAlgorithmName();
|
||||
}
|
||||
|
||||
@Override
|
||||
byte[] compress(byte[] payload) throws CompressionException {
|
||||
return new byte[0]
|
||||
}
|
||||
|
||||
@Override
|
||||
byte[] decompress(byte[] compressed) throws CompressionException {
|
||||
return new byte[0]
|
||||
}
|
||||
}
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* Copyright (C) 2019 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.io
|
||||
|
||||
class FakeServiceDescriptorClassLoader extends ClassLoader {
|
||||
|
@ -16,4 +31,16 @@ class FakeServiceDescriptorClassLoader extends ClassLoader {
|
|||
return super.getResources(name)
|
||||
}
|
||||
}
|
||||
|
||||
static void runWithFake(String fakeDescriptor, Closure closure) {
|
||||
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader()
|
||||
try {
|
||||
Thread.currentThread().setContextClassLoader(new FakeServiceDescriptorClassLoader(originalClassLoader, fakeDescriptor))
|
||||
closure.run()
|
||||
} finally {
|
||||
if(originalClassLoader != null) {
|
||||
Thread.currentThread().setContextClassLoader(originalClassLoader)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* Copyright (C) 2019 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.io
|
||||
|
||||
class NoServiceDescriptorClassLoader extends ClassLoader {
|
||||
|
@ -13,4 +28,16 @@ class NoServiceDescriptorClassLoader extends ClassLoader {
|
|||
return super.getResources(name)
|
||||
}
|
||||
}
|
||||
|
||||
static void runWith(Closure closure) {
|
||||
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader()
|
||||
try {
|
||||
Thread.currentThread().setContextClassLoader(new NoServiceDescriptorClassLoader(originalClassLoader))
|
||||
closure.run()
|
||||
} finally {
|
||||
if(originalClassLoader != null) {
|
||||
Thread.currentThread().setContextClassLoader(originalClassLoader)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.io
|
||||
|
||||
import io.jsonwebtoken.io.Deserializer
|
||||
import io.jsonwebtoken.jackson.io.JacksonDeserializer
|
||||
import io.jsonwebtoken.orgjson.io.OrgJsonDeserializer
|
||||
import io.jsonwebtoken.gson.io.GsonDeserializer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
import static org.easymock.EasyMock.createMock
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class RuntimeClasspathDeserializerLocatorTest {
|
||||
|
||||
private static final String TEST_SERVICE_DESCRIPTOR = "io.jsonwebtoken.io.Deserializer.test.orgjson"
|
||||
|
||||
private ClassLoader originalClassLoader
|
||||
|
||||
@Before
|
||||
void setUp() {
|
||||
RuntimeClasspathDeserializerLocator.DESERIALIZER.set(null)
|
||||
}
|
||||
|
||||
@After
|
||||
void teardown() {
|
||||
RuntimeClasspathDeserializerLocator.DESERIALIZER.set(null)
|
||||
restoreOriginalClassLoader()
|
||||
}
|
||||
|
||||
private void restoreOriginalClassLoader() {
|
||||
if(originalClassLoader != null) {
|
||||
Thread.currentThread().setContextClassLoader(originalClassLoader)
|
||||
originalClassLoader = null
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClassIsNotAvailable() {
|
||||
prepareNoServiceDescriptorClassLoader()
|
||||
|
||||
try {
|
||||
new RuntimeClasspathDeserializerLocator().getInstance()
|
||||
fail 'Located Deserializer class, whereas none was expected.'
|
||||
} catch (Exception ex) {
|
||||
assertEquals 'Unable to discover any JSON Deserializer implementations on the classpath.', ex.message
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCompareAndSetFalse() {
|
||||
Deserializer deserializer = createMock(Deserializer)
|
||||
def locator = new RuntimeClasspathDeserializerLocator() {
|
||||
@Override
|
||||
protected boolean compareAndSet(Deserializer d) {
|
||||
RuntimeClasspathDeserializerLocator.DESERIALIZER.set(deserializer)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
def returned = locator.getInstance()
|
||||
assertSame deserializer, returned
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException)
|
||||
void testLocateReturnsNull() {
|
||||
def locator = new RuntimeClasspathDeserializerLocator() {
|
||||
@Override
|
||||
protected Deserializer locate() {
|
||||
return null
|
||||
}
|
||||
}
|
||||
locator.getInstance()
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException)
|
||||
void testCompareAndSetFalseWithNullReturn() {
|
||||
def locator = new RuntimeClasspathDeserializerLocator() {
|
||||
@Override
|
||||
protected boolean compareAndSet(Deserializer d) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
locator.getInstance()
|
||||
}
|
||||
|
||||
@Test
|
||||
void testJackson() {
|
||||
def deserializer = new RuntimeClasspathDeserializerLocator().getInstance()
|
||||
assertTrue deserializer instanceof JacksonDeserializer
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOrgJson() {
|
||||
prepareFakeServiceClassLoader()
|
||||
|
||||
def deserializer = new RuntimeClasspathDeserializerLocator().getInstance()
|
||||
assertTrue deserializer instanceof OrgJsonDeserializer
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGson() {
|
||||
def locator = new RuntimeClasspathDeserializerLocator() {
|
||||
@Override
|
||||
protected boolean isAvailable(String fqcn) {
|
||||
if (JacksonDeserializer.class.getName().equals(fqcn)) {
|
||||
return false; //skip it to allow the Gson impl to be created
|
||||
}
|
||||
if (OrgJsonDeserializer.class.getName().equals(fqcn)) {
|
||||
return false; //skip it to allow the Gson impl to be created
|
||||
}
|
||||
return super.isAvailable(fqcn)
|
||||
}
|
||||
}
|
||||
|
||||
def deserializer = locator.getInstance()
|
||||
assertTrue deserializer instanceof GsonDeserializer
|
||||
}
|
||||
|
||||
private void prepareNoServiceDescriptorClassLoader() {
|
||||
originalClassLoader = Thread.currentThread().getContextClassLoader()
|
||||
Thread.currentThread().setContextClassLoader(new NoServiceDescriptorClassLoader(originalClassLoader))
|
||||
}
|
||||
|
||||
private void prepareFakeServiceClassLoader() {
|
||||
originalClassLoader = Thread.currentThread().getContextClassLoader()
|
||||
Thread.currentThread().setContextClassLoader(new FakeServiceDescriptorClassLoader(originalClassLoader, TEST_SERVICE_DESCRIPTOR))
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 jsonwebtoken.io
|
||||
*
|
||||
* Licensed 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 io.jsonwebtoken.impl.io
|
||||
|
||||
import io.jsonwebtoken.io.Serializer
|
||||
import io.jsonwebtoken.jackson.io.JacksonSerializer
|
||||
import io.jsonwebtoken.orgjson.io.OrgJsonSerializer
|
||||
import io.jsonwebtoken.gson.io.GsonSerializer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
import static org.easymock.EasyMock.createMock
|
||||
import static org.junit.Assert.*
|
||||
|
||||
class RuntimeClasspathSerializerLocatorTest {
|
||||
|
||||
private static final String TEST_SERVICE_DESCRIPTOR = "io.jsonwebtoken.io.Serializer.test.orgjson"
|
||||
|
||||
private ClassLoader originalClassLoader
|
||||
|
||||
@Before
|
||||
void setUp() {
|
||||
RuntimeClasspathSerializerLocator.SERIALIZER.set(null)
|
||||
}
|
||||
|
||||
@After
|
||||
void teardown() {
|
||||
RuntimeClasspathSerializerLocator.SERIALIZER.set(null)
|
||||
restoreOriginalClassLoader()
|
||||
}
|
||||
|
||||
private void restoreOriginalClassLoader() {
|
||||
if(originalClassLoader != null) {
|
||||
Thread.currentThread().setContextClassLoader(originalClassLoader)
|
||||
originalClassLoader = null
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClassIsNotAvailable() {
|
||||
prepareNoServiceDescriptorClassLoader()
|
||||
|
||||
try {
|
||||
new RuntimeClasspathSerializerLocator().getInstance()
|
||||
fail 'Located Deserializer class, whereas none was expected.'
|
||||
} catch (Exception ex) {
|
||||
assertEquals 'Unable to discover any JSON Serializer implementations on the classpath.', ex.message
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCompareAndSetFalse() {
|
||||
Serializer serializer = createMock(Serializer)
|
||||
def locator = new RuntimeClasspathSerializerLocator() {
|
||||
@Override
|
||||
protected boolean compareAndSet(Serializer s) {
|
||||
RuntimeClasspathSerializerLocator.SERIALIZER.set(serializer)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
def returned = locator.getInstance()
|
||||
assertSame serializer, returned
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException)
|
||||
void testLocateReturnsNull() {
|
||||
def locator = new RuntimeClasspathSerializerLocator() {
|
||||
@Override
|
||||
protected Serializer<Object> locate() {
|
||||
return null
|
||||
}
|
||||
}
|
||||
locator.getInstance()
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException)
|
||||
void testCompareAndSetFalseWithNullReturn() {
|
||||
def locator = new RuntimeClasspathSerializerLocator() {
|
||||
@Override
|
||||
protected boolean compareAndSet(Serializer<Object> s) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
locator.getInstance()
|
||||
}
|
||||
|
||||
@Test
|
||||
void testJackson() {
|
||||
def serializer = new RuntimeClasspathSerializerLocator().getInstance()
|
||||
assertTrue serializer instanceof JacksonSerializer
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOrgJson() {
|
||||
prepareFakeServiceClassLoader()
|
||||
|
||||
def serializer = new RuntimeClasspathSerializerLocator().getInstance()
|
||||
assertTrue serializer instanceof OrgJsonSerializer
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGson() {
|
||||
def locator = new RuntimeClasspathSerializerLocator() {
|
||||
@Override
|
||||
protected boolean isAvailable(String fqcn) {
|
||||
if (JacksonSerializer.class.getName().equals(fqcn)) {
|
||||
return false //skip it to allow the Gson impl to be created
|
||||
}
|
||||
if (OrgJsonSerializer.class.getName().equals(fqcn)) {
|
||||
return false //skip it to allow the Gson impl to be created
|
||||
}
|
||||
return super.isAvailable(fqcn)
|
||||
}
|
||||
}
|
||||
|
||||
def serializer = locator.getInstance()
|
||||
assertTrue serializer instanceof GsonSerializer
|
||||
}
|
||||
|
||||
private void prepareNoServiceDescriptorClassLoader() {
|
||||
originalClassLoader = Thread.currentThread().getContextClassLoader()
|
||||
Thread.currentThread().setContextClassLoader(new NoServiceDescriptorClassLoader(originalClassLoader))
|
||||
}
|
||||
|
||||
private void prepareFakeServiceClassLoader() {
|
||||
originalClassLoader = Thread.currentThread().getContextClassLoader()
|
||||
Thread.currentThread().setContextClassLoader(new FakeServiceDescriptorClassLoader(originalClassLoader, TEST_SERVICE_DESCRIPTOR))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package io.jsonwebtoken.impl.lang
|
||||
|
||||
import io.jsonwebtoken.lang.UnknownClassException
|
||||
import org.junit.Test
|
||||
|
||||
class LegacyServicesTest {
|
||||
|
||||
@Test(expected = UnknownClassException)
|
||||
void serviceNotFoundTest() {
|
||||
// try to load a class that will NOT have any services, i.e. this test class.
|
||||
LegacyServices.loadFirst(LegacyServicesTest)
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
io.jsonwebtoken.gson.io.GsonDeserializer
|
|
@ -1 +1 @@
|
|||
io.jsonwebtoken.io.OrgJsonDeserializer
|
||||
io.jsonwebtoken.orgjson.io.OrgJsonDeserializer
|
|
@ -0,0 +1 @@
|
|||
io.jsonwebtoken.gson.io.GsonSerializer
|
|
@ -1 +1 @@
|
|||
io.jsonwebtoken.io.OrgJsonSerializer
|
||||
io.jsonwebtoken.orgjson.io.OrgJsonSerializer
|
|
@ -0,0 +1 @@
|
|||
io.jsonwebtoken.impl.compression.YagCompressionCodec
|
Loading…
Reference in New Issue