Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-58170

Allow credential parameters to shadow credential ids in lookup

    • Icon: New Feature New Feature
    • Resolution: Fixed
    • Icon: Minor Minor
    • credentials-plugin
    • None
    • credentials-2.3.0

      As a pipeline author, suppose I have a pipeline that performs a deployment action that requires credentials from Jenkins. A naive approach might be to add my credentials to the global Jenkins credential store (call it deployKey as its id); then I can reference it in my pipeline later like so:

       

      node {
        // ...
        stage('Deploy') {
          withCredentials([usernameColonPassword(credentialsId: 'deployKey', variable: 'DEPLOY_CREDENTIALS')]) {
            sh 'curl -u "${DEPLOY_CREDENTIALS}" ...'
          }
        }
      }
      

      But then I want to make my pipeline more secure. One option I might have is to have a dedicated Jenkins instance for running deployments. This is a large maintenance burden (e.g., trusted.ci.j.io versus ci.j.io), so digging deeper into Jenkins' existing features, I find user-scoped credentials. I can simply use credentials attached to my user account instead of Jenkins itself, though in order for my pipeline to recognize and use these credentials, I find that I must make them parameterized to the build itself so that I can select my credentials upon starting a build (there's an alternative option using the Authorize Project plugin, but this plugin is not widely used). A neat side-effect of this functionality is that other users can also deploy using the same pipeline but with their own user-scoped credentials. This can also allow me the flexibility of using a default global credential and allowing users to override that specifically for a pipeline run.

      Thus, I modify my pipeline to add a build parameter for the deploy credentials as well as using a modified syntax in withCredentials to indicate that the credentialId parameter being provided is in fact a build parameter name and not the actual credential id. Combined, we get the following updated pipeline:

      properties([parameters([credentials(credentialType: 'com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials', name: 'deployKey', required: true)])])
      node {
        // ...
        stage('Deploy') {
          withCredentials([usernameColonPassword(credentialsId: '${deployKey}', variable: 'DEPLOY_CREDENTIALS')]) {
            sh 'curl -u "${DEPLOY_CREDENTIALS}" ...'
          }
        }
      }
      

      Notice how we need to use a special syntax: credentialsId: '${deployKey}'. This syntax is misleading because it can be easily confused with the similar but not equivalent GString syntax: credentialsId: "${deployKey}". In fact, this latter syntax would never work for a user-scoped credential because user-scoped credentials are currently only looked up if the credential id is provided using the former template syntax.

      What I am proposing we change here is to make credential parameters referenced by name take precedent over a globally-scoped credential with an id of the same name as the parameter name itself. Thus, we could update the above pipeline using this feature to be a little simpler:

       

      properties([parameters([credentials(credentialType: 'com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials', name: 'deployKey', required: true)])])
      node {
        // ...
        stage('Deploy') {
        withCredentials([usernameColonPassword(credentialsId: 'deployKey', variable: 'DEPLOY_CREDENTIALS')]) {
          sh 'curl -u "${DEPLOY_CREDENTIALS}" ...'
        }
       }
      }
      

      The beauty of this approach is that it allows me to go from the first pipeline to the third pipeline simply by updating my build to add a credential parameter with the same name as the credential id I was using before that I want to migrate to a user account. I don't even have to touch the rest of the pipeline!

      This issue relates to JENKINS-47699 in that it's a useful point to help support that feature.

       

       

          [JENKINS-58170] Allow credential parameters to shadow credential ids in lookup

          Matt Sicker created issue -
          Matt Sicker made changes -
          Status Original: Open [ 1 ] New: In Progress [ 3 ]
          Matt Sicker made changes -
          Status Original: In Progress [ 3 ] New: In Review [ 10005 ]
          Matt Sicker made changes -
          Description Original: As a pipeline author, suppose I have a pipeline that performs a deployment action that requires credentials from Jenkins. A naive approach might be to add my credentials to the global Jenkins credential store (call it {{deployKey}} as its id); then I can reference it in my pipeline later like so:

           
          {code:java}
          node {
            // ...
            stage('Deploy') {
              withCredentials([usernameColonPassword(credentialsId: 'deployKey', variable: 'DEPLOY_CREDENTIALS')]) {
                sh 'curl -u "${DEPLOY_CREDENTIALS}" ...'
              }
            }
          }
          {code}
          But then I want to make my pipeline more secure. One option I might have is to have a dedicated Jenkins instance for running deployments. This is a _large_ maintenance burden (e.g., trusted.ci.j.io versus ci.j.io), so digging deeper into Jenkins' existing features, I find user-scoped credentials. I can simply use credentials attached to my user account instead of Jenkins itself, though in order for my pipeline to recognize and use these credentials, I find that I must make them parameterized to the build itself so that I can select my credentials upon starting a build (there's an alternative option using the Authorize Project plugin, but this plugin is not widely used). A neat side-effect of this functionality is that other users can also deploy using the same pipeline but with their own user-scoped credentials. This can also allow me the flexibility of using a default global credential and allowing users to override that specifically for a pipeline run.

          Thus, I modify my pipeline to add a build parameter for the deploy credentials as well as using a modified syntax in {{withCredentials}} to indicate that the {{credentialId}} parameter being provided is in fact a build parameter name and not the actual credential id. Combined, we get the following updated pipeline:
          {code:java}
          properties([parameters([credentials(credentialType: 'com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials', name: 'deployKey', required: true)])])
          node {
            // ...
            stage('Deploy') {
              withCredentials([usernameColonPassword(credentialsId: '${deployKey}', variable: 'DEPLOY_CREDENTIALS')]) {
                sh 'curl -u "${DEPLOY_CREDENTIALS}" ...'
              }
            }
          }
          {code}
          Notice how we need to use a special syntax: {{credentialsId: '${deployKey}'}}. This syntax is misleading because it can be easily confused with the similar but not equivalent GString syntax: {{credentialsId: "${deployKey}"}}. In fact, this latter syntax would never work for a user-scoped credential because user-scoped credentials are currently only looked up if the credential id is provided using that template syntax.

          What I am proposing we change here is to make credential parameters referenced by name take precedent over a globally-scoped credential with an id of the same name as the parameter name itself. Thus, we could update the above pipeline using this feature to be a little simpler:

           
          {code:java}
          properties([parameters([credentials(credentialType: 'com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials', name: 'deployKey', required: true)])])
          node {
            // ...
            stage('Deploy') {
            withCredentials([usernameColonPassword(credentialsId: 'deployKey', variable: 'DEPLOY_CREDENTIALS')]) {
              sh 'curl -u "${DEPLOY_CREDENTIALS}" ...'
            }
           }
          }
          {code}
          The beauty of this approach is that it allows me to go from the first pipeline to the third pipeline simply by updating my build to add a credential parameter with the same name as the credential id I was using before that I want to migrate to a user account. I don't even have to touch the rest of the pipeline!

          This issue relates to JENKINS-47699 in that it's a useful point to help support that feature.

           

           
          New: As a pipeline author, suppose I have a pipeline that performs a deployment action that requires credentials from Jenkins. A naive approach might be to add my credentials to the global Jenkins credential store (call it {{deployKey}} as its id); then I can reference it in my pipeline later like so:

           
          {code:java}
          node {
            // ...
            stage('Deploy') {
              withCredentials([usernameColonPassword(credentialsId: 'deployKey', variable: 'DEPLOY_CREDENTIALS')]) {
                sh 'curl -u "${DEPLOY_CREDENTIALS}" ...'
              }
            }
          }
          {code}
          But then I want to make my pipeline more secure. One option I might have is to have a dedicated Jenkins instance for running deployments. This is a _large_ maintenance burden (e.g., trusted.ci.j.io versus ci.j.io), so digging deeper into Jenkins' existing features, I find user-scoped credentials. I can simply use credentials attached to my user account instead of Jenkins itself, though in order for my pipeline to recognize and use these credentials, I find that I must make them parameterized to the build itself so that I can select my credentials upon starting a build (there's an alternative option using the Authorize Project plugin, but this plugin is not widely used). A neat side-effect of this functionality is that other users can also deploy using the same pipeline but with their own user-scoped credentials. This can also allow me the flexibility of using a default global credential and allowing users to override that specifically for a pipeline run.

          Thus, I modify my pipeline to add a build parameter for the deploy credentials as well as using a modified syntax in {{withCredentials}} to indicate that the {{credentialId}} parameter being provided is in fact a build parameter name and not the actual credential id. Combined, we get the following updated pipeline:
          {code:java}
          properties([parameters([credentials(credentialType: 'com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials', name: 'deployKey', required: true)])])
          node {
            // ...
            stage('Deploy') {
              withCredentials([usernameColonPassword(credentialsId: '${deployKey}', variable: 'DEPLOY_CREDENTIALS')]) {
                sh 'curl -u "${DEPLOY_CREDENTIALS}" ...'
              }
            }
          }
          {code}
          Notice how we need to use a special syntax: {{credentialsId: '${deployKey}'}}. This syntax is misleading because it can be easily confused with the similar but not equivalent GString syntax: {{credentialsId: "${deployKey}"}}. In fact, this latter syntax would never work for a user-scoped credential because user-scoped credentials are currently only looked up if the credential id is provided using the former template syntax.

          What I am proposing we change here is to make credential parameters referenced by name take precedent over a globally-scoped credential with an id of the same name as the parameter name itself. Thus, we could update the above pipeline using this feature to be a little simpler:

           
          {code:java}
          properties([parameters([credentials(credentialType: 'com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials', name: 'deployKey', required: true)])])
          node {
            // ...
            stage('Deploy') {
            withCredentials([usernameColonPassword(credentialsId: 'deployKey', variable: 'DEPLOY_CREDENTIALS')]) {
              sh 'curl -u "${DEPLOY_CREDENTIALS}" ...'
            }
           }
          }
          {code}
          The beauty of this approach is that it allows me to go from the first pipeline to the third pipeline simply by updating my build to add a credential parameter with the same name as the credential id I was using before that I want to migrate to a user account. I don't even have to touch the rest of the pipeline!

          This issue relates to JENKINS-47699 in that it's a useful point to help support that feature.

           

           
          Matt Sicker made changes -
          Link New: This issue relates to JENKINS-44772 [ JENKINS-44772 ]

          Matt Sicker added a comment -

          Linking related issues that can be implemented through this feature.

          Matt Sicker added a comment - Linking related issues that can be implemented through this feature.
          Matt Sicker made changes -
          Link New: This issue relates to JENKINS-47699 [ JENKINS-47699 ]
          Wadeck Follonier made changes -
          Link New: This issue relates to JENKINS-38963 [ JENKINS-38963 ]
          Wadeck Follonier made changes -
          Link New: This issue relates to JENKINS-44774 [ JENKINS-44774 ]
          Wadeck Follonier made changes -
          Link New: This issue relates to JENKINS-44773 [ JENKINS-44773 ]

            jvz Matt Sicker
            jvz Matt Sicker
            Votes:
            0 Vote for this issue
            Watchers:
            8 Start watching this issue

              Created:
              Updated:
              Resolved: