killbill-uncached

util: add KillBillJndiLdapRealm Alternative to the shiro.ini

8/16/2013 3:14:17 PM

Details

diff --git a/server/src/main/java/com/ning/billing/server/modules/KillBillShiroWebModule.java b/server/src/main/java/com/ning/billing/server/modules/KillBillShiroWebModule.java
index c482fff..57b6c5a 100644
--- a/server/src/main/java/com/ning/billing/server/modules/KillBillShiroWebModule.java
+++ b/server/src/main/java/com/ning/billing/server/modules/KillBillShiroWebModule.java
@@ -27,7 +27,9 @@ import com.ning.billing.jaxrs.resources.JaxrsResource;
 import com.ning.billing.util.glue.EhCacheManagerProvider;
 import com.ning.billing.util.glue.IniRealmProvider;
 import com.ning.billing.util.glue.JDBCSessionDaoProvider;
+import com.ning.billing.util.glue.KillBillShiroModule;
 import com.ning.billing.util.security.shiro.dao.JDBCSessionDao;
+import com.ning.billing.util.security.shiro.realm.KillBillJndiLdapRealm;
 
 import com.google.inject.binder.AnnotatedBindingBuilder;
 
@@ -43,6 +45,11 @@ public class KillBillShiroWebModule extends ShiroWebModule {
     protected void configureShiroWeb() {
         bindRealm().toProvider(IniRealmProvider.class).asEagerSingleton();
 
+        final boolean ldapEnabled = Boolean.parseBoolean(System.getProperty(KillBillShiroModule.KILLBILL_LDAP_PROPERTY, "false"));
+        if (ldapEnabled) {
+            bindRealm().to(KillBillJndiLdapRealm.class).asEagerSingleton();
+        }
+
         // Magic provider to configure the cache manager
         bind(CacheManager.class).toProvider(EhCacheManagerProvider.class).asEagerSingleton();
 
diff --git a/util/src/main/java/com/ning/billing/util/config/SecurityConfig.java b/util/src/main/java/com/ning/billing/util/config/SecurityConfig.java
index 124299a..0af56b2 100644
--- a/util/src/main/java/com/ning/billing/util/config/SecurityConfig.java
+++ b/util/src/main/java/com/ning/billing/util/config/SecurityConfig.java
@@ -18,6 +18,7 @@ package com.ning.billing.util.config;
 
 import org.skife.config.Config;
 import org.skife.config.Default;
+import org.skife.config.DefaultNull;
 import org.skife.config.Description;
 
 public interface SecurityConfig extends KillbillConfig {
@@ -26,4 +27,58 @@ public interface SecurityConfig extends KillbillConfig {
     @Default("classpath:shiro.ini")
     @Description("Path to the shiro.ini file (classpath, url or file resource)")
     public String getShiroResourcePath();
+
+    // LDAP Realm
+
+    @Config("killbill.security.ldap.userDnTemplate")
+    @DefaultNull
+    @Description("LDAP server's User DN format (e.g. uid={0},ou=users,dc=mycompany,dc=com)")
+    public String getShiroLDAPUserDnTemplate();
+
+    @Config("killbill.security.ldap.searchBase")
+    @DefaultNull
+    @Description("LDAP search base to use")
+    public String getShiroLDAPSearchBase();
+
+    @Config("killbill.security.ldap.groupSearchFilter")
+    @Default("memberOf=uid={0}")
+    @Description("LDAP search filter to use to find groups (e.g. memberOf=uid={0},ou=users,dc=mycompany,dc=com)")
+    public String getShiroLDAPGroupSearchFilter();
+
+    @Config("killbill.security.ldap.groupNameId")
+    @Default("memberOf")
+    @Description("Group name attribute ID in LDAP")
+    public String getShiroLDAPGroupNameID();
+
+    @Config("killbill.security.ldap.permissionsByGroup")
+    @Default("admin = *:*\n" +
+             "finance = invoice:*, payment:*\n" +
+             "support = entitlement:*, invoice:item_adjust")
+    @Description("LDAP permissions by LDAP group")
+    public String getShiroLDAPPermissionsByGroup();
+
+    @Config("killbill.security.ldap.url")
+    @Default("ldap://127.0.0.1:389")
+    @Description("LDAP server url")
+    public String getShiroLDAPUrl();
+
+    @Config("killbill.security.ldap.systemUsername")
+    @DefaultNull
+    @Description("LDAP username")
+    public String getShiroLDAPSystemUsername();
+
+    @Config("killbill.security.ldap.systemPassword")
+    @DefaultNull
+    @Description("LDAP password")
+    public String getShiroLDAPSystemPassword();
+
+    @Config("killbill.security.ldap.authenticationMechanism")
+    @Default("simple")
+    @Description("LDAP authentication mechanism (e.g. DIGEST-MD5)")
+    public String getShiroLDAPAuthenticationMechanism();
+
+    @Config("killbill.security.ldap.disableSSLCheck")
+    @Default("false")
+    @Description("Whether to ignore SSL certificates checks")
+    public boolean disableShiroLDAPSSLCheck();
 }
diff --git a/util/src/main/java/com/ning/billing/util/glue/KillBillShiroModule.java b/util/src/main/java/com/ning/billing/util/glue/KillBillShiroModule.java
index f2874ce..41e0f5d 100644
--- a/util/src/main/java/com/ning/billing/util/glue/KillBillShiroModule.java
+++ b/util/src/main/java/com/ning/billing/util/glue/KillBillShiroModule.java
@@ -23,6 +23,7 @@ import org.apache.shiro.session.mgt.DefaultSessionManager;
 import org.apache.shiro.session.mgt.SessionManager;
 
 import com.ning.billing.util.security.shiro.dao.JDBCSessionDao;
+import com.ning.billing.util.security.shiro.realm.KillBillJndiLdapRealm;
 
 import com.google.inject.binder.AnnotatedBindingBuilder;
 
@@ -30,8 +31,15 @@ import com.google.inject.binder.AnnotatedBindingBuilder;
 // See com.ning.billing.server.modules.KillBillShiroWebModule for Kill Bill server.
 public class KillBillShiroModule extends ShiroModule {
 
+    public static final String KILLBILL_LDAP_PROPERTY = "killbill.server.ldap";
+
     protected void configureShiro() {
         bindRealm().toProvider(IniRealmProvider.class).asEagerSingleton();
+
+        final boolean ldapEnabled = Boolean.parseBoolean(System.getProperty(KILLBILL_LDAP_PROPERTY, "false"));
+        if (ldapEnabled) {
+            bindRealm().to(KillBillJndiLdapRealm.class).asEagerSingleton();
+        }
     }
 
     @Override
diff --git a/util/src/main/java/com/ning/billing/util/security/shiro/realm/KillBillJndiLdapRealm.java b/util/src/main/java/com/ning/billing/util/security/shiro/realm/KillBillJndiLdapRealm.java
new file mode 100644
index 0000000..a4eb644
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/security/shiro/realm/KillBillJndiLdapRealm.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.security.shiro.realm;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.config.Ini;
+import org.apache.shiro.config.Ini.Section;
+import org.apache.shiro.realm.ldap.JndiLdapContextFactory;
+import org.apache.shiro.realm.ldap.JndiLdapRealm;
+import org.apache.shiro.realm.ldap.LdapContextFactory;
+import org.apache.shiro.realm.ldap.LdapUtils;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.ning.billing.util.config.SecurityConfig;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.inject.Inject;
+
+public class KillBillJndiLdapRealm extends JndiLdapRealm {
+
+    private static final Logger log = LoggerFactory.getLogger(KillBillJndiLdapRealm.class);
+
+    private static final String USERDN_SUBSTITUTION_TOKEN = "{0}";
+
+    private static final SearchControls SUBTREE_SCOPE = new SearchControls();
+
+    static {
+        SUBTREE_SCOPE.setSearchScope(SearchControls.SUBTREE_SCOPE);
+    }
+
+    private static final Splitter SPLITTER = Splitter.on(',').omitEmptyStrings().trimResults();
+
+    private final String searchBase;
+    private final String groupSearchFilter;
+    private final String groupNameId;
+    private final Map<String, Collection<String>> permissionsByGroup = Maps.newLinkedHashMap();
+
+    @Inject
+    public KillBillJndiLdapRealm(final SecurityConfig securityConfig) {
+        super();
+
+        if (securityConfig.getShiroLDAPUserDnTemplate() != null) {
+            setUserDnTemplate(securityConfig.getShiroLDAPUserDnTemplate());
+        }
+
+        final JndiLdapContextFactory contextFactory = (JndiLdapContextFactory) getContextFactory();
+        if (securityConfig.disableShiroLDAPSSLCheck()) {
+            contextFactory.getEnvironment().put("java.naming.ldap.factory.socket", SkipSSLCheckSocketFactory.class.getName());
+        }
+        if (securityConfig.getShiroLDAPUrl() != null) {
+            contextFactory.setUrl(securityConfig.getShiroLDAPUrl());
+        }
+        if (securityConfig.getShiroLDAPSystemUsername() != null) {
+            contextFactory.setSystemUsername(securityConfig.getShiroLDAPSystemUsername());
+        }
+        if (securityConfig.getShiroLDAPSystemPassword() != null) {
+            contextFactory.setSystemPassword(securityConfig.getShiroLDAPSystemPassword());
+        }
+        if (securityConfig.getShiroLDAPAuthenticationMechanism() != null) {
+            contextFactory.setAuthenticationMechanism(securityConfig.getShiroLDAPAuthenticationMechanism());
+        }
+        setContextFactory(contextFactory);
+
+        searchBase = securityConfig.getShiroLDAPSearchBase();
+        groupSearchFilter = securityConfig.getShiroLDAPGroupSearchFilter();
+        groupNameId = securityConfig.getShiroLDAPGroupNameID();
+
+        if (securityConfig.getShiroLDAPPermissionsByGroup() != null) {
+            final Ini ini = new Ini();
+            ini.load(securityConfig.getShiroLDAPPermissionsByGroup());
+            for (final Section section : ini.getSections()) {
+                for (final String role : section.keySet()) {
+                    final Collection<String> permissions = ImmutableList.<String>copyOf(SPLITTER.split(section.get(role)));
+                    permissionsByGroup.put(role, permissions);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected AuthorizationInfo queryForAuthorizationInfo(final PrincipalCollection principals, final LdapContextFactory ldapContextFactory) throws NamingException {
+        final Set<String> userGroups = findLDAPGroupsForUser(principals, ldapContextFactory);
+
+        final SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(userGroups);
+        final Set<String> stringPermissions = groupsPermissions(userGroups);
+        simpleAuthorizationInfo.setStringPermissions(stringPermissions);
+
+        return simpleAuthorizationInfo;
+    }
+
+    private Set<String> findLDAPGroupsForUser(final PrincipalCollection principals, final LdapContextFactory ldapContextFactory) throws NamingException {
+        final String username = (String) getAvailablePrincipal(principals);
+
+        LdapContext systemLdapCtx = null;
+        try {
+            systemLdapCtx = ldapContextFactory.getSystemLdapContext();
+            return findLDAPGroupsForUser(username, systemLdapCtx);
+        } catch (AuthenticationException ex) {
+            log.info("LDAP authentication exception: " + ex.getLocalizedMessage());
+            return ImmutableSet.<String>of();
+        } finally {
+            LdapUtils.closeContext(systemLdapCtx);
+        }
+    }
+
+    private Set<String> findLDAPGroupsForUser(final String userName, final LdapContext ldapCtx) throws NamingException {
+        final NamingEnumeration<SearchResult> foundGroups = ldapCtx.search(searchBase,
+                                                                           groupSearchFilter.replace(USERDN_SUBSTITUTION_TOKEN, userName),
+                                                                           SUBTREE_SCOPE);
+
+        // Extract the name of all the groups
+        final Iterator<SearchResult> groupsIterator = Iterators.<SearchResult>forEnumeration(foundGroups);
+        final Iterator<String> groupsNameIterator = Iterators.<SearchResult, String>transform(groupsIterator,
+                                                                                              new Function<SearchResult, String>() {
+                                                                                                  @Override
+                                                                                                  public String apply(final SearchResult groupEntry) {
+                                                                                                      return extractGroupNameFromSearchResult(groupEntry);
+                                                                                                  }
+                                                                                              });
+        final Iterator<String> finalGroupsNameIterator = Iterators.<String>filter(groupsNameIterator, Predicates.notNull());
+
+        return Sets.newHashSet(finalGroupsNameIterator);
+    }
+
+    private String extractGroupNameFromSearchResult(final SearchResult searchResult) {
+        // Get all attributes for that group
+        final Iterator<? extends Attribute> attributesIterator = Iterators.forEnumeration(searchResult.getAttributes().getAll());
+
+        // Find the attribute representing the group name
+        final Iterator<? extends Attribute> groupNameAttributesIterator = Iterators.filter(attributesIterator,
+                                                                                           new Predicate<Attribute>() {
+                                                                                               @Override
+                                                                                               public boolean apply(final Attribute attribute) {
+                                                                                                   return groupNameId.equalsIgnoreCase(attribute.getID());
+                                                                                               }
+                                                                                           });
+
+        // Extract the group name from the attribute
+        // Note: at this point, groupNameAttributesIterator should really contain a single element
+        final Iterator<String> groupNamesIterator = Iterators.transform(groupNameAttributesIterator,
+                                                                        new Function<Attribute, String>() {
+                                                                            @Override
+                                                                            public String apply(final Attribute groupNameAttribute) {
+                                                                                try {
+                                                                                    final NamingEnumeration<?> enumeration = groupNameAttribute.getAll();
+                                                                                    if (enumeration.hasMore()) {
+                                                                                        return enumeration.next().toString();
+                                                                                    } else {
+                                                                                        return null;
+                                                                                    }
+                                                                                } catch (NamingException namingException) {
+                                                                                    log.warn("Unable to read group name", namingException);
+                                                                                    return null;
+                                                                                }
+                                                                            }
+                                                                        });
+        final Iterator<String> finalGroupNamesIterator = Iterators.<String>filter(groupNamesIterator, Predicates.notNull());
+
+        if (finalGroupNamesIterator.hasNext()) {
+            return finalGroupNamesIterator.next();
+        } else {
+            log.warn("Unable to find an attribute matching {}", groupNameId);
+            return null;
+        }
+    }
+
+    private Set<String> groupsPermissions(final Set<String> groups) {
+        final Set<String> permissions = new HashSet<String>();
+        for (final String group : groups) {
+            final Collection<String> permissionsForGroup = permissionsByGroup.get(group);
+            if (permissionsForGroup != null) {
+                permissions.addAll(permissionsForGroup);
+            }
+        }
+        return permissions;
+    }
+
+    @VisibleForTesting
+    public Map<String, Collection<String>> getPermissionsByGroup() {
+        return permissionsByGroup;
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/security/shiro/realm/SkipSSLCheckSocketFactory.java b/util/src/main/java/com/ning/billing/util/security/shiro/realm/SkipSSLCheckSocketFactory.java
new file mode 100644
index 0000000..d8580e8
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/security/shiro/realm/SkipSSLCheckSocketFactory.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.security.shiro.realm;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SkipSSLCheckSocketFactory extends SocketFactory {
+
+    private static final Logger log = LoggerFactory.getLogger(SkipSSLCheckSocketFactory.class);
+
+    private static SocketFactory skipSSLCheckFactory = null;
+
+    static {
+        final TrustManager[] noOpTrustManagers = new TrustManager[]{
+                new X509TrustManager() {
+                    public X509Certificate[] getAcceptedIssuers() { return null; }
+
+                    public void checkClientTrusted(X509Certificate[] c, String a) { }
+
+                    public void checkServerTrusted(X509Certificate[] c, String a) { }
+                }};
+
+        try {
+            final SSLContext context = SSLContext.getInstance("SSL");
+            context.init(null, noOpTrustManagers, new SecureRandom());
+            skipSSLCheckFactory = context.getSocketFactory();
+        } catch (GeneralSecurityException e) {
+            log.warn("SSL exception", e);
+        }
+    }
+
+    public static SocketFactory getDefault() {
+        return new SkipSSLCheckSocketFactory();
+    }
+
+    public Socket createSocket(final String arg0, final int arg1) throws IOException {
+        return skipSSLCheckFactory.createSocket(arg0, arg1);
+    }
+
+    public Socket createSocket(final InetAddress arg0, final int arg1) throws IOException {
+        return skipSSLCheckFactory.createSocket(arg0, arg1);
+    }
+
+    public Socket createSocket(final String arg0, final int arg1, final InetAddress arg2, final int arg3) throws IOException {
+        return skipSSLCheckFactory.createSocket(arg0, arg1, arg2, arg3);
+    }
+
+    public Socket createSocket(final InetAddress arg0, final int arg1, final InetAddress arg2, final int arg3) throws IOException {
+        return skipSSLCheckFactory.createSocket(arg0, arg1, arg2, arg3);
+    }
+}
diff --git a/util/src/test/java/com/ning/billing/util/security/shiro/realm/TestKillBillJndiLdapRealm.java b/util/src/test/java/com/ning/billing/util/security/shiro/realm/TestKillBillJndiLdapRealm.java
new file mode 100644
index 0000000..8b4d8d2
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/security/shiro/realm/TestKillBillJndiLdapRealm.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.security.shiro.realm;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.subject.SimplePrincipalCollection;
+import org.skife.config.ConfigSource;
+import org.skife.config.ConfigurationObjectFactory;
+import org.skife.config.SimplePropertyConfigSource;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.util.UtilTestSuiteNoDB;
+import com.ning.billing.util.config.SecurityConfig;
+
+import com.google.common.collect.Sets;
+
+public class TestKillBillJndiLdapRealm extends UtilTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testCheckConfiguration() throws Exception {
+        // Test default configuration (see SecurityConfig)
+        final Map<String, Collection<String>> permission = killBillJndiLdapRealm.getPermissionsByGroup();
+
+        Assert.assertEquals(permission.get("admin").size(), 1);
+        Assert.assertEquals(permission.get("admin").iterator().next(), "*:*");
+
+        Assert.assertEquals(permission.get("finance").size(), 2);
+        Assert.assertEquals(Sets.newHashSet(permission.get("finance")), Sets.newHashSet("invoice:*", "payment:*"));
+
+        Assert.assertEquals(permission.get("support").size(), 2);
+        Assert.assertEquals(Sets.newHashSet(permission.get("support")), Sets.newHashSet("entitlement:*", "invoice:item_adjust"));
+    }
+
+    @Test(groups = "external", enabled = false)
+    public void testCheckLDAPConnection() throws Exception {
+        // Convenience method to verify your LDAP connectivity
+        final Properties props = new Properties();
+        props.setProperty("killbill.security.ldap.userDnTemplate", "uid={0},ou=users,dc=mycompany,dc=com");
+        props.setProperty("killbill.security.ldap.searchBase", "ou=groups,dc=mycompany,dc=com");
+        props.setProperty("killbill.security.ldap.groupSearchFilter", "memberOf=uid={0},ou=users,dc=mycompany,dc=com");
+        props.setProperty("killbill.security.ldap.groupNameId", "cn");
+        props.setProperty("killbill.security.ldap.url", "ldap://ldap:389");
+        props.setProperty("killbill.security.ldap.disableSSLCheck", "true");
+        props.setProperty("killbill.security.ldap.systemUsername", "cn=root");
+        props.setProperty("killbill.security.ldap.systemPassword", "password");
+        props.setProperty("killbill.security.ldap.authenticationMechanism", "simple");
+        props.setProperty("killbill.security.ldap.permissionsByGroup", "support-group: entitlement:*\n" +
+                                                                       "finance-group: invoice:*, payment:*\n" +
+                                                                       "ops-group: *:*");
+        final ConfigSource customConfigSource = new SimplePropertyConfigSource(props);
+        final SecurityConfig securityConfig = new ConfigurationObjectFactory(customConfigSource).build(SecurityConfig.class);
+        final KillBillJndiLdapRealm ldapRealm = new KillBillJndiLdapRealm(securityConfig);
+
+        final String username = "pierre";
+        final String password = "password";
+
+        // Check authentication
+        final UsernamePasswordToken token = new UsernamePasswordToken(username, password);
+        final AuthenticationInfo authenticationInfo = ldapRealm.getAuthenticationInfo(token);
+        System.out.println(authenticationInfo);
+
+        // Check permissions
+        final SimplePrincipalCollection principals = new SimplePrincipalCollection(username, username);
+        final AuthorizationInfo authorizationInfo = ldapRealm.queryForAuthorizationInfo(principals, ldapRealm.getContextFactory());
+        System.out.println("Roles: " + authorizationInfo.getRoles());
+        System.out.println("Permissions: " + authorizationInfo.getStringPermissions());
+    }
+}
diff --git a/util/src/test/java/com/ning/billing/util/UtilTestSuiteNoDB.java b/util/src/test/java/com/ning/billing/util/UtilTestSuiteNoDB.java
index a7ab18f..acb66b1 100644
--- a/util/src/test/java/com/ning/billing/util/UtilTestSuiteNoDB.java
+++ b/util/src/test/java/com/ning/billing/util/UtilTestSuiteNoDB.java
@@ -41,6 +41,7 @@ import com.ning.billing.util.cache.CacheControllerDispatcher;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.dao.NonEntityDao;
 import com.ning.billing.util.glue.TestUtilModuleNoDB;
+import com.ning.billing.util.security.shiro.realm.KillBillJndiLdapRealm;
 
 import com.google.inject.Guice;
 import com.google.inject.Injector;
@@ -64,6 +65,8 @@ public class UtilTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     protected AuditUserApi auditUserApi;
     @Inject
     protected SecurityApi securityApi;
+    @Inject
+    protected KillBillJndiLdapRealm killBillJndiLdapRealm;
 
     @BeforeClass(groups = "fast")
     public void beforeClass() throws Exception {