com.jaeksoft.searchlib.util.ActiveDirectory.java Source code

Java tutorial

Introduction

Here is the source code for com.jaeksoft.searchlib.util.ActiveDirectory.java

Source

package com.jaeksoft.searchlib.util;

import java.io.Closeable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

import org.apache.commons.collections.CollectionUtils;

import com.jaeksoft.searchlib.Logging;

/**
 * License Agreement for OpenSearchServer
 * 
 * Copyright (C) 2014 Emmanuel Keller / Jaeksoft
 * 
 * http://www.open-search-server.com
 * 
 * This file is part of OpenSearchServer.
 * 
 * OpenSearchServer is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 * 
 * OpenSearchServer is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * OpenSearchServer. If not, see <http://www.gnu.org/licenses/>.
 **/

public class ActiveDirectory implements Closeable {

    private DirContext dirContext = null;
    private String domainSearchName = null;

    public ActiveDirectory(String username, String password, String domain) throws NamingException {
        if (StringUtils.isEmpty(domain))
            throw new NamingException("The domain is empty");
        Properties properties = new Properties();
        properties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        properties.put(Context.PROVIDER_URL, StringUtils.fastConcat("LDAP://", domain));
        properties.put(Context.SECURITY_PRINCIPAL, StringUtils.fastConcat(username, "@", domain));
        properties.put(Context.SECURITY_CREDENTIALS, password);
        properties.put("java.naming.ldap.attributes.binary", "objectSID");
        properties.put(Context.REFERRAL, "follow");
        dirContext = new InitialDirContext(properties);
        domainSearchName = getDomainSearch(domain);
    }

    public final static String ATTR_CN = "cn";
    public final static String ATTR_MAIL = "mail";
    public final static String ATTR_GIVENNAME = "givenName";
    public final static String ATTR_MEMBEROF = "memberOf";
    public final static String ATTR_OBJECTSID = "objectSid";
    public final static String ATTR_SAMACCOUNTNAME = "sAMAccountName";

    public final static String[] DefaultReturningAttributes = { ATTR_CN, ATTR_MAIL, ATTR_GIVENNAME, ATTR_OBJECTSID,
            ATTR_SAMACCOUNTNAME, ATTR_MEMBEROF };

    private NamingEnumeration<SearchResult> find(String filterExpr, String... returningAttributes)
            throws NamingException {
        SearchControls searchControls = new SearchControls();
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        if (returningAttributes == null || returningAttributes.length == 0)
            returningAttributes = DefaultReturningAttributes;
        searchControls.setReturningAttributes(returningAttributes);
        return dirContext.search(domainSearchName, filterExpr, searchControls);
    }

    public static final Attributes getAttributes(NamingEnumeration<SearchResult> result) throws NamingException {
        if (result == null)
            return null;
        if (!result.hasMore())
            return null;
        SearchResult rs = (SearchResult) result.next();
        return rs.getAttributes();
    }

    public NamingEnumeration<SearchResult> findUser(String username, String... returningAttributes)
            throws NamingException {
        return find(StringUtils.fastConcat("(&((&(objectCategory=Person)(objectClass=User)))(samaccountname=",
                username, "))"), returningAttributes);
    }

    private NamingEnumeration<SearchResult> findGroup(String group) throws NamingException {
        return find(StringUtils.fastConcat("(&((&(objectCategory=Group)(objectClass=Group)))(samaccountname=",
                group, "))"));
    }

    private void findGroups(Collection<ADGroup> groups, Collection<ADGroup> collector, Set<String> searchedGroups)
            throws NamingException {
        if (CollectionUtils.isEmpty(groups))
            return;
        List<ADGroup> newGroups = new ArrayList<ADGroup>();
        for (ADGroup group : groups) {
            if (searchedGroups.contains(group.cn))
                continue;
            collector.add(group);
            searchedGroups.add(group.cn);
            NamingEnumeration<SearchResult> result = findGroup(group.cn);
            Attributes attrs = getAttributes(result);
            if (attrs == null)
                continue;
            collectMemberOf(attrs, newGroups);
        }
        findGroups(newGroups, collector, searchedGroups);
    }

    public void findUserGroups(Attributes userAttrs, Collection<ADGroup> collector) throws NamingException {
        List<ADGroup> groups = new ArrayList<ADGroup>();
        ActiveDirectory.collectMemberOf(userAttrs, groups);
        TreeSet<String> searchedGroups = new TreeSet<String>();
        findGroups(groups, collector, searchedGroups);
    }

    @Override
    public void close() {
        try {
            if (dirContext != null)
                dirContext.close();
            dirContext = null;
        } catch (NamingException e) {
            Logging.warn(e);
        }
    }

    private static String getDomainName(String domain) {
        String[] dcs = StringUtils.split(domain.toUpperCase(), '.');
        return dcs != null && dcs.length > 0 ? dcs[0] : null;
    }

    final public static String getDisplayString(String domain, String user) {
        StringBuilder sb = new StringBuilder();
        String domainName = getDomainName(domain);
        if (domainName != null)
            sb.append(domainName);
        if (user != null) {
            if (sb.length() > 0)
                sb.append('\\');
            sb.append(user);
        }
        return sb.toString();
    }

    public static void collectMemberOf(Attributes attrs, Collection<ADGroup> groups) throws NamingException {
        Attribute tga = attrs.get("memberOf");
        if (tga == null)
            return;
        NamingEnumeration<?> membersOf = tga.getAll();
        while (membersOf.hasMore()) {
            Object memberObject = membersOf.next();
            groups.add(new ADGroup(memberObject.toString()));
        }
        membersOf.close();
    }

    public static class ADGroup {

        public final String cn;
        public final String dc;

        private ADGroup(final String memberOf) {
            String[] parts = StringUtils.split(memberOf, ',');
            String lcn = null;
            String ldc = null;
            for (String part : parts) {
                String[] pair = StringUtils.split(part, "=");
                if (pair == null || pair.length != 2)
                    continue;
                if (lcn == null && "CN".equals(pair[0]))
                    lcn = pair[1];
                if (ldc == null && "DC".equals(pair[0]))
                    ldc = pair[1].toUpperCase();
            }
            this.cn = lcn;
            this.dc = ldc;
        }
    }

    public static String[] toArray(List<ADGroup> groups) {
        if (groups == null)
            return null;
        String[] array = new String[groups.size()];
        int i = 0;
        for (ADGroup group : groups)
            array[i++] = StringUtils.fastConcat(group.dc, '\\', group.cn);
        return array;
    }

    public static void main(String[] args) {
        System.out.println(getDisplayString("sp.int.fr", "01234"));
        System.out.println(
                new ADGroup("CN=GG-TEST-TEST-TEST,OU=Groupes Ressource,OU=Groupes,OU=DSCP,DC=sp,DC=pn,DC=int"));
    }

    private static String getDomainSearch(String domain) {
        String[] dcs = StringUtils.split(domain.toUpperCase(), '.');
        StringBuilder sb = new StringBuilder();
        for (String dc : dcs) {
            if (sb.length() > 0)
                sb.append(',');
            sb.append("DC=");
            sb.append(dc);
        }
        return sb.toString();
    }

    public static String getStringAttribute(Attributes attrs, String name) {
        Attribute attr = attrs.get(name);
        if (attr == null)
            return null;
        String s = attr.toString();
        if (StringUtils.isEmpty(s))
            return s;
        int i = s.indexOf(':');
        if (i == -1)
            throw new IllegalArgumentException(StringUtils.fastConcat("Wrong returned value: ", s));
        return s.substring(i + 1);
    }

    public static String getObjectSID(Attributes attrs) throws NamingException {
        Attribute attr = attrs.get("objectsid");
        if (attr == null)
            throw new NamingException("No ObjectSID attribute");
        Object attrObject = attr.get();
        if (attrObject == null)
            throw new NamingException("ObjectSID is empty");
        if (attrObject instanceof String) {
            String attrString = (String) attrObject;
            if (attrString.startsWith("S-"))
                return attrString;
            return decodeSID(attrString.getBytes());
        } else if (attrObject instanceof byte[]) {
            return decodeSID((byte[]) attrObject);
        } else
            throw new NamingException("Unknown attribute type: " + attrObject.getClass().getName());
    }

    /**
     * The binary data is in the form: byte[0] - revision level byte[1] - count
     * of sub-authorities byte[2-7] - 48 bit authority (big-endian) and then
     * count x 32 bit sub authorities (little-endian)
     * 
     * The String value is: S-Revision-Authority-SubAuthority[n]...
     * 
     * Based on code from here -
     * http://forums.oracle.com/forums/thread.jspa?threadID=1155740&tstart=0
     */
    public static String decodeSID(byte[] sid) {

        final StringBuilder strSid = new StringBuilder("S-");

        // get version
        final int revision = sid[0];
        strSid.append(Integer.toString(revision));

        // next byte is the count of sub-authorities
        final int countSubAuths = sid[1] & 0xFF;

        // get the authority
        long authority = 0;
        // String rid = "";
        for (int i = 2; i <= 7; i++) {
            authority |= ((long) sid[i]) << (8 * (5 - (i - 2)));
        }
        strSid.append("-");
        strSid.append(Long.toHexString(authority));

        // iterate all the sub-auths
        int offset = 8;
        int size = 4; // 4 bytes for each sub auth
        for (int j = 0; j < countSubAuths; j++) {
            long subAuthority = 0;
            for (int k = 0; k < size; k++) {
                subAuthority |= (long) (sid[offset + k] & 0xFF) << (8 * k);
            }

            strSid.append("-");
            strSid.append(subAuthority);

            offset += size;
        }

        return strSid.toString();
    }

}