You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

LdapSearch.java 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2019 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.auth.ldap;
  21. import java.util.Arrays;
  22. import javax.naming.NamingEnumeration;
  23. import javax.naming.NamingException;
  24. import javax.naming.PartialResultException;
  25. import javax.naming.directory.InitialDirContext;
  26. import javax.naming.directory.SearchControls;
  27. import javax.naming.directory.SearchResult;
  28. import org.sonar.api.utils.log.Logger;
  29. import org.sonar.api.utils.log.Loggers;
  30. /**
  31. * Fluent API for building LDAP queries.
  32. *
  33. * @author Evgeny Mandrikov
  34. */
  35. public class LdapSearch {
  36. private static final Logger LOG = Loggers.get(LdapSearch.class);
  37. private final LdapContextFactory contextFactory;
  38. private String baseDn;
  39. private int scope = SearchControls.SUBTREE_SCOPE;
  40. private String request;
  41. private String[] parameters;
  42. private String[] returningAttributes;
  43. public LdapSearch(LdapContextFactory contextFactory) {
  44. this.contextFactory = contextFactory;
  45. }
  46. /**
  47. * Sets BaseDN.
  48. */
  49. public LdapSearch setBaseDn(String baseDn) {
  50. this.baseDn = baseDn;
  51. return this;
  52. }
  53. public String getBaseDn() {
  54. return baseDn;
  55. }
  56. /**
  57. * Sets the search scope.
  58. *
  59. * @see SearchControls#ONELEVEL_SCOPE
  60. * @see SearchControls#SUBTREE_SCOPE
  61. * @see SearchControls#OBJECT_SCOPE
  62. */
  63. public LdapSearch setScope(int scope) {
  64. this.scope = scope;
  65. return this;
  66. }
  67. public int getScope() {
  68. return scope;
  69. }
  70. /**
  71. * Sets request.
  72. */
  73. public LdapSearch setRequest(String request) {
  74. this.request = request;
  75. return this;
  76. }
  77. public String getRequest() {
  78. return request;
  79. }
  80. /**
  81. * Sets search parameters.
  82. */
  83. public LdapSearch setParameters(String... parameters) {
  84. this.parameters = parameters;
  85. return this;
  86. }
  87. public String[] getParameters() {
  88. return parameters;
  89. }
  90. /**
  91. * Sets attributes, which should be returned by search.
  92. */
  93. public LdapSearch returns(String... attributes) {
  94. this.returningAttributes = attributes;
  95. return this;
  96. }
  97. public String[] getReturningAttributes() {
  98. return returningAttributes;
  99. }
  100. /**
  101. * @throws NamingException if unable to perform search
  102. */
  103. public NamingEnumeration<SearchResult> find() throws NamingException {
  104. LOG.debug("Search: {}", this);
  105. NamingEnumeration<SearchResult> result;
  106. InitialDirContext context = null;
  107. boolean threw = false;
  108. try {
  109. context = contextFactory.createBindContext();
  110. SearchControls controls = new SearchControls();
  111. controls.setSearchScope(scope);
  112. controls.setReturningAttributes(returningAttributes);
  113. result = context.search(baseDn, request, parameters, controls);
  114. threw = true;
  115. } finally {
  116. ContextHelper.close(context, threw);
  117. }
  118. return result;
  119. }
  120. /**
  121. * @return result, or null if not found
  122. * @throws NamingException if unable to perform search, or non unique result
  123. */
  124. public SearchResult findUnique() throws NamingException {
  125. NamingEnumeration<SearchResult> result = find();
  126. if (hasMore(result)) {
  127. SearchResult obj = result.next();
  128. if (!hasMore(result)) {
  129. return obj;
  130. }
  131. throw new NamingException("Non unique result for " + toString());
  132. }
  133. return null;
  134. }
  135. private static boolean hasMore(NamingEnumeration<SearchResult> result) throws NamingException {
  136. try {
  137. return result.hasMore();
  138. } catch (PartialResultException e) {
  139. LOG.debug("More result might be forthcoming if the referral is followed", e);
  140. // See LDAP-62 and http://docs.oracle.com/javase/jndi/tutorial/ldap/referral/jndi.html :
  141. // When the LDAP service provider receives a referral despite your having set Context.REFERRAL to "ignore", it will throw a
  142. // PartialResultException(in the API reference documentation) to indicate that more results might be forthcoming if the referral is
  143. // followed. In this case, the server does not support the Manage Referral control and is supporting referral updates in some other
  144. // way.
  145. return false;
  146. }
  147. }
  148. @Override
  149. public String toString() {
  150. return getClass().getSimpleName() + "{" +
  151. "baseDn=" + baseDn +
  152. ", scope=" + scopeToString() +
  153. ", request=" + request +
  154. ", parameters=" + Arrays.toString(parameters) +
  155. ", attributes=" + Arrays.toString(returningAttributes) +
  156. "}";
  157. }
  158. private String scopeToString() {
  159. switch (scope) {
  160. case SearchControls.ONELEVEL_SCOPE:
  161. return "onelevel";
  162. case SearchControls.OBJECT_SCOPE:
  163. return "object";
  164. case SearchControls.SUBTREE_SCOPE:
  165. default:
  166. return "subtree";
  167. }
  168. }
  169. }