Skip to content

Commit

Permalink
Merge pull request #54 from jenkinsci/jenkins-24631
Browse files Browse the repository at this point in the history
[FIXED JENKINS-24631] Expose the credentials details by the XML/JSON API
  • Loading branch information
stephenc committed Jun 2, 2016
2 parents 56b9751 + 5ca10d7 commit defdd84
Show file tree
Hide file tree
Showing 4 changed files with 262 additions and 2 deletions.
Expand Up @@ -48,6 +48,7 @@
import hudson.util.FormValidation;
import hudson.util.HttpResponses;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
Expand Down Expand Up @@ -79,6 +80,7 @@
/**
* An action for a {@link CredentialsStore}
*/
@ExportedBean
public abstract class CredentialsStoreAction
implements Action, IconSpec, AccessControlled, ModelObjectWithContextMenu, ModelObjectWithChildren {

Expand Down Expand Up @@ -109,7 +111,6 @@ public abstract class CredentialsStoreAction
* @return the {@link CredentialsStore}.
*/
@NonNull
@Exported
public abstract CredentialsStore getStore();

/**
Expand Down Expand Up @@ -609,7 +610,6 @@ public CredentialsWrapper.DescriptorImpl getCredentialDescriptor() {
*
* @return a map of the wrapped credentials.
*/
@Exported
@NonNull
public Map<String, CredentialsWrapper> getCredentials() {
Map<String, CredentialsWrapper> result = new LinkedHashMap<String, CredentialsWrapper>();
Expand All @@ -630,6 +630,18 @@ public Map<String, CredentialsWrapper> getCredentials() {
return result;
}

/**
* Exposes the wrapped credentials for the XML API.
*
* @return the wrapped credentials for the XML API.
* @since 2.0.8
*/
@NonNull
@Exported(name = "credentials", visibility = 1)
public List<CredentialsWrapper> getCredentialsList() {
return new ArrayList<CredentialsWrapper>(getCredentials().values());
}

/**
* Get a credential by id.
*
Expand Down Expand Up @@ -874,6 +886,17 @@ public CredentialsWrapper(DomainWrapper domain, Credentials credentials, String
this.id = id;
}

/**
* Return the id for the XML API.
*
* @return the id.
* @since 2.0.8
*/
@Exported
public String getId() {
return id;
}

/**
* Return the URL name.
*
Expand Down
Expand Up @@ -47,7 +47,9 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.annotation.Nonnull;
import jenkins.model.Jenkins;
import jenkins.model.ModelObjectWithContextMenu;
Expand All @@ -57,10 +59,13 @@
import org.jenkins.ui.icon.IconSpec;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;

/**
* An {@link Action} that lets you view the available credentials for any {@link ModelObject}.
*/
@ExportedBean
public class ViewCredentialsAction implements Action, IconSpec, AccessControlled, ModelObjectWithContextMenu {

/**
Expand Down Expand Up @@ -153,6 +158,23 @@ public List<CredentialsStoreAction> getStoreActions() {
return result;
}

/**
* Exposes the {@link #getLocalStores()} for the XML API.
*
* @return the {@link #getLocalStores()} for the XML API.
* @since 2.0.8
*/
@NonNull
@SuppressWarnings("unused") // Stapler XML/JSON API
@Exported(name = "stores")
public Map<String,CredentialsStoreAction> getStoreActionsMap() {
Map<String,CredentialsStoreAction> result = new TreeMap<String, CredentialsStoreAction>();
for (CredentialsStoreAction a: getStoreActions()) {
result.put(a.getUrlName(), a);
}
return result;
}

/**
* Exposes the {@link #getStoreActions()} by {@link CredentialsStoreAction#getUrlName()} for Stapler.
*
Expand Down
@@ -0,0 +1,103 @@
package com.cloudbees.plugins.credentials;

import com.cloudbees.plugins.credentials.domains.Domain;
import com.cloudbees.plugins.credentials.domains.DomainSpecification;
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl;
import com.gargoylesoftware.htmlunit.WebResponse;
import hudson.ExtensionList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

public class CredentialsStoreActionTest {
@Rule
public JenkinsRule j = new JenkinsRule();

@Test
public void smokes() throws Exception {
SystemCredentialsProvider.ProviderImpl system = ExtensionList.lookup(CredentialsProvider.class).get(
SystemCredentialsProvider.ProviderImpl.class);

CredentialsStore systemStore = system.getStore(j.getInstance());

List<Domain> domainList = new ArrayList<Domain>(systemStore.getDomains());
domainList.remove(Domain.global());
for (Domain d : domainList) {
systemStore.removeDomain(d);
}

List<Credentials> credentialsList = new ArrayList<Credentials>(systemStore.getCredentials(Domain.global()));
for (Credentials c : credentialsList) {
systemStore.removeCredentials(Domain.global(), c);
}

JenkinsRule.WebClient wc = j.createWebClient();
WebResponse response = wc.goTo("credentials/store/system/api/xml?depth=5", "application/xml").getWebResponse();
assertThat(response.getContentAsString(), is("<userFacingAction>"
+ "<domains>"
+ "<_>"
+ "<description>"
+ "Credentials that should be available irrespective of domain specification to requirements "
+ "matching."
+ "</description>"
+ "<displayName>Global credentials (unrestricted)</displayName>"
+ "<fullDisplayName>System » Global credentials (unrestricted)</fullDisplayName>"
+ "<fullName>system/_</fullName>"
+ "<global>true</global>"
+ "<urlName>_</urlName>"
+ "</_>"
+ "</domains>"
+ "</userFacingAction>"));

Random entropy = new Random();
String domainName = "test" + entropy.nextInt();
String domainDescription = "test description " + entropy.nextLong();
String credentialId = "test-id-" + entropy.nextInt();
String credentialDescription = "test-account-" + entropy.nextInt();
String credentialUsername = "test-user-" + entropy.nextInt();
systemStore.addDomain(new Domain(domainName, domainDescription, Collections.<DomainSpecification>emptyList()),
new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, credentialId,
credentialDescription, credentialUsername, "test-secret"));
response = wc.goTo("credentials/store/system/api/xml?depth=5", "application/xml").getWebResponse();
assertThat(response.getContentAsString(), is("<userFacingAction>"
+ "<domains>"
+ "<_>"
+ "<description>"
+ "Credentials that should be available irrespective of domain specification to requirements "
+ "matching."
+ "</description>"
+ "<displayName>Global credentials (unrestricted)</displayName>"
+ "<fullDisplayName>System » Global credentials (unrestricted)</fullDisplayName>"
+ "<fullName>system/_</fullName>"
+ "<global>true</global>"
+ "<urlName>_</urlName>"
+ "</_>"
+ "<" + domainName + ">"
+ "<credential>"
+ "<description>" + credentialDescription + "</description>"
+ "<displayName>" + credentialUsername + "/****** (" + credentialDescription + ")</displayName>"
+ "<fullName>system/" + domainName + "/" + credentialId + "</fullName>"
+ "<id>" + credentialId + "</id>"
+ "<typeName>Username with password</typeName>"
+ "</credential>"
+ "<description>"
+ domainDescription
+ "</description>"
+ "<displayName>" + domainName + "</displayName>"
+ "<fullDisplayName>System » " + domainName + "</fullDisplayName>"
+ "<fullName>system/" + domainName + "</fullName>"
+ "<global>false</global>"
+ "<urlName>" + domainName + "</urlName>"
+ "</" + domainName + ">"
+ "</domains>"
+ "</userFacingAction>"));

}
}
@@ -0,0 +1,112 @@
package com.cloudbees.plugins.credentials;

import com.cloudbees.plugins.credentials.domains.Domain;
import com.cloudbees.plugins.credentials.domains.DomainSpecification;
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl;
import com.gargoylesoftware.htmlunit.WebResponse;
import hudson.ExtensionList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import jenkins.model.Jenkins;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

public class ViewCredentialsActionTest {
@Rule
public JenkinsRule j = new JenkinsRule();

@Test
public void smokes() throws Exception {
SystemCredentialsProvider.ProviderImpl system = ExtensionList.lookup(CredentialsProvider.class).get(
SystemCredentialsProvider.ProviderImpl.class);

CredentialsStore systemStore = system.getStore(j.getInstance());

List<Domain> domainList = new ArrayList<Domain>(systemStore.getDomains());
domainList.remove(Domain.global());
for (Domain d: domainList) {
systemStore.removeDomain(d);
}

List<Credentials> credentialsList = new ArrayList<Credentials>(systemStore.getCredentials(Domain.global()));
for (Credentials c: credentialsList) {
systemStore.removeCredentials(Domain.global(), c);
}

JenkinsRule.WebClient wc = j.createWebClient();
WebResponse response = wc.goTo("credentials/api/xml?depth=5", "application/xml").getWebResponse();
assertThat(response.getContentAsString(), is("<rootActionImpl>"
+ "<stores>"
+ "<system>"
+ "<domains>"
+ "<_>"
+ "<description>"
+ "Credentials that should be available irrespective of domain specification to requirements "
+ "matching."
+ "</description>"
+ "<displayName>Global credentials (unrestricted)</displayName>"
+ "<fullDisplayName>System » Global credentials (unrestricted)</fullDisplayName>"
+ "<fullName>system/_</fullName>"
+ "<global>true</global>"
+ "<urlName>_</urlName>"
+ "</_>"
+ "</domains>"
+ "</system>"
+ "</stores>"
+ "</rootActionImpl>"));

Random entropy = new Random();
String domainName = "test"+entropy.nextInt();
String domainDescription = "test description " + entropy.nextLong();
String credentialId = "test-id-" + entropy.nextInt();
String credentialDescription = "test-account-" + entropy.nextInt();
String credentialUsername = "test-user-" + entropy.nextInt();
systemStore.addDomain(new Domain(domainName, domainDescription, Collections.<DomainSpecification>emptyList()),
new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, credentialId,
credentialDescription, credentialUsername, "test-secret"));
response = wc.goTo("credentials/api/xml?depth=5", "application/xml").getWebResponse();
assertThat(response.getContentAsString(), is("<rootActionImpl>"
+ "<stores>"
+ "<system>"
+ "<domains>"
+ "<_>"
+ "<description>"
+ "Credentials that should be available irrespective of domain specification to requirements "
+ "matching."
+ "</description>"
+ "<displayName>Global credentials (unrestricted)</displayName>"
+ "<fullDisplayName>System » Global credentials (unrestricted)</fullDisplayName>"
+ "<fullName>system/_</fullName>"
+ "<global>true</global>"
+ "<urlName>_</urlName>"
+ "</_>"
+ "<" + domainName + ">"
+ "<credential>"
+ "<description>"+ credentialDescription +"</description>"
+ "<displayName>" + credentialUsername + "/****** (" + credentialDescription + ")</displayName>"
+ "<fullName>system/"+ domainName + "/" + credentialId + "</fullName>"
+ "<id>" + credentialId + "</id>"
+ "<typeName>Username with password</typeName>"
+ "</credential>"
+ "<description>"
+ domainDescription
+ "</description>"
+ "<displayName>" + domainName + "</displayName>"
+ "<fullDisplayName>System » " + domainName + "</fullDisplayName>"
+ "<fullName>system/" + domainName + "</fullName>"
+ "<global>false</global>"
+ "<urlName>" + domainName + "</urlName>"
+ "</" + domainName + ">"
+ "</domains>"
+ "</system>"
+ "</stores>"
+ "</rootActionImpl>"));
}

}

0 comments on commit defdd84

Please sign in to comment.