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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2013-2014 ForgeRock AS. 025 */ 026package org.forgerock.opendj.adapter.server3x; 027 028import java.net.InetAddress; 029import java.net.UnknownHostException; 030 031import org.forgerock.opendj.ldap.AbstractSynchronousConnection; 032import org.forgerock.opendj.ldap.ByteString; 033import org.forgerock.opendj.ldap.Connection; 034import org.forgerock.opendj.ldap.ConnectionEventListener; 035import org.forgerock.opendj.ldap.ConnectionFactory; 036import org.forgerock.opendj.ldap.DN; 037import org.forgerock.opendj.ldap.DecodeException; 038import org.forgerock.opendj.ldap.DecodeOptions; 039import org.forgerock.opendj.ldap.IntermediateResponseHandler; 040import org.forgerock.opendj.ldap.LdapException; 041import org.forgerock.opendj.ldap.ResultCode; 042import org.forgerock.opendj.ldap.SearchResultHandler; 043import org.forgerock.opendj.ldap.controls.Control; 044import org.forgerock.opendj.ldap.requests.AddRequest; 045import org.forgerock.opendj.ldap.requests.BindClient; 046import org.forgerock.opendj.ldap.requests.BindRequest; 047import org.forgerock.opendj.ldap.requests.CompareRequest; 048import org.forgerock.opendj.ldap.requests.DeleteRequest; 049import org.forgerock.opendj.ldap.requests.ExtendedRequest; 050import org.forgerock.opendj.ldap.requests.GenericBindRequest; 051import org.forgerock.opendj.ldap.requests.ModifyDNRequest; 052import org.forgerock.opendj.ldap.requests.ModifyRequest; 053import org.forgerock.opendj.ldap.requests.SASLBindRequest; 054import org.forgerock.opendj.ldap.requests.SearchRequest; 055import org.forgerock.opendj.ldap.requests.SimpleBindRequest; 056import org.forgerock.opendj.ldap.requests.UnbindRequest; 057import org.forgerock.opendj.ldap.responses.BindResult; 058import org.forgerock.opendj.ldap.responses.CompareResult; 059import org.forgerock.opendj.ldap.responses.ExtendedResult; 060import org.forgerock.opendj.ldap.responses.GenericExtendedResult; 061import org.forgerock.opendj.ldap.responses.Responses; 062import org.forgerock.opendj.ldap.responses.Result; 063import org.forgerock.util.promise.Promise; 064import org.opends.server.core.AddOperation; 065import org.opends.server.core.BindOperation; 066import org.opends.server.core.CompareOperation; 067import org.opends.server.core.DeleteOperation; 068import org.opends.server.core.ExtendedOperation; 069import org.opends.server.core.ModifyDNOperation; 070import org.opends.server.core.ModifyOperation; 071import org.opends.server.protocols.internal.InternalClientConnection; 072import org.opends.server.protocols.internal.InternalSearchListener; 073import org.opends.server.protocols.internal.InternalSearchOperation; 074import org.opends.server.protocols.internal.Requests; 075import org.opends.server.types.AuthenticationInfo; 076import org.opends.server.types.DirectoryException; 077import org.opends.server.types.SearchFilter; 078import org.opends.server.types.SearchResultEntry; 079import org.opends.server.types.SearchResultReference; 080 081import static org.forgerock.opendj.adapter.server3x.Converters.*; 082import static org.forgerock.opendj.ldap.ByteString.*; 083import static org.forgerock.opendj.ldap.LdapException.*; 084import static org.forgerock.util.promise.Promises.*; 085 086/** 087 * This class provides a connection factory and an adapter for the OpenDJ 2.x 088 * server. 089 */ 090public final class Adapters { 091 092 /** 093 * Constructor. 094 */ 095 private Adapters() { 096 // No implementation required. 097 } 098 099 /** 100 * Returns a new root connection factory. 101 * 102 * @return A new root connection factory. 103 */ 104 public static ConnectionFactory newRootConnectionFactory() { 105 InternalClientConnection icc = InternalClientConnection.getRootConnection(); 106 return newConnectionFactory(icc); 107 } 108 109 /** 110 * Returns a new anonymous connection factory. 111 * 112 * @return A new anonymous connection factory. 113 */ 114 public static ConnectionFactory newAnonymousConnectionFactory() { 115 InternalClientConnection icc = new InternalClientConnection(new AuthenticationInfo()); 116 return newConnectionFactory(icc); 117 } 118 119 /** 120 * Returns a new connection factory for a specified user. 121 * 122 * @param userDN 123 * The specified user's DN. 124 * @return a new connection factory. 125 */ 126 public static ConnectionFactory newConnectionFactoryForUser(final DN userDN) { 127 InternalClientConnection icc = null; 128 try { 129 icc = new InternalClientConnection(to(userDN)); 130 } catch (DirectoryException e) { 131 throw new IllegalStateException(e.getMessage(), e); 132 } 133 return newConnectionFactory(icc); 134 } 135 136 /** 137 * Returns a new connection factory. 138 * 139 * @param icc 140 * The internal client connection from server side. 141 * @return A new SDK connection factory. 142 */ 143 public static ConnectionFactory newConnectionFactory(final InternalClientConnection icc) { 144 return new ConnectionFactory() { 145 146 @Override 147 public void close() { 148 // Nothing to do. 149 } 150 151 @Override 152 public Promise<Connection, LdapException> getConnectionAsync() { 153 // TODO change the path... 154 return newResultPromise(newConnection(icc)); 155 } 156 157 @Override 158 public Connection getConnection() throws LdapException { 159 return newConnection(icc); 160 } 161 }; 162 } 163 164 /** 165 * Returns a new root connection. 166 * 167 * @return A new root connection. 168 */ 169 public static Connection newRootConnection() { 170 return newConnection(InternalClientConnection.getRootConnection()); 171 } 172 173 /** 174 * Returns a new connection for an anonymous user. 175 * 176 * @return A new connection. 177 */ 178 public static Connection newAnonymousConnection() { 179 return newConnection(new InternalClientConnection(new AuthenticationInfo())); 180 } 181 182 /** 183 * Returns a new connection for a specified user. 184 * 185 * @param dn 186 * The DN of the user. 187 * @return A new connection for a specified user. 188 * @throws LdapException 189 * If no such object. 190 */ 191 public static Connection newConnectionForUser(final DN dn) throws LdapException { 192 try { 193 return newConnection(new InternalClientConnection(to(dn))); 194 } catch (DirectoryException e) { 195 throw newLdapException(Responses.newResult(ResultCode.NO_SUCH_OBJECT)); 196 } 197 } 198 199 private static Connection newConnection(final InternalClientConnection icc) { 200 return new AbstractSynchronousConnection() { 201 202 @Override 203 public Result search(final SearchRequest request, final SearchResultHandler handler) 204 throws LdapException { 205 InternalSearchListener internalSearchListener = new InternalSearchListener() { 206 207 @Override 208 public void handleInternalSearchReference( 209 InternalSearchOperation searchOperation, 210 SearchResultReference searchReference) throws DirectoryException { 211 handler.handleReference(from(searchReference)); 212 } 213 214 @Override 215 public void handleInternalSearchEntry(InternalSearchOperation searchOperation, 216 SearchResultEntry searchEntry) throws DirectoryException { 217 handler.handleEntry(from(searchEntry)); 218 } 219 }; 220 221 final SearchFilter filter = toSearchFilter(request.getFilter()); 222 final org.opends.server.protocols.internal.SearchRequest sr = 223 Requests.newSearchRequest(to(request.getName()), request.getScope(), filter) 224 .setDereferenceAliasesPolicy(request.getDereferenceAliasesPolicy()) 225 .setSizeLimit(request.getSizeLimit()) 226 .setTimeLimit(request.getTimeLimit()) 227 .setTypesOnly(request.isTypesOnly()) 228 .addAttribute(request.getAttributes()) 229 .addControl(to(request.getControls())); 230 return getResponseResult(icc.processSearch(sr, internalSearchListener)); 231 } 232 233 @Override 234 public void removeConnectionEventListener(ConnectionEventListener listener) { 235 // Internal client connection don't have any connection events. 236 } 237 238 @Override 239 public Result modifyDN(final ModifyDNRequest request) throws LdapException { 240 final ModifyDNOperation modifyDNOperation = 241 icc.processModifyDN(valueOf(request.getName()), 242 valueOf(request.getNewRDN()), 243 request.isDeleteOldRDN(), 244 request.getNewSuperior() != null ? valueOf(request.getNewSuperior()) : null, 245 to(request.getControls())); 246 return getResponseResult(modifyDNOperation); 247 } 248 249 @Override 250 public Result modify(final ModifyRequest request) throws LdapException { 251 final ModifyOperation modifyOperation = 252 icc.processModify(valueOf(request.getName()), toRawModifications(request 253 .getModifications()), to(request.getControls())); 254 return getResponseResult(modifyOperation); 255 } 256 257 @Override 258 public boolean isValid() { 259 // Always true. 260 return true; 261 } 262 263 @Override 264 public boolean isClosed() { 265 return false; 266 } 267 268 @Override 269 public <R extends ExtendedResult> R extendedRequest(final ExtendedRequest<R> request, 270 final IntermediateResponseHandler handler) throws LdapException { 271 272 final ExtendedOperation extendedOperation = 273 icc.processExtendedOperation(request.getOID(), request.getValue(), 274 to(request.getControls())); 275 276 final Result result = getResponseResult(extendedOperation); 277 final GenericExtendedResult genericExtendedResult = 278 Responses.newGenericExtendedResult(result.getResultCode()) 279 .setDiagnosticMessage(result.getDiagnosticMessage()).setMatchedDN( 280 result.getMatchedDN()).setValue( 281 extendedOperation.getResponseValue().toByteString()); 282 try { 283 R extendedResult = 284 request.getResultDecoder().decodeExtendedResult(genericExtendedResult, 285 new DecodeOptions()); 286 for (final Control control : result.getControls()) { 287 extendedResult.addControl(control); 288 } 289 return extendedResult; 290 291 } catch (DecodeException e) { 292 org.opends.server.types.DN matchedDN = extendedOperation.getMatchedDN(); 293 return request.getResultDecoder().newExtendedErrorResult( 294 extendedOperation.getResultCode(), 295 matchedDN != null ? matchedDN.toString() : null, 296 extendedOperation.getErrorMessage().toString()); 297 } 298 } 299 300 @Override 301 public Result delete(final DeleteRequest request) throws LdapException { 302 final DeleteOperation deleteOperation = 303 icc.processDelete(valueOf(request.getName()), to(request.getControls())); 304 return getResponseResult(deleteOperation); 305 } 306 307 @Override 308 public CompareResult compare(final CompareRequest request) throws LdapException { 309 final CompareOperation compareOperation = 310 icc.processCompare(valueOf(request.getName()), 311 request.getAttributeDescription().toString(), 312 request.getAssertionValue(), to(request.getControls())); 313 314 CompareResult result = Responses.newCompareResult(compareOperation.getResultCode()); 315 return getResponseResult(compareOperation, result); 316 } 317 318 @Override 319 public void close(final UnbindRequest request, final String reason) { 320 // no implementation in open-ds. 321 } 322 323 @Override 324 public BindResult bind(final BindRequest request) throws LdapException { 325 BindOperation bindOperation = null; 326 if (request instanceof SimpleBindRequest) { 327 bindOperation = 328 icc.processSimpleBind(valueOf(request.getName()), 329 ByteString.wrap(((SimpleBindRequest) request).getPassword()), 330 to(request.getControls())); 331 } else if (request instanceof SASLBindRequest) { 332 String serverName = null; 333 try { 334 serverName = InetAddress.getByName(null).getCanonicalHostName(); 335 } catch (UnknownHostException e) { 336 // nothing to do. 337 } 338 BindClient bindClient = request.createBindClient(serverName); 339 do { 340 final GenericBindRequest genericBindRequest = bindClient.nextBindRequest(); 341 bindOperation = 342 icc.processSASLBind( 343 valueOf(request.getName()), 344 ((SASLBindRequest) request).getSASLMechanism(), 345 getCredentials(genericBindRequest.getAuthenticationValue()), 346 to(request.getControls())); 347 } while (bindOperation.getResultCode() == ResultCode.SASL_BIND_IN_PROGRESS); 348 349 bindClient.dispose(); 350 351 } else { // not supported 352 throw newLdapException(Responses.newResult(ResultCode.AUTH_METHOD_NOT_SUPPORTED)); 353 } 354 BindResult result = Responses.newBindResult(bindOperation.getResultCode()); 355 result.setServerSASLCredentials(bindOperation.getSASLCredentials()); 356 357 if (result.isSuccess()) { 358 return result; 359 } else { 360 throw newLdapException(result); 361 } 362 } 363 364 @Override 365 public void addConnectionEventListener(ConnectionEventListener listener) { 366 // Internal client connection don't have any connection events. 367 } 368 369 @Override 370 public Result add(final AddRequest request) throws LdapException { 371 final AddOperation addOperation = 372 icc.processAdd(valueOf(request.getName()), to(request 373 .getAllAttributes()), to(request.getControls())); 374 return getResponseResult(addOperation); 375 } 376 377 @Override 378 public String toString() { 379 return icc.toString(); 380 } 381 }; 382 } 383}