如何在文件夹级别读取 Jenkins 凭证

Ban*_*ona 10 java jenkins jenkins-plugins jenkins-groovy jenkins-pipeline

我正在尝试将凭证从 Jenkins 迁移到另一个凭证存储。

我想从 Jenkins 商店读取凭据,并找到了此脚本(https://github.com/tkrzeminski/jenkins-groovy-scripts/blob/master/show-all-credentials.groovy

该脚本对根级别的全局域的SystemCredentialsProvider 凭据执行正常的工作。

但我的凭据存储在一个文件夹中,因此该脚本对我不起作用。

我正在使用 Jenkins 脚本控制台来执行脚本。

如果我导航到 Jenkins 凭证配置页面并将鼠标悬停在凭证条目之一的图标上,工具提示会显示“文件夹凭证提供程序”。

=================================================== ==

问题:如何从 Jenkins 的文件夹中读取所有凭据?

=================================================== ==

请参阅下面的脚本:

import jenkins.model.*
import com.cloudbees.plugins.credentials.*
import com.cloudbees.plugins.credentials.impl.*
import com.cloudbees.plugins.credentials.domains.*
import com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey
import com.cloudbees.jenkins.plugins.awscredentials.AWSCredentialsImpl
import org.jenkinsci.plugins.plaincredentials.StringCredentials
import org.jenkinsci.plugins.plaincredentials.impl.FileCredentialsImpl

def showRow = { credentialType, secretId, username = null, password = null, description = null ->
  println("${credentialType} : ".padLeft(20) + secretId?.padRight(38)+" | " +username?.padRight(20)+" | " +password?.padRight(40) + " | " +description)
}

// set Credentials domain name (null means is it global)
domainName = null

credentialsStore = Jenkins.instance.getExtensionList('com.cloudbees.plugins.credentials.SystemCredentialsProvider')[0]?.getStore()
domain = new Domain(domainName, null, Collections.<DomainSpecification>emptyList())

credentialsStore?.getCredentials(domain).each{
  if(it instanceof UsernamePasswordCredentialsImpl)
    showRow("user/password", it.id, it.username, it.password?.getPlainText(), it.description)
  else if(it instanceof BasicSSHUserPrivateKey)
    showRow("ssh priv key", it.id, it.passphrase?.getPlainText(), it.privateKeySource?.getPrivateKey(), it.description)
  else if(it instanceof AWSCredentialsImpl)
    showRow("aws", it.id, it.accessKey, it.secretKey?.getPlainText(), it.description)
  else if(it instanceof StringCredentials)
    showRow("secret text", it.id, it.secret?.getPlainText(), '', it.description)
  else if(it instanceof FileCredentialsImpl)
    showRow("secret file", it.id, it.content?.text, '', it.description)
  else
    showRow("something else", it.id, '', '', '')
}

return
Run Code Online (Sandbox Code Playgroud)

Sha*_*ybc 7

我只是创建了一个 Java 类来从全局或文件夹范围检索凭据:

import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.hudson.plugins.folder.Folder

class JenkinsCredentials
{
    /**
    * this method gets a 'StandardUsernamePasswordCredentials' for a userName, the 'StandardUsernamePasswordCredentials' object
    * have 2 fields - user and password, which can be used to login to other systems,
    * the username is searched for first in the global jenkins level, and if not found then it is searched at the folder level
     */
    public static StandardUsernamePasswordCredentials getCredentialsByUsername(String userName, String fromFolder) throws Exception
    {
        List credsList = CredentialsProvider.lookupCredentials(StandardUsernamePasswordCredentials.class, Jenkins.getInstance());
        if(credsList == null || credsList.size() == 0)
        {
            credsList = getFolderLevelCredentialsList(fromFolder);
        }
        return credsList.findResult { it.username == userName ? it : null };
    }

    /**
    * this method gets a 'StandardUsernamePasswordCredentials' for a userID, the 'StandardUsernamePasswordCredentials' object
    * have 2 fields - user and password, which can be used to login to other systems,
    * the userID is searched for first in the global jenkins level, and if not found then it is searched at the folder level
     */
    public static StandardUsernamePasswordCredentials getCredentialsByID(String userID, String fromFolder) throws Exception
    {
        List credsList = CredentialsProvider.lookupCredentials(StandardUsernamePasswordCredentials.class, Jenkins.getInstance());
        if(credsList == null || credsList.size() == 0)
        {
            credsList = getFolderLevelCredentialsList(fromFolder);
        }
        return credsList.findResult { it.id == userID ? it : null };
    }

    /**
    * this method gets a 'StandardUsernamePasswordCredentials' for a userName, the 'StandardUsernamePasswordCredentials' object
    * have 2 fields - user and password, which can be used to login to other systems,
    * the username is searched for at the folder level who's name we provided as 'fromFolder'
     */
    public static StandardUsernamePasswordCredentials getFolderCredentialsByUsername(String userName, String fromFolder) throws Exception
    {
        List credsList = getFolderLevelCredentialsList(fromFolder);
        return credsList.findResult { it.username == userName ? it : null };
    }

    /**
    * this method gets a 'StandardUsernamePasswordCredentials' for a userID, the 'StandardUsernamePasswordCredentials' object
    * have 2 fields - user and password, which can be used to login to other systems,
    * the userID is searched for at the folder level who's name we provided as 'fromFolder'
    */
    public static StandardUsernamePasswordCredentials getFolderCredentialsByID(String userID, String fromFolder) throws Exception
    {
        List credsList = getFolderLevelCredentialsList(fromFolder);
        return credsList.findResult { it.id == userID ? it : null };
    }

    /**
    * this method gets a list of credentials set at a folder level, the method receives the folder name to get the credentials from
     */
    public static List getFolderLevelCredentialsList(String folderName)
    {
        return CredentialsProvider.lookupCredentials(StandardUsernamePasswordCredentials.class, getFolderItem(folderName));
    }

    /**
    * this method fetch a 'Folder' item from jenkins instance by a folder name
    * it then can be used in the other methods to search for a user credentials at that folder level
     */
    public static Folder getFolderItem(String folderName)
    {
        def allJenkinsItems = Jenkins.getInstance().getItems();
        for (currentJenkinsItem in allJenkinsItems)
        {
            if(currentJenkinsItem instanceof Folder)
            {
                if(((Folder)currentJenkinsItem).getFullName().contains(folderName))
                {
                    return (Folder)currentJenkinsItem;
                }
            }
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样使用它:

从名为“MyJenkinsFolder”的文件夹中获取用户名“shay bc”的凭据:

// get the credentials
StandardUsernamePasswordCredentials shaybcCredentials = JenkinsCredentials.getFolderCredentialsByUsername("shay bc", "MyJenkinsFolder")

// login to some system using the credentials
sshSession.login(shaybcCredentials.getUsername(), shaybcCredentials.getPassword())
Run Code Online (Sandbox Code Playgroud)

从名为“MyJenkinsFolder”的文件夹中获取用户 ID“sbc”的凭据:

// get the credentials
StandardUsernamePasswordCredentials sbcCredentials = JenkinsCredentials.getFolderCredentialsByID("sbc", "MyJenkinsFolder")

// login to some system using the credentials
sshSession.login(sbcCredentials.getUsername(), sbcCredentials.getPassword())
Run Code Online (Sandbox Code Playgroud)

  • 愿意在您的答案中添加更多细节吗?很高兴看到完整的解决方案而不是平面类,谢谢! (2认同)

Kin*_*don 5

@Shaybc 共享的类是大部分解决方案,尽管评论者认为它本身并不是一个完整的解决方案,但我能够猜测如何在我自己的 Groovy 脚本中将其正确实现为这样一个完整的解决方案。

\n\n

JenkinsCredentials.getFolderItem(\'foldername\') 返回的文件夹采用与 Jenkins 中的文件夹寻址相同的字符串。因此,如果您有一个具有描述性名称的文件夹和一个别名,例如“文件夹名称”和“文件夹名称”,则用于检索文件夹本身的正确字符串是“文件夹名称”。

\n\n

此处提供的示例显示了如何从全局存储中检索凭据,这是Jenkins.instance\xe2\x80\x93\xc2\xa0 对于完整文档,他们提供的脚本复制到此处:

\n\n
import jenkins.*\nimport jenkins.model.* \nimport hudson.*\nimport hudson.model.*\ndef jenkinsCredentials = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(\n        com.cloudbees.plugins.credentials.Credentials.class,\n        Jenkins.instance,\n        null,\n        null\n);\nfor (creds in jenkinsCredentials) {\n    println(jenkinsCredentials.id)\n    }\n
Run Code Online (Sandbox Code Playgroud)\n\n

因此,通过定义 @Shaybc 所描述的方式来启动脚本class JenkinsCredentials,然后不要使用 调用CredentialsProvider.lookupCredentials,而是Jenkins.instance检索文件夹并将其传递到那里。

\n\n

我的文件夹名为“ft”,(跳过顶部的导入/样板以及 JenkinsCredentials 帮助程序类的定义)我在 Groovy 脚本中调用的其余部分如下所示:

\n\n
def folder = JenkinsCredentials.getFolderItem(\'ft\');\n\ndef creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(\n        com.cloudbees.plugins.credentials.Credentials.class,\n        folder,\n        null,\n        null\n);\nfor (c in creds) {\n    println(c.id + ": " + c.description)\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

希望这对某人有帮助!顺便说一句,如果您的凭据存储在文件中而不是字符串中,您还有一个步骤...该文件的“description”属性中不会有任何有用的内容,您需要存储在的字节流c.getContent()

\n\n

因此,最后一个循环使用一些元编程来动态检查要调用的 getContent() 方法的可用性:

\n\n
for (c in creds) {\n if (c.metaClass.respondsTo(c, \'getContent\')) {\n  println(\n   IOUtils.toString(c.getContent(), StandardCharsets.UTF_8)\n  )\n }\n println(c.id + ": " + c.description)\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

最后一部分是从这个答案借来的,它还展示了如何导入StandardCharsets和IOUtils:/sf/answers/2965255471/

\n


小智 5

import jenkins.model.*
import hudson.model.ModelObject
import com.cloudbees.plugins.credentials.*
import com.cloudbees.plugins.credentials.impl.*
import com.cloudbees.plugins.credentials.domains.*
import com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey
import com.cloudbees.jenkins.plugins.awscredentials.AWSCredentialsImpl
import org.jenkinsci.plugins.plaincredentials.StringCredentials
import org.jenkinsci.plugins.plaincredentials.impl.FileCredentialsImpl
import com.cloudbees.hudson.plugins.folder.Folder


class DeepCredentialsPrinter {

  private static final boolean DEBUG = false;

  private final out;
  private final Set<CredentialsStore> visitedStores = new HashSet<>();

  DeepCredentialsPrinter(out) {
      this.out = out;
  }

  private void start() {
    process(Jenkins.getInstance())
  }

  private void process(ItemGroup group) {
    printCreds(group);

    List<ItemGroup> items = group.getItems();
    if (items == null || items.isEmpty()) {
      return;
    }

    for (item in items) {
      if (item instanceof ItemGroup) {
        process(item);
      } else if (item instanceof Item) {
        printCreds(item)
      } else {
        if (DEBUG) {
          out.println("[DEBUG] unsupported item type: " + item.getClass().getCanonicalName());
        }
      }
    }
  }

  private void printCreds(ModelObject model) {
    for (store in CredentialsProvider.lookupStores(model)) {
      if (visitedStores.add(store)) { // only visit new stores
        print(model.getFullName(), store.getCredentials(Domain.global()));
      }
    }
  }

  private void print(String fullname, List<Credentials> creds) {
    if (creds.isEmpty()) {
      if (DEBUG) {
        out.println("[DEBUG] No credentials in /" + fullname);
      }
    } else {
      for (c in creds) {
        out.println("Folder: /" + fullname)
        out.println("   id: " + c.id)
        if (c.properties.description) {
            out.println("   description: " + c.description)
        }
        if (c.properties.username) {
            out.println("   username: " + c.username)
        }
        if (c.properties.password) {
            out.println("   password: " + c.password)
        }
        if (c.properties.passphrase) {
            out.println("   passphrase: " + c.passphrase)
        }
        if (c.properties.secret) {
            out.println("   secret: " + c.secret)
        }
        if (c.properties.secretBytes) {
            out.println("    secretBytes: ")
            out.println("\n" + new String(c.secretBytes.getPlainData()))
        }
        if (c.properties.privateKeySource) {
            out.println("   privateKey: " + c.getPrivateKey())
        }
        if (c.properties.apiToken) {
            out.println("   apiToken: " + c.apiToken)
        }
        if (c.properties.token) {
            out.println("   token: " + c.token)
        }
        out.println("")
        }
    }
  }
}

new DeepCredentialsPrinter(getBinding().out).start();
Run Code Online (Sandbox Code Playgroud)

根据以上所有评论,以下是列出所有文件夹级别凭据的代码,该代码根据凭据类型打印所有属性