fix(auth): make local verification portable
Keep upstream identity visible while limiting logout to local auth. Inject the restore privilege gate so the deterministic core tests do not depend on the host OS, and confine descriptor-backed projection tests to Linux. Accept the real remaining Pi timeout budget instead of an exact millisecond.
This commit is contained in:
@@ -70,6 +70,7 @@ const passwordHash =
|
||||
"$argon2id$v=19$m=65536,t=3,p=1$AAECAwQFBgcICQoLDA0ODw$DRo8ZSPI8G5OCvnFFapbVEjP69aDjy1Sw9i2743cPC4";
|
||||
const userId = "6ba7b810-9dad-4ed1-80b4-00c04fd430c8";
|
||||
const roots: string[] = [];
|
||||
const linuxTest = test.runIf(process.platform === "linux");
|
||||
|
||||
afterEach(() => {
|
||||
fsHook.path = undefined;
|
||||
@@ -276,7 +277,7 @@ function expectDenied(operation: () => unknown): void {
|
||||
}
|
||||
}
|
||||
|
||||
test("loads ready projection as one immutable auth and local-users snapshot", () => {
|
||||
linuxTest("loads ready projection as one immutable auth and local-users snapshot", () => {
|
||||
const root = projectionRoot();
|
||||
const fixture = localProjectionFixture("synthetic-user", passwordHash);
|
||||
const generation = writeReadyProjection(root, fixture);
|
||||
@@ -298,7 +299,7 @@ test("loads ready projection as one immutable auth and local-users snapshot", ()
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test("loads a complete OIDC projection without a users snapshot", () => {
|
||||
linuxTest("loads a complete OIDC projection without a users snapshot", () => {
|
||||
const root = projectionRoot();
|
||||
const generation = writeReadyOidcProjection(root);
|
||||
const loaded = createProjectedAuthenticationConfigProvider(root).current();
|
||||
@@ -309,7 +310,7 @@ test("loads a complete OIDC projection without a users snapshot", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("loadConfig selects an immutable projected local provider and its in-memory registry", async () => {
|
||||
linuxTest("loadConfig selects an immutable projected local provider and its in-memory registry", async () => {
|
||||
const root = projectionRoot();
|
||||
writeReadyProjection(root, localProjectionFixture("projected-user", passwordHash));
|
||||
|
||||
@@ -323,7 +324,7 @@ test("loadConfig selects an immutable projected local provider and its in-memory
|
||||
expect(await registry?.findByUsername("PROJECTED-USER")).toMatchObject({ username: "projected-user" });
|
||||
});
|
||||
|
||||
test("loadConfig selects an immutable projected OIDC provider without direct-file fallback", () => {
|
||||
linuxTest("loadConfig selects an immutable projected OIDC provider without direct-file fallback", () => {
|
||||
const root = projectionRoot();
|
||||
writeReadyOidcProjection(root);
|
||||
|
||||
@@ -338,7 +339,7 @@ test("loadConfig selects an immutable projected OIDC provider without direct-fil
|
||||
});
|
||||
});
|
||||
|
||||
test("rejects a trailing-slash runtime root", () => {
|
||||
linuxTest("rejects a trailing-slash runtime root", () => {
|
||||
const root = projectionRoot();
|
||||
writeReadyProjection(
|
||||
root,
|
||||
@@ -349,7 +350,7 @@ test("rejects a trailing-slash runtime root", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test.each([
|
||||
linuxTest.each([
|
||||
["missing", undefined],
|
||||
[
|
||||
"blocked",
|
||||
@@ -381,7 +382,7 @@ test.each([
|
||||
);
|
||||
});
|
||||
|
||||
test.each([
|
||||
linuxTest.each([
|
||||
"root traversal",
|
||||
"CURRENT symlink",
|
||||
"CURRENT hardlink",
|
||||
@@ -436,7 +437,7 @@ test.runIf(process.geteuid?.() === 0)(
|
||||
},
|
||||
);
|
||||
|
||||
test("rejects a foreign group with the correct owner", () => {
|
||||
linuxTest("rejects a foreign group with the correct owner", () => {
|
||||
const root = projectionRoot();
|
||||
writeReadyProjection(
|
||||
root,
|
||||
@@ -449,7 +450,7 @@ test("rejects a foreign group with the correct owner", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("enumerates closed namespaces without path-based readdirSync", () => {
|
||||
linuxTest("enumerates closed namespaces without path-based readdirSync", () => {
|
||||
const root = projectionRoot();
|
||||
const generation = writeReadyProjection(
|
||||
root,
|
||||
@@ -462,7 +463,7 @@ test("enumerates closed namespaces without path-based readdirSync", () => {
|
||||
).toBe(generation);
|
||||
});
|
||||
|
||||
test("rejects a symlinked runtime root", () => {
|
||||
linuxTest("rejects a symlinked runtime root", () => {
|
||||
const root = projectionRoot();
|
||||
writeReadyProjection(
|
||||
root,
|
||||
@@ -476,7 +477,7 @@ test("rejects a symlinked runtime root", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test.each([
|
||||
linuxTest.each([
|
||||
"root",
|
||||
"generations",
|
||||
"selected generation",
|
||||
@@ -514,7 +515,7 @@ test.each([
|
||||
);
|
||||
});
|
||||
|
||||
test.each(["manifest", "generation", "size", "digest"])(
|
||||
linuxTest.each(["manifest", "generation", "size", "digest"])(
|
||||
"rejects changed %s integrity data without secret disclosure",
|
||||
(kind) => {
|
||||
const root = projectionRoot();
|
||||
@@ -542,7 +543,7 @@ test.each(["manifest", "generation", "size", "digest"])(
|
||||
},
|
||||
);
|
||||
|
||||
test("switches atomically to a later complete generation", () => {
|
||||
linuxTest("switches atomically to a later complete generation", () => {
|
||||
const root = projectionRoot();
|
||||
const first = writeReadyProjection(
|
||||
root,
|
||||
@@ -564,7 +565,7 @@ test("switches atomically to a later complete generation", () => {
|
||||
expect(provider.current().runtimeProjection?.generation).toBe(second);
|
||||
});
|
||||
|
||||
test("retries once when CURRENT is atomically replaced between lstat and open", () => {
|
||||
linuxTest("retries once when CURRENT is atomically replaced between lstat and open", () => {
|
||||
const root = projectionRoot();
|
||||
const first = writeReadyProjection(
|
||||
root,
|
||||
@@ -591,7 +592,7 @@ test("retries once when CURRENT is atomically replaced between lstat and open",
|
||||
).toBe(second);
|
||||
});
|
||||
|
||||
test("retries once when CURRENT is replaced after the final identity read", () => {
|
||||
linuxTest("retries once when CURRENT is replaced after the final identity read", () => {
|
||||
const root = projectionRoot();
|
||||
const first = writeReadyProjection(
|
||||
root,
|
||||
@@ -628,7 +629,7 @@ test("retries once when CURRENT is replaced after the final identity read", () =
|
||||
expect(observations).toBe(3);
|
||||
});
|
||||
|
||||
test("retries once when CURRENT is replaced between root descriptor and path observations", () => {
|
||||
linuxTest("retries once when CURRENT is replaced between root descriptor and path observations", () => {
|
||||
const root = projectionRoot();
|
||||
const first = writeReadyProjection(
|
||||
root,
|
||||
@@ -678,7 +679,7 @@ test("retries once when CURRENT is replaced between root descriptor and path obs
|
||||
expect(replacedCurrent).toBe(true);
|
||||
});
|
||||
|
||||
test("rejects a second CURRENT replacement after the one permitted retry", () => {
|
||||
linuxTest("rejects a second CURRENT replacement after the one permitted retry", () => {
|
||||
const root = projectionRoot();
|
||||
const first = writeReadyProjection(
|
||||
root,
|
||||
@@ -717,7 +718,7 @@ test("rejects a second CURRENT replacement after the one permitted retry", () =>
|
||||
);
|
||||
});
|
||||
|
||||
test("fails deterministically when generations is replaced during a load", () => {
|
||||
linuxTest("fails deterministically when generations is replaced during a load", () => {
|
||||
const root = projectionRoot();
|
||||
const generation = writeReadyProjection(
|
||||
root,
|
||||
@@ -748,7 +749,7 @@ test("fails deterministically when generations is replaced during a load", () =>
|
||||
);
|
||||
});
|
||||
|
||||
test("fails deterministically when the selected generation directory is replaced during a load", () => {
|
||||
linuxTest("fails deterministically when the selected generation directory is replaced during a load", () => {
|
||||
const root = projectionRoot();
|
||||
const generation = writeReadyProjection(
|
||||
root,
|
||||
@@ -780,7 +781,7 @@ test("fails deterministically when the selected generation directory is replaced
|
||||
);
|
||||
});
|
||||
|
||||
test.each(["corrupt", "symlink"])(
|
||||
linuxTest.each(["corrupt", "symlink"])(
|
||||
"rejects a %s retained predecessor generation",
|
||||
(kind) => {
|
||||
const root = projectionRoot();
|
||||
@@ -819,7 +820,7 @@ test.each(["corrupt", "symlink"])(
|
||||
},
|
||||
);
|
||||
|
||||
test("has no direct-file fallback when CURRENT is absent", () => {
|
||||
linuxTest("has no direct-file fallback when CURRENT is absent", () => {
|
||||
const root = projectionRoot();
|
||||
const generation = writeReadyProjection(
|
||||
root,
|
||||
@@ -839,7 +840,7 @@ test("has no direct-file fallback when CURRENT is absent", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("in-flight snapshot authenticates A after selection B and deletion A, while a new load sees B", async () => {
|
||||
linuxTest("in-flight snapshot authenticates A after selection B and deletion A, while a new load sees B", async () => {
|
||||
const root = projectionRoot();
|
||||
const first = writeReadyProjection(
|
||||
root,
|
||||
|
||||
Reference in New Issue
Block a user