Skip to content

Commit

Permalink
[JENKINS-37778] Support credentials in environment
Browse files Browse the repository at this point in the history
  • Loading branch information
rsandell committed Nov 3, 2016
1 parent 6e953c4 commit 5346000
Show file tree
Hide file tree
Showing 14 changed files with 965 additions and 76 deletions.
5 changes: 5 additions & 0 deletions pipeline-model-definition/pom.xml
Expand Up @@ -111,6 +111,11 @@
<artifactId>script-security</artifactId>
<version>1.22</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>credentials-binding</artifactId>
<version>1.9</version>
</dependency>

<!-- JSON schema stuff -->
<dependency>
Expand Down
Expand Up @@ -26,7 +26,7 @@ package org.jenkinsci.plugins.pipeline.modeldefinition.model
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString

import org.jenkinsci.plugins.pipeline.modeldefinition.steps.CredentialWrapper
import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.Whitelisted
import org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper

Expand Down Expand Up @@ -143,11 +143,22 @@ public class Root implements NestedModel, Serializable {
*/
@Whitelisted
List<String> getEnvVars() {
return environment.collect { k, v ->
return environment.findAll{k, v -> !(v instanceof CredentialWrapper)}.collect { k, v ->
"${k}=${v}"
}
}

@Whitelisted
Map<String, CredentialWrapper> getEnvCredentials() {
Map<String, CredentialWrapper> m = [:]
environment.each {k, v ->
if (v instanceof CredentialWrapper) {
m["${k}"] = v;
}
}
return m
}

/**
* Returns a list of notification closures whose conditions have been satisfied and should be run.
*
Expand Down
Expand Up @@ -26,8 +26,11 @@ package org.jenkinsci.plugins.pipeline.modeldefinition.model
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import org.jenkinsci.plugins.pipeline.modeldefinition.steps.CredentialWrapper
import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.Whitelisted

import javax.annotation.Nonnull

/**
* An individual stage to be executed within the build.
*
Expand Down Expand Up @@ -106,11 +109,23 @@ public class Stage implements NestedModel, Serializable {
* @return a list of "key=value" strings.
*/
List<String> getEnvVars() {
return environment.collect { k, v ->
return environment.findAll{k, v -> !(v instanceof CredentialWrapper)}.collect { k, v ->
"${k}=${v}"
}
}

@Whitelisted
@Nonnull
Map<String, CredentialWrapper> getEnvCredentials() {
Map<String, CredentialWrapper> m = [:]
environment.each {k, v ->
if (v instanceof CredentialWrapper) {
m["${k}"] = v;
}
}
return m
}


@Override
@Whitelisted
Expand Down
@@ -0,0 +1,211 @@
/*
* The MIT License
*
* Copyright (c) 2016, CloudBees, Inc.
*
* 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.pipeline.modeldefinition.model;

import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.IdCredentials;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.model.Run;
import org.apache.commons.jexl.context.HashMapContext;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.plugins.credentialsbinding.impl.CredentialNotFoundException;
import org.jenkinsci.plugins.plaincredentials.FileCredentials;
import org.jenkinsci.plugins.plaincredentials.StringCredentials;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* Simplified {@link org.jenkinsci.plugins.credentialsbinding.Binding} handler for use in {@code environment {} }
*/
public abstract class CredentialsBindingHandler implements ExtensionPoint {

public boolean handles(Class<? extends StandardCredentials> c) {
return type().isAssignableFrom(c);
}

public boolean handles(StandardCredentials c) {
return handles(c.getClass());
}

@Nonnull
public abstract Class<? extends StandardCredentials> type();

@Nonnull
public abstract List<Map<String, Object>> getWithCredentialsParameters(String credentialsId);

@Nonnull
public static ExtensionList<CredentialsBindingHandler> all() {
return ExtensionList.lookup(CredentialsBindingHandler.class);
}

@Nonnull
public static Set<Class<? extends StandardCredentials>> supportedTypes() {
Set<Class<? extends StandardCredentials>> set = new HashSet<>();
for (CredentialsBindingHandler handler : all()) {
set.add(handler.type());
}
return set;
}

@Nonnull
public static Set<String> supportedTypeNames() {
Set<String> set = new HashSet<>();
for (Class<? extends StandardCredentials> c : supportedTypes()) {
set.add(c.getSimpleName());
}
return set;
}

@CheckForNull
public static CredentialsBindingHandler forCredential(StandardCredentials c) {
for (CredentialsBindingHandler handler : all()) {
if (handler.handles(c)) {
return handler;
}
}
return null;
}

@Nonnull
public static CredentialsBindingHandler forId(String id, Run context) throws CredentialNotFoundException {
IdCredentials cred = CredentialsProvider.findCredentialById(id, IdCredentials.class, context);
if (cred==null) {
throw new CredentialNotFoundException(id);
}
if (cred instanceof StandardCredentials) {
CredentialsBindingHandler handler = forCredential((StandardCredentials)cred);
if (handler == null) {
throw new CredentialNotFoundException(String.format("No suitable binding handler could be found for type %s." +
"Supported types are %s.",
cred.getClass().getName(),
StringUtils.join(supportedTypeNames(), ',')));
}
return handler;
} else {
throw new CredentialNotFoundException(String.format("Credentials %s is of type %s where " +
"StandardCredentials is the expected type.",
id, cred.getClass().getName()));
}
}

public static class EnvVarResolver implements Serializable {
private static final long serialVersionUID = 1L;

private final String value;


public EnvVarResolver() {
this.value = "%s";
}

public EnvVarResolver(String value) {
this.value = value;
}

public String resolve(String varName) {
return String.format(this.value, varName);
}
}

@Extension
public static class UsernamePasswordHandler extends CredentialsBindingHandler {

@Override
public Class<? extends StandardCredentials> type() {
return StandardUsernamePasswordCredentials.class;
}

@Nonnull
@Override
public List<Map<String, Object>> getWithCredentialsParameters(String credentialsId) {
Map<String, Object> map = new HashMap<>();
map.put("$class", "UsernamePasswordBinding");
map.put("variable", new EnvVarResolver());
map.put("credentialsId", credentialsId);
Map<String, Object> map2 = new HashMap<>();
map2.put("$class", "UsernamePasswordMultiBinding");
map2.put("usernameVariable", new EnvVarResolver("%s_USR"));
map2.put("passwordVariable", new EnvVarResolver("%s_PSW"));
map2.put("credentialsId", credentialsId);
return Arrays.asList(map, map2);
}

}

@Extension
public static class FileCredentialsHandler extends CredentialsBindingHandler {

@Nonnull
@Override
public Class<? extends StandardCredentials> type() {
return FileCredentials.class;
}

@Nonnull
@Override
public List<Map<String, Object>> getWithCredentialsParameters(String credentialsId) {
Map<String, Object> map = new HashMap<>();
map.put("$class", "FileBinding");
map.put("variable", new EnvVarResolver());
map.put("credentialsId", credentialsId);
return Collections.singletonList(map);
}
}

@Extension
public static class StringCredentialsHandler extends CredentialsBindingHandler {

@Nonnull
@Override
public Class<? extends StandardCredentials> type() {
return StringCredentials.class;
}

@Nonnull
@Override
public List<Map<String, Object>> getWithCredentialsParameters(String credentialsId) {
Map<String, Object> map = new HashMap<>();
map.put("$class", "StringBinding");
map.put("variable", new EnvVarResolver());
map.put("credentialsId", credentialsId);
return Collections.singletonList(map);
}
}
}
@@ -0,0 +1,80 @@
/*
* The MIT License
*
* Copyright (c) 2016, CloudBees, Inc.
*
* 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.pipeline.modeldefinition.steps;



import org.jenkinsci.plugins.pipeline.modeldefinition.model.CredentialsBindingHandler;
import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.Whitelisted;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Helper for simplified Credentials handling in {@code environment{}}
*/
public class CredentialWrapper implements Serializable {
private static final long serialVersionUID = 1L;

private final String credentialId;
private final List<Map<String, Object>> withCredentialsParameters;

CredentialWrapper(String credentialId, List<Map<String, Object>> withCredentialsParameters) {
this.withCredentialsParameters = withCredentialsParameters;
this.credentialId = credentialId;
}

@Whitelisted
public String getCredentialId() {
return credentialId;
}

@Whitelisted
public void addParameters(String envVarName, List<Map<String, Object>> list) {
list.addAll(resolveParameters(envVarName));
}

@Whitelisted
public List<Map<String, Object>> resolveParameters(String envVarName) {
List<Map<String, Object>> newList = new ArrayList<>(withCredentialsParameters.size());
for (Map<String, Object> params : withCredentialsParameters) {
Map<String, Object> newP = new HashMap<>();
for (Map.Entry<String, Object> p : params.entrySet()) {
Object value = p.getValue();
if (value instanceof CredentialsBindingHandler.EnvVarResolver) {
newP.put(p.getKey(), ((CredentialsBindingHandler.EnvVarResolver)value).resolve(envVarName));
} else {
newP.put(p.getKey(), p.getValue());
}
}
newList.add(newP);
}
return newList;
}
}

0 comments on commit 5346000

Please sign in to comment.