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 }