user3711448
user3711448

Reputation: 81

Shiro LDAP Authorization config

Could you please help me with the following situation?

Background information:

[main]

contextFactory = org.apache.shiro.realm.ldap.JndiLdapContextFactory 
contextFactory.url = ldaps://<SERVER>:636 
contextFactory.systemUsername = <USERNAME>@<COMPANY>
contextFactory.systemPassword = <PASSWORD>
contextFactory.environment[java.naming.security.protocol] = ssl 

realm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm 
realm.ldapContextFactory = $contextFactory 
realm.searchBase = "OU=<APPDIR>,DC=<COMPANY>,DC=lcl" 
realm.groupRolesMap = "CN=<ROLE>,OU=<APPDIR>,DC=<COMPANY>,DC=lcl":"Admin"

[roles]

 # 'Admin' role has permissions * 
 Admin = * 

Goal

Problem

Questions

Upvotes: 8

Views: 2933

Answers (1)

Alessandro Da Rugna
Alessandro Da Rugna

Reputation: 4695

I ran into a similar issue using Shiro 1.2.4. Your Shiro configuration is probably OK and the problem lies in ActiveDirectory configuration.

In my setup some users had the userPrincipalName attribute set, while other users hadn't. You can check your in AD server with Sysinternals Active Directory Explorer for example.
This attribute is the one used by Shiro to search for a particular user, then it looks for groups defined in the memberOf attribute.
Take a look at ActiveDirectoryRealm.java source code, method Set<String> getRoleNamesForUser(String username, LdapContext ldapContext) the exact query used is
String searchFilter = "(&(objectClass=*)(userPrincipalName={0}))";

So you have two solutions:

  • Set userPrincipalName attribute on every user
  • Change how Shiro searches for users

I went for the second solution. Changing the search query is harder than it should be: you have to customize queryForAuthorizationInfo and getRoleNamesForUser (because its private) methods of ActiveDirectoryRealm class. This is how I did it:

public class CustomActiveDirectoryRealm extends ActiveDirectoryRealm {

    @Override
    protected AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principals, LdapContextFactory ldapContextFactory) throws NamingException {
        String username = (String) getAvailablePrincipal(principals);

        // Perform context search
        LdapContext ldapContext = ldapContextFactory.getSystemLdapContext();

        Set<String> roleNames = null;

        try {
            roleNames = getRoleNamesForUser(username, ldapContext);

        } finally {
            LdapUtils.closeContext(ldapContext);
        }

        return buildAuthorizationInfo(roleNames);
    }

    // Customize your search query here
    private static final String USER_SEARCH_FILTER = "(&(objectClass=*)(sn={0}))";
    private Set<String> getRoleNamesForUser(String username, LdapContext ldapContext) throws NamingException {
        Set<String> roleNames;
        roleNames = new LinkedHashSet<String>();

        SearchControls searchCtls = new SearchControls();
        searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);

        String userPrincipalName = username.replace("acegas\\", "");
        if (principalSuffix != null) {
            userPrincipalName += principalSuffix;
        }

        Object[] searchArguments = new Object[]{userPrincipalName};

        NamingEnumeration answer = ldapContext.search(searchBase, USER_SEARCH_FILTER, searchArguments, searchCtls);

        while (answer.hasMoreElements()) {
            SearchResult sr = (SearchResult) answer.next();

            Attributes attrs = sr.getAttributes();

            if (attrs != null) {
                NamingEnumeration ae = attrs.getAll();
                while (ae.hasMore()) {
                    Attribute attr = (Attribute) ae.next();

                    if (attr.getID().equals("memberOf")) {

                        Collection<String> groupNames = LdapUtils.getAllAttributeValues(attr);

                        Collection<String> rolesForGroups = getRoleNamesForGroups(groupNames);
                        roleNames.addAll(rolesForGroups);
                    }
                }
            }
        }
        return roleNames;
    }
}

And then of course use this class as Realm in shiro.ini

[main]
realm = your.package.CustomActiveDirectoryRealm
realm.ldapContextFactory = $contextFactory 
realm.searchBase = "OU=<APPDIR>,DC=<COMPANY>,DC=lcl" 
realm.groupRolesMap = "CN=<ROLE>,OU=<APPDIR>,DC=<COMPANY>,DC=lcl":"Admin"

Upvotes: 1

Related Questions