CI / Client installer (macos-latest) (push) Canceled after 0s
CI / Client installer (ubuntu-latest) (push) Canceled after 0s
CI / Client installer (windows-latest) (push) Canceled after 0s
CI / test (22) (push) Canceled after 0s
CI / test (24) (push) Canceled after 0s
CI / release-smoke (push) Canceled after 0s
72 lines
3.8 KiB
TypeScript
72 lines
3.8 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import test from "node:test";
|
|
import { configureService, renderUnit, servicePaths, startService, uninstallService } from "../src/admin.js";
|
|
|
|
test("dingtalk-admin writes remote OAuth settings and secret-free hardened unit", () => {
|
|
const root = mkdtempSync(join(tmpdir(), "ding-admin-"));
|
|
const old = process.env.XDG_CONFIG_HOME;
|
|
process.env.XDG_CONFIG_HOME = join(root, "config");
|
|
try {
|
|
mkdirSync(join(root, ".secrets"), { mode: 0o700 });
|
|
writeFileSync(join(root, ".secrets", "dingtalk.json"), "{}", { mode: 0o600 });
|
|
writeFileSync(join(root, ".secrets", "xft.key"), "secret", { mode: 0o600 });
|
|
chmodSync(join(root, ".secrets", "dingtalk.json"), 0o600);
|
|
const paths = configureService({
|
|
projectRoot: root, publicBaseUrl: "https://approval.example.com", corpId: "ding-corp", port: 3000,
|
|
credentialsFile: ".secrets/dingtalk.json", xftOpenApiAppId: "app",
|
|
xftOpenApiSecretFile: ".secrets/xft.key", xftOpenApiEnterpriseId: "enterprise",
|
|
dingtalkClientOnlyProcessCodes: "PROC-SIGN,PROC-CLIENT",
|
|
});
|
|
assert.equal(statSync(paths.env).mode & 0o777, 0o600);
|
|
assert.equal(statSync(paths.refSecret).mode & 0o777, 0o600);
|
|
assert.equal(statSync(paths.sessionSecret).mode & 0o777, 0o600);
|
|
const runtime = readFileSync(paths.env, "utf8");
|
|
assert.match(runtime, /MCP_REMOTE_AUTH_ENABLED="true"/);
|
|
assert.match(runtime, /DINGTALK_CORP_ID="ding-corp"/);
|
|
assert.match(runtime, /MCP_AUTH_DATABASE=/);
|
|
assert.match(runtime, /APP_MODE="gateway"/);
|
|
assert.match(runtime, /DINGTALK_CLIENT_ONLY_PROCESS_CODES="PROC-SIGN,PROC-CLIENT"/);
|
|
assert.doesNotMatch(runtime, /DWS_/);
|
|
assert.doesNotMatch(runtime, /MCP_TRUSTED_LOCAL_USER_ID|CLIENT_SECRET/);
|
|
|
|
const unit = renderUnit(paths, "/usr/bin/node");
|
|
assert.match(unit, /ProtectSystem=strict/);
|
|
assert.doesNotMatch(unit, /ding-corp|CLIENT_SECRET/);
|
|
|
|
mkdirSync(join(root, "dist", "src"), { recursive: true });
|
|
writeFileSync(join(root, "dist", "src", "index.js"), "// built");
|
|
const calls: string[][] = [];
|
|
startService(root, (args) => calls.push(args));
|
|
assert.deepEqual(calls.map((call) => call[0]), ["daemon-reload", "enable", "restart"]);
|
|
uninstallService(root, (args) => calls.push(args));
|
|
assert.equal(existsSync(paths.env), false);
|
|
assert.equal(existsSync(paths.refSecret), true);
|
|
assert.equal(existsSync(paths.sessionSecret), true);
|
|
} finally {
|
|
if (old === undefined) delete process.env.XDG_CONFIG_HOME; else process.env.XDG_CONFIG_HOME = old;
|
|
}
|
|
});
|
|
|
|
test("dingtalk-admin requires HTTPS and references XFT secret files", () => {
|
|
const root = mkdtempSync(join(tmpdir(), "ding-admin-openapi-"));
|
|
mkdirSync(join(root, ".secrets"), { mode: 0o700 });
|
|
writeFileSync(join(root, ".secrets", "dingtalk.json"), "{}", { mode: 0o600 });
|
|
writeFileSync(join(root, ".secrets", "xft.key"), "1".repeat(32), { mode: 0o600 });
|
|
assert.throws(() => configureService({
|
|
projectRoot: root, publicBaseUrl: "http://approval.example.com", corpId: "ding-corp", port: 3000,
|
|
credentialsFile: ".secrets/dingtalk.json",
|
|
}), /HTTPS/);
|
|
const paths = configureService({
|
|
projectRoot: root, publicBaseUrl: "https://approval.example.com", corpId: "ding-corp", port: 3000,
|
|
credentialsFile: ".secrets/dingtalk.json", xftOpenApiAppId: "open-app",
|
|
xftOpenApiSecretFile: ".secrets/xft.key", xftOpenApiEnterpriseId: "TEST0001",
|
|
});
|
|
const runtime = readFileSync(paths.env, "utf8");
|
|
assert.match(runtime, /XFT_OPEN_API_ENABLED="true"/);
|
|
assert.match(runtime, /XFT_OPEN_API_AUTHORITY_SECRET_FILE=/);
|
|
assert.doesNotMatch(runtime, new RegExp("1".repeat(32)));
|
|
});
|