forked from smallrye/smallrye-jwt
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
smallrye#211 - Prototype work to support custom Claim Converters.
- Loading branch information
Showing
3 changed files
with
350 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
324 changes: 324 additions & 0 deletions
324
implementation/src/test/java/io/smallrye/jwt/auth/cdi/ClaimConverterTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,324 @@ | ||
package io.smallrye.jwt.auth.cdi; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
|
||
import java.lang.annotation.Annotation; | ||
import java.lang.reflect.Member; | ||
import java.lang.reflect.Type; | ||
import java.util.Collections; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
import javax.enterprise.context.Dependent; | ||
import javax.enterprise.context.RequestScoped; | ||
import javax.enterprise.context.spi.CreationalContext; | ||
import javax.enterprise.inject.Default; | ||
import javax.enterprise.inject.Produces; | ||
import javax.enterprise.inject.spi.Annotated; | ||
import javax.enterprise.inject.spi.Bean; | ||
import javax.enterprise.inject.spi.BeanManager; | ||
import javax.enterprise.inject.spi.CDI; | ||
import javax.enterprise.inject.spi.InjectionPoint; | ||
import javax.enterprise.inject.spi.PassivationCapable; | ||
import javax.enterprise.util.AnnotationLiteral; | ||
import javax.inject.Inject; | ||
|
||
import org.eclipse.microprofile.jwt.Claim; | ||
import org.eclipse.microprofile.jwt.ClaimValue; | ||
import org.eclipse.microprofile.jwt.Claims; | ||
import org.eclipse.microprofile.jwt.JsonWebToken; | ||
import org.jboss.weld.junit4.WeldInitiator; | ||
import org.jose4j.jws.JsonWebSignature; | ||
import org.jose4j.jwt.JwtClaims; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
|
||
import io.smallrye.converters.SmallRyeConvertersBuilder; | ||
import io.smallrye.converters.api.Converters; | ||
import io.smallrye.jwt.KeyUtils; | ||
import io.smallrye.jwt.auth.principal.DefaultJWTCallerPrincipal; | ||
import io.smallrye.jwt.build.Jwt; | ||
|
||
@SuppressWarnings("CdiUnproxyableBeanTypesInspection") | ||
public class ClaimConverterTest { | ||
@Rule | ||
public WeldInitiator weld = WeldInitiator.from( | ||
ClaimInjectionBean.class, | ||
ClaimConverterTest.class, | ||
RawConverterBean.class, | ||
ClaimConverterBean.class, | ||
ClaimValueProducer.class, | ||
CommonJwtProducer.class) | ||
.addBeans() | ||
.activate(RequestScoped.class) | ||
// This will auto register with an Extension and the Type of the injection Point. | ||
.addBeans(new ClaimInjectionBean<>(String.class)) | ||
.addBeans(new ClaimInjectionBean<>(Byte.class)) | ||
.addBeans(new ClaimInjectionBean<>(Short.class)) | ||
.addBeans(new ClaimInjectionBean<>(Integer.class)) | ||
.addBeans(new ClaimInjectionBean<>(Long.class)) | ||
.addBeans(new ClaimInjectionBean<>(Float.class)) | ||
.addBeans(new ClaimInjectionBean<>(Double.class)) | ||
.addBeans(new ClaimInjectionBean<>(Boolean.class)) | ||
.addBeans(new ClaimInjectionBean<>(Character.class)) | ||
.inject(this) | ||
.build(); | ||
|
||
@Inject | ||
private JsonWebToken jsonWebToken; | ||
@Inject | ||
private RawConverterBean raw; | ||
@Inject | ||
private ClaimConverterBean claim; | ||
|
||
@Test | ||
public void convertString() { | ||
assertEquals("jdoe", raw.getName()); | ||
assertEquals("jdoe", claim.getName().getValue()); | ||
assertEquals("jdoe", jsonWebToken.<String> getClaim("preferred_username")); | ||
} | ||
|
||
@Test | ||
public void convertRawWrapperTypes() { | ||
assertEquals(1, raw.getByteClaim().byteValue()); | ||
assertEquals(9, raw.getShortClaim().shortValue()); | ||
assertEquals(99, raw.getIntegerClaim().intValue()); | ||
assertEquals(999, raw.getLongClaim().longValue()); | ||
assertEquals(99.9, raw.getFloatClaim(), 0.001); | ||
assertEquals(99.99, raw.getDoubeClaim(), 0.001); | ||
assertEquals(true, raw.getBooleanClaim()); | ||
} | ||
|
||
@Produces | ||
@RequestScoped | ||
private static JsonWebToken jwt() throws Exception { | ||
String jwt = Jwt.claims("/token-converter.json").sign(); | ||
JsonWebSignature jws = new JsonWebSignature(); | ||
jws.setKey(KeyUtils.readPublicKey("/publicKey.pem")); | ||
jws.setCompactSerialization(jwt); | ||
JwtClaims claims = JwtClaims.parse(jws.getPayload()); | ||
return new DefaultJWTCallerPrincipal(jwt, claims); | ||
} | ||
|
||
@RequestScoped | ||
private static class RawConverterBean { | ||
@Inject | ||
@Claim("preferred_username") | ||
private String name; | ||
@Inject | ||
@Claim("byte") | ||
private Byte byteClaim; | ||
@Inject | ||
@Claim("short") | ||
private Short shortClaim; | ||
@Inject | ||
@Claim("integer") | ||
private Integer integerClaim; | ||
@Inject | ||
@Claim("float") | ||
private Float floatClaim; | ||
@Inject | ||
@Claim("double") | ||
private Double doubeClaim; | ||
@Inject | ||
@Claim("boolean") | ||
private Boolean booleanClaim; | ||
@Inject | ||
@Claim("long") | ||
private Long longClaim; | ||
|
||
String getName() { | ||
return name; | ||
} | ||
|
||
public Byte getByteClaim() { | ||
return byteClaim; | ||
} | ||
|
||
public Short getShortClaim() { | ||
return shortClaim; | ||
} | ||
|
||
public Integer getIntegerClaim() { | ||
return integerClaim; | ||
} | ||
|
||
public Float getFloatClaim() { | ||
return floatClaim; | ||
} | ||
|
||
public Double getDoubeClaim() { | ||
return doubeClaim; | ||
} | ||
|
||
public Boolean getBooleanClaim() { | ||
return booleanClaim; | ||
} | ||
|
||
public Long getLongClaim() { | ||
return longClaim; | ||
} | ||
} | ||
|
||
@RequestScoped | ||
private static class ClaimConverterBean { | ||
@Inject | ||
@Claim("preferred_username") | ||
private ClaimValue<String> name; | ||
|
||
ClaimValue<String> getName() { | ||
return name; | ||
} | ||
} | ||
|
||
private static class ClaimInjectionBean<T> implements Bean<T>, PassivationCapable { | ||
private final Class klass; | ||
private final Converters converters; | ||
|
||
public ClaimInjectionBean(final Class klass) { | ||
this.klass = klass; | ||
this.converters = new SmallRyeConvertersBuilder().build(); | ||
} | ||
|
||
@Override | ||
public Class<?> getBeanClass() { | ||
return ClaimInjectionBean.class; | ||
} | ||
|
||
@Override | ||
public Set<InjectionPoint> getInjectionPoints() { | ||
return new HashSet<>(); | ||
} | ||
|
||
@Override | ||
public boolean isNullable() { | ||
return false; | ||
} | ||
|
||
@Override | ||
public T create(final CreationalContext<T> creationalContext) { | ||
final JsonWebToken jsonWebToken = CDI.current().select(JsonWebToken.class).get(); | ||
if (jsonWebToken == null) { | ||
return null; | ||
} | ||
|
||
final BeanManager beanManager = CDI.current().getBeanManager(); | ||
final InjectionPoint injectionPoint = (InjectionPoint) beanManager.getInjectableReference(new InjectionPoint() { | ||
@Override | ||
public Type getType() { | ||
return InjectionPoint.class; | ||
} | ||
|
||
@Override | ||
public Set<Annotation> getQualifiers() { | ||
return Collections.<Annotation> singleton(new AnnotationLiteral<Default>() { | ||
}); | ||
} | ||
|
||
@Override | ||
public Bean<?> getBean() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public Member getMember() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public Annotated getAnnotated() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public boolean isDelegate() { | ||
return false; | ||
} | ||
|
||
@Override | ||
public boolean isTransient() { | ||
return false; | ||
} | ||
}, creationalContext); | ||
|
||
final String claimName = getClaimName(injectionPoint); | ||
if (claimName != null) { | ||
final Object claim = jsonWebToken.getClaim(claimName); | ||
if (claim == null) { | ||
return null; | ||
} | ||
return (T) converters.convertValue(claim.toString(), klass); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
@Override | ||
public void destroy(final T instance, final CreationalContext<T> creationalContext) { | ||
|
||
} | ||
|
||
@Override | ||
public Set<Type> getTypes() { | ||
return Collections.singleton(klass); | ||
} | ||
|
||
@Override | ||
public Set<Annotation> getQualifiers() { | ||
return Collections.singleton(ClaimLiteral.INSTANCE); | ||
} | ||
|
||
@Override | ||
public Class<? extends Annotation> getScope() { | ||
return Dependent.class; | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return this.getClass().getName() + "_" + klass; | ||
} | ||
|
||
@Override | ||
public Set<Class<? extends Annotation>> getStereotypes() { | ||
return Collections.emptySet(); | ||
} | ||
|
||
@Override | ||
public boolean isAlternative() { | ||
return false; | ||
} | ||
|
||
@Override | ||
public String getId() { | ||
return getName(); | ||
} | ||
|
||
private static String getClaimName(InjectionPoint ip) { | ||
String name = null; | ||
for (Annotation ann : ip.getQualifiers()) { | ||
if (ann instanceof Claim) { | ||
Claim claim = (Claim) ann; | ||
name = claim.standard() == Claims.UNKNOWN ? claim.value() : claim.standard().name(); | ||
} | ||
} | ||
return name; | ||
} | ||
} | ||
|
||
private static final class ClaimLiteral extends AnnotationLiteral<Claim> implements Claim { | ||
|
||
public static final ClaimLiteral INSTANCE = new ClaimLiteral(); | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
@Override | ||
public String value() { | ||
return INSTANCE.value(); | ||
} | ||
|
||
@Override | ||
public Claims standard() { | ||
return INSTANCE.standard(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"iss": "https://server.example.com", | ||
"jti": "a-123", | ||
"sub": "24400320", | ||
"upn": "[email protected]", | ||
"preferred_username": "jdoe", | ||
"aud": "s6BhdRkqt3", | ||
"exp": 1311281970, | ||
"iat": 1311280970, | ||
"auth_time": 1311280969, | ||
|
||
"byte": 1, | ||
"short": 9, | ||
"integer": 99, | ||
"long": 999, | ||
"float": 99.9, | ||
"double": 99.99, | ||
"boolean": "true", | ||
"char": "y" | ||
} |