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-2014 UnboundID Corp.
026     */
027    package com.unboundid.directory.sdk.broker.api;
028    
029    import com.unboundid.directory.sdk.broker.config.StoreAdapterConfig;
030    import com.unboundid.directory.sdk.broker.internal.IdentityBrokerExtension;
031    import com.unboundid.directory.sdk.http.types.AuthenticationException;
032    import com.unboundid.directory.sdk.http.types.AuthenticationRequest;
033    import com.unboundid.directory.sdk.broker.types.IdentityBrokerContext;
034    import com.unboundid.directory.sdk.broker.types.MetaDataMods;
035    import com.unboundid.directory.sdk.broker.types.StoreCreateRequest;
036    import com.unboundid.directory.sdk.broker.types.StoreDeleteRequest;
037    import com.unboundid.directory.sdk.broker.types.StoreSearchRequest;
038    import com.unboundid.directory.sdk.broker.types.StoreUpdateRequest;
039    import com.unboundid.directory.sdk.common.internal.Configurable;
040    import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
041    import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
042    import com.unboundid.scim.data.BaseResource;
043    import com.unboundid.scim.schema.ResourceDescriptor;
044    import com.unboundid.scim.sdk.Resources;
045    import com.unboundid.scim.sdk.SCIMException;
046    import com.unboundid.scim.sdk.SCIMFilterType;
047    import com.unboundid.scim.sdk.SCIMQueryAttributes;
048    import com.unboundid.util.ByteString;
049    import com.unboundid.util.Extensible;
050    import com.unboundid.util.ThreadSafety;
051    import com.unboundid.util.ThreadSafetyLevel;
052    import com.unboundid.util.args.ArgumentException;
053    import com.unboundid.util.args.ArgumentParser;
054    
055    import java.util.Collection;
056    import java.util.Collections;
057    import java.util.List;
058    import 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)
111    public 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    }