com.unboundid.directory.sdk.http.scripting.ScriptedOAuthTokenHandler Maven / Gradle / Ivy
Show all versions of server-sdk Show documentation
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* docs/licenses/cddl.txt
* or http://www.opensource.org/licenses/cddl1.php.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* docs/licenses/cddl.txt. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Portions Copyright 2010-2024 Ping Identity Corporation
*/
package com.unboundid.directory.sdk.http.scripting;
import com.unboundid.directory.sdk.common.internal.Reconfigurable;
import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension;
import com.unboundid.directory.sdk.http.config.OAuthTokenHandlerConfig;
import com.unboundid.directory.sdk.http.types.HTTPServerContext;
import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension;
import com.unboundid.ldap.sdk.DN;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.scim.sdk.OAuthToken;
import com.unboundid.scim.sdk.OAuthTokenStatus;
import com.unboundid.scim.sdk.SCIMRequest;
import com.unboundid.util.Extensible;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;
import com.unboundid.util.args.ArgumentException;
import com.unboundid.util.args.ArgumentParser;
import java.security.GeneralSecurityException;
import java.util.List;
/**
* This class defines an API that must be implemented by extensions which will
* handle incoming SCIM requests with OAuth 2.0 bearer token authentication.
* The OAuthTokenHandler is responsible for decoding the bearer token and
* checking it for authenticity and validity.
*
* OAuth provides a method for clients to access a protected resource on
* behalf of a resource owner. In the general case, before a client can
* access a protected resource, it must first obtain an authorization
* grant from the resource owner and then exchange the authorization
* grant for an access token. The access token represents the grant's
* scope, duration, and other attributes specified by the authorization
* grant. The client accesses the protected resource by presenting the
* access token to the resource server (i.e. the Directory or Proxy Server with
* the SCIM HTTP Servlet enabled).
*
* The access token provides an abstraction, replacing different
* authorization constructs (e.g., username and password, assertion) for
* a single token understood by the resource server. This abstraction
* enables issuing access tokens valid for a short time period, as well
* as removing the resource server's need to understand a wide range of
* authentication schemes. See "OAuth 2.0 Authorization Framework: Bearer
* Token Usage" (RFC 6750) for the full
* specification and details.
*
* TLS security is required to use OAuth 2.0 bearer tokens, as specified in
* RFC 6750. A bearer token may be used by any party
* in possession of that token (the "bearer"), and thus needs to be protected
* when transmitted across the network. Implementations of this API should take
* special care to verify that the token came from a trusted source (using a
* secret key or some other signing mechanism to prove that the token is
* authentic). Please read "OAuth 2.0 Threat Model and Security Considerations"
* (RFC 6819) for a comprehensive list of
* security threats to consider when working with OAuth bearer tokens.
*
* The OAuthTokenHandler is also responsible for extracting an authorization DN
* from the bearer token (or otherwise providing one), which will be used to
* apply access controls before returning the protected resource. There are also
* methods to extract the expiration date of the token as well as verify that
* the intended audience is this server (to deal with token redirect).
*
* The order these methods are called by the SCIM HTTP Servlet Extension is as
* follows:
*
* - decodeOAuthToken()
* - isTokenAuthentic()
* - isTokenForThisServer()
* - isTokenExpired()
* - validateToken()
* - getAuthzDN()
*
* If any of the methods fail or return an error result, the server will return
* an appropriate "unauthorized" response to the client.
*
* Configuring Scripted OAuth Token Handlers
* In order to configure a token handler created using this API, use a command
* like:
*
* dsconfig create-oauth-token-handler \
* --handler-name "{handler-name}" \
* --type groovy-scripted \
* --set "script-class:{class-name}" \
* --set "script-argument:{name=value}"
*
* where "{handler-name}" is the name to use for the token handler
* instance, "{class-name}" is the fully-qualified name of the Groovy
* class that extends
* {@code com.unboundid.directory.sdk.http.scripting.ScriptedOAuthTokenHandler},
* and "{name=value}" represents name-value pairs for any arguments to
* provide to the token handler. If multiple arguments should be provided to
* the token handler, then the
* "--set extension-argument:{name=value}
" option should be
* provided multiple times.
*
* @see com.unboundid.directory.sdk.http.api.OAuthTokenHandler
*/
@Extensible()
@DirectoryServerExtension()
@DirectoryProxyServerExtension(appliesToLocalContent=false,
appliesToRemoteContent=true)
@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE)
public abstract class ScriptedOAuthTokenHandler
implements Reconfigurable
{
/**
* Creates a new instance of this alert handler. All token handler
* implementations must include a default constructor, but any initialization
* should generally be done in the {@code initializeTokenHandler} method.
*/
public ScriptedOAuthTokenHandler()
{
// No implementation is required.
}
/**
* {@inheritDoc}
*/
@Override
public void defineConfigArguments(final ArgumentParser parser)
throws ArgumentException
{
// No arguments will be allowed by default.
}
/**
* Initializes this token handler.
*
* @param serverContext A handle to the server context for the server in
* which this extension is running.
* @param config The general configuration for this token handler.
* @param parser The argument parser which has been initialized from
* the configuration for this token handler.
*
* @throws com.unboundid.ldap.sdk.LDAPException If a problem occurs while
* initializing this token handler.
*/
public void initializeTokenHandler(final HTTPServerContext serverContext,
final OAuthTokenHandlerConfig config,
final ArgumentParser parser)
throws LDAPException
{
// No initialization will be performed by default.
}
/**
* {@inheritDoc}
*/
@Override
public boolean isConfigurationAcceptable(final OAuthTokenHandlerConfig config,
final ArgumentParser parser,
final List unacceptableReasons)
{
// No extended validation will be performed by default.
return true;
}
/**
* {@inheritDoc}
*/
@Override
public ResultCode applyConfiguration(final OAuthTokenHandlerConfig config,
final ArgumentParser parser,
final List adminActionsRequired,
final List messages)
{
// By default, no configuration changes will be applied. If there are any
// arguments, then add an admin action message indicating that the extension
// needs to be restarted for any changes to take effect.
if (! parser.getNamedArguments().isEmpty())
{
adminActionsRequired.add(
"No configuration change has actually been applied. The new " +
"configuration will not take effect until the HTTP Connection " +
"Handler is disabled and re-enabled or until the server is restarted.");
}
return ResultCode.SUCCESS;
}
/**
* Performs any cleanup which may be necessary when this token handler is to
* be taken out of service.
*/
public void finalizeTokenHandler()
{
// No implementation is required.
}
/**
* Creates an {@link OAuthToken} instance from the incoming token value.
*
* Implementers may choose to return a subclass of {@link OAuthToken} in order
* to provide convenience methods for interacting with the token. This can be
* helpful because the returned {@link OAuthToken} is passed to all of the
* other methods in this class.
*
* @param base64TokenValue the base64-encoded bearer token value
* @return a {@link OAuthToken} instance. This must not be {@code null}.
* @throws GeneralSecurityException if there is an error decoding the token
*/
public abstract OAuthToken decodeOAuthToken(final String base64TokenValue)
throws GeneralSecurityException;
/**
* Determines whether the given token is expired.
*
* @param token the OAuth 2.0 bearer token.
* @return {@code true} if the token is already expired, {@code false} if not.
* @throws GeneralSecurityException if there is an error determining the
* token's expiration date
*/
public abstract boolean isTokenExpired(final OAuthToken token)
throws GeneralSecurityException;
/**
* Determines whether the incoming token is authentic (i.e. that it came from
* a trusted authorization server and not an attacker). Implementers are
* encouraged to use signed tokens and use this method to verify the
* signature, but other methods such as symmetric key encryption (using a
* shared secret) can be used as well.
*
* @param token the OAuth 2.0 bearer token.
* @return {@code true} if the bearer token can be verified as authentic and
* originating from a trusted source, {@code false} if not.
* @throws GeneralSecurityException if there is an error determining whether
* the token is authentic
*/
public abstract boolean isTokenAuthentic(final OAuthToken token)
throws GeneralSecurityException;
/**
* Determines whether the incoming token is targeted for this server. This
* allows the implementation to reject the token early in the validation
* process if it can see that the intended recipient was not this server.
*
* @param token the OAuth 2.0 bearer token.
* @return {@code true} if the bearer token identifies this server as the
* intended recipient, {@code false} if not.
* @throws GeneralSecurityException if there is an error determining whether
* the token is for this server
*/
public abstract boolean isTokenForThisServer(final OAuthToken token)
throws GeneralSecurityException;
/**
* Determines whether the incoming token is valid for the given request. This
* method should verify that the token is legitimate and grants access to the
* requested resource specified in the {@link SCIMRequest}. This typically
* involves checking the token scope and any other attributes granted by the
* authorization grant. Implementations may need to call back to the
* authorization server to verify that the token is still valid and has not
* been revoked.
*
* @param token the OAuth 2.0 bearer token.
* @param scimRequest the {@link SCIMRequest} that we are validating.
* @return an {@link OAuthTokenStatus} object which indicates whether the
* bearer token is valid and grants access to the target resource.
* This must not be {@code null}.
* @throws GeneralSecurityException if there is an error determining whether
* the token is valid
*/
public abstract OAuthTokenStatus validateToken(final OAuthToken token,
final SCIMRequest scimRequest)
throws GeneralSecurityException;
/**
* Extracts the DN of the authorization entry (for which to apply access
* controls) from the incoming token.
*
* This may require performing an LDAP search in order to find the DN that
* matches a certain attribute value contained in the token.
*
* @param token the OAuth 2.0 bearer token.
* @return the authorization DN to use.
* @throws GeneralSecurityException if there is an error determining the
* authorization user DN
*/
public abstract DN getAuthzDN(final OAuthToken token)
throws GeneralSecurityException;
}