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.jose.util.DateUtils; 027import com.nimbusds.jwt.JWTClaimsSet; 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 private 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); 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); 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 public InternetAddress getEmailClaim(final String name) { 360 361 try { 362 return JSONObjectUtils.getEmail(claims, name); 363 } catch (ParseException e) { 364 return null; 365 } 366 } 367 368 369 /** 370 * Sets an email string based claim. 371 * 372 * @param name The claim name. Must not be {@code null}. 373 * @param value The claim value. If {@code null} any existing claim 374 * with the same name will be removed. 375 */ 376 public void setEmailClaim(final String name, final InternetAddress value) { 377 378 if (value != null) 379 setClaim(name, value.getAddress()); 380 else 381 claims.remove(name); 382 } 383 384 385 /** 386 * Gets a date / time based claim, represented as the number of seconds 387 * from 1970-01-01T0:0:0Z as measured in UTC until the date / time. 388 * 389 * @param name The claim name. Must not be {@code null}. 390 * 391 * @return The claim value, {@code null} if not specified or parsing 392 * failed. 393 */ 394 public Date getDateClaim(final String name) { 395 396 try { 397 return DateUtils.fromSecondsSinceEpoch(JSONObjectUtils.getNumber(claims, name).longValue()); 398 } catch (Exception e) { 399 return null; 400 } 401 } 402 403 404 /** 405 * Sets a date / time based claim, represented as the number of seconds 406 * from 1970-01-01T0:0:0Z as measured in UTC until the date / time. 407 * 408 * @param name The claim name. Must not be {@code null}. 409 * @param value The claim value. If {@code null} any existing claim 410 * with the same name will be removed. 411 */ 412 public void setDateClaim(final String name, final Date value) { 413 414 if (value != null) 415 setClaim(name, DateUtils.toSecondsSinceEpoch(value)); 416 else 417 claims.remove(name); 418 } 419 420 421 /** 422 * Gets a string list based claim. 423 * 424 * @param name The claim name. Must not be {@code null}. 425 * 426 * @return The claim value, {@code null} if not specified or parsing 427 * failed. 428 */ 429 public List<String> getStringListClaim(final String name) { 430 431 try { 432 return Arrays.asList(JSONObjectUtils.getStringArray(claims, name)); 433 } catch (ParseException e) { 434 return null; 435 } 436 } 437 438 439 /** 440 * Gets the JSON object representation of this claims set. 441 * 442 * <p>Example: 443 * 444 * <pre> 445 * { 446 * "country" : "USA", 447 * "country#en" : "USA", 448 * "country#de_DE" : "Vereinigte Staaten", 449 * "country#fr_FR" : "Etats Unis" 450 * } 451 * </pre> 452 * 453 * @return The JSON object representation. 454 */ 455 public JSONObject toJSONObject() { 456 457 return claims; 458 } 459 460 461 /** 462 * Gets the JSON Web Token (JWT) claims set for this claim set. 463 * 464 * @return The JWT claims set. 465 * 466 * @throws ParseException If the conversion to a JWT claims set fails. 467 */ 468 public JWTClaimsSet toJWTClaimsSet() 469 throws ParseException { 470 471 try { 472 return JWTClaimsSet.parse(claims); 473 474 } catch (java.text.ParseException e) { 475 476 throw new ParseException(e.getMessage(), e); 477 } 478 } 479}