Skip to content

Commit

Permalink
smallrye#211 - Prototype work to support custom Claim Converters.
Browse files Browse the repository at this point in the history
  • Loading branch information
radcortez committed Mar 30, 2020
1 parent d966828 commit 72c041f
Show file tree
Hide file tree
Showing 3 changed files with 350 additions and 0 deletions.
6 changes: 6 additions & 0 deletions implementation/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@
<artifactId>smallrye-config</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.smallrye.converters</groupId>
<artifactId>smallrye-converters</artifactId>
<version>1.0.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
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();
}
}
}
20 changes: 20 additions & 0 deletions implementation/src/test/resources/token-converter.json
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"
}

0 comments on commit 72c041f

Please sign in to comment.