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. The default implementation throws
310       * UnsupportedOperationException; subclasses should override this if they
311       * support the user metadata attributes.
312       *
313       * @param scimId       the id of the user to who's metadata to update
314       * @param metaDataMods the set of modifications to the user metadata
315       *                     (may be {@code null})
316       * @param indexedMetaDataMods the set of modifications to the indexed metadata
317       *                            (may be {@code null})
318       * @throws SCIMException if there is a problem updating the metadata
319       */
320      public void updateUserMetaData(final String scimId,
321                                     final MetaDataMods metaDataMods,
322                                     final MetaDataMods indexedMetaDataMods)
323              throws SCIMException
324      {
325        throw new com.unboundid.scim.sdk.UnsupportedOperationException(
326                "This StoreAdapter (" + getExtensionName() + ") is not " +
327                "configured to store the user metadata attributes.");
328      }
329    
330      /**
331       * Update the user large metadata for the specified user with the specified
332       * modifications. The default implementation throws
333       * UnsupportedOperationException; subclasses should override this if they
334       * support the user metadata attributes.
335       *
336       * @param scimId       the id of the user to who's metadata to update
337       * @param metaDataMods the set of modifications
338       * @throws SCIMException if there is a problem updating the metadata
339       */
340      public void updateUserLargeMetaData(final String scimId,
341                                          final MetaDataMods metaDataMods)
342              throws SCIMException
343      {
344        throw new com.unboundid.scim.sdk.UnsupportedOperationException(
345                "This StoreAdapter (" + getExtensionName() + ") is not " +
346                "configured to store the user metadata attributes.");
347      }
348    
349      /**
350       * Retrieve the user metadata for the specified user. The default
351       * implementation throws UnsupportedOperationException; subclasses should
352       * override this if they support the user metadata attributes.
353       *
354       * @param scimId the id of the user
355       * @return the set of values for the metadata attribute
356       * @throws SCIMException if there is a problem retrieving the metadata
357       */
358      public Collection<ByteString> getUserMetaData(final String scimId)
359              throws SCIMException
360      {
361        throw new com.unboundid.scim.sdk.UnsupportedOperationException(
362                "This StoreAdapter (" + getExtensionName() + ") is not " +
363                "configured to store the user metadata attributes.");
364      }
365    
366      /**
367       * Retrieve the user indexed metadata for the specified user. The default
368       * implementation throws UnsupportedOperationException; subclasses should
369       * override this if they support the user metadata attributes.
370       *
371       * @param scimId the id of the user
372       * @return the set of values for the indexed metadata attribute
373       * @throws SCIMException if there is a problem retrieving the large metadata
374       */
375      public Collection<ByteString> getUserIndexedMetaData(final String scimId)
376              throws SCIMException
377      {
378        throw new com.unboundid.scim.sdk.UnsupportedOperationException(
379                "This StoreAdapter (" + getExtensionName() + ") is not " +
380                "configured to store the user metadata attributes.");
381      }
382    
383      /**
384       * Retrieve the user large metadata for the specified user. The default
385       * implementation throws UnsupportedOperationException; subclasses should
386       * override this if they support the user metadata attributes.
387       *
388       * @param scimId the id of the user
389       * @return the set of values for the large metadata attribute
390       * @throws SCIMException if there is a problem retrieving the large metadata
391       */
392      public Collection<ByteString> getUserLargeMetaData(final String scimId)
393              throws SCIMException
394      {
395        throw new com.unboundid.scim.sdk.UnsupportedOperationException(
396                "This StoreAdapter (" + getExtensionName() + ") is not " +
397                "configured to store the user metadata attributes.");
398      }
399    
400      /**
401       * Retrieve resources that contain the specified user indexed metadata value.
402       *
403       * @param filterType    The filter type to use.
404       * @param value         The user indexed metadata value to search on.
405       * @param requestAttrs  The attributes to return.
406       *
407       * @return  The resources that contain the specified user indexed metadata
408       *          value. Guaranteed not to be {@code null}, but could be empty.
409       *
410       * @throws SCIMException if there is a problem fulfilling the search request
411       */
412      public Resources<? extends BaseResource> searchIndexedMetaData(
413          final SCIMFilterType filterType, final ByteString value,
414          final SCIMQueryAttributes requestAttrs)
415          throws SCIMException
416      {
417        throw new com.unboundid.scim.sdk.UnsupportedOperationException(
418                "This StoreAdapter (" + getExtensionName() + ") is not " +
419                "configured to store the user metadata attributes.");
420      }
421    }