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 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.

Unfortunately, this resolution by CPS threads rules out the use of this plugin for legacy (Freestyle) builds where we currently have no way of matching the currently running code path to a Run of a build. If a solution to that problem is found, code contributions are welcome.

Run identification: known, or nothing - never guessed

Ephemeral credentials only ever mean something in the context of one specific Run; there is no sensible answer to "which run's cache applies here" other than the actual run asking the question. Every real consumer of a credential ID -- CredentialsProvider.findCredentialById (id, type, Run, ...) and everything built on it (withCredentials, checkout, sshagent, this plugin's own WithEphemeralCredentials/EphemeralCredentialsAccessor) -- already has the Run in hand.

A credentials-plugin API update proposed in pull request #1071 allows passing that Run object all the way down to a provider implementation's own overridable methods instead of discarding it before it gets here, as the earlier releases did (see getCredentialsInItemGroup(Class, ItemGroup, Authentication, List, Run) below). With that feature in place, this class simply uses the value directly -- no fallback correlation is attempted through any other channel.

NOTE: Until the credentials-plugin API update is merged, a version from Jenkins Incrementals or a locally built fork can be pinned in pom.xml file.

When a caller genuinely has no Run to give at all and calls the 4-argument overload below, e.g. a job-config credential dropdown - the only correct answer is an empty list: "no ephemeral credential applies here," never a guess.

  • 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)
      Called only when the caller has no Run to give at all - see the 5-argument overload below, which is what every real Run-based lookup actually goes through. Ephemeral credentials are meaningless without knowing which run they belong to, so this always answers with an empty list rather than guessing at one - see the class javadoc for why an earlier revision's guesswork here was removed.
      Overrides:
      getCredentialsInItemGroup in class com.cloudbees.plugins.credentials.CredentialsProvider
    • 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, @CheckForNull Run<?,?> run)
      The run-aware overload - see the class javadoc ("Run identification: known, or nothing"). When run is supplied, it is used directly: exactly this run's own cache, nothing else, no correlation guesswork at all. When it isn't (a caller with no run to give, e.g. a job-config credential dropdown), this falls through to the 4-argument override above, which answers with an empty list rather than guessing.
      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.