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 * trunk/ds/resource/legal-notices/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 * trunk/ds/resource/legal-notices/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 2013-2016 UnboundID Corp.
026 */
027package com.unboundid.directory.sdk.broker.api;
028
029import com.unboundid.directory.sdk.broker.config.StoreAdapterConfig;
030import com.unboundid.directory.sdk.broker.internal.IdentityBrokerExtension;
031import com.unboundid.directory.sdk.http.types.AuthenticationException;
032import com.unboundid.directory.sdk.http.types.AuthenticationRequest;
033import com.unboundid.directory.sdk.broker.types.IdentityBrokerContext;
034import com.unboundid.directory.sdk.broker.types.MetaDataMods;
035import com.unboundid.directory.sdk.broker.types.StoreCreateRequest;
036import com.unboundid.directory.sdk.broker.types.StoreDeleteRequest;
037import com.unboundid.directory.sdk.broker.types.StoreSearchRequest;
038import com.unboundid.directory.sdk.broker.types.StoreUpdateRequest;
039import com.unboundid.directory.sdk.common.internal.Configurable;
040import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
041import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
042import com.unboundid.scim.data.BaseResource;
043import com.unboundid.scim.schema.ResourceDescriptor;
044import com.unboundid.scim.sdk.Resources;
045import com.unboundid.scim.sdk.SCIMException;
046import com.unboundid.scim.sdk.SCIMFilterType;
047import com.unboundid.scim.sdk.SCIMQueryAttributes;
048import com.unboundid.util.ByteString;
049import com.unboundid.util.Extensible;
050import com.unboundid.util.ThreadSafety;
051import com.unboundid.util.ThreadSafetyLevel;
052import com.unboundid.util.args.ArgumentException;
053import com.unboundid.util.args.ArgumentParser;
054
055import java.util.Collection;
056import java.util.Collections;
057import java.util.List;
058import java.util.Map;
059
060
061/**
062 * This class defines an API that must be implemented by extensions that need
063 * to store user data in some non-LDAP data store. This adapter API is generic
064 * and can support a wide range of repositories. When using multiple Identity
065 * Broker instances in a deployment, the data store should be accessible from
066 * all instances.
067 * <p>
068 * A Store Adapter can optionally support authentication by implementing the
069 * {@code supportsAuthentication()} and
070 * {@code authenticate(StoreAuthenticationRequest)} methods.
071 * <p>
072 * A Store Adapter can also opt to store the Identity Broker user metadata
073 * attributes, which contain the consents, OAuth tokens, and consent history,
074 * among other things. This metadata is separated into two categories: small
075 * and large. Both types are multi-valued and store raw binary values. The
076 * main difference is that the "small" metadata will typically contain small
077 * binary values and be accessed more frequently, whereas the "large" metadata
078 * will contain more sizable values (or a larger number of values) and be
079 * accessed less frequently. Implementers should take this into consideration
080 * when deciding where to store these metadata attributes.
081 * <p>
082 * After the StoreAdapter is initialized (via the initializeStoreAdapter()
083 * method), all methods will be guarded by a call to {@link #isAvailable()} to
084 * make sure that the adapter is currently connected and ready to service
085 * requests. If this returns {@code false}, the DataView will return an
086 * appropriate 503 response code to clients until the adapter becomes available
087 * again.
088 * <H2>Configuring Store Adapters</H2>
089 * In order to configure a store adapter created using this API, use
090 * a command like:
091 * <PRE>
092 *      dsconfig create-store-adapter \
093 *           ---adapter-name "<I>{name}</I>" \
094 *           --type third-party \
095 *           --set "extension-class:<I>{class-name}</I>" \
096 *           --set "extension-argument:<I>{name=value}</I>"
097 * </PRE>
098 * where "<I>{name}</I>" is the name to use for the token store
099 * instance, "<I>{class-name}</I>" is the fully-qualified name of the Java class
100 * that extends
101 * {@code com.unboundid.directory.sdk.broker.api.StoreAdapter},
102 * and "<I>{name=value}</I>" represents name-value pairs for any arguments to
103 * provide to the store adapter. If multiple arguments should be
104 * provided to extension, then the
105 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be
106 * provided multiple times.
107 */
108@Extensible()
109@IdentityBrokerExtension
110@ThreadSafety(level= ThreadSafetyLevel.INTERFACE_THREADSAFE)
111public abstract class StoreAdapter implements UnboundIDExtension,
112        Configurable, ExampleUsageProvider
113{
114  /**
115   * Creates a new instance of this store adapter.  All
116   * implementations must include a default constructor, but any
117   * initialization should generally be done in the
118   * {@link #initializeStoreAdapter} method.
119   */
120  public StoreAdapter()
121  {
122    // No implementation is required.
123  }
124
125  /**
126   * {@inheritDoc}
127   */
128  @Override
129  public abstract String getExtensionName();
130
131  /**
132   * {@inheritDoc}
133   */
134  @Override
135  public abstract String[] getExtensionDescription();
136
137  /**
138   * {@inheritDoc}
139   */
140  @Override
141  public Map<List<String>,String> getExamplesArgumentSets()
142  {
143    return Collections.emptyMap();
144  }
145
146  /**
147   * {@inheritDoc}
148   */
149  @Override
150  public void defineConfigArguments(final ArgumentParser parser)
151         throws ArgumentException
152  {
153    // No arguments will be allowed by default.
154  }
155
156  /**
157   * Initializes this store adapter. Any initialization should be performed
158   * here. This method should generally store the
159   * {@link com.unboundid.directory.sdk.broker.types.IdentityBrokerContext} in
160   * a class member so that it can be used elsewhere in the implementation.
161   * <p>
162   * The default implementation is empty.
163   *
164   * @param  serverContext  A handle to the server context for the server in
165   *                        which this extension is running. Extensions should
166   *                        typically store this in a class member.
167   * @param  config         The general configuration for this object.
168   * @param  parser         The argument parser which has been initialized from
169   *                        the configuration for this store adapter.
170   * @throws Exception      If a problem occurs while initializing this store
171   *                        adapter.
172   */
173  public void initializeStoreAdapter(final IdentityBrokerContext serverContext,
174                                     final StoreAdapterConfig config,
175                                     final ArgumentParser parser)
176      throws Exception
177  {
178    // No initialization will be performed by default.
179  }
180
181  /**
182   * This hook is called when the DataView is disabled or the Identity Broker
183   * shuts down. Any clean-up of this store adapter should be performed here.
184   * <p>
185   * The default implementation is empty.
186   */
187  public void finalizeStoreAdapter()
188  {
189    // No implementation is performed by default.
190  }
191
192  /**
193   * Determines whether this StoreAdapter can handle the provided
194   * authentication request. The default implementation returns {@code false},
195   * but subclasses may override this if they do support authentication.
196   *
197   * @param request the class of the authentication request.
198   * @return true if the StoreAdapter supports the provided authentication
199   *         request, false otherwise.
200   */
201  public boolean supportsAuthentication(
202      final Class<? extends AuthenticationRequest> request)
203  {
204    return false;
205  }
206
207  /**
208   * Determines whether this StoreAdapter supports reading and writing the user
209   * metadata attributes. The default implementation returns {@code false}, but
210   * subclasses may override this if they do support storing the metadata
211   * attributes.
212   *
213   * @return true if the StoreAdapter supports the Broker metadata, false
214   *         otherwise.
215   */
216  public boolean supportsUserMetaData()
217  {
218    return false;
219  }
220
221  /**
222   * Authenticate the specified user against the backend data store. The default
223   * implementation throws UnsupportedOperationException; subclasses that
224   * support authentication should override this.
225   *
226   * @param scimId the id of the user to authenticate.
227   * @param request the authentication request.
228   * @return <code>true</code> if authentication succeeds or <code>false</code>
229   *         if this StoreAdaptor is unable to support the authentication
230   *         request. In this case, other StoreAdaptors that supports the
231   *         authentication request will be tried.
232   * @throws SCIMException if there is a problem authenticating the user
233   * @throws AuthenticationException if authentication fails
234   */
235  public boolean authenticate(final String scimId,
236                              final AuthenticationRequest request)
237          throws SCIMException, AuthenticationException
238  {
239    throw new com.unboundid.scim.sdk.UnsupportedOperationException(
240            "This StoreAdapter (" + getExtensionName() + ") is not " +
241            "configured to support authentication.");
242  }
243
244  /**
245   * Determines whether this StoreAdapter is currently available and
246   * in-service. This may return {@code false} in the case that all
247   * backend servers are down, for example; during this time the DataView
248   * will return an appropriate 503 response code to clients.
249   *
250   * @return {@code true} if the StoreAdapter initialized and connected;
251   *         {@code false} otherwise.
252   */
253  public abstract boolean isAvailable();
254
255  /**
256   * Gets a ResourceDescriptor that describes the schema for the objects that
257   * will be returned by this StoreAdapter. The StoreAdapter will always be
258   * initialized before this method is called, and the {@link #isAvailable()}
259   * method will be checked as well.
260   *
261   * @return a SCIM ResourceDescriptor object. This may not be {@code null}.
262   */
263  public abstract ResourceDescriptor getNativeSchema();
264
265  /**
266   * Fetches the entries that match the specified criteria.
267   *
268   * @param request the search request
269   * @return a collection of SCIM resources that are in the native schema. This
270   *         may be empty, but it may not be {@code null}.
271   * @throws SCIMException if there is a problem fulfilling the search request
272   */
273  public abstract Resources<? extends BaseResource> search(
274          final StoreSearchRequest request) throws SCIMException;
275
276  /**
277   * Create the specified entry in the native data store.
278   *
279   * @param request the create request
280   * @return the resource that was just created, scoped according to the
281   *         SCIMQueryAttributes contained in the request
282   * @throws SCIMException if there is a problem creating the entry
283   */
284  public abstract BaseResource create(final StoreCreateRequest request)
285          throws SCIMException;
286
287  /**
288   * Update the specified entry in the native data store.
289   *
290   * @param request the update request
291   * @return the updated resource, scoped according to the SCIMQueryAttributes
292   *         contained in the request
293   * @throws SCIMException if there is a problem modifying the entry
294   */
295  public abstract BaseResource update(final StoreUpdateRequest request)
296          throws SCIMException;
297
298  /**
299   * Delete the specified entry from the native data store.
300   *
301   * @param request the delete request
302   * @throws SCIMException if there is a problem deleting the entry
303   */
304  public abstract void delete(final StoreDeleteRequest request)
305          throws SCIMException;
306
307  /**
308   * Update the user metadata and/or user indexed metadata for the specified
309   * user with the specified modifications. At least one of the MetaDataMods
310   * parameters will be non-null. The default implementation throws
311   * UnsupportedOperationException; subclasses should override this if they
312   * support the user metadata attributes.
313   *
314   * @param scimId       the id of the user whose metadata is to be updated
315   * @param metaDataMods the set of modifications to the user metadata
316   *                     (may be {@code null})
317   * @param indexedMetaDataMods the set of modifications to the indexed metadata
318   *                            (may be {@code null})
319   * @param largeMetaDataMods the set of modifications to the large metadata
320   *                            (may be {@code null})
321   * @throws SCIMException if there is a problem updating the metadata
322   */
323  public void updateUserMetaData(final String scimId,
324                                 final MetaDataMods metaDataMods,
325                                 final MetaDataMods indexedMetaDataMods,
326                                 final MetaDataMods largeMetaDataMods)
327          throws SCIMException
328  {
329    throw new com.unboundid.scim.sdk.UnsupportedOperationException(
330            "This StoreAdapter (" + getExtensionName() + ") is not " +
331            "configured to store the user metadata attributes.");
332  }
333
334  /**
335   * Retrieve the user metadata for the specified user. The default
336   * implementation throws UnsupportedOperationException; subclasses should
337   * override this if they support the user metadata attributes.
338   *
339   * @param scimId the id of the user
340   * @return the set of values for the metadata attribute
341   * @throws SCIMException if there is a problem retrieving the metadata
342   */
343  public Collection<ByteString> getUserMetaData(final String scimId)
344          throws SCIMException
345  {
346    throw new com.unboundid.scim.sdk.UnsupportedOperationException(
347            "This StoreAdapter (" + getExtensionName() + ") is not " +
348            "configured to store the user metadata attributes.");
349  }
350
351  /**
352   * Retrieve the user indexed metadata for the specified user. The default
353   * implementation throws UnsupportedOperationException; subclasses should
354   * override this if they support the user metadata attributes.
355   *
356   * @param scimId the id of the user
357   * @return the set of values for the indexed metadata attribute
358   * @throws SCIMException if there is a problem retrieving the large metadata
359   */
360  public Collection<ByteString> getUserIndexedMetaData(final String scimId)
361          throws SCIMException
362  {
363    throw new com.unboundid.scim.sdk.UnsupportedOperationException(
364            "This StoreAdapter (" + getExtensionName() + ") is not " +
365            "configured to store the user metadata attributes.");
366  }
367
368  /**
369   * Retrieve the user large metadata for the specified user. The default
370   * implementation throws UnsupportedOperationException; subclasses should
371   * override this if they support the user metadata attributes.
372   *
373   * @param scimId the id of the user
374   * @return the set of values for the large metadata attribute
375   * @throws SCIMException if there is a problem retrieving the large metadata
376   */
377  public Collection<ByteString> getUserLargeMetaData(final String scimId)
378          throws SCIMException
379  {
380    throw new com.unboundid.scim.sdk.UnsupportedOperationException(
381            "This StoreAdapter (" + getExtensionName() + ") is not " +
382            "configured to store the user metadata attributes.");
383  }
384
385  /**
386   * Retrieve resources that contain the specified user indexed metadata value.
387   *
388   * @param filterType    The filter type to use.
389   * @param value         The user indexed metadata value to search on.
390   * @param requestAttrs  The attributes to return.
391   *
392   * @return  The resources that contain the specified user indexed metadata
393   *          value. Guaranteed not to be {@code null}, but could be empty.
394   *
395   * @throws SCIMException if there is a problem fulfilling the search request
396   */
397  public Resources<? extends BaseResource> searchIndexedMetaData(
398      final SCIMFilterType filterType, final ByteString value,
399      final SCIMQueryAttributes requestAttrs)
400      throws SCIMException
401  {
402    throw new com.unboundid.scim.sdk.UnsupportedOperationException(
403            "This StoreAdapter (" + getExtensionName() + ") is not " +
404            "configured to store the user metadata attributes.");
405  }
406}