Skip to content

Commit

Permalink
Merge branch 'nfalco79-feature/JENKINS-39991'
Browse files Browse the repository at this point in the history
  • Loading branch information
imod committed Dec 27, 2016
2 parents 58c5aed + 31c76f8 commit d1599bf
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 30 deletions.
Expand Up @@ -3,8 +3,11 @@

import java.io.StringReader;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
Expand All @@ -13,6 +16,7 @@
import java.util.logging.Logger;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
Expand Down Expand Up @@ -40,7 +44,8 @@

public class CredentialsHelper {

private final static Logger LOGGER = Logger.getLogger(CredentialsHelper.class.getName());
private static final Logger LOGGER = Logger.getLogger(CredentialsHelper.class.getName());
private static final Collection<String> ATTRIBUTES_TO_KEEPT = Arrays.asList("filePermissions", "directoryPermissions", "configuration");

/**
* hide constructor
Expand Down Expand Up @@ -102,6 +107,7 @@ public static String fillAuthentication(String mavenSettingsContent, final Boole
}
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(content)));

Map<String, Node> removedMavenServers = Collections.emptyMap();

// locate the server node(s)
XPath xpath = XPathFactory.newInstance().newXPath();
Expand All @@ -113,13 +119,15 @@ public static String fillAuthentication(String mavenSettingsContent, final Boole
settingsNode.appendChild(serversNode);
} else {
// remove the server nodes
removeMavenServerDefinitions(serversNode, mavenServerId2jenkinsCredential.keySet(), isReplaceAllServerDefinitions);
removedMavenServers = removeMavenServerDefinitions(serversNode, mavenServerId2jenkinsCredential.keySet(), Boolean.TRUE.equals(isReplaceAllServerDefinitions));
}

for (Entry<String, StandardUsernameCredentials> mavenServerId2JenkinsCredential : mavenServerId2jenkinsCredential.entrySet()) {

final StandardUsernameCredentials credential = mavenServerId2JenkinsCredential.getValue();
String mavenServerId = mavenServerId2JenkinsCredential.getKey();

Node currentDefinition = removedMavenServers.get(mavenServerId);
if (credential instanceof StandardUsernamePasswordCredentials) {

StandardUsernamePasswordCredentials usernamePasswordCredentials = (StandardUsernamePasswordCredentials) credential;
Expand All @@ -138,6 +146,7 @@ public static String fillAuthentication(String mavenSettingsContent, final Boole
server.appendChild(id);
server.appendChild(username);
server.appendChild(password);
copyServerAttributes(currentDefinition, server);

serversNode.appendChild(server);
} else if (credential instanceof SSHUserPrivateKey) {
Expand Down Expand Up @@ -167,6 +176,7 @@ public static String fillAuthentication(String mavenSettingsContent, final Boole

workDir.mkdirs();
FilePath privateKeyFile = workDir.createTextTempFile("private-key-", ".pem", privateKeyContent, true);
privateKeyFile.chmod(0600);
tempFiles.add(privateKeyFile.getRemote());
LOGGER.log(Level.FINE, "Create {0}", new Object[]{privateKeyFile.getRemote()});

Expand All @@ -180,6 +190,7 @@ public static String fillAuthentication(String mavenSettingsContent, final Boole
server.appendChild(username);
server.appendChild(privateKey);
server.appendChild(passphrase);
copyServerAttributes(currentDefinition, server);

serversNode.appendChild(server);
} else {
Expand All @@ -192,37 +203,70 @@ public static String fillAuthentication(String mavenSettingsContent, final Boole
// save the result
StringWriter writer = new StringWriter();
Transformer xformer = TransformerFactory.newInstance().newTransformer();
xformer.setOutputProperty(OutputKeys.INDENT, "yes");
xformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
xformer.transform(new DOMSource(doc), new StreamResult(writer));
content = writer.toString();

return content;
}

/*
* Copy non credential attributes from a node to other
*/
private static void copyServerAttributes(Node from, Node to) {
if (from == null || to == null) {
// nothing to copy
return;
}

NodeList nodes = from.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
String name = StringUtils.trimToNull(node.getNodeName());
if (ATTRIBUTES_TO_KEEPT.contains(name)) {
to.appendChild(node);
}
}
}

/**
* Removes all childs
* Removes all children
*
* @param serversNode
* the node to remove all childs from
* the node to remove all children from
* @param credentialKeys
* list of server id to replace
* @param replaceAll
* if remove all server nodes
*/
private static void removeMavenServerDefinitions(final Node serversNode, final Set<String> credentialKeys, final Boolean replaceAll) {
private static Map<String, Node> removeMavenServerDefinitions(final Node serversNode, final Set<String> credentialKeys, final boolean replaceAll) {
Map<String, Node> serverId2Node = new LinkedHashMap<>(credentialKeys.size());

final NodeList serverNodes = serversNode.getChildNodes();
for (int i = 0; i < serverNodes.getLength(); i++) {
final Node server = serverNodes.item(i);
String serverId = getServerId(server);
if (Boolean.TRUE.equals(replaceAll) || (credentialKeys.contains(serverId))) {
serversNode.removeChild(server);
if (replaceAll || (credentialKeys.contains(serverId))) {
Node removed = serversNode.removeChild(server);
if (credentialKeys.contains(serverId)) {
serverId2Node.put(serverId, removed);
}
--i;
}
}


return serverId2Node;
}

private static String getServerId(Node server) {
private static String getServerId(final Node server) {
NodeList nodes = server.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
String name = node.getNodeName();
String content = node.getTextContent();
if ("id".equals(name.toLowerCase())) {
String name = StringUtils.lowerCase(node.getNodeName());
String content = StringUtils.trimToNull(node.getTextContent());
if ("id".equals(name)) {
return content;
}
}
Expand Down
@@ -1,33 +1,37 @@

package org.jenkinsci.plugins.configfiles.maven.security;

import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.io.IOUtils;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl;

public class CredentialsHelperTest {

final static String PWD = "MY_NEW_PWD";
final static String PWD_2 = "{COQLCE6DU6GtcS5P=}";
final static int SERVER_COUNT = 5;

@Rule
public JenkinsRule jenkins = new JenkinsRule();
Expand Down Expand Up @@ -83,7 +87,7 @@ public void testIfServerAuthIsReplacedWithinSettingsXmlWhenReplaceFalse() throws
XPath xpath = XPathFactory.newInstance().newXPath();
NodeList nodes = (NodeList) xpath.evaluate("/settings/servers/server", doc, XPathConstants.NODESET);
// the 'settings_test.xml' actually contains 4 server tags, but we remove all and only inject the ones managed by the plugin
Assert.assertEquals("there is not the same number of server tags anymore in the settings.xml", 4, nodes.getLength());
Assert.assertEquals("there is not the same number of server tags anymore in the settings.xml", SERVER_COUNT, nodes.getLength());

}

Expand Down Expand Up @@ -112,4 +116,49 @@ public void testSettingsXmlIsNotChangedWithoutCredentialsWhenReplaceFalse() thro
Assert.assertEquals("no changes should have been made to the settings", settingsContent, replacedContent);

}

@Issue("JENKINS-39991")
@Test
public void testIfServerElementAreKeeptWhenMatchCredentialsWhenReplaceFalse() throws Exception {
testIfServerElementAreKeeptWhenMatchCredentials(false);

}

@Issue("JENKINS-39991")
@Test
public void testIfServerElementAreKeeptWhenMatchCredentialsWhenReplaceTrue() throws Exception {
testIfServerElementAreKeeptWhenMatchCredentials(true);
}

private void testIfServerElementAreKeeptWhenMatchCredentials(boolean replaceAll) throws Exception {
final String serverId = "jenkins-39991";

Map<String, StandardUsernameCredentials> serverId2Credentials = new HashMap<String, StandardUsernameCredentials>();
serverId2Credentials.put(serverId, new UsernamePasswordCredentialsImpl(CredentialsScope.SYSTEM, "my*", "some desc", "peter", PWD));

final String settingsContent = IOUtils.toString(CredentialsHelperTest.class.getResourceAsStream("/settings_test.xml"));
List<String> tempFiles = new ArrayList<String>();
final String replacedContent = CredentialsHelper.fillAuthentication(settingsContent, replaceAll, serverId2Credentials, jenkins.jenkins.createPath("tmp"), tempFiles);

// read original server settings
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(settingsContent)));
XPath xpath = XPathFactory.newInstance().newXPath();
Node server = (Node) xpath.evaluate("/settings/servers/server[id='" + serverId + "']", doc, XPathConstants.NODE);
String filePermissions = xpath.evaluate("filePermissions", server);
String directoryPermissions = xpath.evaluate("directoryPermissions", server);
String configuration = xpath.evaluate("configuration", server);

// ensure it is still a valid XML document
doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(replacedContent)));
// locate the node(s)
xpath = XPathFactory.newInstance().newXPath();

server = (Node) xpath.evaluate("/settings/servers/server[id='" + serverId + "']", doc, XPathConstants.NODE);
Assert.assertEquals("password is not at the correct location", PWD, xpath.evaluate("password", server));
Assert.assertEquals("username is not set correct", "peter", xpath.evaluate("username", server));
Assert.assertEquals("filePermissions is not set correct", filePermissions, xpath.evaluate("filePermissions", server));
Assert.assertEquals("directoryPermissions is not set correct", directoryPermissions, xpath.evaluate("directoryPermissions", server));
Assert.assertEquals("configuration is not set correct", configuration, xpath.evaluate("configuration", server));
}

}
22 changes: 16 additions & 6 deletions src/test/resources/settings_test.xml
Expand Up @@ -91,12 +91,22 @@
<username>dummy1</username>
<password>XXX</password>
</server>
<server>
<id>my-passphraseserver</id>
<passphrase>AAAAAAAAA</passphrase>
<privateKey>OldKey</privateKey>
</server>

<server>
<id>my-passphraseserver</id>
<passphrase>AAAAAAAAA</passphrase>
<privateKey>OldKey</privateKey>
</server>
<server>
<id>jenkins-39991</id>
<passphrase>secret</passphrase>
<privateKey>ssh-rsa qwerty==</privateKey>
<filePermissions>644</filePermissions>
<directoryPermissions>744</directoryPermissions>
<configuration>
<interactive>false</interactive>
</configuration>
</server>

</servers>

<!-- mirrors | This is a list of mirrors to be used in downloading artifacts
Expand Down

0 comments on commit d1599bf

Please sign in to comment.