feat(device-manager): trust Hub owner scopes
This commit is contained in:
@@ -15,6 +15,22 @@ test("Launcher handoff becomes an opaque Device Manager session and trusted acto
|
||||
return jsonResponse(200, {
|
||||
ok: true,
|
||||
launcherSessionId,
|
||||
access: {
|
||||
allowed: true,
|
||||
hubRole: "owner",
|
||||
ownerScopes: [
|
||||
{
|
||||
scopeKind: "company",
|
||||
ownerRef: "client:client_dctouch",
|
||||
displayName: "DC Touch",
|
||||
},
|
||||
{
|
||||
scopeKind: "personal",
|
||||
ownerRef: "user:user_root",
|
||||
displayName: "DC SUDO",
|
||||
},
|
||||
],
|
||||
},
|
||||
user: {
|
||||
id: "user_root",
|
||||
email: "root@example.test",
|
||||
@@ -53,11 +69,18 @@ test("Launcher handoff becomes an opaque Device Manager session and trusted acto
|
||||
const context = auth.currentContext(request);
|
||||
assert.equal(context.actor.userRef, "user:user_root");
|
||||
assert.equal(context.actor.hubRole, "owner");
|
||||
assert.deepEqual(context.actor.ownerScopes, [{
|
||||
scopeKind: "personal",
|
||||
ownerRef: "user:user_root",
|
||||
displayName: "DC SUDO",
|
||||
}]);
|
||||
assert.deepEqual(context.actor.ownerScopes, [
|
||||
{
|
||||
scopeKind: "company",
|
||||
ownerRef: "client:client_dctouch",
|
||||
displayName: "DC Touch",
|
||||
},
|
||||
{
|
||||
scopeKind: "personal",
|
||||
ownerRef: "user:user_root",
|
||||
displayName: "DC SUDO",
|
||||
},
|
||||
]);
|
||||
assert.deepEqual(context.actor.groupRefs, [
|
||||
"group:nodedc:device-core:admin",
|
||||
"group:nodedc:superadmin",
|
||||
@@ -76,6 +99,11 @@ test("invalid identity and explicit Device Core block never produce an actor", a
|
||||
fetchImpl: async () => jsonResponse(200, {
|
||||
ok: true,
|
||||
launcherSessionId,
|
||||
access: {
|
||||
allowed: true,
|
||||
hubRole: "admin",
|
||||
ownerScopes: [],
|
||||
},
|
||||
user,
|
||||
}),
|
||||
});
|
||||
@@ -97,6 +125,65 @@ test("invalid identity and explicit Device Core block never produce an actor", a
|
||||
}
|
||||
});
|
||||
|
||||
test("production auth fails closed when Launcher omits trusted Device Core access", async () => {
|
||||
const auth = createDeviceManagerAuth({
|
||||
env: productionEnv(),
|
||||
fetchImpl: async () => jsonResponse(200, {
|
||||
ok: true,
|
||||
launcherSessionId,
|
||||
user: {
|
||||
id: "user_root",
|
||||
email: "root@example.test",
|
||||
name: "DC SUDO",
|
||||
groups: ["nodedc:superadmin"],
|
||||
},
|
||||
}),
|
||||
});
|
||||
const handoff = mockResponse();
|
||||
await auth.handleHandoff(
|
||||
{ method: "GET", headers: {} },
|
||||
handoff,
|
||||
new URL("https://device.example.test/auth/nodedc/handoff?token=handoff-secret"),
|
||||
);
|
||||
const cookie = String(handoff.getHeader("set-cookie")).split(";", 1)[0];
|
||||
const response = mockResponse();
|
||||
assert.equal(await auth.authorize(
|
||||
{ method: "GET", headers: { cookie, accept: "application/json" } },
|
||||
response,
|
||||
new URL("https://device.example.test/api/device-manager/session"),
|
||||
), true);
|
||||
assert.equal(response.statusCode, 403);
|
||||
assert.equal(JSON.parse(response.body).error, "device_manager_access_denied");
|
||||
});
|
||||
|
||||
test("an injected file-backed token takes precedence over broad platform env tokens", async () => {
|
||||
const calls = [];
|
||||
const auth = createDeviceManagerAuth({
|
||||
env: { ...productionEnv(), NODEDC_INTERNAL_ACCESS_TOKEN: "broad-platform-token" },
|
||||
internalToken: "scoped-file-token",
|
||||
fetchImpl: async (url, init) => {
|
||||
calls.push({ url, init });
|
||||
return jsonResponse(200, {
|
||||
ok: true,
|
||||
launcherSessionId,
|
||||
access: { allowed: true, hubRole: "member", ownerScopes: [] },
|
||||
user: {
|
||||
id: "device-member",
|
||||
email: "member@example.test",
|
||||
name: "Device Member",
|
||||
groups: ["nodedc:device-core:access"],
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
await auth.handleHandoff(
|
||||
{ method: "GET", headers: {} },
|
||||
mockResponse(),
|
||||
new URL("https://device.example.test/auth/nodedc/handoff?token=handoff-secret"),
|
||||
);
|
||||
assert.equal(calls[0].init.headers.Authorization, "Bearer scoped-file-token");
|
||||
});
|
||||
|
||||
function productionEnv() {
|
||||
return {
|
||||
NODE_ENV: "production",
|
||||
|
||||
Reference in New Issue
Block a user