Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JENKINS-41004] Do not report credentials with IDs masked by nearer folders #83

Merged
merged 6 commits into from Jan 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any non-IdCredentials, really? Should we just deprecate that possibility?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've see one implementation - cannot recall where - so I am not going to pull the plug on them until I have the implementation guidelines written and published

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