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

com.jaeksoft.searchlib.util.ActiveDirectory Maven / Gradle / Ivy

Go to download

OpenSearchServer is a powerful, enterprise-class, search engine program. Using the web user interface, the crawlers (web, file, database, ...) and the REST/RESTFul API you will be able to integrate quickly and easily advanced full-text search capabilities in your application. OpenSearchServer runs on Windows and Linux/Unix/BSD.

The newest version!
/**
 * License Agreement for OpenSearchServer
 * 
 * Copyright (C) 2014-2015 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 .
 **/
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.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;

import org.apache.commons.collections.CollectionUtils;

import com.jaeksoft.searchlib.Logging;

public class ActiveDirectory implements Closeable {

	private final DirContext dirContext;

	private final String domainSearchName;

	public ActiveDirectory(String serverName, 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");

		domainSearchName = getDomainSearch(domain);
		String login = StringUtils.fastConcat(username, "@", domain);
		if (serverName != null) {
			properties.put(Context.PROVIDER_URL,
					StringUtils.fastConcat("ldap://", serverName, ":389"));
		}
		properties.put(Context.SECURITY_PRINCIPAL, login);
		properties.put(Context.SECURITY_CREDENTIALS, password);
		properties.put(Context.REFERRAL, "follow");
		properties.put("java.naming.ldap.attributes.binary", "objectSID");
		dirContext = new InitialLdapContext(properties, null);
	}

	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 ATTR_DN = "DistinguishedName";

	private NamingEnumeration find(String filterExpr,
			String... returningAttributes) throws NamingException {
		SearchControls searchControls = new SearchControls();
		searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
		searchControls.setReturningAttributes(returningAttributes);
		return dirContext.search(domainSearchName, filterExpr, searchControls);
	}

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

	public NamingEnumeration findUser(String username)
			throws NamingException {
		return find(
				StringUtils.fastConcat(
						"(&(objectCategory=person)(objectClass=user)(samAccountType=805306368)(sAMAccountName=",
						username, "))"), ATTR_CN, ATTR_MAIL, ATTR_GIVENNAME,
				ATTR_OBJECTSID, ATTR_SAMACCOUNTNAME, ATTR_MEMBEROF, ATTR_DN);
	}

	private NamingEnumeration findGroup(String group)
			throws NamingException {
		return find(
				StringUtils.fastConcat(
						"(&(objectCategory=group)(objectClass=group)(samAccountType=268435456)(sAMAccountName=",
						group, "))"), ATTR_CN, ATTR_MAIL, ATTR_GIVENNAME,
				ATTR_OBJECTSID, ATTR_SAMACCOUNTNAME, ATTR_MEMBEROF, ATTR_DN);
	}

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

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

	public void findUserGroup(String userDN, Collection collector)
			throws NamingException {
		String filter = StringUtils.fastConcat(
				"(member:1.2.840.113556.1.4.1941:=", userDN, ')');
		NamingEnumeration results = find(filter, ATTR_OBJECTSID,
				ATTR_DN);
		while (results.hasMore()) {
			SearchResult searchResult = results.next();
			Attributes groupAttrs = searchResult.getAttributes();
			Logging.info("ATTRS: " + groupAttrs.toString());
			ADGroup adGroup = new ADGroup(getObjectSID(groupAttrs),
					getStringAttribute(groupAttrs, ATTR_DN));
			collector.add(adGroup);
		}
	}

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

	private static String getDomainName(String domain) {
		String[] dcs = StringUtils.split(domain, '.');
		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().toLowerCase();
	}

	public static void collectMemberOf(Attributes attrs,
			Collection 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(getObjectSID(attrs), memberObject.toString()));
		}
		membersOf.close();
	}

	public static class ADGroup {

		public final String sid;
		public final String cn1;
		public final String cn2;
		public final String dc;

		private ADGroup(final String sid, final String memberOf) {
			this.sid = sid;
			String[] parts = StringUtils.split(memberOf, ',');
			String lcn1 = null;
			String lcn2 = null;
			String ldc = null;
			for (String part : parts) {
				String[] pair = StringUtils.split(part, "=");
				if (pair == null || pair.length != 2)
					continue;
				if ("cn".equalsIgnoreCase(pair[0])) {
					if (lcn1 == null)
						lcn1 = pair[1];
					else if (lcn2 == null)
						lcn2 = pair[1];
				}
				if (ldc == null && "dc".equalsIgnoreCase(pair[0]))
					ldc = pair[1];
			}
			this.cn1 = lcn1;
			this.cn2 = lcn2;
			this.dc = ldc;
		}
	}

	public static String[] toArray(Collection groups,
			String... additionalGroups) {
		TreeSet groupSet = new TreeSet();
		for (ADGroup group : groups) {
			if ("builtin".equalsIgnoreCase(group.cn2))
				groupSet.add(group.cn1.toLowerCase());
			else
				groupSet.add(StringUtils.fastConcat(group.dc, '\\', group.cn1)
						.toLowerCase());
			groupSet.add(group.sid);
		}
		if (additionalGroups != null)
			for (String additionalGroup : additionalGroups)
				groupSet.add(additionalGroup);
		return groupSet.toArray(new String[groupSet.size()]);
	}

	public static void main(String[] args) throws NamingException {
		System.out.println(getDisplayString("sp.int.fr", "01234"));
		System.out
				.println(new ADGroup(
						null,
						"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, '.');
		StringBuilder sb = new StringBuilder();
		boolean first = true;
		for (String dc : dcs) {
			if (!first)
				sb.append(',');
			else
				first = false;
			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).trim();
	}

	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();
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy