package net.sourceforge.stripes.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import net.sourceforge.stripes.FilterEnabledTestBase;
import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.StrictBinding;
import net.sourceforge.stripes.action.StrictBinding.Policy;
import net.sourceforge.stripes.exception.StripesRuntimeException;
import net.sourceforge.stripes.mock.MockRoundtrip;
import net.sourceforge.stripes.util.Log;
import net.sourceforge.stripes.util.bean.PropertyExpression;
import net.sourceforge.stripes.util.bean.PropertyExpressionEvaluation;
import net.sourceforge.stripes.validation.Validate;
import net.sourceforge.stripes.validation.ValidateNestedProperties;
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* Tests binding security.
*/
public class BindingSecurityTests extends FilterEnabledTestBase {
public static class NoAnnotation implements ActionBean {
private ActionBeanContext context;
public String[] getTestProperties() {
return new String[] { "foo", "bar", "baz" };
}
public boolean[] getExpectSuccess() {
return new boolean[] { true, true, true };
}
public ActionBeanContext getContext() {
return context;
}
public void setContext(ActionBeanContext context) {
this.context = context;
}
private String foo, bar, baz;
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
public String getBaz() {
return baz;
}
public void setBaz(String baz) {
this.baz = baz;
}
@DefaultHandler
public Resolution execute() {
return null;
}
}
@StrictBinding
public static class DefaultAnnotation extends BindingSecurityTests.NoAnnotation {
@Override
public boolean[] getExpectSuccess() {
return new boolean[] { false, false, false };
}
}
@StrictBinding(allow = "foo,bar")
public static class ImplicitDeny extends BindingSecurityTests.NoAnnotation {
@Override
public boolean[] getExpectSuccess() {
return new boolean[] { true, true, false };
}
}
@StrictBinding(allow = "foo,bar,baz", deny = "baz,baz.**")
public static class ExplicitDeny extends BindingSecurityTests.NoAnnotation {
@Override
public boolean[] getExpectSuccess() {
return new boolean[] { true, true, false };
}
}
@StrictBinding(defaultPolicy = Policy.ALLOW)
public static class ImplicitAllow extends BindingSecurityTests.NoAnnotation {
@Override
public boolean[] getExpectSuccess() {
return new boolean[] { true, true, true };
}
}
public static class Blah {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@StrictBinding
public static class HonorValidateAnnotations extends BindingSecurityTests.NoAnnotation {
@Override
public boolean[] getExpectSuccess() {
return new boolean[] { true, true, true, true, true };
}
@Override
public String[] getTestProperties() {
return new String[] { "foo", "bar", "baz", "blah", "blah.name" };
}
@Validate
private String foo;
private String bar;
private String baz;
@ValidateNestedProperties(@Validate(field = "name"))
private Blah blah;
@Override
public String getFoo() {
return foo;
}
@Override
public void setFoo(String foo) {
this.foo = foo;
}
@Validate
@Override
public String getBar() {
return bar;
}
@Override
public void setBar(String bar) {
this.bar = bar;
}
@Override
public String getBaz() {
return baz;
}
@Validate
@Override
public void setBaz(String baz) {
this.baz = baz;
}
public Blah getBlah() {
return blah;
}
public void setBlah(Blah blah) {
this.blah = blah;
}
}
@StrictBinding(deny = "**")
public static class OverrideValidateAnnotations extends
BindingSecurityTests.HonorValidateAnnotations {
@Override
public boolean[] getExpectSuccess() {
return new boolean[] { false, false, false, false, false };
}
}
private static final Log log = Log.getInstance(BindingSecurityTests.class);
@Test(groups = "fast")
public void bindingPolicyEnforcement() {
try {
evaluate(new NoAnnotation());
evaluate(new DefaultAnnotation());
evaluate(new ImplicitDeny());
evaluate(new ExplicitDeny());
evaluate(new ImplicitAllow());
evaluate(new HonorValidateAnnotations());
evaluate(new OverrideValidateAnnotations());
}
catch (Exception e) {
StripesRuntimeException re = new StripesRuntimeException(e.getMessage(), e);
re.setStackTrace(e.getStackTrace());
throw re;
}
}
public void evaluate(NoAnnotation bean) throws Exception {
String[] properties = bean.getTestProperties();
boolean[] expect = bean.getExpectSuccess();
Class<? extends NoAnnotation> beanType = bean.getClass();
MockRoundtrip trip = new MockRoundtrip(getMockServletContext(), beanType);
for (String p : properties)
trip.addParameter(p, p + "Value");
trip.execute();
bean = trip.getActionBean(beanType);
for (int i = 0; i < properties.length; i++) {
String fullName = beanType.getSimpleName() + "." + properties[i];
log.debug("Testing binding security on ", fullName);
PropertyExpression pe = PropertyExpression.getExpression(properties[i]);
PropertyExpressionEvaluation eval = new PropertyExpressionEvaluation(pe, bean);
Object value = eval.getValue();
Assert.assertEquals(value != null, expect[i], "Property " + fullName + " should"
+ (expect[i] ? " not" : "") + " be null but it is" + (expect[i] ? "" : " not"));
}
}
@Test(groups = "fast")
@SuppressWarnings("unused")
public void protectedClasses() {
class TestBean implements ActionBean {
public void setContext(ActionBeanContext context) {
}
public ClassLoader getClassLoader() {
return null;
}
public ActionBeanContext getContext() {
return null;
}
public HttpServletRequest getRequest() {
return null;
}
public HttpServletResponse getResponse() {
return null;
}
public HttpSession getSession() {
return null;
}
public TestBean getOther() {
return null;
}
}
final String[] expressions = {
// Direct, single node
"class",
"classLoader",
"context",
"request",
"response",
"session",
// Indirect, last node
"other.class",
"other.classLoader",
"other.context",
"other.request",
"other.response",
"other.session",
// Indirect, not first node, not last node
"other.class.name",
"other.request.cookies",
"other.session.id",
};
final TestBean bean = new TestBean();
final BindingPolicyManager bpm = new BindingPolicyManager(TestBean.class);
for (String expression : expressions) {
log.debug("Testing illegal expression: " + expression);
PropertyExpression pe = PropertyExpression.getExpression(expression);
PropertyExpressionEvaluation eval = new PropertyExpressionEvaluation(pe, bean);
Assert.assertFalse(bpm.isBindingAllowed(eval), "Binding should not be allowed for expression " + expression);
}
}
public static void main(String[] args) {
BindingSecurityTests tests = new BindingSecurityTests();
try {
tests.initCtx();
tests.bindingPolicyEnforcement();
tests.protectedClasses();
} finally {
tests.closeCtx();
}
}
}