All Downloads are FREE. Search and download functionalities are using the official Maven repository.

apoc.load.LoadLdap Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [http://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package apoc.load;

import static apoc.ApocConfig.apocConfig;

import apoc.Extended;
import com.novell.ldap.*;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.neo4j.logging.Log;
import org.neo4j.procedure.Context;
import org.neo4j.procedure.Description;
import org.neo4j.procedure.Mode;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.Procedure;

@Extended
public class LoadLdap {

    @Context
    public Log log;

    @Procedure(name = "apoc.load.ldap", mode = Mode.READ)
    @Description(
            "apoc.load.ldap(\"key\" or {connectionMap},{searchMap}) Load entries from an ldap source (yield entry)")
    public Stream ldapQuery(
            @Name("connection") final Object conn, @Name("search") final Map search) {

        LDAPManager mgr = new LDAPManager(getConnectionMap(conn, log));

        return mgr.executeSearch(search);
    }

    public static Map getConnectionMap(Object conn, Log log) {
        if (conn instanceof String) {
            // String value = "ldap.forumsys.com cn=read-only-admin,dc=example,dc=com password";
            String key = String.format("apoc.loadldap.%s.config", conn);
            String value = apocConfig().getString(key);
            // format   
            if (value == null) {
                // fallback: if `apoc.loadldap..config` is not set
                // we check for a config with key `apoc.loadldap.config`
                String keyOld = String.format("apoc.loadldap%s.config", conn);
                value = apocConfig().getString(keyOld);

                // if the value is set and log == null (that is, not from the test LoadLdapTest.testLoadLDAPConfig),
                // we print a log warn, since the correct way should be with a dot before 
                if (value != null && log != null) {
                    String msgWarn = "Not to cause breaking-change, the current config `%s` is valid,\n"
                            + "but in future releases it will be removed in favor of `%s` (with dot before `%s`),\n"
                            + "as documented here: https://neo4j.com/labs/apoc/5/database-integration/load-ldap/#_credentials.\n";
                    String msgWarnFormatted = String.format(msgWarn, keyOld, key, conn);
                    log.warn(msgWarnFormatted);
                }
            }

            // if neither `apoc.loadldap..config` nor `apoc.loadldap.config` is set.
            // we throw an error
            if (value == null) {
                throw new RuntimeException("No " + key + " ldap access configuration specified");
            }
            Map config = new HashMap<>();
            String[] sConf = value.split(" ");
            config.put("ldapHost", sConf[0]);
            config.put("loginDN", sConf[1]);
            config.put("loginPW", sConf[2]);

            return config;

        } else {
            return (Map) conn;
        }
    }

    public static class LDAPManager {
        private static final String LDAP_HOST_P = "ldapHost";
        private static final String LDAP_LOGIN_DN_P = "loginDN";
        private static final String LDAP_LOGIN_PW_P = "loginPW";
        private static final String SEARCH_BASE_P = "searchBase";
        private static final String SEARCH_SCOPE_P = "searchScope";
        private static final String SEARCH_FILTER_P = "searchFilter";
        private static final String SEARCH_ATTRIBUTES_P = "attributes";

        private static final String SCOPE_BASE = "SCOPE_BASE";
        private static final String SCOPE_ONE = "SCOPE_ONE";
        private static final String SCOPE_SUB = "SCOPE_SUB";

        private int ldapPort;
        private int ldapVersion = LDAPConnection.LDAP_V3;
        private String ldapHost;
        private String loginDN;
        private String password;
        private LDAPConnection lc;
        private List attributeList;

        public LDAPManager(Map connParms) {

            String sLdapHostPort = (String) connParms.get(LDAP_HOST_P);
            if (sLdapHostPort.indexOf(":") > -1) {
                this.ldapHost = sLdapHostPort.substring(0, sLdapHostPort.indexOf(":"));
                this.ldapPort = Integer.parseInt(sLdapHostPort.substring(sLdapHostPort.indexOf(":") + 1));
            } else {
                this.ldapHost = sLdapHostPort;
                this.ldapPort = 389; // default
            }

            this.loginDN = (String) connParms.get(LDAP_LOGIN_DN_P);
            this.password = (String) connParms.get(LDAP_LOGIN_PW_P);
        }

        public Stream executeSearch(Map search) {
            try {
                Iterator> supplier = new SearchResultsIterator(doSearch(search), attributeList);
                Spliterator> spliterator =
                        Spliterators.spliteratorUnknownSize(supplier, Spliterator.ORDERED);
                return StreamSupport.stream(spliterator, false)
                        .map(LDAPResult::new)
                        .onClose(() -> closeIt(lc));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public LDAPSearchResults doSearch(Map search) {
            // parse search parameters
            String searchBase = (String) search.get(SEARCH_BASE_P);
            String searchFilter = (String) search.get(SEARCH_FILTER_P);
            String sScope = (String) search.get(SEARCH_SCOPE_P);
            attributeList = (List) search.get(SEARCH_ATTRIBUTES_P);
            if (attributeList == null) attributeList = new ArrayList<>();
            int searchScope = LDAPConnection.SCOPE_SUB;
            if (sScope.equals(SCOPE_BASE)) {
                searchScope = LDAPConnection.SCOPE_BASE;
            } else if (sScope.equals(SCOPE_ONE)) {
                searchScope = LDAPConnection.SCOPE_ONE;
            } else if (sScope.equals(SCOPE_SUB)) {
                searchScope = LDAPConnection.SCOPE_SUB;
            } else {
                throw new RuntimeException(
                        "Invalid scope:" + sScope + ". value scopes are SCOPE_BASE, SCOPE_ONE and SCOPE_SUB");
            }
            // getting an ldap connection
            try {
                lc = getConnection();
                // execute query
                LDAPSearchConstraints cons = new LDAPSearchConstraints();
                cons.setMaxResults(0); // no limit
                LDAPSearchResults searchResults = null;
                if (attributeList == null || attributeList.size() == 0) {
                    searchResults = lc.search(searchBase, searchScope, searchFilter, null, false, cons);
                } else {
                    searchResults = lc.search(
                            searchBase, searchScope, searchFilter, attributeList.toArray(new String[0]), false, cons);
                }
                return searchResults;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        public static void closeIt(LDAPConnection lc) {
            try {
                lc.disconnect();
            } catch (Exception e) {
                // ignore
            }
        }

        private LDAPConnection getConnection() throws LDAPException, UnsupportedEncodingException {
            //        LDAPSocketFactory ssf;
            //        Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
            // String path ="C:\\j2sdk1.4.2_09\\jre\\lib\\security\\cacerts";
            // op("the trustStore: " + System.getProperty("javax.net.ssl.trustStore"));
            // System.setProperty("javax.net.ssl.trustStore", path);
            //        op(" reading the strustStore: " + System.getProperty("javax.net.ssl.trustStore"));
            //        ssf = new LDAPJSSESecureSocketFactory();
            //        LDAPConnection.setSocketFactory(ssf);

            LDAPConnection lc = new LDAPConnection();
            lc.connect(ldapHost, ldapPort);

            // bind to the server
            lc.bind(ldapVersion, loginDN, password.getBytes("UTF8"));
            // tbd
            // LDAPConnection pooling here?
            //
            return lc;
        }
    }

    private static class SearchResultsIterator implements Iterator> {
        private final LDAPSearchResults lsr;
        private final List attributes;
        private Map map;

        public SearchResultsIterator(LDAPSearchResults lsr, List attributes) {
            this.lsr = lsr;
            this.attributes = attributes;
            this.map = get();
        }

        @Override
        public boolean hasNext() {
            return this.map != null;
        }

        @Override
        public Map next() {
            Map current = this.map;
            this.map = get();
            return current;
        }

        public Map get() {
            if (handleEndOfResults()) return null;
            try {
                Map entry = new LinkedHashMap<>(attributes.size() + 1);
                LDAPEntry en = null;
                en = lsr.next();
                entry.put("dn", en.getDN());
                if (attributes != null && attributes.size() > 0) {
                    for (int col = 0; col < attributes.size(); col++) {
                        Object val = readValue(en.getAttributeSet().getAttribute(attributes.get(col)));
                        if (val != null) entry.put(attributes.get(col), val);
                    }
                } else {
                    // make it dynamic
                    Iterator iter = en.getAttributeSet().iterator();
                    while (iter.hasNext()) {
                        LDAPAttribute attr = iter.next();
                        Object val = readValue(attr);
                        if (val != null) entry.put(attr.getName(), readValue(attr));
                    }
                }
                return entry;

            } catch (LDAPException e) {
                throw new RuntimeException("Error getting next ldap entry " + e.getLDAPErrorMessage());
            }
        }

        private boolean handleEndOfResults() {
            if (!lsr.hasMore()) {
                return true;
            }
            return false;
        }

        private Object readValue(LDAPAttribute att) {
            if (att == null) return null;
            if (att.size() == 1) {
                // single value
                // for now everything is string
                return att.getStringValue();
            } else {
                return att.getStringValueArray();
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy