diff --git a/pom.xml b/pom.xml index 40639fd1..0dee8c64 100644 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,7 @@ org.jenkins-ci.plugins credentials - 2.1.0 + 2.1.11 true diff --git a/src/main/java/com/cloudbees/hudson/plugins/folder/properties/FolderCredentialsProvider.java b/src/main/java/com/cloudbees/hudson/plugins/folder/properties/FolderCredentialsProvider.java index ee148d6d..c02fb508 100644 --- a/src/main/java/com/cloudbees/hudson/plugins/folder/properties/FolderCredentialsProvider.java +++ b/src/main/java/com/cloudbees/hudson/plugins/folder/properties/FolderCredentialsProvider.java @@ -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; @@ -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; @@ -119,21 +124,25 @@ public List getCredentials(@NonNull Class type, @N public List getCredentials(@NonNull Class type, @Nullable ItemGroup itemGroup, @Nullable Authentication authentication, @NonNull List domainRequirements) { - if (authentication == null) { - authentication = ACL.SYSTEM; - } List result = new ArrayList(); + Set ids = new HashSet(); 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) { @@ -161,6 +170,61 @@ public List getCredentials(@NonNull Class type, @N return super.getCredentials(type, item, authentication, domainRequirements); } + /** + * {@inheritDoc} + */ + @NonNull + @Override + public ListBoxModel getCredentialIds(@NonNull Class type, + @Nullable ItemGroup itemGroup, + @Nullable Authentication authentication, + @NonNull List domainRequirements, + @NonNull CredentialsMatcher matcher) { + ListBoxModel result = new ListBoxModel(); + Set ids = new HashSet(); + 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 ListBoxModel getCredentialIds(@NonNull Class type, @NonNull Item item, + @Nullable Authentication authentication, + @NonNull List 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} */ diff --git a/src/test/java/com/cloudbees/hudson/plugins/folder/properties/FolderCredentialsProviderTest.java b/src/test/java/com/cloudbees/hudson/plugins/folder/properties/FolderCredentialsProviderTest.java index 23b261a8..46c01a95 100644 --- a/src/test/java/com/cloudbees/hudson/plugins/folder/properties/FolderCredentialsProviderTest.java +++ b/src/test/java/com/cloudbees/hudson/plugins/folder/properties/FolderCredentialsProviderTest.java @@ -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; @@ -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 jobsToUsers = new HashMap(); + 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 jobsToUsers = new HashMap(); + 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 jobsToUsers = new HashMap(); + 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 jobsToUsers = new HashMap(); + 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 { + + @Override + public boolean isApplicable(Class jobType) { + return true; + } + + @Override + public String getDisplayName() { + return "Probe credentials exist"; + } + } + } + private CredentialsStore getFolderStore(Folder f) { Iterable stores = CredentialsProvider.lookupStores(f); CredentialsStore folderStore = null;