SEC-450: Added group subtree to LDAP test server and extra tests for DefaultLdapAuthoritiesPopulator to make sure searchSubtree parameter works as expected.
This commit is contained in:
parent
e189bc685f
commit
4ba77fa736
|
@ -40,10 +40,11 @@ import javax.naming.directory.SearchControls;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default strategy for obtaining user role information from the directory.
|
* The default strategy for obtaining user role information from the directory.
|
||||||
*
|
* <p/>
|
||||||
* <p>It obtains roles by performing a search for "groups" the user is a member of.</p>
|
* <p>It obtains roles by performing a search for "groups" the user is a member of.</p>
|
||||||
*
|
* <p/>
|
||||||
* <p>A typical group search scenario would be where each group/role is specified using the <tt>groupOfNames</tt>
|
* <p/>
|
||||||
|
* A typical group search scenario would be where each group/role is specified using the <tt>groupOfNames</tt>
|
||||||
* (or <tt>groupOfUniqueNames</tt>) LDAP objectClass and the user's DN is listed in the <tt>member</tt> (or
|
* (or <tt>groupOfUniqueNames</tt>) LDAP objectClass and the user's DN is listed in the <tt>member</tt> (or
|
||||||
* <tt>uniqueMember</tt>) attribute to indicate that they should be assigned that role. The following LDIF sample has
|
* <tt>uniqueMember</tt>) attribute to indicate that they should be assigned that role. The following LDIF sample has
|
||||||
* the groups stored under the DN <tt>ou=groups,dc=acegisecurity,dc=org</tt> and a group called "developers" with
|
* the groups stored under the DN <tt>ou=groups,dc=acegisecurity,dc=org</tt> and a group called "developers" with
|
||||||
|
@ -55,13 +56,14 @@ import javax.naming.directory.SearchControls;
|
||||||
* member: uid=ben,ou=people,dc=acegisecurity,dc=orgmember: uid=marissa,ou=people,dc=acegisecurity,dc=orgou: developer
|
* member: uid=ben,ou=people,dc=acegisecurity,dc=orgmember: uid=marissa,ou=people,dc=acegisecurity,dc=orgou: developer
|
||||||
* </pre>
|
* </pre>
|
||||||
* </p>
|
* </p>
|
||||||
* <p>The group search is performed within a DN specified by the <tt>groupSearchBase</tt> property, which should
|
* <p/>
|
||||||
|
* The group search is performed within a DN specified by the <tt>groupSearchBase</tt> property, which should
|
||||||
* be relative to the root DN of its <tt>InitialDirContextFactory</tt>. If the search base is null, group searching is
|
* be relative to the root DN of its <tt>InitialDirContextFactory</tt>. If the search base is null, group searching is
|
||||||
* disabled. The filter used in the search is defined by the <tt>groupSearchFilter</tt> property, with the filter
|
* disabled. The filter used in the search is defined by the <tt>groupSearchFilter</tt> property, with the filter
|
||||||
* argument {0} being the full DN of the user. You can also optionally use the parameter {1}, which will be substituted
|
* argument {0} being the full DN of the user. You can also optionally use the parameter {1}, which will be substituted
|
||||||
* with the username. You can also specify which attribute defines the role name by setting
|
* with the username. You can also specify which attribute defines the role name by setting
|
||||||
* the <tt>groupRoleAttribute</tt> property (the default is "cn").</p>
|
* the <tt>groupRoleAttribute</tt> property (the default is "cn").</p>
|
||||||
*
|
* <p/>
|
||||||
* <p>The configuration below shows how the group search might be performed with the above schema.
|
* <p>The configuration below shows how the group search might be performed with the above schema.
|
||||||
* <pre>
|
* <pre>
|
||||||
* <bean id="ldapAuthoritiesPopulator"
|
* <bean id="ldapAuthoritiesPopulator"
|
||||||
|
@ -76,7 +78,11 @@ import javax.naming.directory.SearchControls;
|
||||||
* </bean>
|
* </bean>
|
||||||
* </pre>
|
* </pre>
|
||||||
* A search for roles for user "uid=ben,ou=people,dc=acegisecurity,dc=org" would return the single granted authority
|
* A search for roles for user "uid=ben,ou=people,dc=acegisecurity,dc=org" would return the single granted authority
|
||||||
* "ROLE_DEVELOPER".</p>
|
* "ROLE_DEVELOPER".
|
||||||
|
* </p>
|
||||||
|
* <p/>
|
||||||
|
* The single-level search is performed by default. Setting the <tt>searchSubTree</tt> property to true will enable
|
||||||
|
* a search of the entire subtree under <tt>groupSearchBase</tt>.
|
||||||
*
|
*
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
|
@ -88,10 +94,14 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||||
|
|
||||||
//~ Instance fields ================================================================================================
|
//~ Instance fields ================================================================================================
|
||||||
|
|
||||||
/** A default role which will be assigned to all authenticated users if set */
|
/**
|
||||||
|
* A default role which will be assigned to all authenticated users if set
|
||||||
|
*/
|
||||||
private GrantedAuthority defaultRole = null;
|
private GrantedAuthority defaultRole = null;
|
||||||
|
|
||||||
/** An initial context factory is only required if searching for groups is required. */
|
/**
|
||||||
|
* An initial context factory is only required if searching for groups is required.
|
||||||
|
*/
|
||||||
private InitialDirContextFactory initialDirContextFactory = null;
|
private InitialDirContextFactory initialDirContextFactory = null;
|
||||||
private LdapTemplate ldapTemplate;
|
private LdapTemplate ldapTemplate;
|
||||||
|
|
||||||
|
@ -101,16 +111,24 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||||
*/
|
*/
|
||||||
private SearchControls searchControls = new SearchControls();
|
private SearchControls searchControls = new SearchControls();
|
||||||
|
|
||||||
/** The ID of the attribute which contains the role name for a group */
|
/**
|
||||||
|
* The ID of the attribute which contains the role name for a group
|
||||||
|
*/
|
||||||
private String groupRoleAttribute = "cn";
|
private String groupRoleAttribute = "cn";
|
||||||
|
|
||||||
/** The base DN from which the search for group membership should be performed */
|
/**
|
||||||
|
* The base DN from which the search for group membership should be performed
|
||||||
|
*/
|
||||||
private String groupSearchBase = null;
|
private String groupSearchBase = null;
|
||||||
|
|
||||||
/** The pattern to be used for the user search. {0} is the user's DN */
|
/**
|
||||||
|
* The pattern to be used for the user search. {0} is the user's DN
|
||||||
|
*/
|
||||||
private String groupSearchFilter = "(member={0})";
|
private String groupSearchFilter = "(member={0})";
|
||||||
|
|
||||||
/** Attributes of the User's LDAP Object that contain role name information. */
|
/**
|
||||||
|
* Attributes of the User's LDAP Object that contain role name information.
|
||||||
|
*/
|
||||||
|
|
||||||
// private String[] userRoleAttributes = null;
|
// private String[] userRoleAttributes = null;
|
||||||
private String rolePrefix = "ROLE_";
|
private String rolePrefix = "ROLE_";
|
||||||
|
@ -138,7 +156,6 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||||
* roles for the given user (on top of those obtained from the standard
|
* roles for the given user (on top of those obtained from the standard
|
||||||
* search implemented by this class).
|
* search implemented by this class).
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* @param ldapUser the user who's roles are required
|
* @param ldapUser the user who's roles are required
|
||||||
* @return the extra roles which will be merged with those returned by the group search
|
* @return the extra roles which will be merged with those returned by the group search
|
||||||
*/
|
*/
|
||||||
|
@ -152,7 +169,6 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||||
* the supplied LdapUserDetails object.
|
* the supplied LdapUserDetails object.
|
||||||
*
|
*
|
||||||
* @param userDetails the user who's authorities are required
|
* @param userDetails the user who's authorities are required
|
||||||
*
|
|
||||||
* @return the set of roles granted to the user.
|
* @return the set of roles granted to the user.
|
||||||
*/
|
*/
|
||||||
public final GrantedAuthority[] getGrantedAuthorities(LdapUserDetails userDetails) {
|
public final GrantedAuthority[] getGrantedAuthorities(LdapUserDetails userDetails) {
|
||||||
|
@ -236,10 +252,8 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||||
*
|
*
|
||||||
* @param userDn the user's distinguished name.
|
* @param userDn the user's distinguished name.
|
||||||
* @param userAttributes the retrieved user's attributes (unused by default).
|
* @param userAttributes the retrieved user's attributes (unused by default).
|
||||||
*
|
|
||||||
* @return the set of roles obtained from a group membership search, or null if <tt>groupSearchBase</tt> has been
|
* @return the set of roles obtained from a group membership search, or null if <tt>groupSearchBase</tt> has been
|
||||||
* set.
|
* set.
|
||||||
*
|
|
||||||
* @deprecated Subclasses should implement <tt>getAdditionalRoles</tt> instead.
|
* @deprecated Subclasses should implement <tt>getAdditionalRoles</tt> instead.
|
||||||
*/
|
*/
|
||||||
protected Set getGroupMembershipRoles(String userDn, Attributes userAttributes) {
|
protected Set getGroupMembershipRoles(String userDn, Attributes userAttributes) {
|
||||||
|
@ -311,6 +325,11 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
|
||||||
this.rolePrefix = rolePrefix;
|
this.rolePrefix = rolePrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to true, a subtree scope search will be performed. If false a single-level search is used.
|
||||||
|
*
|
||||||
|
* @param searchSubtree set to true to enable searching of the entire tree below the <tt>groupSearchBase</tt>.
|
||||||
|
*/
|
||||||
public void setSearchSubtree(boolean searchSubtree) {
|
public void setSearchSubtree(boolean searchSubtree) {
|
||||||
int searchScope = searchSubtree ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE;
|
int searchScope = searchSubtree ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE;
|
||||||
searchControls.setSearchScope(searchScope);
|
searchControls.setSearchScope(searchScope);
|
||||||
|
|
|
@ -23,8 +23,6 @@ import java.util.Hashtable;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
|
@ -36,7 +34,7 @@ public abstract class AbstractLdapServerTestCase extends TestCase {
|
||||||
protected static final String MANAGER_PASSWORD = "acegisecurity";
|
protected static final String MANAGER_PASSWORD = "acegisecurity";
|
||||||
|
|
||||||
// External server config
|
// External server config
|
||||||
// private static final String PROVIDER_URL = "ldap://monkeymachine:389/"+ROOT_DN;
|
// private static final String PROVIDER_URL = "ldap://gorille:389/"+ROOT_DN;
|
||||||
// private static final String CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
|
// private static final String CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
|
||||||
// private static final Hashtable EXTRA_ENV = new Hashtable();
|
// private static final Hashtable EXTRA_ENV = new Hashtable();
|
||||||
|
|
||||||
|
@ -52,7 +50,8 @@ public abstract class AbstractLdapServerTestCase extends TestCase {
|
||||||
|
|
||||||
//~ Constructors ===================================================================================================
|
//~ Constructors ===================================================================================================
|
||||||
|
|
||||||
protected AbstractLdapServerTestCase() {}
|
protected AbstractLdapServerTestCase() {
|
||||||
|
}
|
||||||
|
|
||||||
protected AbstractLdapServerTestCase(String string) {
|
protected AbstractLdapServerTestCase(String string) {
|
||||||
super(string);
|
super(string);
|
||||||
|
@ -64,7 +63,8 @@ public abstract class AbstractLdapServerTestCase extends TestCase {
|
||||||
return idf;
|
return idf;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onSetUp() {}
|
protected void onSetUp() {
|
||||||
|
}
|
||||||
|
|
||||||
public final void setUp() {
|
public final void setUp() {
|
||||||
idf = new DefaultInitialDirContextFactory(PROVIDER_URL);
|
idf = new DefaultInitialDirContextFactory(PROVIDER_URL);
|
||||||
|
|
|
@ -22,8 +22,6 @@ import javax.naming.directory.DirContext;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
|
@ -73,7 +71,8 @@ public class LdapTemplateTests extends AbstractLdapServerTestCase {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
fail("Expected LdapDataAccessException on NamingException");
|
fail("Expected LdapDataAccessException on NamingException");
|
||||||
} catch (LdapDataAccessException expected) {}
|
} catch (LdapDataAccessException expected) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSearchForSingleAttributeValues() {
|
public void testSearchForSingleAttributeValues() {
|
||||||
|
@ -81,8 +80,9 @@ public class LdapTemplateTests extends AbstractLdapServerTestCase {
|
||||||
|
|
||||||
Set values = template.searchForSingleAttributeValues("ou=groups", "(member={0})", new String[]{param}, "ou");
|
Set values = template.searchForSingleAttributeValues("ou=groups", "(member={0})", new String[]{param}, "ou");
|
||||||
|
|
||||||
assertEquals("Expected 2 results from search", 2, values.size());
|
assertEquals("Expected 3 results from search", 3, values.size());
|
||||||
assertTrue(values.contains("developer"));
|
assertTrue(values.contains("developer"));
|
||||||
assertTrue(values.contains("manager"));
|
assertTrue(values.contains("manager"));
|
||||||
|
assertTrue(values.contains("submanager"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class LdapTestServer {
|
||||||
|
|
||||||
//~ Methods ========================================================================================================
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
public void createGroup(String cn, String ou, String[] memberDns) {
|
public void createGroup(String cn, String groupContext, String ou, String[] memberDns) {
|
||||||
Attributes group = new BasicAttributes("cn", cn);
|
Attributes group = new BasicAttributes("cn", cn);
|
||||||
Attribute members = new BasicAttribute("member");
|
Attribute members = new BasicAttribute("member");
|
||||||
Attribute orgUnit = new BasicAttribute("ou", ou);
|
Attribute orgUnit = new BasicAttribute("ou", ou);
|
||||||
|
@ -82,7 +82,8 @@ public class LdapTestServer {
|
||||||
group.put(orgUnit);
|
group.put(orgUnit);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
serverContext.createSubcontext("cn=" + cn + ",ou=groups", group);
|
DirContext ctx = serverContext.createSubcontext("cn=" + cn + "," + groupContext, group);
|
||||||
|
System.out.println("Created group " + ctx.getNameInNamespace());
|
||||||
} catch (NameAlreadyBoundException ignore) {
|
} catch (NameAlreadyBoundException ignore) {
|
||||||
// System.out.println(" group " + cn + " already exists.");
|
// System.out.println(" group " + cn + " already exists.");
|
||||||
} catch (NamingException ne) {
|
} catch (NamingException ne) {
|
||||||
|
@ -122,7 +123,7 @@ public class LdapTestServer {
|
||||||
ou.put(objectClass);
|
ou.put(objectClass);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
serverContext.createSubcontext("ou=" + name, ou);
|
serverContext.createSubcontext(name, ou);
|
||||||
} catch (NameAlreadyBoundException ignore) {
|
} catch (NameAlreadyBoundException ignore) {
|
||||||
// System.out.println(" ou " + name + " already exists.");
|
// System.out.println(" ou " + name + " already exists.");
|
||||||
} catch (NamingException ne) {
|
} catch (NamingException ne) {
|
||||||
|
@ -188,16 +189,19 @@ public class LdapTestServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initTestData() {
|
private void initTestData() {
|
||||||
createOu("people");
|
createOu("ou=people");
|
||||||
createOu("groups");
|
createOu("ou=groups");
|
||||||
|
createOu("ou=subgroups,ou=groups");
|
||||||
|
|
||||||
createUser("bob", "Bob Hamilton", "bobspassword");
|
createUser("bob", "Bob Hamilton", "bobspassword");
|
||||||
createUser("ben", "Ben Alex", "{SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=");
|
createUser("ben", "Ben Alex", "{SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=");
|
||||||
|
|
||||||
String[] developers = new String[]{
|
String[] developers = new String[]{
|
||||||
"uid=ben,ou=people,dc=acegisecurity,dc=org", "uid=bob,ou=people,dc=acegisecurity,dc=org"
|
"uid=ben,ou=people,dc=acegisecurity,dc=org", "uid=bob,ou=people,dc=acegisecurity,dc=org"
|
||||||
};
|
};
|
||||||
createGroup("developers", "developer", developers);
|
createGroup("developers", "ou=groups", "developer", developers);
|
||||||
createGroup("managers", "manager", new String[] {developers[0]});
|
createGroup("managers", "ou=groups", "manager", new String[]{developers[0]});
|
||||||
|
createGroup("submanagers", "ou=subgroups,ou=groups", "submanager", new String[]{developers[0]});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
@ -243,7 +247,9 @@ public class LdapTestServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Recursively deletes a directory */
|
/**
|
||||||
|
* Recursively deletes a directory
|
||||||
|
*/
|
||||||
private boolean deleteDir(File dir) {
|
private boolean deleteDir(File dir) {
|
||||||
if (dir.isDirectory()) {
|
if (dir.isDirectory()) {
|
||||||
String[] children = dir.list();
|
String[] children = dir.list();
|
||||||
|
|
|
@ -23,13 +23,14 @@ import org.acegisecurity.userdetails.ldap.LdapUserDetailsImpl;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
import javax.naming.directory.BasicAttributes;
|
import javax.naming.directory.BasicAttributes;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* DOCUMENT ME!
|
||||||
DOCUMENT ME!
|
|
||||||
*
|
*
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
|
@ -63,6 +64,7 @@ public class DefaultLdapAuthoritiesPopulatorTests extends AbstractLdapServerTest
|
||||||
// GrantedAuthority[] authorities =
|
// GrantedAuthority[] authorities =
|
||||||
// populator.getGrantedAuthorities(user.createUserDetails());
|
// populator.getGrantedAuthorities(user.createUserDetails());
|
||||||
// assertEquals("User should have three roles", 3, authorities.length);
|
// assertEquals("User should have three roles", 3, authorities.length);
|
||||||
|
|
||||||
// }
|
// }
|
||||||
public void testDefaultRoleIsAssignedWhenSet() {
|
public void testDefaultRoleIsAssignedWhenSet() {
|
||||||
DefaultLdapAuthoritiesPopulator populator = new DefaultLdapAuthoritiesPopulator(getInitialCtxFactory(),
|
DefaultLdapAuthoritiesPopulator populator = new DefaultLdapAuthoritiesPopulator(getInitialCtxFactory(),
|
||||||
|
@ -118,6 +120,48 @@ public class DefaultLdapAuthoritiesPopulatorTests extends AbstractLdapServerTest
|
||||||
|
|
||||||
GrantedAuthority[] authorities = populator.getGrantedAuthorities(user.createUserDetails());
|
GrantedAuthority[] authorities = populator.getGrantedAuthorities(user.createUserDetails());
|
||||||
assertEquals("Should have 1 role", 1, authorities.length);
|
assertEquals("Should have 1 role", 1, authorities.length);
|
||||||
assertTrue(authorities[0].equals("ROLE_MANAGER"));
|
assertEquals("ROLE_MANAGER", authorities[0].getAuthority());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testSubGroupRolesAreNotFoundByDefault() {
|
||||||
|
DefaultLdapAuthoritiesPopulator populator = new DefaultLdapAuthoritiesPopulator(getInitialCtxFactory(),
|
||||||
|
"ou=groups");
|
||||||
|
populator.setGroupRoleAttribute("ou");
|
||||||
|
populator.setConvertToUpperCase(true);
|
||||||
|
|
||||||
|
LdapUserDetailsImpl.Essence user = new LdapUserDetailsImpl.Essence();
|
||||||
|
user.setUsername("manager");
|
||||||
|
user.setDn("uid=ben,ou=people,dc=acegisecurity,dc=org");
|
||||||
|
|
||||||
|
GrantedAuthority[] authorities = populator.getGrantedAuthorities(user.createUserDetails());
|
||||||
|
assertEquals("Should have 2 roles", 2, authorities.length);
|
||||||
|
Set roles = new HashSet(2);
|
||||||
|
roles.add(authorities[0].getAuthority());
|
||||||
|
roles.add(authorities[1].getAuthority());
|
||||||
|
assertTrue(roles.contains("ROLE_MANAGER"));
|
||||||
|
assertTrue(roles.contains("ROLE_DEVELOPER"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSubGroupRolesAreFoundWhenSubtreeSearchIsEnabled() {
|
||||||
|
DefaultLdapAuthoritiesPopulator populator = new DefaultLdapAuthoritiesPopulator(getInitialCtxFactory(),
|
||||||
|
"ou=groups");
|
||||||
|
populator.setGroupRoleAttribute("ou");
|
||||||
|
populator.setConvertToUpperCase(true);
|
||||||
|
populator.setSearchSubtree(true);
|
||||||
|
|
||||||
|
LdapUserDetailsImpl.Essence user = new LdapUserDetailsImpl.Essence();
|
||||||
|
user.setUsername("manager");
|
||||||
|
user.setDn("uid=ben,ou=people,dc=acegisecurity,dc=org");
|
||||||
|
|
||||||
|
GrantedAuthority[] authorities = populator.getGrantedAuthorities(user.createUserDetails());
|
||||||
|
assertEquals("Should have 3 roles", 3, authorities.length);
|
||||||
|
Set roles = new HashSet(3);
|
||||||
|
roles.add(authorities[0].getAuthority());
|
||||||
|
roles.add(authorities[1].getAuthority());
|
||||||
|
roles.add(authorities[2].getAuthority());
|
||||||
|
assertTrue(roles.contains("ROLE_MANAGER"));
|
||||||
|
assertTrue(roles.contains("ROLE_DEVELOPER"));
|
||||||
|
assertTrue(roles.contains("ROLE_SUBMANAGER"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue