Skip to content

Commit

Permalink
[JENKINS-28440] Added tests to reproduce and explain JENKINS-28440.
Browse files Browse the repository at this point in the history
  • Loading branch information
ikedam committed May 19, 2015
1 parent 8373984 commit be67b45
Showing 1 changed file with 288 additions and 1 deletion.
289 changes: 288 additions & 1 deletion test/src/test/java/hudson/util/RobustReflectionConverterTest.java
Expand Up @@ -24,17 +24,41 @@

package hudson.util;

import hudson.cli.CLI;
import hudson.diagnosis.OldDataMonitor;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Items;
import hudson.model.JobProperty;
import hudson.model.JobPropertyDescriptor;
import hudson.model.Descriptor;
import hudson.model.FreeStyleProject;
import hudson.model.Job;
import hudson.model.Saveable;
import hudson.security.ACL;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;

import jenkins.model.Jenkins;
import static org.junit.Assert.*;
import net.sf.json.JSONObject;

import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.JenkinsRule.WebClient;
import org.jvnet.hudson.test.TestExtension;
import org.jvnet.hudson.test.recipes.LocalData;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;

import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.HttpMethod;
import com.gargoylesoftware.htmlunit.WebRequestSettings;

public class RobustReflectionConverterTest {

Expand All @@ -52,5 +76,268 @@ public class RobustReflectionConverterTest {
String text = data.values().iterator().next().extra;
assertTrue(text, text.contains("Could not call hudson.triggers.TimerTrigger.readResolve"));
}


// Testing describable object to demonstrate what is expected with RobustReflectionConverter#addCriticalField
// This should be configured with a specific keyword,
// and should reject configurations with other keywords.
// GUI related implementations (@DataBoundConstructor and newInstance) aren't used actually
// (no jelly files are provides and they don't work actually),
// but written to clarify a use case.
public static class AcceptOnlySpecificKeyword extends AbstractDescribableImpl<AcceptOnlySpecificKeyword>{
public static final String ACCEPT_KEYWORD = "accept";
private final String keyword;

@DataBoundConstructor
public AcceptOnlySpecificKeyword(String keyword) {
this.keyword = keyword;
}

public String getKeyword() {
return keyword;
}

public boolean isAcceptable() {
return ACCEPT_KEYWORD.equals(keyword);
}

public Object readResolve() throws Exception {
if (!ACL.SYSTEM.equals(Jenkins.getAuthentication())) {
// called via REST / CLI with authentication
if (!isAcceptable()) {
// Reject invalid configuration via REST / CLI.
throw new Exception(String.format("Bad keyword: %s", getKeyword()));
}
}
return this;
}

@TestExtension
public static class DescriptorImpl extends Descriptor<AcceptOnlySpecificKeyword> {
@Override
public String getDisplayName() {
return "AcceptOnlySpecificKeyword";
}

@Override
public AcceptOnlySpecificKeyword newInstance(StaplerRequest req, JSONObject formData)
throws FormException {
AcceptOnlySpecificKeyword instance = super.newInstance(req, formData);
if (!instance.isAcceptable()) {
throw new FormException(String.format("Bad keyword: %s", instance.getKeyword()), "keyword");
}
return instance;
}
}
}

public static class KeywordProperty extends JobProperty<Job<?,?>> {
private final AcceptOnlySpecificKeyword nonCriticalField;
private final AcceptOnlySpecificKeyword criticalField;

public KeywordProperty(AcceptOnlySpecificKeyword nonCriticalField, AcceptOnlySpecificKeyword criticalField) {
this.nonCriticalField = nonCriticalField;
this.criticalField = criticalField;
}

public AcceptOnlySpecificKeyword getNonCriticalField() {
return nonCriticalField;
}

public AcceptOnlySpecificKeyword getCriticalField() {
return criticalField;
}

@TestExtension
public static class DescriptorImpl extends JobPropertyDescriptor {
@Override
public String getDisplayName() {
return "KeywordProperty";
}

@Override
public JobProperty<?> newInstance(StaplerRequest req, JSONObject formData)
throws FormException {
// unfortunately, default newInstance bypasses newInstances for members.
formData = formData.getJSONObject("keywordProperty");
@SuppressWarnings("unchecked")
Descriptor<AcceptOnlySpecificKeyword> d = Jenkins.getInstance().getDescriptor(AcceptOnlySpecificKeyword.class);
return new KeywordProperty(
d.newInstance(req, formData.getJSONObject("nonCriticalField")),
d.newInstance(req, formData.getJSONObject("criticalField"))
);
}
}
}

private static final String CONFIGURATION_TEMPLATE =
"<?xml version='1.0' encoding='UTF-8'?>"
+ "<project>"
+ "<properties>"
+ "<hudson.util.RobustReflectionConverterTest_-KeywordProperty>"
+ "<nonCriticalField>"
+ "<keyword>%s</keyword>"
+ "</nonCriticalField>"
+ "<criticalField>"
+ "<keyword>%s</keyword>"
+ "</criticalField>"
+ "</hudson.util.RobustReflectionConverterTest_-KeywordProperty>"
+ "</properties>"
+ "</project>";

@Test
public void testRestInterfaceFailure() throws Exception {
Items.XSTREAM2.addCriticalField(KeywordProperty.class, "criticalField");

// without addCriticalField. This is accepted.
{
FreeStyleProject p = r.createFreeStyleProject();
p.addProperty(new KeywordProperty(
new AcceptOnlySpecificKeyword(AcceptOnlySpecificKeyword.ACCEPT_KEYWORD),
new AcceptOnlySpecificKeyword(AcceptOnlySpecificKeyword.ACCEPT_KEYWORD)
));
p.save();

// Configure a bad keyword via REST.
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
WebClient wc = r.createWebClient();
wc.login("test", "test");
WebRequestSettings req = new WebRequestSettings(
wc.createCrumbedUrl(String.format("%s/config.xml", p.getUrl())),
HttpMethod.POST
);
req.setRequestBody(String.format(CONFIGURATION_TEMPLATE, "badvalue", AcceptOnlySpecificKeyword.ACCEPT_KEYWORD));
wc.getPage(req);

// AcceptOnlySpecificKeyword with bad value is not instantiated for rejected with readResolve,
assertNull(p.getProperty(KeywordProperty.class).getNonCriticalField());
assertEquals(AcceptOnlySpecificKeyword.ACCEPT_KEYWORD, p.getProperty(KeywordProperty.class).getCriticalField().getKeyword());

// but save to the disk.
r.jenkins.reload();

p = r.jenkins.getItemByFullName(p.getFullName(), FreeStyleProject.class);
assertEquals("badvalue", p.getProperty(KeywordProperty.class).getNonCriticalField().getKeyword());
assertEquals(AcceptOnlySpecificKeyword.ACCEPT_KEYWORD, p.getProperty(KeywordProperty.class).getCriticalField().getKeyword());
}

// with addCriticalField. This is not accepted.
{
FreeStyleProject p = r.createFreeStyleProject();
p.addProperty(new KeywordProperty(
new AcceptOnlySpecificKeyword(AcceptOnlySpecificKeyword.ACCEPT_KEYWORD),
new AcceptOnlySpecificKeyword(AcceptOnlySpecificKeyword.ACCEPT_KEYWORD)
));
p.save();

// Configure a bad keyword via REST.
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
WebClient wc = r.createWebClient();
wc.login("test", "test");
WebRequestSettings req = new WebRequestSettings(
wc.createCrumbedUrl(String.format("%s/config.xml", p.getUrl())),
HttpMethod.POST
);
req.setRequestBody(String.format(CONFIGURATION_TEMPLATE, AcceptOnlySpecificKeyword.ACCEPT_KEYWORD, "badvalue"));

try {
wc.getPage(req);
// TODO: fail("Submitting unacceptable configuration via REST should fail.");
} catch (FailingHttpStatusCodeException e) {
// pass
}

// Configuration should not be updated for a failure of the critical field,
// TODO: assertNotEquals("badvalue", p.getProperty(KeywordProperty.class).getCriticalField().getKeyword());

r.jenkins.reload();

// rejected configuration is not saved
p = r.jenkins.getItemByFullName(p.getFullName(), FreeStyleProject.class);
assertNotEquals("badvalue", p.getProperty(KeywordProperty.class).getCriticalField().getKeyword());
}
}

@Test
public void testCliFailure() throws Exception {
Items.XSTREAM2.addCriticalField(KeywordProperty.class, "criticalField");

// without addCriticalField. This is accepted.
{
FreeStyleProject p = r.createFreeStyleProject();
p.addProperty(new KeywordProperty(
new AcceptOnlySpecificKeyword(AcceptOnlySpecificKeyword.ACCEPT_KEYWORD),
new AcceptOnlySpecificKeyword(AcceptOnlySpecificKeyword.ACCEPT_KEYWORD)
));
p.save();

// Configure a bad keyword via CLI.
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
CLI cli = new CLI(r.getURL());
ByteArrayOutputStream stdout = new ByteArrayOutputStream();
ByteArrayOutputStream stderr = new ByteArrayOutputStream();
int ret = cli.execute(
Arrays.asList(
"update-job",
p.getFullName(),
"--username",
"test",
"--password",
"test"
),
new ByteArrayInputStream(String.format(CONFIGURATION_TEMPLATE, "badvalue", AcceptOnlySpecificKeyword.ACCEPT_KEYWORD).getBytes()),
stdout,
stderr
);
assertEquals(0, ret);

// AcceptOnlySpecificKeyword with bad value is not instantiated for rejected with readResolve,
assertNull(p.getProperty(KeywordProperty.class).getNonCriticalField());
assertEquals(AcceptOnlySpecificKeyword.ACCEPT_KEYWORD, p.getProperty(KeywordProperty.class).getCriticalField().getKeyword());

// but save to the disk.
r.jenkins.reload();

p = r.jenkins.getItemByFullName(p.getFullName(), FreeStyleProject.class);
assertEquals("badvalue", p.getProperty(KeywordProperty.class).getNonCriticalField().getKeyword());
}

// with addCriticalField. This is not accepted.
{
FreeStyleProject p = r.createFreeStyleProject();
p.addProperty(new KeywordProperty(
new AcceptOnlySpecificKeyword(AcceptOnlySpecificKeyword.ACCEPT_KEYWORD),
new AcceptOnlySpecificKeyword(AcceptOnlySpecificKeyword.ACCEPT_KEYWORD)
));
p.save();

// Configure a bad keyword via CLI.
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
CLI cli = new CLI(r.getURL());
ByteArrayOutputStream stdout = new ByteArrayOutputStream();
ByteArrayOutputStream stderr = new ByteArrayOutputStream();
int ret = cli.execute(
Arrays.asList(
"update-job",
p.getFullName(),
"--username",
"test",
"--password",
"test"
),
new ByteArrayInputStream(String.format(CONFIGURATION_TEMPLATE, AcceptOnlySpecificKeyword.ACCEPT_KEYWORD, "badvalue").getBytes()),
stdout,
stderr
);
// TODO: assertNotEquals(0, ret);

// Configuration should not be updated for a failure of the critical field,
// TODO: assertNotEquals("badvalue", p.getProperty(KeywordProperty.class).getCriticalField().getKeyword());

r.jenkins.reload();

// rejected configuration is not saved
p = r.jenkins.getItemByFullName(p.getFullName(), FreeStyleProject.class);
assertNotEquals("badvalue", p.getProperty(KeywordProperty.class).getCriticalField().getKeyword());
}
}
}

0 comments on commit be67b45

Please sign in to comment.