com.cloudbees.plugins.credentials.CredentialsProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.cloudbees.plugins.credentials.CredentialsProvider.java

Source

/*
 * The MIT License
 *
 * Copyright (c) 2011-2016, CloudBees, Inc., Stephen Connolly.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.cloudbees.plugins.credentials;

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;
import hudson.BulkChange;
import hudson.DescriptorExtensionList;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.Util;
import hudson.init.InitMilestone;
import hudson.model.Cause;
import hudson.model.Computer;
import hudson.model.ComputerSet;
import hudson.model.Describable;
import hudson.model.Descriptor;
import hudson.model.DescriptorVisibilityFilter;
import hudson.model.Fingerprint;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.Job;
import hudson.model.ModelObject;
import hudson.model.Node;
import hudson.model.ParameterValue;
import hudson.model.ParametersAction;
import hudson.model.Queue;
import hudson.model.Run;
import hudson.model.User;
import hudson.model.queue.Tasks;
import hudson.security.ACL;
import hudson.security.Permission;
import hudson.security.PermissionGroup;
import hudson.security.PermissionScope;
import hudson.security.SecurityRealm;
import hudson.util.ListBoxModel;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
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.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.model.FingerprintFacet;
import jenkins.model.Jenkins;
import jenkins.util.Timer;
import org.acegisecurity.Authentication;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.NullOutputStream;
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.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;

import static com.cloudbees.plugins.credentials.CredentialsStoreAction.FINGERPRINT_XML;

/**
 * An extension point for providing {@link Credentials}.
 */
public abstract class CredentialsProvider extends Descriptor<CredentialsProvider>
        implements ExtensionPoint, Describable<CredentialsProvider>, IconSpec {

    /**
     * A {@link CredentialsProvider} that does nothing for use as a marker
     *
     * @since 2.1.1
     */
    public static final CredentialsProvider NONE = new CredentialsProvider() {
        /**
         * {@inheritDoc}
         */
        @NonNull
        @Override
        public <C extends Credentials> List<C> getCredentials(@NonNull Class<C> type, @Nullable ItemGroup itemGroup,
                @Nullable Authentication authentication) {
            return Collections.emptyList();
        }
    };

    /**
     * The permission group for credentials.
     *
     * @since 1.8
     */
    public static final PermissionGroup GROUP = new PermissionGroup(CredentialsProvider.class,
            Messages._CredentialsProvider_PermissionGroupTitle());
    /**
     * Where an immediate action against a job requires that a credential be selected by the user triggering the
     * action, this permission allows the user to select a credential from their private credential store. Immediate
     * actions could include: building with parameters, tagging a build, deploying artifacts, etc.
     *
     * @since 1.16
     */
    public static final Permission USE_OWN = new Permission(GROUP, "UseOwn",
            Messages._CredentialsProvider_UseOwnPermissionDescription(),
            Boolean.getBoolean("com.cloudbees.plugins.credentials.UseOwnPermission") ? Jenkins.ADMINISTER
                    : Job.BUILD,
            Boolean.getBoolean("com.cloudbees.plugins.credentials.UseOwnPermission"),
            new PermissionScope[] { PermissionScope.ITEM });
    /**
     * Where an immediate action against a job requires that a credential be selected by the user triggering the
     * action, this permission allows the user to select a credential from those credentials available within the
     * scope of the job. Immediate actions could include: building with parameters, tagging a build,
     * deploying artifacts, etc.
     *
     * This permission is implied by {@link Job#CONFIGURE} as anyone who can configure the job can configure the
     * job to use credentials within the item scope anyway.
     *
     * @since 1.16
     */
    public static final Permission USE_ITEM = new Permission(GROUP, "UseItem",
            Messages._CredentialsProvider_UseItemPermissionDescription(), Job.CONFIGURE,
            Boolean.getBoolean("com.cloudbees.plugins.credentials.UseItemPermission"),
            new PermissionScope[] { PermissionScope.ITEM });
    /**
     * Our logger.
     *
     * @since 1.6
     */
    private static final Logger LOGGER = Logger.getLogger(CredentialsProvider.class.getName());
    /**
     * The scopes that we allow credential permissions on.
     *
     * @since 1.12.
     */
    private static final PermissionScope[] SCOPES = new PermissionScope[] { PermissionScope.ITEM,
            PermissionScope.ITEM_GROUP, PermissionScope.JENKINS };
    /**
     * The permission for adding credentials to a {@link CredentialsStore}.
     *
     * @since 1.8
     */
    public static final Permission CREATE = new Permission(GROUP, "Create",
            Messages._CredentialsProvider_CreatePermissionDescription(), Permission.CREATE, true, SCOPES);
    /**
     * The permission for updating credentials in a {@link CredentialsStore}.
     *
     * @since 1.8
     */
    public static final Permission UPDATE = new Permission(GROUP, "Update",
            Messages._CredentialsProvider_UpdatePermissionDescription(), Permission.UPDATE, true, SCOPES);
    /**
     * The permission for viewing credentials in a {@link CredentialsStore}.
     *
     * @since 1.8
     */
    public static final Permission VIEW = new Permission(GROUP, "View",
            Messages._CredentialsProvider_ViewPermissionDescription(), Permission.READ, true, SCOPES);
    /**
     * The permission for removing credentials from a {@link CredentialsStore}.
     *
     * @since 1.8
     */
    public static final Permission DELETE = new Permission(GROUP, "Delete",
            Messages._CredentialsProvider_DeletePermissionDescription(), Permission.DELETE, true, SCOPES);
    /**
     * The permission for managing credential domains in a {@link CredentialsStore}.
     *
     * @since 1.8
     */
    public static final Permission MANAGE_DOMAINS = new Permission(GROUP, "ManageDomains",
            Messages._CredentialsProvider_ManageDomainsPermissionDescription(), Permission.CONFIGURE, true, SCOPES);

    /**
     * Default constructor.
     */
    @SuppressWarnings("unchecked")
    public CredentialsProvider() {
        super(Descriptor.self());
    }

    /**
     * Returns all the registered {@link com.cloudbees.plugins.credentials.Credentials} descriptors.
     *
     * @return all the registered {@link com.cloudbees.plugins.credentials.Credentials} descriptors.
     */
    public static DescriptorExtensionList<Credentials, CredentialsDescriptor> allCredentialsDescriptors() {
        // TODO switch to Jenkins.getInstance() once 2.0+ is the baseline
        return Jenkins.getActiveInstance().getDescriptorList(Credentials.class);
    }

    /**
     * Returns all credentials which are available to the {@link ACL#SYSTEM} {@link Authentication}
     * within the {@link jenkins.model.Jenkins#getInstance()}.
     *
     * @param type the type of credentials to get.
     * @param <C>  the credentials type.
     * @return the list of credentials.
     * @deprecated use {@link #lookupCredentials(Class, Item, Authentication, List)},
     * {@link #lookupCredentials(Class, Item, Authentication, DomainRequirement...)},
     * {@link #lookupCredentials(Class, ItemGroup, Authentication, List)}
     * or {@link #lookupCredentials(Class, ItemGroup, Authentication, DomainRequirement...)}
     */
    @Deprecated
    @NonNull
    @SuppressWarnings("unused") // API entry point for consumers
    public static <C extends Credentials> List<C> lookupCredentials(@NonNull Class<C> type) {
        return lookupCredentials(type, (Item) null, ACL.SYSTEM);
    }

    /**
     * Returns all credentials which are available to the specified {@link Authentication}
     * within the {@link jenkins.model.Jenkins#getInstance()}.
     *
     * @param type           the type of credentials to get.
     * @param authentication the authentication.
     * @param <C>            the credentials type.
     * @return the list of credentials.
     * @deprecated use {@link #lookupCredentials(Class, Item, Authentication, List)},
     * {@link #lookupCredentials(Class, Item, Authentication, DomainRequirement...)},
     * {@link #lookupCredentials(Class, ItemGroup, Authentication, List)}
     * or {@link #lookupCredentials(Class, ItemGroup, Authentication, DomainRequirement...)}
     */
    @Deprecated
    @NonNull
    @SuppressWarnings("unused") // API entry point for consumers
    public static <C extends Credentials> List<C> lookupCredentials(@NonNull Class<C> type,
            @Nullable Authentication authentication) {
        return lookupCredentials(type, Jenkins.getInstance(), authentication);
    }

    /**
     * Returns all credentials which are available to the {@link ACL#SYSTEM} {@link Authentication}
     * for use by the specified {@link Item}.
     *
     * @param type the type of credentials to get.
     * @param item the item.
     * @param <C>  the credentials type.
     * @return the list of credentials.
     * @deprecated use {@link #lookupCredentials(Class, Item, Authentication, List)}
     * or {@link #lookupCredentials(Class, Item, Authentication, DomainRequirement...)}
     */
    @Deprecated
    @NonNull
    @SuppressWarnings("unused") // API entry point for consumers
    public static <C extends Credentials> List<C> lookupCredentials(@NonNull Class<C> type, @Nullable Item item) {
        return item == null ? lookupCredentials(type, Jenkins.getInstance(), ACL.SYSTEM)
                : lookupCredentials(type, item, ACL.SYSTEM);
    }

    /**
     * Returns all credentials which are available to the {@link ACL#SYSTEM} {@link Authentication}
     * for use by the {@link Item}s in the specified {@link ItemGroup}.
     *
     * @param type      the type of credentials to get.
     * @param itemGroup the item group.
     * @param <C>       the credentials type.
     * @return the list of credentials.
     * @deprecated use {@link #lookupCredentials(Class, ItemGroup, Authentication, List)}
     * or {@link #lookupCredentials(Class, ItemGroup, Authentication, DomainRequirement...)}
     */
    @Deprecated
    @NonNull
    @SuppressWarnings("unused") // API entry point for consumers
    public static <C extends Credentials> List<C> lookupCredentials(@NonNull Class<C> type,
            @Nullable ItemGroup itemGroup) {
        return lookupCredentials(type, itemGroup, ACL.SYSTEM);
    }

    /**
     * Returns all credentials which are available to the specified {@link Authentication}
     * for use by the {@link Item}s in the specified {@link ItemGroup}.
     *
     * @param type           the type of credentials to get.
     * @param itemGroup      the item group.
     * @param authentication the authentication.
     * @param <C>            the credentials type.
     * @return the list of credentials.
     * @deprecated use {@link #lookupCredentials(Class, ItemGroup, Authentication, List)}
     * or {@link #lookupCredentials(Class, ItemGroup, Authentication, DomainRequirement...)}
     */
    @Deprecated
    @NonNull
    @SuppressWarnings({ "unchecked", "unused" }) // API entry point for consumers
    public static <C extends Credentials> List<C> lookupCredentials(@NonNull Class<C> type,
            @Nullable ItemGroup itemGroup, @Nullable Authentication authentication) {
        return lookupCredentials(type, itemGroup, authentication, Collections.<DomainRequirement>emptyList());
    }

    /**
     * Returns all credentials which are available to the specified {@link Authentication}
     * for use by the specified {@link Item}.
     *
     * @param type           the type of credentials to get.
     * @param authentication the authentication.
     * @param item           the item.
     * @param <C>            the credentials type.
     * @return the list of credentials.
     * @deprecated use {@link #lookupCredentials(Class, Item, Authentication, List)}
     * or {@link #lookupCredentials(Class, Item, Authentication, DomainRequirement...)}
     */
    @Deprecated
    @NonNull
    @SuppressWarnings("unused") // API entry point for consumers
    public static <C extends Credentials> List<C> lookupCredentials(@NonNull Class<C> type, @Nullable Item item,
            @Nullable Authentication authentication) {
        return lookupCredentials(type, item, authentication, Collections.<DomainRequirement>emptyList());
    }

    /**
     * Returns all credentials which are available to the specified {@link Authentication}
     * for use by the {@link Item}s in the specified {@link ItemGroup}.
     *
     * @param type               the type of credentials to get.
     * @param itemGroup          the item group.
     * @param authentication     the authentication.
     * @param domainRequirements the credential domains to match.
     * @param <C>                the credentials type.
     * @return the list of credentials.
     * @since 1.5
     */
    @NonNull
    @SuppressWarnings({ "unchecked", "unused" }) // API entry point for consumers
    public static <C extends Credentials> List<C> lookupCredentials(@NonNull Class<C> type,
            @Nullable ItemGroup itemGroup, @Nullable Authentication authentication,
            @Nullable DomainRequirement... domainRequirements) {
        return lookupCredentials(type, itemGroup, authentication, Arrays.asList(domainRequirements));
    }

    /**
     * Returns all credentials which are available to the specified {@link Authentication}
     * for use by the {@link Item}s in the specified {@link ItemGroup}.
     *
     * @param type               the type of credentials to get.
     * @param itemGroup          the item group.
     * @param authentication     the authentication.
     * @param domainRequirements the credential domains to match.
     * @param <C>                the credentials type.
     * @return the list of credentials.
     * @since 1.5
     */
    @NonNull
    @SuppressWarnings({ "unchecked", "unused" }) // API entry point for consumers
    public static <C extends Credentials> List<C> lookupCredentials(@NonNull Class<C> type,
            @Nullable ItemGroup itemGroup, @Nullable Authentication authentication,
            @Nullable List<DomainRequirement> domainRequirements) {
        type.getClass(); // throw NPE if null
        // TODO switch to Jenkins.getInstance() once 2.0+ is the baseline
        Jenkins jenkins = Jenkins.getActiveInstance();
        itemGroup = itemGroup == null ? jenkins : itemGroup;
        authentication = authentication == null ? ACL.SYSTEM : authentication;
        domainRequirements = domainRequirements == null ? Collections.<DomainRequirement>emptyList()
                : domainRequirements;
        CredentialsResolver<Credentials, C> resolver = CredentialsResolver.getResolver(type);
        if (resolver != null) {
            LOGGER.log(Level.FINE, "Resolving legacy credentials of type {0} with resolver {1}",
                    new Object[] { type, resolver });
            final List<Credentials> originals = lookupCredentials(resolver.getFromClass(), itemGroup,
                    authentication, domainRequirements);
            LOGGER.log(Level.FINE, "Original credentials for resolving: {0}", originals);
            return resolver.resolve(originals);
        }
        List<C> result = new ArrayList<C>();
        Set<String> ids = new HashSet<String>();
        for (CredentialsProvider provider : all()) {
            if (provider.isEnabled(itemGroup) && provider.isApplicable(type)) {
                try {
                    for (C c : provider.getCredentials(type, itemGroup, authentication, domainRequirements)) {
                        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);
                        }
                    }
                } catch (NoClassDefFoundError e) {
                    LOGGER.log(Level.FINE, "Could not retrieve provider credentials from " + provider
                            + " likely due to missing optional dependency", e);
                }
            }
        }
        Collections.sort(result, new CredentialsNameComparator());
        return result;
    }

    /**
     * Returns a {@link ListBoxModel} of all credentials which are available to the specified {@link Authentication}
     * for use by the {@link Item}s in the specified {@link ItemGroup}.
     *
     * @param type               the type of credentials to get.
     * @param authentication     the authentication.
     * @param itemGroup          the item group.
     * @param domainRequirements the credential domains to match.
     * @param matcher            the additional filtering to apply to the credentials
     * @param <C>                the credentials type.
     * @return the {@link ListBoxModel} of {@link IdCredentials#getId()} with the corresponding display names as
     * provided by {@link CredentialsNameProvider}.
     * @since 2.1.0
     */
    public static <C extends IdCredentials> ListBoxModel listCredentials(@NonNull Class<C> type,
            @Nullable ItemGroup itemGroup, @Nullable Authentication authentication,
            @Nullable List<DomainRequirement> domainRequirements, @Nullable CredentialsMatcher matcher) {
        type.getClass(); // throw NPE if null
        // TODO switch to Jenkins.getInstance() once 2.0+ is the baseline
        Jenkins jenkins = Jenkins.getActiveInstance();
        itemGroup = itemGroup == null ? jenkins : itemGroup;
        authentication = authentication == null ? ACL.SYSTEM : authentication;
        domainRequirements = domainRequirements == null ? Collections.<DomainRequirement>emptyList()
                : domainRequirements;
        matcher = matcher == null ? CredentialsMatchers.always() : matcher;
        CredentialsResolver<Credentials, C> resolver = CredentialsResolver.getResolver(type);
        if (resolver != null && IdCredentials.class.isAssignableFrom(resolver.getFromClass())) {
            LOGGER.log(Level.FINE, "Listing legacy credentials of type {0} identified by resolver {1}",
                    new Object[] { type, resolver });
            return listCredentials((Class) resolver.getFromClass(), itemGroup, authentication, domainRequirements,
                    matcher);
        }
        ListBoxModel result = new ListBoxModel();
        Set<String> ids = new HashSet<String>();
        for (CredentialsProvider provider : all()) {
            if (provider.isEnabled(itemGroup) && provider.isApplicable(type)) {
                try {
                    for (ListBoxModel.Option option : provider.getCredentialIds(type, itemGroup, authentication,
                            domainRequirements, matcher)) {
                        if (ids.add(option.value)) {
                            result.add(option);
                        }
                    }
                } catch (NoClassDefFoundError e) {
                    LOGGER.log(Level.FINE, "Could not retrieve provider credentials from " + provider
                            + " likely due to missing optional dependency", e);
                }
            }
        }
        Collections.sort(result, new ListBoxModelOptionComparator());
        return result;
    }

    /**
     * Returns all credentials which are available to the specified {@link Authentication}
     * for use by the specified {@link Item}.
     *
     * @param type               the type of credentials to get.
     * @param authentication     the authentication.
     * @param item               the item.
     * @param domainRequirements the credential domains to match.
     * @param <C>                the credentials type.
     * @return the list of credentials.
     * @since 1.5
     */
    @NonNull
    @SuppressWarnings("unused") // API entry point for consumers
    public static <C extends Credentials> List<C> lookupCredentials(@NonNull Class<C> type, @Nullable Item item,
            @Nullable Authentication authentication, DomainRequirement... domainRequirements) {
        return lookupCredentials(type, item, authentication, Arrays.asList(domainRequirements));
    }

    /**
     * Returns all credentials which are available to the specified {@link Authentication}
     * for use by the specified {@link Item}.
     *
     * @param type               the type of credentials to get.
     * @param authentication     the authentication.
     * @param item               the item.
     * @param domainRequirements the credential domains to match.
     * @param <C>                the credentials type.
     * @return the list of credentials.
     * @since 1.5
     */
    @NonNull
    @SuppressWarnings("unused") // API entry point for consumers
    public static <C extends Credentials> List<C> lookupCredentials(@NonNull Class<C> type, @Nullable Item item,
            @Nullable Authentication authentication, @Nullable List<DomainRequirement> domainRequirements) {
        type.getClass(); // throw NPE if null
        if (item == null) {
            return lookupCredentials(type, Jenkins.getInstance(), authentication, domainRequirements);
        }
        if (item instanceof ItemGroup) {
            return lookupCredentials(type, (ItemGroup) item, authentication, domainRequirements);
        }
        authentication = authentication == null ? ACL.SYSTEM : authentication;
        domainRequirements = domainRequirements == null ? Collections.<DomainRequirement>emptyList()
                : domainRequirements;
        CredentialsResolver<Credentials, C> resolver = CredentialsResolver.getResolver(type);
        if (resolver != null) {
            LOGGER.log(Level.FINE, "Resolving legacy credentials of type {0} with resolver {1}",
                    new Object[] { type, resolver });
            final List<Credentials> originals = lookupCredentials(resolver.getFromClass(), item, authentication,
                    domainRequirements);
            LOGGER.log(Level.FINE, "Original credentials for resolving: {0}", originals);
            return resolver.resolve(originals);
        }
        List<C> result = new ArrayList<C>();
        Set<String> ids = new HashSet<String>();
        for (CredentialsProvider provider : all()) {
            if (provider.isEnabled(item) && provider.isApplicable(type)) {
                try {
                    for (C c : provider.getCredentials(type, item, authentication, domainRequirements)) {
                        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);
                        }
                    }
                } catch (NoClassDefFoundError e) {
                    LOGGER.log(Level.FINE, "Could not retrieve provider credentials from " + provider
                            + " likely due to missing optional dependency", e);
                }
            }
        }

        Collections.sort(result, new CredentialsNameComparator());
        return result;
    }

    /**
     * Returns a {@link ListBoxModel} of all credentials which are available to the specified {@link Authentication}
     * for use by the specified {@link Item}.
     *
     * @param type               the type of credentials to get.
     * @param authentication     the authentication.
     * @param item               the item.
     * @param domainRequirements the credential domains to match.
     * @param matcher            the additional filtering to apply to the credentials
     * @param <C>                the credentials type.
     * @return the {@link ListBoxModel} of {@link IdCredentials#getId()} with the corresponding display names as
     * provided by {@link CredentialsNameProvider}.
     * @since 2.1.0
     */
    @NonNull
    public static <C extends IdCredentials> ListBoxModel listCredentials(@NonNull Class<C> type,
            @Nullable Item item, @Nullable Authentication authentication,
            @Nullable List<DomainRequirement> domainRequirements, @Nullable CredentialsMatcher matcher) {
        type.getClass(); // throw NPE if null
        if (item == null) {
            return listCredentials(type, Jenkins.getInstance(), authentication, domainRequirements, matcher);
        }
        if (item instanceof ItemGroup) {
            return listCredentials(type, (ItemGroup) item, authentication, domainRequirements, matcher);
        }
        authentication = authentication == null ? ACL.SYSTEM : authentication;
        domainRequirements = domainRequirements == null ? Collections.<DomainRequirement>emptyList()
                : domainRequirements;
        CredentialsResolver<Credentials, C> resolver = CredentialsResolver.getResolver(type);
        if (resolver != null && IdCredentials.class.isAssignableFrom(resolver.getFromClass())) {
            LOGGER.log(Level.FINE, "Listing legacy credentials of type {0} identified by resolver {1}",
                    new Object[] { type, resolver });
            return listCredentials((Class) resolver.getFromClass(), item, authentication, domainRequirements,
                    matcher);
        }
        ListBoxModel result = new ListBoxModel();
        Set<String> ids = new HashSet<String>();
        for (CredentialsProvider provider : all()) {
            if (provider.isEnabled(item) && provider.isApplicable(type)) {
                try {
                    for (ListBoxModel.Option option : provider.getCredentialIds(type, item, authentication,
                            domainRequirements, matcher)) {
                        if (ids.add(option.value)) {
                            result.add(option);
                        }
                    }
                } catch (NoClassDefFoundError e) {
                    LOGGER.log(Level.FINE, "Could not retrieve provider credentials from " + provider
                            + " likely due to missing optional dependency", e);
                }
            }
        }
        Collections.sort(result, new ListBoxModelOptionComparator());
        return result;
    }

    /**
     * Returns the scopes allowed for credentials stored within the specified object or {@code null} if the
     * object is not relevant for scopes and the object's container should be considered instead.
     *
     * @param object the object.
     * @return the set of scopes that are relevant for the object or {@code null} if the object is not a credentials
     * container.
     */
    @CheckForNull
    public static Set<CredentialsScope> lookupScopes(ModelObject object) {
        object = CredentialsDescriptor.unwrapContext(object);
        Set<CredentialsScope> result = null;
        for (CredentialsProvider provider : all()) {
            if (provider.isEnabled(object)) {
                try {
                    Set<CredentialsScope> scopes = provider.getScopes(object);
                    if (scopes != null) {
                        // if multiple providers for the same object, then combine scopes
                        if (result == null) {
                            result = new LinkedHashSet<CredentialsScope>();
                        }
                        result.addAll(scopes);
                    }
                } catch (NoClassDefFoundError e) {
                    // ignore optional dependency
                }
            }
        }
        return result;
    }

    /**
     * Tests if the supplied context has any credentials stores associated with it.
     *
     * @param context the context object.
     * @return {@code true} if and only if the supplied context has at least one {@link CredentialsStore} associated
     * with it.
     * @since 2.1.5
     */
    public static boolean hasStores(final ModelObject context) {
        for (CredentialsProvider p : all()) {
            if (p.isEnabled(context) && p.getStore(context) != null) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns a lazy {@link Iterable} of all the {@link CredentialsStore} instances contributing credentials to the
     * supplied object.
     *
     * @param context the {@link Item} or {@link ItemGroup} or {@link User} to get the {@link CredentialsStore}s of.
     * @return a lazy {@link Iterable} of all {@link CredentialsStore} instances.
     * @since 1.8
     */
    public static Iterable<CredentialsStore> lookupStores(final ModelObject context) {
        final ExtensionList<CredentialsProvider> providers = all();
        return new Iterable<CredentialsStore>() {
            public Iterator<CredentialsStore> iterator() {
                return new Iterator<CredentialsStore>() {
                    private ModelObject current = context;
                    private Iterator<CredentialsProvider> iterator = providers.iterator();
                    private CredentialsStore next;

                    public boolean hasNext() {
                        if (next != null) {
                            return true;
                        }
                        while (current != null) {
                            while (iterator.hasNext()) {
                                CredentialsProvider p = iterator.next();
                                if (!p.isEnabled(context)) {
                                    continue;
                                }
                                next = p.getStore(current);
                                if (next != null) {
                                    return true;
                                }
                            }
                            // now walk up the model object tree
                            // TODO make this an extension point perhaps ContextResolver could help
                            if (current instanceof Item) {
                                current = ((Item) current).getParent();
                                iterator = providers.iterator();
                            } else if (current instanceof User) {
                                Jenkins jenkins = Jenkins.getActiveInstance();
                                Authentication a;
                                if (jenkins.hasPermission(USE_ITEM) && current == User.current()) {
                                    // this is the fast path for the 99% of cases
                                    a = Jenkins.getAuthentication();
                                } else {
                                    try {
                                        a = ((User) current).impersonate();
                                    } catch (UsernameNotFoundException e) {
                                        a = null;
                                    }
                                }
                                if (current == User.current() && jenkins.getACL().hasPermission(a, USE_ITEM)) {
                                    current = jenkins;
                                    iterator = providers.iterator();
                                } else {
                                    current = null;
                                }
                            } else if (current instanceof Jenkins) {
                                // escape
                                current = null;
                            } else if (current instanceof ComputerSet) {
                                current = Jenkins.getActiveInstance();
                                iterator = providers.iterator();
                            } else if (current instanceof Computer) {
                                current = Jenkins.getActiveInstance();
                                iterator = providers.iterator();
                            } else if (current instanceof Node) {
                                current = Jenkins.getActiveInstance();
                                iterator = providers.iterator();
                            } else {
                                // fall back to Jenkins as the ultimate parent of everything else
                                current = Jenkins.getActiveInstance();
                                iterator = providers.iterator();
                            }
                        }
                        return false;
                    }

                    public CredentialsStore next() {
                        if (!hasNext()) {
                            throw new NoSuchElementException();
                        }
                        try {
                            return next;
                        } finally {
                            next = null;
                        }
                    }

                    public void remove() {
                        throw new UnsupportedOperationException();
                    }
                };
            }
        };
    }

    /**
     * Make a best effort to ensure that the supplied credential is a snapshot credential (i.e. self-contained and
     * does not reference any external stores). <b>WARNING:</b> May produce unusual results if presented an exotic
     * credential that implements multiple distinct credential types at the same time, e.g. a credential that is
     * simultaneously a TLS certificate and a SSH key pair and a GPG key pair all at the same time... unless the
     * author of that credential type also provides a {@link CredentialsSnapshotTaker} that can handle such a
     * tripple play.
     *
     * @param credential the credential.
     * @param <C>        the type of credential.
     * @return the credential or a snapshot of the credential.
     * @since 1.14
     */
    @SuppressWarnings("unchecked")
    public static <C extends Credentials> C snapshot(C credential) {
        return (C) snapshot(Credentials.class, credential);
    }

    /**
     * Make a best effort to ensure that the supplied credential is a snapshot credential (i.e. self-contained and
     * does not reference any external stores)
     *
     * @param clazz      the type of credential that we are trying to snapshot (specified so that if there is more than
     *                   one type of snapshot able credential interface implemented by the credentials,
     *                   then they can be separated out.
     * @param credential the credential.
     * @param <C>        the type of credential.
     * @return the credential or a snapshot of the credential.
     * @since 1.14
     */
    @SuppressWarnings("unchecked")
    public static <C extends Credentials> C snapshot(Class<C> clazz, C credential) {
        Class bestType = null;
        CredentialsSnapshotTaker bestTaker = null;
        for (CredentialsSnapshotTaker taker : ExtensionList.lookup(CredentialsSnapshotTaker.class)) {
            if (clazz.isAssignableFrom(taker.type()) && taker.type().isInstance(credential)) {
                if (bestTaker == null || bestType.isAssignableFrom(taker.type())) {
                    bestTaker = taker;
                    bestType = taker.type();
                }
            }
        }
        if (bestTaker == null) {
            return credential;
        }
        return clazz.cast(bestTaker.snapshot(credential));
    }

    /**
     * Helper method to get the default authentication to use for an {@link Item}.
     */
    @NonNull
    /*package*/ static Authentication getDefaultAuthenticationOf(Item item) {
        if (item instanceof Queue.Task) {
            return Tasks.getAuthenticationOf((Queue.Task) item);
        } else {
            return ACL.SYSTEM;
        }
    }

    /**
     * A common requirement for plugins is to resolve a specific credential by id in the context of a specific run.
     * Given that the credential itself could be resulting from a build parameter expression and the complexities of
     * determining the scope of items from which the credential should be resolved in a chain of builds, this method
     * provides the correct answer.
     *
     * @param id                 either the id of the credential to find or a parameter expression for the id.
     * @param type               the type of credential to find.
     * @param run                the {@link Run} defining the context within which to find the credential.
     * @param domainRequirements the domain requirements of the credential.
     * @param <C>                the credentials type.
     * @return the credential or {@code null} if either the credential cannot be found or the user triggering the run
     * is not permitted to use the credential in the context of the run.
     * @since 1.16
     */
    @CheckForNull
    public static <C extends IdCredentials> C findCredentialById(@NonNull String id, @NonNull Class<C> type,
            @NonNull Run<?, ?> run, DomainRequirement... domainRequirements) {
        return findCredentialById(id, type, run, Arrays.asList(domainRequirements));
    }

    /**
     * A common requirement for plugins is to resolve a specific credential by id in the context of a specific run.
     * Given that the credential itself could be resulting from a build parameter expression and the complexities of
     * determining the scope of items from which the credential should be resolved in a chain of builds, this method
     * provides the correct answer.
     *
     * @param id                 either the id of the credential to find or a parameter expression for the id.
     * @param type               the type of credential to find.
     * @param run                the {@link Run} defining the context within which to find the credential.
     * @param domainRequirements the domain requirements of the credential.
     * @param <C>                the credentials type.
     * @return the credential or {@code null} if either the credential cannot be found or the user triggering the run
     * is not permitted to use the credential in the context of the run.
     * @since 1.16
     */
    @CheckForNull
    public static <C extends IdCredentials> C findCredentialById(@NonNull String id, @NonNull Class<C> type,
            @NonNull Run<?, ?> run, @Nullable List<DomainRequirement> domainRequirements) {
        id.getClass(); // throw NPE if null;
        type.getClass(); // throw NPE if null;
        run.getClass(); // throw NPE if null;

        // first we need to find out if this id is pre-selected or a parameter
        id = id.trim();
        boolean isParameter = false;
        boolean isDefaultValue = false;
        if (id.startsWith("${") && id.endsWith("}")) {
            final ParametersAction action = run.getAction(ParametersAction.class);
            if (action != null) {
                final ParameterValue parameter = action.getParameter(id.substring(2, id.length() - 1));
                if (parameter instanceof CredentialsParameterValue) {
                    isParameter = true;
                    isDefaultValue = ((CredentialsParameterValue) parameter).isDefaultValue();
                    id = ((CredentialsParameterValue) parameter).getValue();
                }
            }
        }
        // non parameters or default parameter values can only come from the job's context
        if (!isParameter || isDefaultValue) {
            // we use the default authentication of the job as those are the only ones that can be configured
            // if a different strategy is in play it doesn't make sense to consider the run-time authentication
            // as you would have no way to configure it
            Authentication runAuth = CredentialsProvider.getDefaultAuthenticationOf(run.getParent());
            List<C> candidates = new ArrayList<C>();
            // we want the credentials available to the user the build is running as
            candidates.addAll(
                    CredentialsProvider.lookupCredentials(type, run.getParent(), runAuth, domainRequirements));
            // if that user can use the item's credentials, add those in too
            if (runAuth != ACL.SYSTEM && run.getACL().hasPermission(runAuth, CredentialsProvider.USE_ITEM)) {
                candidates.addAll(CredentialsProvider.lookupCredentials(type, run.getParent(), ACL.SYSTEM,
                        domainRequirements));
            }
            return CredentialsMatchers.firstOrNull(candidates, CredentialsMatchers.withId(id));
        }
        // this is a parameter and not the default value, we need to determine who triggered the build
        final Map.Entry<User, Run<?, ?>> triggeredBy = triggeredBy(run);
        final Authentication a = triggeredBy == null ? Jenkins.ANONYMOUS : triggeredBy.getKey().impersonate();
        List<C> candidates = new ArrayList<C>();
        if (triggeredBy != null && run == triggeredBy.getValue()
                && run.getACL().hasPermission(a, CredentialsProvider.USE_OWN)) {
            // the user triggered this job directly and they are allowed to supply their own credentials, so
            // add those into the list. We do not want to follow the chain for the user's authentication
            // though, as there is no way to limit how far the passed-through parameters can be used
            candidates.addAll(CredentialsProvider.lookupCredentials(type, run.getParent(), a, domainRequirements));
        }
        if (run.getACL().hasPermission(a, CredentialsProvider.USE_ITEM)) {
            // the triggering user is allowed to use the item's credentials, so add those into the list
            // we use the default authentication of the job as those are the only ones that can be configured
            // if a different strategy is in play it doesn't make sense to consider the run-time authentication
            // as you would have no way to configure it
            Authentication runAuth = CredentialsProvider.getDefaultAuthenticationOf(run.getParent());
            // we want the credentials available to the user the build is running as
            candidates.addAll(
                    CredentialsProvider.lookupCredentials(type, run.getParent(), runAuth, domainRequirements));
            // if that user can use the item's credentials, add those in too
            if (runAuth != ACL.SYSTEM && run.getACL().hasPermission(runAuth, CredentialsProvider.USE_ITEM)) {
                candidates.addAll(CredentialsProvider.lookupCredentials(type, run.getParent(), ACL.SYSTEM,
                        domainRequirements));
            }
        }
        C result = CredentialsMatchers.firstOrNull(candidates, CredentialsMatchers.withId(id));
        // if the run has not completed yet then we can safely assume that the credential is being used for this run
        // so we will track it's usage. We use isLogUpdated() as it could be used during post production
        return run.isLogUpdated() ? track(run, result) : result;
    }

    /**
     * Identifies the {@link User} and {@link Run} that triggered the supplied {@link Run}.
     *
     * @param run the {@link Run} to find the trigger of.
     * @return the trigger of the supplied run or {@code null} if this could not be determined.
     */
    @CheckForNull
    private static Map.Entry<User, Run<?, ?>> triggeredBy(Run<?, ?> run) {
        Cause.UserIdCause cause = run.getCause(Cause.UserIdCause.class);
        if (cause != null) {
            User u = User.get(cause.getUserId(), false, Collections.emptyMap());
            return u == null ? null : new AbstractMap.SimpleImmutableEntry<User, Run<?, ?>>(u, run);
        }
        Cause.UpstreamCause c = run.getCause(Cause.UpstreamCause.class);
        run = (c != null) ? c.getUpstreamRun() : null;
        while (run != null) {
            cause = run.getCause(Cause.UserIdCause.class);
            if (cause != null) {
                User u = User.get(cause.getUserId(), false, Collections.emptyMap());
                return u == null ? null : new AbstractMap.SimpleImmutableEntry<User, Run<?, ?>>(u, run);
            }
            c = run.getCause(Cause.UpstreamCause.class);

            run = (c != null) ? c.getUpstreamRun() : null;
        }
        return null;
    }

    /**
     * Returns the list of all {@link CredentialsProvider}.
     *
     * @return the list of all {@link CredentialsProvider}.
     */
    public static ExtensionList<CredentialsProvider> all() {
        return ExtensionList.lookup(CredentialsProvider.class);
    }

    /**
     * Returns only those {@link CredentialsProvider} that are {@link #isEnabled()}.
     *
     * @return a list of {@link CredentialsProvider} that are {@link #isEnabled()}.
     * @since 2.0
     */
    public static List<CredentialsProvider> enabled() {
        List<CredentialsProvider> providers = new ArrayList<CredentialsProvider>(
                ExtensionList.lookup(CredentialsProvider.class));
        for (Iterator<CredentialsProvider> iterator = providers.iterator(); iterator.hasNext();) {
            CredentialsProvider p = iterator.next();
            if (!p.isEnabled()) {
                iterator.remove();
            }
        }
        return providers;
    }

    /**
     * Returns only those {@link CredentialsProvider} that are {@link #isEnabled()} within a specific context.
     *
     * @param context the context in which to get the list.
     * @return a list of {@link CredentialsProvider} that are {@link #isEnabled()}.
     * @since 2.0
     */
    public static List<CredentialsProvider> enabled(Object context) {
        List<CredentialsProvider> providers = new ArrayList<CredentialsProvider>(
                ExtensionList.lookup(CredentialsProvider.class));
        for (Iterator<CredentialsProvider> iterator = providers.iterator(); iterator.hasNext();) {
            CredentialsProvider p = iterator.next();
            if (!p.isEnabled(context)) {
                iterator.remove();
            }
        }
        return providers;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Descriptor<CredentialsProvider> getDescriptor() {
        return this;
    }

    /**
     * Returns {@code true} if this {@link CredentialsProvider} is enabled.
     *
     * @return {@code true} if this {@link CredentialsProvider} is enabled.
     * @since 2.0
     */
    public final boolean isEnabled() {
        return CredentialsProviderManager.isEnabled(this);
    }

    /**
     * Returns {@code true} if this {@link CredentialsProvider} is enabled in the specified context.
     *
     * @param context the context.
     * @return {@code true} if this {@link CredentialsProvider} is enabled in the specified context.
     * @since 2.0
     */
    public boolean isEnabled(Object context) {
        if (!isEnabled()) {
            return false;
        }
        for (DescriptorVisibilityFilter filter : DescriptorVisibilityFilter.all()) {
            if (!filter.filter(context, this)) {
                return false;
            }
        }
        return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getDisplayName() {
        return StringUtils.join(StringUtils.splitByCharacterTypeCamelCase(getClass().getSimpleName()), ' ');
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getIconClassName() {
        return "icon-credentials-credentials";
    }

    /**
     * Returns the scopes allowed for credentials stored within the specified object or {@code null} if the
     * object is not relevant for scopes and the object's container should be considered instead.
     *
     * @param object the object.
     * @return the set of scopes that are relevant for the object or {@code null} if the object is not a credentials
     * container.
     */
    public Set<CredentialsScope> getScopes(ModelObject object) {
        return null;
    }

    /**
     * Returns the {@link CredentialsStore} that this {@link CredentialsProvider} maintains specifically for this
     * {@link ModelObject} or {@code null} if either the object is not a credentials container or this
     * {@link CredentialsProvider} does not maintain a store specifically bound to this {@link ModelObject}.
     *
     * @param object the {@link Item} or {@link ItemGroup} or {@link User} that the store is being requested of.
     * @return either {@code null} or a scoped {@link CredentialsStore} where
     * {@link com.cloudbees.plugins.credentials.CredentialsStore#getContext()} {@code == object}.
     * @since 1.8
     */
    @CheckForNull
    public CredentialsStore getStore(@CheckForNull ModelObject object) {
        return null;
    }

    /**
     * Returns the credentials provided by this provider which are available to the specified {@link Authentication}
     * for items in the specified {@link ItemGroup}
     *
     * @param type           the type of credentials to return.
     * @param itemGroup      the item group (if {@code null} assume {@link hudson.model.Hudson#getInstance()}.
     * @param authentication the authentication (if {@code null} assume {@link hudson.security.ACL#SYSTEM}.
     * @param <C>            the credentials type.
     * @return the list of credentials.
     */
    @NonNull
    public abstract <C extends Credentials> List<C> getCredentials(@NonNull Class<C> type,
            @Nullable ItemGroup itemGroup, @Nullable Authentication authentication);

    /**
     * Returns the credentials provided by this provider which are available to the specified {@link Authentication}
     * for items in the specified {@link ItemGroup} and are appropriate for the specified {@link com.cloudbees
     * .plugins.credentials.domains.DomainRequirement}s.
     *
     * @param type               the type of credentials to return.
     * @param itemGroup          the item group (if {@code null} assume {@link hudson.model.Hudson#getInstance()}.
     * @param authentication     the authentication (if {@code null} assume {@link hudson.security.ACL#SYSTEM}.
     * @param domainRequirements the credential domains to match (if the {@link CredentialsProvider} does not support
     *                           {@link DomainRequirement}s then it should
     *                           assume the match is true).
     * @param <C>                the credentials type.
     * @return the list of credentials.
     * @since 1.5
     */
    @NonNull
    public <C extends Credentials> List<C> getCredentials(@NonNull Class<C> type, @Nullable ItemGroup itemGroup,
            @Nullable Authentication authentication, @NonNull List<DomainRequirement> domainRequirements) {
        return getCredentials(type, itemGroup, authentication);
    }

    /**
     * Returns a {@link ListBoxModel} of the credentials provided by this provider which are available to the
     * specified {@link Authentication} for items in the specified {@link ItemGroup} and are appropriate for the
     * specified {@link DomainRequirement}s.
     * <strong>NOTE:</strong> implementations are recommended to override this method if the actual secret information
     * is being stored external from Jenkins and the non-secret information can be accessed with lesser tracability
     * requirements. The default implementation just uses {@link #getCredentials(Class, Item, Authentication, List)}
     * to build the {@link ListBoxModel}. Handling the {@link CredentialsMatcher} may require standing up a proxy
     * instance to apply the matcher against if {@link CredentialsMatchers#describe(CredentialsMatcher)} returns
     * {@code null}
     *
     * @param <C>                the credentials type.
     * @param type               the type of credentials to return.
     * @param itemGroup          the item group (if {@code null} assume {@link hudson.model.Hudson#getInstance()}.
     * @param authentication     the authentication (if {@code null} assume {@link ACL#SYSTEM}.
     * @param domainRequirements the credential domain to match.
     * @param matcher            the additional filtering to apply to the credentials
     * @return the {@link ListBoxModel} of {@link IdCredentials#getId()} with names provided by
     * {@link CredentialsNameProvider}.
     * @since 2.1.0
     */
    @NonNull
    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();
        for (IdCredentials c : getCredentials(type, itemGroup, authentication, domainRequirements)) {
            if (matcher.matches(c)) {
                result.add(CredentialsNameProvider.name(c), c.getId());
            }
        }
        return result;
    }

    /**
     * Returns the credentials provided by this provider which are available to the specified {@link Authentication}
     * for the specified {@link Item}
     *
     * @param type           the type of credentials to return.
     * @param item           the item.
     * @param authentication the authentication (if {@code null} assume {@link hudson.security.ACL#SYSTEM}.
     * @param <C>            the credentials type.
     * @return the list of credentials.
     */
    @NonNull
    public <C extends Credentials> List<C> getCredentials(@NonNull Class<C> type, @NonNull Item item,
            @Nullable Authentication authentication) {
        item.getClass();
        return getCredentials(type, item.getParent(), authentication);
    }

    /**
     * Returns the credentials provided by this provider which are available to the specified {@link Authentication}
     * for the specified {@link Item} and are appropriate for the specified {@link DomainRequirement}s.
     *
     * @param type               the type of credentials to return.
     * @param item               the item.
     * @param authentication     the authentication (if {@code null} assume {@link hudson.security.ACL#SYSTEM}.
     * @param domainRequirements the credential domain to match.
     * @param <C>                the credentials type.
     * @return the list of credentials.
     * @since 1.5
     */
    @NonNull
    public <C extends Credentials> List<C> getCredentials(@NonNull Class<C> type, @NonNull Item item,
            @Nullable Authentication authentication, @NonNull List<DomainRequirement> domainRequirements) {
        return getCredentials(type, item instanceof ItemGroup ? (ItemGroup) item : item.getParent(), authentication,
                domainRequirements);
    }

    /**
     * Returns a {@link ListBoxModel} of the credentials provided by this provider which are available to the
     * specified {@link Authentication} for the specified {@link Item} and are appropriate for the
     * specified {@link DomainRequirement}s.
     * <strong>NOTE:</strong> implementations are recommended to override this method if the actual secret information
     * is being stored external from Jenkins and the non-secret information can be accessed with lesser tracability
     * requirements. The default implementation just uses {@link #getCredentials(Class, Item, Authentication, List)}
     * to build the {@link ListBoxModel}. Handling the {@link CredentialsMatcher} may require standing up a proxy
     * instance to apply the matcher against.
     *
     * @param type               the type of credentials to return.
     * @param item               the item.
     * @param authentication     the authentication (if {@code null} assume {@link hudson.security.ACL#SYSTEM}.
     * @param domainRequirements the credential domain to match.
     * @param matcher            the additional filtering to apply to the credentials
     * @param <C>                the credentials type.
     * @return the {@link ListBoxModel} of {@link IdCredentials#getId()} with names provided by
     * {@link CredentialsNameProvider}.
     * @since 2.1.0
     */
    @NonNull
    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 ItemGroup) {
            return getCredentialIds(type, (ItemGroup) item, authentication, domainRequirements, matcher);
        }
        ListBoxModel result = new ListBoxModel();
        for (IdCredentials c : getCredentials(type, item, authentication, domainRequirements)) {
            if (matcher.matches(c)) {
                result.add(CredentialsNameProvider.name(c), c.getId());
            }
        }
        return result;
    }

    /**
     * Returns {@code true} if this {@link CredentialsProvider} can provide credentials of the supplied type.
     *
     * @param clazz the base type of {@link Credentials} to check.
     * @return {@code true} if and only if there is at least one {@link CredentialsDescriptor} matching the required
     * {@link Credentials} interface that {@link #isApplicable(Descriptor)}.
     * @since 2.0
     */
    public final boolean isApplicable(Class<? extends Credentials> clazz) {
        if (!isEnabled()) {
            return false;
        }
        for (CredentialsDescriptor d : ExtensionList.lookup(CredentialsDescriptor.class)) {
            if (clazz.isAssignableFrom(d.clazz) && isApplicable(d)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns {@code true} if the supplied {@link Descriptor} is applicable to this {@link CredentialsProvider}.
     *
     * @param descriptor the {@link Descriptor} to check.
     * @return {@code true} if and only if the supplied {@link Descriptor} is applicable in this
     * {@link CredentialsProvider}.
     * @since 2.0
     */
    public final boolean isApplicable(Descriptor<?> descriptor) {
        if (!isEnabled()) {
            return false;
        }
        if (descriptor instanceof CredentialsDescriptor) {
            if (!((CredentialsDescriptor) descriptor).isApplicable(this)) {
                return false;
            }
        }
        for (DescriptorVisibilityFilter filter : DescriptorVisibilityFilter.all()) {
            if (!filter.filter(this, descriptor)) {
                return false;
            }
        }
        return _isApplicable(descriptor);
    }

    /**
     * {@link CredentialsProvider} subtypes can override this method to veto some {@link Descriptor}s
     * from being available from their store. This is often useful when you are building
     * a custom store that holds a specific type of credentials or where you want to limit the
     * number of choices given to the users.
     *
     * @param descriptor the {@link Descriptor} to check.
     * @return {@code true} if the supplied {@link Descriptor} is applicable in this {@link CredentialsProvider}
     * @since 2.0
     */
    protected boolean _isApplicable(Descriptor<?> descriptor) {
        return true;
    }

    /**
     * Returns the list of {@link CredentialsDescriptor} instances that are applicable within this
     * {@link CredentialsProvider}.
     *
     * @return the list of {@link CredentialsDescriptor} instances that are applicable within this
     * {@link CredentialsProvider}.
     * @since 2.0
     */
    public final List<CredentialsDescriptor> getCredentialsDescriptors() {
        List<CredentialsDescriptor> result = DescriptorVisibilityFilter.apply(this,
                ExtensionList.lookup(CredentialsDescriptor.class));
        if (!(result instanceof ArrayList)) {
            // should never happen, but let's be defensive in case the DescriptorVisibilityFilter contract changes
            result = new ArrayList<CredentialsDescriptor>(result);
        }
        for (Iterator<CredentialsDescriptor> iterator = result.iterator(); iterator.hasNext();) {
            CredentialsDescriptor d = iterator.next();
            if (!_isApplicable(d)) {
                iterator.remove();
            }
        }
        return result;
    }

    /**
     * Checks if there is at least one {@link CredentialsDescriptor} applicable within this {@link CredentialsProvider}.
     *
     * @return {@code true} if and ony if there is at least one {@link CredentialsDescriptor} applicable within this
     * {@link CredentialsProvider}.
     * @since 2.0
     */
    public final boolean hasCredentialsDescriptors() {
        ExtensionList<DescriptorVisibilityFilter> filters = DescriptorVisibilityFilter.all();
        OUTER: for (CredentialsDescriptor d : ExtensionList.lookup(CredentialsDescriptor.class)) {
            for (DescriptorVisibilityFilter f : filters) {
                if (!f.filter(this, d)) {
                    // not visible, let's try the next descriptor
                    continue OUTER;
                }
            }
            if (_isApplicable(d)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Retrieves the {@link Fingerprint} for a specific credential.
     *
     * @param c the credential.
     * @return the {@link Fingerprint} or {@code null} if the credential has no fingerprint associated with it.
     * @throws IOException if the credential's fingerprint hash could not be computed.
     * @since 2.1.1
     */
    @CheckForNull
    public static Fingerprint getFingerprintOf(@NonNull Credentials c) throws IOException {
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            DigestOutputStream out = new DigestOutputStream(new NullOutputStream(), md5);
            try {
                FINGERPRINT_XML.toXML(c, new OutputStreamWriter(out, Charset.forName("UTF-8")));
            } finally {
                IOUtils.closeQuietly(out);
            }
            return Jenkins.getActiveInstance().getFingerprintMap().get(Util.toHexString(md5.digest()));
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("JLS mandates MD5 as a supported digest algorithm");
        }
    }

    /**
     * Creates a fingerprint that can be used to track the usage of a specific credential.
     *
     * @param c the credential to fingerprint.
     * @return the Fingerprint.
     * @throws IOException if the credential's fingerprint hash could not be computed.
     * @since 2.1.1
     */
    @NonNull
    public static Fingerprint getOrCreateFingerprintOf(@NonNull Credentials c) throws IOException {
        String pseudoFilename = String.format("Credential id=%s name=%s",
                c instanceof IdCredentials ? ((IdCredentials) c).getId() : "unknown",
                CredentialsNameProvider.name(c));
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            DigestOutputStream out = new DigestOutputStream(new NullOutputStream(), md5);
            try {
                FINGERPRINT_XML.toXML(c, new OutputStreamWriter(out, Charset.forName("UTF-8")));
            } finally {
                IOUtils.closeQuietly(out);
            }
            return Jenkins.getActiveInstance().getFingerprintMap().getOrCreate(null, pseudoFilename, md5.digest());
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("JLS mandates MD5 as a supported digest algorithm");
        }
    }

    /**
     * Track the usage of credentials in a specific build.
     *
     * @param build       the run to tag the fingerprint
     * @param credentials the credentials to fingerprint.
     * @param <C>         the credentials type.
     * @return the supplied credentials for method chaining.
     * @since 2.1.1
     */
    @CheckForNull
    public static <C extends Credentials> C track(@NonNull Run build, @CheckForNull C credentials) {
        if (credentials != null) {
            trackAll(build, Collections.singletonList(credentials));
        }
        return credentials;
    }

    /**
     * Track the usage of credentials in a specific build.
     *
     * @param build       the run to tag the fingerprint
     * @param credentials the credentials to fingerprint.
     * @param <C>         the credentials type.
     * @return the supplied credentials for method chaining.
     * @since 2.1.1
     */
    @NonNull
    public static <C extends Credentials> List<C> trackAll(@NonNull Run build, C... credentials) {
        if (credentials != null) {
            return trackAll(build, Arrays.asList(credentials));
        }
        return Collections.emptyList();
    }

    /**
     * Track the usage of credentials in a specific build.
     *
     * @param build       the run to tag the fingerprint
     * @param credentials the credentials to fingerprint.
     * @param <C>         the credentials type.
     * @return the supplied credentials for method chaining.
     * @since 2.1.1
     */
    @NonNull
    public static <C extends Credentials> List<C> trackAll(@NonNull Run build, @NonNull List<C> credentials) {
        for (Credentials c : credentials) {
            if (c != null) {
                try {
                    getOrCreateFingerprintOf(c).addFor(build);
                } catch (IOException e) {
                    LOGGER.log(Level.FINEST, "Could not track usage of " + c, e);
                }
            }
        }
        return credentials;
    }

    /**
     * Track the usage of credentials in a specific node.
     * Would be used for example when launching an agent.
     * @param node        the node to tag the fingerprint
     * @param credentials the credentials to fingerprint.
     * @param <C>         the credentials type.
     * @return the supplied credentials for method chaining.
     * @since 2.1.1
     */
    @CheckForNull
    public static <C extends Credentials> C track(@NonNull Node node, @CheckForNull C credentials) {
        if (credentials != null) {
            trackAll(node, Collections.singletonList(credentials));
        }
        return credentials;
    }

    /**
     * Track the usage of credentials in a specific node.
     * Would be used for example when launching an agent.
     * @param node        the node to tag the fingerprint
     * @param credentials the credentials to fingerprint.
     * @param <C>         the credentials type.
     * @return the supplied credentials for method chaining.
     * @since 2.1.1
     */
    @NonNull
    public static <C extends Credentials> List<C> trackAll(@NonNull Node node, C... credentials) {
        if (credentials != null) {
            return trackAll(node, Arrays.asList(credentials));
        }
        return Collections.emptyList();
    }

    /**
     * Track the usage of credentials in a specific node.
     * Would be used for example when launching an agent.
     * @param node        the node to tag the fingerprint
     * @param credentials the credentials to fingerprint.
     * @param <C>         the credentials type.
     * @return the supplied credentials for method chaining.
     * @since 2.1.1
     */
    @NonNull
    public static <C extends Credentials> List<C> trackAll(@NonNull Node node, @NonNull List<C> credentials) {
        long timestamp = System.currentTimeMillis();
        String nodeName = node.getNodeName();
        for (Credentials c : credentials) {
            if (c != null) {
                try {
                    Fingerprint fingerprint = getOrCreateFingerprintOf(c);
                    BulkChange change = new BulkChange(fingerprint);
                    try {
                        Collection<FingerprintFacet> facets = fingerprint.getFacets();
                        // purge any old facets
                        long start = timestamp;
                        for (Iterator<FingerprintFacet> iterator = facets.iterator(); iterator.hasNext();) {
                            FingerprintFacet f = iterator.next();
                            if (f instanceof NodeCredentialsFingerprintFacet && StringUtils.equals(nodeName,
                                    ((NodeCredentialsFingerprintFacet) f).getNodeName())) {
                                start = Math.min(start, f.getTimestamp());
                                iterator.remove();
                            }
                        }
                        // add in the new one
                        facets.add(new NodeCredentialsFingerprintFacet(node, fingerprint, start, timestamp));
                    } finally {
                        change.commit();
                    }
                } catch (IOException e) {
                    LOGGER.log(Level.FINEST, "Could not track usage of " + c, e);
                }
            }
        }
        return credentials;
    }

    /**
     * Track the usage of credentials in a specific item but not associated with a specific build, for example SCM
     * polling.
     *
     * @param item        the item to tag the fingerprint against
     * @param credentials the credentials to fingerprint.
     * @param <C>         the credentials type.
     * @return the supplied credentials for method chaining.
     * @since 2.1.1
     */
    @CheckForNull
    public static <C extends Credentials> C track(@NonNull Item item, @CheckForNull C credentials) {
        if (credentials != null) {
            trackAll(item, Collections.singletonList(credentials));
        }
        return credentials;
    }

    /**
     * Track the usage of credentials in a specific item but not associated with a specific build, for example SCM
     * polling.
     *
     * @param item        the item to tag the fingerprint against
     * @param credentials the credentials to fingerprint.
     * @param <C>         the credentials type.
     * @return the supplied credentials for method chaining.
     * @since 2.1.1
     */
    @NonNull
    public static <C extends Credentials> List<C> trackAll(@NonNull Item item, C... credentials) {
        if (credentials != null) {
            return trackAll(item, Arrays.asList(credentials));
        }
        return Collections.emptyList();
    }

    /**
     * Track the usage of credentials in a specific item but not associated with a specific build, for example SCM
     * polling.
     *
     * @param item        the item to tag the fingerprint against
     * @param credentials the credentials to fingerprint.
     * @param <C>         the credentials type.
     * @return the supplied credentials for method chaining.
     * @since 2.1.1
     */
    @NonNull
    public static <C extends Credentials> List<C> trackAll(@NonNull Item item, @NonNull List<C> credentials) {
        long timestamp = System.currentTimeMillis();
        String fullName = item.getFullName();
        for (Credentials c : credentials) {
            if (c != null) {
                try {
                    Fingerprint fingerprint = getOrCreateFingerprintOf(c);
                    BulkChange change = new BulkChange(fingerprint);
                    try {
                        Collection<FingerprintFacet> facets = fingerprint.getFacets();
                        // purge any old facets
                        long start = timestamp;
                        for (Iterator<FingerprintFacet> iterator = facets.iterator(); iterator.hasNext();) {
                            FingerprintFacet f = iterator.next();
                            if (f instanceof ItemCredentialsFingerprintFacet && StringUtils.equals(fullName,
                                    ((ItemCredentialsFingerprintFacet) f).getItemFullName())) {
                                start = Math.min(start, f.getTimestamp());
                                iterator.remove();
                            }
                        }
                        // add in the new one
                        facets.add(new ItemCredentialsFingerprintFacet(item, fingerprint, start, timestamp));
                    } finally {
                        change.commit();
                    }
                } catch (IOException e) {
                    LOGGER.log(Level.FINEST, "Could not track usage of " + c, e);
                }
            }
        }
        return credentials;
    }

    /**
     * A helper method for Groovy Scripting to address use cases such as JENKINS-39317 where all credential stores
     * need to be resaved. As this is a potentially very expensive operation the method has been marked
     * {@link DoNotUse} in order to ensure that no plugin attempts to call this method. If invoking this method
     * from an {@code init.d} Groovy script, ensure that the call is guarded by a marker file such that
     *
     * @throws IOException if things go wrong.
     */
    @Restricted(DoNotUse.class) // Do not use from plugins
    public static void saveAll() throws IOException {
        LOGGER.entering(CredentialsProvider.class.getName(), "saveAll");
        try {
            final Jenkins jenkins = Jenkins.getActiveInstance();
            jenkins.checkPermission(Jenkins.ADMINISTER);
            LOGGER.log(Level.INFO, "Forced save credentials stores: Requested by {0}",
                    StringUtils.defaultIfBlank(Jenkins.getAuthentication().getName(), "anonymous"));
            Timer.get().execute(new Runnable() {
                @Override
                public void run() {
                    SecurityContext ctx = ACL.impersonate(ACL.SYSTEM);
                    try {
                        if (jenkins.getInitLevel().compareTo(InitMilestone.JOB_LOADED) < 0) {
                            LOGGER.log(Level.INFO,
                                    "Forced save credentials stores: Initialization has not completed");
                            while (jenkins.getInitLevel().compareTo(InitMilestone.JOB_LOADED) < 0) {
                                LOGGER.log(Level.INFO, "Forced save credentials stores: Sleeping for 1 second");
                                try {
                                    Thread.sleep(1000);
                                } catch (InterruptedException e) {
                                    LOGGER.log(Level.WARNING,
                                            "Forced save credentials stores: Aborting due to interrupt", e);
                                    return;
                                }
                            }
                            LOGGER.log(Level.INFO, "Forced save credentials stores: Initialization has completed");
                        }
                        LOGGER.log(Level.INFO, "Forced save credentials stores: Processing Jenkins");
                        for (CredentialsStore s : lookupStores(jenkins)) {
                            try {
                                s.save();
                            } catch (IOException e) {
                                LOGGER.log(Level.WARNING, "Forced save credentials stores: Could not save " + s, e);
                            }
                        }
                        LOGGER.log(Level.INFO, "Forced save credentials stores: Processing Items...");
                        int count = 0;
                        for (Item item : jenkins.getAllItems(Item.class)) {
                            count++;
                            if (count % 100 == 0) {
                                LOGGER.log(Level.INFO,
                                        "Forced save credentials stores: Processing Items ({0} processed)", count);
                            }
                            for (CredentialsStore s : lookupStores(item)) {
                                if (item == s.getContext()) {
                                    // only save if the store is associated with this context item as otherwise will
                                    // have been saved already / later
                                    try {
                                        s.save();
                                    } catch (IOException e) {
                                        LOGGER.log(Level.WARNING,
                                                "Forced save credentials stores: Could not save " + s, e);
                                    }
                                }
                            }
                        }
                        LOGGER.log(Level.INFO, "Forced save credentials stores: Processing Users...");
                        count = 0;
                        for (User user : User.getAll()) {
                            count++;
                            if (count % 100 == 0) {
                                LOGGER.log(Level.INFO,
                                        "Forced save credentials stores: Processing Users ({0} processed)", count);
                            }
                            // HACK ALERT we just want to access the user's stores so we do just enough impersonation
                            // to ensure that User.current() == user
                            // while we could use User.impersonate() that would force a query against the backing
                            // SecurityRealm to revalidate
                            ACL.impersonate(new UsernamePasswordAuthenticationToken(user.getId(), "",
                                    new GrantedAuthority[] { SecurityRealm.AUTHENTICATED_AUTHORITY }));
                            for (CredentialsStore s : lookupStores(user)) {
                                if (user == s.getContext()) {
                                    // only save if the store is associated with this context item as otherwise will
                                    // have been saved already / later
                                    try {
                                        s.save();
                                    } catch (IOException e) {
                                        LOGGER.log(Level.WARNING,
                                                "Forced save credentials stores: Could not save " + s, e);
                                    }
                                }
                            }
                        }
                    } finally {
                        LOGGER.log(Level.INFO, "Forced save credentials stores: Completed");
                        SecurityContextHolder.setContext(ctx);
                    }
                }
            });
        } finally {
            LOGGER.exiting(CredentialsProvider.class.getName(), "saveAll");
        }
    }

    /**
     * A {@link Comparator} for {@link ListBoxModel.Option} instances.
     *
     * @since 2.1.0
     */
    private static class ListBoxModelOptionComparator implements Comparator<ListBoxModel.Option> {
        /**
         * The locale to compare with.
         */
        private final Locale locale;
        /**
         * The {@link Collator}
         */
        private transient Collator collator;

        public ListBoxModelOptionComparator() {
            StaplerRequest req = Stapler.getCurrentRequest();
            if (req != null) {
                locale = req.getLocale();
            } else {
                locale = Locale.getDefault();
            }
            collator = Collator.getInstance(locale);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int compare(ListBoxModel.Option o1, ListBoxModel.Option o2) {
            return collator.compare(o1.name.toLowerCase(locale), o2.name.toLowerCase(locale));
        }
    }
}