1
0
Fork 0
arangodb/js/server/modules/@arangodb/foxx/legacy/sessions.js

260 lines
7.9 KiB
JavaScript

'use strict';
// //////////////////////////////////////////////////////////////////////////////
// / DISCLAIMER
// /
// / Copyright 2013-2014 triAGENS GmbH, Cologne, Germany
// / Copyright 2015 ArangoDB GmbH, Cologne, Germany
// /
// / Licensed under the Apache License, Version 2.0 (the "License")
// / you may not use this file except in compliance with the License.
// / You may obtain a copy of the License at
// /
// / http://www.apache.org/licenses/LICENSE-2.0
// /
// / Unless required by applicable law or agreed to in writing, software
// / distributed under the License is distributed on an "AS IS" BASIS,
// / WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// / See the License for the specific language governing permissions and
// / limitations under the License.
// /
// / Copyright holder is ArangoDB GmbH, Cologne, Germany
// /
// / @author Jan Steemann
// / @author Lucas Dohmen
// / @author Alan Plum
// //////////////////////////////////////////////////////////////////////////////
const joi = require('joi');
const Foxx = require('@arangodb/foxx/legacy');
const crypto = require('@arangodb/crypto');
const paramSchema = joi.string().optional().description('Foxx session ID');
// //////////////////////////////////////////////////////////////////////////////
// / @brief decorates the controller with session logic
// //////////////////////////////////////////////////////////////////////////////
function decorateController (auth, controller) {
var cfg = auth.configuration;
if (cfg.param) {
controller.allRoutes.queryParam(cfg.param, paramSchema);
}
controller.before('/*', function (req) {
var sessions = auth.getSessionStorage();
var sid;
if (cfg.param) {
sid = req.params(cfg.param);
}
if (!sid && cfg.cookie) {
sid = req.cookie(cfg.cookie.name, cfg.cookie.secret ? {
signed: {
secret: cfg.cookie.secret,
algorithm: cfg.cookie.algorithm
}
} : undefined);
}
if (!sid && cfg.header) {
sid = req.headers[cfg.header.toLowerCase()];
}
if (sid) {
if (cfg.jwt) {
sid = crypto.jwtDecode(cfg.jwt.secret, sid, !cfg.jwt.verify);
}
try {
req.session = sessions.get(sid);
} catch (e) {
if (!(e instanceof sessions.errors.SessionNotFound)) {
throw e;
}
}
}
if (!req.session && cfg.autoCreateSession) {
req.session = sessions.create();
}
});
controller.after('/*', function (req, res) {
if (req.session) {
var sid = req.session.forClient();
if (cfg.jwt) {
sid = crypto.jwtEncode(cfg.jwt.secret, sid, cfg.jwt.algorithm);
}
if (cfg.cookie) {
res.cookie(cfg.cookie.name, sid, {
ttl: req.session.getTTL() / 1000,
signed: cfg.cookie.secret ? {
secret: cfg.cookie.secret,
algorithm: cfg.cookie.algorithm
} : undefined
});
}
if (cfg.header) {
res.set(cfg.header, sid);
}
}
});
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief convenience wrapper for session destruction logic
// //////////////////////////////////////////////////////////////////////////////
function createDestroySessionHandler (auth, opts) {
if (!opts) {
opts = {};
}
if (typeof opts === 'function') {
opts = {after: opts};
}
if (!opts.after) {
opts.after = function (req, res) {
res.json({message: 'logged out'});
};
}
var cfg = auth.configuration;
return function (req, res, injected) {
if (typeof opts.before === 'function') {
opts.before(req, res, injected);
}
if (req.session && typeof req.session.delete === 'function') {
req.session.delete();
}
if (cfg.autoCreateSession) {
req.session = auth.getSessionStorage().create();
} else {
if (cfg.cookie) {
res.cookie(cfg.cookie.name, '', {
ttl: -(7 * 24 * 60 * 60),
sign: cfg.cookie.secret ? {
secret: cfg.cookie.secret,
algorithm: cfg.cookie.algorithm
} : undefined
});
}
delete req.session;
}
if (typeof opts.after === 'function') {
opts.after(req, res, injected);
}
};
}
var sessionTypes = ['cookie', 'header'];
class Sessions {
// //////////////////////////////////////////////////////////////////////////////
// / @brief constructor
// //////////////////////////////////////////////////////////////////////////////
constructor (opts) {
if (!opts) {
opts = {};
}
if (opts.type) {
if (opts.type === 'cookie') {
delete opts.header;
opts.cookie = opts.cookie || true;
} else if (opts.type === 'header') {
delete opts.cookie;
opts.header = opts.header || true;
} else {
throw new Error('Only the following session types are supported at this time: ' + sessionTypes.join(', '));
}
}
if (opts.cookie) {
if (opts.cookie === true) {
opts.cookie = {};
} else if (typeof opts.cookie === 'string') {
opts.cookie = {name: opts.cookie};
} else if (typeof opts.cookie !== 'object') {
throw new Error('Expected cookie settings to be an object, boolean or string.');
}
if (!opts.cookie.name) {
opts.cookie.name = 'sid';
} else if (typeof opts.cookie.name !== 'string') {
throw new Error('Cookie name must be a string or empty.');
}
if (opts.cookie.secret && typeof opts.cookie.secret !== 'string') {
throw new Error('Cookie secret must be a string or empty.');
}
}
if (opts.header) {
if (opts.header === true) {
opts.header = 'X-Session-Id';
} else if (typeof opts.header !== 'string') {
throw new Error('Header name must be true, a string or empty.');
}
}
if (opts.param) {
if (opts.param === true) {
opts.param = 'FOXXSID';
} else if (typeof opts.param !== 'string') {
throw new Error('Param name must be true, a string or empty.');
}
}
if (opts.jwt) {
if (opts.jwt === true) {
opts.jwt = {};
} else if (typeof opts.jwt === 'string') {
opts.jwt = {secret: opts.jwt};
} else if (typeof opts.jwt !== 'object') {
throw new Error('Expected JWT settings to be an object, boolean or string.');
}
if (opts.jwt.verify !== false) {
opts.jwt.verify = true;
}
if (!opts.jwt.secret) {
opts.jwt.secret = '';
if (!opts.jwt.algorithm) {
opts.jwt.algorithm = 'none';
} else if (opts.jwt.algorithm !== 'none') {
throw new Error('Must provide a JWT secret to use any algorithm other than "none".');
}
} else {
if (typeof opts.jwt.secret !== 'string') {
throw new Error('Header JWT secret must be a string or empty.');
}
if (!opts.jwt.algorithm) {
opts.jwt.algorithm = 'HS256';
} else {
opts.jwt.algorithm = crypto.jwtCanonicalAlgorithmName(opts.jwt.algorithm);
}
}
}
if (opts.autoCreateSession !== false) {
opts.autoCreateSession = true;
}
if (!opts.sessionStorage) {
if (opts.sessionStorageApp) {
opts.sessionStorage = opts.sessionStorageApp;
} else {
opts.sessionStorage = '/_system/sessions';
}
}
this.configuration = opts;
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief fetches the session storage
// //////////////////////////////////////////////////////////////////////////////
getSessionStorage () {
if (typeof this.configuration.sessionStorage !== 'string') {
return this.configuration.sessionStorage;
}
return Foxx.getExports(this.configuration.sessionStorage).sessionStorage;
}
}
exports.sessionTypes = sessionTypes;
exports.Sessions = Sessions;
exports.decorateController = decorateController;
exports.createDestroySessionHandler = createDestroySessionHandler;