feat(windows): add recoverable existing-agent credential upgrade
This commit is contained in:
1 parent
3cfb0d1abe
commit
97a0139e3d
25 files changed
+11122
-108
No files matched your search
@@ -74,7 +74,7 @@ def agent_management_content(can_manage_agents: bool) -> str:
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<p id="agentEnrollmentResultCopy">
|
||||
This token is displayed only for this enrollment step.
|
||||
Keep it private. MinerOps does not store the plaintext token.
|
||||
</p>
|
||||
@@ -152,7 +152,9 @@ def agent_management_content(can_manage_agents: bool) -> str:
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="agentManagementContent" hidden>
|
||||
<div id="agentManagementContent"
|
||||
data-can-manage-agents="{str(can_manage_agents).lower()}"
|
||||
hidden>
|
||||
<section class="agent-summary-grid" aria-label="Agent Management summary">
|
||||
<div class="panel agent-summary-card">
|
||||
<span>Registered Agents</span>
|
||||
@@ -197,6 +199,7 @@ def agent_management_content(can_manage_agents: bool) -> str:
|
||||
<th>Last check-in</th>
|
||||
<th>Credential</th>
|
||||
<th>Routing</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="agentRows"></tbody>
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"agentCreateSubmit",
|
||||
"agentCreateStatus",
|
||||
"agentEnrollmentResult",
|
||||
"agentEnrollmentResultTitle",
|
||||
"agentEnrollmentResultCopy",
|
||||
"agentEnrollmentToken",
|
||||
"agentEnrollmentExpires",
|
||||
"agentEnrollmentDownload",
|
||||
@@ -48,6 +50,8 @@
|
||||
};
|
||||
|
||||
let enrollments = [];
|
||||
let packageEndpoint = "/api/agent-enrollments/windows-package";
|
||||
let packageFilename = "MinerOpsAgent-Windows.zip";
|
||||
|
||||
function text(value, fallback) {
|
||||
if (value === null || value === undefined || value === "") {
|
||||
@@ -143,6 +147,27 @@
|
||||
cell(words(agent.routing_state))
|
||||
);
|
||||
|
||||
const action = document.createElement("td");
|
||||
const canManage =
|
||||
el.agentManagementContent.dataset.canManageAgents === "true";
|
||||
if (
|
||||
canManage &&
|
||||
agent.credential_state === "legacy" &&
|
||||
agent.routing_state === "assigned"
|
||||
) {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.className = "quiet-button";
|
||||
button.textContent = "Upgrade credential";
|
||||
button.addEventListener("click", function () {
|
||||
createCredentialTransition(agent);
|
||||
});
|
||||
action.appendChild(button);
|
||||
} else {
|
||||
action.textContent = "—";
|
||||
}
|
||||
row.appendChild(action);
|
||||
|
||||
el.agentRows.appendChild(row);
|
||||
});
|
||||
|
||||
@@ -188,7 +213,10 @@
|
||||
cell(words(enrollment.status)),
|
||||
cell(dateTime(enrollment.created_at)),
|
||||
cell(dateTime(enrollment.expires_at)),
|
||||
cell(text(enrollment.enrolled_agent_id, "Not enrolled"))
|
||||
cell(text(
|
||||
enrollment.enrolled_agent_id || enrollment.target_agent_id,
|
||||
"Not enrolled"
|
||||
))
|
||||
);
|
||||
|
||||
el.agentEnrollmentRows.appendChild(row);
|
||||
@@ -338,15 +366,15 @@
|
||||
});
|
||||
|
||||
const enrollment = payload.data;
|
||||
|
||||
el.agentEnrollmentToken.textContent =
|
||||
enrollment.enrollment_token;
|
||||
|
||||
el.agentEnrollmentExpires.textContent =
|
||||
"Expires " + dateTime(enrollment.expires_at);
|
||||
|
||||
el.agentEnrollmentCopyStatus.textContent = "";
|
||||
el.agentEnrollmentResult.hidden = false;
|
||||
showOneTimeToken({
|
||||
token: enrollment.enrollment_token,
|
||||
expiresAt: enrollment.expires_at,
|
||||
title: "Enrollment created",
|
||||
copy: "This token creates a new Agent identity. Keep it private. " +
|
||||
"MinerOps does not store the plaintext token.",
|
||||
endpoint: "/api/agent-enrollments/windows-package",
|
||||
filename: "MinerOpsAgent-Windows.zip"
|
||||
});
|
||||
el.agentCreateStatus.textContent = "Enrollment created.";
|
||||
|
||||
el.agentDisplayName.value = "";
|
||||
@@ -360,6 +388,54 @@
|
||||
}
|
||||
}
|
||||
|
||||
function showOneTimeToken(options) {
|
||||
el.agentEnrollmentToken.textContent = options.token;
|
||||
el.agentEnrollmentExpires.textContent =
|
||||
"Expires " + dateTime(options.expiresAt);
|
||||
el.agentEnrollmentResultTitle.textContent = options.title;
|
||||
el.agentEnrollmentResultCopy.textContent = options.copy;
|
||||
el.agentEnrollmentCopyStatus.textContent = "";
|
||||
el.agentEnrollmentResult.hidden = false;
|
||||
packageEndpoint = options.endpoint;
|
||||
packageFilename = options.filename;
|
||||
}
|
||||
|
||||
async function createCredentialTransition(agent) {
|
||||
const confirmed = window.confirm(
|
||||
"Create a one-time credential upgrade for " + agent.agent_id +
|
||||
"? Its Agent identity, Site, and Zone will remain unchanged."
|
||||
);
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
const payload = await api("/api/agent-credential-transitions", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({agent_id: agent.agent_id})
|
||||
});
|
||||
const transition = payload.data;
|
||||
showOneTimeToken({
|
||||
token: transition.transition_token,
|
||||
expiresAt: transition.expires_at,
|
||||
title: "Existing Agent credential upgrade created",
|
||||
copy: "This token is bound to the existing Agent identity " +
|
||||
transition.target_agent_id + ". It cannot enroll a new " +
|
||||
"Agent or transfer its Zone.",
|
||||
endpoint: "/api/agent-credential-transitions/windows-package",
|
||||
filename: "MinerOpsAgent-Credential-Upgrade-Windows.zip"
|
||||
});
|
||||
el.agentCreateStatus.textContent =
|
||||
"Credential upgrade created for " + transition.target_agent_id + ".";
|
||||
await refreshData(false);
|
||||
} catch (error) {
|
||||
el.agentCreateStatus.textContent =
|
||||
error.message || "Credential upgrade could not be created.";
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadWindowsPackage() {
|
||||
const token = el.agentEnrollmentToken.textContent;
|
||||
|
||||
@@ -375,7 +451,7 @@
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
"/api/agent-enrollments/windows-package",
|
||||
packageEndpoint,
|
||||
{
|
||||
method: "POST",
|
||||
credentials: "same-origin",
|
||||
@@ -408,7 +484,7 @@
|
||||
const link = document.createElement("a");
|
||||
|
||||
link.href = downloadUrl;
|
||||
link.download = "MinerOpsAgent-Windows.zip";
|
||||
link.download = packageFilename;
|
||||
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
Reference in new issue
Block a user