Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow other Collection's implementations (List, Set, Queue) to be used as JSON array #700

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 31 additions & 27 deletions lib/src/main/java/com/auth0/jwt/JWTCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -351,10 +351,10 @@ public Builder withClaim(String name, Instant value) throws IllegalArgumentExcep
/**
* Add a custom Map Claim with the given items.
* <p>
* Accepted nested types are {@linkplain Map} and {@linkplain List} with basic types
* Accepted nested types are {@linkplain Map} and {@linkplain Collection} with basic types
* {@linkplain Boolean}, {@linkplain Integer}, {@linkplain Long}, {@linkplain Double},
* {@linkplain String} and {@linkplain Date}. {@linkplain Map}s cannot contain null keys or values.
* {@linkplain List}s can contain null elements.
* {@linkplain Collection}s can contain null elements.
*
* @param name the Claim's name.
* @param map the Claim's key-values.
Expand All @@ -365,34 +365,34 @@ public Builder withClaim(String name, Map<String, ?> map) throws IllegalArgument
assertNonNull(name);
// validate map contents
if (map != null && !validateClaim(map)) {
throw new IllegalArgumentException("Expected map containing Map, List, Boolean, Integer, "
+ "Long, Double, String and Date");
throw new IllegalArgumentException("Expected map containing Map, Collection (List, Set, Queue), "
+ "Boolean, Integer, Long, Double, String and Date");
}
addClaim(name, map);
return this;
}

/**
* Add a custom List Claim with the given items.
* Add a custom Collection (can be a List, a Set or a Queue) Claim with the given items.
* <p>
* Accepted nested types are {@linkplain Map} and {@linkplain List} with basic types
* Accepted nested types are {@linkplain Map} and {@linkplain Collection} with basic types
* {@linkplain Boolean}, {@linkplain Integer}, {@linkplain Long}, {@linkplain Double},
* {@linkplain String} and {@linkplain Date}. {@linkplain Map}s cannot contain null keys or values.
* {@linkplain List}s can contain null elements.
* {@linkplain Collection}s can contain null elements.
*
* @param name the Claim's name.
* @param list the Claim's list of values.
* @param collection the Claim's collection of values.
* @return this same Builder instance.
* @throws IllegalArgumentException if the name is null, or if the list contents does not validate.
* @throws IllegalArgumentException if the name is null, or if the collection contents does not validate.
*/
public Builder withClaim(String name, List<?> list) throws IllegalArgumentException {
public Builder withClaim(String name, Collection<?> collection) throws IllegalArgumentException {
assertNonNull(name);
// validate list contents
if (list != null && !validateClaim(list)) {
throw new IllegalArgumentException("Expected list containing Map, List, Boolean, Integer, "
+ "Long, Double, String and Date");
// validate collection contents
if (collection != null && !validateClaim(collection)) {
throw new IllegalArgumentException("Expected Collection containing Map, Collection (List, Set, Queue), "
+ "Boolean, Integer, Long, Double, String and Date");
}
addClaim(name, list);
addClaim(name, collection);
return this;
}

Expand Down Expand Up @@ -455,10 +455,10 @@ public Builder withArrayClaim(String name, Long[] items) throws IllegalArgumentE
* Add specific Claims to set as the Payload. If the provided map is null then
* nothing is changed.
* <p>
* Accepted types are {@linkplain Map} and {@linkplain List} with basic types
* Accepted types are {@linkplain Map} and {@linkplain Collection} with basic types
* {@linkplain Boolean}, {@linkplain Integer}, {@linkplain Long}, {@linkplain Double},
* {@linkplain String} and {@linkplain Date}.
* {@linkplain Map}s and {@linkplain List}s can contain null elements.
* {@linkplain Map}s and {@linkplain Collection}s can contain null elements.
* </p>
*
* <p>
Expand All @@ -476,8 +476,8 @@ public Builder withPayload(Map<String, ?> payloadClaims) throws IllegalArgumentE
}

if (!validatePayload(payloadClaims)) {
throw new IllegalArgumentException("Claim values must only be of types Map, List, Boolean, Integer, "
+ "Long, Double, String, Date, Instant, and Null");
throw new IllegalArgumentException("Claim values must only be of types Map, Collection "
+ "(List, Set, Queue), Boolean, Integer, Long, Double, String, Date, Instant, and Null");
}

// add claims only after validating all claims so as not to corrupt the claims map of this builder
Expand Down Expand Up @@ -521,11 +521,15 @@ private boolean validatePayload(Map<String, ?> payload) {
assertNonNull(key);

Object value = entry.getValue();
if (value instanceof List && !validateClaim((List<?>) value)) {
if (value instanceof Collection && !validateClaim((Collection<?>) value)) {
return false;
} else if (value instanceof Map && !validateClaim((Map<?, ?>) value)) {
}

if (value instanceof Map && !validateClaim((Map<?, ?>) value)) {
return false;
} else if (!isSupportedType(value)) {
}

if (!isSupportedType(value)) {
return false;
}
}
Expand All @@ -547,9 +551,9 @@ private static boolean validateClaim(Map<?, ?> map) {
return true;
}

private static boolean validateClaim(List<?> list) {
// accept null values in list
for (Object object : list) {
private static boolean validateClaim(Collection<?> collection) {
// accept null values in collection
for (Object object : collection) {
if (!isSupportedType(object)) {
return false;
}
Expand All @@ -558,8 +562,8 @@ private static boolean validateClaim(List<?> list) {
}

private static boolean isSupportedType(Object value) {
if (value instanceof List) {
return validateClaim((List<?>) value);
if (value instanceof Collection) {
return validateClaim((Collection<?>) value);
} else if (value instanceof Map) {
return validateClaim((Map<?, ?>) value);
} else {
Expand Down
75 changes: 72 additions & 3 deletions lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -800,7 +800,7 @@ public void shouldOverwriteExistingPayloadWhenSettingSamePayloadKey() {
@Test
public void withPayloadShouldNotAllowCustomType() {
exception.expect(IllegalArgumentException.class);
exception.expectMessage("Claim values must only be of types Map, List, Boolean, Integer, Long, Double, String, Date, Instant, and Null");
exception.expectMessage("Claim values must only be of types Map, Collection (List, Set, Queue), Boolean, Integer, Long, Double, String, Date, Instant, and Null");

Map<String, Object> payload = new HashMap<>();
payload.put("entry", "value");
Expand All @@ -824,10 +824,79 @@ public void withPayloadShouldAllowNullListItems() {
assertThat(payloadJson, JsonMatcher.hasEntry("list", Arrays.asList("item1", null, "item2")));
}

@Test
public void withPayloadShouldAllowCollectionInGeneral() {
Map<String, Object> payload1 = new HashMap<>();
Map<String, Object> payload2 = new HashMap<>();
Map<String, Object> payload3 = new HashMap<>();
Map<String, Object> payload4 = new HashMap<>();

Set<String> set = new HashSet<>();

set.add("item1");
set.add("item2");

Queue<String> queue = new ArrayDeque<>();

queue.add("item3");
queue.add("item4");

Set<String> treeSet = new TreeSet<>();

treeSet.add("item6");
treeSet.add("item5");

List<String> linkedList = new LinkedList<>(); // Can be both a queue and a list

linkedList.add("item7");
linkedList.add("item8");

payload1.put("set", set);
payload2.put("queue", queue);
payload3.put("treeSet", treeSet);
payload4.put("linkedList", linkedList);

String jwt1 = JWTCreator.init()
.withPayload(payload1)
.sign(Algorithm.HMAC256("secret"));

String jwt2 = JWTCreator.init()
.withPayload(payload2)
.sign(Algorithm.HMAC256("secret"));

String jwt3 = JWTCreator.init()
.withPayload(payload3)
.sign(Algorithm.HMAC256("secret"));

String jwt4 = JWTCreator.init()
.withPayload(payload4)
.sign(Algorithm.HMAC256("secret"));

assertThat(jwt1, is(notNullValue()));
String[] parts1 = jwt1.split("\\.");
String payloadJson1 = new String(Base64.getUrlDecoder().decode(parts1[1]), StandardCharsets.UTF_8);
assertThat(payloadJson1, JsonMatcher.hasEntry("set", Arrays.asList("item2", "item1"))); // HashSet does not ensure inserting order

assertThat(jwt2, is(notNullValue()));
String[] parts2 = jwt2.split("\\.");
String payloadJson2 = new String(Base64.getUrlDecoder().decode(parts2[1]), StandardCharsets.UTF_8);
assertThat(payloadJson2, JsonMatcher.hasEntry("queue", Arrays.asList("item3", "item4")));

assertThat(jwt3, is(notNullValue()));
String[] parts3 = jwt3.split("\\.");
String payloadJson3 = new String(Base64.getUrlDecoder().decode(parts3[1]), StandardCharsets.UTF_8);
assertThat(payloadJson3, JsonMatcher.hasEntry("treeSet", Arrays.asList("item5", "item6")));

assertThat(jwt4, is(notNullValue()));
String[] parts4 = jwt4.split("\\.");
String payloadJson4 = new String(Base64.getUrlDecoder().decode(parts4[1]), StandardCharsets.UTF_8);
assertThat(payloadJson4, JsonMatcher.hasEntry("linkedList", Arrays.asList("item7", "item8")));
}

@Test
public void withPayloadShouldNotAllowListWithCustomType() {
exception.expect(IllegalArgumentException.class);
exception.expectMessage("Claim values must only be of types Map, List, Boolean, Integer, Long, Double, String, Date, Instant, and Null");
exception.expectMessage("Claim values must only be of types Map, Collection (List, Set, Queue), Boolean, Integer, Long, Double, String, Date, Instant, and Null");

Map<String, Object> payload = new HashMap<>();
payload.put("list", Arrays.asList("item1", new UserPojo("name", 42)));
Expand All @@ -839,7 +908,7 @@ public void withPayloadShouldNotAllowListWithCustomType() {
@Test
public void withPayloadShouldNotAllowMapWithCustomType() {
exception.expect(IllegalArgumentException.class);
exception.expectMessage("Claim values must only be of types Map, List, Boolean, Integer, Long, Double, String, Date, Instant, and Null");
exception.expectMessage("Claim values must only be of types Map, Collection (List, Set, Queue), Boolean, Integer, Long, Double, String, Date, Instant, and Null");

Map<String, Object> payload = new HashMap<>();
payload.put("entry", "value");
Expand Down