com.jaeksoft.searchlib.util.ActiveDirectory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of opensearchserver Show documentation
Show all versions of opensearchserver Show documentation
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();
}
}