dl
Version:
DreamLab Libs
448 lines (393 loc) • 13.3 kB
JavaScript
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;