001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2013-2015 ForgeRock AS. 015 */ 016 017package org.forgerock.json.jose.jwt; 018 019import static org.forgerock.json.jose.jwt.JwtClaimsSetKey.AUD; 020import static org.forgerock.json.jose.jwt.JwtClaimsSetKey.EXP; 021import static org.forgerock.json.jose.jwt.JwtClaimsSetKey.IAT; 022import static org.forgerock.json.jose.jwt.JwtClaimsSetKey.ISS; 023import static org.forgerock.json.jose.jwt.JwtClaimsSetKey.JTI; 024import static org.forgerock.json.jose.jwt.JwtClaimsSetKey.NBF; 025import static org.forgerock.json.jose.jwt.JwtClaimsSetKey.SUB; 026import static org.forgerock.json.jose.jwt.JwtClaimsSetKey.TYP; 027import static org.forgerock.json.jose.jwt.JwtClaimsSetKey.getClaimSetKey; 028 029import java.net.URI; 030import java.util.ArrayList; 031import java.util.Date; 032import java.util.List; 033import java.util.Map; 034 035import org.forgerock.json.jose.utils.IntDate; 036import org.forgerock.json.jose.utils.StringOrURI; 037 038/** 039 * An implementation that holds a JWT's Claims Set. 040 * <p> 041 * Provides methods to set claims for all the reserved claim names as well as custom claims. 042 * 043 * @since 2.0.0 044 */ 045public class JwtClaimsSet extends JWObject implements Payload { 046 047 /** 048 * Constructs a new, empty JwtClaimsSet. 049 */ 050 public JwtClaimsSet() { 051 } 052 053 /** 054 * Constructs a new JwtClaimsSet, with its claims set to the contents of the given Map. 055 * 056 * @param claims A Map containing the claims to be set in the Claims Set. 057 */ 058 public JwtClaimsSet(Map<String, Object> claims) { 059 setClaims(claims); 060 } 061 062 /** 063 * Gets the type of the contents of the Claims Set. 064 * <p> 065 * The values used for this claim SHOULD come from the same value space as the JWT header parameter "typ", 066 * with the same rules applying. 067 * 068 * @param type The Claims Set content type. 069 */ 070 public void setType(String type) { 071 put(TYP.value(), type); 072 } 073 074 /** 075 * Gets the type of the contents of the Claims Set. 076 * <p> 077 * The values used for this claim SHOULD come from the same value space as the JWT header parameter "typ", 078 * with the same rules applying. 079 * 080 * @return The Claims Set content type. 081 */ 082 public String getType() { 083 return get(TYP.value()).asString(); 084 } 085 086 /** 087 * Sets the unique ID of the JWT. 088 * 089 * @param jwtId The JWT's ID. 090 */ 091 public void setJwtId(String jwtId) { 092 put(JTI.value(), jwtId); 093 } 094 095 /** 096 * Gets the unique ID of the JWT. 097 * 098 * @return The JWT's ID. 099 */ 100 public String getJwtId() { 101 return get(JTI.value()).asString(); 102 } 103 104 /** 105 * Sets the issuer this JWT was issued by. 106 * <p> 107 * The given issuer can be any arbitrary string without any ":" characters, if the string does contain a ":" 108 * character then it must be a valid URI. 109 * 110 * @param issuer The JWT's issuer. 111 */ 112 public void setIssuer(String issuer) { 113 StringOrURI.validateStringOrURI(issuer); 114 put(ISS.value(), issuer); 115 } 116 117 /** 118 * Sets the issuer this JWT was issued by. 119 * 120 * @param issuer The JWT's issuer. 121 */ 122 public void setIssuer(URI issuer) { 123 put(ISS.value(), issuer.toString()); 124 } 125 126 /** 127 * 128 * Gets the issuer this JWT was issued by. 129 * 130 * @return The JWT's issuer. 131 */ 132 public String getIssuer() { 133 return get(ISS.value()).asString(); 134 } 135 136 /** 137 * Sets the subject this JWT is issued to. 138 * <p> 139 * The given subject can be any arbitrary string without any ":" characters, if the string does contain a ":" 140 * character then it must be a valid URI. 141 * 142 * @param subject The JWT's principal. 143 * @see #setSubject(java.net.URI) 144 */ 145 public void setSubject(String subject) { 146 StringOrURI.validateStringOrURI(subject); 147 put(SUB.value(), subject); 148 } 149 150 /** 151 * Sets the subject this JWT is issued to. 152 * 153 * @param subject The JWT's principal. 154 * @see #setSubject(String) 155 */ 156 public void setSubject(URI subject) { 157 put(SUB.value(), subject.toString()); 158 } 159 160 /** 161 * Gets the subject this JWT is issued to. 162 * 163 * @return The JWT's principal. 164 */ 165 public String getSubject() { 166 return get(SUB.value()).asString(); 167 } 168 169 /** 170 * Adds an entry to the JWT's intended audience list, in the Claims Set. 171 * <p> 172 * The given audience can be any arbitrary string without any ":" characters, if the string does contain a ":" 173 * character then it must be a valid URI. 174 * 175 * @param audience The JWT's intended audience. 176 * @see #addAudience(java.net.URI) 177 */ 178 public void addAudience(String audience) { 179 StringOrURI.validateStringOrURI(audience); 180 getAudienceNullCheck().add(audience); 181 } 182 183 /** 184 * Adds an entry to the JWT's intended audience list, in the Claims Set. 185 * 186 * @param audience The JWT's intended audience. 187 * @see #addAudience(String) 188 */ 189 public void addAudience(URI audience) { 190 getAudienceNullCheck().add(audience.toString()); 191 } 192 193 /** 194 * Gets the JWT's intended audience list from the Claims Set. 195 * <p>If audience claim has not been set before, the method will create the list to contain the audience 196 * claim and set it in the Claims Set. 197 * 198 * @return The list of the JWT's intended audience. 199 */ 200 private List<String> getAudienceNullCheck() { 201 List<String> audienceList = getAudience(); 202 if (audienceList == null) { 203 audienceList = new ArrayList<>(); 204 put(AUD.value(), audienceList); 205 } 206 return audienceList; 207 } 208 209 /** 210 * Gets the intended audience for the JWT from the Claims Set. 211 * 212 * @return The JWT's intended audience. 213 */ 214 public List<String> getAudience() { 215 return get(AUD.value()).asList(String.class); 216 } 217 218 /** 219 * Sets the time the JWT was issued at, in the Claims Set. 220 * <p> 221 * The given date will be converted into an {@link IntDate} to be stored in the JWT Claims Set. 222 * 223 * @param issuedAtTime The JWT's issued at time. 224 */ 225 public void setIssuedAtTime(Date issuedAtTime) { 226 put(IAT.value(), IntDate.toIntDate(issuedAtTime)); 227 } 228 229 /** 230 * Sets the time the JWT was issued at, in the Claims Set. 231 * <p> 232 * This method takes a long representation of the number of <strong>seconds</strong> have passed since epoch. 233 * 234 * @param issuedAtTime The JWT's issued at time as a long in seconds. 235 * @see #setIssuedAtTime(java.util.Date) 236 */ 237 private void setIssuedAtTime(long issuedAtTime) { 238 put(IAT.value(), issuedAtTime); 239 } 240 241 /** 242 * Gets the time the JWT was issued at, from the Claims Set. 243 * 244 * @return The JWT's issued at time. 245 */ 246 public Date getIssuedAtTime() { 247 return IntDate.fromIntDate(get(IAT.value()).asLong()); 248 } 249 250 /** 251 * Sets the time the JWT is not allowed to be processed before, in the Claims Set. 252 * <p> 253 * The given date will be converted into an {@link IntDate} to be stored in the JWT Claims Set. 254 * 255 * @param notBeforeTime The JWT's not before time. 256 */ 257 public void setNotBeforeTime(Date notBeforeTime) { 258 put(NBF.value(), IntDate.toIntDate(notBeforeTime)); 259 } 260 261 /** 262 * Sets the time the JWT is not allowed to be processed before, in the Claims Set. 263 * <p> 264 * The method takes a long representation of the number of <strong>seconds</strong> have passed since epoch. 265 * 266 * @param notBeforeTime The JWT's not before time. 267 * @see #setNotBeforeTime(java.util.Date) 268 */ 269 private void setNotBeforeTime(long notBeforeTime) { 270 put(NBF.value(), notBeforeTime); 271 } 272 273 /** 274 * Gets the time the JWT is not allowed to be processed before, from the Claims Set. 275 * 276 * @return The JWT's not before time. 277 */ 278 public Date getNotBeforeTime() { 279 return IntDate.fromIntDate(get(NBF.value()).asLong()); 280 } 281 282 /** 283 * Sets the expiration time of the JWT in the Claims Set. 284 * <p> 285 * The given date will be converted into an {@link IntDate} to be stored in the JWT Claims Set. 286 * 287 * @param expirationTime The JWT's expiration time. 288 */ 289 public void setExpirationTime(Date expirationTime) { 290 put(EXP.value(), IntDate.toIntDate(expirationTime)); 291 } 292 293 /** 294 * Sets the expiration time of the JWT in the Claims Set. 295 * <p> 296 * This method takes a long representation of the number of <strong>seconds</strong> have passed since epoch. 297 * 298 * @param expirationTime The JWT's expiration time as a long in seconds. 299 * @see #setExpirationTime(java.util.Date) 300 */ 301 private void setExpirationTime(long expirationTime) { 302 put(EXP.value(), expirationTime); 303 } 304 305 /** 306 * Gets the expiration time of the JWT from the Claims Set. 307 * 308 * @return The JWT's expiration time. 309 */ 310 public Date getExpirationTime() { 311 return IntDate.fromIntDate(get(EXP.value()).asLong()); 312 } 313 314 /** 315 * Sets a claim with the specified name and value. 316 * <p> 317 * If the key matches one of the reserved claim names, then the relevant <tt>set</tt> method is called to set that 318 * claim with the specified name and value. 319 * 320 * @param key The claim name. 321 * @param value The claim value. 322 */ 323 public void setClaim(String key, Object value) { 324 325 JwtClaimsSetKey claimsSetKey = getClaimSetKey(key.toUpperCase()); 326 327 switch (claimsSetKey) { 328 case TYP: { 329 checkValueIsOfType(value, String.class); 330 setType((String) value); 331 break; 332 } 333 case JTI: { 334 checkValueIsOfType(value, String.class); 335 setJwtId((String) value); 336 break; 337 } 338 case ISS: { 339 if (isValueOfType(value, URI.class)) { 340 setIssuer((URI) value); 341 } else { 342 checkValueIsOfType(value, String.class); 343 setIssuer((String) value); 344 } 345 break; 346 } 347 case SUB: { 348 if (isValueOfType(value, URI.class)) { 349 setSubject((URI) value); 350 } else { 351 checkValueIsOfType(value, String.class); 352 setSubject((String) value); 353 } 354 break; 355 } 356 case AUD: { 357 if (isValueOfType(value, List.class)) { 358 List<?> audienceList = (List<?>) value; 359 for (Object audience : audienceList) { 360 if (isValueOfType(audience, URI.class)) { 361 addAudience((URI) audience); 362 } else { 363 checkValueIsOfType(audience, String.class); 364 addAudience((String) audience); 365 } 366 } 367 } else { 368 if (isValueOfType(value, URI.class)) { 369 addAudience((URI) value); 370 } else { 371 checkValueIsOfType(value, String.class); 372 addAudience((String) value); 373 } 374 } 375 break; 376 } 377 case IAT: { 378 if (isValueOfType(value, Number.class)) { 379 setIssuedAtTime(((Number) value).longValue()); 380 } else { 381 checkValueIsOfType(value, Date.class); 382 setIssuedAtTime((Date) value); 383 } 384 break; 385 } 386 case NBF: { 387 if (isValueOfType(value, Number.class)) { 388 setNotBeforeTime(((Number) value).longValue()); 389 } else { 390 checkValueIsOfType(value, Date.class); 391 setNotBeforeTime((Date) value); 392 } 393 break; 394 } 395 case EXP: { 396 if (isValueOfType(value, Number.class)) { 397 setExpirationTime(((Number) value).longValue()); 398 } else { 399 checkValueIsOfType(value, Date.class); 400 setExpirationTime((Date) value); 401 } 402 break; 403 } 404 default: { 405 put(key, value); 406 } 407 } 408 } 409 410 /** 411 * Sets claims using the values contained in the specified map. 412 * 413 * @param claims The Map to use to set the claims. 414 */ 415 public void setClaims(Map<String, Object> claims) { 416 for (String key : claims.keySet()) { 417 setClaim(key, claims.get(key)); 418 } 419 } 420 421 /** 422 * Gets a claim value for the specified key. 423 * <p> 424 * If the key matches one of the reserved claim names, then the relevant <tt>get</tt> method is called to get that 425 * claim value. 426 * 427 * @param key The claim name. 428 * @return The value stored against the claim name. 429 */ 430 public Object getClaim(String key) { 431 432 JwtClaimsSetKey claimsSetKey = getClaimSetKey(key.toUpperCase()); 433 434 Object value; 435 436 switch (claimsSetKey) { 437 case TYP: { 438 value = getType(); 439 break; 440 } 441 case JTI: { 442 value = getJwtId(); 443 break; 444 } 445 case ISS: { 446 value = getIssuer(); 447 break; 448 } 449 case SUB: { 450 value = getSubject(); 451 break; 452 } 453 case AUD: { 454 value = getAudience(); 455 break; 456 } 457 case IAT: { 458 value = getIssuedAtTime(); 459 break; 460 } 461 case NBF: { 462 value = getNotBeforeTime(); 463 break; 464 } 465 case EXP: { 466 value = getExpirationTime(); 467 break; 468 } 469 default: { 470 value = get(key).getObject(); 471 } 472 } 473 474 return value; 475 } 476 477 /** 478 * Gets a claim value for the specified claim name and then casts it to the specified type. 479 * 480 * @param key The claim name. 481 * @param clazz The class of the required type. 482 * @param <T> The required type for the claim value. 483 * @return The value stored against the claim name. 484 * @see #getClaim(String) 485 */ 486 public <T> T getClaim(String key, Class<T> clazz) { 487 return clazz.cast(getClaim(key)); 488 } 489 490 /** 491 * Builds the JWT's Claims Set into a <code>String</code> representation of a JSON object. 492 * 493 * @return A JSON string. 494 */ 495 public String build() { 496 return toString(); 497 } 498}