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 }