001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * docs/licenses/cddl.txt
011 * or http://www.opensource.org/licenses/cddl1.php.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * docs/licenses/cddl.txt.  If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 *      Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 *      Copyright 2016-2021 Ping Identity Corporation
026 */
027
028
029package com.unboundid.directory.sdk.common.types;
030
031import com.unboundid.util.NotExtensible;
032
033import java.util.Collections;
034import java.util.Map;
035import java.util.Objects;
036import java.util.Set;
037import java.util.concurrent.TimeUnit;
038
039/**
040 * Object returned from a request to an AccessTokenValidator to validate a
041 * token. The fields of this object are derived from the IETF OAuth 2.0 Token
042 * Introspection specification (RFC7662).
043 * <p>
044 * The decision as to whether an access token is accepted or not varies by
045 * product. For the Directory Server, Directory Proxy Server,
046 * Data Sync Server, or Data Metrics Server, different REST APIs
047 * may enforce different authorization rules, depending on their configuration;
048 * please refer to the relevant documentation. In general, a token's
049 * {@code active} property must be true.
050 * <p>
051 * For the PingAuthorize Server, authorization decisions are made by the policy
052 * engine. All properties of the TokenValidationResult may be accessed by
053 * policies; however, the default policy may only examine a subset of those
054 * properties (e.g. especially the {@code active} property).
055 */
056@NotExtensible()
057public final class TokenValidationResult {
058
059
060  private final String accessToken;
061
062  private final Boolean active;
063
064  private final Set<String> scope;
065
066  private final String clientId;
067
068  private final String username;
069
070  private final String tokenType;
071
072  private final Long expirationTime;
073
074  private final Long issuedAt;
075
076  private final Long notUsedBefore;
077
078  private final String tokenSubject;
079
080  private final Set<String> audience;
081
082  private final String tokenIdentifier;
083
084  /**
085   * This field contains any extension values implemented by the
086   * Token Validator.
087   */
088  private final Map<String,Object> additionalProperties;
089
090  /**
091   * Private constructor for builder.
092   * @param builder Builder source for properties.
093   */
094  private TokenValidationResult(final Builder builder)
095  {
096    this.accessToken = builder.accessToken;
097    this.active = builder.active;
098    this.scope = builder.scope;
099    this.clientId = builder.clientId;
100    this.username = builder.username;
101    this.tokenType = builder.tokenType;
102    this.expirationTime = builder.expirationTime;
103    this.issuedAt = builder.issuedAt;
104    this.notUsedBefore = builder.notUsedBefore;
105    this.tokenSubject = builder.tokenSubject;
106    this.audience = builder.audience;
107    this.tokenIdentifier = builder.tokenIdentifier;
108    this.additionalProperties = (builder.additionalProperties == null) ?
109        Collections.emptyMap() :
110        builder.additionalProperties;
111  }
112
113  /**
114   * Get the actual access token as issued by the authorization server.
115   * @return the access token. May be null.
116   */
117  public String getAccessToken() {
118    return accessToken;
119  }
120
121  /**
122   * Get whether the token is active. Per RFC 7662, a value of true for this
123   * property indicates that the given token was issued by the Token
124   * Validator's associated authorization server, has not been revoked by the
125   * resource owner, and is within its given time window of validity
126   * (e.g. after its issuance time and before its expiration time).
127   * @return the active state.
128   */
129  public Boolean getActive() {
130    return active;
131  }
132
133  /**
134   * Get the scopes granted to this token.
135   * @return set of scope names.
136   */
137  public Set<String> getScope() {
138    return scope;
139  }
140
141  /**
142   * Get the OAuth2 client Id of the client that requested the token.
143   * @return client Id string.
144   */
145  public String getClientId() {
146    return clientId;
147  }
148
149  /**
150   * Get a human-readable user name for the resource owner that authorized
151   * the token.
152   * @return user name string.
153   */
154  public String getUsername() {
155    return username;
156  }
157
158  /**
159   * Get the token type.  The type of the token is defined in section 5.1 of
160   * OAuth 2.0 [RFC6749].
161   * @return token type string.
162   */
163  public String getTokenType() {
164    return tokenType;
165  }
166
167  /**
168   * Get the token expiration time in seconds since January 1 1970 UTC.
169   * @return expiration time in seconds.
170   */
171  public Long getExpirationTime() {
172    return expirationTime;
173  }
174
175  /**
176   * Get the token issue time in seconds since January 1 1970 UTC.
177   * @return token issue time in seconds.
178   */
179  public Long getIssuedAt() {
180    return issuedAt;
181  }
182
183  /**
184   * Get the not used before time in seconds since January 1 1070 UTC.
185   * @return not used before time in seconds.
186   */
187  public Long getNotUsedBefore() {
188    return notUsedBefore;
189  }
190
191  /**
192   * Get the token subject as defined in JWT [RFC7519].  This value should
193   * contain the subject ID as known to the external authorization server rather
194   * than the local server.
195   * @return token subject string.
196   */
197  public String getTokenSubject() {
198    return tokenSubject;
199  }
200
201  /**
202   * Get the token's intended audience(s).  The audience is a Service-specific
203   * identifier representing the intended audience(s) for this token, as
204   * defined in JWT [RFC7519].
205   * @return token audience.
206   */
207  public Set<String> getAudience() {
208    return audience;
209  }
210
211  /**
212   * Get the unique identifier for this token. as defined in section 4.1.7 of
213   * JWT [RFC7519].
214   * @return token unique identifier string.
215   */
216  public String getTokenIdentifier() {
217    return tokenIdentifier;
218  }
219
220  /**
221   * Get any extension properties associated with the token.
222   * @return Map of property names to values.
223   */
224  public Map<String, Object> getAdditionalProperties() {
225    return additionalProperties;
226  }
227
228  /**
229   * A Builder for TokenValidationResult.
230   */
231  public static class Builder
232  {
233    private final String accessToken;
234    private Boolean active;
235    private Set<String> scope;
236    private String clientId;
237    private String username;
238    private String tokenType;
239    private Long expirationTime;
240    private Long issuedAt;
241    private Long notUsedBefore;
242    private String tokenSubject;
243    private Set<String> audience;
244    private String tokenIdentifier;
245    private Map<String,Object> additionalProperties;
246
247
248    /**
249     * Creates a new builder object.
250     * @param active      true if the token is active, false if not.  Per RFC
251     *                    7662, a value of true for this property indicates
252     *                    that the given token was issued by the Token
253     *                    Validator's associated authorization server, has not
254     *                    been revoked by the resource owner, and is within its
255     *                    given time window of validity (e.g. after its issuance
256     *                    time and before its expiration time).
257     */
258    public Builder(final boolean active)
259    {
260      this(null, active);
261    }
262
263    /**
264     * Creates a new builder object.
265     * @param accessToken the actual access token, as received from the
266     *                    authorization server.  May be null.
267     * @param active      true if the token is active, false if not.  Per RFC
268     *                    7662, a value of true for this property indicates
269     *                    that the given token was issued by the Token
270     *                    Validator's associated authorization server, has not
271     *                    been revoked by the resource owner, and is within its
272     *                    given time window of validity (e.g. after its issuance
273     *                    time and before its expiration time).
274     */
275    public Builder(final String accessToken, final boolean active)
276    {
277      this.accessToken = accessToken;
278      this.active = active;
279      setScope(Collections.emptySet());
280    }
281
282    /**
283     * Set the active field of the token validation result.
284     *
285     * @param active true if the token is active, false if not.  Per RFC 7662,
286     *               a value of true for this property indicates that the given
287     *               token was issued by the Token Validator's associated
288     *               authorization server, has not been revoked by the resource
289     *               owner, and is within its given time window of validity
290     *               (e.g. after its issuance time and before its expiration
291     *               time).
292     * @return this.
293     */
294    public Builder setActive(final boolean active)
295    {
296      this.active = active;
297      return this;
298    }
299
300    /**
301     * Set the optional scope field of the token validation result.
302     *
303     * @param scope A list of strings representing the scopes associated with
304     *              this token.
305     * @return this.
306     */
307    public Builder setScope(final Set<String> scope)
308    {
309      this.scope = (scope == null) ? Collections.emptySet() : scope;
310      return this;
311    }
312
313    /**
314     * Set the optional clientId field of the token validation result.
315     *
316     * @param clientId Client identifier for the OAuth 2.0 client that
317     *                 requested this token.
318     * @return this.
319     */
320    public Builder setClientId(final String clientId)
321    {
322      this.clientId = clientId;
323      return this;
324    }
325
326    /**
327     * Set the optional username field of the token validation result.
328     *
329     * @param username Human-readable identifier for the resource owner who
330     *                 authorized this token.
331     * @return this.
332     */
333    public Builder setUsername(final String username)
334    {
335      this.username = username;
336      return this;
337    }
338
339    /**
340     * Set the optional tokenType field of the token validation result.
341     *
342     * @param tokenType Type of the token as defined in section 5.1 of OAuth
343     *                  2.0 [RFC6749].
344     * @return this.
345     */
346    public Builder setTokenType(final String tokenType)
347    {
348      this.tokenType = tokenType;
349      return this;
350    }
351
352    /**
353     * Set the optional expirationTime field of the token validation result.
354     *
355     * @param expirationTime Integer timestamp, measured in the number of
356     *                       time units since January 1 1970 UTC, indicating
357     *                       when this token will expire.
358     * @param timeUnit the time units of the specified expiration time.
359     * @return this.
360     */
361    public Builder setExpirationTime(
362        final long expirationTime,
363        final TimeUnit timeUnit)
364    {
365      this.expirationTime = timeUnit.toSeconds(expirationTime);
366      return this;
367    }
368
369    /**
370     * Set the optional issuedAt field of the token validation result.
371     *
372     * @param issuedAt Integer timestamp, measured in the number of time
373     *                 units since January 1 1970 UTC, indicating when this
374     *                 token was originally issued.
375     * @param timeUnit the time units of the specified issuedAt time.
376     * @return this.
377     */
378    public Builder setIssuedAt(
379        final long issuedAt,
380        final TimeUnit timeUnit)
381    {
382      this.issuedAt = timeUnit.toSeconds(issuedAt);
383      return this;
384    }
385
386    /**
387     * Set the optional notUsedBefore field of the token validation result.
388     *
389     * @param notUsedBefore Integer timestamp, measured in the number of
390     *                      time units since January 1 1970 UTC, indicating
391     *                      when this token is not to be used before.
392     * @param timeUnit the time units of the specified notUsedBefore time.
393     * @return this.
394     */
395    public Builder setNotUsedBefore(
396        final long notUsedBefore,
397        final TimeUnit timeUnit)
398    {
399      this.notUsedBefore = timeUnit.toSeconds(notUsedBefore);
400      return this;
401    }
402
403    /**
404     * Set the optional subjectToken field of the token validation result.
405     *
406     * @param tokenSubject Subject of the token, as defined in JWT [RFC7519].
407     *                     This value should contain the subject ID as known to
408     *                     the external authorization server rather than the
409     *                     local server.
410     * @return this.
411     */
412    public Builder setTokenSubject(final String tokenSubject)
413    {
414      this.tokenSubject = tokenSubject;
415      return this;
416    }
417
418    /**
419     * Set the optional audience field of the token validation result.
420     *
421     * @param audience Service-specific identifiers representing the
422     *                 intended audience(s) for this token, as defined in
423     *                 JWT [RFC7519].
424     * @return this.
425     */
426    public Builder setAudience(final Set<String> audience)
427    {
428      this.audience = audience;
429      return this;
430    }
431
432    /**
433     * Set the optional tokenIdentifier field of the token validation result.
434     *
435     * @param tokenIdentifier Unique string identifier for the token, as
436     *                        defined in section 4.1.7 of JWT [RFC7519].
437     * @return this.
438     */
439    public Builder setTokenIdentifier(final String tokenIdentifier)
440    {
441      this.tokenIdentifier = tokenIdentifier;
442      return this;
443    }
444
445    /**
446     * Set the optional additionalProperties field of the token validation
447     * result.
448     * @param properties Map of property names to values, containing any
449     *                   extension values implemented by the
450     *                   AccessTokenValidator.
451     * @return this.
452     */
453    public Builder setAdditionalProperties(
454        final Map<String,Object> properties) {
455
456      this.additionalProperties = properties;
457      return this;
458    }
459
460    /**
461     * Builds a new TokenValidationResult from the parameters
462     * previously supplied to the builder.
463     *
464     * @return a new TokenValidationResult.
465     */
466    public TokenValidationResult build()
467    {
468      return new TokenValidationResult(this);
469    }
470  }
471
472  @Override
473  public boolean equals(final Object other) {
474    if (this == other) {
475      return true;
476    }
477    if (other == null || getClass() != other.getClass()) {
478      return false;
479    }
480    final TokenValidationResult that = (TokenValidationResult) other;
481    return Objects.equals(accessToken, that.accessToken) &&
482        active.equals(that.active) &&
483        Objects.equals(scope, that.scope) &&
484        Objects.equals(clientId, that.clientId) &&
485        Objects.equals(username, that.username) &&
486        Objects.equals(tokenType, that.tokenType) &&
487        Objects.equals(expirationTime, that.expirationTime) &&
488        Objects.equals(issuedAt, that.issuedAt) &&
489        Objects.equals(notUsedBefore, that.notUsedBefore) &&
490        Objects.equals(tokenSubject, that.tokenSubject) &&
491        Objects.equals(audience, that.audience) &&
492        Objects.equals(tokenIdentifier, that.tokenIdentifier) &&
493        Objects.equals(additionalProperties, that.additionalProperties);
494  }
495
496  @Override
497  public int hashCode() {
498    return Objects.hash(accessToken, active, scope, clientId, username,
499        tokenType, expirationTime, issuedAt, notUsedBefore, tokenSubject,
500        audience, tokenIdentifier, additionalProperties);
501  }
502}