killbill-uncached
Changes
util/src/main/java/com/ning/billing/util/security/shiro/realm/KillBillJndiLdapRealm.java 222(+222 -0)
util/src/main/java/com/ning/billing/util/security/shiro/realm/SkipSSLCheckSocketFactory.java 78(+78 -0)
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 {