You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
185 lines
4.8 KiB
185 lines
4.8 KiB
'use strict'
|
|
|
|
const crypto = require('crypto')
|
|
|
|
const Cookie = require('./cookie')
|
|
const cookieSignature = require('cookie-signature')
|
|
const { configure: configureStringifier } = require('safe-stable-stringify')
|
|
|
|
const stringify = configureStringifier({ bigint: false })
|
|
|
|
const maxAge = Symbol('maxAge')
|
|
const secretKey = Symbol('secretKey')
|
|
const sign = Symbol('sign')
|
|
const addDataToSession = Symbol('addDataToSession')
|
|
const generateId = Symbol('generateId')
|
|
const requestKey = Symbol('request')
|
|
const cookieOptsKey = Symbol('cookieOpts')
|
|
const originalHash = Symbol('originalHash')
|
|
const hash = Symbol('hash')
|
|
|
|
module.exports = class Session {
|
|
constructor (request, idGenerator, cookieOpts, secret, prevSession = {}) {
|
|
this[generateId] = idGenerator
|
|
this.expires = null
|
|
this.cookie = new Cookie(cookieOpts)
|
|
this[cookieOptsKey] = cookieOpts
|
|
this[maxAge] = cookieOpts.maxAge
|
|
this[secretKey] = secret
|
|
this[addDataToSession](prevSession)
|
|
this[requestKey] = request
|
|
this.touch()
|
|
if (!this.sessionId) {
|
|
this.sessionId = this[generateId](this[requestKey])
|
|
this.encryptedSessionId = this[sign]()
|
|
}
|
|
this[originalHash] = this[hash]()
|
|
}
|
|
|
|
touch () {
|
|
if (this[maxAge]) {
|
|
this.expires = new Date(Date.now() + this[maxAge])
|
|
this.cookie.expires = this.expires
|
|
}
|
|
}
|
|
|
|
regenerate (callback) {
|
|
if (callback) {
|
|
const session = new Session(this[requestKey], this[generateId], this[cookieOptsKey], this[secretKey])
|
|
|
|
this[requestKey].sessionStore.set(session.sessionId, session, error => {
|
|
this[requestKey].session = session
|
|
|
|
callback(error)
|
|
})
|
|
} else {
|
|
return new Promise((resolve, reject) => {
|
|
const session = new Session(this[requestKey], this[generateId], this[cookieOptsKey], this[secretKey])
|
|
|
|
this[requestKey].sessionStore.set(session.sessionId, session, error => {
|
|
this[requestKey].session = session
|
|
|
|
if (error) {
|
|
reject(error)
|
|
} else {
|
|
resolve()
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
[addDataToSession] (prevSession) {
|
|
for (const key in prevSession) {
|
|
if (!['expires', 'cookie'].includes(key)) {
|
|
this[key] = prevSession[key]
|
|
}
|
|
}
|
|
}
|
|
|
|
get (key) {
|
|
return this[key]
|
|
}
|
|
|
|
set (key, value) {
|
|
this[key] = value
|
|
}
|
|
|
|
destroy (callback) {
|
|
if (callback) {
|
|
this[requestKey].sessionStore.destroy(this.sessionId, error => {
|
|
this[requestKey].session = null
|
|
|
|
callback(error)
|
|
})
|
|
} else {
|
|
return new Promise((resolve, reject) => {
|
|
this[requestKey].sessionStore.destroy(this.sessionId, error => {
|
|
this[requestKey].session = null
|
|
|
|
if (error) {
|
|
reject(error)
|
|
} else {
|
|
resolve()
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
reload (callback) {
|
|
if (callback) {
|
|
this[requestKey].sessionStore.get(this.sessionId, (error, session) => {
|
|
this[requestKey].session = new Session(this[requestKey], this[generateId], this[cookieOptsKey], this[secretKey], session)
|
|
|
|
callback(error)
|
|
})
|
|
} else {
|
|
return new Promise((resolve, reject) => {
|
|
this[requestKey].sessionStore.get(this.sessionId, (error, session) => {
|
|
this[requestKey].session = new Session(this[requestKey], this[generateId], this[cookieOptsKey], this[secretKey], session)
|
|
|
|
if (error) {
|
|
reject(error)
|
|
} else {
|
|
resolve()
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
save (callback) {
|
|
if (callback) {
|
|
this[requestKey].sessionStore.set(this.sessionId, this, error => {
|
|
callback(error)
|
|
})
|
|
} else {
|
|
return new Promise((resolve, reject) => {
|
|
this[requestKey].sessionStore.set(this.sessionId, this, error => {
|
|
if (error) {
|
|
reject(error)
|
|
} else {
|
|
resolve()
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
[sign] () {
|
|
return cookieSignature.sign(this.sessionId, this[secretKey])
|
|
}
|
|
|
|
[hash] () {
|
|
const sess = this
|
|
const str = stringify(sess, function (key, val) {
|
|
// ignore sess.cookie property
|
|
if (this === sess && key === 'cookie') {
|
|
return
|
|
}
|
|
|
|
return val
|
|
})
|
|
|
|
return crypto
|
|
.createHash('sha1')
|
|
.update(str, 'utf8')
|
|
.digest('hex')
|
|
}
|
|
|
|
isModified () {
|
|
return this[originalHash] !== this[hash]()
|
|
}
|
|
|
|
static restore (request, idGenerator, cookieOpts, secret, prevSession) {
|
|
const restoredSession = new Session(request, idGenerator, cookieOpts, secret, prevSession)
|
|
const restoredCookie = new Cookie(cookieOpts)
|
|
restoredCookie.expires = new Date(prevSession.cookie.expires)
|
|
restoredSession.cookie = restoredCookie
|
|
restoredSession.expires = restoredCookie.expires
|
|
restoredSession[originalHash] = restoredSession[hash]()
|
|
return restoredSession
|
|
}
|
|
}
|