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
			| 
								 
											3 years ago
										 
									 | 
							
								'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
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 |