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