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