This repository has been archived on 2026-03-27. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
drive/tests/server/wopi_token_test.ts
Sienna Meridian Satterwhite 58237d9e44 Initial commit — Drive, an S3 file browser with WOPI editing
Lightweight replacement for the upstream La Suite Numérique drive
(Django/Celery/Next.js) built as a single Deno binary.

Server (Deno + Hono):
- S3 file operations via AWS SigV4 (no SDK) with pre-signed URLs
- WOPI host for Collabora Online (CheckFileInfo, GetFile, PutFile, locks)
- Ory Kratos session auth + CSRF protection
- Ory Keto permission model (OPL namespaces, not yet wired to routes)
- PostgreSQL metadata with recursive folder sizes
- S3 backfill API for registering files uploaded outside the UI
- OpenTelemetry tracing + metrics (opt-in via OTEL_ENABLED)

Frontend (React 19 + Cunningham v4 + react-aria):
- File browser with GridList, keyboard nav, multi-select
- Collabora editor iframe (full-screen, form POST, postMessage)
- Profile menu, waffle menu, drag-drop upload, asset type badges
- La Suite integration service theming (runtime CSS)

Testing (549 tests):
- 235 server unit tests (Deno) — 90%+ coverage
- 278 UI unit tests (Vitest) — 90%+ coverage
- 11 E2E tests (Playwright)
- 12 integration service tests (Playwright)
- 13 WOPI integration tests (Playwright + Docker Compose + Collabora)

MIT licensed.
2026-03-25 18:28:37 +00:00

193 lines
5.1 KiB
TypeScript

/**
* Tests for WOPI token generation and verification.
*/
import {
assertEquals,
assertNotEquals,
} from "https://deno.land/std@0.224.0/assert/mod.ts";
import {
generateWopiToken,
verifyWopiToken,
} from "../../server/wopi/token.ts";
const TEST_SECRET = "test-secret-for-wopi-tokens";
Deno.test("generateWopiToken returns a JWT with 3 parts", async () => {
const token = await generateWopiToken(
"file-123",
"user-456",
"Test User",
true,
3600,
TEST_SECRET,
);
const parts = token.split(".");
assertEquals(parts.length, 3);
});
Deno.test("verifyWopiToken validates a valid token", async () => {
const token = await generateWopiToken(
"file-abc",
"user-def",
"Alice",
true,
3600,
TEST_SECRET,
);
const payload = await verifyWopiToken(token, TEST_SECRET);
assertNotEquals(payload, null);
assertEquals(payload!.fid, "file-abc");
assertEquals(payload!.uid, "user-def");
assertEquals(payload!.unm, "Alice");
assertEquals(payload!.wr, true);
});
Deno.test("verifyWopiToken returns payload with canWrite=false", async () => {
const token = await generateWopiToken(
"file-123",
"user-789",
"Bob",
false,
3600,
TEST_SECRET,
);
const payload = await verifyWopiToken(token, TEST_SECRET);
assertNotEquals(payload, null);
assertEquals(payload!.wr, false);
});
Deno.test("verifyWopiToken rejects expired tokens", async () => {
const token = await generateWopiToken(
"file-123",
"user-456",
"Test",
true,
-10,
TEST_SECRET,
);
const payload = await verifyWopiToken(token, TEST_SECRET);
assertEquals(payload, null);
});
Deno.test("verifyWopiToken rejects tampered tokens", async () => {
const token = await generateWopiToken(
"file-123",
"user-456",
"Test",
true,
3600,
TEST_SECRET,
);
const parts = token.split(".");
const tamperedPayload = parts[1].slice(0, -1) +
(parts[1].slice(-1) === "A" ? "B" : "A");
const tampered = `${parts[0]}.${tamperedPayload}.${parts[2]}`;
const payload = await verifyWopiToken(tampered, TEST_SECRET);
assertEquals(payload, null);
});
Deno.test("verifyWopiToken rejects wrong secret", async () => {
const token = await generateWopiToken(
"file-123",
"user-456",
"Test",
true,
3600,
TEST_SECRET,
);
const payload = await verifyWopiToken(token, "wrong-secret");
assertEquals(payload, null);
});
Deno.test("verifyWopiToken rejects malformed tokens", async () => {
assertEquals(await verifyWopiToken("", TEST_SECRET), null);
assertEquals(await verifyWopiToken("a.b", TEST_SECRET), null);
assertEquals(await verifyWopiToken("not-a-jwt", TEST_SECRET), null);
assertEquals(await verifyWopiToken("a.b.c.d", TEST_SECRET), null);
});
Deno.test("generated tokens have correct iat and exp", async () => {
const before = Math.floor(Date.now() / 1000);
const token = await generateWopiToken(
"file-123",
"user-456",
"Test",
true,
7200,
TEST_SECRET,
);
const after = Math.floor(Date.now() / 1000);
const payload = await verifyWopiToken(token, TEST_SECRET);
assertNotEquals(payload, null);
assertEquals(payload!.iat >= before, true);
assertEquals(payload!.iat <= after, true);
assertEquals(payload!.exp, payload!.iat + 7200);
});
Deno.test("two tokens for different files have different signatures", async () => {
const t1 = await generateWopiToken("file-1", "user-1", "A", true, 3600, TEST_SECRET);
const t2 = await generateWopiToken("file-2", "user-1", "A", true, 3600, TEST_SECRET);
assertNotEquals(t1, t2);
});
Deno.test("verifyWopiToken with tampered header rejects", async () => {
const token = await generateWopiToken(
"file-123",
"user-456",
"Test",
true,
3600,
TEST_SECRET,
);
const parts = token.split(".");
// Change the header
const tamperedHeader = parts[0].slice(0, -1) +
(parts[0].slice(-1) === "A" ? "B" : "A");
const tampered = `${tamperedHeader}.${parts[1]}.${parts[2]}`;
const payload = await verifyWopiToken(tampered, TEST_SECRET);
assertEquals(payload, null);
});
Deno.test("verifyWopiToken with tampered signature rejects", async () => {
const token = await generateWopiToken(
"file-123",
"user-456",
"Test",
true,
3600,
TEST_SECRET,
);
const parts = token.split(".");
const tamperedSig = parts[2].slice(0, -1) +
(parts[2].slice(-1) === "A" ? "B" : "A");
const tampered = `${parts[0]}.${parts[1]}.${tamperedSig}`;
const payload = await verifyWopiToken(tampered, TEST_SECRET);
assertEquals(payload, null);
});
Deno.test("token roundtrip preserves all payload fields", async () => {
const token = await generateWopiToken(
"file-roundtrip",
"user-roundtrip",
"Roundtrip User",
false,
1800,
TEST_SECRET,
);
const payload = await verifyWopiToken(token, TEST_SECRET);
assertNotEquals(payload, null);
assertEquals(payload!.fid, "file-roundtrip");
assertEquals(payload!.uid, "user-roundtrip");
assertEquals(payload!.unm, "Roundtrip User");
assertEquals(payload!.wr, false);
assertEquals(typeof payload!.iat, "number");
assertEquals(typeof payload!.exp, "number");
});