diff --git a/lib/src/main/java/com/auth0/jwt/JWTCreator.java b/lib/src/main/java/com/auth0/jwt/JWTCreator.java index bfcb9147..f079a425 100644 --- a/lib/src/main/java/com/auth0/jwt/JWTCreator.java +++ b/lib/src/main/java/com/auth0/jwt/JWTCreator.java @@ -351,10 +351,10 @@ public Builder withClaim(String name, Instant value) throws IllegalArgumentExcep /** * Add a custom Map Claim with the given items. *

- * 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. @@ -365,34 +365,34 @@ public Builder withClaim(String name, Map 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. *

- * 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; } @@ -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. *

- * 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. *

* *

@@ -476,8 +476,8 @@ public Builder withPayload(Map 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 @@ -521,11 +521,15 @@ private boolean validatePayload(Map 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; } } @@ -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; } @@ -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 { diff --git a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java index 53cd267b..9ce28e3a 100644 --- a/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java +++ b/lib/src/test/java/com/auth0/jwt/JWTCreatorTest.java @@ -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 payload = new HashMap<>(); payload.put("entry", "value"); @@ -824,10 +824,79 @@ public void withPayloadShouldAllowNullListItems() { assertThat(payloadJson, JsonMatcher.hasEntry("list", Arrays.asList("item1", null, "item2"))); } + @Test + public void withPayloadShouldAllowCollectionInGeneral() { + Map payload1 = new HashMap<>(); + Map payload2 = new HashMap<>(); + Map payload3 = new HashMap<>(); + Map payload4 = new HashMap<>(); + + Set set = new HashSet<>(); + + set.add("item1"); + set.add("item2"); + + Queue queue = new ArrayDeque<>(); + + queue.add("item3"); + queue.add("item4"); + + Set treeSet = new TreeSet<>(); + + treeSet.add("item6"); + treeSet.add("item5"); + + List 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 payload = new HashMap<>(); payload.put("list", Arrays.asList("item1", new UserPojo("name", 42))); @@ -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 payload = new HashMap<>(); payload.put("entry", "value");