Skip to content

Commit

Permalink
Move userId of parameter value to action
Browse files Browse the repository at this point in the history
Signed-off-by: Matt Sicker <boards@gmail.com>
  • Loading branch information
jvz committed Jul 24, 2019
1 parent 4685d62 commit 223098c
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.domains.Domain;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.EnvVars;
import hudson.Util;
Expand Down Expand Up @@ -43,39 +42,21 @@ public class CredentialsParameterValue extends ParameterValue {
*/
private final boolean isDefaultValue;

/**
* The user who supplied the parameter value.
*/
private transient final String userId;

@DataBoundConstructor
public CredentialsParameterValue(String name, String value, String description) {
this(name, value, description, false);
}

public CredentialsParameterValue(String name, String value, String description, boolean isDefaultValue) {
this(name, value, description, isDefaultValue, Jenkins.getAuthentication().getName());
}

CredentialsParameterValue(String name, String value, String description, boolean isDefaultValue, String userId) {
super(name, description);
this.value = value;
this.isDefaultValue = isDefaultValue;
this.userId = userId;
}

public String getValue() {
return value;
}

/**
* Returns the user id of who filled in this credentials parameter.
*/
@CheckForNull
String getUserId() {
return userId;
}

/**
* {@inheritDoc}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

package com.cloudbees.plugins.credentials;

import hudson.model.Cause;
import hudson.model.InvisibleAction;
import hudson.model.ParameterValue;
import hudson.model.ParametersAction;
Expand All @@ -33,20 +34,49 @@
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* Contains a collection of {@link CredentialsParameterValue} values provided to a {@link Run}.
* When attached to a run, this action will import any CredentialsParameterValues contained in the run's optionally
* present {@link ParametersAction}.
* Invisible run action for tracking which user supplied which {@link CredentialsParameterValue} to a {@link Run}.
* This class exposes {@link #add(String, CredentialsParameterValue)} and {@link #addFromParameterValues(String, Collection)}
* for adding these parameter values to an existing action. When this action is attached to a run, this action imports
* any CredentialsParameterValues contained in the run's {@link ParametersAction} and associates it to the optionally
* present {@link hudson.model.Cause.UserIdCause}. Parameters without a user id cannot reference
* {@linkplain CredentialsScope#USER user-scoped credentials}.
*
* @since TODO
*/
public class CredentialsParametersAction extends InvisibleAction implements RunAction2, Iterable<CredentialsParameterValue> {
public class CredentialsParametersAction extends InvisibleAction implements RunAction2 {

private final Map<String, CredentialsParameterValue> values = new ConcurrentHashMap<>();
/**
* Maps a user id to a CredentialsParameterValue to indicate the user performing the credential action.
*/
static class AuthenticatedCredentials {
private final String userId;
private final CredentialsParameterValue parameterValue;

private AuthenticatedCredentials(@CheckForNull String userId, @Nonnull CredentialsParameterValue parameterValue) {
this.userId = userId;
this.parameterValue = parameterValue;
}

/**
* @return the effective user id of who supplied this credentials parameter value
*/
String getUserId() {
return userId;
}

/**
* @return the credentials parameter value supplied
*/
CredentialsParameterValue getParameterValue() {
return parameterValue;
}
}

private final Map<String, AuthenticatedCredentials> values = new ConcurrentHashMap<>();

/**
* Looks up an existing CredentialsParameterAction for a Run or adapts and attaches an existing ParametersAction to
Expand All @@ -68,30 +98,30 @@ public class CredentialsParametersAction extends InvisibleAction implements RunA
/**
* Adds a CredentialsParameterValue to this action if it does not already exist.
*/
public void add(@Nonnull CredentialsParameterValue value) {
values.put(value.getName(), value);
public void add(@CheckForNull String userId, @Nonnull CredentialsParameterValue parameterValue) {
values.put(parameterValue.getName(), new AuthenticatedCredentials(userId, parameterValue));
}

/**
* Adds CredentialsParameterValues from a collection of ParameterValues. Shortcut for filtering and adding items
* via {@link #add(CredentialsParameterValue)}.
* Bulk add of CredentialsParameterValues from a heterogeneous collection of parameter values.
*/
public void addFromParameterValues(@Nonnull Collection<ParameterValue> values) {
values.stream()
public void addFromParameterValues(@CheckForNull String userId, @Nonnull Collection<ParameterValue> parameterValues) {
parameterValues.stream()
.filter(CredentialsParameterValue.class::isInstance)
.map(CredentialsParameterValue.class::cast)
.forEach(this::add);
.forEach(parameterValue -> add(userId, parameterValue));
}

@CheckForNull CredentialsParameterValue findParameterByName(@Nonnull String name) {
@CheckForNull AuthenticatedCredentials findCredentialsByParameterName(@Nonnull String name) {
return values.get(name);
}

@Override
public void onAttached(Run<?, ?> r) {
final ParametersAction action = r.getAction(ParametersAction.class);
if (action != null) {
addFromParameterValues(action.getParameters());
final Cause.UserIdCause cause = r.getCause(Cause.UserIdCause.class);
addFromParameterValues(cause == null ? null : cause.getUserId(), action.getParameters());
}
}

Expand All @@ -100,8 +130,4 @@ public void onLoad(Run<?, ?> r) {
// this space intentionally left blank
}

@Override
public @Nonnull Iterator<CredentialsParameterValue> iterator() {
return values.values().iterator();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -880,17 +880,17 @@ public static <C extends IdCredentials> C findCredentialById(@NonNull String id,
String inputUserId = null;
final CredentialsParametersAction action = CredentialsParametersAction.forRun(run);
if (action != null) {
final CredentialsParameterValue parameter = id.startsWith("${") && id.endsWith("}") ?
final CredentialsParametersAction.AuthenticatedCredentials credentials = id.startsWith("${") && id.endsWith("}") ?
// denotes explicitly that this is a parameterized credential
action.findParameterByName(id.substring(2, id.length() - 1)) :
action.findCredentialsByParameterName(id.substring(2, id.length() - 1)) :
// otherwise, we can check to see if there is a matching credential parameter name that shadows an
// existing global credential id
action.findParameterByName(id);
if (parameter != null) {
action.findCredentialsByParameterName(id);
if (credentials != null) {
isParameter = true;
isDefaultValue = parameter.isDefaultValue();
id = Util.fixNull(parameter.getValue());
inputUserId = parameter.getUserId();
inputUserId = credentials.getUserId();
isDefaultValue = credentials.getParameterValue().isDefaultValue();
id = Util.fixNull(credentials.getParameterValue().getValue());
}
}
// non parameters or default parameter values can only come from the job's context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
import hudson.model.FreeStyleProject;
import hudson.model.ParametersAction;
import hudson.model.ParametersDefinitionProperty;
import hudson.model.User;
import hudson.security.ACL;
import hudson.security.ACLContext;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
Expand All @@ -52,6 +55,11 @@ public static void setUpClass() throws IOException {
j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
CredentialsProvider.lookupStores(j.jenkins).iterator().next()
.addCredentials(Domain.global(), new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, "cred-id", "global credential", "root", "correct horse battery staple"));
final User alpha = User.getOrCreateByIdOrFullName("alpha");
try (ACLContext ignored = ACL.as(alpha)) {
CredentialsProvider.lookupStores(alpha).iterator().next()
.addCredentials(Domain.global(), new UsernamePasswordCredentialsImpl(CredentialsScope.USER, "alpha-cred-id", "user credentials", "root", "hello world"));
}
}

@Test
Expand All @@ -65,10 +73,48 @@ public void forRunReturnsNullWhenRunHasNoParameters() throws Exception {
public void forRunCopiesParametersWhenRunIsParameterized() throws Exception {
final FreeStyleProject project = j.createFreeStyleProject();
project.addProperty(new ParametersDefinitionProperty(new CredentialsParameterDefinition("cred", "", null, IdCredentials.class.getName(), true)));
final CredentialsParameterValue parameterValue = new CredentialsParameterValue("cred", "cred-id", "", false, "alpha");
final FreeStyleBuild build = j.assertBuildStatusSuccess(project.scheduleBuild2(0, new Cause.UserIdCause("alpha"), new ParametersAction(parameterValue)));
final FreeStyleBuild build = j.assertBuildStatusSuccess(project.scheduleBuild2(0, new Cause.UserIdCause("alpha"), forParameter("cred-id")));
final CredentialsParametersAction action = CredentialsParametersAction.forRun(build);
assertNotNull(action);
assertEquals(parameterValue, action.findParameterByName("cred"));
final CredentialsParametersAction.AuthenticatedCredentials creds = action.findCredentialsByParameterName("cred");
assertNotNull(creds);
assertEquals("alpha", creds.getUserId());
assertEquals("cred-id", creds.getParameterValue().getValue());
}

@Test
public void forRunUsesUserIdCauseForInitialParametersOwnership() throws Exception {
final FreeStyleProject project = j.createFreeStyleProject();
project.addProperty(new ParametersDefinitionProperty(new CredentialsParameterDefinition("cred", "", null, IdCredentials.class.getName(), true)));

// no cause means no user id
{
final CredentialsParametersAction action = CredentialsParametersAction.forRun(
j.assertBuildStatusSuccess(
project.scheduleBuild2(0,
forParameter("cred-id"))));
assertNotNull(action);
final CredentialsParametersAction.AuthenticatedCredentials creds = action.findCredentialsByParameterName("cred");
assertNotNull(creds);
assertNull(creds.getUserId());
assertEquals("cred-id", creds.getParameterValue().getValue());
}

// cause of type UserIdCause means that user id
{
final CredentialsParametersAction action = CredentialsParametersAction.forRun(
j.assertBuildStatusSuccess(
project.scheduleBuild2(0,
new Cause.UserIdCause("alpha"), forParameter("alpha-cred-id"))));
assertNotNull(action);
final CredentialsParametersAction.AuthenticatedCredentials creds = action.findCredentialsByParameterName("cred");
assertNotNull(creds);
assertEquals("alpha", creds.getUserId());
assertEquals("alpha-cred-id", creds.getParameterValue().getValue());
}
}

private static ParametersAction forParameter(String credentialsId) {
return new ParametersAction(new CredentialsParameterValue("cred", credentialsId, null, false));
}
}

0 comments on commit 223098c

Please sign in to comment.