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
 | |
|   }
 | |
| }
 |