From 346b4fe88ad031e16a9b7289cbe7be0497ab3103 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 23 Mar 2022 15:25:20 -0400 Subject: [PATCH 1/5] Introduce `Credentials.forRun` to comtextualize secrets --- .../cloudbees/plugins/credentials/Credentials.java | 13 +++++++++++++ .../plugins/credentials/CredentialsProvider.java | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/src/main/java/com/cloudbees/plugins/credentials/Credentials.java b/src/main/java/com/cloudbees/plugins/credentials/Credentials.java index f1c5107b..e556bd03 100644 --- a/src/main/java/com/cloudbees/plugins/credentials/Credentials.java +++ b/src/main/java/com/cloudbees/plugins/credentials/Credentials.java @@ -27,6 +27,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; import hudson.ExtensionPoint; import hudson.model.Describable; +import hudson.model.Run; import java.io.Serializable; /** @@ -54,4 +55,16 @@ public interface Credentials extends Describable, Serializable, Ext @NonNull @SuppressWarnings("unchecked") CredentialsDescriptor getDescriptor(); + + /** + * Optionally produce a special value when used in the context of a particular build. + * @param context a build wishing to consume these credentials + * @return contextualized credentials, preferably implementing the same interfaces (if not of the same concrete type); by default, {@code this} + * @see CredentialsProvider#findCredentialById(java.lang.String, java.lang.Class, hudson.model.Run, com.cloudbees.plugins.credentials.domains.DomainRequirement...) + */ + @NonNull + default Credentials forRun(@NonNull Run context) { + return this; + } + } diff --git a/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java b/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java index 3db35424..ff910564 100644 --- a/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java +++ b/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java @@ -955,6 +955,10 @@ public static C findCredentialById(@NonNull String id, } } C result = CredentialsMatchers.firstOrNull(candidates, CredentialsMatchers.withId(id)); + Credentials contextualized = result.forRun(run); + if (type.isInstance(contextualized)) { + result = type.cast(contextualized); + } // 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; From 80afc6a267e1b6f45101f0cf3450a9d8d31457d0 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 23 Mar 2022 15:41:03 -0400 Subject: [PATCH 2/5] Handle null result --- .../plugins/credentials/CredentialsProvider.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java b/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java index ff910564..2ae9d369 100644 --- a/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java +++ b/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java @@ -955,9 +955,11 @@ public static C findCredentialById(@NonNull String id, } } C result = CredentialsMatchers.firstOrNull(candidates, CredentialsMatchers.withId(id)); - Credentials contextualized = result.forRun(run); - if (type.isInstance(contextualized)) { - result = type.cast(contextualized); + if (result != null) { + Credentials contextualized = result.forRun(run); + if (type.isInstance(contextualized)) { + result = type.cast(contextualized); + } } // 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 From c1818e6b16f89fc2b7334d7c3cf0fbc3db9cb08e Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 23 Mar 2022 17:16:45 -0400 Subject: [PATCH 3/5] Call `track` on the original, not the clone --- .../plugins/credentials/CredentialsProvider.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java b/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java index 2ae9d369..5ddcb0fd 100644 --- a/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java +++ b/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java @@ -955,15 +955,18 @@ public static C findCredentialById(@NonNull String id, } } 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 + if (run.isLogUpdated()) { + track(run, result); + } if (result != null) { Credentials contextualized = result.forRun(run); if (type.isInstance(contextualized)) { - result = type.cast(contextualized); + return type.cast(contextualized); } } - // 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; + return result; } /** From 4e379351da8a5c31a3a1bac022e1878714d22e3b Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 23 Mar 2022 17:18:38 -0400 Subject: [PATCH 4/5] Warn if `getRun` returns an incompatible implementation class --- .../com/cloudbees/plugins/credentials/CredentialsProvider.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java b/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java index 5ddcb0fd..7ca8fa62 100644 --- a/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java +++ b/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java @@ -964,6 +964,8 @@ public static C findCredentialById(@NonNull String id, Credentials contextualized = result.forRun(run); if (type.isInstance(contextualized)) { return type.cast(contextualized); + } else { + LOGGER.warning(() -> "Ignoring " + contextualized.getClass().getName() + " return value of " + result.getClass().getName() + ".forRun since it is not assignable to " + type.getName()); } } return result; From 4b0eba28ebffc2735c2108ca8b0b834ad9b8a080 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 23 Mar 2022 17:40:27 -0400 Subject: [PATCH 5/5] Calling `forRun` from the other return path of `findCredentialById` --- .../plugins/credentials/CredentialsProvider.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java b/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java index 7ca8fa62..84c5f08a 100644 --- a/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java +++ b/src/main/java/com/cloudbees/plugins/credentials/CredentialsProvider.java @@ -916,7 +916,8 @@ public static C findCredentialById(@NonNull String id, CredentialsProvider.lookupCredentials(type, run.getParent(), ACL.SYSTEM, domainRequirements) ); } - return CredentialsMatchers.firstOrNull(candidates, CredentialsMatchers.withId(id)); + // TODO should this be calling track? + return contextualize(type, CredentialsMatchers.firstOrNull(candidates, CredentialsMatchers.withId(id)), run); } // this is a parameter and not the default value, we need to determine who triggered the build final Map.Entry> triggeredBy = triggeredBy(run); @@ -960,15 +961,20 @@ public static C findCredentialById(@NonNull String id, if (run.isLogUpdated()) { track(run, result); } - if (result != null) { - Credentials contextualized = result.forRun(run); + return contextualize(type, result, run); + } + + @CheckForNull + private static C contextualize(@NonNull Class type, @CheckForNull C credentials, @NonNull Run run) { + if (credentials != null) { + Credentials contextualized = credentials.forRun(run); if (type.isInstance(contextualized)) { return type.cast(contextualized); } else { - LOGGER.warning(() -> "Ignoring " + contextualized.getClass().getName() + " return value of " + result.getClass().getName() + ".forRun since it is not assignable to " + type.getName()); + LOGGER.warning(() -> "Ignoring " + contextualized.getClass().getName() + " return value of " + credentials.getClass().getName() + ".forRun since it is not assignable to " + type.getName()); } } - return result; + return credentials; } /**