Files
Webklar-Kundenbereich/test/teamAuth.test.js
T
ClaudeandClaude Opus 5.5 2659385205 Mitarbeiter nur noch per Label: Kunden-Konten bekommen keinen Team-Zugang
Kunden haben Konten im selben Appwrite-Projekt wie das Ticketsystem. Bisher
galt als Mitarbeiter, wer einen employees-Eintrag hatte - den legte das
Ticketsystem beim Login jedes Kontos automatisch an, und jedes angemeldete
Konto konnte ihn auch selbst anlegen. Damit kam ein Kunde ueber /api/team an
Chats, Kundenansicht, Angebote und Finanzen.

- staffLabels.js: Mitarbeiter = Label "mitarbeiter" oder "admin" (setzt nur
  der Server-Key)
- Team-Eingang (requireStaffJwt) und Portal-Login-Sperre nur noch per Label;
  kein Abgleich ueber employees-Eintrag oder Mail mehr
- Portal-Login: Konto-Abruf gestoert -> 503 fuer alle (fail closed)
- Mitarbeiter anlegen/bearbeiten setzt das Label "mitarbeiter"
- scripts/mitarbeiter-zugang.mjs: --status (markiert employees-Eintraege von
  Kunden), --label, --eintrag-loeschen (nur Eintrag, nie das Konto), --rechte
  (Probelauf, --anwenden mit Sicherung, --zurueck): any/guests/users ->
  label:mitarbeiter + label:admin in Collections, Buckets und bei Dokument-/
  Datei-Sicherheit auch einzelnen Dokumenten und Dateien

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 10:54:09 +00:00

81 lines
3.7 KiB
JavaScript

import { test, after } from 'node:test'
import assert from 'node:assert/strict'
import { Router } from 'express'
import { startAppwriteMock } from './support/appwriteMock.js'
import { startFakeStripe } from './support/fakeStripe.js'
import { useTestEnv } from './support/testEnv.js'
// Team-Eingang /api/team: Appwrite-JWT aus dem Ticketsystem, jedes Konto mit Label mitarbeiter oder admin
const aw = await startAppwriteMock()
const st = await startFakeStripe()
useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url })
const { startTestApp, rawGet } = await import('./support/testApp.js')
const { requireStaffJwt } = await import('../server/middleware/staffJwt.js')
const { corsMiddleware } = await import('../server/middleware/cors.js')
const probeRouter = Router()
probeRouter.use(requireStaffJwt)
probeRouter.get('/', (req, res) => res.json(req.session))
const app = await startTestApp({
'/': corsMiddleware(['https://ticket.webklar.com']),
'/probe': probeRouter,
})
after(() => Promise.all([app.close(), aw.close(), st.close()]))
aw.seed('employees', { $id: 'e_staff2', userId: 'u_staff2', displayName: 'Andrej', email: 'andrej@webklar.com' })
aw.seed('employees', { $id: 'e_nico', userId: '', displayName: 'Nico', email: 'nico@webklar.com' })
// Beim Login eines Kunden ins Ticketsystem entstand frueher automatisch ein employees-Eintrag
aw.seed('employees', { $id: 'e_kunde', userId: 'u_kunde_e', displayName: 'Kunde', email: 'kunde@example.invalid' })
const probe = (jwt) => rawGet('/probe', jwt ? { headers: { authorization: `Bearer ${jwt}` } } : {})
test('ohne, ungueltiges und halbes JWT -> 401', async () => {
for (const jwt of [null, 'jwt-falsch', 'jwt-halb']) assert.equal((await probe(jwt)).status, 401, String(jwt))
})
test('Kunde (kein Mitarbeiter, kein Label) -> 403', async () => {
const res = await probe('jwt-kunde')
assert.equal(res.status, 403)
assert.equal(res.body.error, 'Kein Mitarbeiterkonto.')
})
test('Kunde mit employees-Eintrag, aber ohne Label -> 403', async () => {
const res = await probe('jwt-kunde-eintrag')
assert.equal(res.status, 403)
assert.equal(res.body.error, 'Kein Mitarbeiterkonto.')
})
test('Mail gleicht einem Mitarbeiter (bestaetigt oder nicht), kein Label -> 403', async () => {
assert.equal((await probe('jwt-mail-ungeprueft')).status, 403)
assert.equal((await probe('jwt-mail-geprueft')).status, 403)
})
test('Label mitarbeiter (ohne admin) -> erlaubt, Name aus employees, Sitzung in Admin-Form', async () => {
const res = await probe('jwt-staff')
assert.equal(res.status, 200)
assert.deepEqual(res.body, { role: 'admin', isAdmin: false, appwriteUserId: 'u_staff2', name: 'Andrej', email: 'andrej@webklar.com' })
})
test('Label admin ohne employees-Eintrag -> erlaubt, isAdmin true', async () => {
const res = await probe('jwt-admin')
assert.equal(res.status, 200)
assert.equal(res.body.isAdmin, true)
})
test('CORS: erlaubte Herkunft bekommt die Upload-Header, fremde nichts', async () => {
const ok = await rawGet('/probe', { headers: { origin: 'https://ticket.webklar.com' } })
assert.equal(ok.headers.get('access-control-allow-origin'), 'https://ticket.webklar.com')
assert.equal(
ok.headers.get('access-control-allow-headers'),
'Authorization, Content-Type, X-File-Name, X-Attachment-Kind, X-Duration-Ms, X-Image-Width, X-Image-Height'
)
const bad = await rawGet('/probe', { headers: { origin: 'https://evil.example' } })
assert.equal(bad.headers.get('access-control-allow-origin'), null)
})
test('CORS: Preflight der erlaubten Herkunft -> 204 ohne JWT-Pruefung', async () => {
const res = await fetch(`${app.url}/probe`, { method: 'OPTIONS', headers: { origin: 'https://ticket.webklar.com' } })
assert.equal(res.status, 204)
})