-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #34227 from sberyozkin/test_jwt_custom_claim
Support custom claim types in quarkus-test-security-jwt and quarkus-test-security-oidc
- Loading branch information
Showing
11 changed files
with
578 additions
and
187 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
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
59 changes: 59 additions & 0 deletions
59
test-framework/security-jwt/src/main/java/io/quarkus/test/security/jwt/ClaimType.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,59 @@ | ||
package io.quarkus.test.security.jwt; | ||
|
||
import java.io.StringReader; | ||
|
||
import jakarta.json.Json; | ||
import jakarta.json.JsonArray; | ||
import jakarta.json.JsonObject; | ||
import jakarta.json.JsonReader; | ||
|
||
public enum ClaimType { | ||
LONG { | ||
@Override | ||
public Long convert(String value) { | ||
return Long.parseLong(value); | ||
} | ||
}, | ||
INTEGER { | ||
@Override | ||
public Integer convert(String value) { | ||
return Integer.parseInt(value); | ||
} | ||
}, | ||
BOOLEAN { | ||
@Override | ||
public Boolean convert(String value) { | ||
return Boolean.parseBoolean(value); | ||
} | ||
}, | ||
STRING { | ||
@Override | ||
public String convert(String value) { | ||
return value; | ||
} | ||
}, | ||
JSON_ARRAY { | ||
@Override | ||
public JsonArray convert(String value) { | ||
try (JsonReader jsonReader = Json.createReader(new StringReader(value))) { | ||
return jsonReader.readArray(); | ||
} | ||
} | ||
}, | ||
JSON_OBJECT { | ||
@Override | ||
public JsonObject convert(String value) { | ||
try (JsonReader jsonReader = Json.createReader(new StringReader(value))) { | ||
return jsonReader.readObject(); | ||
} | ||
} | ||
}, | ||
DEFAULT { | ||
@Override | ||
public String convert(String value) { | ||
return value; | ||
} | ||
}; | ||
|
||
abstract Object convert(String value); | ||
} |
110 changes: 110 additions & 0 deletions
110
...rity-jwt/src/main/java/io/quarkus/test/security/jwt/JwtTestSecurityIdentityAugmentor.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,110 @@ | ||
package io.quarkus.test.security.jwt; | ||
|
||
import java.lang.annotation.Annotation; | ||
import java.util.Arrays; | ||
import java.util.Collections; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
import jakarta.json.Json; | ||
import jakarta.json.JsonValue; | ||
|
||
import org.eclipse.microprofile.jwt.Claims; | ||
import org.eclipse.microprofile.jwt.JsonWebToken; | ||
|
||
import io.quarkus.security.identity.SecurityIdentity; | ||
import io.quarkus.security.runtime.QuarkusSecurityIdentity; | ||
import io.quarkus.test.security.TestSecurityIdentityAugmentor; | ||
|
||
public class JwtTestSecurityIdentityAugmentor implements TestSecurityIdentityAugmentor { | ||
private static Map<String, ClaimType> standardClaimTypes = Map.of( | ||
Claims.exp.name(), ClaimType.LONG, | ||
Claims.iat.name(), ClaimType.LONG, | ||
Claims.nbf.name(), ClaimType.LONG, | ||
Claims.auth_time.name(), ClaimType.LONG, | ||
Claims.email_verified.name(), ClaimType.BOOLEAN); | ||
|
||
@Override | ||
public SecurityIdentity augment(final SecurityIdentity identity, final Annotation[] annotations) { | ||
QuarkusSecurityIdentity.Builder builder = QuarkusSecurityIdentity.builder(identity); | ||
|
||
final JwtSecurity jwtSecurity = findJwtSecurity(annotations); | ||
builder.setPrincipal(new JsonWebToken() { | ||
|
||
@Override | ||
public String getName() { | ||
return identity.getPrincipal().getName(); | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
@Override | ||
public <T> T getClaim(String claimName) { | ||
if (Claims.groups.name().equals(claimName)) { | ||
return (T) identity.getRoles(); | ||
} | ||
if (jwtSecurity != null && jwtSecurity.claims() != null) { | ||
for (Claim claim : jwtSecurity.claims()) { | ||
if (claim.key().equals(claimName)) { | ||
return (T) wrapValue(claim, convertClaimValue(claim)); | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
public Set<String> getClaimNames() { | ||
if (jwtSecurity != null && jwtSecurity.claims() != null) { | ||
return Arrays.stream(jwtSecurity.claims()).map(Claim::key).collect(Collectors.toSet()); | ||
} | ||
return Collections.emptySet(); | ||
} | ||
|
||
}); | ||
|
||
return builder.build(); | ||
} | ||
|
||
private static JwtSecurity findJwtSecurity(Annotation[] annotations) { | ||
for (Annotation ann : annotations) { | ||
if (ann instanceof JwtSecurity) { | ||
return (JwtSecurity) ann; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
private Object wrapValue(Claim claim, Object convertedClaimValue) { | ||
Claims claimType = getClaimType(claim.key()); | ||
if (Claims.UNKNOWN == claimType) { | ||
if (convertedClaimValue instanceof Long) { | ||
return Json.createValue((Long) convertedClaimValue); | ||
} else if (convertedClaimValue instanceof Integer) { | ||
return Json.createValue((Integer) convertedClaimValue); | ||
} else if (convertedClaimValue instanceof Boolean) { | ||
return (Boolean) convertedClaimValue ? JsonValue.TRUE : JsonValue.FALSE; | ||
} | ||
} | ||
return convertedClaimValue; | ||
} | ||
|
||
protected Claims getClaimType(String claimName) { | ||
Claims claimType; | ||
try { | ||
claimType = Claims.valueOf(claimName); | ||
} catch (IllegalArgumentException e) { | ||
claimType = Claims.UNKNOWN; | ||
} | ||
return claimType; | ||
} | ||
|
||
private Object convertClaimValue(Claim claim) { | ||
ClaimType type = claim.type(); | ||
if (type == ClaimType.DEFAULT && standardClaimTypes.containsKey(claim.key())) { | ||
type = standardClaimTypes.get(claim.key()); | ||
} | ||
return type.convert(claim.value()); | ||
} | ||
|
||
} |
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
75 changes: 75 additions & 0 deletions
75
...-jwt/src/test/java/io/quarkus/test/security/jwt/JwtTestSecurityIdentityAugmentorTest.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,75 @@ | ||
package io.quarkus.test.security.jwt; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
import java.lang.annotation.Annotation; | ||
import java.security.Principal; | ||
import java.util.Set; | ||
|
||
import jakarta.json.JsonArray; | ||
import jakarta.json.JsonNumber; | ||
import jakarta.json.JsonObject; | ||
import jakarta.json.JsonValue; | ||
|
||
import org.eclipse.microprofile.jwt.Claims; | ||
import org.eclipse.microprofile.jwt.JsonWebToken; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import io.quarkus.security.identity.SecurityIdentity; | ||
import io.quarkus.security.runtime.QuarkusSecurityIdentity; | ||
|
||
public class JwtTestSecurityIdentityAugmentorTest { | ||
|
||
@Test | ||
@JwtSecurity(claims = { | ||
@Claim(key = "exp", value = "123456789"), | ||
@Claim(key = "iat", value = "123456788"), | ||
@Claim(key = "nbf", value = "123456787"), | ||
@Claim(key = "auth_time", value = "123456786"), | ||
@Claim(key = "customlong", value = "123456785", type = ClaimType.LONG), | ||
@Claim(key = "email", value = "[email protected]"), | ||
@Claim(key = "email_verified", value = "true"), | ||
@Claim(key = "email_checked", value = "false", type = ClaimType.BOOLEAN), | ||
@Claim(key = "jsonarray_claim", value = "[\"1\", \"2\"]", type = ClaimType.JSON_ARRAY), | ||
@Claim(key = "jsonobject_claim", value = "{\"a\":\"1\", \"b\":\"2\"}", type = ClaimType.JSON_OBJECT) | ||
}) | ||
public void testClaimValues() throws Exception { | ||
SecurityIdentity identity = QuarkusSecurityIdentity.builder() | ||
.setPrincipal(new Principal() { | ||
@Override | ||
public String getName() { | ||
return "alice"; | ||
} | ||
|
||
}) | ||
.addRole("user") | ||
.build(); | ||
|
||
JwtTestSecurityIdentityAugmentor augmentor = new JwtTestSecurityIdentityAugmentor(); | ||
|
||
Annotation[] annotations = JwtTestSecurityIdentityAugmentorTest.class.getMethod("testClaimValues").getAnnotations(); | ||
JsonWebToken jwt = (JsonWebToken) augmentor.augment(identity, annotations).getPrincipal(); | ||
|
||
assertEquals("alice", jwt.getName()); | ||
assertEquals(Set.of("user"), jwt.getGroups()); | ||
|
||
assertEquals(123456789, jwt.getExpirationTime()); | ||
assertEquals(123456788, jwt.getIssuedAtTime()); | ||
assertEquals(123456787, (Long) jwt.getClaim(Claims.nbf.name())); | ||
assertEquals(123456786, (Long) jwt.getClaim(Claims.auth_time.name())); | ||
assertEquals(123456785, ((JsonNumber) jwt.getClaim("customlong")).longValue()); | ||
assertEquals("[email protected]", jwt.getClaim(Claims.email)); | ||
assertTrue((Boolean) jwt.getClaim(Claims.email_verified.name())); | ||
assertEquals(JsonValue.FALSE, jwt.getClaim("email_checked")); | ||
|
||
JsonArray array = jwt.getClaim("jsonarray_claim"); | ||
assertEquals("1", array.getString(0)); | ||
assertEquals("2", array.getString(1)); | ||
|
||
JsonObject map = jwt.getClaim("jsonobject_claim"); | ||
assertEquals("1", map.getString("a")); | ||
assertEquals("2", map.getString("b")); | ||
} | ||
|
||
} |
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
Oops, something went wrong.