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 }