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

时间:2019-07-19 07:24:16

标签: jenkins jenkins-pipeline jenkins-plugins jenkins-groovy

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

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

该脚本可以为根级别的全局域的SystemCredentialsProvider凭据执行OK操作。

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

我正在使用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

3 个答案:

答案 0 :(得分:2)

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

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

示例provided here显示了如何从全局存储中检索凭证,这是Jenkins.instance –为完整的文档,它们提供的脚本复制在这里:

import jenkins.*
import jenkins.model.* 
import hudson.*
import hudson.model.*
def jenkinsCredentials = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
        com.cloudbees.plugins.credentials.Credentials.class,
        Jenkins.instance,
        null,
        null
);
for (creds in jenkinsCredentials) {
    println(jenkinsCredentials.id)
    }

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

我的文件夹被称为“ ft”,(跳过顶部的import / boilerplate和JenkinsCredentials帮助器类的定义),我在Groovy脚本中的其余调用看起来像:

def folder = JenkinsCredentials.getFolderItem('ft');

def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
        com.cloudbees.plugins.credentials.Credentials.class,
        folder,
        null,
        null
);
for (c in creds) {
    println(c.id + ": " + c.description)
}

希望这对某人有所帮助!顺便说一句,如果您的凭据存储在文件中而不是字符串中,那么您需要再执行一步...该文件在“ description”属性中将没有任何有用的内容,您需要将字节流存储在{{ 1}}

因此,该最终循环带有一些元编程,可以动态检查要调用的getContent()方法的可用性:

c.getContent()

最后一部分是从该答案中借用的,它还显示了如何导入StandardCharsets和IOUtils:https://stackoverflow.com/a/42360792/661659

答案 1 :(得分:1)

只需将其添加到@Shaybc这个非常棒的类中...如果您像我一样并且具有嵌套文件夹,则需要更改Class方法getFolderItem以递归到所有“子文件夹”中...否则,您只会得到顶级(根)詹金斯物品的退货/点击。

这里就是这样,替换getFolderItem方法并添加getFolderItemRecursively方法来完成工作。

private static Folder getFolderItemRecursively(Folder folder, String folderName) {
    for (nestedItem in folder.getItems()) {
        if (nestedItem instanceof Folder) {
            if(nestedItem.getFullName().contains(folderName)) {
                return nestedItem;
            } else {
                def recurse = getFolderItemRecursively(nestedItem, folderName);
                if (recurse instanceof Folder) {
                    return recurse;
                }
            }
        }
    }
}
public static Folder getFolderItem(String folderName)
{
    for (item in Jenkins.getInstance().getItems())
    {
        if(item instanceof Folder)
        {
            if(item.getFullName().contains(folderName))
            {
                return item;
            } else {
                def recurse = getFolderItemRecursively(item, folderName);
                if (recurse instanceof Folder) {
                    return recurse;
                }
            }
        }
    }
}

答案 2 :(得分:0)

与最初的问题有些相关,我需要清点存储在 Jenkins 中的所有凭据。到目前为止,没有一个答案提供了一种开箱即用的解决方案来转储所有 Jenkins 凭据...... 基于@TimWelch 和@Kingdon 提供的答案元素,我编写了以下 groovy(管理员)脚本,该脚本打印所有凭据 ID 及其类型,从根文件夹开始到最深的文件夹(递归),同时还避免打印重复项:

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() {
    out.println("Folder,Credentials Type,Credentials ID") // header
    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.printf("/%s,%s,%s\n", fullname, c.getClass().getSimpleName(), c.id)
      }
    }
  }
}

new DeepCredentialsPrinter(getBinding().out).start();

要点:https://gist.github.com/fabienrenaud/349764873855533abc71b7fadafdf29e

可以轻松扩展 printCreds 方法,以便为每种机密类型打印更多数据。