[Java] Reference / update of Active Directory

Introduction

I want to be able to get and update the user list in order to provide the function to maintain AD users. In Java, you can access AD using JNDI.

AD reference

Get a single user

To search by specifying the user ID for AD authentication, refer to the URL below.

https://www.earthlink.co.jp/engineerblog/intra-mart-engineerblog/3336/

Get user list

For searches that are expected to have multiple results, such as a list of users belonging to a particular OU, you need to be aware of the LDAP page size. For Windows Active Directory, perhaps 1,000 is the maximum size of the result. If you want to get more than that, repeat the search while paging.

Sample to get user list while paging


Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://server"); //AD server address
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "userid@domain"); //User ID to log on to AD server@domain
env.put(Context.SECURITY_CREDENTIALS, "password"); //password

final int PAGE_SIZE = 1000;

InitialLdapContext context = null;
try {
	context = new InitialLdapContext(env, null);

	//Specifying search conditions
	String name = "OU=aaa, DC=bbb, DC=ccc";
	String filter = "(objectCategory=user)";

	SearchControls control = new SearchControls();
	control.setSearchScope(SearchControls.SUBTREE_SCOPE);

	byte[] cookie = null;

	do {
		context.setRequestControls(new Control[] { new PagedResultsControl(PAGE_SIZE, cookie,
				Control.CRITICAL) });

		//Search execution
		NamingEnumeration<SearchResult> results = context.search(name, filter, control);

		//Get results
		while (results.hasMore()) {
			SearchResult result = results.next();
			AdUserBean user = new AdUserBean(result);

			System.out.println(user);
		}

		//Check if there is a next page
		cookie = null;

		if (context.getResponseControls() != null) {
			Optional<Control> found = Arrays.stream(context.getResponseControls())
					.filter(c -> c instanceof PagedResultsResponseControl).findFirst();

			if (found.isPresent()) {
				PagedResultsResponseControl prrc = (PagedResultsResponseControl) found.get();
				cookie = prrc.getCookie();
			}
		}

	} while (cookie != null);

} finally {
	try {
		if (context != null) {
			context.close();
		}
	} catch (NamingException e) {
	}
}

AdUserBean


public class AdUserBean {

	private String cn = null;
	private String name = null;
	/**name*/
	private String displayName = null;
	/**User ID + domain*/
	private String userPrincipalName = null;
	/**LDAP Distinguished Name*/
	private String distinguishedName = null;
	/**mail address*/
	private String mail = null;
	/**Pager*/
	private String pager = null;

	/**
	 *constructor
	 */
	public AdUserBean() {

	}


	/**
	 *constructor
	 *
	 *Get content from Active Directory search results
	 *
	 * @param result
	 * @throws NamingException
	 */
	public AdUserBean(SearchResult result) throws NamingException {
		NamingEnumeration<? extends Attribute> attributes = result.getAttributes().getAll();

		while (attributes.hasMoreElements()) {
			Attribute attribute = attributes.next();
			Object value = attribute.get();

			if ("name".equals(attribute.getID())) {
				if (value instanceof String) {
					name = (String) value;
				}

			} else if ("displayName".equals(attribute.getID())) {
				if (value instanceof String) {
					displayName = (String) value;
				}

			} else if ("cn".equals(attribute.getID())) {
				if (value instanceof String) {
					cn = (String) value;
				}

			} else if ("userPrincipalName".equals(attribute.getID())) {
				if (value instanceof String) {
					userPrincipalName = (String) value;
				}

			} else if ("pager".equals(attribute.getID())) {
				if (value instanceof String) {
					pager = (String) value;
				}

			} else if ("mail".equals(attribute.getID())) {
				if (value instanceof String) {
					mail = (String) value;
				}

			} else if ("distinguishedName".equals(attribute.getID())) {
				if (value instanceof String) {
					distinguishedName = (String) value;
				}

			}
		}

	}

//The following is omitted
}

Difference between hasMore and hasMoreElements

There are two types of NamingEnumeration class, which is the return value of thesearch ()method, the hasMoreElements method in the ordinary ʻEnumeration class and the original hasMore` method.

while (results.hasMore()) {
	SearchResult result = results.next();
	//Abbreviation
}

Using the hasMore method throws a NamingException, for example, when trying to get more than the page size. https://docs.oracle.com/javase/jp/11/docs/api/java.naming/javax/naming/NamingEnumeration.html

However, when searching AD while paging as in the previous sample, no error will occur even if the page size specified on the code side> the page size on the server side, and the search result will be the page size on the server side. returned.

How to take attribute values

Active Directory attributes include items with a single value and items with multiple values. Please note that the acquisition method is different for each.

Excerpt from the acquisition part of the attribute value


/**Pager*/
private String pager = null;
/**Pager(Other) */
private List<String> otherPager = null;

public AdUserBean(SearchResult result) throws NamingException {
	NamingEnumeration<? extends Attribute> attributes = result.getAttributes().getAll();

	while (attributes.hasMoreElements()) {
		Attribute attribute = attributes.next();
		Object value = attribute.get();

		if ("pager".equals(attribute.getID())) {
			//Pager(Single item)
			if (value instanceof String) {
				pager = (String) value;
			}

		} else if ("otherPager".equals(attribute.getID())) {
			//Pager(Other) (Multiple items)
			NamingEnumeration<?> values = attribute.getAll();

			while (values.hasMore()) {
				if (value instanceof String) {
					if (otherPager == null) {
						otherPager = new ArrayList<String>();
					}

					value = values.next();

					otherPager.add((String) value);
				}
			}

		}
	}
}

Change user attributes

Change user attributes


private static void update() throws NamingException {

	Hashtable<String, String> env = new Hashtable<String, String>();
	env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
	env.put(Context.PROVIDER_URL, "ldap://server"); //AD server address
	env.put(Context.SECURITY_AUTHENTICATION, "simple");
	env.put(Context.SECURITY_PRINCIPAL, "user@domain"); //User ID to log on to AD server@domain
	env.put(Context.SECURITY_CREDENTIALS, "password"); //password

	DirContext context = null;
	try {
		context = new InitialDirContext(env);

		Attributes attrs = new BasicAttributes();
		attrs.put(new BasicAttribute("pager", "123456"));
		attrs.put(new BasicAttribute("telephoneNumber", "1234567"));

		String name = "CN=username, OU=xxx, DC=yyy, DC=zzz";

		context.modifyAttributes(name, DirContext.REPLACE_ATTRIBUTE, attrs);

	} finally {
		try {
			if (context != null) {
				context.close();
			}
		} catch (NamingException e) {
		}
	}
}

The DN specified in the argument of modifyAttributes must specify the OU to which the user (object) belongs. If you omit the OU hierarchy, an error will occur if "object not found".

Reference of attributes and search filters

The email address is a single item, but multiple phone numbers can be specified.

Descriptions of Active Directory user attributes (there are other explanation pages for each type of attribute) https://docs.microsoft.com/en-us/windows/win32/ad/naming-properties

Active Directory Attribute List https://docs.microsoft.com/en-us/windows/win32/adschema/attributes-all

LDAP search filter syntax http://software.fujitsu.com/jp/manual/manualfiles/M050000/B1WN4911/01/idmgr07/idmgr447.htm https://www.ibm.com/support/knowledgecenter/ja/SSYJ99_8.5.0/admin-system/rbug_ldapfltrxprns.html

Recommended Posts

[Java] Reference / update of Active Directory
Think of a Java update strategy
Memo: [Java] Check the contents of the directory
Java8 method reference
java8 method reference
JAVA reference materials
java directory creation
My Java reference
[Java] Overview of Java
Java review ③ (Basic usage of arrays / reference type)
Expired collection of java
Predicted Features of Java
[Java] Significance of serialVersionUID
NIO.2 review of java
Review of java Shilber
Mac Java Home Directory
java --Unification of comments
Utilization of Active Hash
History of Java annotation
java (merits of polymorphism)
Pitfalls of Active Hash
NIO review of java
[Java] Three features of Java
Summary of Java support 2018
[Java] Difference between assignment of basic type variable and assignment of reference type variable
Weak reference of JAVA reused instance allocated for each thread
[Java] Integer wrapper class reference
About an instance of java
[Java] Mirage-Basic usage of SQL
Java update for Scala users
Java VB.net service reference halfway
[Java] Practice of exception handling [Exception]
[Java11] Stream Summary -Advantages of Stream-
Basics of character operation (java)
4th day of java learning
[Java] Beginner's understanding of Servlet-①
Java end of month plusMonths
[Java] Summary of regular expressions
[Java] Summary of operators (operator)
[Java] Implementation of Faistel Network
Update timing of Eclipse org.eclipse.wst.common.component
[Java] Comparator of Collection class
Summary of Java language basics
[Java] Summary of for statements
Summary of Java Math class
Enumeration of all combinations Java
java (inheritance of is-a principle)
Implementation of gzip in java
Advantages and disadvantages of Java
Benefits of Java static method
[Java] Summary of control syntax
Implementation of tri-tree in Java
Stop automatic update of Ubuntu
Update timing of Eclipse org.eclipse.wst.common.component
Summary of java error processing
[Java] Summary of design patterns
[Java] Summary of mathematical operations
Handling of java floating point [Note] while reading the reference book
The story of not knowing the behavior of String by passing Java by reference