mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-29 15:22:15 +00:00
SEC-714: Refactor PreAuthenticatedGrantedAuthoritiesSetter and PreAuthenticatedGrantedAuthoritiesRetriever
http://jira.springframework.org/browse/SEC-714
This commit is contained in:
parent
42a80931c1
commit
712f1770d9
@ -0,0 +1,15 @@
|
|||||||
|
package org.springframework.security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that a object stores GrantedAuthority objects.
|
||||||
|
* <p>
|
||||||
|
* Typically used in a pre-authenticated scenario when an AuthenticationDetails instance may also be
|
||||||
|
* used to obtain user authorities.
|
||||||
|
*
|
||||||
|
* @author Ruud Senden
|
||||||
|
* @author Luke Taylor
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public interface GrantedAuthoritiesContainer {
|
||||||
|
GrantedAuthority[] getGrantedAuthorities();
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package org.springframework.security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that a object can be used to store and retrieve GrantedAuthority objects.
|
||||||
|
* <p>
|
||||||
|
* Typically used in a pre-authenticated scenario when an AuthenticationDetails instance may also be
|
||||||
|
* used to obtain user authorities.
|
||||||
|
*
|
||||||
|
* @author Ruud Senden
|
||||||
|
* @author Luke Taylor
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public interface MutableGrantedAuthoritiesContainer extends GrantedAuthoritiesContainer {
|
||||||
|
/**
|
||||||
|
* Used to store authorities in the containing object.
|
||||||
|
*/
|
||||||
|
void setGrantedAuthorities(GrantedAuthority[] authorities);
|
||||||
|
}
|
@ -1,18 +0,0 @@
|
|||||||
package org.springframework.security.providers.preauth;
|
|
||||||
|
|
||||||
import org.springframework.security.GrantedAuthority;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface that allows for retrieval of a list of pre-authenticated Granted
|
|
||||||
* Authorities.
|
|
||||||
*
|
|
||||||
* @author Ruud Senden
|
|
||||||
* @since 2.0
|
|
||||||
*/
|
|
||||||
public interface PreAuthenticatedGrantedAuthoritiesRetriever {
|
|
||||||
/**
|
|
||||||
* @return GrantedAuthority[] List of pre-authenticated GrantedAuthorities
|
|
||||||
*/
|
|
||||||
GrantedAuthority[] getPreAuthenticatedGrantedAuthorities();
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
package org.springframework.security.providers.preauth;
|
|
||||||
|
|
||||||
import org.springframework.security.GrantedAuthority;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Counterpart of PreAuthenticatedGrantedAuthoritiesRetriever that allows
|
|
||||||
* setting a list of pre-authenticated GrantedAuthorities. This interface is not
|
|
||||||
* actually being used by the PreAuthenticatedAuthenticationProvider or one of
|
|
||||||
* its related classes, but may be useful for classes that also implement
|
|
||||||
* PreAuthenticatedGrantedAuthoritiesRetriever.
|
|
||||||
*
|
|
||||||
* @author Ruud Senden
|
|
||||||
* @since 2.0
|
|
||||||
*/
|
|
||||||
public interface PreAuthenticatedGrantedAuthoritiesSetter {
|
|
||||||
/**
|
|
||||||
* @param aPreAuthenticatedGrantedAuthorities
|
|
||||||
* The pre-authenticated GrantedAuthority[] to set
|
|
||||||
*/
|
|
||||||
void setPreAuthenticatedGrantedAuthorities(GrantedAuthority[] aPreAuthenticatedGrantedAuthorities);
|
|
||||||
}
|
|
@ -2,6 +2,7 @@ package org.springframework.security.providers.preauth;
|
|||||||
|
|
||||||
import org.springframework.security.userdetails.UserDetails;
|
import org.springframework.security.userdetails.UserDetails;
|
||||||
import org.springframework.security.userdetails.User;
|
import org.springframework.security.userdetails.User;
|
||||||
|
import org.springframework.security.GrantedAuthoritiesContainer;
|
||||||
import org.springframework.security.GrantedAuthority;
|
import org.springframework.security.GrantedAuthority;
|
||||||
import org.springframework.security.AuthenticationException;
|
import org.springframework.security.AuthenticationException;
|
||||||
import org.springframework.security.Authentication;
|
import org.springframework.security.Authentication;
|
||||||
@ -20,10 +21,8 @@ import org.springframework.util.Assert;
|
|||||||
* PreAuthenticatedAuthenticationToken.getDetails().
|
* PreAuthenticatedAuthenticationToken.getDetails().
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* The details object as returned by
|
* The details object as returned by PreAuthenticatedAuthenticationToken.getDetails() must implement the
|
||||||
* PreAuthenticatedAuthenticationToken.getDetails() must implement the
|
* {@link GrantedAuthoritiesContainer} interface for this implementation to work.
|
||||||
* PreAuthenticatedGrantedAuthoritiesRetriever interface for this implementation
|
|
||||||
* to work.
|
|
||||||
*
|
*
|
||||||
* @author Ruud Senden
|
* @author Ruud Senden
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
@ -32,14 +31,14 @@ public class PreAuthenticatedGrantedAuthoritiesUserDetailsService implements Aut
|
|||||||
/**
|
/**
|
||||||
* Get a UserDetails object based on the user name contained in the given
|
* Get a UserDetails object based on the user name contained in the given
|
||||||
* token, and the GrantedAuthorities as returned by the
|
* token, and the GrantedAuthorities as returned by the
|
||||||
* PreAuthenticatedGrantedAuthoritiesRetriever implementation as returned by
|
* GrantedAuthoritiesContainer implementation as returned by
|
||||||
* the token.getDetails() method.
|
* the token.getDetails() method.
|
||||||
*/
|
*/
|
||||||
public UserDetails loadUserDetails(Authentication token) throws AuthenticationException {
|
public UserDetails loadUserDetails(Authentication token) throws AuthenticationException {
|
||||||
Assert.notNull(token.getDetails());
|
Assert.notNull(token.getDetails());
|
||||||
Assert.isInstanceOf(PreAuthenticatedGrantedAuthoritiesRetriever.class, token.getDetails());
|
Assert.isInstanceOf(GrantedAuthoritiesContainer.class, token.getDetails());
|
||||||
GrantedAuthority[] preAuthenticatedGrantedAuthorities = ((PreAuthenticatedGrantedAuthoritiesRetriever) token.getDetails())
|
GrantedAuthority[] preAuthenticatedGrantedAuthorities = ((GrantedAuthoritiesContainer) token.getDetails())
|
||||||
.getPreAuthenticatedGrantedAuthorities();
|
.getGrantedAuthorities();
|
||||||
UserDetails ud = new User(token.getName(), "N/A", true, true, true, true, preAuthenticatedGrantedAuthorities);
|
UserDetails ud = new User(token.getName(), "N/A", true, true, true, true, preAuthenticatedGrantedAuthorities);
|
||||||
return ud;
|
return ud;
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,9 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.springframework.security.providers.preauth.PreAuthenticatedGrantedAuthoritiesRetriever;
|
|
||||||
import org.springframework.security.providers.preauth.PreAuthenticatedGrantedAuthoritiesSetter;
|
|
||||||
import org.springframework.security.ui.WebAuthenticationDetails;
|
import org.springframework.security.ui.WebAuthenticationDetails;
|
||||||
import org.springframework.security.GrantedAuthority;
|
import org.springframework.security.GrantedAuthority;
|
||||||
|
import org.springframework.security.MutableGrantedAuthoritiesContainer;
|
||||||
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
@ -16,10 +15,11 @@ import org.springframework.util.Assert;
|
|||||||
* pre-authenticated Granted Authorities.
|
* pre-authenticated Granted Authorities.
|
||||||
*
|
*
|
||||||
* @author Ruud Senden
|
* @author Ruud Senden
|
||||||
|
* @author Luke Taylor
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
public class PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails extends WebAuthenticationDetails implements
|
public class PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails extends WebAuthenticationDetails implements
|
||||||
PreAuthenticatedGrantedAuthoritiesRetriever, PreAuthenticatedGrantedAuthoritiesSetter {
|
MutableGrantedAuthoritiesContainer {
|
||||||
public static final long serialVersionUID = 1L;
|
public static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private GrantedAuthority[] preAuthenticatedGrantedAuthorities = null;
|
private GrantedAuthority[] preAuthenticatedGrantedAuthorities = null;
|
||||||
@ -28,35 +28,22 @@ public class PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails extends
|
|||||||
super(request);
|
super(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public GrantedAuthority[] getGrantedAuthorities() {
|
||||||
* @return The String representation of this object.
|
|
||||||
*/
|
|
||||||
public String toString() {
|
|
||||||
StringBuffer sb = new StringBuffer();
|
|
||||||
sb.append(super.toString() + "; ");
|
|
||||||
sb.append("preAuthenticatedGrantedAuthorities: " + Arrays.asList(preAuthenticatedGrantedAuthorities));
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see org.springframework.security.providers.preauth.PreAuthenticatedGrantedAuthoritiesRetriever#getPreAuthenticatedGrantedAuthorities()
|
|
||||||
*/
|
|
||||||
public GrantedAuthority[] getPreAuthenticatedGrantedAuthorities() {
|
|
||||||
Assert.notNull(preAuthenticatedGrantedAuthorities, "Pre-authenticated granted authorities have not been set");
|
Assert.notNull(preAuthenticatedGrantedAuthorities, "Pre-authenticated granted authorities have not been set");
|
||||||
GrantedAuthority[] result = new GrantedAuthority[preAuthenticatedGrantedAuthorities.length];
|
GrantedAuthority[] result = new GrantedAuthority[preAuthenticatedGrantedAuthorities.length];
|
||||||
System.arraycopy(preAuthenticatedGrantedAuthorities, 0, result, 0, result.length);
|
System.arraycopy(preAuthenticatedGrantedAuthorities, 0, result, 0, result.length);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
public void setGrantedAuthorities(GrantedAuthority[] authorities) {
|
||||||
* (non-Javadoc)
|
this.preAuthenticatedGrantedAuthorities = new GrantedAuthority[authorities.length];
|
||||||
*
|
System.arraycopy(authorities, 0, preAuthenticatedGrantedAuthorities, 0, preAuthenticatedGrantedAuthorities.length);
|
||||||
* @see org.springframework.security.providers.preauth.j2ee.PreAuthenticatedGrantedAuthoritiesSetter#setJ2eeBasedGrantedAuthorities()
|
|
||||||
*/
|
|
||||||
public void setPreAuthenticatedGrantedAuthorities(GrantedAuthority[] aJ2eeBasedGrantedAuthorities) {
|
|
||||||
this.preAuthenticatedGrantedAuthorities = new GrantedAuthority[aJ2eeBasedGrantedAuthorities.length];
|
|
||||||
System.arraycopy(aJ2eeBasedGrantedAuthorities, 0, preAuthenticatedGrantedAuthorities, 0, preAuthenticatedGrantedAuthorities.length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
sb.append(super.toString() + "; ");
|
||||||
|
sb.append("preAuthenticatedGrantedAuthorities: " + Arrays.asList(preAuthenticatedGrantedAuthorities));
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ package org.springframework.security.ui.preauth.j2ee;
|
|||||||
|
|
||||||
import org.springframework.security.ui.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails;
|
import org.springframework.security.ui.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails;
|
||||||
import org.springframework.security.ui.WebAuthenticationDetailsSource;
|
import org.springframework.security.ui.WebAuthenticationDetailsSource;
|
||||||
import org.springframework.security.providers.preauth.PreAuthenticatedGrantedAuthoritiesSetter;
|
|
||||||
import org.springframework.security.GrantedAuthority;
|
import org.springframework.security.GrantedAuthority;
|
||||||
|
import org.springframework.security.MutableGrantedAuthoritiesContainer;
|
||||||
import org.springframework.security.authoritymapping.Attributes2GrantedAuthoritiesMapper;
|
import org.springframework.security.authoritymapping.Attributes2GrantedAuthoritiesMapper;
|
||||||
import org.springframework.security.authoritymapping.MappableAttributesRetriever;
|
import org.springframework.security.authoritymapping.MappableAttributesRetriever;
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ public class J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource extends Web
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Build the authentication details object. If the specified authentication
|
* Build the authentication details object. If the specified authentication
|
||||||
* details class implements the PreAuthenticatedGrantedAuthoritiesSetter, a
|
* details class implements {@link MutableGrantedAuthoritiesContainer}, a
|
||||||
* list of pre-authenticated Granted Authorities will be set based on the
|
* list of pre-authenticated Granted Authorities will be set based on the
|
||||||
* J2EE roles for the current user.
|
* J2EE roles for the current user.
|
||||||
*
|
*
|
||||||
@ -56,9 +56,9 @@ public class J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource extends Web
|
|||||||
*/
|
*/
|
||||||
public Object buildDetails(Object context) {
|
public Object buildDetails(Object context) {
|
||||||
Object result = super.buildDetails(context);
|
Object result = super.buildDetails(context);
|
||||||
if (result instanceof PreAuthenticatedGrantedAuthoritiesSetter) {
|
if (result instanceof MutableGrantedAuthoritiesContainer) {
|
||||||
((PreAuthenticatedGrantedAuthoritiesSetter) result)
|
((MutableGrantedAuthoritiesContainer) result)
|
||||||
.setPreAuthenticatedGrantedAuthorities(getJ2eeBasedGrantedAuthorities((HttpServletRequest)context));
|
.setGrantedAuthorities(getJ2eeBasedGrantedAuthorities((HttpServletRequest)context));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.springframework.security.providers.preauth;
|
package org.springframework.security.providers.preauth;
|
||||||
|
|
||||||
|
import org.springframework.security.GrantedAuthoritiesContainer;
|
||||||
import org.springframework.security.GrantedAuthorityImpl;
|
import org.springframework.security.GrantedAuthorityImpl;
|
||||||
import org.springframework.security.GrantedAuthority;
|
import org.springframework.security.GrantedAuthority;
|
||||||
import org.springframework.security.userdetails.UserDetails;
|
import org.springframework.security.userdetails.UserDetails;
|
||||||
@ -53,8 +54,8 @@ public class PreAuthenticatedGrantedAuthoritiesUserDetailsServiceTests extends T
|
|||||||
private void testGetUserDetails(final String userName, final GrantedAuthority[] gas) {
|
private void testGetUserDetails(final String userName, final GrantedAuthority[] gas) {
|
||||||
PreAuthenticatedGrantedAuthoritiesUserDetailsService svc = new PreAuthenticatedGrantedAuthoritiesUserDetailsService();
|
PreAuthenticatedGrantedAuthoritiesUserDetailsService svc = new PreAuthenticatedGrantedAuthoritiesUserDetailsService();
|
||||||
PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(userName, "dummy");
|
PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(userName, "dummy");
|
||||||
token.setDetails(new PreAuthenticatedGrantedAuthoritiesRetriever() {
|
token.setDetails(new GrantedAuthoritiesContainer() {
|
||||||
public GrantedAuthority[] getPreAuthenticatedGrantedAuthorities() {
|
public GrantedAuthority[] getGrantedAuthorities() {
|
||||||
return gas;
|
return gas;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -15,9 +15,7 @@ import junit.framework.TestCase;
|
|||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author TSARDD
|
* @author TSARDD
|
||||||
* @since 18-okt-2007
|
|
||||||
*/
|
*/
|
||||||
public class PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetailsTests extends TestCase {
|
public class PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetailsTests extends TestCase {
|
||||||
|
|
||||||
@ -25,7 +23,7 @@ public class PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetailsTests ext
|
|||||||
PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails details = new PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails(
|
PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails details = new PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails(
|
||||||
getRequest("testUser", new String[] {}));
|
getRequest("testUser", new String[] {}));
|
||||||
GrantedAuthority[] gas = new GrantedAuthority[] { new GrantedAuthorityImpl("Role1"), new GrantedAuthorityImpl("Role2") };
|
GrantedAuthority[] gas = new GrantedAuthority[] { new GrantedAuthorityImpl("Role1"), new GrantedAuthorityImpl("Role2") };
|
||||||
details.setPreAuthenticatedGrantedAuthorities(gas);
|
details.setGrantedAuthorities(gas);
|
||||||
String toString = details.toString();
|
String toString = details.toString();
|
||||||
assertTrue("toString should contain Role1", toString.contains("Role1"));
|
assertTrue("toString should contain Role1", toString.contains("Role1"));
|
||||||
assertTrue("toString should contain Role2", toString.contains("Role2"));
|
assertTrue("toString should contain Role2", toString.contains("Role2"));
|
||||||
@ -37,18 +35,17 @@ public class PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetailsTests ext
|
|||||||
GrantedAuthority[] gas = new GrantedAuthority[] { new GrantedAuthorityImpl("Role1"), new GrantedAuthorityImpl("Role2") };
|
GrantedAuthority[] gas = new GrantedAuthority[] { new GrantedAuthorityImpl("Role1"), new GrantedAuthorityImpl("Role2") };
|
||||||
Collection expectedGas = Arrays.asList(gas);
|
Collection expectedGas = Arrays.asList(gas);
|
||||||
|
|
||||||
details.setPreAuthenticatedGrantedAuthorities(gas);
|
details.setGrantedAuthorities(gas);
|
||||||
Collection returnedGas = Arrays.asList(details.getPreAuthenticatedGrantedAuthorities());
|
Collection returnedGas = Arrays.asList(details.getGrantedAuthorities());
|
||||||
assertTrue("Collections do not contain same elements; expected: " + expectedGas + ", returned: " + returnedGas, expectedGas
|
assertTrue("Collections do not contain same elements; expected: " + expectedGas + ", returned: " + returnedGas,
|
||||||
.containsAll(returnedGas)
|
expectedGas.containsAll(returnedGas) && returnedGas.containsAll(expectedGas));
|
||||||
&& returnedGas.containsAll(expectedGas));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void testGetWithoutSetPreAuthenticatedGrantedAuthorities() {
|
public final void testGetWithoutSetPreAuthenticatedGrantedAuthorities() {
|
||||||
PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails details = new PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails(
|
PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails details = new PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails(
|
||||||
getRequest("testUser", new String[] {}));
|
getRequest("testUser", new String[] {}));
|
||||||
try {
|
try {
|
||||||
GrantedAuthority[] gas = details.getPreAuthenticatedGrantedAuthorities();
|
GrantedAuthority[] gas = details.getGrantedAuthorities();
|
||||||
fail("Expected exception didn't occur");
|
fail("Expected exception didn't occur");
|
||||||
} catch (IllegalArgumentException expected) {
|
} catch (IllegalArgumentException expected) {
|
||||||
} catch (Exception unexpected) {
|
} catch (Exception unexpected) {
|
||||||
|
@ -21,7 +21,6 @@ import org.springframework.mock.web.MockHttpServletRequest;
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author TSARDD
|
* @author TSARDD
|
||||||
* @since 18-okt-2007
|
|
||||||
*/
|
*/
|
||||||
public class J2eeBasedPreAuthenticatedWebAuthenticationDetailsSourceTests extends TestCase {
|
public class J2eeBasedPreAuthenticatedWebAuthenticationDetailsSourceTests extends TestCase {
|
||||||
|
|
||||||
@ -92,7 +91,7 @@ public class J2eeBasedPreAuthenticatedWebAuthenticationDetailsSourceTests extend
|
|||||||
assertTrue("Returned object not of type PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails, actual type: " + o.getClass(),
|
assertTrue("Returned object not of type PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails, actual type: " + o.getClass(),
|
||||||
o instanceof PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails);
|
o instanceof PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails);
|
||||||
PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails details = (PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails) o;
|
PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails details = (PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails) o;
|
||||||
GrantedAuthority[] gas = details.getPreAuthenticatedGrantedAuthorities();
|
GrantedAuthority[] gas = details.getGrantedAuthorities();
|
||||||
assertNotNull("Granted authorities should not be null", gas);
|
assertNotNull("Granted authorities should not be null", gas);
|
||||||
assertTrue("Number of granted authorities should be " + expectedRoles.length, gas.length == expectedRoles.length);
|
assertTrue("Number of granted authorities should be " + expectedRoles.length, gas.length == expectedRoles.length);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user