diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index 75601792c7d414ce42aa039065bff0947cd6b8c0..6e9f67369db94b8b049d8048b64cf2d18541c40d 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -153,6 +153,7 @@ import org.kohsuke.stapler.jelly.InternationalizedStringExpression.RawHtmlArgume import com.google.common.base.Predicate; import com.google.common.base.Predicates; +import hudson.model.PasswordParameterDefinition; import hudson.util.RunList; import java.util.concurrent.atomic.AtomicLong; import javax.annotation.CheckForNull; @@ -1756,7 +1757,7 @@ public class Functions { } return ((Secret) o).getEncryptedValue(); } - if (getIsUnitTest()) { + if (getIsUnitTest() && !o.equals(PasswordParameterDefinition.DEFAULT_VALUE)) { throw new SecurityException("attempted to render plaintext ‘" + o + "’ in password field; use a getter of type Secret instead"); } return o.toString(); diff --git a/core/src/main/java/hudson/model/PasswordParameterDefinition.java b/core/src/main/java/hudson/model/PasswordParameterDefinition.java index f4cad5a8c32c2e86369a736d70ec8b0139cc1215..f3dfe793562c6d548e804592cb503e611521e36e 100644 --- a/core/src/main/java/hudson/model/PasswordParameterDefinition.java +++ b/core/src/main/java/hudson/model/PasswordParameterDefinition.java @@ -31,6 +31,7 @@ import hudson.Extension; import hudson.util.Secret; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; +import org.kohsuke.accmod.restrictions.NoExternalUse; /** * Parameter whose value is a {@link Secret} and is hidden from the UI. @@ -40,6 +41,9 @@ import org.kohsuke.accmod.restrictions.DoNotUse; */ public class PasswordParameterDefinition extends SimpleParameterDefinition { + @Restricted(NoExternalUse.class) + public static final String DEFAULT_VALUE = ""; + private Secret defaultValue; @DataBoundConstructor @@ -66,6 +70,9 @@ public class PasswordParameterDefinition extends SimpleParameterDefinition { @Override public PasswordParameterValue createValue(StaplerRequest req, JSONObject jo) { PasswordParameterValue value = req.bindJSON(PasswordParameterValue.class, jo); + if (value.getValue().getPlainText().equals(DEFAULT_VALUE)) { + value = new PasswordParameterValue(getName(), getDefaultValue()); + } value.setDescription(getDescription()); return value; } diff --git a/core/src/main/resources/hudson/model/PasswordParameterDefinition/index.jelly b/core/src/main/resources/hudson/model/PasswordParameterDefinition/index.jelly index 9bac23608cc5a435122249e64f2429574c9cbe8b..81b8c0190ae2a9bca17389a5205e2723999564d6 100644 --- a/core/src/main/resources/hudson/model/PasswordParameterDefinition/index.jelly +++ b/core/src/main/resources/hudson/model/PasswordParameterDefinition/index.jelly @@ -29,7 +29,7 @@ THE SOFTWARE.
- +
\ No newline at end of file diff --git a/test/src/test/java/hudson/model/PasswordParameterDefinitionTest.java b/test/src/test/java/hudson/model/PasswordParameterDefinitionTest.java index 8b4dcb2906e8b8ff34e642804ce9fcc4b44d8715..2129c9fce9b726eda9aa91362a52fd7c326fbd43 100644 --- a/test/src/test/java/hudson/model/PasswordParameterDefinitionTest.java +++ b/test/src/test/java/hudson/model/PasswordParameterDefinitionTest.java @@ -24,10 +24,18 @@ package hudson.model; +import com.gargoylesoftware.htmlunit.html.HtmlForm; +import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; +import hudson.Launcher; +import java.io.IOException; +import jenkins.model.Jenkins; import static org.junit.Assert.assertEquals; 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.MockAuthorizationStrategy; +import org.jvnet.hudson.test.TestBuilder; public class PasswordParameterDefinitionTest { @@ -40,4 +48,52 @@ public class PasswordParameterDefinitionTest { assertEquals("s3cr3t", ((PasswordParameterDefinition) p.getProperty(ParametersDefinitionProperty.class).getParameterDefinition("p")).getDefaultValue()); } + @Issue("JENKINS-36476") + @Test public void defaultValueAlwaysAvailable() throws Exception { + j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); + j.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy(). + grant(Jenkins.ADMINISTER).everywhere().to("admin"). + grant(Jenkins.READ, Item.READ, Item.BUILD).everywhere().to("dev")); + FreeStyleProject p = j.createFreeStyleProject(); + p.addProperty(new ParametersDefinitionProperty(new PasswordParameterDefinition("secret", "s3cr3t", ""))); + p.getBuildersList().add(new TestBuilder() { + @Override public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { + listener.getLogger().println("I heard about a " + build.getEnvironment(listener).get("secret") + "!"); + return true; + } + }); + JenkinsRule.WebClient wc = j.createWebClient(); + wc.getOptions().setThrowExceptionOnFailingStatusCode(false); // ParametersDefinitionProperty/index.jelly sends a 405 but really it is OK + // Control case: admin can use default value. + j.submit(wc.login("admin").getPage(p, "build?delay=0sec").getFormByName("parameters")); + j.waitUntilNoActivity(); + FreeStyleBuild b1 = p.getLastBuild(); + assertEquals(1, b1.getNumber()); + j.assertLogContains("I heard about a s3cr3t!", j.assertBuildStatusSuccess(b1)); + // Another control case: anyone can enter a different value. + HtmlForm form = wc.login("dev").getPage(p, "build?delay=0sec").getFormByName("parameters"); + HtmlPasswordInput input = form.getInputByName("value"); + input.setText("rumor"); + j.submit(form); + j.waitUntilNoActivity(); + FreeStyleBuild b2 = p.getLastBuild(); + assertEquals(2, b2.getNumber()); + j.assertLogContains("I heard about a rumor!", j.assertBuildStatusSuccess(b2)); + // Test case: anyone can use default value. + j.submit(wc.login("dev").getPage(p, "build?delay=0sec").getFormByName("parameters")); + j.waitUntilNoActivity(); + FreeStyleBuild b3 = p.getLastBuild(); + assertEquals(3, b3.getNumber()); + j.assertLogContains("I heard about a s3cr3t!", j.assertBuildStatusSuccess(b3)); + // Another control case: blank values. + form = wc.login("dev").getPage(p, "build?delay=0sec").getFormByName("parameters"); + input = form.getInputByName("value"); + input.setText(""); + j.submit(form); + j.waitUntilNoActivity(); + FreeStyleBuild b4 = p.getLastBuild(); + assertEquals(4, b4.getNumber()); + j.assertLogContains("I heard about a !", j.assertBuildStatusSuccess(b4)); + } + }