Skip to content

Commit

Permalink
[JENKINS-23468] Permit multiple variables per binding (not yet used).
Browse files Browse the repository at this point in the history
  • Loading branch information
jglick committed Jan 8, 2015
1 parent 4e3e96b commit 595d5b9
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 111 deletions.
19 changes: 2 additions & 17 deletions pom.xml
Expand Up @@ -5,7 +5,7 @@
<parent> <parent>
<groupId>org.jenkins-ci.plugins</groupId> <groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId> <artifactId>plugin</artifactId>
<version>1.532.2</version> <version>1.580.1</version>
</parent> </parent>


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


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

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


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

</project> </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; package org.jenkinsci.plugins.credentialsbinding;


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


import hudson.model.Job; import hudson.model.Run;
import hudson.security.ACL; import hudson.model.TaskListener;
import hudson.util.VariableResolver; import java.util.Collections;
import jenkins.model.Jenkins; import java.util.Map;
import org.acegisecurity.Authentication; import java.util.Set;
import org.kohsuke.stapler.DataBoundConstructor; 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 * @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 variable;
private final String credentialsId;


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


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

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


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

/** Callback for processing during a build. */ /** Callback for processing during a build. */
public interface Environment { 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. */ /** Sets up bindings for a build. */
public abstract Environment bind(@Nonnull AbstractBuild build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException; 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);
* Looks up the actual credentials. } else {
* @param build the build. throw new AbstractMethodError("you must override bindSingle");
* @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;
} }
throw new FileNotFoundException(credentialsId);
} }


@Override public BindingDescriptor<C> getDescriptor() { @Override public final MultiEnvironment bind(Run<?,?> build, FilePath workspace, Launcher launcher, TaskListener listener) throws IOException, InterruptedException {
return (BindingDescriptor<C>) super.getDescriptor(); 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; import org.kohsuke.stapler.AncestorInPath;


/** /**
* Describes a {@link Binding} kind. * Describes a {@link MultiBinding} kind.
* @param <C> type of credentials to be bound * @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(); 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.Extension;
import hudson.FilePath; import hudson.FilePath;
import hudson.Launcher; import hudson.Launcher;
import hudson.model.AbstractBuild; import hudson.model.Computer;
import hudson.model.BuildListener; import hudson.model.Node;
import hudson.model.Run;
import hudson.model.TaskListener;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; import java.util.UUID;


Expand All @@ -47,14 +49,17 @@ public class FileBinding extends Binding<FileCredentials> {
return FileCredentials.class; 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); 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()); final FilePath dir = secrets.child(UUID.randomUUID().toString());
dir.mkdirs(); dir.mkdirs();
secrets.chmod(/*0700*/448); secrets.chmod(/*0700*/448);
final FilePath secret = dir.child(credentials.getFileName()); final FilePath secret = dir.child(credentials.getFileName());
secret.copyFrom(credentials.getContent()); copy(secret, credentials);
return new Environment() { return new Environment() {
@Override public String value() { @Override public String value() {
return secret.getRemote(); 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> { @Extension public static class DescriptorImpl extends BindingDescriptor<FileCredentials> {


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


@SuppressWarnings({"rawtypes", "unchecked"}) // inherited from BuildWrapper
public class SecretBuildWrapper extends 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; this.bindings = bindings;
} }


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


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


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


Expand Down

0 comments on commit 595d5b9

Please sign in to comment.