001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2013-2016 ForgeRock AS.
015 */
016package org.forgerock.opendj.adapter.server3x;
017
018import java.net.InetAddress;
019import java.net.UnknownHostException;
020
021import org.forgerock.opendj.ldap.AbstractSynchronousConnection;
022import org.forgerock.opendj.ldap.ByteString;
023import org.forgerock.opendj.ldap.Connection;
024import org.forgerock.opendj.ldap.ConnectionEventListener;
025import org.forgerock.opendj.ldap.ConnectionFactory;
026import org.forgerock.opendj.ldap.DN;
027import org.forgerock.opendj.ldap.DecodeException;
028import org.forgerock.opendj.ldap.DecodeOptions;
029import org.forgerock.opendj.ldap.IntermediateResponseHandler;
030import org.forgerock.opendj.ldap.LdapException;
031import org.forgerock.opendj.ldap.ResultCode;
032import org.forgerock.opendj.ldap.SearchResultHandler;
033import org.forgerock.opendj.ldap.controls.Control;
034import org.forgerock.opendj.ldap.requests.AddRequest;
035import org.forgerock.opendj.ldap.requests.BindClient;
036import org.forgerock.opendj.ldap.requests.BindRequest;
037import org.forgerock.opendj.ldap.requests.CompareRequest;
038import org.forgerock.opendj.ldap.requests.DeleteRequest;
039import org.forgerock.opendj.ldap.requests.ExtendedRequest;
040import org.forgerock.opendj.ldap.requests.GenericBindRequest;
041import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
042import org.forgerock.opendj.ldap.requests.ModifyRequest;
043import org.forgerock.opendj.ldap.requests.SASLBindRequest;
044import org.forgerock.opendj.ldap.requests.SearchRequest;
045import org.forgerock.opendj.ldap.requests.SimpleBindRequest;
046import org.forgerock.opendj.ldap.requests.UnbindRequest;
047import org.forgerock.opendj.ldap.responses.BindResult;
048import org.forgerock.opendj.ldap.responses.CompareResult;
049import org.forgerock.opendj.ldap.responses.ExtendedResult;
050import org.forgerock.opendj.ldap.responses.GenericExtendedResult;
051import org.forgerock.opendj.ldap.responses.Responses;
052import org.forgerock.opendj.ldap.responses.Result;
053import org.forgerock.util.promise.Promise;
054import org.opends.server.core.AddOperation;
055import org.opends.server.core.BindOperation;
056import org.opends.server.core.CompareOperation;
057import org.opends.server.core.DeleteOperation;
058import org.opends.server.core.ExtendedOperation;
059import org.opends.server.protocols.internal.InternalClientConnection;
060import org.opends.server.protocols.internal.InternalSearchListener;
061import org.opends.server.protocols.internal.InternalSearchOperation;
062import org.opends.server.protocols.internal.Requests;
063import org.opends.server.types.DirectoryException;
064import org.opends.server.types.SearchFilter;
065import org.opends.server.types.SearchResultEntry;
066import org.opends.server.types.SearchResultReference;
067
068import static org.forgerock.opendj.adapter.server3x.Converters.*;
069import static org.forgerock.opendj.ldap.ByteString.*;
070import static org.forgerock.opendj.ldap.LdapException.*;
071import static org.forgerock.util.promise.Promises.*;
072
073/** This class provides a connection factory and an adapter for the OpenDJ 2.x server. */
074public final class Adapters {
075
076    /** Constructor. */
077    private Adapters() {
078        // No implementation required.
079    }
080
081    /**
082     * Returns a new root connection factory.
083     *
084     * @return A new root connection factory.
085     */
086    public static ConnectionFactory newRootConnectionFactory() {
087        InternalClientConnection icc = InternalClientConnection.getRootConnection();
088        return newConnectionFactory(icc);
089    }
090
091    /**
092     * Returns a new connection factory.
093     *
094     * @param icc
095     *            The internal client connection from server side.
096     * @return A new SDK connection factory.
097     */
098    public static ConnectionFactory newConnectionFactory(final InternalClientConnection icc) {
099        return new ConnectionFactory() {
100            @Override
101            public Promise<Connection, LdapException> getConnectionAsync() {
102               try
103              {
104                return newResultPromise(getConnection());
105              }
106              catch (LdapException e)
107              {
108                return newExceptionPromise(e);
109              }
110            }
111
112            @Override
113            public Connection getConnection() throws LdapException {
114                return newConnection(icc);
115            }
116
117            @Override
118            public void close() {
119                // Nothing to do.
120            }
121        };
122    }
123
124    /**
125     * Returns a new connection.
126     *
127     * @param icc
128     *            The internal client connection from server side.
129     * @return A new SDK connection.
130     */
131    public static Connection newConnection(final InternalClientConnection icc) {
132      return new AbstractSynchronousConnection() {
133
134          @Override
135          public Result search(final SearchRequest request, final SearchResultHandler handler)
136                  throws LdapException {
137              InternalSearchListener internalSearchListener = new InternalSearchListener() {
138
139                  @Override
140                  public void handleInternalSearchReference(
141                          InternalSearchOperation searchOperation,
142                          SearchResultReference searchReference) throws DirectoryException {
143                      handler.handleReference(from(searchReference));
144                  }
145
146                  @Override
147                  public void handleInternalSearchEntry(InternalSearchOperation searchOperation,
148                          SearchResultEntry searchEntry) throws DirectoryException {
149                      handler.handleEntry(from(searchEntry));
150                  }
151              };
152
153              final SearchFilter filter = toSearchFilter(request.getFilter());
154              final org.opends.server.protocols.internal.SearchRequest sr =
155                  Requests.newSearchRequest(request.getName(), request.getScope(), filter)
156                      .setDereferenceAliasesPolicy(request.getDereferenceAliasesPolicy())
157                      .setSizeLimit(request.getSizeLimit())
158                      .setTimeLimit(request.getTimeLimit())
159                      .setTypesOnly(request.isTypesOnly())
160                      .addAttribute(request.getAttributes())
161                      .addControl(to(request.getControls()));
162              return getResponseResult(icc.processSearch(sr, internalSearchListener));
163          }
164
165          @Override
166          public void removeConnectionEventListener(ConnectionEventListener listener) {
167              // Internal client connection don't have any connection events.
168          }
169
170          @Override
171          public Result modifyDN(final ModifyDNRequest request) throws LdapException {
172              return getResponseResult(icc.processModifyDN(request));
173          }
174
175          @Override
176          public Result modify(final ModifyRequest request) throws LdapException {
177              return getResponseResult(icc.processModify(request));
178          }
179
180          @Override
181          public boolean isValid() {
182              // Always true.
183              return true;
184          }
185
186          @Override
187          public boolean isClosed() {
188              return false;
189          }
190
191          @Override
192          public <R extends ExtendedResult> R extendedRequest(final ExtendedRequest<R> request,
193                  final IntermediateResponseHandler handler) throws LdapException {
194
195              final ExtendedOperation extendedOperation =
196                      icc.processExtendedOperation(request.getOID(), request.getValue(),
197                              to(request.getControls()));
198
199              final Result result = getResponseResult(extendedOperation);
200              final GenericExtendedResult genericExtendedResult =
201                      Responses.newGenericExtendedResult(result.getResultCode())
202                               .setDiagnosticMessage(result.getDiagnosticMessage())
203                               .setMatchedDN(result.getMatchedDN())
204                               .setValue(extendedOperation.getResponseValue());
205              try {
206                  R extendedResult =
207                          request.getResultDecoder().decodeExtendedResult(genericExtendedResult,
208                                  new DecodeOptions());
209                  for (final Control control : result.getControls()) {
210                      extendedResult.addControl(control);
211                  }
212                  return extendedResult;
213
214              } catch (DecodeException e) {
215                  DN matchedDN = extendedOperation.getMatchedDN();
216                  return request.getResultDecoder().newExtendedErrorResult(
217                          extendedOperation.getResultCode(),
218                          matchedDN != null ? matchedDN.toString() : null,
219                          extendedOperation.getErrorMessage().toString());
220              }
221          }
222
223          @Override
224          public Result delete(final DeleteRequest request) throws LdapException {
225              final DeleteOperation deleteOperation =
226                      icc.processDelete(valueOfObject(request.getName()), to(request.getControls()));
227              return getResponseResult(deleteOperation);
228          }
229
230          @Override
231          public CompareResult compare(final CompareRequest request) throws LdapException {
232              final CompareOperation compareOperation =
233                      icc.processCompare(valueOfObject(request.getName()),
234                              request.getAttributeDescription().toString(),
235                              request.getAssertionValue(), to(request.getControls()));
236
237              CompareResult result = Responses.newCompareResult(compareOperation.getResultCode());
238              return getResponseResult(compareOperation, result);
239          }
240
241          @Override
242          public void close(final UnbindRequest request, final String reason) {
243              // no implementation in open-ds.
244          }
245
246          @Override
247          public BindResult bind(final BindRequest request) throws LdapException {
248              BindOperation bindOperation = null;
249              if (request instanceof SimpleBindRequest) {
250                  bindOperation =
251                          icc.processSimpleBind(valueOfUtf8(request.getName()),
252                                  ByteString.wrap(((SimpleBindRequest) request).getPassword()),
253                                  to(request.getControls()));
254              } else if (request instanceof SASLBindRequest) {
255                  String serverName = null;
256                  try {
257                      serverName = InetAddress.getByName(null).getCanonicalHostName();
258                  } catch (UnknownHostException e) {
259                      // nothing to do.
260                  }
261                  BindClient bindClient = request.createBindClient(serverName);
262                  do {
263                      final GenericBindRequest genericBindRequest = bindClient.nextBindRequest();
264                      bindOperation =
265                              icc.processSASLBind(
266                                      valueOfUtf8(request.getName()),
267                                      ((SASLBindRequest) request).getSASLMechanism(),
268                                      getCredentials(genericBindRequest.getAuthenticationValue()),
269                                      to(request.getControls()));
270                  } while (bindOperation.getResultCode() == ResultCode.SASL_BIND_IN_PROGRESS);
271
272                  bindClient.dispose();
273
274              } else { // not supported
275                  throw newLdapException(Responses.newResult(ResultCode.AUTH_METHOD_NOT_SUPPORTED));
276              }
277              BindResult result = Responses.newBindResult(bindOperation.getResultCode());
278              result.setServerSASLCredentials(bindOperation.getSASLCredentials());
279
280              if (result.isSuccess()) {
281                  return result;
282              } else {
283                  throw newLdapException(result);
284              }
285          }
286
287          @Override
288          public void addConnectionEventListener(ConnectionEventListener listener) {
289              // Internal client connection don't have any connection events.
290          }
291
292          @Override
293          public Result add(final AddRequest request) throws LdapException {
294              final AddOperation addOperation =
295                      icc.processAdd(valueOfObject(request.getName()), to(request
296                              .getAllAttributes()), to(request.getControls()));
297              return getResponseResult(addOperation);
298          }
299
300          @Override
301          public String toString() {
302              return icc.toString();
303          }
304      };
305  }
306
307}