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}