/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed 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 org.springframework.security.access.expression.method;
import static org.assertj.core.api.Assertions.assertThat;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.Test;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.expression.method.PreInvocationExpressionAttribute;
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.util.SimpleMethodInvocation;
@SuppressWarnings("unchecked")
public class MethodExpressionVoterTests {
private TestingAuthenticationToken joe = new TestingAuthenticationToken("joe",
"joespass", "ROLE_blah");
private PreInvocationAuthorizationAdviceVoter am = new PreInvocationAuthorizationAdviceVoter(
new ExpressionBasedPreInvocationAdvice());
@Test
public void hasRoleExpressionAllowsUserWithRole() throws Exception {
MethodInvocation mi = new SimpleMethodInvocation(new TargetImpl(),
methodTakingAnArray());
assertThat(am.vote(joe, mi,
createAttributes(new PreInvocationExpressionAttribute(null, null,
"hasRole('blah')")))).isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);
}
@Test
public void hasRoleExpressionDeniesUserWithoutRole() throws Exception {
List<ConfigAttribute> cad = new ArrayList<ConfigAttribute>(1);
cad.add(new PreInvocationExpressionAttribute(null, null, "hasRole('joedoesnt')"));
MethodInvocation mi = new SimpleMethodInvocation(new TargetImpl(),
methodTakingAnArray());
assertThat(am.vote(joe, mi, cad)).isEqualTo(AccessDecisionVoter.ACCESS_DENIED);
}
@Test
public void matchingArgAgainstAuthenticationNameIsSuccessful() throws Exception {
MethodInvocation mi = new SimpleMethodInvocation(new TargetImpl(),
methodTakingAString(), "joe");
assertThat(am.vote(joe, mi,
createAttributes(new PreInvocationExpressionAttribute(null, null,
"(#argument == principal) and (principal == 'joe')"))))
.isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);
}
@Test
public void accessIsGrantedIfNoPreAuthorizeAttributeIsUsed() throws Exception {
Collection arg = createCollectionArg("joe", "bob", "sam");
MethodInvocation mi = new SimpleMethodInvocation(new TargetImpl(),
methodTakingACollection(), arg);
assertThat(am.vote(joe, mi,
createAttributes(new PreInvocationExpressionAttribute(
"(filterObject == 'jim')", "collection", null))))
.isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);
// All objects should have been removed, because the expression is always false
assertThat(arg).isEmpty();
}
@Test
public void collectionPreFilteringIsSuccessful() throws Exception {
List arg = createCollectionArg("joe", "bob", "sam");
MethodInvocation mi = new SimpleMethodInvocation(new TargetImpl(),
methodTakingACollection(), arg);
am.vote(joe, mi, createAttributes(new PreInvocationExpressionAttribute(
"(filterObject == 'joe' or filterObject == 'sam')", "collection",
"permitAll")));
assertThat(arg).containsExactly("joe","sam");
}
@Test(expected = IllegalArgumentException.class)
public void arraysCannotBePrefiltered() throws Exception {
MethodInvocation mi = new SimpleMethodInvocation(new TargetImpl(),
methodTakingAnArray(), createArrayArg("sam", "joe"));
am.vote(joe, mi, createAttributes(new PreInvocationExpressionAttribute(
"(filterObject == 'jim')", "someArray", null)));
}
@Test(expected = IllegalArgumentException.class)
public void incorrectFilterTargetNameIsRejected() throws Exception {
MethodInvocation mi = new SimpleMethodInvocation(new TargetImpl(),
methodTakingACollection(), createCollectionArg("joe", "bob"));
am.vote(joe, mi, createAttributes(new PreInvocationExpressionAttribute(
"(filterObject == 'joe')", "collcetion", null)));
}
@Test(expected = IllegalArgumentException.class)
public void nullNamedFilterTargetIsRejected() throws Exception {
MethodInvocation mi = new SimpleMethodInvocation(new TargetImpl(),
methodTakingACollection(), new Object[] { null });
am.vote(joe, mi, createAttributes(new PreInvocationExpressionAttribute(
"(filterObject == 'joe')", "collection", null)));
}
@Test
public void ruleDefinedInAClassMethodIsApplied() throws Exception {
MethodInvocation mi = new SimpleMethodInvocation(new TargetImpl(),
methodTakingAString(), "joe");
assertThat(
am.vote(joe, mi,
createAttributes(new PreInvocationExpressionAttribute(null, null,
"T(org.springframework.security.access.expression.method.SecurityRules).isJoe(#argument)"))))
.isEqualTo(AccessDecisionVoter.ACCESS_GRANTED);
}
private List<ConfigAttribute> createAttributes(ConfigAttribute... attributes) {
return Arrays.asList(attributes);
}
private List createCollectionArg(Object... elts) {
ArrayList result = new ArrayList(elts.length);
result.addAll(Arrays.asList(elts));
return result;
}
private Object createArrayArg(Object... elts) {
ArrayList result = new ArrayList(elts.length);
result.addAll(Arrays.asList(elts));
return result.toArray(new Object[0]);
}
private Method methodTakingAnArray() throws Exception {
return Target.class.getMethod("methodTakingAnArray", Object[].class);
}
private Method methodTakingAString() throws Exception {
return Target.class.getMethod("methodTakingAString", String.class);
}
private Method methodTakingACollection() throws Exception {
return Target.class.getMethod("methodTakingACollection", Collection.class);
}
// ~ Inner Classes
// ==================================================================================================
private interface Target {
void methodTakingAnArray(Object[] args);
void methodTakingAString(String argument);
Collection methodTakingACollection(Collection collection);
}
private static class TargetImpl implements Target {
public void methodTakingAnArray(Object[] args) {
}
public void methodTakingAString(String argument) {
};
public Collection methodTakingACollection(Collection collection) {
return collection;
}
}
}