keeweb/app/scripts/storage/storage-base.js

221 lines
7.9 KiB
JavaScript
Raw Normal View History

2017-01-31 07:50:28 +01:00
const Backbone = require('backbone');
const Logger = require('../util/logger');
const AppSettingsModel = require('../models/app-settings-model');
const RuntimeDataModel = require('../models/runtime-data-model');
const Links = require('../const/links');
const FeatureDetector = require('../util/feature-detector');
2016-03-27 09:06:23 +02:00
2017-01-31 07:50:28 +01:00
const MaxRequestRetries = 3;
2016-03-27 15:18:05 +02:00
2017-01-31 07:50:28 +01:00
const StorageBase = function() {
2016-03-27 09:06:23 +02:00
};
_.extend(StorageBase.prototype, {
name: null,
icon: null,
iconSvg: null,
enabled: false,
system: false,
uipos: null,
logger: null,
appSettings: AppSettingsModel.instance,
2016-03-27 14:57:22 +02:00
runtimeData: RuntimeDataModel.instance,
2016-03-27 09:06:23 +02:00
init: function() {
if (!this.name) {
throw 'Failed to init provider: no name';
}
if (!this.system) {
2017-01-31 07:50:28 +01:00
const enabled = this.appSettings.get(this.name);
2016-03-27 09:06:23 +02:00
if (typeof enabled === 'boolean') {
this.enabled = enabled;
}
}
this.logger = new Logger('storage-' + this.name);
if (this._oauthReturnMessage) {
this.logger.debug('OAuth return message', this._oauthReturnMessage);
this._oauthProcessReturn(this._oauthReturnMessage, _.noop);
delete this._oauthReturnMessage;
delete sessionStorage.authStorage;
}
2016-03-27 09:46:43 +02:00
return this;
2016-03-27 09:42:48 +02:00
},
setEnabled: function(enabled) {
this.enabled = enabled;
},
handleOAuthReturnMessage(message) {
this._oauthReturnMessage = message;
},
2016-03-27 09:42:48 +02:00
_xhr: function(config) {
2017-01-31 07:50:28 +01:00
const xhr = new XMLHttpRequest();
2016-03-27 09:42:48 +02:00
if (config.responseType) {
xhr.responseType = config.responseType;
}
2017-01-31 07:50:28 +01:00
const statuses = config.statuses || [200];
2016-07-17 13:30:38 +02:00
xhr.addEventListener('load', () => {
2016-03-27 15:18:05 +02:00
if (statuses.indexOf(xhr.status) >= 0) {
return config.success && config.success(xhr.response, xhr);
}
2016-07-17 13:30:38 +02:00
if (xhr.status === 401 && this._oauthToken) {
this._oauthRefreshToken(err => {
2016-03-27 15:18:05 +02:00
if (err) {
return config.error && config.error('unauthorized', xhr);
} else {
config.tryNum = (config.tryNum || 0) + 1;
if (config.tryNum >= MaxRequestRetries) {
2016-07-17 13:30:38 +02:00
this.logger.info('Too many authorize attempts, fail request', config.url);
2016-03-27 15:18:05 +02:00
return config.error && config.error('unauthorized', xhr);
}
2016-07-17 13:30:38 +02:00
this.logger.info('Repeat request, try #' + config.tryNum, config.url);
this._xhr(config);
2016-03-27 15:18:05 +02:00
}
});
} else {
2016-03-27 09:42:48 +02:00
return config.error && config.error('http status ' + xhr.status, xhr);
}
});
2016-07-17 13:30:38 +02:00
xhr.addEventListener('error', () => {
2016-03-27 15:18:05 +02:00
return config.error && config.error('network error', xhr);
2016-03-27 09:42:48 +02:00
});
2016-07-17 13:30:38 +02:00
xhr.addEventListener('timeout', () => {
2016-03-27 15:18:05 +02:00
return config.error && config.error('timeout', xhr);
2016-03-27 09:42:48 +02:00
});
xhr.open(config.method || 'GET', config.url);
2016-03-27 14:57:22 +02:00
if (this._oauthToken) {
2017-04-16 17:00:35 +02:00
xhr.setRequestHeader('Authorization', 'Bearer ' + this._oauthToken.accessToken);
2016-03-27 14:57:22 +02:00
}
2016-07-17 13:30:38 +02:00
_.forEach(config.headers, (value, key) => {
2016-03-27 09:42:48 +02:00
xhr.setRequestHeader(key, value);
});
xhr.send(config.data);
2016-03-27 13:54:35 +02:00
},
_openPopup: function(url, title, width, height) {
2017-01-31 07:50:28 +01:00
const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left;
const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top;
2016-03-27 13:54:35 +02:00
2017-01-31 07:50:28 +01:00
const winWidth = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
const winHeight = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height;
2016-03-27 13:54:35 +02:00
2017-01-31 07:50:28 +01:00
const left = ((winWidth / 2) - (width / 2)) + dualScreenLeft;
const top = ((winHeight / 2) - (height / 2)) + dualScreenTop;
2016-03-27 13:54:35 +02:00
2017-01-31 07:50:28 +01:00
let settings = {
2016-03-27 13:54:35 +02:00
width: width,
height: height,
left: left,
top: top,
dialog: 'yes',
dependent: 'yes',
scrollbars: 'yes',
location: 'yes'
};
2016-07-17 13:30:38 +02:00
settings = Object.keys(settings).map(key => key + '=' + settings[key]).join(',');
if (FeatureDetector.isStandalone) {
sessionStorage.authStorage = this.name;
}
2016-03-27 13:54:35 +02:00
return window.open(url, title, settings);
2016-03-27 14:57:22 +02:00
},
2016-04-03 16:33:34 +02:00
_getOauthRedirectUrl: function() {
2017-01-31 07:50:28 +01:00
let redirectUrl = window.location.href;
2016-04-03 16:33:34 +02:00
if (redirectUrl.lastIndexOf('file:', 0) === 0) {
redirectUrl = Links.WebApp;
}
return redirectUrl;
},
2016-03-27 15:18:05 +02:00
_oauthAuthorize: function(callback) {
2016-07-17 13:30:38 +02:00
if (this._oauthToken && !this._oauthToken.expired) {
2016-03-27 15:18:05 +02:00
return callback();
}
2017-01-31 07:50:28 +01:00
const opts = this._getOAuthConfig();
const oldToken = this.runtimeData.get(this.name + 'OAuthToken');
2016-03-27 15:18:05 +02:00
if (oldToken && !oldToken.expired) {
2016-07-17 13:30:38 +02:00
this._oauthToken = oldToken;
2016-03-27 15:18:05 +02:00
callback();
2016-03-27 14:57:22 +02:00
return;
}
2017-04-16 17:24:58 +02:00
const url = opts.url + '?client_id={cid}&scope={scope}&response_type=token&redirect_uri={url}'
2016-04-03 16:33:34 +02:00
.replace('{cid}', encodeURIComponent(opts.clientId))
2016-03-27 16:47:29 +02:00
.replace('{scope}', encodeURIComponent(opts.scope))
2016-04-03 16:33:34 +02:00
.replace('{url}', encodeURIComponent(this._getOauthRedirectUrl()));
2016-07-17 13:30:38 +02:00
this.logger.debug('OAuth popup opened');
if (!this._openPopup(url, 'OAuth', opts.width, opts.height)) {
2016-03-27 15:36:07 +02:00
callback('cannot open popup');
}
2017-01-31 07:50:28 +01:00
const popupClosed = () => {
2016-03-27 14:57:22 +02:00
Backbone.off('popup-closed', popupClosed);
window.removeEventListener('message', windowMessage);
2016-07-17 13:30:38 +02:00
this.logger.error('OAuth error', 'popup closed');
2016-03-27 15:18:05 +02:00
callback('popup closed');
2016-03-27 14:57:22 +02:00
};
2017-01-31 07:50:28 +01:00
const windowMessage = e => {
2016-03-27 14:57:22 +02:00
if (!e.data) {
return;
}
Backbone.off('popup-closed', popupClosed);
window.removeEventListener('message', windowMessage);
this._oauthProcessReturn(e.data, callback);
2016-03-27 14:57:22 +02:00
};
Backbone.on('popup-closed', popupClosed);
window.addEventListener('message', windowMessage);
},
_oauthProcessReturn: function(message, callback) {
const token = this._oauthMsgToToken(message);
if (token.error) {
this.logger.error('OAuth error', token.error, token.errorDescription);
callback(token.error);
} else {
this._oauthToken = token;
this.runtimeData.set(this.name + 'OAuthToken', token);
this.logger.debug('OAuth token received');
callback();
}
},
2016-03-27 14:57:22 +02:00
_oauthMsgToToken: function(data) {
if (data.error || !data.token_type) {
return { error: data.error || 'no token', errorDescription: data.error_description };
}
return {
tokenType: data.token_type,
accessToken: data.access_token,
authenticationToken: data.authentication_token,
expiresIn: data.expires_in,
scope: data.scope,
userId: data.user_id
};
2016-03-27 15:18:05 +02:00
},
_oauthRefreshToken: function(callback) {
this._oauthToken.expired = true;
this.runtimeData.set(this.name + 'OAuthToken', this._oauthToken);
this._oauthAuthorize(callback);
},
_oauthRevokeToken: function(url) {
2017-01-31 07:50:28 +01:00
const token = this.runtimeData.get(this.name + 'OAuthToken');
if (token) {
2017-04-16 17:00:35 +02:00
if (url) {
this._xhr({
url: url.replace('{token}', token.accessToken),
statuses: [200, 401]
});
}
this.runtimeData.unset(this.name + 'OAuthToken');
this._oauthToken = null;
}
2016-03-27 09:06:23 +02:00
}
});
StorageBase.extend = Backbone.Model.extend;
module.exports = StorageBase;