UNPKG

sgapps-server

Version:
232 lines (184 loc) 6.45 kB
//@ts-nocheck /*! * cookies * Copyright(c) 2014 Jed Schmidt, http://jed.is/ * Copyright(c) 2015-2016 Douglas Christopher Wilson * MIT Licensed */ var deprecate = require('../depd')('cookies'); var Keygrip = require('../keygrip'); var http = require('http'); var cache = {}; /** * RegExp to match field-content in RFC 7230 sec 3.2 * * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] * field-vchar = VCHAR / obs-text * obs-text = %x80-FF */ var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/; /** * RegExp to match Same-Site cookie attribute value. */ var sameSiteRegExp = /^(?:lax|strict)$/i; function Cookies(request, response, options) { if (!(this instanceof Cookies)) return new Cookies(request, response, options); this.secure = undefined; this.request = request; this.response = response; if (options) { if (Array.isArray(options)) { // array of key strings deprecate('"keys" argument; provide using options {"keys": [...]}'); this.keys = new Keygrip(options); } else if (options.constructor && options.constructor.name === 'Keygrip') { // any keygrip constructor to allow different versions deprecate('"keys" argument; provide using options {"keys": keygrip}'); this.keys = options; } else { this.keys = Array.isArray(options.keys) ? new Keygrip(options.keys) : options.keys; this.secure = options.secure; } } } Cookies.prototype.get = function(name, opts) { var sigName = name + ".sig", header, match, value, remote, data, index, signed = opts && opts.signed !== undefined ? opts.signed : !!this.keys; header = this.request.headers.cookie; if (!header) return; match = header.match(getPattern(name)); if (!match) return; value = match[1]; if (!opts || !signed) return value; remote = this.get(sigName); if (!remote) return; data = name + "=" + value; if (!this.keys) throw new Error('.keys required for signed cookies'); index = this.keys.index(data, remote); if (index < 0) { this.set(sigName, null, {path: "/", signed: false }, true); } else { index && this.set(sigName, this.keys.sign(data), { signed: false }, true); return value; } }; Cookies.prototype.set = function(name, value, opts, skipError) { var res = this.response, req = this.request, headers = res.getHeader("Set-Cookie") || [], secure = this.secure !== undefined ? !!this.secure : req.protocol === 'https' || (req.connection || {}).encrypted, cookie = new Cookie(name, value, opts), signed = opts && opts.signed !== undefined ? opts.signed : !!this.keys; if (typeof headers == "string") headers = [headers]; if (!secure && opts && opts.secure) { if (skipError) return this; throw new Error('Cannot send secure cookie over unencrypted connection'); } cookie.secure = secure; if (opts && "secure" in opts) cookie.secure = opts.secure; if (opts && "secureProxy" in opts) { if (!skipError) { deprecate('"secureProxy" option; use "secure" option, provide "secure" to constructor if needed'); } cookie.secure = opts.secureProxy; } pushCookie(headers, cookie); if (opts && signed) { if (!this.keys) { if (skipError) return this; throw new Error('.keys required for signed cookies'); } cookie.value = this.keys.sign(cookie.toString()); cookie.name += ".sig"; pushCookie(headers, cookie); } // disabled // var setHeader = res.set ? http.OutgoingMessage.prototype.setHeader : res.setHeader var setHeader = res.setHeader || http.OutgoingMessage.prototype.setHeader; setHeader.call(res, 'Set-Cookie', headers); return this; }; function Cookie(name, value, attrs) { if (!fieldContentRegExp.test(name)) { throw new TypeError('argument name is invalid'); } if (value && !fieldContentRegExp.test(value)) { throw new TypeError('argument value is invalid'); } if (!value) { this.expires = new Date(0); } this.name = name; this.value = value || ""; var itemName; for (itemName in attrs) { this[itemName] = attrs[itemName]; } if (this.path && !fieldContentRegExp.test(this.path)) { throw new TypeError('option path is invalid'); } if (this.domain && !fieldContentRegExp.test(this.domain)) { throw new TypeError('option domain is invalid'); } if (this.sameSite && this.sameSite !== true && !sameSiteRegExp.test(this.sameSite)) { throw new TypeError('option sameSite is invalid'); } } Cookie.prototype.path = "/"; Cookie.prototype.expires = undefined; Cookie.prototype.domain = undefined; Cookie.prototype.httpOnly = true; Cookie.prototype.sameSite = false; Cookie.prototype.secure = false; Cookie.prototype.overwrite = false; Cookie.prototype.toString = function() { return this.name + "=" + this.value; }; Cookie.prototype.toHeader = function() { var header = this.toString(); if (this.maxAge) this.expires = new Date(Date.now() + this.maxAge); if (this.path ) header += "; path=" + this.path; if (this.expires ) header += "; expires=" + this.expires.toUTCString(); if (this.domain ) header += "; domain=" + this.domain; if (this.sameSite ) header += "; samesite=" + (this.sameSite === true ? 'strict' : this.sameSite.toLowerCase()); if (this.secure ) header += "; secure"; if (this.httpOnly ) header += "; httponly"; return header; }; // back-compat so maxage mirrors maxAge Object.defineProperty(Cookie.prototype, 'maxage', { configurable: true, enumerable: true, get: function () { return this.maxAge; }, set: function (val) { this.maxAge = val; return this.maxAge; } }); deprecate.property(Cookie.prototype, 'maxage', '"maxage"; use "maxAge" instead'); function getPattern(name) { if (cache[name]) return cache[name]; cache[name] = new RegExp( "(?:^|;) *" + name.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&") + "=([^;]*)" ); return cache[name]; } function pushCookie(headers, cookie) { if (cookie.overwrite) { for (var i = headers.length - 1; i >= 0; i--) { if (headers[i].indexOf(cookie.name + '=') === 0) { headers.splice(i, 1); } } } headers.push(cookie.toHeader()); } Cookies.connect = Cookies.express = function(keys) { return function(req, res, next) { req.cookies = res.cookies = new Cookies(req, res, { keys: keys }); next(); }; }; Cookies.Cookie = Cookie; module.exports = Cookies;