From: Nick Downing Date: Sun, 23 Jan 2022 07:54:56 +0000 (+1100) Subject: Improve session handling, so incoming/outgoing cookie is only checked/set once X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?p=ndcode_site.git;a=commitdiff_plain;h=b6b4073ff33a08fcded23bd742fece2e57bdf802 Improve session handling, so incoming/outgoing cookie is only checked/set once --- diff --git a/_config/site.jst b/_config/site.jst index 39cd99f..6871bc0 100644 --- a/_config/site.jst +++ b/_config/site.jst @@ -1,4 +1,6 @@ let assert = require('assert') +let cookie = require('cookie') +let crypto = require('crypto') let logjson = (await import('@ndcode/logjson')).default //let NodeMailerCache = require('@ndcode/nodemailer_cache') let XDate = require('xdate') @@ -97,6 +99,69 @@ return async (resources, root, prev_site) => { return /*await*/ this.zettair_cache.get(this.root + pathname) } + // customize template serving to initialize env.now, env.session_key first + async serve_jst(env, pathname, ...args) { + let template + try { + template = await this.jst_cache.get(pathname) + } + catch (err) { + if (!(err instanceof Error) || err.code !== 'ENOENT') + throw err + return false + } + env.site = this + + // added part + env.now = XDate.now() + let transaction = await this.database.Transaction() + try { + let sessions = await ( + await transaction.get({}) + ).get('sessions', {}) + + let cookies = cookie.parse(env.request.headers.cookie || '') + let session, expires = new XDate(env.now) + if ( + Object.prototype.hasOwnProperty.call(cookies, 'session_key') && + ( + session = await sessions.get( + env.session_key = cookies.session_key + ) + ) !== undefined && + env.now < await session.get('expires', 0) + ) + // if session key is already in database, the requester supports + // cookies, and each access extends the session expiry by 1 month + expires.addMonths(1) + else { + // first request for session, maybe a bot, retain for only 1 day + expires.addDays(1) + + do { + env.session_key = crypto.randomBytes(16).toString('hex') + } while (sessions.has(env.session_key)) + session = transaction.LazyObject() + sessions.set(env.session_key, session) + } + + await session.set('expires', expires.getTime()) + env.response.setHeader( + 'Set-Cookie', + `session_key=${env.session_key}; expires=${expires.toUTCString()}; path=/;` + ) + + await transaction.commit() + } + catch (error) { + transaction.rollback() + throw error + } + + await template(env, ...args) + return true + } + // customize the file search/serving algorithm for this particular website async respond(env) { if ( diff --git a/_lib/session_cookie.jst b/_lib/session_cookie.jst index 861e702..98c8761 100644 --- a/_lib/session_cookie.jst +++ b/_lib/session_cookie.jst @@ -1,46 +1,20 @@ -let cookie = require('cookie') -let crypto = require('crypto') let XDate = require('xdate') -// note: this routine is "almost" idempotent -// it will return the same session key each time -// expiry may be increased slightly at each call return async (env, transaction) => { let sessions = await ( await transaction.get({}) ).get('sessions', {}) - let cookies = cookie.parse(env.request.headers.cookie || '') - let now = Date.now() - - let session, expires = new XDate(now) - if ( - Object.prototype.hasOwnProperty.call(cookies, 'session_key') && - (session = await sessions.get(cookies.session_key)) !== undefined && - now < await session.get('expires', 0) - ) { - // if session key is already in database, we know the requester supports - // cookies, therefore each access extends the session expiry by 1 month - env.session_key = cookies.session_key - expires.addMonths(1) - } - else { - // first request for session, maybe a bot, retain session for only 1 day - if (!Object.prototype.hasOwnProperty.call(env, 'session_key')) { - do { - env.session_key = crypto.randomBytes(16).toString('hex') - } while (sessions.has(env.session_key)) - } - session = await sessions.get(env.session_key, {}) + let session = await sessions.get(env.session_key) + if (session === undefined) { + // this should never happen, but could happen if we take more than a day + // to process an incoming request, and database is cleaned in the meantime + let expires = new XDate(env.now) expires.addDays(1) + session = Transaction.json_to_logjson({expires: expires.getTime()}) + sessions.set(env.session_key, session) } - await session.set('expires', expires.getTime()) - env.response.setHeader( - 'Set-Cookie', - `session_key=${env.session_key}; expires=${expires.toUTCString()}; path=/;` - ) - env.signed_in_as = await session.get_json('signed_in_as', null) return session }