LCOV - code coverage report
Current view: top level - auth/ldap - Helper.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 37 263 14.1 %
Date: 2022-11-19 15:00:39 Functions: 2 17 11.8 %

          Line data    Source code
       1             : // Copyright (C) 2009 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.auth.ldap;
      16             : 
      17             : import com.google.common.base.Throwables;
      18             : import com.google.common.cache.Cache;
      19             : import com.google.common.collect.ImmutableSet;
      20             : import com.google.common.flogger.FluentLogger;
      21             : import com.google.gerrit.common.Nullable;
      22             : import com.google.gerrit.common.data.ParameterizedString;
      23             : import com.google.gerrit.entities.AccountGroup;
      24             : import com.google.gerrit.metrics.Description;
      25             : import com.google.gerrit.metrics.Description.Units;
      26             : import com.google.gerrit.metrics.MetricMaker;
      27             : import com.google.gerrit.metrics.Timer0;
      28             : import com.google.gerrit.server.account.AccountException;
      29             : import com.google.gerrit.server.account.AuthenticationFailedException;
      30             : import com.google.gerrit.server.auth.NoSuchUserException;
      31             : import com.google.gerrit.server.config.ConfigUtil;
      32             : import com.google.gerrit.server.config.GerritServerConfig;
      33             : import com.google.gerrit.util.ssl.BlindHostnameVerifier;
      34             : import com.google.gerrit.util.ssl.BlindSSLSocketFactory;
      35             : import com.google.inject.Inject;
      36             : import com.google.inject.Singleton;
      37             : import com.google.inject.name.Named;
      38             : import java.io.IOException;
      39             : import java.security.PrivilegedActionException;
      40             : import java.security.PrivilegedExceptionAction;
      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.Properties;
      47             : import java.util.Set;
      48             : import java.util.concurrent.TimeUnit;
      49             : import javax.naming.CompositeName;
      50             : import javax.naming.Context;
      51             : import javax.naming.Name;
      52             : import javax.naming.NamingEnumeration;
      53             : import javax.naming.NamingException;
      54             : import javax.naming.PartialResultException;
      55             : import javax.naming.directory.Attribute;
      56             : import javax.naming.directory.DirContext;
      57             : import javax.naming.ldap.InitialLdapContext;
      58             : import javax.naming.ldap.LdapContext;
      59             : import javax.naming.ldap.StartTlsRequest;
      60             : import javax.naming.ldap.StartTlsResponse;
      61             : import javax.net.ssl.SSLSocketFactory;
      62             : import javax.security.auth.Subject;
      63             : import javax.security.auth.login.LoginContext;
      64             : import javax.security.auth.login.LoginException;
      65             : import org.eclipse.jgit.lib.Config;
      66             : 
      67             : @Singleton
      68             : class Helper {
      69           2 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      70             : 
      71             :   static final String LDAP_UUID = "ldap:";
      72           2 :   static final String STARTTLS_PROPERTY = Helper.class.getName() + ".startTls";
      73             : 
      74             :   private final Cache<String, ImmutableSet<String>> parentGroups;
      75             :   private final Config config;
      76             :   private final String server;
      77             :   private final String username;
      78             :   private final String password;
      79             :   private final String referral;
      80             :   private final boolean startTls;
      81             :   private final boolean supportAnonymous;
      82             :   private final boolean sslVerify;
      83             :   private final String authentication;
      84             :   private volatile LdapSchema ldapSchema;
      85             :   private final String readTimeoutMillis;
      86             :   private final String connectTimeoutMillis;
      87             :   private final boolean useConnectionPooling;
      88             :   private final boolean groupsVisibleToAll;
      89             :   private final Timer0 loginLatencyTimer;
      90             :   private final Timer0 userSearchLatencyTimer;
      91             :   private final Timer0 groupSearchLatencyTimer;
      92             :   private final Timer0 groupExpansionLatencyTimer;
      93             : 
      94             :   @Inject
      95             :   Helper(
      96             :       @GerritServerConfig Config config,
      97             :       @Named(LdapModule.PARENT_GROUPS_CACHE) Cache<String, ImmutableSet<String>> parentGroups,
      98           2 :       MetricMaker metricMaker) {
      99           2 :     this.config = config;
     100           2 :     this.server = LdapRealm.optional(config, "server");
     101           2 :     this.username = LdapRealm.optional(config, "username");
     102           2 :     this.password = LdapRealm.optional(config, "password", "");
     103           2 :     this.referral = LdapRealm.optional(config, "referral", "ignore");
     104           2 :     this.startTls = config.getBoolean("ldap", "startTls", false);
     105           2 :     this.supportAnonymous = config.getBoolean("ldap", "supportAnonymous", true);
     106           2 :     this.sslVerify = config.getBoolean("ldap", "sslverify", true);
     107           2 :     this.groupsVisibleToAll = config.getBoolean("ldap", "groupsVisibleToAll", false);
     108           2 :     this.authentication = LdapRealm.optional(config, "authentication", "simple");
     109           2 :     String readTimeout = LdapRealm.optional(config, "readTimeout");
     110           2 :     if (readTimeout != null) {
     111           0 :       readTimeoutMillis =
     112           0 :           Long.toString(ConfigUtil.getTimeUnit(readTimeout, 0, TimeUnit.MILLISECONDS));
     113             :     } else {
     114           2 :       readTimeoutMillis = null;
     115             :     }
     116           2 :     String connectTimeout = LdapRealm.optional(config, "connectTimeout");
     117           2 :     if (connectTimeout != null) {
     118           0 :       connectTimeoutMillis =
     119           0 :           Long.toString(ConfigUtil.getTimeUnit(connectTimeout, 0, TimeUnit.MILLISECONDS));
     120             :     } else {
     121           2 :       connectTimeoutMillis = null;
     122             :     }
     123           2 :     this.parentGroups = parentGroups;
     124           2 :     this.useConnectionPooling = LdapRealm.optional(config, "useConnectionPooling", false);
     125             : 
     126           2 :     this.loginLatencyTimer =
     127           2 :         metricMaker.newTimer(
     128             :             "ldap/login_latency",
     129           2 :             new Description("Latency of logins").setCumulative().setUnit(Units.NANOSECONDS));
     130           2 :     this.userSearchLatencyTimer =
     131           2 :         metricMaker.newTimer(
     132             :             "ldap/user_search_latency",
     133             :             new Description("Latency for searching the user account")
     134           2 :                 .setCumulative()
     135           2 :                 .setUnit(Units.NANOSECONDS));
     136           2 :     this.groupSearchLatencyTimer =
     137           2 :         metricMaker.newTimer(
     138             :             "ldap/group_search_latency",
     139             :             new Description("Latency for querying the groups membership of an account")
     140           2 :                 .setCumulative()
     141           2 :                 .setUnit(Units.NANOSECONDS));
     142           2 :     this.groupExpansionLatencyTimer =
     143           2 :         metricMaker.newTimer(
     144             :             "ldap/group_expansion_latency",
     145             :             new Description("Latency for expanding nested groups")
     146           2 :                 .setCumulative()
     147           2 :                 .setUnit(Units.NANOSECONDS));
     148           2 :   }
     149             : 
     150             :   Timer0 getGroupSearchLatencyTimer() {
     151           0 :     return groupSearchLatencyTimer;
     152             :   }
     153             : 
     154             :   private Properties createContextProperties() {
     155           0 :     final Properties env = new Properties();
     156           0 :     env.put(Context.INITIAL_CONTEXT_FACTORY, LdapRealm.LDAP);
     157           0 :     env.put(Context.PROVIDER_URL, server);
     158           0 :     if (server.startsWith("ldaps:") && !sslVerify) {
     159           0 :       Class<? extends SSLSocketFactory> factory = BlindSSLSocketFactory.class;
     160           0 :       env.put("java.naming.ldap.factory.socket", factory.getName());
     161             :     }
     162           0 :     if (readTimeoutMillis != null) {
     163           0 :       env.put("com.sun.jndi.ldap.read.timeout", readTimeoutMillis);
     164             :     }
     165           0 :     if (connectTimeoutMillis != null) {
     166           0 :       env.put("com.sun.jndi.ldap.connect.timeout", connectTimeoutMillis);
     167             :     }
     168           0 :     if (useConnectionPooling) {
     169           0 :       env.put("com.sun.jndi.ldap.connect.pool", "true");
     170             :     }
     171           0 :     return env;
     172             :   }
     173             : 
     174             :   private LdapContext createContext(Properties env) throws IOException, NamingException {
     175           0 :     LdapContext ctx = new InitialLdapContext(env, null);
     176           0 :     if (startTls) {
     177           0 :       StartTlsResponse tls = (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());
     178           0 :       SSLSocketFactory sslfactory = null;
     179           0 :       if (!sslVerify) {
     180           0 :         sslfactory = (SSLSocketFactory) BlindSSLSocketFactory.getDefault();
     181           0 :         tls.setHostnameVerifier(BlindHostnameVerifier.getInstance());
     182             :       }
     183           0 :       tls.negotiate(sslfactory);
     184           0 :       ctx.addToEnvironment(STARTTLS_PROPERTY, tls);
     185             :     }
     186           0 :     return ctx;
     187             :   }
     188             : 
     189             :   void close(DirContext ctx) {
     190             :     try {
     191           0 :       StartTlsResponse tls = (StartTlsResponse) ctx.removeFromEnvironment(STARTTLS_PROPERTY);
     192           0 :       if (tls != null) {
     193           0 :         tls.close();
     194             :       }
     195           0 :     } catch (IOException | NamingException e) {
     196           0 :       logger.atWarning().withCause(e).log("Cannot close LDAP startTls handle");
     197           0 :     }
     198             :     try {
     199           0 :       ctx.close();
     200           0 :     } catch (NamingException e) {
     201           0 :       logger.atWarning().withCause(e).log("Cannot close LDAP handle");
     202           0 :     }
     203           0 :   }
     204             : 
     205             :   DirContext open() throws IOException, NamingException, LoginException {
     206           0 :     final Properties env = createContextProperties();
     207           0 :     env.put(Context.SECURITY_AUTHENTICATION, authentication);
     208           0 :     env.put(Context.REFERRAL, referral);
     209           0 :     if ("GSSAPI".equals(authentication)) {
     210           0 :       return kerberosOpen(env);
     211             :     }
     212             : 
     213           0 :     if (!supportAnonymous && username != null) {
     214           0 :       env.put(Context.SECURITY_PRINCIPAL, username);
     215           0 :       env.put(Context.SECURITY_CREDENTIALS, password);
     216             :     }
     217             : 
     218           0 :     LdapContext ctx = createContext(env);
     219             : 
     220           0 :     if (supportAnonymous && username != null) {
     221           0 :       ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, username);
     222           0 :       ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password);
     223           0 :       ctx.reconnect(null);
     224             :     }
     225           0 :     return ctx;
     226             :   }
     227             : 
     228             :   @Nullable
     229             :   private DirContext kerberosOpen(Properties env)
     230             :       throws IOException, LoginException, NamingException {
     231           0 :     LoginContext ctx = new LoginContext("KerberosLogin");
     232           0 :     try (Timer0.Context ignored = loginLatencyTimer.start()) {
     233           0 :       ctx.login();
     234             :     }
     235           0 :     Subject subject = ctx.getSubject();
     236             :     try {
     237           0 :       return Subject.doAs(
     238           0 :           subject, (PrivilegedExceptionAction<DirContext>) () -> createContext(env));
     239           0 :     } catch (PrivilegedActionException e) {
     240           0 :       Throwables.throwIfInstanceOf(e.getException(), IOException.class);
     241           0 :       Throwables.throwIfInstanceOf(e.getException(), NamingException.class);
     242           0 :       Throwables.throwIfInstanceOf(e.getException(), RuntimeException.class);
     243           0 :       logger.atWarning().withCause(e.getException()).log("Internal error");
     244           0 :       return null;
     245             :     } finally {
     246           0 :       ctx.logout();
     247             :     }
     248             :   }
     249             : 
     250             :   DirContext authenticate(String dn, String password) throws AccountException {
     251           0 :     final Properties env = createContextProperties();
     252           0 :     try (Timer0.Context ignored = loginLatencyTimer.start()) {
     253           0 :       env.put(Context.REFERRAL, referral);
     254             : 
     255           0 :       if (!supportAnonymous) {
     256           0 :         env.put(Context.SECURITY_AUTHENTICATION, "simple");
     257           0 :         env.put(Context.SECURITY_PRINCIPAL, dn);
     258           0 :         env.put(Context.SECURITY_CREDENTIALS, password);
     259             :       }
     260             : 
     261           0 :       LdapContext ctx = createContext(env);
     262             : 
     263           0 :       if (supportAnonymous) {
     264           0 :         ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
     265           0 :         ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, dn);
     266           0 :         ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password);
     267           0 :         ctx.reconnect(null);
     268             :       }
     269             : 
     270           0 :       return ctx;
     271           0 :     } catch (IOException | NamingException e) {
     272           0 :       throw new AuthenticationFailedException("Incorrect username or password", e);
     273             :     }
     274             :   }
     275             : 
     276             :   LdapSchema getSchema(DirContext ctx) {
     277           0 :     if (ldapSchema == null) {
     278           0 :       synchronized (this) {
     279           0 :         if (ldapSchema == null) {
     280           0 :           ldapSchema = new LdapSchema(ctx);
     281             :         }
     282           0 :       }
     283             :     }
     284           0 :     return ldapSchema;
     285             :   }
     286             : 
     287             :   LdapQuery.Result findAccount(
     288             :       Helper.LdapSchema schema, DirContext ctx, String username, boolean fetchMemberOf)
     289             :       throws NamingException, AccountException {
     290           0 :     final HashMap<String, String> params = new HashMap<>();
     291           0 :     params.put(LdapRealm.USERNAME, username);
     292             : 
     293             :     List<LdapQuery> accountQueryList;
     294           0 :     if (fetchMemberOf && schema.type.accountMemberField() != null) {
     295           0 :       accountQueryList = schema.accountWithMemberOfQueryList;
     296             :     } else {
     297           0 :       accountQueryList = schema.accountQueryList;
     298             :     }
     299             : 
     300           0 :     for (LdapQuery accountQuery : accountQueryList) {
     301           0 :       List<LdapQuery.Result> res = accountQuery.query(ctx, params, userSearchLatencyTimer);
     302           0 :       if (res.size() == 1) {
     303           0 :         return res.get(0);
     304           0 :       } else if (res.size() > 1) {
     305           0 :         throw new AccountException("Duplicate users: " + username);
     306             :       }
     307           0 :     }
     308           0 :     throw new NoSuchUserException(username);
     309             :   }
     310             : 
     311             :   Set<AccountGroup.UUID> queryForGroups(
     312             :       final DirContext ctx, String username, LdapQuery.Result account) throws NamingException {
     313           0 :     final LdapSchema schema = getSchema(ctx);
     314           0 :     final Set<String> groupDNs = new HashSet<>();
     315             : 
     316           0 :     if (!schema.groupMemberQueryList.isEmpty()) {
     317           0 :       final HashMap<String, String> params = new HashMap<>();
     318             : 
     319           0 :       if (account == null) {
     320             :         try {
     321           0 :           account = findAccount(schema, ctx, username, false);
     322           0 :         } catch (AccountException e) {
     323           0 :           return Collections.emptySet();
     324           0 :         }
     325             :       }
     326           0 :       for (String name : schema.groupMemberQueryList.get(0).getParameters()) {
     327           0 :         params.put(name, account.get(name));
     328           0 :       }
     329             : 
     330           0 :       params.put(LdapRealm.USERNAME, username);
     331             : 
     332           0 :       for (LdapQuery groupMemberQuery : schema.groupMemberQueryList) {
     333           0 :         for (LdapQuery.Result r : groupMemberQuery.query(ctx, params, groupSearchLatencyTimer)) {
     334           0 :           try (Timer0.Context ignored = groupExpansionLatencyTimer.start()) {
     335           0 :             recursivelyExpandGroups(groupDNs, schema, ctx, r.getDN());
     336             :           }
     337           0 :         }
     338           0 :       }
     339             :     }
     340             : 
     341           0 :     if (schema.accountMemberField != null) {
     342           0 :       if (account == null || account.getAll(schema.accountMemberField) == null) {
     343             :         try {
     344           0 :           account = findAccount(schema, ctx, username, true);
     345           0 :         } catch (AccountException e) {
     346           0 :           return Collections.emptySet();
     347           0 :         }
     348             :       }
     349             : 
     350           0 :       final Attribute groupAtt = account.getAll(schema.accountMemberField);
     351           0 :       if (groupAtt != null) {
     352           0 :         final NamingEnumeration<?> groups = groupAtt.getAll();
     353             :         try {
     354           0 :           while (groups.hasMore()) {
     355           0 :             final String nextDN = (String) groups.next();
     356           0 :             recursivelyExpandGroups(groupDNs, schema, ctx, nextDN);
     357           0 :           }
     358           0 :         } catch (PartialResultException e) {
     359             :           // Ignored
     360           0 :         }
     361             :       }
     362             :     }
     363             : 
     364           0 :     final Set<AccountGroup.UUID> actual = new HashSet<>();
     365           0 :     for (String dn : groupDNs) {
     366           0 :       actual.add(AccountGroup.uuid(LDAP_UUID + dn));
     367           0 :     }
     368             : 
     369           0 :     if (actual.isEmpty()) {
     370           0 :       return Collections.emptySet();
     371             :     }
     372           0 :     return ImmutableSet.copyOf(actual);
     373             :   }
     374             : 
     375             :   private void recursivelyExpandGroups(
     376             :       final Set<String> groupDNs,
     377             :       final LdapSchema schema,
     378             :       final DirContext ctx,
     379             :       final String groupDN) {
     380           0 :     if (groupDNs.add(groupDN)
     381             :         && schema.accountMemberField != null
     382             :         && schema.accountMemberExpandGroups) {
     383           0 :       ImmutableSet<String> cachedParentsDNs = parentGroups.getIfPresent(groupDN);
     384           0 :       if (cachedParentsDNs == null) {
     385             :         // Recursively identify the groups it is a member of.
     386           0 :         ImmutableSet.Builder<String> dns = ImmutableSet.builder();
     387             :         try {
     388           0 :           final Name compositeGroupName = new CompositeName().add(groupDN);
     389           0 :           final Attribute in =
     390           0 :               ctx.getAttributes(compositeGroupName, schema.accountMemberFieldArray)
     391           0 :                   .get(schema.accountMemberField);
     392           0 :           if (in != null) {
     393           0 :             final NamingEnumeration<?> groups = in.getAll();
     394             :             try {
     395           0 :               while (groups.hasMore()) {
     396           0 :                 dns.add((String) groups.next());
     397             :               }
     398           0 :             } catch (PartialResultException e) {
     399             :               // Ignored
     400           0 :             }
     401             :           }
     402           0 :         } catch (NamingException e) {
     403           0 :           logger.atWarning().withCause(e).log("Could not find group %s", groupDN);
     404           0 :         }
     405           0 :         cachedParentsDNs = dns.build();
     406           0 :         parentGroups.put(groupDN, cachedParentsDNs);
     407             :       }
     408           0 :       for (String dn : cachedParentsDNs) {
     409           0 :         recursivelyExpandGroups(groupDNs, schema, ctx, dn);
     410           0 :       }
     411             :     }
     412           0 :   }
     413             : 
     414             :   public boolean groupsVisibleToAll() {
     415           0 :     return this.groupsVisibleToAll;
     416             :   }
     417             : 
     418             :   class LdapSchema {
     419             :     final LdapType type;
     420             : 
     421             :     final ParameterizedString accountFullName;
     422             :     final ParameterizedString accountEmailAddress;
     423             :     final ParameterizedString accountSshUserName;
     424             :     final String accountMemberField;
     425             :     final boolean accountMemberExpandGroups;
     426             :     final String[] accountMemberFieldArray;
     427             :     final List<LdapQuery> accountQueryList;
     428             :     final List<LdapQuery> accountWithMemberOfQueryList;
     429             : 
     430             :     final List<String> groupBases;
     431             :     final SearchScope groupScope;
     432             :     final ParameterizedString groupPattern;
     433             :     final ParameterizedString groupName;
     434             :     final List<LdapQuery> groupMemberQueryList;
     435             : 
     436           0 :     LdapSchema(DirContext ctx) {
     437           0 :       type = discoverLdapType(ctx);
     438           0 :       groupMemberQueryList = new ArrayList<>();
     439           0 :       accountQueryList = new ArrayList<>();
     440           0 :       accountWithMemberOfQueryList = new ArrayList<>();
     441             : 
     442           0 :       final Set<String> accountAtts = new HashSet<>();
     443             : 
     444             :       // Group query
     445             :       //
     446             : 
     447           0 :       groupBases = LdapRealm.optionalList(config, "groupBase");
     448           0 :       groupScope = LdapRealm.scope(config, "groupScope");
     449           0 :       groupPattern = LdapRealm.paramString(config, "groupPattern", type.groupPattern());
     450           0 :       groupName = LdapRealm.paramString(config, "groupName", type.groupName());
     451           0 :       final String groupMemberPattern =
     452           0 :           LdapRealm.optdef(config, "groupMemberPattern", type.groupMemberPattern());
     453             : 
     454           0 :       for (String groupBase : groupBases) {
     455           0 :         if (groupMemberPattern != null) {
     456           0 :           final LdapQuery groupMemberQuery =
     457             :               new LdapQuery(
     458             :                   groupBase,
     459             :                   groupScope,
     460             :                   new ParameterizedString(groupMemberPattern),
     461           0 :                   Collections.emptySet());
     462           0 :           if (groupMemberQuery.getParameters().isEmpty()) {
     463           0 :             throw new IllegalArgumentException("No variables in ldap.groupMemberPattern");
     464             :           }
     465             : 
     466           0 :           accountAtts.addAll(groupMemberQuery.getParameters());
     467             : 
     468           0 :           groupMemberQueryList.add(groupMemberQuery);
     469             :         }
     470           0 :       }
     471             : 
     472             :       // Account query
     473             :       //
     474           0 :       accountFullName = LdapRealm.paramString(config, "accountFullName", type.accountFullName());
     475           0 :       if (accountFullName != null) {
     476           0 :         accountAtts.addAll(accountFullName.getParameterNames());
     477             :       }
     478           0 :       accountEmailAddress =
     479           0 :           LdapRealm.paramString(config, "accountEmailAddress", type.accountEmailAddress());
     480           0 :       if (accountEmailAddress != null) {
     481           0 :         accountAtts.addAll(accountEmailAddress.getParameterNames());
     482             :       }
     483           0 :       accountSshUserName =
     484           0 :           LdapRealm.paramString(config, "accountSshUserName", type.accountSshUserName());
     485           0 :       if (accountSshUserName != null) {
     486           0 :         accountAtts.addAll(accountSshUserName.getParameterNames());
     487             :       }
     488           0 :       accountMemberField =
     489           0 :           LdapRealm.optdef(config, "accountMemberField", type.accountMemberField());
     490           0 :       if (accountMemberField != null) {
     491           0 :         accountMemberFieldArray = new String[] {accountMemberField};
     492             :       } else {
     493           0 :         accountMemberFieldArray = null;
     494             :       }
     495           0 :       accountMemberExpandGroups =
     496           0 :           LdapRealm.optional(config, "accountMemberExpandGroups", type.accountMemberExpandGroups());
     497             : 
     498           0 :       final SearchScope accountScope = LdapRealm.scope(config, "accountScope");
     499           0 :       final String accountPattern =
     500           0 :           LdapRealm.reqdef(config, "accountPattern", type.accountPattern());
     501             : 
     502             :       Set<String> accountWithMemberOfAtts;
     503           0 :       if (accountMemberField != null) {
     504           0 :         accountWithMemberOfAtts = new HashSet<>(accountAtts);
     505           0 :         accountWithMemberOfAtts.add(accountMemberField);
     506             :       } else {
     507           0 :         accountWithMemberOfAtts = null;
     508             :       }
     509           0 :       for (String accountBase : LdapRealm.requiredList(config, "accountBase")) {
     510           0 :         LdapQuery accountQuery =
     511             :             new LdapQuery(
     512             :                 accountBase, accountScope, new ParameterizedString(accountPattern), accountAtts);
     513           0 :         if (accountQuery.getParameters().isEmpty()) {
     514           0 :           throw new IllegalArgumentException("No variables in ldap.accountPattern");
     515             :         }
     516           0 :         accountQueryList.add(accountQuery);
     517             : 
     518           0 :         if (accountWithMemberOfAtts != null) {
     519           0 :           LdapQuery accountWithMemberOfQuery =
     520             :               new LdapQuery(
     521             :                   accountBase,
     522             :                   accountScope,
     523             :                   new ParameterizedString(accountPattern),
     524             :                   accountWithMemberOfAtts);
     525           0 :           accountWithMemberOfQueryList.add(accountWithMemberOfQuery);
     526             :         }
     527           0 :       }
     528           0 :     }
     529             : 
     530             :     LdapType discoverLdapType(DirContext ctx) {
     531             :       try {
     532           0 :         return LdapType.guessType(ctx);
     533           0 :       } catch (NamingException e) {
     534           0 :         logger.atWarning().withCause(e).log(
     535             :             "Cannot discover type of LDAP server at %s,"
     536             :                 + " assuming the server is RFC 2307 compliant.",
     537             :             server);
     538           0 :         return LdapType.RFC_2307;
     539             :       }
     540             :     }
     541             :   }
     542             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750