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