我们正在尝试在脚本中通过 API 创建 Jenkins 证书凭据,该脚本会在 Azure 中生成应用程序注册,然后应将相应的证书放入 Jenkins 凭据存储中。
我们使用的是 Jenkins 版本 2.426.2
有人知道如何提交表格才能创建凭证吗?
这是我们的代码:
$form = @{
'id' = $CredentialId
'description' = $CredentialDescription
'file' = $CertificateFilePath
'password' = $CertificatePassword
}
$userId = $env:JENKINSAPIUSER
$token = $env:JENKINSTOKEN
$pair = "$($userId):$($token)"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
$basicAuthValue = "Basic $encodedCreds"
$headers = @{
'Authorization' = "Basic $encodedCreds"
}
$jenkinsUri = "$JenkinsUrl/credentials/store/$CredentialStore/domain/_/createCredentials/"
Invoke-RestMethod -uri $jenkinsUri -Method Post -Headers $headers -Form $form
文本:错误 400 此页面需要提交表单
我们还尝试通过 xml api 添加它:
$xmlPayload = @"
<com.cloudbees.plugins.credentials.impl.CertificateCredentialsImpl>
<scope>GLOBAL</scope>
<id>$CredentialId</id>
<description>$CredentialDescription</description>
<file>$([System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes($CertificateFilePath)))</file>
<password>$($CertificatePassword | ConvertFrom-SecureString -AsPlainText)</password>
</com.cloudbees.plugins.credentials.impl.CertificateCredentialsImpl>
"@
#$response = Invoke-JenkinsWebRequest -Uri $jenkinsUri -Method Post -Form $json
$response = Invoke-JenkinsWebRequest -Uri "$jenkinsUri/config.xml" -Method Post -Body $xmlPayload -ContentType "application/xml"
此外,我们尝试对 Web UI 进行逆向工程,但这并没有带来任何重要的见解。然而,这是我们确定的用于准备表单数据的函数:
function buildFormTree(form) {
try {
// I initially tried to use an associative array with DOM elements as keys
// but that doesn't seem to work neither on IE nor Firefox.
// so I switch back to adding a dynamic property on DOM.
form.formDom = {}; // root object
var doms = []; // DOMs that we added 'formDom' for.
doms.push(form);
let addProperty = function (parent, name, value) {
name = shortenName(name);
if (parent[name] != null) {
if (parent[name].push == null) {
// is this array?
parent[name] = [parent[name]];
}
parent[name].push(value);
} else {
parent[name] = value;
}
};
// find the grouping parent node, which will have @name.
// then return the corresponding object in the map
let findParent = function (e) {
var p = findFormParent(e, form);
if (p == null) {
return {};
}
var m = p.formDom;
if (m == null) {
// this is a new grouping node
doms.push(p);
p.formDom = m = {};
addProperty(findParent(p), p.getAttribute("name"), m);
}
return m;
};
var jsonElement = null;
for (var i = 0; i < form.elements.length; i++) {
var e = form.elements[i];
if (e.name == "json") {
jsonElement = e;
continue;
}
if (e.tagName == "FIELDSET") {
continue;
}
if (e.tagName == "SELECT" && e.multiple) {
var values = [];
for (var o = 0; o < e.options.length; o++) {
var opt = e.options.item(o);
if (opt.selected) {
values.push(opt.value);
}
}
addProperty(findParent(e), e.name, values);
continue;
}
var p;
var r;
var type = e.getAttribute("type");
if (type == null) {
type = "";
}
switch (type.toLowerCase()) {
case "button":
case "submit":
break;
case "checkbox":
p = findParent(e);
var checked = xor(e.checked, e.classList.contains("negative"));
if (!e.groupingNode) {
let v = e.getAttribute("json");
if (v) {
// if the special attribute is present, we'll either set the value or not. useful for an array of checkboxes
// we can't use @value because IE6 sets the value to be "on" if it's left unspecified.
if (checked) {
addProperty(p, e.name, v);
}
} else {
// otherwise it'll bind to boolean
addProperty(p, e.name, checked);
}
} else {
if (checked) {
addProperty(p, e.name, (e.formDom = {}));
}
}
break;
case "file":
// to support structured form submission with file uploads,
// rename form field names to unique ones, and leave this name mapping information
// in JSON. this behavior is backward incompatible, so only do
// this when
p = findParent(e);
if (e.getAttribute("jsonAware") != null) {
var on = e.getAttribute("originalName");
if (on != null) {
addProperty(p, on, e.name);
} else {
var uniqName = "file" + iota++;
addProperty(p, e.name, uniqName);
e.setAttribute("originalName", e.name);
e.name = uniqName;
}
}
// switch to multipart/form-data to support file submission
// @enctype is the standard, but IE needs @encoding.
form.enctype = form.encoding = "multipart/form-data";
crumb.appendToForm(form);
break;
case "radio":
if (!e.checked) {
break;
}
r = 0;
while (e.name.substring(r, r + 8) == "removeme") {
r = e.name.indexOf("_", r + 8) + 1;
}
p = findParent(e);
if (e.groupingNode) {
addProperty(
p,
e.name.substring(r),
(e.formDom = { value: e.value }),
);
} else {
addProperty(p, e.name.substring(r), e.value);
}
break;
case "password":
p = findParent(e);
addProperty(p, e.name, e.value);
// must be kept in sync with RedactSecretJsonForTraceSanitizer.REDACT_KEY
addProperty(p, "$redact", shortenName(e.name));
break;
default:
p = findParent(e);
addProperty(p, e.name, e.value);
if (e.classList.contains("complex-password-field")) {
addProperty(p, "$redact", shortenName(e.name));
}
break;
}
}
jsonElement.value = JSON.stringify(form.formDom);
// clean up
for (i = 0; i < doms.length; i++) {
doms[i].formDom = null;
}
return true;
} catch (e) {
alert(e + "\n(form not submitted)");
return false;
}
}