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