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