Class EphemeralCredentialsProvider

java.lang.Object
hudson.model.Descriptor<com.cloudbees.plugins.credentials.CredentialsProvider>
com.cloudbees.plugins.credentials.CredentialsProvider
io.jenkins.plugins.ephemeral_credentials.EphemeralCredentialsProvider
All Implemented Interfaces:
ExtensionPoint, Describable<com.cloudbees.plugins.credentials.CredentialsProvider>, Saveable, Loadable, OnMaster, IconSpec

@Extension public class EphemeralCredentialsProvider extends com.cloudbees.plugins.credentials.CredentialsProvider

A CredentialsProvider that only ever answers with ephemeral_credentials that some currently executing Pipeline build itself put into it. Everything is held in a plain in-memory map, keyed by Run.getExternalizableId(); nothing here is Saveable and nothing is ever written to disk.

This class is loaded once, as an ordinary @Extension, at Jenkins startup - unlike a shared-library src/ class, which is recompiled per build and therefore cannot hold state shared across builds. That is the whole reason this exists as a real plugin instead of living in a JSL.

getCredentialsInItemGroup(java.lang.Class<C>, hudson.model.ItemGroup, org.springframework.security.core.Authentication, java.util.List<com.cloudbees.plugins.credentials.domains.DomainRequirement>) is invoked by Jenkins' generic ephemeral_credentials lookup from many unrelated contexts (job-config dropdowns, other plugins enumerating what's available, freestyle builds, etc.), not just from a deliberate request for a specific ID. It therefore stays purely passive - it never prompts for anything, it only serves what has already been put(hudson.model.Run<?, ?>, java.lang.String, com.cloudbees.plugins.credentials.Credentials) into it. Deciding when to interactively resolve a missing credential is the caller's job (see WithEphemeralCredentials.groovy step).

Identifying which build is asking

A single build's Pipeline script can be executing on several different node/agent blocks at once (parallel branches) or move between agents across sequential stages, so there is no stable hudson.model.Executor to correlate against. What is stable for the whole life of the build is its FlowExecutionOwner, reachable from whichever CpsThread happens to be running the code that triggered this lookup - see CpsRuns.current().

That resolves correctly when the caller is itself CPS-interpreted code (our own WithEphemeralCredentials.groovy, a shared-library script, the Jenkinsfile itself). It does not resolve when the caller is a step's own internal Java implementation running off the CPS interpreter thread entirely - credentials-binding's withCredentials, for example, performs its own findCredentialById call from such a thread, where CpsThread.current() is null.

The unidentifiable-run fallback, and why it's scoped to itemGroup

When the run can't be identified, this falls back to considering other runs' caches too and lets the caller's own by-ID filtering (e.g. CredentialsProvider.findCredentialById) pick the right entry - but only among runs whose job lives within the itemGroup this method was actually asked about, not literally every cached run project-wide. This matters because Jenkins' extension API never passes the specific calling Run this deep - only its containing itemGroup (the job's own folder, or Jenkins root) and effective Authentication - so there is no way to identify the exact run from this method's parameters alone. Without the itemGroup scoping, an unfiltered "every cached run" fallback would let a build in one folder receive another, unrelated build's ephemeral value from a completely different folder/team/permission scope, purely because both happened to cache something under the same literal credential ID at the same time - a genuine cross-tenant credential leak, since Jenkins folder-scoped credentials exist specifically to enforce that boundary. Scoping the fallback to itemGroup containment restores that boundary. A narrower residual risk remains: two concurrent runs of the same or sibling jobs within the same folder scope, caching different values under the same literal ID, can still collide - weigh that against how likely concurrent builds sharing an ID are for whatever pipeline uses this.

  • Constructor Details

    • EphemeralCredentialsProvider

      public EphemeralCredentialsProvider()
  • Method Details

    • get

      public static EphemeralCredentialsProvider get()
    • getCredentialsInItemGroup

      @NonNull public <C extends com.cloudbees.plugins.credentials.Credentials> List<C> getCredentialsInItemGroup(@NonNull Class<C> type, @NonNull ItemGroup itemGroup, @Nullable org.springframework.security.core.Authentication authentication, @NonNull List<com.cloudbees.plugins.credentials.domains.DomainRequirement> domainRequirements)
      Overrides:
      getCredentialsInItemGroup in class com.cloudbees.plugins.credentials.CredentialsProvider
    • put

      public void put(@NonNull Run<?,?> run, @NonNull String credentialsId, @NonNull com.cloudbees.plugins.credentials.Credentials credentials)
      Caches ephemeral_credentials under credentialsId, visible only to lookups made from within run's own Pipeline execution.
    • find

      @CheckForNull public com.cloudbees.plugins.credentials.Credentials find(@NonNull Run<?,?> run, @NonNull String credentialsId)
    • has

      public boolean has(@NonNull Run<?,?> run, @NonNull String credentialsId)
    • forget

      public void forget(@NonNull Run<?,?> run)
      Drops every credential cached for run. Called by EphemeralCredentialsRunListener once the build is finalized or deleted, regardless of how it ended - this is the authoritative cleanup path, not any finally block in the pipeline script, since a hard-killed build can skip the latter entirely.
    • forget

      public boolean forget(@NonNull Run<?,?> run, @NonNull String credentialsId)
      Drops just credentialsId from run's cache, leaving any other entries for that run untouched - unlike forget(Run), which is the whole-run cleanup path called only by EphemeralCredentialsRunListener. This overload is what backs the pipeline-facing ephemeralCredentialsForget/ EphemeralCredentialsAccessor single-entry removal.
      Returns:
      whether an entry was actually present and removed.