UNPKG

stackpress

Version:

Incept is a content management framework.

126 lines (125 loc) 3.88 kB
import { SignJWT, jwtVerify } from 'jose'; import Exception from '../Exception.js'; import { matchAnyEvent, matchAnyRoute } from './helpers.js'; export default class SessionServer { static _access = {}; static _expires = 0; static _key = 'session'; static _seed = 'abc123'; static get access() { return this._access; } static get seed() { return this._seed; } static get key() { return this._key; } static set expires(value) { this._expires = value; } static async authorize(req, res, permits = []) { if (Object.keys(this._access).length === 0) { return true; } const session = this.load(req); permits.unshift({ method: req.method.toUpperCase(), route: req.url.pathname }); const permitted = await session.can(...permits); if (!permitted) { res.setError(Exception .for('Unauthorized') .withCode(401) .toResponse()); return false; } res.setResults(await session.authorization()); return true; } static configure(key, seed, access) { this._key = key; this._seed = seed; this._access = access; return this; } static async create(data) { const seed = new TextEncoder().encode(this.seed); const signer = new SignJWT(data) .setProtectedHeader({ alg: 'HS256' }) .setIssuedAt(); if (!this._expires) { return await signer.sign(seed); } return await signer.setExpirationTime(this._expires).sign(seed); } static token(req) { if (req.session.has(this.key)) { return req.session(this.key); } return null; } static load(token) { if (typeof token === 'string') { return new SessionServer(token); } return new SessionServer(this.token(token) || ''); } token; _data; constructor(token) { this.token = token; } async authorization() { const data = await this.data(); return { id: 0, roles: ['GUEST'], ...(data || {}), token: this.token, permits: await this.permits() }; } async data() { if (typeof this._data === 'undefined') { this._data = null; if (this.token.length) { const seed = new TextEncoder().encode(SessionServer.seed); try { const { payload } = await jwtVerify(this.token, seed); this._data = typeof payload === 'string' ? JSON.parse(payload) : payload; } catch (e) { } } } return this._data; } async guest() { const data = await this.data(); return data === null; } async can(...permits) { if (permits.length === 0) { return true; } const permissions = await this.permits(); const events = permissions.filter(permission => typeof permission === 'string'); const routes = permissions.filter(permission => typeof permission !== 'string'); return Array.isArray(permits) && permits.every(permit => typeof permit === 'string' ? matchAnyEvent(permit, events) : matchAnyRoute(permit, routes)); } async permits() { const data = await this.data(); const roles = data?.roles || ['GUEST']; return roles.map(role => SessionServer.access[role] || []).flat().filter((value, index, self) => self.indexOf(value) === index); } save(res) { res.session.set(SessionServer.key, this.token); return this; } } ;