From 04cd2633c22182f4dcc7e6c3746c468e35afce15 Mon Sep 17 00:00:00 2001 From: Nikolas Falco Date: Mon, 17 Jan 2022 22:08:49 +0100 Subject: [PATCH] [JENKINS-58902] Non-user-scoped credentials are not shown when build authentication is configured Fix CredentialProvider to gather system credentials when users have USE_ITEM USE_OWN permission. --- .../credentials/CredentialsProvider.java | 113 ++++++++++-------- 1 file changed, 66 insertions(+), 47 deletions(-) diff --git a/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java b/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java index 8afe42deb..cf66657c2 100644 --- a/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java +++ b/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java @@ -23,12 +23,54 @@ */ package com.cloudbees.plugins.credentials; -import com.cloudbees.plugins.credentials.builds.CredentialsParameterBinding; +import static com.cloudbees.plugins.credentials.CredentialsStoreAction.FINGERPRINT_XML; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.security.DigestOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.Collator; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; +import org.jenkins.ui.icon.IconSpec; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.DoNotUse; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.Stapler; +import org.kohsuke.stapler.StaplerRequest; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + import com.cloudbees.plugins.credentials.builds.CredentialsParameterBinder; +import com.cloudbees.plugins.credentials.builds.CredentialsParameterBinding; import com.cloudbees.plugins.credentials.common.IdCredentials; import com.cloudbees.plugins.credentials.domains.DomainRequirement; import com.cloudbees.plugins.credentials.fingerprints.ItemCredentialsFingerprintFacet; import com.cloudbees.plugins.credentials.fingerprints.NodeCredentialsFingerprintFacet; + import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -62,48 +104,9 @@ import hudson.security.PermissionScope; import hudson.security.SecurityRealm; import hudson.util.ListBoxModel; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; -import java.security.DigestOutputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.text.Collator; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; import jenkins.model.FingerprintFacet; import jenkins.model.Jenkins; import jenkins.util.Timer; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; -import org.jenkins.ui.icon.IconSpec; -import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.DoNotUse; -import org.kohsuke.accmod.restrictions.NoExternalUse; -import org.kohsuke.stapler.Stapler; -import org.kohsuke.stapler.StaplerRequest; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.UsernameNotFoundException; - -import static com.cloudbees.plugins.credentials.CredentialsStoreAction.FINGERPRINT_XML; /** * An extension point for providing {@link Credentials}. @@ -205,16 +208,16 @@ public abstract class CredentialsProvider extends Descriptor List lookupCredentialsInItem(@NonNull C for (CredentialsProvider provider : all()) { if (provider.isEnabled(item) && provider.isApplicable(type)) { try { - for (C c: provider.getCredentialsInItem(type, item, authentication, domainRequirements)) { + List credentials = provider.getCredentialsInItem(type, item, authentication, domainRequirements); + // also lookup credentials as SYSTEM if granted for this item + if (authentication != ACL.SYSTEM2 + && (item.getACL().hasPermission2(authentication, CredentialsProvider.USE_ITEM) + || item.getACL().hasPermission2(authentication, CredentialsProvider.USE_OWN))) { + credentials.addAll(provider.getCredentialsInItem(type, item, ACL.SYSTEM2, domainRequirements)); + } + + for (C c: credentials) { if (!(c instanceof IdCredentials) || ids.add(((IdCredentials) c).getId())) { // if IdCredentials, only add if we haven't added already // if not IdCredentials, always add @@ -633,9 +644,14 @@ public static ListBoxModel listCredentialsInItem(@NonN for (CredentialsProvider provider : all()) { if (provider.isEnabled(item) && provider.isApplicable(type)) { try { - for (ListBoxModel.Option option : provider.getCredentialIdsInItem( - type, item, authentication, domainRequirements, matcher == null ? CredentialsMatchers.always() : matcher) - ) { + ListBoxModel credentialIds = provider.getCredentialIdsInItem(type, item, authentication, domainRequirements, matcher); + // also lookup credentials with scope SYSTEM when user has grants for this item + if (authentication != ACL.SYSTEM2 + && (item.getACL().hasPermission2(authentication, CredentialsProvider.USE_ITEM) + || item.getACL().hasPermission2(authentication, CredentialsProvider.USE_OWN))) { + credentialIds.addAll(provider.getCredentialIdsInItem(type, item, ACL.SYSTEM2, domainRequirements, matcher)); + } + for (ListBoxModel.Option option : credentialIds) { if (ids.add(option.value)) { result.add(option); } @@ -713,6 +729,7 @@ public static Iterable lookupStores(final ModelObject context) private Iterator iterator = providers.iterator(); private CredentialsStore next; + @Override public boolean hasNext() { if (next != null) { return true; @@ -773,6 +790,7 @@ public boolean hasNext() { return false; } + @Override public CredentialsStore next() { if (!hasNext()) { throw new NoSuchElementException(); @@ -869,6 +887,7 @@ public static C findCredentialById(@NonNull String id, /** * @deprecated Use {@link #findCredentialById(String, Class, Run, List)} instead. */ + @Deprecated public static C findCredentialById(@NonNull String id, @NonNull Class type, @NonNull Run run, DomainRequirement... domainRequirements) {