xtool/contrib/mORMot/SQLite3/Samples/ThirdPartyDemos/DigDiver/AngularJS/js/syn-auth.js

492 lines
12 KiB
JavaScript

angular.module('syn-auth', ['em-utils'], function($provide) {
// the number of login retries on 403 before logout
var MAX_RECONNECT_COUNT = 5;
$provide.factory('SynAuth', function(crc32, sha256) {
function SynAuth(host, port) {
var defaults = {
host: '',
defaultPort: '888',
User : "",
fRoot : "",
fSessionID : 0,
fSessionIDHexa8 : "",
fSessionPrivateKey : 0,
fSessionTickCountOffset : 0,
fLastSessionTickCount : 0,
PasswordHashHexa : "",
fServerTimeStampOffset : 0,
fcallBack : null,
ffailCallBack : null
}; // SynAuth namespace
this.connectionReady = false;
this.readyCallbacks = [];
for ( var p in defaults ) {
this[p] = defaults[p];
}
if ( host ) {
this.setHost(host, port);
}
}
var sp = SynAuth.prototype;
sp.ready = function(cb) {
if ( this.connectionReady ) {
cb(this);
} else {
this.readyCallbacks.push(cb);
}
};
sp.fireReadyCbs = function() {
var _this = this;
angular.forEach(this.readyCallbacks, function(cb){
cb(_this);
});
};
sp.wrap = function(method){
var that = this;
return function(){
method.apply(that, arguments);
};
};
sp.setHost = function(host, port) {
if ( !host.match(/\:\d+$/) ) {
host += ':'+(port || this.defaultPort);
}
this.host = host;
};
sp.LogIn = function (root, username, password, callback, failCallback){
this.fRoot = root;
this.User = username;
this.PasswordHashHexa = sha256.hash("salt"+password);
if (callback) {this.fcallBack = callback;}
if (failCallback) {this.ffailCallback = failCallback;}
$.get(this.host+"/"+root+"/TimeStamp", this.wrap(this.gotTimeStamp));
}
sp.LogInAgain = function(callback){ //after timeout error for silent re-login
this.fSessionID = 0;
this.fSessionIDHexa8 = "";
this.fSessionPrivateKey = 0;
if (callback) {this.fcallBack = callback;} else {this.fcallBack = null;}
$.get(this.host+"/"+this.fRoot+"/TimeStamp", this.wrap(this.gotTimeStamp));
}
sp.gotTimeStamp = function (timestamp) {
var s = '', d = new Date(), clientTime = '';
timestamp = parseInt(timestamp, 10);
s = d.getFullYear().toString(2);
while(s.length < 13) { s = '0'+s;}
clientTime = s;
s = d.getMonth().toString(2);
while(s.length < 4) { s = '0'+s;}
clientTime = clientTime +s;
s = (d.getDate()-1).toString(2);
while(s.length < 5) { s = '0'+s;}
clientTime = clientTime +s;
s = d.getHours().toString(2);
while(s.length < 5) { s = '0'+s;}
clientTime = clientTime +s;
s = d.getMinutes().toString(2);
while(s.length < 6) { s = '0'+s;}
clientTime = clientTime +s;
s = d.getSeconds().toString(2);
while(s.length < 6) { s = '0'+s;}
clientTime = clientTime +s;
this.fServerTimeStampOffset = (timestamp - Math.floor(d.getTime()/10));
$.get(this.host+"/"+this.fRoot+"/auth?UserName="+this.User, this.wrap(this.gotNonce));
}
sp.gotNonce = function (aNonce){
var that = this;
//create client nonce
var aClientNonce = "", s = "", d = new Date();
aClientNonce = d.getFullYear().toString();
s = d.getMonth().toString();
if (s.length === 1) { s = '0'+s;}
aClientNonce = aClientNonce + '-' + s;
s = d.getDate().toString();
if (s.length === 1) { s = '0'+s;}
aClientNonce = aClientNonce + '-' + s + ' ';
s = d.getHours().toString();
if (s.length === 1) { s = '0'+s;}
aClientNonce = aClientNonce + s;
s = d.getMinutes().toString();
if (s.length === 1) { s = '0'+s;}
aClientNonce = aClientNonce + ':' + s;
s = d.getSeconds().toString();
if (s.length === 1) { s = '0'+s;}
aClientNonce = aClientNonce + ':' + s;
aClientNonce = sha256.hash(aClientNonce);
s = this.host+"/"+ this.fRoot+"/auth?UserName="+this.User+"&Password=" +
sha256.hash(this.fRoot+aNonce.result+aClientNonce+this.User+this.PasswordHashHexa )+
"&ClientNonce="+aClientNonce;
$.ajax({
type: "GET",
dataType: "json",
url: s,
success: function(){ that.gotSession.apply(that, arguments); },
error: function(){ that.ffailCallback.apply(that, arguments); }
});
};
sp.gotSession = function (aSessionKey){
var sessArr = aSessionKey.result.split('+');
this.fSessionID = parseInt(sessArr[0], 10);
this.fSessionIDHexa8 = this.fSessionID.toString(16);
while ( this.fSessionIDHexa8.length < 8 ) { this.fSessionIDHexa8 = '0'+this.fSessionIDHexa8; }
this.fSessionPrivateKey = crc32(this.PasswordHashHexa, crc32(aSessionKey.result, 0));
if (this.fcallBack != null) {
this.connectionReady = true;
this.fireReadyCbs();
this.fcallBack();
}
}
sp.SessionSign = function (url) {
var Tix, Nonce, s, ss;
Tix = Date.now(); // # of ms since Epoch
if ( Tix <= this.fLastSessionTickCount ) {
this.fLastSessionTickCount += 1;
} else {
this.fLastSessionTickCount = Tix;
}
Nonce = Tix.toString(16);
while ( Nonce.length < 8 ) { Nonce = '0'+Nonce; }
if ( Nonce.length > 8 ) { Nonce = Nonce.slice(Nonce.length-8) }
ss = crc32(url, crc32(Nonce, this.fSessionPrivateKey)).toString(16);
while ( ss.length < 8 ) { ss = '0'+ss; }
s = url.indexOf("?") == -1 ? url+'?session_signature=' : url+'&session_signature=';
return s + this.fSessionIDHexa8 + Nonce + ss;
}
sp.getURL = function(uri) {
return this.host+'/'+this.SessionSign(uri);
}
sp.Logout = function (callback) {
if (this.fSessionID == 0) {if (callback){callback();}} else {
$.get(this.host+"/"+this.fRoot+"/auth?UserName="+this.User+"&Session="+this.fSessionID, callback);
this.fRoot = '';
this.User = '';
this.fSessionID = 0;
this.fSessionIDHexa8 = "";
this.fSessionPrivateKey = 0;
}
}
return SynAuth;
});
// wps
// wp/%/settings
// wp/%/group
// wp/%/bounce
// infinitypropertycomau
// garethinfinitypropertycomau
// bernieinfinitypropertycomau
var forEach = angular.forEach;
function isArray(value) {
return Object.prototype.toString.apply(value) == '[object Array]';
}
function isObject(value){return value != null && typeof value == 'object';}
function sortedKeys(obj) {
var keys = [];
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
keys.push(key);
}
}
return keys.sort();
}
function forEachSorted(obj, iterator, context) {
var keys = sortedKeys(obj);
for ( var i = 0; i < keys.length; i++) {
iterator.call(context, obj[keys[i]], keys[i]);
}
return keys;
}
function encodeUriQuery(val, pctEncodeSpaces) {
return encodeURIComponent(val).
replace(/%40/gi, '@').
replace(/%3A/gi, ':').
replace(/%24/g, '$').
replace(/%2C/gi, ',').
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
}
function buildQueryString(params) {
var parts = [];
forEachSorted(params, function(value, key) {
if (value == null || value == undefined) return;
if (!isArray(value)) value = [value];
forEach(value, function(v) {
if (isObject(v)) {
v = toJson(v);
}
parts.push(encodeUriQuery(key) + '=' +
encodeUriQuery(v));
});
});
return parts.join('&');
}
function buildUrl(url, params) {
if (!params) return url;
var qs = buildQueryString(params);
return url + ((url.indexOf('?') == -1) ? '?' : '&') + qs;
}
$provide.factory('synConn', function(SynAuth, emAuth, $http, $q, emsLoadingBar) {
var synConn = {
opts: {
host: emAuth.get('host'),
port: emAuth.get('port'),
email: emAuth.get('email'),
password: emAuth.get('password'),
secure: emAuth.get('secure')
}
};
var roots = [
'wps',
'wp/\\d+/settings',
'wp/\\d+/group',
'wp/\\d+/bounce'
];
var connCache = {};
function setOpts(o) {
synConn.opts = o;
if ( !o.host ) { throw new Exception('[SynConn] setOpts. "host" option is not defined.'); }
if ( !o.email ) { throw new Exception('[SynConn] setOpts. "email" option is not defined.'); }
if ( !o.password ) { throw new Exception('[SynConn] setOpts. "password" option is not defined.'); }
o.port = o.port || '888';
}
function supplant (str, o) {
return str.replace(
/\{([^{}]*)\}/g,
function (a, b) {
var r = o[b];
return typeof r === 'string' || typeof r === 'number' ? r : a;
}
);
}
function getBaseURL() {
var pr = synConn.opts.secure ? 'https' : 'http';
return pr+'://'+synConn.opts.host+':'+synConn.opts.port+'/';
}
function createNewSynAuth(root, done) {
var pr = synConn.opts.secure ? 'https' : 'http';
//return { SynAuth: 'dummy'+(new Date()).getTime() };
var sa = new SynAuth(pr+'://'+synConn.opts.host, synConn.opts.port);
sa.LogIn(root, synConn.opts.email, synConn.opts.password, function(){
done(null, sa);
}, function(xhr, status, msg){
// failback
var err = new Error(msg);
err.status = xhr.status;
done(err);
});
return sa;
}
function getConnectionFromURi(uri, done) {
var found = false;
// looking for cached connection
for ( var root in connCache ) {
if ( uri.indexOf(root) === 0 ) {
return connCache[root].ready(function(conn){
done(null, conn);
});
}
}
// creating new connection
for (var r, res, i = 0; i < roots.length; i++) {
if ( res = (new RegExp(roots[i],'i')).exec(uri) ) {
r = res[0]; // matched root uri, like wp/1/settings
found = true;
connCache[r] = createNewSynAuth(r, function(err, sa){
if ( err ) {
delete connCache[r];
return done(err);
}
return done(null, sa);
});
}
}
if ( !found ) {
done(new Error('getConnectionFromURi: Cannot get rootURi for '+uri));
}
}
function removeCachedConnection(sa) {
var found = false;
// looking for cached connection
for ( var root in connCache ) {
if ( connCache[root] === sa) {
delete connCache[root];
break;
}
}
}
function http(o) {
var that = {
sa: null,
err: null,
errorCb: null,
successCb: null
};
if ( !o.headers ) {
o.headers = {};
}
var reconnect = 0;
var wp = emAuth.get('workplace');
if ( wp ) {
synConn.opts.wpId = emAuth.get('workplace').ID;
}
o.uri = supplant(o.uri, synConn.opts);
function connect() {
getConnectionFromURi(o.uri, function(err, sa){
if ( err ) { that.err = err; }
that.sa = sa;
setTimeout(function() {
if ( err ) { return that.errorCb(err); }
runRequest();
}, 1);
});
}
connect();
function runRequest() {
var url;
if ( o.params ) {
url = that.sa.getURL( buildUrl(o.uri, o.params) );
delete o.params;
} else {
url = that.sa.getURL(o.uri);
}
if ( o.data ) {
if ( o.headers['Content-Type'] === 'application/x-www-form-urlencoded' ) {
o.data = buildQueryString(o.data);
}
}
var opts = angular.extend({ url : url }, o);
delete opts.uri;
opts.method = opts.method || 'GET';
$http(opts).success(function(){
that.successCb.apply(this, arguments);
emsLoadingBar.stop();
reconnect = 0;
}).error(function(data){
var err = new Error(data.ErrorText);
err.ErrorCode = data.ErrorCode;
if ( err && err.ErrorCode == 403 ) {
if ( reconnect == MAX_RECONNECT_COUNT ) {
alert('Authentication failure. You will be logged out now. Shall this error repeat, contact your server administrator.');
window.location.hash = '#/logout';
return;
}
removeCachedConnection(that.sa);
reconnect += 1;
connect();
} else {
var args = Array.prototype.slice.call(arguments);
args.unshift(err);
that.errorCb.apply(this, args);
emsLoadingBar.stop();
}
});
emsLoadingBar.start();
}
return {
success: function(cb){
that.successCb = cb;
return this;
},
error: function(cb){
that.errorCb = cb;
return this;
}
};
}
function touch(uri, done) {
getConnectionFromURi(uri, done);
}
synConn.logout = function() {
connCache = {};
};
//getConnectionFromURi('wp/1/settings/GetTemplateMessage?ID=');
synConn.setOpts = setOpts;
synConn.http = http;
synConn.touch = touch;
synConn.getBaseURL = getBaseURL;
return synConn;
});
});