Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shiro LDAP Authorization config

Could you please help me with the following situation?

Background information:

  • I'm using the Vaadin framework.
  • I'm using the Java security framework Shiro
  • I'm using ssl.
  • Authentication works.
  • Username syntax = [email protected] , [email protected]
  • memberOf field is being used as role.
  • shiro.ini

[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

  • Authorization mapping based on the memberOf field from the currentUser.

Problem

  • currentUser.hasRole("Admin") always return false.

Questions

  • Is the above shiro.ini correct?
  • How do I fix the problem?
like image 524
user3711448 Avatar asked Jun 05 '14 14:06

user3711448


1 Answers

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"
like image 141
Alessandro Da Rugna Avatar answered Oct 11 '22 00:10

Alessandro Da Rugna