Wechsel Ticketsystem → Projekt-Admin per Einmal-Link, Rückweg-Button
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9b14a74fcc
commit
0555a2274d
7 files changed
+164
-1
No files matched your search
@@ -0,0 +1,85 @@
|
||||
import { test, after } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { startAppwriteMock } from './support/appwriteMock.js'
|
||||
import { startFakeStripe } from './support/fakeStripe.js'
|
||||
import { useTestEnv } from './support/testEnv.js'
|
||||
|
||||
// Wechsel Ticketsystem -> Projekt-Admin per Einmal-Link (purpose 'switch'), Rueckweg-Button im Admin
|
||||
const aw = await startAppwriteMock()
|
||||
const st = await startFakeStripe()
|
||||
useTestEnv({ appwriteUrl: aw.url, stripeUrl: st.url })
|
||||
const { config } = await import('../server/config.js')
|
||||
const { startTestApp, post, rawGet } = await import('./support/testApp.js')
|
||||
const { default: authRoutes } = await import('../server/routes/auth.js')
|
||||
const { default: switchLinkRoutes } = await import('../server/routes/admin/switchLink.js')
|
||||
const { createStaffPreviewToken } = await import('../server/services/staffPreviewTokens.js')
|
||||
const app = await startTestApp({ '/api/auth': authRoutes, '/api/admin/switch-link': switchLinkRoutes })
|
||||
after(() => Promise.all([app.close(), aw.close(), st.close()]))
|
||||
|
||||
aw.seed('employees', { $id: 'e_staff2', userId: 'u_staff2', displayName: 'Andrej', email: 'andrej@webklar.com' })
|
||||
|
||||
const bearer = (jwt) => ({ headers: { authorization: `Bearer ${jwt}` } })
|
||||
const switchLink = (jwt) => post('/api/admin/switch-link', {}, jwt ? bearer(jwt) : {})
|
||||
const tokenOf = (openUrl) => new URL(openUrl).searchParams.get('token')
|
||||
const portalSession = (res) => {
|
||||
const cookie = res.headers.getSetCookie().find((c) => c.startsWith(`${config.cookieName}=`))
|
||||
if (!cookie) return null
|
||||
const raw = decodeURIComponent(cookie.split(';')[0].slice(config.cookieName.length + 1))
|
||||
return JSON.parse(raw.slice(2, raw.lastIndexOf('.')))
|
||||
}
|
||||
|
||||
test('switch-link: ohne, ungueltiges und halbes JWT -> 401', async () => {
|
||||
assert.equal((await switchLink(null)).status, 401)
|
||||
assert.equal((await switchLink('jwt-falsch')).status, 401)
|
||||
assert.equal((await switchLink('jwt-halb')).status, 401)
|
||||
})
|
||||
|
||||
test('switch-link: kein Mitarbeiter -> 403', async () => {
|
||||
assert.equal((await switchLink('jwt-kunde')).status, 403)
|
||||
})
|
||||
|
||||
test('switch-link: Mitarbeiter ohne Admin-Label bekommt einen Link', async () => {
|
||||
const res = await switchLink('jwt-staff')
|
||||
assert.equal(res.status, 200)
|
||||
assert.match(res.body.openUrl, /^https:\/\/project\.webklar\.com\/api\/auth\/staff-switch\?token=[0-9a-f]{64}$/)
|
||||
})
|
||||
|
||||
test('staff-switch: gueltig -> Admin-Sitzung und /admin.html, zweites Mal -> Login', async () => {
|
||||
const { body } = await switchLink('jwt-staff')
|
||||
const path = `/api/auth/staff-switch?token=${tokenOf(body.openUrl)}`
|
||||
const res = await rawGet(path)
|
||||
assert.equal(res.status, 302)
|
||||
assert.equal(res.headers.get('location'), '/admin.html')
|
||||
const session = portalSession(res)
|
||||
assert.equal(session.role, 'admin')
|
||||
assert.equal(session.isAdmin, false)
|
||||
assert.equal(session.appwriteUserId, 'u_staff2')
|
||||
const again = await rawGet(path)
|
||||
assert.equal(again.status, 302)
|
||||
assert.equal(again.headers.get('location'), '/login.html?error=Link%20abgelaufen%2C%20bitte%20anmelden')
|
||||
assert.equal(portalSession(again), null)
|
||||
})
|
||||
|
||||
test('staff-switch: nach 61 Sekunden abgelaufen', async (t) => {
|
||||
const { body } = await switchLink('jwt-staff')
|
||||
const now = Date.now()
|
||||
t.mock.method(Date, 'now', () => now + 61 * 1000)
|
||||
const res = await rawGet(`/api/auth/staff-switch?token=${tokenOf(body.openUrl)}`)
|
||||
assert.match(res.headers.get('location'), /^\/login\.html\?error=/)
|
||||
})
|
||||
|
||||
test('Tokens nicht vertauschbar: Switch ueber staff-preview, Preview ueber staff-switch', async () => {
|
||||
const { body } = await switchLink('jwt-staff')
|
||||
assert.equal((await rawGet(`/api/auth/staff-preview?token=${tokenOf(body.openUrl)}`)).status, 410)
|
||||
const previewToken = createStaffPreviewToken({ userId: 'u_admin', email: 'kenso@webklar.com', name: 'Kenso', previewUrl: 'https://x.project.webklar.com/' })
|
||||
const res = await rawGet(`/api/auth/staff-switch?token=${previewToken}`)
|
||||
assert.match(res.headers.get('location'), /^\/login\.html\?error=/)
|
||||
assert.equal(portalSession(res), null)
|
||||
})
|
||||
|
||||
test('admin.html: Rueckweg ins Ticketsystem in der Kopfzeile', () => {
|
||||
const html = readFileSync(new URL('../public/admin.html', import.meta.url), 'utf8')
|
||||
const actions = html.slice(html.indexOf('portal-header-actions'), html.indexOf('id="logout-btn"'))
|
||||
assert.match(actions, /<a class="portal-btn-outline" href="https:\/\/ticket\.webklar\.com\/tickets">← Ticketsystem<\/a>/)
|
||||
})
|
||||
Reference in new issue
Block a user