/* * Copyright 2012-2017 CodeLibs Project and the Others. * * 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 org.codelibs.fess.ldap; import static org.codelibs.core.stream.StreamUtil.stream; import java.util.ArrayList; import java.util.Base64; import java.util.Collections; import java.util.Hashtable; import java.util.List; import java.util.Locale; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Collectors; import javax.naming.Context; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.directory.BasicAttribute; import javax.naming.directory.BasicAttributes; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; import javax.naming.directory.ModificationItem; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import org.codelibs.core.lang.StringUtil; import org.codelibs.fess.Constants; import org.codelibs.fess.entity.FessUser; import org.codelibs.fess.es.user.exentity.Group; import org.codelibs.fess.es.user.exentity.Role; import org.codelibs.fess.es.user.exentity.User; import org.codelibs.fess.exception.LdapOperationException; import org.codelibs.fess.helper.SystemHelper; import org.codelibs.fess.mylasta.direction.FessConfig; import org.codelibs.fess.util.ComponentUtil; import org.codelibs.fess.util.OptionalUtil; import org.dbflute.optional.OptionalEntity; import org.dbflute.util.DfTypeUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LdapManager { private static final Logger logger = LoggerFactory.getLogger(LdapManager.class); protected ThreadLocal<DirContextHolder> contextLocal = new ThreadLocal<>(); protected volatile boolean isBind = false; protected Hashtable<String, String> createEnvironment(final String initialContextFactory, final String securityAuthentication, final String providerUrl, final String principal, final String credntials) { final Hashtable<String, String> env = new Hashtable<>(); env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory); env.put(Context.SECURITY_AUTHENTICATION, securityAuthentication); env.put(Context.PROVIDER_URL, providerUrl); env.put(Context.SECURITY_PRINCIPAL, principal); env.put(Context.SECURITY_CREDENTIALS, credntials); if (providerUrl != null && providerUrl.startsWith("ldaps://")) { env.put(Context.SECURITY_PROTOCOL, "ssl"); } return env; } protected Hashtable<String, String> createAdminEnv() { final FessConfig fessConfig = ComponentUtil.getFessConfig(); return createEnvironment(// fessConfig.getLdapInitialContextFactory(), // fessConfig.getLdapSecurityAuthentication(), fessConfig.getLdapProviderUrl(), // fessConfig.getLdapAdminSecurityPrincipal(), // fessConfig.getLdapAdminSecurityCredentials()); } protected Hashtable<String, String> createSearchEnv(final String username, final String password) { final FessConfig fessConfig = ComponentUtil.getFessConfig(); return createEnvironment(// fessConfig.getLdapInitialContextFactory(), // fessConfig.getLdapSecurityAuthentication(), // fessConfig.getLdapProviderUrl(), // fessConfig.getLdapSecurityPrincipal(username), password); } protected Hashtable<String, String> createSearchEnv() { final FessConfig fessConfig = ComponentUtil.getFessConfig(); return createEnvironment(// fessConfig.getLdapInitialContextFactory(), // fessConfig.getLdapSecurityAuthentication(), fessConfig.getLdapProviderUrl(), // fessConfig.getLdapAdminSecurityPrincipal(), // fessConfig.getLdapAdminSecurityCredentials()); } public void updateConfig() { isBind = false; } protected boolean validate() { if (!isBind) { final Hashtable<String, String> env = createAdminEnv(); try (DirContextHolder holder = getDirContext(() -> env)) { final DirContext context = holder.get(); if (logger.isDebugEnabled()) { logger.debug("Logged in as Bind DN.", context); } isBind = true; } catch (final Exception e) { logger.warn("LDAP configuration is wrong.", e); } } return isBind; } public OptionalEntity<FessUser> login(final String username, final String password) { final FessConfig fessConfig = ComponentUtil.getFessConfig(); if (StringUtil.isBlank(fessConfig.getLdapProviderUrl())) { return OptionalEntity.empty(); } if (!validate()) { return OptionalEntity.empty(); } final Hashtable<String, String> env = createSearchEnv(username, password); try (DirContextHolder holder = getDirContext(() -> env)) { final DirContext context = holder.get(); if (logger.isDebugEnabled()) { logger.debug("Logged in.", context); } return OptionalEntity.of(createLdapUser(username, env)); } catch (final Exception e) { logger.debug("Login failed.", e); } return OptionalEntity.empty(); } public OptionalEntity<FessUser> login(final String username) { final Hashtable<String, String> env = createSearchEnv(); try (DirContextHolder holder = getDirContext(() -> env)) { final DirContext context = holder.get(); if (logger.isDebugEnabled()) { logger.debug("Logged in.", context); } return OptionalEntity.of(createLdapUser(username, env)); } catch (final Exception e) { logger.debug("Login failed.", e); } return OptionalEntity.empty(); } protected LdapUser createLdapUser(final String username, final Hashtable<String, String> env) { return new LdapUser(env, username); } public String[] getRoles(final LdapUser ldapUser, final String bindDn, final String accountFilter) { final SystemHelper systemHelper = ComponentUtil.getSystemHelper(); final FessConfig fessConfig = ComponentUtil.getFessConfig(); final List<String> roleList = new ArrayList<>(); if (fessConfig.isLdapRoleSearchUserEnabled()) { roleList.add(systemHelper.getSearchRoleByUser(ldapUser.getName())); } // LDAP: cn=%s // AD: (&(objectClass=user)(sAMAccountName=%s)) final String filter = String.format(accountFilter, ldapUser.getName()); search(bindDn, filter, new String[] { fessConfig.getLdapMemberofAttribute() }, () -> ldapUser.getEnvironment(), result -> { processSearchRoles(result, (entryDn, name) -> { final boolean isRole = entryDn.toLowerCase(Locale.ROOT).indexOf("ou=role") != -1; if (isRole) { if (fessConfig.isLdapRoleSearchRoleEnabled()) { roleList.add(systemHelper.getSearchRoleByRole(name)); } } else if (fessConfig.isLdapRoleSearchGroupEnabled()) { roleList.add(systemHelper.getSearchRoleByGroup(name)); } }); }); return roleList.toArray(new String[roleList.size()]); } protected void processSearchRoles(final List<SearchResult> result, final BiConsumer<String, String> consumer) throws NamingException { final FessConfig fessConfig = ComponentUtil.getFessConfig(); for (final SearchResult srcrslt : result) { final Attributes attrs = srcrslt.getAttributes(); //get group attr final Attribute attr = attrs.get(fessConfig.getLdapMemberofAttribute()); if (attr == null) { continue; } for (int i = 0; i < attr.size(); i++) { final Object attrValue = attr.get(i); if (attrValue != null) { final String entryDn = attrValue.toString(); int start = 0; int end = 0; start = entryDn.indexOf("CN="); if (start < 0) { start = entryDn.indexOf("cn="); } if (start == -1) { continue; } start += 3; end = entryDn.indexOf(','); String name; if (end == -1) { name = entryDn.substring(start); } else { name = entryDn.substring(start, end); } consumer.accept(entryDn, name); } } } } protected void setAttributeValue(final List<SearchResult> result, final String name, final Consumer<Object> consumer) { final List<Object> attrList = getAttributeValueList(result, name); if (!attrList.isEmpty()) { consumer.accept(attrList.get(0)); } } protected List<Object> getAttributeValueList(final List<SearchResult> result, final String name) { try { for (final SearchResult srcrslt : result) { final Attributes attrs = srcrslt.getAttributes(); final Attribute attr = attrs.get(name); if (attr == null) { continue; } final List<Object> attrList = new ArrayList<>(); for (int i = 0; i < attr.size(); i++) { final Object attrValue = attr.get(i); if (attrValue != null) { attrList.add(attrValue); } } return attrList; } return Collections.emptyList(); } catch (final NamingException e) { throw new LdapOperationException("Failed to parse attribute values for " + name, e); } } public void apply(final User user) { final FessConfig fessConfig = ComponentUtil.getFessConfig(); if (!fessConfig.isLdapAdminEnabled(user.getName())) { return; } final Supplier<Hashtable<String, String>> adminEnv = () -> createAdminEnv(); search(fessConfig.getLdapAdminUserBaseDn(), fessConfig.getLdapAdminUserFilter(user.getName()), null, adminEnv, result -> { if (!result.isEmpty()) { setAttributeValue(result, fessConfig.getLdapAttrSurname(), o -> user.setSurname(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrGivenName(), o -> user.setGivenName(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrMail(), o -> user.setMail(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrEmployeeNumber(), o -> user.setEmployeeNumber(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrTelephoneNumber(), o -> user.setTelephoneNumber(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrHomePhone(), o -> user.setHomePhone(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrHomePostalAddress(), o -> user.setHomePostalAddress(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrLabeleduri(), o -> user.setLabeledURI(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrRoomNumber(), o -> user.setRoomNumber(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrDescription(), o -> user.setDescription(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrTitle(), o -> user.setTitle(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrPager(), o -> user.setPager(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrStreet(), o -> user.setStreet(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrPostalCode(), o -> user.setPostalCode(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrPhysicalDeliveryOfficeName(), o -> user.setPhysicalDeliveryOfficeName(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrDestinationIndicator(), o -> user.setDestinationIndicator(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrInternationalisdnNumber(), o -> user.setInternationaliSDNNumber(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrState(), o -> user.setState(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrEmployeeType(), o -> user.setEmployeeType(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrFacsimileTelephoneNumber(), o -> user.setFacsimileTelephoneNumber(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrPostOfficeBox(), o -> user.setPostOfficeBox(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrInitials(), o -> user.setInitials(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrCarLicense(), o -> user.setCarLicense(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrMobile(), o -> user.setMobile(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrPostalAddress(), o -> user.setPostalAddress(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrCity(), o -> user.setCity(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrTeletexTerminalIdentifier(), o -> user.setTeletexTerminalIdentifier(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrX121Address(), o -> user.setX121Address(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrBusinessCategory(), o -> user.setBusinessCategory(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrRegisteredAddress(), o -> user.setRegisteredAddress(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrDisplayName(), o -> user.setDisplayName(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrPreferredLanguage(), o -> user.setPreferredLanguage(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrDepartmentNumber(), o -> user.setDepartmentNumber(o.toString())); setAttributeValue(result, fessConfig.getLdapAttrUidNumber(), o -> user.setUidNumber(DfTypeUtil.toLong(o))); setAttributeValue(result, fessConfig.getLdapAttrGidNumber(), o -> user.setGidNumber(DfTypeUtil.toLong(o))); setAttributeValue(result, fessConfig.getLdapAttrHomeDirectory(), o -> user.setHomeDirectory(o.toString())); } }); // groups and roles search(fessConfig.getLdapAdminUserBaseDn(), fessConfig.getLdapAdminUserFilter(user.getName()), new String[] { fessConfig.getLdapMemberofAttribute() }, adminEnv, result -> { if (!result.isEmpty()) { final List<String> groupList = new ArrayList<>(); final List<String> roleList = new ArrayList<>(); final String lowerGroupDn = fessConfig.getLdapAdminGroupBaseDn().toLowerCase(Locale.ROOT); final String lowerRoleDn = fessConfig.getLdapAdminRoleBaseDn().toLowerCase(Locale.ROOT); processSearchRoles(result, (entryDn, name) -> { final String lowerEntryDn = entryDn.toLowerCase(Locale.ROOT); if (lowerEntryDn.indexOf(lowerGroupDn) != -1) { groupList.add(Base64.getUrlEncoder().encodeToString(name.getBytes(Constants.CHARSET_UTF_8))); } else if (lowerEntryDn.indexOf(lowerRoleDn) != -1) { roleList.add(Base64.getUrlEncoder().encodeToString(name.getBytes(Constants.CHARSET_UTF_8))); } }); user.setGroups(groupList.toArray(new String[groupList.size()])); user.setRoles(roleList.toArray(new String[roleList.size()])); } }); } public void insert(final User user) { final FessConfig fessConfig = ComponentUtil.getFessConfig(); if (!fessConfig.isLdapAdminEnabled(user.getName())) { return; } final Supplier<Hashtable<String, String>> adminEnv = () -> createAdminEnv(); final String userDN = fessConfig.getLdapAdminUserSecurityPrincipal(user.getName()); // attributes search(fessConfig.getLdapAdminUserBaseDn(), fessConfig.getLdapAdminUserFilter(user.getName()), null, adminEnv, result -> { if (!result.isEmpty()) { modifyUserAttributes(user, adminEnv, userDN, result, fessConfig); } else { final BasicAttributes entry = new BasicAttributes(); addUserAttributes(entry, user, fessConfig); final Attribute oc = fessConfig.getLdapAdminUserObjectClassAttribute(); entry.put(oc); insert(userDN, entry, adminEnv); } }); // groups and roles search(fessConfig.getLdapAdminUserBaseDn(), fessConfig.getLdapAdminUserFilter(user.getName()), new String[] { fessConfig.getLdapMemberofAttribute() }, adminEnv, result -> { if (!result.isEmpty()) { final List<String> oldGroupList = new ArrayList<>(); final List<String> oldRoleList = new ArrayList<>(); final String lowerGroupDn = fessConfig.getLdapAdminGroupBaseDn().toLowerCase(Locale.ROOT); final String lowerRoleDn = fessConfig.getLdapAdminRoleBaseDn().toLowerCase(Locale.ROOT); processSearchRoles(result, (entryDn, name) -> { final String lowerEntryDn = entryDn.toLowerCase(Locale.ROOT); if (lowerEntryDn.indexOf(lowerGroupDn) != -1) { oldGroupList.add(name); } else if (lowerEntryDn.indexOf(lowerRoleDn) != -1) { oldRoleList.add(name); } }); final List<String> newGroupList = stream(user.getGroupNames()).get(stream -> stream.collect(Collectors.toList())); stream(user.getGroupNames()).of(stream -> stream.forEach(name -> { if (oldGroupList.contains(name)) { oldGroupList.remove(name); newGroupList.remove(name); } })); oldGroupList.stream().forEach( name -> { search(fessConfig.getLdapAdminGroupBaseDn(), fessConfig.getLdapAdminGroupFilter(name), null, adminEnv, subResult -> { if (!subResult.isEmpty()) { final List<ModificationItem> modifyList = new ArrayList<>(); modifyDeleteEntry(modifyList, "member", userDN); modify(fessConfig.getLdapAdminGroupSecurityPrincipal(name), modifyList, adminEnv); } }); }); newGroupList.stream().forEach( name -> { search(fessConfig.getLdapAdminGroupBaseDn(), fessConfig.getLdapAdminGroupFilter(name), null, adminEnv, subResult -> { if (!!subResult.isEmpty()) { final Group group = new Group(); group.setName(name); insert(group); } final List<ModificationItem> modifyList = new ArrayList<>(); modifyAddEntry(modifyList, "member", userDN); modify(fessConfig.getLdapAdminGroupSecurityPrincipal(name), modifyList, adminEnv); }); }); final List<String> newRoleList = stream(user.getRoleNames()).get(stream -> stream.collect(Collectors.toList())); stream(user.getRoleNames()).of(stream -> stream.forEach(name -> { if (oldRoleList.contains(name)) { oldRoleList.remove(name); newRoleList.remove(name); } })); oldRoleList.stream().forEach( name -> { search(fessConfig.getLdapAdminRoleBaseDn(), fessConfig.getLdapAdminRoleFilter(name), null, adminEnv, subResult -> { if (!subResult.isEmpty()) { final List<ModificationItem> modifyList = new ArrayList<>(); modifyDeleteEntry(modifyList, "member", userDN); modify(fessConfig.getLdapAdminRoleSecurityPrincipal(name), modifyList, adminEnv); } }); }); newRoleList.stream().forEach( name -> { search(fessConfig.getLdapAdminRoleBaseDn(), fessConfig.getLdapAdminRoleFilter(name), null, adminEnv, subResult -> { if (!!subResult.isEmpty()) { final Role role = new Role(); role.setName(name); insert(role); } final List<ModificationItem> modifyList = new ArrayList<>(); modifyAddEntry(modifyList, "member", userDN); modify(fessConfig.getLdapAdminRoleSecurityPrincipal(name), modifyList, adminEnv); }); }); } else { stream(user.getGroupNames()).of( stream -> stream.forEach(name -> { search(fessConfig.getLdapAdminGroupBaseDn(), fessConfig.getLdapAdminGroupFilter(name), null, adminEnv, subResult -> { if (!!subResult.isEmpty()) { final Group group = new Group(); group.setName(name); insert(group); } final List<ModificationItem> modifyList = new ArrayList<>(); modifyAddEntry(modifyList, "member", userDN); modify(fessConfig.getLdapAdminGroupSecurityPrincipal(name), modifyList, adminEnv); }); })); stream(user.getRoleNames()).of( stream -> stream.forEach(name -> { search(fessConfig.getLdapAdminRoleBaseDn(), fessConfig.getLdapAdminRoleFilter(name), null, adminEnv, subResult -> { if (!!subResult.isEmpty()) { final Role role = new Role(); role.setName(name); insert(role); } final List<ModificationItem> modifyList = new ArrayList<>(); modifyAddEntry(modifyList, "member", userDN); modify(fessConfig.getLdapAdminRoleSecurityPrincipal(name), modifyList, adminEnv); }); })); } }); } protected void modifyUserAttributes(final User user, final Supplier<Hashtable<String, String>> adminEnv, final String userDN, final List<SearchResult> result, final FessConfig fessConfig) { final List<ModificationItem> modifyList = new ArrayList<>(); if (user.getOriginalPassword() != null) { modifyReplaceEntry(modifyList, "userPassword", user.getOriginalPassword()); } final String attrSurname = fessConfig.getLdapAttrSurname(); OptionalUtil .ofNullable(user.getSurname()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrSurname, s)) .orElse(() -> getAttributeValueList(result, attrSurname).stream().forEach( v -> modifyDeleteEntry(modifyList, attrSurname, v))); final String attrGivenName = fessConfig.getLdapAttrGivenName(); OptionalUtil .ofNullable(user.getGivenName()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrGivenName, s)) .orElse(() -> getAttributeValueList(result, attrGivenName).stream().forEach( v -> modifyDeleteEntry(modifyList, attrGivenName, v))); final String attrMail = fessConfig.getLdapAttrMail(); OptionalUtil.ofNullable(user.getMail()).filter(StringUtil::isNotBlank).ifPresent(s -> modifyReplaceEntry(modifyList, attrMail, s)) .orElse(() -> getAttributeValueList(result, attrMail).stream().forEach(v -> modifyDeleteEntry(modifyList, attrMail, v))); final String attrEmployeeNumber = fessConfig.getLdapAttrEmployeeNumber(); OptionalUtil .ofNullable(user.getEmployeeNumber()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrEmployeeNumber, s)) .orElse(() -> getAttributeValueList(result, attrEmployeeNumber).stream().forEach( v -> modifyDeleteEntry(modifyList, attrEmployeeNumber, v))); final String attrTelephoneNumber = fessConfig.getLdapAttrTelephoneNumber(); OptionalUtil .ofNullable(user.getTelephoneNumber()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrTelephoneNumber, s)) .orElse(() -> getAttributeValueList(result, attrTelephoneNumber).stream().forEach( v -> modifyDeleteEntry(modifyList, attrTelephoneNumber, v))); final String attrHomePhone = fessConfig.getLdapAttrHomePhone(); OptionalUtil .ofNullable(user.getHomePhone()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrHomePhone, s)) .orElse(() -> getAttributeValueList(result, attrHomePhone).stream().forEach( v -> modifyDeleteEntry(modifyList, attrHomePhone, v))); final String attrHomePostalAddress = fessConfig.getLdapAttrHomePostalAddress(); OptionalUtil .ofNullable(user.getHomePostalAddress()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrHomePostalAddress, s)) .orElse(() -> getAttributeValueList(result, attrHomePostalAddress).stream().forEach( v -> modifyDeleteEntry(modifyList, attrHomePostalAddress, v))); final String attrLabeledURI = fessConfig.getLdapAttrLabeleduri(); OptionalUtil .ofNullable(user.getLabeledURI()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrLabeledURI, s)) .orElse(() -> getAttributeValueList(result, attrLabeledURI).stream().forEach( v -> modifyDeleteEntry(modifyList, attrLabeledURI, v))); final String attrRoomNumber = fessConfig.getLdapAttrRoomNumber(); OptionalUtil .ofNullable(user.getRoomNumber()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrRoomNumber, s)) .orElse(() -> getAttributeValueList(result, attrRoomNumber).stream().forEach( v -> modifyDeleteEntry(modifyList, attrRoomNumber, v))); final String attrDescription = fessConfig.getLdapAttrDescription(); OptionalUtil .ofNullable(user.getDescription()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrDescription, s)) .orElse(() -> getAttributeValueList(result, attrDescription).stream().forEach( v -> modifyDeleteEntry(modifyList, attrDescription, v))); final String attrTitle = fessConfig.getLdapAttrTitle(); OptionalUtil.ofNullable(user.getTitle()).filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrTitle, s)) .orElse(() -> getAttributeValueList(result, attrTitle).stream().forEach(v -> modifyDeleteEntry(modifyList, attrTitle, v))); final String attrPager = fessConfig.getLdapAttrPager(); OptionalUtil.ofNullable(user.getPager()).filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrPager, s)) .orElse(() -> getAttributeValueList(result, attrPager).stream().forEach(v -> modifyDeleteEntry(modifyList, attrPager, v))); final String attrStreet = fessConfig.getLdapAttrStreet(); OptionalUtil .ofNullable(user.getStreet()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrStreet, s)) .orElse(() -> getAttributeValueList(result, attrStreet).stream().forEach(v -> modifyDeleteEntry(modifyList, attrStreet, v))); final String attrPostalCode = fessConfig.getLdapAttrPostalCode(); OptionalUtil .ofNullable(user.getPostalCode()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrPostalCode, s)) .orElse(() -> getAttributeValueList(result, attrPostalCode).stream().forEach( v -> modifyDeleteEntry(modifyList, attrPostalCode, v))); final String attrPhysicalDeliveryOfficeName = fessConfig.getLdapAttrPhysicalDeliveryOfficeName(); OptionalUtil .ofNullable(user.getPhysicalDeliveryOfficeName()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrPhysicalDeliveryOfficeName, s)) .orElse(() -> getAttributeValueList(result, attrPhysicalDeliveryOfficeName).stream().forEach( v -> modifyDeleteEntry(modifyList, attrPhysicalDeliveryOfficeName, v))); final String attrDestinationIndicator = fessConfig.getLdapAttrDestinationIndicator(); OptionalUtil .ofNullable(user.getDestinationIndicator()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrDestinationIndicator, s)) .orElse(() -> getAttributeValueList(result, attrDestinationIndicator).stream().forEach( v -> modifyDeleteEntry(modifyList, attrDestinationIndicator, v))); final String attrInternationaliSDNNumber = fessConfig.getLdapAttrInternationalisdnNumber(); OptionalUtil .ofNullable(user.getInternationaliSDNNumber()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrInternationaliSDNNumber, s)) .orElse(() -> getAttributeValueList(result, attrInternationaliSDNNumber).stream().forEach( v -> modifyDeleteEntry(modifyList, attrInternationaliSDNNumber, v))); final String attrState = fessConfig.getLdapAttrState(); OptionalUtil.ofNullable(user.getState()).filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrState, s)) .orElse(() -> getAttributeValueList(result, attrState).stream().forEach(v -> modifyDeleteEntry(modifyList, attrState, v))); final String attrEmployeeType = fessConfig.getLdapAttrEmployeeType(); OptionalUtil .ofNullable(user.getEmployeeType()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrEmployeeType, s)) .orElse(() -> getAttributeValueList(result, attrEmployeeType).stream().forEach( v -> modifyDeleteEntry(modifyList, attrEmployeeType, v))); final String attrFacsimileTelephoneNumber = fessConfig.getLdapAttrFacsimileTelephoneNumber(); OptionalUtil .ofNullable(user.getFacsimileTelephoneNumber()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrFacsimileTelephoneNumber, s)) .orElse(() -> getAttributeValueList(result, attrFacsimileTelephoneNumber).stream().forEach( v -> modifyDeleteEntry(modifyList, attrFacsimileTelephoneNumber, v))); final String attrPostOfficeBox = fessConfig.getLdapAttrPostOfficeBox(); OptionalUtil .ofNullable(user.getPostOfficeBox()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrPostOfficeBox, s)) .orElse(() -> getAttributeValueList(result, attrPostOfficeBox).stream().forEach( v -> modifyDeleteEntry(modifyList, attrPostOfficeBox, v))); final String attrInitials = fessConfig.getLdapAttrInitials(); OptionalUtil .ofNullable(user.getInitials()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrInitials, s)) .orElse(() -> getAttributeValueList(result, attrInitials).stream().forEach( v -> modifyDeleteEntry(modifyList, attrInitials, v))); final String attrCarLicense = fessConfig.getLdapAttrCarLicense(); OptionalUtil .ofNullable(user.getCarLicense()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrCarLicense, s)) .orElse(() -> getAttributeValueList(result, attrCarLicense).stream().forEach( v -> modifyDeleteEntry(modifyList, attrCarLicense, v))); final String attrMobile = fessConfig.getLdapAttrMobile(); OptionalUtil .ofNullable(user.getMobile()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrMobile, s)) .orElse(() -> getAttributeValueList(result, attrMobile).stream().forEach(v -> modifyDeleteEntry(modifyList, attrMobile, v))); final String attrPostalAddress = fessConfig.getLdapAttrPostalAddress(); OptionalUtil .ofNullable(user.getPostalAddress()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrPostalAddress, s)) .orElse(() -> getAttributeValueList(result, attrPostalAddress).stream().forEach( v -> modifyDeleteEntry(modifyList, attrPostalAddress, v))); final String attrCity = fessConfig.getLdapAttrCity(); OptionalUtil.ofNullable(user.getCity()).filter(StringUtil::isNotBlank).ifPresent(s -> modifyReplaceEntry(modifyList, attrCity, s)) .orElse(() -> getAttributeValueList(result, attrCity).stream().forEach(v -> modifyDeleteEntry(modifyList, attrCity, v))); final String attrTeletexTerminalIdentifier = fessConfig.getLdapAttrTeletexTerminalIdentifier(); OptionalUtil .ofNullable(user.getTeletexTerminalIdentifier()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrTeletexTerminalIdentifier, s)) .orElse(() -> getAttributeValueList(result, attrTeletexTerminalIdentifier).stream().forEach( v -> modifyDeleteEntry(modifyList, attrTeletexTerminalIdentifier, v))); final String attrX121Address = fessConfig.getLdapAttrX121Address(); OptionalUtil .ofNullable(user.getX121Address()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrX121Address, s)) .orElse(() -> getAttributeValueList(result, attrX121Address).stream().forEach( v -> modifyDeleteEntry(modifyList, attrX121Address, v))); final String attrBusinessCategory = fessConfig.getLdapAttrBusinessCategory(); OptionalUtil .ofNullable(user.getBusinessCategory()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrBusinessCategory, s)) .orElse(() -> getAttributeValueList(result, attrBusinessCategory).stream().forEach( v -> modifyDeleteEntry(modifyList, attrBusinessCategory, v))); final String attrRegisteredAddress = fessConfig.getLdapAttrRegisteredAddress(); OptionalUtil .ofNullable(user.getRegisteredAddress()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrRegisteredAddress, s)) .orElse(() -> getAttributeValueList(result, attrRegisteredAddress).stream().forEach( v -> modifyDeleteEntry(modifyList, attrRegisteredAddress, v))); final String attrDisplayName = fessConfig.getLdapAttrDisplayName(); OptionalUtil .ofNullable(user.getDisplayName()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrDisplayName, s)) .orElse(() -> getAttributeValueList(result, attrDisplayName).stream().forEach( v -> modifyDeleteEntry(modifyList, attrDisplayName, v))); final String attrPreferredLanguage = fessConfig.getLdapAttrPreferredLanguage(); OptionalUtil .ofNullable(user.getPreferredLanguage()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrPreferredLanguage, s)) .orElse(() -> getAttributeValueList(result, attrPreferredLanguage).stream().forEach( v -> modifyDeleteEntry(modifyList, attrPreferredLanguage, v))); final String attrDepartmentNumber = fessConfig.getLdapAttrDepartmentNumber(); OptionalUtil .ofNullable(user.getDepartmentNumber()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrDepartmentNumber, s)) .orElse(() -> getAttributeValueList(result, attrDepartmentNumber).stream().forEach( v -> modifyDeleteEntry(modifyList, attrDepartmentNumber, v))); final String attrUidNumber = fessConfig.getLdapAttrUidNumber(); OptionalUtil .ofNullable(user.getUidNumber()) .filter(s -> StringUtil.isNotBlank(s.toString())) .ifPresent(s -> modifyReplaceEntry(modifyList, attrUidNumber, s.toString())) .orElse(() -> getAttributeValueList(result, attrUidNumber).stream().forEach( v -> modifyDeleteEntry(modifyList, attrUidNumber, v))); final String attrGidNumber = fessConfig.getLdapAttrGidNumber(); OptionalUtil .ofNullable(user.getGidNumber()) .filter(s -> StringUtil.isNotBlank(s.toString())) .ifPresent(s -> modifyReplaceEntry(modifyList, attrGidNumber, s.toString())) .orElse(() -> getAttributeValueList(result, attrGidNumber).stream().forEach( v -> modifyDeleteEntry(modifyList, attrGidNumber, v))); final String attrHomeDirectory = fessConfig.getLdapAttrHomeDirectory(); OptionalUtil .ofNullable(user.getHomeDirectory()) .filter(StringUtil::isNotBlank) .ifPresent(s -> modifyReplaceEntry(modifyList, attrHomeDirectory, s)) .orElse(() -> getAttributeValueList(result, attrHomeDirectory).stream().forEach( v -> modifyDeleteEntry(modifyList, attrHomeDirectory, v))); modify(userDN, modifyList, adminEnv); } protected void addUserAttributes(final BasicAttributes entry, final User user, final FessConfig fessConfig) { entry.put(new BasicAttribute("cn", user.getName())); entry.put(new BasicAttribute("userPassword", user.getOriginalPassword())); OptionalUtil.ofNullable(user.getSurname()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrSurname(), s))); OptionalUtil.ofNullable(user.getGivenName()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrGivenName(), s))); OptionalUtil.ofNullable(user.getMail()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrMail(), s))); OptionalUtil.ofNullable(user.getEmployeeNumber()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrEmployeeNumber(), s))); OptionalUtil.ofNullable(user.getTelephoneNumber()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrTelephoneNumber(), s))); OptionalUtil.ofNullable(user.getHomePhone()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrHomePhone(), s))); OptionalUtil.ofNullable(user.getHomePostalAddress()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrHomePostalAddress(), s))); OptionalUtil.ofNullable(user.getLabeledURI()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrLabeleduri(), s))); OptionalUtil.ofNullable(user.getRoomNumber()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrRoomNumber(), s))); OptionalUtil.ofNullable(user.getDescription()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrDescription(), s))); OptionalUtil.ofNullable(user.getTitle()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrTitle(), s))); OptionalUtil.ofNullable(user.getPager()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrPager(), s))); OptionalUtil.ofNullable(user.getStreet()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrStreet(), s))); OptionalUtil.ofNullable(user.getPostalCode()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrPostalCode(), s))); OptionalUtil.ofNullable(user.getPhysicalDeliveryOfficeName()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrPhysicalDeliveryOfficeName(), s))); OptionalUtil.ofNullable(user.getDestinationIndicator()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrDestinationIndicator(), s))); OptionalUtil.ofNullable(user.getInternationaliSDNNumber()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrInternationalisdnNumber(), s))); OptionalUtil.ofNullable(user.getState()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrState(), s))); OptionalUtil.ofNullable(user.getEmployeeType()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrEmployeeType(), s))); OptionalUtil.ofNullable(user.getFacsimileTelephoneNumber()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrFacsimileTelephoneNumber(), s))); OptionalUtil.ofNullable(user.getPostOfficeBox()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrPostOfficeBox(), s))); OptionalUtil.ofNullable(user.getInitials()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrInitials(), s))); OptionalUtil.ofNullable(user.getCarLicense()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrCarLicense(), s))); OptionalUtil.ofNullable(user.getMobile()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrMobile(), s))); OptionalUtil.ofNullable(user.getPostalAddress()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrPostalAddress(), s))); OptionalUtil.ofNullable(user.getCity()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrCity(), s))); OptionalUtil.ofNullable(user.getTeletexTerminalIdentifier()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrTeletexTerminalIdentifier(), s))); OptionalUtil.ofNullable(user.getX121Address()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrX121Address(), s))); OptionalUtil.ofNullable(user.getBusinessCategory()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrBusinessCategory(), s))); OptionalUtil.ofNullable(user.getRegisteredAddress()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrRegisteredAddress(), s))); OptionalUtil.ofNullable(user.getDisplayName()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrDisplayName(), s))); OptionalUtil.ofNullable(user.getPreferredLanguage()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrPreferredLanguage(), s))); OptionalUtil.ofNullable(user.getDepartmentNumber()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrDepartmentNumber(), s))); OptionalUtil.ofNullable(user.getUidNumber()).filter(s -> StringUtil.isNotBlank(s.toString())) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrUidNumber(), s))); OptionalUtil.ofNullable(user.getGidNumber()).filter(s -> StringUtil.isNotBlank(s.toString())) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrGidNumber(), s))); OptionalUtil.ofNullable(user.getHomeDirectory()).filter(StringUtil::isNotBlank) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrHomeDirectory(), s))); } public void delete(final User user) { final FessConfig fessConfig = ComponentUtil.getFessConfig(); if (!fessConfig.isLdapAdminEnabled(user.getName())) { return; } final Supplier<Hashtable<String, String>> adminEnv = () -> createAdminEnv(); final String userDN = fessConfig.getLdapAdminUserSecurityPrincipal(user.getName()); stream(user.getGroupNames()).of(stream -> stream.forEach(name -> { search(fessConfig.getLdapAdminGroupBaseDn(), fessConfig.getLdapAdminGroupFilter(name), null, adminEnv, subResult -> { if (!!subResult.isEmpty()) { final Group group = new Group(); group.setName(name); insert(group); } final List<ModificationItem> modifyList = new ArrayList<>(); modifyDeleteEntry(modifyList, "member", userDN); modify(fessConfig.getLdapAdminGroupSecurityPrincipal(name), modifyList, adminEnv); }); })); stream(user.getRoleNames()).of(stream -> stream.forEach(name -> { search(fessConfig.getLdapAdminRoleBaseDn(), fessConfig.getLdapAdminRoleFilter(name), null, adminEnv, subResult -> { if (!!subResult.isEmpty()) { final Role role = new Role(); role.setName(name); insert(role); } final List<ModificationItem> modifyList = new ArrayList<>(); modifyDeleteEntry(modifyList, "member", userDN); modify(fessConfig.getLdapAdminRoleSecurityPrincipal(name), modifyList, adminEnv); }); })); search(fessConfig.getLdapAdminUserBaseDn(), fessConfig.getLdapAdminUserFilter(user.getName()), null, adminEnv, result -> { if (!result.isEmpty()) { delete(userDN, adminEnv); } else { logger.info("{} does not exist in LDAP server.", user.getName()); } }); } public void insert(final Role role) { final FessConfig fessConfig = ComponentUtil.getFessConfig(); if (!fessConfig.isLdapAdminEnabled()) { return; } final Supplier<Hashtable<String, String>> adminEnv = () -> createAdminEnv(); search(fessConfig.getLdapAdminRoleBaseDn(), fessConfig.getLdapAdminRoleFilter(role.getName()), null, adminEnv, result -> { if (!result.isEmpty()) { logger.info("{} exists in LDAP server.", role.getName()); } else { final String entryDN = fessConfig.getLdapAdminRoleSecurityPrincipal(role.getName()); final BasicAttributes entry = new BasicAttributes(); addRoleAttributes(entry, role, fessConfig); final Attribute oc = fessConfig.getLdapAdminRoleObjectClassAttribute(); entry.put(oc); insert(entryDN, entry, adminEnv); } }); } protected void addRoleAttributes(final BasicAttributes entry, final Role user, final FessConfig fessConfig) { // nothing } public void delete(final Role role) { final FessConfig fessConfig = ComponentUtil.getFessConfig(); if (!fessConfig.isLdapAdminEnabled()) { return; } final Supplier<Hashtable<String, String>> adminEnv = () -> createAdminEnv(); search(fessConfig.getLdapAdminRoleBaseDn(), fessConfig.getLdapAdminRoleFilter(role.getName()), null, adminEnv, result -> { if (!result.isEmpty()) { final String entryDN = fessConfig.getLdapAdminRoleSecurityPrincipal(role.getName()); delete(entryDN, adminEnv); } else { logger.info("{} does not exist in LDAP server.", role.getName()); } }); } public void apply(final Group group) { final FessConfig fessConfig = ComponentUtil.getFessConfig(); if (!fessConfig.isLdapAdminEnabled()) { return; } final Supplier<Hashtable<String, String>> adminEnv = () -> createAdminEnv(); search(fessConfig.getLdapAdminGroupBaseDn(), fessConfig.getLdapAdminGroupFilter(group.getName()), null, adminEnv, result -> { if (!result.isEmpty()) { setAttributeValue(result, fessConfig.getLdapAttrGidNumber(), o -> group.setGidNumber(DfTypeUtil.toLong(o))); } }); } public void insert(final Group group) { final FessConfig fessConfig = ComponentUtil.getFessConfig(); if (!fessConfig.isLdapAdminEnabled()) { return; } final Supplier<Hashtable<String, String>> adminEnv = () -> createAdminEnv(); final String entryDN = fessConfig.getLdapAdminGroupSecurityPrincipal(group.getName()); search(fessConfig.getLdapAdminGroupBaseDn(), fessConfig.getLdapAdminGroupFilter(group.getName()), null, adminEnv, result -> { if (!result.isEmpty()) { logger.info("{} exists in LDAP server.", group.getName()); modifyGroupAttributes(group, adminEnv, entryDN, result, fessConfig); } else { final BasicAttributes entry = new BasicAttributes(); addGroupAttributes(entry, group, fessConfig); final Attribute oc = fessConfig.getLdapAdminGroupObjectClassAttribute(); entry.put(oc); insert(entryDN, entry, adminEnv); } }); } protected void modifyGroupAttributes(final Group group, final Supplier<Hashtable<String, String>> adminEnv, final String entryDN, final List<SearchResult> result, final FessConfig fessConfig) { final List<ModificationItem> modifyList = new ArrayList<>(); final String attrGidNumber = fessConfig.getLdapAttrGidNumber(); OptionalUtil .ofNullable(group.getGidNumber()) .filter(s -> StringUtil.isNotBlank(s.toString())) .ifPresent(s -> modifyReplaceEntry(modifyList, attrGidNumber, s.toString())) .orElse(() -> getAttributeValueList(result, attrGidNumber).stream().forEach( v -> modifyDeleteEntry(modifyList, attrGidNumber, v))); modify(entryDN, modifyList, adminEnv); } protected void addGroupAttributes(final BasicAttributes entry, final Group group, final FessConfig fessConfig) { OptionalUtil.ofNullable(group.getGidNumber()).filter(s -> StringUtil.isNotBlank(s.toString())) .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrGidNumber(), s))); } public void delete(final Group group) { final FessConfig fessConfig = ComponentUtil.getFessConfig(); if (!fessConfig.isLdapAdminEnabled()) { return; } final Supplier<Hashtable<String, String>> adminEnv = () -> createAdminEnv(); search(fessConfig.getLdapAdminGroupBaseDn(), fessConfig.getLdapAdminGroupFilter(group.getName()), null, adminEnv, result -> { if (!result.isEmpty()) { final String entryDN = fessConfig.getLdapAdminGroupSecurityPrincipal(group.getName()); delete(entryDN, adminEnv); } else { logger.info("{} does not exist in LDAP server.", group.getName()); } }); } public boolean changePassword(final String username, final String password) { final FessConfig fessConfig = ComponentUtil.getFessConfig(); if (!fessConfig.isLdapAdminEnabled(username)) { return false; } final Supplier<Hashtable<String, String>> adminEnv = () -> createAdminEnv(); final String userDN = fessConfig.getLdapAdminUserSecurityPrincipal(username); search(fessConfig.getLdapAdminUserBaseDn(), fessConfig.getLdapAdminUserFilter(username), null, adminEnv, result -> { if (!result.isEmpty()) { final List<ModificationItem> modifyList = new ArrayList<>(); modifyReplaceEntry(modifyList, "userPassword", password); modify(userDN, modifyList, adminEnv); } else { throw new LdapOperationException("User is not found: " + username); } }); return true; } protected void insert(final String entryDN, final Attributes entry, final Supplier<Hashtable<String, String>> envSupplier) { try (DirContextHolder holder = getDirContext(envSupplier)) { logger.debug("Inserting {}", entryDN); holder.get().createSubcontext(entryDN, entry); } catch (final NamingException e) { throw new LdapOperationException("Failed to add " + entryDN, e); } } protected void delete(final String entryDN, final Supplier<Hashtable<String, String>> envSupplier) { try (DirContextHolder holder = getDirContext(envSupplier)) { logger.debug("Deleting {}", entryDN); holder.get().destroySubcontext(entryDN); } catch (final NamingException e) { throw new LdapOperationException("Failed to delete " + entryDN, e); } } protected void search(final String baseDn, final String filter, final String[] returningAttrs, final Supplier<Hashtable<String, String>> envSupplier, final SearcConsumer consumer) { try (DirContextHolder holder = getDirContext(envSupplier)) { final SearchControls controls = new SearchControls(); controls.setSearchScope(SearchControls.SUBTREE_SCOPE); if (returningAttrs != null) { controls.setReturningAttributes(returningAttrs); } consumer.accept(Collections.list(holder.get().search(baseDn, filter, controls))); } catch (final NamingException e) { throw new LdapOperationException("Failed to search " + baseDn + " with " + filter, e); } } protected void modifyAddEntry(final List<ModificationItem> modifyList, final String name, final String value) { final Attribute attr = new BasicAttribute(name, value); final ModificationItem mod = new ModificationItem(DirContext.ADD_ATTRIBUTE, attr); modifyList.add(mod); } protected void modifyReplaceEntry(final List<ModificationItem> modifyList, final String name, final String value) { final Attribute attr = new BasicAttribute(name, value); final ModificationItem mod = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr); modifyList.add(mod); } protected void modifyDeleteEntry(final List<ModificationItem> modifyList, final String name, final Object value) { final Attribute attr = new BasicAttribute(name, value); final ModificationItem mod = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, attr); modifyList.add(mod); } protected void modify(final String dn, final List<ModificationItem> modifyList, final Supplier<Hashtable<String, String>> envSupplier) { if (modifyList.isEmpty()) { return; } try (DirContextHolder holder = getDirContext(envSupplier)) { holder.get().modifyAttributes(dn, modifyList.toArray(new ModificationItem[modifyList.size()])); } catch (final NamingException e) { throw new LdapOperationException("Failed to search " + dn, e); } } interface SearcConsumer { void accept(List<SearchResult> t) throws NamingException; } protected DirContextHolder getDirContext(final Supplier<Hashtable<String, String>> envSupplier) { DirContextHolder holder = contextLocal.get(); if (holder == null) { final Hashtable<String, String> env = envSupplier.get(); try { holder = new DirContextHolder(new InitialDirContext(env)); contextLocal.set(holder); return holder; } catch (final NamingException e) { throw new LdapOperationException("Failed to create DirContext.", e); } } else { holder.inc(); return holder; } } protected class DirContextHolder implements AutoCloseable { private final DirContext context; private int counter = 1; protected DirContextHolder(final DirContext context) { this.context = context; } public DirContext get() { return context; } public void inc() { counter++; } @Override public void close() { if (counter > 1) { counter--; } else { contextLocal.remove(); if (context != null) { try { context.close(); } catch (final NamingException e) { // ignored } } } } } }