001package com.nimbusds.openid.connect.sdk.claims; 002 003 004import java.net.URI; 005import java.net.URL; 006import java.util.*; 007 008import javax.mail.internet.InternetAddress; 009 010import net.minidev.json.JSONObject; 011 012import com.nimbusds.langtag.LangTag; 013import com.nimbusds.langtag.LangTagUtils; 014 015import com.nimbusds.jwt.JWTClaimsSet; 016 017import com.nimbusds.oauth2.sdk.ParseException; 018import com.nimbusds.oauth2.sdk.util.DateUtils; 019import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 020 021 022/** 023 * Claims set serialisable to a JSON object. 024 */ 025public abstract class ClaimsSet { 026 027 028 /** 029 * The JSON object representation of the claims set. 030 */ 031 private final JSONObject claims; 032 033 034 /** 035 * Creates a new empty claims set. 036 */ 037 protected ClaimsSet() { 038 039 claims = new JSONObject(); 040 } 041 042 043 /** 044 * Creates a new claims set from the specified JSON object. 045 * 046 * @param jsonObject The JSON object. Must not be {@code null}. 047 */ 048 protected ClaimsSet(final JSONObject jsonObject) { 049 050 if (jsonObject == null) 051 throw new IllegalArgumentException("The JSON object must not be null"); 052 053 claims = jsonObject; 054 } 055 056 057 /** 058 * Puts all claims from the specified other claims set. 059 * 060 * @param other The other claims set. Must not be {@code null}. 061 */ 062 public void putAll(final ClaimsSet other) { 063 064 putAll(other.claims); 065 } 066 067 068 /** 069 * Puts all claims from the specified map. 070 * 071 * @param claims The claims to put. Must not be {@code null}. 072 */ 073 public void putAll(final Map<String,Object> claims) { 074 075 this.claims.putAll(claims); 076 } 077 078 079 /** 080 * Gets a claim. 081 * 082 * @param name The claim name. Must not be {@code null}. 083 * 084 * @return The claim value, {@code null} if not specified. 085 */ 086 public Object getClaim(final String name) { 087 088 return claims.get(name); 089 } 090 091 092 /** 093 * Gets a claim that casts to the specified class. 094 * 095 * @param name The claim name. Must not be {@code null}. 096 * @param clazz The Java class that the claim value should cast to. 097 * Must not be {@code null}. 098 * 099 * @return The claim value, {@code null} if not specified or casting 100 * failed. 101 */ 102 public <T> T getClaim(final String name, final Class<T> clazz) { 103 104 try { 105 return JSONObjectUtils.getGeneric(claims, name, clazz); 106 } catch (ParseException e) { 107 return null; 108 } 109 } 110 111 112 /** 113 * Returns a map of all instances, including language-tagged, of a 114 * claim with the specified base name. 115 * 116 * <p>Example JSON serialised claims set: 117 * 118 * <pre> 119 * { 120 * "month" : "January", 121 * "month#de" : "Januar" 122 * "month#es" : "enero", 123 * "month#it" : "gennaio" 124 * } 125 * </pre> 126 * 127 * <p>The "month" claim instances as java.util.Map: 128 * 129 * <pre> 130 * null => "January" (no language tag) 131 * "de" => "Januar" 132 * "es" => "enero" 133 * "it" => "gennaio" 134 * </pre> 135 * 136 * @param name The claim name. Must not be {@code null}. 137 * @param clazz The Java class that the claim values should cast to. 138 * Must not be {@code null}. 139 * 140 * @return The matching language-tagged claim values, empty map if 141 * none. A {@code null} key indicates the value has no language 142 * tag (corresponds to the base name). 143 */ 144 public <T> Map<LangTag,T> getLangTaggedClaim(final String name, final Class<T> clazz) { 145 146 Map<LangTag,Object> matches = LangTagUtils.find(name, claims); 147 Map<LangTag,T> out = new HashMap<>(); 148 149 for (Map.Entry<LangTag,Object> entry: matches.entrySet()) { 150 151 LangTag langTag = entry.getKey(); 152 String compositeKey = name + (langTag != null ? "#" + langTag : ""); 153 154 try { 155 out.put(langTag, JSONObjectUtils.getGeneric(claims, compositeKey, clazz)); 156 } catch (ParseException e) { 157 // skip 158 } 159 } 160 161 return out; 162 } 163 164 165 /** 166 * Sets a claim. 167 * 168 * @param name The claim name, with an optional language tag. Must not 169 * be {@code null}. 170 * @param value The claim value. Should serialise to a JSON entity. If 171 * {@code null} any existing claim with the same name will 172 * be removed. 173 */ 174 public void setClaim(final String name, final Object value) { 175 176 if (value != null) 177 claims.put(name, value); 178 else 179 claims.remove(name); 180 } 181 182 183 /** 184 * Sets a claim with an optional language tag. 185 * 186 * @param name The claim name. Must not be {@code null}. 187 * @param value The claim value. Should serialise to a JSON entity. 188 * If {@code null} any existing claim with the same name 189 * and language tag (if any) will be removed. 190 * @param langTag The language tag of the claim value, {@code null} if 191 * not tagged. 192 */ 193 public void setClaim(final String name, final Object value, final LangTag langTag) { 194 195 String keyName = langTag != null ? name + "#" + langTag : name; 196 setClaim(keyName, value); 197 } 198 199 200 /** 201 * Gets a string-based claim. 202 * 203 * @param name The claim name. Must not be {@code null}. 204 * 205 * @return The claim value, {@code null} if not specified or casting 206 * failed. 207 */ 208 public String getStringClaim(final String name) { 209 210 try { 211 return JSONObjectUtils.getString(claims, name); 212 } catch (ParseException e) { 213 return null; 214 } 215 } 216 217 218 /** 219 * Gets a string-based claim with an optional language tag. 220 * 221 * @param name The claim name. Must not be {@code null}. 222 * @param langTag The language tag of the claim value, {@code null} to 223 * get the non-tagged value. 224 * 225 * @return The claim value, {@code null} if not specified or casting 226 * failed. 227 */ 228 public String getStringClaim(final String name, final LangTag langTag) { 229 230 return langTag == null ? getStringClaim(name) : getStringClaim(name + '#' + langTag); 231 } 232 233 234 /** 235 * Gets a boolean-based claim. 236 * 237 * @param name The claim name. Must not be {@code null}. 238 * 239 * @return The claim value, {@code null} if not specified or casting 240 * failed. 241 */ 242 public Boolean getBooleanClaim(final String name) { 243 244 try { 245 return JSONObjectUtils.getBoolean(claims, name); 246 } catch (ParseException e) { 247 return null; 248 } 249 } 250 251 252 /** 253 * Gets a number-based claim. 254 * 255 * @param name The claim name. Must not be {@code null}. 256 * 257 * @return The claim value, {@code null} if not specified or casting 258 * failed. 259 */ 260 public Number getNumberClaim(final String name) { 261 262 try { 263 return JSONObjectUtils.getNumber(claims, name); 264 } catch (ParseException e) { 265 return null; 266 } 267 } 268 269 270 /** 271 * Gets an URL string based claim. 272 * 273 * @param name The claim name. Must not be {@code null}. 274 * 275 * @return The claim value, {@code null} if not specified or parsing 276 * failed. 277 */ 278 public URL getURLClaim(final String name) { 279 280 try { 281 return JSONObjectUtils.getURL(claims, name); 282 } catch (ParseException e) { 283 return null; 284 } 285 } 286 287 288 /** 289 * Sets an URL string based claim. 290 * 291 * @param name The claim name. Must not be {@code null}. 292 * @param value The claim value. If {@code null} any existing claim 293 * with the same name will be removed. 294 */ 295 public void setURLClaim(final String name, final URL value) { 296 297 if (value != null) 298 setClaim(name, value.toString()); 299 else 300 claims.remove(name); 301 } 302 303 304 /** 305 * Gets an URI string based claim. 306 * 307 * @param name The claim name. Must not be {@code null}. 308 * 309 * @return The claim value, {@code null} if not specified or parsing 310 * failed. 311 */ 312 public URI getURIClaim(final String name) { 313 314 try { 315 return JSONObjectUtils.getURI(claims, name); 316 } catch (ParseException e) { 317 return null; 318 } 319 } 320 321 322 /** 323 * Sets an URI string based claim. 324 * 325 * @param name The claim name. Must not be {@code null}. 326 * @param value The claim value. If {@code null} any existing claim 327 * with the same name will be removed. 328 */ 329 public void setURIClaim(final String name, final URI value) { 330 331 if (value != null) 332 setClaim(name, value.toString()); 333 else 334 claims.remove(name); 335 } 336 337 338 /** 339 * Gets an email string based claim. 340 * 341 * @param name The claim name. Must not be {@code null}. 342 * 343 * @return The claim value, {@code null} if not specified or parsing 344 * failed. 345 */ 346 public InternetAddress getEmailClaim(final String name) { 347 348 try { 349 return JSONObjectUtils.getEmail(claims, name); 350 } catch (ParseException e) { 351 return null; 352 } 353 } 354 355 356 /** 357 * Sets an email string based claim. 358 * 359 * @param name The claim name. Must not be {@code null}. 360 * @param value The claim value. If {@code null} any existing claim 361 * with the same name will be removed. 362 */ 363 public void setEmailClaim(final String name, final InternetAddress value) { 364 365 if (value != null) 366 setClaim(name, value.getAddress()); 367 else 368 claims.remove(name); 369 } 370 371 372 /** 373 * Gets a date / time based claim, represented as the number of seconds 374 * from 1970-01-01T0:0:0Z as measured in UTC until the date / time. 375 * 376 * @param name The claim name. Must not be {@code null}. 377 * 378 * @return The claim value, {@code null} if not specified or parsing 379 * failed. 380 */ 381 public Date getDateClaim(final String name) { 382 383 try { 384 return DateUtils.fromSecondsSinceEpoch(JSONObjectUtils.getNumber(claims, name).longValue()); 385 } catch (Exception e) { 386 return null; 387 } 388 } 389 390 391 /** 392 * Sets a date / time based claim, represented as the number of seconds 393 * from 1970-01-01T0:0:0Z as measured in UTC until the date / time. 394 * 395 * @param name The claim name. Must not be {@code null}. 396 * @param value The claim value. If {@code null} any existing claim 397 * with the same name will be removed. 398 */ 399 public void setDateClaim(final String name, final Date value) { 400 401 if (value != null) 402 setClaim(name, DateUtils.toSecondsSinceEpoch(value)); 403 else 404 claims.remove(name); 405 } 406 407 408 /** 409 * Gets a string list based claim. 410 * 411 * @param name The claim name. Must not be {@code null}. 412 * 413 * @return The claim value, {@code null} if not specified or parsing 414 * failed. 415 */ 416 public List<String> getStringListClaim(final String name) { 417 418 try { 419 return Arrays.asList(JSONObjectUtils.getStringArray(claims, name)); 420 } catch (ParseException e) { 421 return null; 422 } 423 } 424 425 426 /** 427 * Gets the JSON object representation of this claims set. 428 * 429 * <p>Example: 430 * 431 * <pre> 432 * { 433 * "country" : "USA", 434 * "country#en" : "USA", 435 * "country#de_DE" : "Vereinigte Staaten", 436 * "country#fr_FR" : "Etats Unis" 437 * } 438 * </pre> 439 * 440 * @return The JSON object representation. 441 */ 442 public JSONObject toJSONObject() { 443 444 return claims; 445 } 446 447 448 /** 449 * Gets the JSON Web Token (JWT) claims set for this claim set. 450 * 451 * @return The JWT claims set. 452 * 453 * @throws ParseException If the conversion to a JWT claims set fails. 454 */ 455 public JWTClaimsSet toJWTClaimsSet() 456 throws ParseException { 457 458 try { 459 return JWTClaimsSet.parse(claims); 460 461 } catch (java.text.ParseException e) { 462 463 throw new ParseException(e.getMessage(), e); 464 } 465 } 466}