protected Set<String> rolesFor(PrincipalCollection principals, String userNameIn,
          final LdapContext ldapCtx, final LdapContextFactory ldapContextFactory, Session session)
          throws NamingException {
    final Set<String> roleNames = new HashSet<>();
    final Set<String> groupNames = new HashSet<>();
    final String userName;
    if (getUserLowerCase()) {
      LOGGER.debug("userLowerCase true");
      userName = userNameIn.toLowerCase();
    } else {
      userName = userNameIn;
    }

    String userDn = getUserDnForSearch(userName);

    // Activate paged results
    int pageSize = getPagingSize();
    LOGGER.debug("Ldap PagingSize: {}", pageSize);
    int numResults = 0;
    try {
      ldapCtx.addToEnvironment(Context.REFERRAL, "ignore");

      ldapCtx.setRequestControls(new Control[]{new PagedResultsControl(pageSize,
            Control.NONCRITICAL)});

      // ldapsearch -h localhost -p 33389 -D
      // uid=guest,ou=people,dc=hadoop,dc=apache,dc=org -w guest-password
      // -b dc=hadoop,dc=apache,dc=org -s sub '(objectclass=*)'
      NamingEnumeration<SearchResult> searchResultEnum = null;
      SearchControls searchControls = getGroupSearchControls();
      try {
        if (groupSearchEnableMatchingRuleInChain) {
          searchResultEnum = ldapCtx.search(
              getGroupSearchBase(),
              String.format(
                  MATCHING_RULE_IN_CHAIN_FORMAT, groupObjectClass, memberAttribute, userDn),
              searchControls);
          while (searchResultEnum != null && searchResultEnum.hasMore()) {
            // searchResults contains all the groups in search scope
            numResults++;
            final SearchResult group = searchResultEnum.next();

            Attribute attribute = group.getAttributes().get(getGroupIdAttribute());
            String groupName = attribute.get().toString();

            String roleName = roleNameFor(groupName);
            if (roleName != null) {
              roleNames.add(roleName);
            } else {
              roleNames.add(groupName);
            }
          }
        } else {
          // Default group search filter
          String searchFilter = String.format("(objectclass=%1$s)", groupObjectClass);

          // If group search filter is defined in Shiro config, then use it
          if (groupSearchFilter != null) {
            searchFilter = expandTemplate(groupSearchFilter, userName);
            //searchFilter = String.format("%1$s", groupSearchFilter);
          }
          LOGGER.debug("Group SearchBase|SearchFilter|GroupSearchScope: " + "{}|{}|{}",
              getGroupSearchBase(), searchFilter, groupSearchScope);
          searchResultEnum = ldapCtx.search(
              getGroupSearchBase(),
              searchFilter,
              searchControls);
          while (searchResultEnum != null && searchResultEnum.hasMore()) {
            // searchResults contains all the groups in search scope
            numResults++;
            final SearchResult group = searchResultEnum.next();
            addRoleIfMember(userDn, group, roleNames, groupNames, ldapContextFactory);
          }
        }
      } catch (PartialResultException e) {
        LOGGER.debug("Ignoring PartitalResultException");
      } finally {
        if (searchResultEnum != null) {
          searchResultEnum.close();
        }
      }
      // Re-activate paged results
      ldapCtx.setRequestControls(new Control[]{new PagedResultsControl(pageSize,
              null, Control.CRITICAL)});
    } catch (SizeLimitExceededException e) {
      LOGGER.info("Only retrieved first {} groups due to SizeLimitExceededException.", numResults);
    } catch (IOException e) {
      LOGGER.error("Unabled to setup paged results");
    }
    // save role names and group names in session so that they can be
    // easily looked up outside of this object
    session.setAttribute(SUBJECT_USER_ROLES, roleNames);
    session.setAttribute(SUBJECT_USER_GROUPS, groupNames);
    if (!groupNames.isEmpty() && (principals instanceof MutablePrincipalCollection)) {
      ((MutablePrincipalCollection) principals).addAll(groupNames, getName());
    }
    LOGGER.debug("User RoleNames: {}::{}", userName, roleNames);
    return roleNames;
  }
@Test
  void testRolesFor() throws NamingException {
    LdapRealm realm = new LdapRealm();
    realm.setGroupSearchBase("cn=groups,dc=apache");
    realm.setGroupObjectClass("posixGroup");
    realm.setMemberAttributeValueTemplate("cn={0},ou=people,dc=apache");
    HashMap<String, String> rolesByGroups = new HashMap<>();
    rolesByGroups.put("group-three", "zeppelin-role");
    realm.setRolesByGroup(rolesByGroups);

    LdapContextFactory ldapContextFactory = mock(LdapContextFactory.class);
    LdapContext ldapCtx = mock(LdapContext.class);
    Session session = mock(Session.class);

    // expected search results
    BasicAttributes group1 = new BasicAttributes();
    group1.put(realm.getGroupIdAttribute(), "group-one");
    group1.put(realm.getMemberAttribute(), "principal");

    // user doesn't belong to this group
    BasicAttributes group2 = new BasicAttributes();
    group2.put(realm.getGroupIdAttribute(), "group-two");
    group2.put(realm.getMemberAttribute(), "someoneelse");

    // mapped to a different Zeppelin role
    BasicAttributes group3 = new BasicAttributes();
    group3.put(realm.getGroupIdAttribute(), "group-three");
    group3.put(realm.getMemberAttribute(), "principal");

    NamingEnumeration<SearchResult> results = enumerationOf(group1, group2, group3);
    when(ldapCtx.search(any(String.class), any(String.class), any(SearchControls.class)))
            .thenReturn(results);

    Set<String> roles = realm.rolesFor(
            new SimplePrincipalCollection("principal", "ldapRealm"),
            "principal", ldapCtx, ldapContextFactory, session);

    verify(ldapCtx).search("cn=groups,dc=apache", "(objectclass=posixGroup)",
            realm.getGroupSearchControls());

    assertEquals(new HashSet<>(Arrays.asList("group-one", "zeppelin-role")), roles);
  }