Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #83 from stephenc/jenkins-41004
[JENKINS-41004] Do not report credentials with IDs masked by nearer folders
  • Loading branch information
stephenc committed Jan 27, 2017
2 parents 0776cd4 + ae55e47 commit ff0c530
Show file tree
Hide file tree
Showing 3 changed files with 305 additions and 6 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Expand Up @@ -55,7 +55,7 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>credentials</artifactId>
<version>2.1.0</version>
<version>2.1.11</version>
<optional>true</optional>
</dependency>
<dependency>
Expand Down
Expand Up @@ -28,11 +28,14 @@
import com.cloudbees.hudson.plugins.folder.AbstractFolderProperty;
import com.cloudbees.hudson.plugins.folder.AbstractFolderPropertyDescriptor;
import com.cloudbees.plugins.credentials.Credentials;
import com.cloudbees.plugins.credentials.CredentialsMatcher;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsNameProvider;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.CredentialsStore;
import com.cloudbees.plugins.credentials.CredentialsStoreAction;
import com.cloudbees.plugins.credentials.common.IdCredentials;
import com.cloudbees.plugins.credentials.domains.Domain;
import com.cloudbees.plugins.credentials.domains.DomainCredentials;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
Expand All @@ -52,12 +55,14 @@
import hudson.security.AccessDeniedException2;
import hudson.security.Permission;
import hudson.util.CopyOnWriteMap;
import hudson.util.ListBoxModel;
import java.io.IOException;
import java.io.ObjectStreamException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -119,21 +124,25 @@ public <C extends Credentials> List<C> getCredentials(@NonNull Class<C> type, @N
public <C extends Credentials> List<C> getCredentials(@NonNull Class<C> type, @Nullable ItemGroup itemGroup,
@Nullable Authentication authentication,
@NonNull List<DomainRequirement> domainRequirements) {
if (authentication == null) {
authentication = ACL.SYSTEM;
}
List<C> result = new ArrayList<C>();
Set<String> ids = new HashSet<String>();
if (ACL.SYSTEM.equals(authentication)) {
while (itemGroup != null) {
if (itemGroup instanceof AbstractFolder) {
final AbstractFolder<?> folder = AbstractFolder.class.cast(itemGroup);
FolderCredentialsProperty property = folder.getProperties().get(FolderCredentialsProperty.class);
if (property != null) {
result.addAll(DomainCredentials.getCredentials(
for (C c : DomainCredentials.getCredentials(
property.getDomainCredentialsMap(),
type,
domainRequirements,
CredentialsMatchers.always()));
CredentialsMatchers.always())) {
if (!(c instanceof IdCredentials) || ids.add(((IdCredentials) c).getId())) {
// if IdCredentials, only add if we havent added already
// if not IdCredentials, always add
result.add(c);
}
}
}
}
if (itemGroup instanceof Item) {
Expand Down Expand Up @@ -161,6 +170,61 @@ public <C extends Credentials> List<C> getCredentials(@NonNull Class<C> type, @N
return super.getCredentials(type, item, authentication, domainRequirements);
}

/**
* {@inheritDoc}
*/
@NonNull
@Override
public <C extends IdCredentials> ListBoxModel getCredentialIds(@NonNull Class<C> type,
@Nullable ItemGroup itemGroup,
@Nullable Authentication authentication,
@NonNull List<DomainRequirement> domainRequirements,
@NonNull CredentialsMatcher matcher) {
ListBoxModel result = new ListBoxModel();
Set<String> ids = new HashSet<String>();
if (ACL.SYSTEM.equals(authentication)) {
while (itemGroup != null) {
if (itemGroup instanceof AbstractFolder) {
final AbstractFolder<?> folder = AbstractFolder.class.cast(itemGroup);
FolderCredentialsProperty property = folder.getProperties().get(FolderCredentialsProperty.class);
if (property != null) {
for (C c : DomainCredentials.getCredentials(
property.getDomainCredentialsMap(),
type,
domainRequirements,
matcher)) {
if (ids.add(c.getId())) {
result.add(CredentialsNameProvider.name(c), c.getId());
}
}
}
}
if (itemGroup instanceof Item) {
itemGroup = ((Item)itemGroup).getParent();
} else {
break;
}
}
}
return result;
}

/**
* {@inheritDoc}
*/
@NonNull
@Override
public <C extends IdCredentials> ListBoxModel getCredentialIds(@NonNull Class<C> type, @NonNull Item item,
@Nullable Authentication authentication,
@NonNull List<DomainRequirement> domainRequirements,
@NonNull CredentialsMatcher matcher) {
if (item instanceof AbstractFolder) {
// credentials defined in the folder should be available in the context of the folder
return getCredentialIds(type, (ItemGroup) item, authentication, domainRequirements, matcher);
}
return getCredentialIds(type, item.getParent(), authentication, domainRequirements, matcher);
}

/**
* {@inheritDoc}
*/
Expand Down
Expand Up @@ -25,25 +25,48 @@

import com.cloudbees.hudson.plugins.folder.Folder;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsNameProvider;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.CredentialsStore;
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
import com.cloudbees.plugins.credentials.common.IdCredentials;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.Domain;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.Computer;
import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.Result;
import hudson.model.User;
import hudson.security.ACL;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Builder;
import hudson.util.ListBoxModel;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import jenkins.security.QueueItemAuthenticatorConfiguration;
import org.acegisecurity.Authentication;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.hamcrest.StringDescription;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.MockAuthorizationStrategy;
import org.jvnet.hudson.test.MockQueueItemAuthenticator;
import org.jvnet.hudson.test.TestExtension;
import org.kohsuke.stapler.DataBoundConstructor;

import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasProperty;
Expand Down Expand Up @@ -114,6 +137,218 @@ public void credentialsListableAtFolderScope() throws Exception {
assertThat(asItem.get(0).value, is("test-id"));
}

@Test
public void given_folderCredential_when_builtAsSystem_then_credentialFound() throws Exception {
Folder f = createFolder();
CredentialsStore folderStore = getFolderStore(f);
folderStore.addCredentials(Domain.global(),
new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, "foo-manchu", "Dr. Fu Manchu", "foo",
"manchu"));
FreeStyleProject prj = f.createProject(FreeStyleProject.class, "job");
prj.getBuildersList().add(new HasCredentialBuilder("foo-manchu"));
r.buildAndAssertSuccess(prj);
}

@Test
public void given_folderCredential_when_builtAsUserWithUseItem_then_credentialFound() throws Exception {
Folder f = createFolder();
CredentialsStore folderStore = getFolderStore(f);
folderStore.addCredentials(Domain.global(),
new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, "foo-manchu", "Dr. Fu Manchu", "foo",
"manchu"));
FreeStyleProject prj = f.createProject(FreeStyleProject.class, "job");
prj.getBuildersList().add(new HasCredentialBuilder("foo-manchu"));

JenkinsRule.DummySecurityRealm realm = r.createDummySecurityRealm();
r.jenkins.setSecurityRealm(realm);

MockAuthorizationStrategy strategy = new MockAuthorizationStrategy();
strategy.grant(CredentialsProvider.USE_ITEM).everywhere().to("bob");
strategy.grant(Item.BUILD).everywhere().to("bob");
strategy.grant(Computer.BUILD).everywhere().to("bob");

r.jenkins.setAuthorizationStrategy(strategy);
HashMap<String, Authentication> jobsToUsers = new HashMap<String, Authentication>();
jobsToUsers.put(prj.getFullName(), User.get("bob").impersonate());
MockQueueItemAuthenticator authenticator = new MockQueueItemAuthenticator(jobsToUsers);

QueueItemAuthenticatorConfiguration.get().getAuthenticators().clear();
QueueItemAuthenticatorConfiguration.get().getAuthenticators().add(authenticator);
r.buildAndAssertSuccess(prj);
}

@Test
public void given_folderCredential_when_builtAsUserWithoutUseItem_then_credentialNotFound() throws Exception {
Folder f = createFolder();
CredentialsStore folderStore = getFolderStore(f);
folderStore.addCredentials(Domain.global(),
new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, "foo-manchu", "Dr. Fu Manchu", "foo",
"manchu"));
FreeStyleProject prj = f.createProject(FreeStyleProject.class, "job");
prj.getBuildersList().add(new HasCredentialBuilder("foo-manchu"));

JenkinsRule.DummySecurityRealm realm = r.createDummySecurityRealm();
r.jenkins.setSecurityRealm(realm);

MockAuthorizationStrategy strategy = new MockAuthorizationStrategy();
strategy.grant(Item.BUILD).everywhere().to("bob");
strategy.grant(Computer.BUILD).everywhere().to("bob");

r.jenkins.setAuthorizationStrategy(strategy);
HashMap<String, Authentication> jobsToUsers = new HashMap<String, Authentication>();
jobsToUsers.put(prj.getFullName(), User.get("bob").impersonate());
MockQueueItemAuthenticator authenticator = new MockQueueItemAuthenticator(jobsToUsers);

QueueItemAuthenticatorConfiguration.get().getAuthenticators().clear();
QueueItemAuthenticatorConfiguration.get().getAuthenticators().add(authenticator);
r.assertBuildStatus(Result.FAILURE, prj.scheduleBuild2(0).get());
}

@Test
public void given_folderAndSystemCredentials_when_builtAsUserWithUseItem_then_folderCredentialFound() throws Exception {
SystemCredentialsProvider.getInstance().getCredentials().add(
new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, "foo-manchu", "You don't want me", "bar", "fly")
);
Folder f = createFolder();
CredentialsStore folderStore = getFolderStore(f);
folderStore.addCredentials(Domain.global(),
new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, "foo-manchu", "Dr. Fu Manchu", "foo",
"manchu"));
FreeStyleProject prj = f.createProject(FreeStyleProject.class, "job");
prj.getBuildersList().add(new HasCredentialBuilder("foo-manchu", Matchers.hasProperty("username", is("foo"))));

JenkinsRule.DummySecurityRealm realm = r.createDummySecurityRealm();
r.jenkins.setSecurityRealm(realm);

MockAuthorizationStrategy strategy = new MockAuthorizationStrategy();
strategy.grant(CredentialsProvider.USE_ITEM).everywhere().to("bob");
strategy.grant(Item.BUILD).everywhere().to("bob");
strategy.grant(Computer.BUILD).everywhere().to("bob");

r.jenkins.setAuthorizationStrategy(strategy);
HashMap<String, Authentication> jobsToUsers = new HashMap<String, Authentication>();
jobsToUsers.put(prj.getFullName(), User.get("bob").impersonate());
MockQueueItemAuthenticator authenticator = new MockQueueItemAuthenticator(jobsToUsers);

QueueItemAuthenticatorConfiguration.get().getAuthenticators().clear();
QueueItemAuthenticatorConfiguration.get().getAuthenticators().add(authenticator);
try {
r.buildAndAssertSuccess(prj);
} catch (Exception e) {
FreeStyleBuild build = prj.getLastBuild();
if (build != null) {
System.out.println(JenkinsRule.getLog(build));
}
throw e;
}
}

@Test
public void given_nestedFolderAndSystemCredentials_when_builtAsUserWithUseItem_then_folderCredentialFound() throws Exception {
SystemCredentialsProvider.getInstance().getCredentials().add(
new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, "foo-manchu", "You don't want me", "bar", "fly")
);
Folder f = createFolder();
CredentialsStore folderStore = getFolderStore(f);
folderStore.addCredentials(Domain.global(),
new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, "foo-manchu", "Prof. Xavier", "prof",
"xavier"));
Folder child = f.createProject(Folder.class, "child");
getFolderStore(child).addCredentials(Domain.global(),
new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, "foo-manchu", "Dr. Fu Manchu", "foo",
"manchu"));
FreeStyleProject prj = child.createProject(FreeStyleProject.class, "job");
prj.getBuildersList().add(new HasCredentialBuilder("foo-manchu", Matchers.hasProperty("username", is("foo"))));

JenkinsRule.DummySecurityRealm realm = r.createDummySecurityRealm();
r.jenkins.setSecurityRealm(realm);

MockAuthorizationStrategy strategy = new MockAuthorizationStrategy();
strategy.grant(CredentialsProvider.USE_ITEM).everywhere().to("bob");
strategy.grant(Item.BUILD).everywhere().to("bob");
strategy.grant(Computer.BUILD).everywhere().to("bob");

r.jenkins.setAuthorizationStrategy(strategy);
HashMap<String, Authentication> jobsToUsers = new HashMap<String, Authentication>();
jobsToUsers.put(prj.getFullName(), User.get("bob").impersonate());
MockQueueItemAuthenticator authenticator = new MockQueueItemAuthenticator(jobsToUsers);

QueueItemAuthenticatorConfiguration.get().getAuthenticators().clear();
QueueItemAuthenticatorConfiguration.get().getAuthenticators().add(authenticator);
try {
r.buildAndAssertSuccess(prj);
} catch (Exception e) {
FreeStyleBuild build = prj.getLastBuild();
if (build != null) {
System.out.println(JenkinsRule.getLog(build));
}
throw e;
}
}

public static class HasCredentialBuilder extends Builder {

private final String id;
private Matcher<?> matcher;

@DataBoundConstructor
public HasCredentialBuilder(String id) {
this.id = id;
}

public HasCredentialBuilder(String id, Matcher<?> matcher) {
this.id = id;
this.matcher = matcher;
}

public String getId() {
return id;
}

public Matcher<?> getMatcher() {
return matcher;
}

@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener)
throws InterruptedException, IOException {
IdCredentials credentials = CredentialsProvider.findCredentialById(id, IdCredentials.class, build);
if (credentials == null) {
listener.getLogger().printf("Could not find any credentials with id %s%n", id);
build.setResult(Result.FAILURE);
return false;
} else {
listener.getLogger()
.printf("Found %s credentials with id %s%n", CredentialsNameProvider.name(credentials), id);
if (matcher != null) {
if (matcher.matches(credentials)) {
listener.getLogger().println("Credentials match criteria");
} else {
StringDescription description = new StringDescription();
matcher.describeMismatch(credentials, description);
listener.getLogger().println(description.toString());
return false;
}
}
return true;
}
}

@TestExtension
public static class DescriptorImpl extends BuildStepDescriptor<Builder> {

@Override
public boolean isApplicable(Class<? extends AbstractProject> jobType) {
return true;
}

@Override
public String getDisplayName() {
return "Probe credentials exist";
}
}
}

private CredentialsStore getFolderStore(Folder f) {
Iterable<CredentialsStore> stores = CredentialsProvider.lookupStores(f);
CredentialsStore folderStore = null;
Expand Down

0 comments on commit ff0c530

Please sign in to comment.