Skip to content
Permalink
Browse files

Merge pull request #46 from jenkinsci/JENKINS-37778

[JENKINS-37778] Support credentials in environment
  • Loading branch information
rsandell committed Nov 9, 2016
2 parents ec6f0ea + 88814c6 commit f4e86644f5f483602da9b7686d9fc4a0e6b51e07
Showing with 975 additions and 86 deletions.
  1. +6 −1 pipeline-model-definition/pom.xml
  2. +12 −1 ...model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/model/Root.groovy
  3. +15 −1 ...odel-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/model/Stage.groovy
  4. +215 −0 ...src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/model/CredentialsBindingHandler.java
  5. +80 −0 ...inition/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/steps/CredentialWrapper.java
  6. +2 −1 ...n/src/main/resources/org/jenkinsci/plugins/pipeline/modeldefinition/ClosureModelTranslator.groovy
  7. +106 −78 ...inition/src/main/resources/org/jenkinsci/plugins/pipeline/modeldefinition/ModelInterpreter.groovy
  8. +27 −3 ...rc/main/resources/org/jenkinsci/plugins/pipeline/modeldefinition/PropertiesToMapTranslator.groovy
  9. +34 −0 ...definition/src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/AbstractModelDefTest.java
  10. +1 −1 ...odel-definition/src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/EnvironmentTest.java
  11. +134 −0 ...src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/steps/CredentialWrapperStepTest.java
  12. +123 −0 ...del-definition/src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/util/HasArchived.java
  13. +80 −0 ...rc/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/util/InputStreamContainingString.java
  14. +50 −0 pipeline-model-definition/src/test/resources/credentials/mixedEnv.groovy
  15. +40 −0 pipeline-model-definition/src/test/resources/credentials/noBinding.groovy
  16. +50 −0 pipeline-model-definition/src/test/resources/credentials/usernamePassword.groovy
@@ -106,6 +106,11 @@
<artifactId>structs</artifactId>
<version>1.5</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>credentials-binding</artifactId>
<version>1.9</version>
</dependency>

<!-- JSON schema stuff -->
<dependency>
@@ -143,7 +148,7 @@
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-cps-global-lib</artifactId>
<version>2.4</version>
<version>2.3</version>
<scope>test</scope>
</dependency>

@@ -27,6 +27,7 @@ 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.workflow.support.steps.build.RunWrapper

/**
@@ -120,11 +121,21 @@ public class Root 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}"
}
}

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.
*
@@ -26,6 +26,9 @@ 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 javax.annotation.Nonnull

/**
* An individual stage to be executed within the build.
@@ -103,11 +106,22 @@ 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}"
}
}

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


@Override
public void modelFromMap(Map<String,Object> m) {
@@ -0,0 +1,215 @@
/*
* 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.credentialsbinding.impl.FileBinding;
import org.jenkinsci.plugins.credentialsbinding.impl.StringBinding;
import org.jenkinsci.plugins.credentialsbinding.impl.UsernamePasswordBinding;
import org.jenkinsci.plugins.credentialsbinding.impl.UsernamePasswordMultiBinding;
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.class.getName());
map.put("variable", new EnvVarResolver());
map.put("credentialsId", credentialsId);
Map<String, Object> map2 = new HashMap<>();
map2.put("$class", UsernamePasswordMultiBinding.class.getName());
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.class.getName());
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.class.getName());
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;
}
}
@@ -25,6 +25,7 @@ package org.jenkinsci.plugins.pipeline.modeldefinition

import org.jenkinsci.plugins.pipeline.modeldefinition.model.AbstractBuildConditionResponder
import org.jenkinsci.plugins.pipeline.modeldefinition.model.ClosureContentsChecker
import org.jenkinsci.plugins.pipeline.modeldefinition.model.Environment
import org.jenkinsci.plugins.pipeline.modeldefinition.model.MappedClosure
import org.jenkinsci.plugins.pipeline.modeldefinition.model.MethodMissingWrapper
import org.jenkinsci.plugins.pipeline.modeldefinition.model.MethodsToList
@@ -131,7 +132,7 @@ public class ClosureModelTranslator implements MethodMissingWrapper, Serializabl
}
// if it's a PropertiesToMap, we use PropertiesToMapTranslator to translate it into the right form.
else if (Utils.assignableFromWrapper(PropertiesToMap.class, actualType)) {
def ptm = new PropertiesToMapTranslator(script)
def ptm = new PropertiesToMapTranslator(script, Utils.assignableFromWrapper(Environment.class, actualType))
resolveClosure(argValue, ptm)
resultValue = ptm.toNestedModel(actualType)
}

0 comments on commit f4e8664

Please sign in to comment.
You can’t perform that action at this time.