LdapBrowser.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /*
  2. * Password Management Servlets (PWM)
  3. * http://www.pwm-project.org
  4. *
  5. * Copyright (c) 2006-2009 Novell, Inc.
  6. * Copyright (c) 2009-2017 The PWM Project
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  21. */
  22. package password.pwm.ldap;
  23. import com.novell.ldapchai.ChaiEntry;
  24. import com.novell.ldapchai.exception.ChaiOperationException;
  25. import com.novell.ldapchai.exception.ChaiUnavailableException;
  26. import com.novell.ldapchai.provider.ChaiProvider;
  27. import com.novell.ldapchai.provider.ChaiProviderFactory;
  28. import com.novell.ldapchai.provider.DirectoryVendor;
  29. import com.novell.ldapchai.provider.SearchScope;
  30. import com.novell.ldapchai.util.ChaiUtility;
  31. import com.novell.ldapchai.util.SearchHelper;
  32. import password.pwm.AppProperty;
  33. import password.pwm.config.Configuration;
  34. import password.pwm.config.profile.LdapProfile;
  35. import password.pwm.config.stored.StoredConfigurationImpl;
  36. import password.pwm.error.ErrorInformation;
  37. import password.pwm.error.PwmError;
  38. import password.pwm.error.PwmUnrecoverableException;
  39. import password.pwm.util.logging.PwmLogger;
  40. import java.io.Serializable;
  41. import java.util.ArrayList;
  42. import java.util.Collections;
  43. import java.util.HashMap;
  44. import java.util.HashSet;
  45. import java.util.List;
  46. import java.util.Map;
  47. import java.util.Set;
  48. import java.util.TreeMap;
  49. public class LdapBrowser {
  50. private static final PwmLogger LOGGER = PwmLogger.forClass(LdapBrowser.class);
  51. private final StoredConfigurationImpl storedConfiguration;
  52. private final ChaiProviderFactory chaiProviderFactory ;
  53. private final Map<String,ChaiProvider> providerCache = new HashMap<>();
  54. public LdapBrowser(
  55. final ChaiProviderFactory chaiProviderFactory,
  56. final StoredConfigurationImpl storedConfiguration
  57. )
  58. throws PwmUnrecoverableException
  59. {
  60. this.chaiProviderFactory = chaiProviderFactory;
  61. this.storedConfiguration = storedConfiguration;
  62. }
  63. public LdapBrowseResult doBrowse(final String profile, final String dn) throws PwmUnrecoverableException {
  64. try {
  65. return doBrowseImpl(figureLdapProfileID(profile), dn);
  66. } catch (ChaiUnavailableException | ChaiOperationException e) {
  67. throw PwmUnrecoverableException.fromChaiException(e);
  68. } catch (Exception e) {
  69. throw new PwmUnrecoverableException(new ErrorInformation(PwmError.ERROR_LDAP_DATA_ERROR,e.getMessage()));
  70. }
  71. }
  72. public void close() {
  73. for (final ChaiProvider chaiProvider : providerCache.values()) {
  74. chaiProvider.close();
  75. }
  76. providerCache.clear();
  77. }
  78. private LdapBrowseResult doBrowseImpl(final String profileID, final String dn) throws PwmUnrecoverableException, ChaiUnavailableException, ChaiOperationException {
  79. final LdapBrowseResult result = new LdapBrowseResult();
  80. {
  81. final Map<String, Boolean> childDNs = new TreeMap<>();
  82. childDNs.putAll(getChildEntries(profileID, dn));
  83. for (final Map.Entry<String, Boolean> entry : childDNs.entrySet()) {
  84. final String childDN = entry.getKey();
  85. final DNInformation dnInformation = new DNInformation();
  86. dnInformation.setDn(childDN);
  87. dnInformation.setEntryName(entryNameFromDN(childDN));
  88. if (entry.getValue()) {
  89. result.getNavigableDNlist().add(dnInformation);
  90. } else {
  91. result.getSelectableDNlist().add(dnInformation);
  92. }
  93. }
  94. result.setMaxResults(childDNs.size() >= getMaxSizeLimit());
  95. }
  96. result.setDn(dn);
  97. result.setProfileID(profileID);
  98. final Configuration configuration = new Configuration(storedConfiguration);
  99. if (configuration.getLdapProfiles().size() > 1) {
  100. result.getProfileList().addAll(configuration.getLdapProfiles().keySet());
  101. }
  102. if (adRootDNList(profileID).contains(dn)) {
  103. result.setParentDN("");
  104. } else if (dn != null && !dn.isEmpty()) {
  105. final ChaiEntry dnEntry = getChaiProvider(profileID).getEntryFactory().newChaiEntry(dn);
  106. final ChaiEntry parentEntry = dnEntry.getParentEntry();
  107. if (parentEntry == null) {
  108. result.setParentDN("");
  109. } else {
  110. result.setParentDN(parentEntry.getEntryDN());
  111. }
  112. }
  113. return result;
  114. }
  115. private ChaiProvider getChaiProvider(final String profile) throws PwmUnrecoverableException {
  116. if (!providerCache.containsKey(profile)) {
  117. final Configuration configuration = new Configuration(storedConfiguration);
  118. final LdapProfile ldapProfile = LdapProfile.makeFromStoredConfiguration(storedConfiguration, profile);
  119. final ChaiProvider chaiProvider = LdapOperationsHelper.openProxyChaiProvider(chaiProviderFactory, null,ldapProfile,configuration,null);
  120. providerCache.put(profile,chaiProvider);
  121. }
  122. return providerCache.get(profile);
  123. }
  124. private String figureLdapProfileID(final String profile) {
  125. final Configuration configuration = new Configuration(storedConfiguration);
  126. if (configuration.getLdapProfiles().keySet().contains(profile)) {
  127. return profile;
  128. }
  129. return configuration.getLdapProfiles().keySet().iterator().next();
  130. }
  131. private int getMaxSizeLimit() {
  132. final Configuration configuration = new Configuration(storedConfiguration);
  133. return Integer.parseInt(configuration.readAppProperty(AppProperty.LDAP_BROWSER_MAX_ENTRIES));
  134. }
  135. private Map<String, Boolean> getChildEntries(
  136. final String profile,
  137. final String dn
  138. )
  139. throws ChaiUnavailableException, PwmUnrecoverableException, ChaiOperationException
  140. {
  141. final HashMap<String, Boolean> returnMap = new HashMap<>();
  142. final ChaiProvider chaiProvider = getChaiProvider(profile);
  143. if ((dn == null || dn.isEmpty()) && chaiProvider.getDirectoryVendor() == DirectoryVendor.ACTIVE_DIRECTORY) {
  144. final Set<String> adRootDNList = adRootDNList(profile);
  145. for (final String rootDN : adRootDNList) {
  146. returnMap.put(rootDN, true);
  147. }
  148. } else {
  149. final Map<String, Map<String, List<String>>> results;
  150. {
  151. final SearchHelper searchHelper = new SearchHelper();
  152. searchHelper.setFilter("(objectclass=*)");
  153. searchHelper.setMaxResults(getMaxSizeLimit());
  154. searchHelper.setAttributes("subordinateCount");
  155. searchHelper.setSearchScope(SearchScope.ONE);
  156. results = chaiProvider.searchMultiValues(dn, searchHelper);
  157. }
  158. for (final Map.Entry<String, Map<String, List<String>>> entry : results.entrySet()) {
  159. final String resultDN = entry.getKey();
  160. final Map<String, List<String>> attributeResults = entry.getValue();
  161. boolean hasSubs = false;
  162. if (attributeResults.containsKey("subordinateCount")) { // only eDir actually returns this operational attribute
  163. final Integer subordinateCount = Integer.parseInt(attributeResults.get("subordinateCount").iterator().next());
  164. hasSubs = subordinateCount > 0;
  165. } else {
  166. final SearchHelper searchHelper = new SearchHelper();
  167. searchHelper.setFilter("(objectclass=*)");
  168. searchHelper.setMaxResults(1);
  169. searchHelper.setAttributes(Collections.emptyList());
  170. searchHelper.setSearchScope(SearchScope.ONE);
  171. try {
  172. final Map<String, Map<String, String>> subSearchResults = chaiProvider.search(resultDN, searchHelper);
  173. hasSubs = !subSearchResults.isEmpty();
  174. } catch (Exception e) {
  175. LOGGER.debug("error during subordinate entry count of " + dn + ", error: " + e.getMessage());
  176. }
  177. }
  178. returnMap.put(resultDN, hasSubs);
  179. }
  180. }
  181. return returnMap;
  182. }
  183. private Set<String> adRootDNList(final String profile) throws ChaiUnavailableException, ChaiOperationException, PwmUnrecoverableException {
  184. final ChaiProvider chaiProvider = getChaiProvider(profile);
  185. final Set<String> adRootValues = new HashSet<>();
  186. if (chaiProvider.getDirectoryVendor() == DirectoryVendor.ACTIVE_DIRECTORY) {
  187. final ChaiEntry chaiEntry = ChaiUtility.getRootDSE(chaiProvider);
  188. adRootValues.addAll(chaiEntry.readMultiStringAttribute("namingContexts"));
  189. }
  190. return adRootValues;
  191. }
  192. private static String entryNameFromDN(final String dn) {
  193. int start = dn.indexOf("=");
  194. start = start == -1 ? 0 : start + 1;
  195. int end = dn.indexOf(",");
  196. if (end == -1) {
  197. end = dn.length();
  198. }
  199. return dn.substring(start,end);
  200. }
  201. public static class LdapBrowseResult implements Serializable {
  202. private String dn;
  203. private String profileID;
  204. private String parentDN;
  205. private List<String> profileList = new ArrayList<>();
  206. private boolean maxResults;
  207. private List<DNInformation> navigableDNlist = new ArrayList<>();
  208. private List<DNInformation> selectableDNlist = new ArrayList<>();
  209. public String getDn() {
  210. return dn;
  211. }
  212. public void setDn(final String dn) {
  213. this.dn = dn;
  214. }
  215. public String getProfileID() {
  216. return profileID;
  217. }
  218. public void setProfileID(final String profileID) {
  219. this.profileID = profileID;
  220. }
  221. public String getParentDN() {
  222. return parentDN;
  223. }
  224. public void setParentDN(final String parentDN) {
  225. this.parentDN = parentDN;
  226. }
  227. public List<String> getProfileList() {
  228. return profileList;
  229. }
  230. public boolean isMaxResults() {
  231. return maxResults;
  232. }
  233. public void setMaxResults(final boolean maxResults) {
  234. this.maxResults = maxResults;
  235. }
  236. public List<DNInformation> getNavigableDNlist() {
  237. return navigableDNlist;
  238. }
  239. public List<DNInformation> getSelectableDNlist() {
  240. return selectableDNlist;
  241. }
  242. }
  243. public static class DNInformation implements Serializable {
  244. private String entryName;
  245. private String dn;
  246. public String getEntryName() {
  247. return entryName;
  248. }
  249. public void setEntryName(final String entryName) {
  250. this.entryName = entryName;
  251. }
  252. public String getDn() {
  253. return dn;
  254. }
  255. public void setDn(final String dn) {
  256. this.dn = dn;
  257. }
  258. }
  259. }