64 lines
4.8 KiB
TypeScript
64 lines
4.8 KiB
TypeScript
import assert from "node:assert/strict"
|
|
import test from "node:test"
|
|
import { randomUUID } from "node:crypto"
|
|
import Fastify from "fastify"
|
|
import jwt from "jsonwebtoken"
|
|
import cookie from "@fastify/cookie"
|
|
import authPlugin from "../src/plugins/auth"
|
|
import { secrets } from "../src/utils/secrets"
|
|
import { collaborationRoutes, collaborationGuestRoutes } from "../src/routes/collaboration"
|
|
import { CollaborationService } from "../src/modules/collaboration/service"
|
|
import { collaborationDatabase, testDatabaseUrl } from "./helpers/collaboration-db"
|
|
|
|
test("HTTP guest sessions never authorize normal APIs or other subrooms", { skip: !testDatabaseUrl }, async () => {
|
|
const fixture = await collaborationDatabase(["0075_project_collaboration.sql"])
|
|
const app = Fastify(), ownerId = randomUUID(), mail: string[] = []
|
|
const previousSecret = secrets.JWT_SECRET
|
|
secrets.JWT_SECRET = "collaboration-test-only-secret-" + randomUUID()
|
|
const service = new CollaborationService(fixture.db, { publicUrl: "https://owner.example", guestPepper: () => "p".repeat(64),
|
|
sendMail: async (to, subject, html) => { mail.push(html); return { success: true } } })
|
|
const options = { service, apiUrl: "https://owner.example/backend" }
|
|
try {
|
|
await fixture.pool.query("INSERT INTO tenants VALUES (1,'Owner',null)")
|
|
await fixture.pool.query("INSERT INTO auth_users(id,email,is_admin) VALUES ($1,'owner@example.test',true)", [ownerId])
|
|
await fixture.pool.query("INSERT INTO auth_tenant_users(tenant_id,user_id) VALUES (1,$1)", [ownerId])
|
|
await fixture.pool.query("INSERT INTO projects(id,tenant,name,profiles,archived) VALUES (1,1,'Internal','{}',false)")
|
|
app.decorate("db", fixture.db)
|
|
await app.register(cookie)
|
|
await app.register(collaborationGuestRoutes, options)
|
|
await app.register(async protectedApp => {
|
|
await protectedApp.register(authPlugin)
|
|
await protectedApp.register(collaborationRoutes, options)
|
|
}, { prefix: "/api" })
|
|
const ownerToken = jwt.sign({ user_id: ownerId, tenant_id: 1, is_admin: true }, secrets.JWT_SECRET)
|
|
const headers = { authorization: `Bearer ${ownerToken}` }
|
|
const created = await app.inject({ method: "POST", url: "/api/collaboration/rooms", headers, payload: { projectId: 1, name: "Guest room" } })
|
|
assert.equal(created.statusCode, 200, created.body)
|
|
const roomId = created.json().id
|
|
const invited = await app.inject({ method: "POST", url: `/api/collaboration/rooms/${roomId}/invitations`, headers,
|
|
payload: { kind: "guest", email: "guest@example.test", name: "Guest", sendEmail: false } })
|
|
assert.equal(invited.statusCode, 200, invited.body)
|
|
const token = new URL(invited.json().url).hash.slice("#invite=".length)
|
|
const requested = await app.inject({ method: "POST", url: "/guest/api/auth/request", payload: { invitationToken: token } })
|
|
assert.equal(requested.statusCode, 200, requested.body)
|
|
const verified = await app.inject({ method: "POST", url: "/guest/api/auth/verify", payload: {
|
|
challengeId: requested.json().challengeId, code: mail[0].match(/<strong>(\d+)<\/strong>/)![1],
|
|
} })
|
|
assert.equal(verified.statusCode, 200, verified.body)
|
|
const guestHeaders = { authorization: `Bearer ${verified.json().token}` }
|
|
const accepted = await app.inject({ method: "POST", url: `/guest/api/invitations/${invited.json().id}/accept`, headers: guestHeaders, payload: { token } })
|
|
assert.equal(accepted.statusCode, 200, accepted.body)
|
|
assert.equal((await app.inject({ url: "/api/collaboration", headers: guestHeaders })).statusCode, 401)
|
|
assert.equal((await app.inject({ url: "/guest/api/rooms", headers })).statusCode, 401)
|
|
assert.equal((await app.inject({ url: `/guest/api/rooms/${roomId}`, headers: guestHeaders })).statusCode, 200)
|
|
assert.equal((await app.inject({ url: `/guest/api/rooms/${randomUUID()}`, headers: guestHeaders })).statusCode, 404)
|
|
const sent = await app.inject({ method: "POST", url: `/guest/api/rooms/${roomId}/messages`, headers: guestHeaders,
|
|
payload: { text: "Guest message", clientId: randomUUID(), tenantId: 999, principalId: ownerId } })
|
|
assert.equal(sent.statusCode, 200, sent.body)
|
|
const stored = await fixture.pool.query("SELECT p.kind FROM collaboration_messages m JOIN collaboration_principals p ON p.id=m.principal_id WHERE m.id=$1", [sent.json().id])
|
|
assert.equal(stored.rows[0].kind, "guest")
|
|
assert.equal((await app.inject({ method: "POST", url: "/guest/api/logout", headers: guestHeaders })).statusCode, 200)
|
|
assert.equal((await app.inject({ url: "/guest/api/rooms", headers: guestHeaders })).statusCode, 401)
|
|
} finally { secrets.JWT_SECRET = previousSecret; await app.close(); await fixture.close() }
|
|
})
|