Skip to content

Commit

Permalink
Merge pull request #2 from jenkinsci/multivars-JENKINS-23468
Browse files Browse the repository at this point in the history
[FIXED JENKINS-23468] Permit multiple variables per binding
  • Loading branch information
jglick committed Jan 12, 2015
2 parents 4e3e96b + 6f03caf commit 91e5d8e
Show file tree
Hide file tree
Showing 20 changed files with 442 additions and 116 deletions.
19 changes: 2 additions & 17 deletions pom.xml
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>1.532.2</version>
<version>1.580.1</version>
</parent>

<groupId>org.jenkins-ci.plugins</groupId>
Expand All @@ -31,10 +31,6 @@
<tag>HEAD</tag>
</scm>

<properties>
<maven-hpi-plugin.version>1.99</maven-hpi-plugin.version>
</properties>

<repositories>
<repository>
<id>repo.jenkins-ci.org</id>
Expand All @@ -52,7 +48,7 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>credentials</artifactId>
<version>1.16.1</version>
<version>1.20</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
Expand All @@ -61,15 +57,4 @@
</dependency>
</dependencies>

<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-release-plugin</artifactId>
<version>2.5</version>
</plugin>
</plugins>
</pluginManagement>
</build>

</project>
81 changes: 40 additions & 41 deletions src/main/java/org/jenkinsci/plugins/credentialsbinding/Binding.java
Expand Up @@ -24,57 +24,41 @@

package org.jenkinsci.plugins.credentialsbinding;

import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import hudson.ExtensionPoint;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.model.AbstractBuild;
import hudson.model.AbstractDescribableImpl;
import hudson.model.BuildListener;
import hudson.model.Cause;
import hudson.model.Item;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import javax.annotation.Nonnull;

import hudson.model.Job;
import hudson.security.ACL;
import hudson.util.VariableResolver;
import jenkins.model.Jenkins;
import org.acegisecurity.Authentication;
import hudson.model.Run;
import hudson.model.TaskListener;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.kohsuke.stapler.DataBoundConstructor;

/**
* A way of binding a kind of credentials to an environment variable during a build.
* A binding of a single variable.
* @param <C> a kind of credentials
*/
public abstract class Binding<C extends StandardCredentials> extends AbstractDescribableImpl<Binding<C>> implements ExtensionPoint {
public abstract class Binding<C extends StandardCredentials> extends MultiBinding<C> {

private final String variable;
private final String credentialsId;

/** For use with {@link DataBoundConstructor}. */
protected Binding(String variable, String credentialsId) {
super(credentialsId);
this.variable = variable;
this.credentialsId = credentialsId;
}

/** Type token. */
protected abstract Class<C> type();

/** Environment variable name. */
public String getVariable() {
return variable;
}

/** Identifier of the credentials to be bound. */
public String getCredentialsId() {
return credentialsId;
}

/** Callback for processing during a build. */
public interface Environment {

Expand All @@ -86,25 +70,40 @@ public interface Environment {

}

@Deprecated
@SuppressWarnings("rawtypes")
public Environment bind(@Nonnull AbstractBuild build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException {
return bindSingle(build, build.getWorkspace(), launcher, listener);
}

/** Sets up bindings for a build. */
public abstract Environment bind(@Nonnull AbstractBuild build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException;

/**
* Looks up the actual credentials.
* @param build the build.
* @return the credentials
* @throws FileNotFoundException if the credentials could not be found (for convenience, rather than returning null)
*/
protected final @Nonnull C getCredentials(@Nonnull AbstractBuild<?,?> build) throws IOException {
C c = CredentialsProvider.findCredentialById(credentialsId, type(), build);
if (c != null) {
return c;
public /* abstract */Environment bindSingle(@Nonnull Run<?,?> build, FilePath workspace, Launcher launcher, TaskListener listener) throws IOException, InterruptedException {
if (Util.isOverridden(Binding.class, getClass(), "bind", AbstractBuild.class, Launcher.class, BuildListener.class) && build instanceof AbstractBuild && listener instanceof BuildListener) {
return bind((AbstractBuild) build, launcher, (BuildListener) listener);
} else {
throw new AbstractMethodError("you must override bindSingle");
}
throw new FileNotFoundException(credentialsId);
}

@Override public BindingDescriptor<C> getDescriptor() {
return (BindingDescriptor<C>) super.getDescriptor();
@Override public final MultiEnvironment bind(Run<?,?> build, FilePath workspace, Launcher launcher, TaskListener listener) throws IOException, InterruptedException {
final Environment single = bindSingle(build, workspace, launcher, listener);
return new MultiEnvironment() {
public Map<String,String> values() {
return Collections.singletonMap(variable, single.value());
}
public void unbind() throws IOException, InterruptedException {
single.unbind();
}
};
}

@Override public final Set<String> variables() {
return Collections.singleton(variable);
}

@Deprecated
protected final @Nonnull C getCredentials(@Nonnull AbstractBuild<?,?> build) throws IOException {
return super.getCredentials(build);
}

}
Expand Up @@ -37,10 +37,10 @@
import org.kohsuke.stapler.AncestorInPath;

/**
* Describes a {@link Binding} kind.
* Describes a {@link MultiBinding} kind.
* @param <C> type of credentials to be bound
*/
public abstract class BindingDescriptor<C extends StandardCredentials> extends Descriptor<Binding<C>> {
public abstract class BindingDescriptor<C extends StandardCredentials> extends Descriptor<MultiBinding<C>> {

protected abstract Class<C> type();

Expand Down
@@ -0,0 +1,98 @@
/*
* The MIT License
*
* Copyright 2015 Jesse Glick.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package org.jenkinsci.plugins.credentialsbinding;

import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import hudson.ExtensionPoint;
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Run;
import hudson.model.TaskListener;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nonnull;
import org.kohsuke.stapler.DataBoundConstructor;

/**
* A way of binding a kind of credentials to an environment variable during a build.
* @param <C> a kind of credentials
*/
public abstract class MultiBinding<C extends StandardCredentials> extends AbstractDescribableImpl<MultiBinding<C>> implements ExtensionPoint {

private final String credentialsId;

/** For use with {@link DataBoundConstructor}. */
protected MultiBinding(String credentialsId) {
this.credentialsId = credentialsId;
}

/** Type token. */
protected abstract Class<C> type();

/** Identifier of the credentials to be bound. */
public final String getCredentialsId() {
return credentialsId;
}

/** Callback for processing during a build. */
public interface MultiEnvironment {

/** Produces the value of the environment variables. */
Map<String,String> values();

/** Performs any needed cleanup. */
void unbind() throws IOException, InterruptedException;

}

/** Sets up bindings for a build. */
public abstract MultiEnvironment bind(@Nonnull Run<?,?> build, FilePath workspace, Launcher launcher, TaskListener listener) throws IOException, InterruptedException;

/** Defines keys expected to be set in {@link MultiEnvironment#values}, particularly any that might be sensitive. */
public abstract Set<String> variables();

/**
* Looks up the actual credentials.
* @param build the build.
* @return the credentials
* @throws FileNotFoundException if the credentials could not be found (for convenience, rather than returning null)
*/
protected final @Nonnull C getCredentials(@Nonnull Run<?,?> build) throws IOException {
C c = CredentialsProvider.findCredentialById(credentialsId, type(), build);
if (c != null) {
return c;
}
throw new FileNotFoundException(credentialsId);
}

@Override public BindingDescriptor<C> getDescriptor() {
return (BindingDescriptor<C>) super.getDescriptor();
}

}
Expand Up @@ -27,8 +27,10 @@
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.model.Computer;
import hudson.model.Node;
import hudson.model.Run;
import hudson.model.TaskListener;
import java.io.IOException;
import java.util.UUID;

Expand All @@ -47,14 +49,17 @@ public class FileBinding extends Binding<FileCredentials> {
return FileCredentials.class;
}

@Override public Environment bind(AbstractBuild build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException {
@Override public Environment bindSingle(Run<?,?> build, FilePath workspace, Launcher launcher, TaskListener listener) throws IOException, InterruptedException {
FileCredentials credentials = getCredentials(build);
FilePath secrets = build.getBuiltOn().getRootPath().child("secretFiles");
Computer computer = workspace.toComputer();
Node node = computer == null ? null : computer.getNode();
FilePath root = node == null ? workspace : node.getRootPath();
FilePath secrets = root.child("secretFiles");
final FilePath dir = secrets.child(UUID.randomUUID().toString());
dir.mkdirs();
secrets.chmod(/*0700*/448);
final FilePath secret = dir.child(credentials.getFileName());
secret.copyFrom(credentials.getContent());
copy(secret, credentials);
return new Environment() {
@Override public String value() {
return secret.getRemote();
Expand All @@ -65,6 +70,10 @@ public class FileBinding extends Binding<FileCredentials> {
};
}

protected void copy(FilePath secret, FileCredentials credentials) throws IOException, InterruptedException {
secret.copyFrom(credentials.getContent());
}

@Extension public static class DescriptorImpl extends BindingDescriptor<FileCredentials> {

@Override protected Class<FileCredentials> type() {
Expand Down
Expand Up @@ -32,48 +32,49 @@
import hudson.tasks.BuildWrapper;
import hudson.tasks.BuildWrapperDescriptor;
import java.io.IOException;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jenkinsci.plugins.credentialsbinding.Binding;
import org.jenkinsci.plugins.credentialsbinding.MultiBinding;
import org.kohsuke.stapler.DataBoundConstructor;

@SuppressWarnings({"rawtypes", "unchecked"}) // inherited from BuildWrapper
public class SecretBuildWrapper extends BuildWrapper {

private final List<Binding<?>> bindings;
private final List<? extends MultiBinding<?>> bindings;

@DataBoundConstructor public SecretBuildWrapper(List<Binding<?>> bindings) {
@DataBoundConstructor public SecretBuildWrapper(List<? extends MultiBinding<?>> bindings) {
this.bindings = bindings;
}

public List<Binding<?>> getBindings() {
public List<? extends MultiBinding<?>> getBindings() {
return bindings;
}

@Override public Environment setUp(AbstractBuild build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException {
final Map<String,Binding.Environment> m = new HashMap<String,Binding.Environment>();
for (Binding binding : bindings) {
m.put(binding.getVariable(), binding.bind(build, launcher, listener));
final List<MultiBinding.MultiEnvironment> m = new ArrayList<MultiBinding.MultiEnvironment>();
for (MultiBinding binding : bindings) {
m.add(binding.bind(build, build.getWorkspace(), launcher, listener));
}
return new Environment() {
@Override public void buildEnvVars(Map<String,String> env) {
for (Map.Entry<String,Binding.Environment> entry : m.entrySet()) {
env.put(entry.getKey(), entry.getValue().value());
for (MultiBinding.MultiEnvironment e : m) {
env.putAll(e.values());
}
}
@Override public boolean tearDown(AbstractBuild build, BuildListener listener) throws IOException, InterruptedException {
for (Binding.Environment env : m.values()) {
env.unbind();
for (MultiBinding.MultiEnvironment e : m) {
e.unbind();
}
return true;
}
};
}

@Override public void makeSensitiveBuildVariables(AbstractBuild build, Set<String> sensitiveVariables) {
for (Binding binding : bindings) {
sensitiveVariables.add(binding.getVariable());
for (MultiBinding binding : bindings) {
sensitiveVariables.addAll(binding.variables());
}
}

Expand Down

0 comments on commit 91e5d8e

Please sign in to comment.