Files
Aarnav Tale 0512565f8e feat: replace openapi hashing system with /version
Apparently I didn't use my brain cells and rely on the /version
endpoint that Headscale has exposed since 0.26 (our lowest supported
version). Switching to that significantly simplifies the API surface.
2026-05-25 11:51:02 -04:00

142 lines
5.2 KiB
TypeScript

import { describe, expect, test } from "vitest";
import { getBootstrapClient, getRuntimeClient, HS_VERSIONS } from "../setup/env";
describe.sequential.for(HS_VERSIONS)("Headscale %s: Pre-auth Keys", (version) => {
test("pre-auth keys can be created", async () => {
const client = await getRuntimeClient(version);
const preAuthKeyUser = await client.users.create({ name: "preauthkeyuser@" });
expect(preAuthKeyUser).toBeDefined();
expect(preAuthKeyUser.name).toBe("preauthkeyuser@");
const expiry = new Date(Date.now() + 3600 * 1000);
const preAuthKey = await client.preAuthKeys.create({
user: preAuthKeyUser.id,
ephemeral: false,
reusable: false,
expiration: expiry,
aclTags: null,
});
expect(preAuthKey).toBeDefined();
expect(preAuthKey.user?.id).toBe(preAuthKeyUser.id);
expect(preAuthKey.ephemeral).toBe(false);
expect(preAuthKey.reusable).toBe(false);
expect(preAuthKey.aclTags).toEqual([]);
expect(new Date(preAuthKey.expiration).getTime()).toBeCloseTo(expiry.getTime(), -2);
});
test("pre-auth keys can be created with ACL tags", async () => {
const client = await getRuntimeClient(version);
const [preAuthKeyUser] = await client.users.list({ name: "preauthkeyuser@" });
expect(preAuthKeyUser).toBeDefined();
expect(preAuthKeyUser.name).toBe("preauthkeyuser@");
const aclTags = ["tag:test1", "tag:test2"];
const preAuthKey = await client.preAuthKeys.create({
user: preAuthKeyUser.id,
ephemeral: true,
reusable: true,
expiration: null,
aclTags,
});
expect(preAuthKey).toBeDefined();
expect(preAuthKey.user?.id).toBe(preAuthKeyUser.id);
expect(preAuthKey.ephemeral).toBe(true);
expect(preAuthKey.reusable).toBe(true);
expect(preAuthKey.aclTags.sort()).toEqual(aclTags.sort());
});
test("tag-only pre-auth keys (0.28+)", async (context) => {
const bootstrap = await getBootstrapClient(version);
if (!bootstrap.capabilities.preAuthKeysHaveStableIds) {
context.skip();
}
const client = await getRuntimeClient(version);
const aclTags = ["tag:server", "tag:prod"];
const preAuthKey = await client.preAuthKeys.create({
user: null,
ephemeral: false,
reusable: true,
expiration: null,
aclTags,
});
expect(preAuthKey).toBeDefined();
expect(preAuthKey.user).toBeNull();
expect(preAuthKey.ephemeral).toBe(false);
expect(preAuthKey.reusable).toBe(true);
expect(preAuthKey.aclTags.sort()).toEqual(aclTags.sort());
});
test("pre-auth keys can be listed", async () => {
const client = await getRuntimeClient(version);
const [preAuthKeyUser] = await client.users.list({ name: "preauthkeyuser@" });
expect(preAuthKeyUser).toBeDefined();
expect(preAuthKeyUser.name).toBe("preauthkeyuser@");
const preAuthKeys = await client.preAuthKeys.listForUser(preAuthKeyUser.id);
expect(Array.isArray(preAuthKeys)).toBe(true);
expect(preAuthKeys.length).toBeGreaterThanOrEqual(2);
});
test("all pre-auth keys can be listed without user filter (0.28+)", async (context) => {
const bootstrap = await getBootstrapClient(version);
if (!bootstrap.capabilities.preAuthKeysHaveStableIds) {
context.skip();
}
const client = await getRuntimeClient(version);
const [preAuthKeyUser] = await client.users.list({ name: "preauthkeyuser@" });
expect(preAuthKeyUser).toBeDefined();
const allKeys = await client.preAuthKeys.listAll!();
expect(Array.isArray(allKeys)).toBe(true);
expect(allKeys.length).toBeGreaterThanOrEqual(2);
const userSpecificKeys = await client.preAuthKeys.listForUser(preAuthKeyUser.id);
for (const userKey of userSpecificKeys) {
const found = allKeys.find((k) => k.key === userKey.key);
expect(found).toBeDefined();
}
});
test("listAll returns keys with correct structure (0.28+)", async (context) => {
const bootstrap = await getBootstrapClient(version);
if (!bootstrap.capabilities.preAuthKeysHaveStableIds) {
context.skip();
}
const client = await getRuntimeClient(version);
const allKeys = await client.preAuthKeys.listAll!();
for (const key of allKeys) {
expect(key.id).toBeDefined();
expect(key.key).toBeDefined();
expect(typeof key.reusable).toBe("boolean");
expect(typeof key.ephemeral).toBe("boolean");
expect(typeof key.used).toBe("boolean");
expect(key.expiration).toBeDefined();
expect(key.createdAt).toBeDefined();
}
});
test("pre-auth keys can be expired", async () => {
const client = await getRuntimeClient(version);
const [preAuthKeyUser] = await client.users.list({ name: "preauthkeyuser@" });
expect(preAuthKeyUser).toBeDefined();
expect(preAuthKeyUser.name).toBe("preauthkeyuser@");
const preAuthKeys = await client.preAuthKeys.listForUser(preAuthKeyUser.id);
expect(preAuthKeys.length).toBeGreaterThanOrEqual(2);
const preAuthKeyToExpire = preAuthKeys[0];
await client.preAuthKeys.expire(preAuthKeyToExpire);
const preAuthKeysAfterExpire = await client.preAuthKeys.listForUser(preAuthKeyUser.id);
const expiredKey = preAuthKeysAfterExpire.find((key) => key.key === preAuthKeyToExpire.key);
expect(expiredKey).toBeDefined();
});
});