UNPKG

dl

Version:

DreamLab Libs

448 lines (393 loc) 13.3 kB
var core = require('core'), Loader = core.http.Loader, BinaryData = core.data.BinaryData, Request = core.http.Request, Types = core.common.Types, NauthUserProfile = require('./NauthUserProfile.js').NauthUserProfile, NauthUserServices = require('./NauthUserServices.js').NauthUserServices; /** * User authorisation sessions API * * @param {core.http.Request} request HTTP request * @param {core.http.Response} response HTTP response * @constructor */ var NauthManager = function (request, response) { this._request = request; this._response = response; this._portalId = 1; // Onet this._onetApp = null; this._cookieName = 'onet_token'; this._profile = null; this._mus = null; }; /** * Sets id of portal Nauth is used in * * @param {Number} portalId Portal ID * @type NauthManager */ NauthManager.prototype.setPortalId = function (portalId) { portalId = parseInt(portalId, 10); if (!isNaN(portalId)) { this._portalId = portalId; this._cookieName = 'onet_token'; if (portalId != 1) { this._cookieName += portalId; } } return this; }; /** * Sets X-Onet-App * * @param {String} onetApp Application name * @type NauthManager */ NauthManager.prototype.setOnetApp = function (onetApp) { this._onetApp = onetApp; return this; }; /** * Checks whether current host is considered as secured * * @type Boolean * @private */ NauthManager.prototype.isSecuredHost = function () { return NauthManager.SECURED_HOST && (this._request.getProto() === 'https:' || this._request.getHeader('x-forwarded-proto') === 'https'); }; /** * Returns user profile object * * @param {Function} callback Callback function; input: err (code, message), data * @type NauthManager */ NauthManager.prototype.getUserProfile = function (callback) { var that = this; this._hasValidToken(function (err, data) { if (!data) { that._profile = new NauthUserProfile(); } callback(err, that._profile); }); return this; }; /** * Returns user services object * * @param {Function} callback Callback function; input: err (code, message), data * @type NauthManager */ NauthManager.prototype.getUserServices = function (callback) { var that = this; this._hasValidToken(function (err, data) { if (!data) { that._mus = new NauthUserServices(); } callback(err, that._mus); }); return this; }; /** * Exchanges given auth code to access token. * * @param {String} code Authorization code * @param {Boolean} [rememberMe=false] Whether option "remember me on this device" was selected * @param {Function} callback Callback function; input: err (code, message), data * @type NauthManager */ NauthManager.prototype.getTokenFromCode = function (code, rememberMe, callback) { if (arguments.length < 3) { callback = arguments[1]; rememberMe = false; } if (!code) { this._removeToken(); callback(null, null); return this; } var request = new Request('http://' + NauthManager.OPAL.HOSTS.OPEN + '/token'), that = this; this._load(request, {grant_type: 'authorization_code', code: code}, function (err, data) { if (err || !data.hasOwnProperty('access_token')) { if (!err || !err.hasOwnProperty('code') || err.code != 401) { that.log('an error occurred while exchanging auth_code to token ' + JSON.stringify(err || data), {auth_code: code}); } callback(err, null); return; } that._saveToken(data.access_token, rememberMe); callback(err, null); }); return this; }; /** * Sets HTTP cookie. * * @param {String} cookie Cookie name * @param {String|Number} value Cookie value * @param {Number} [expires=0] Number of milliseconds time offset when cookie will expire * (negative number removes cookie) * @type NauthManager */ NauthManager.prototype.setCookie = function (name, value, expires) { var cookie = this._request.getHeader('cookie'), cookies = this._response.headers.getHeader('set-cookie'); value = encodeURIComponent(value); if (expires < 0) { if (cookie !== null) { cookie = cookie.replace(new RegExp('(^|;\\s*)' + name + '=[^;]*(;\\s*)?', 'g'), '$2'); this._request.setHeader('cookie', cookie === '' ? null : cookie, true); } } else { if (cookie !== null && new RegExp('(^|;\\s*)' + name + '=').test(cookie)) { cookie = cookie.replace(new RegExp('((^|;\\s*)' + name + '=)[^;]*(;\\s*)?', 'g'), '$1' + value + '$3'); } else { if (cookie) { cookie += '; ' + name + '=' + value; } else { cookie = name + '=' + value; } } this._request.setHeader('cookie', cookie, true); } cookie = name + '=' + value; if (expires) { cookie += '; expires=' + new Date(new Date().getTime() + expires).toUTCString(); } cookie += '; path=/' + (this.isSecuredHost() ? '; secure' : '') + '; httponly'; if (!cookies) { cookies = [cookie]; } else if (Types.isArray(cookies)) { cookies.push(cookie); } else { cookies = [cookies, cookie]; } this._response.headers.setHeader('p3p', 'CP="ALL DSP COR IVD IVA PSD PSA TEL TAI CUS ADM CUR CON SAM OUR IND"', true); this._response.headers.setHeader('set-cookie', cookies, true); return this; }; /** * Checkes whether current user token is valid * * @param {Function} callback Callback function; input: err (code, message), data * @type NauthManager * @private */ NauthManager.prototype._hasValidToken = function (callback) { var that = this; // missing token if (this._request.getCookie(this._cookieName) === null) { callback(null, false); return this; } // profile has been fetched already // check for being logged if (this._profile !== null && !this._profile.isLogged()) { // we have token and it should be "forget" this._removeToken(); callback(null, false); return this; } // could not fetch user data with current token // (means: token is invalid) this._fetchUserData(function () { if (!that._profile) { // we have token and it should be "forget" that._removeToken(); callback(null, false); } else { // everything is correct callback(null, true); } }); return this; }; /** * Removes current token from storage * * @type NauthManager * @private */ NauthManager.prototype._removeToken = function () { return this.setCookie(this._cookieName, 'invalidate', -1000000); }; /** * Stores given token. * * @param {string} token Access token value * @param {Boolean} rememberMe Whether option "remember me on this device" was selected * @type NauthManager * @private */ NauthManager.prototype._saveToken = function (token, rememberMe) { return this.setCookie(this._cookieName, token, rememberMe ? NauthManager.REMEMBER_ME_INTERVAL + Math.round(Math.random() * 48 * 3600000) : 0); }; /** * Fetches user data (profile and services) for current token * * @param {Function} callback Callback function; input: err (code, message) * @return NauthManager * @private */ NauthManager.prototype._fetchUserData = function (callback) { if (this._profile !== null) { callback(null); return this; } var request = new Request('http://' + NauthManager.OPAL.HOSTS.OPEN + '/profile'), token = this._request.getCookie(this._cookieName), that = this; if (token === null) { callback({code: 34, message: 'Unauthorized'}, null); return this; } request.setHeader('authorization', 'Bearer ' + token, true); this._load(request, null, function (err, data) { if (err) { if (!err.hasOwnProperty('code') || err.code != 401) { that.log('an error occurred while fetching profile ' + JSON.stringify(err)); } } else { that._profile = new NauthUserProfile(data.profile); that._mus = new NauthUserServices(data.mus); } callback(err); }); return this; }; /** * Loads data through using REST * * @param {core.http.Request} request HTTP request * @param {String|core.data.BinaryData|Object} [body=""] POST data body * @param {Function} callback Callback function; input: err (code, message), data * @type NauthManager * @private */ NauthManager.prototype._load = function (request, body, callback) { var host = NauthManager.OPAL.GATEWAY ? NauthManager.OPAL.GATEWAY.split(':', 3) : null; request.setHeader('host', request.getConnectionHost(), true); request.setHeader('accept', '*/*'); request.setHeader('x-onet-portal', this._portalId, true); request.setHeader('x-onet-app', this._onetApp, true); request.setHeader('x-caller-host', this._request.getHost(), true); request.setHeader('x-caller-cmd', this._request.getPath() + this._request.getQueryString(), true); request.setHeader('referer', this._request.getHeader('referer') || '', true); if (host) { request.setConnectionHost(host[0]); if (host.length > 1) { request.setPort(host[1]); } } else { request.setConnectionHost(request.getHost()); request.setPort(request.getPort()); } if (typeof body != 'undefined' && body !== null) { if (!(body instanceof String) && !(body instanceof BinaryData)) { var data = ''; Object.keys(body).forEach(function (name) { data += '&' + encodeURIComponent(name) + '=' + encodeURIComponent(body[name]); }); body = data.substr(1); } if (request.getMethod() == 'GET') { request.setMethod('POST'); } if (request.getMethod() == 'POST' && request.getHeader('content-type') === null) { request.setHeader('content-type', 'application/x-www-form-urlencoded', true); } request.setHeader('content-length', body instanceof BinaryData ? body.length() : body.length, true); request.setBody(body); } else { request.setHeader('content-length', 0, true); } var loader = new Loader(request).setTimeout(NauthManager.TIMEOUT); loader.addEventListener(Loader.Event.LOADED, function (e) { if (e.data.getStatusCode() != 200) { callback({code: e.data.getStatusCode(), message: e.data.getBody().toString()}, null); return; } var data = e.data.getBody().toString(); if (e.data.isContentType('application/json') || e.data.isContentType('text/javascript')) { try { data = JSON.parse(data); } catch (ex) { callback({code: 1, message: ex.toString()}, null); return; } if (data.hasOwnProperty('error') && data.error !== null) { callback({code: (data.error_code ? data.error_code - 1000 : 200), message: (data.error_description ? data.error_description : data.error)}, null); return; } } callback(null, data); }); loader.addEventListener(Loader.Event.ERROR, function (e) { callback({code: e.code, message: e.message, data: e.data}, null); }); loader.load(); return this; }; /** * Writes message to application log * * @param {String} message Log message * @parm {Object} [args={}] Extra log data * @param {String} [type="error"] Log level (error, warn, info) * @type NauthManager * @private */ NauthManager.prototype.log = function (message, args, type) { var aux = '', key; if (args) { for (key in args) { if (args.hasOwnProperty(key)) { aux += key + '=' + args[key] + '; '; } } } console[type || 'error']('ES/NAuth: ' + this._request.getCookie(this._cookieName) + ' ' + message + ' portal_id=' + this._portalId + '; app_id=' + this._onetApp + '; referer=' + this._request.getHeader('referer') + '; ' + aux); return this; }; /** * Host is allowed to be considered as secured (SSL) * @static * @constant */ NauthManager.SECURED_HOST = true; NauthManager.OPAL = {}; /** * OPAL gateway host (with optional port number). * Leave empty to apply default OPAL behavior (recommended) * @static * @constant */ NauthManager.OPAL.GATEWAY = null; NauthManager.OPAL.HOSTS = {}; /** * Interface for ordinary application * @static * @constant */ NauthManager.OPAL.HOSTS.OPEN = 'nauth.authorisation.onetapi.pl'; /** * Number of milliseconds, when permanent login expires * @static * @constant */ NauthManager.REMEMBER_ME_INTERVAL = 60 * 60 * 24 * 14 * 1000; // 14 days /** * {core.http.Loader} timeout [ms] * @static */ NauthManager.TIMEOUT = 900; exports.NauthManager = NauthManager;