UNPKG

ares-ide

Version:

A browser-based code editor and UI designer for Enyo 2 projects

384 lines (377 loc) 11.9 kB
/* global async, ares */ /** * @see https://www.dropbox.com/developers/reference/api * @see https://www.dropbox.com/developers/blog/20 */ enyo.kind({ name: "DropboxAuthConfig", kind: "FittableRows", // private members debug: false, requestTokenUrl: "https://api.dropbox.com/1/oauth/request_token", authorizeUrl: "https://www.dropbox.com/1/oauth/authorize", accessTokenUrl: "https://api.dropbox.com/1/oauth/access_token", accountInfoUrl: "/res/services/dropbox", requestToken: "", // from dropbox requestTokenSecret: "", // from dropbox uid: "", // from dropbox // public members published: { serviceId: "", // from ide.json serviceName: "", // from ide.json auth: {} }, // emitted events events: { onUpdateAuth: "", onError: "", onStartWaiting: "", onStopWaiting: "" }, // static UI elements components: [ {kind: "Ares.Groupbox", components: [ {kind: "onyx.GroupboxHeader", name: "serviceName"}, {components: [ {content: "User Name: ", kind: "Ares.GroupBoxItemKey"}, {name: "name", kind: "Ares.GroupBoxItemValue"} ]}, {components: [ {content: "Email: ", kind: "Ares.GroupBoxItemKey"}, {name: "email", kind: "Ares.GroupBoxItemValue"} ]}, {components: [ {content: "Country Code:", kind: "Ares.GroupBoxItemKey"}, {name: "countryCode", kind: "Ares.GroupBoxItemValue"} ]}, {components: [ {content: "Quota (Max):", kind: "Ares.GroupBoxItemKey"}, {name: "quota", kind: "Ares.GroupBoxItemValue"} ]}, {components: [ {content: "Usage (Private):", kind: "Ares.GroupBoxItemKey"}, {name: "privateBytes", kind: "Ares.GroupBoxItemValue"} ]}, {components: [ {content: "Usage (Shared):", kind: "Ares.GroupBoxItemKey"}, {name: "sharedBytes", kind: "Ares.GroupBoxItemValue"} ]} ]}, {kind: "FittableColumns", components: [ {name: "renewBtn", kind: "onyx.Button", content: "Renew", disabled: false, ontap: "renew"}, {name: "checkBtn", kind: "onyx.Button", content: "Check", disabled: true, ontap: "check"} ]}, {name: "footnote"} ], create: function() { ares.setupTraceLogger(this); this.inherited(arguments); this.trace("title:", this.title); this.$.serviceName.setContent(this.serviceName); this.auth = this.auth || {}; this.auth.headers = this.auth.headers || {}; if (this.auth.headers.authorization) { this.$.checkBtn.setDisabled(false); } this.waitAccountInfo(); }, render: function() { if (ares.isPopupAllowed()) { this.$.footnote.setContent(""); } else { this.$.footnote.setContent("You need to accept Dropbox popup when asked..."); } this.inherited(arguments); }, check: function() { var self = this; // Disable [Check] button during async processing... this.$.checkBtn.setDisabled(true); async.series([ this.waitAccountInfo.bind(this), this.authenticate.bind(this), this.getAccountInfo.bind(this), this.displayAccountInfo.bind(this) ], function(err, results) { enyo.log("DropboxAuthConfig.check: err:", err, "results:", results); self.doStopWaiting(); // ... and re-enable it after success of failure. self.$.checkBtn.setDisabled(false); if (err) { self.doError({ msg: "Unable to check Dropbox Account", details: err.toString() }); } }); }, renew: function() { var self = this; // Disable [Renew] button during async processing... this.$.renewBtn.setDisabled(true); async.series([ this.waitAccountInfo.bind(this), this.getRequestToken.bind(this), this.authorize.bind(this), this.getAccessToken.bind(this), this.saveAccessToken.bind(this), this.getAccountInfo.bind(this), this.displayAccountInfo.bind(this) ], function(err, results) { enyo.log("DropboxAuthConfig.renew: err:", err, "results:", results); self.doStopWaiting(); // ... and re-enable it after success of failure. self.$.renewBtn.setDisabled(false); if (err) { self.doError({ msg: "Unable to renew Dropbox Account tokens", details: err.toString() }); } }); }, //* @private getOauthTimestamp: function() { return Date.now() /1000 |0; }, //* @private getOauthNonce: function() { // 24-characters random string (same method as // multipart/form-data separator) var nonce = ""; for (var i = 0; i < 24; i++) { nonce += Math.floor(Math.random() * 10).toString(16); } return nonce; }, makeOAuthRequestTokenObject: function(appKey, appSecret) { var obj = { // http://oauth.net/core/1.0/#rfc.section.6.1.1 oauth_signature_method: "PLAINTEXT", oauth_consumer_key: appKey, oauth_signature: appSecret + '&' }; return obj; }, makeOAuthHeaderObject: function(appKey, appSecret, token, tokenSecret) { var obj = { // http://oauth.net/core/1.0/#rfc.section.6.1.1 oauth_signature_method: "PLAINTEXT", oauth_consumer_key: appKey, oauth_token: token, oauth_signature: appSecret + '&' + tokenSecret }; return obj; }, //* @private makeOAuthHeader: function(prefix, params) { var header = prefix + ' ', sep = ', '; header += 'oauth_version="1.0"'; enyo.forEach(enyo.keys(params), function(key) { header += sep + key + '="' + params[key] + '"'; }, this); this.log("header:", header); return header; }, //* @private getRequestToken: function(next) { if (!this.auth.appKey || !this.auth.appSecret) { this.warn("Ares IDE mis-configuration: missing Dropbox AppKey and/or AppSecret in ide.json. Get those values from https://www.dropbox.com/developers/apps"); next(new Error("Ares is not configured as a Dropbox application: Contact your system administrator")); return; } this.doStartWaiting({msg: "Dropbox: waiting for request token..."}); var reqOptions = { url: this.requestTokenUrl, method: 'POST', handleAs: 'text', cacheBust: false, headers: { 'cache-control': false, Authorization: this.makeOAuthHeader('OAuth', this.makeOAuthRequestTokenObject(this.auth.appKey, this.auth.appSecret)) } }; this.log("options:", reqOptions); var req = new enyo.Ajax(reqOptions); req.response(this, function(inSender, inValue) { this.log("response:", inValue); var form = ares.decodeWebForm(inValue); this.log("form:", form); this.requestTokenSecret = form.oauth_token_secret; this.requestToken = form.oauth_token; this.uid = form.uid; next(); }); req.error(this, function(inSender, inError) { this.log("response:", inError); next(new Error(inError)); }); req.go(); }, // XXX better offload this popup sequence to a dedicated enyo Object authorize: function(next) { this.doStartWaiting({msg: "Dropbox: waiting for user's authorization..."}); var url = this.authorizeUrl + '?oauth_token=' + encodeURIComponent(this.requestToken); var popup = window.open(url, "Dropbox Authentication", "resizeable=1,width=1024, height=600"); // Check 20s that the popup is closed... otherwise fails var self = this, delay = 20, timer = setInterval(function() { if(popup.closed) { clearInterval(timer); enyo.log("DropboxAuthConfig.waitAuthorization: Dropbox popup closed"); next(); } else if (!--delay) { clearInterval(timer); enyo.log("DropboxAuthConfig.waitAuthorization: failure: User did not accept or close the authorization window"); popup.close(); next(new Error("Dropbbox Authorization window did not close")); } else { self.doStartWaiting({msg: "Dropbox: waiting for the accessToken (" + delay + "s left)..."}); } }, 1000); }, getAccessToken: function(next) { this.doStartWaiting({msg: "Dropbox: waiting for the accessToken..."}); var reqOptions = { url: this.accessTokenUrl, method: 'POST', handleAs: 'text', cacheBust: false, headers: { 'cache-control': false, Authorization: this.makeOAuthHeader('OAuth', this.makeOAuthHeaderObject(this.auth.appKey, this.auth.appSecret, this.requestToken, this.requestTokenSecret)) } }; this.log("options:", reqOptions); var req = new enyo.Ajax(reqOptions); req.response(this, function(inSender, inValue) { this.log("response:", inValue); var form = ares.decodeWebForm(inValue); this.log("form:", form); this.auth.accessTokenSecret = form.oauth_token_secret; this.auth.accessToken = form.oauth_token; this.auth.uid = form.uid; this.auth.headers.authorization = this.makeOAuthHeader('OAuth', this.makeOAuthHeaderObject(this.auth.appKey, this.auth.appSecret, this.auth.accessToken, this.auth.accessTokenSecret)); next(); }); req.error(this, function(inSender, inError) { this.log("response:", inError); next(new Error(inError)); }); req.go(); }, saveAccessToken: function(next) { var event = { serviceId: this.serviceId, auth: { accessToken: this.auth.accessToken, accessTokenSecret: this.auth.accessTokenSecret, uid: this.auth.uid, headers: ares.clone(this.auth.headers) } }; this.log("event:", event); this.doUpdateAuth(event); delete this.auth.requestToken; delete this.auth.requestTokenSecret; this.$.checkBtn.setDisabled(false); next(); }, /** * Server round-trip to record the applications credentials as * a server cookie. This is the only step where the * application key is exchanged in clear text between the * browser client & the ARES server. */ authenticate: function(next) { var reqOptions = { url: this.accountInfoUrl, method: 'POST', handleAs: 'json' }; this.log("options:", reqOptions); var req = new enyo.Ajax(reqOptions); req.response(this, function(inSender, inValue) { this.log("response:", inValue); next(); }); req.error(this, function(inSender, inError) { var errMsg; try { errMsg = enyo.json.parse(inSender.xhrResponse.body).error; } catch(e) { errMsg = inError; } this.log("errMsg:", errMsg); next(new Error(errMsg)); }); req.go({auth: enyo.json.stringify({ appKey: this.auth.appKey, appSecret: this.auth.appSecret, uid: this.auth.uid, accessToken: this.auth.accessToken, accessTokenSecret: this.auth.accessTokenSecret })}); }, getAccountInfo: function(next) { this.doStartWaiting({msg: "Dropbox: waiting for user's account information..."}); var reqOptions = { url: this.accountInfoUrl, method: 'GET', handleAs: 'json' }; this.log("options:", reqOptions); var req = new enyo.Ajax(reqOptions); req.response(this, function(inSender, inValue) { this.log("response:", inValue); this.accountInfo = inValue; next(); }); req.error(this, function(inSender, inError) { var errMsg; try { errMsg = enyo.json.parse(inSender.xhrResponse.body).error; } catch(e) { errMsg = inError; } this.log("errMsg:", errMsg); next(new Error(errMsg)); }); req.go({auth: enyo.json.stringify({ appKey: this.auth.appKey, appSecret: this.auth.appSecret, uid: this.auth.uid, accessToken: this.auth.accessToken, accessTokenSecret: this.auth.accessTokenSecret })}); }, waitAccountInfo: function(next) { this.$.name.setContent("..."); this.$.email.setContent("..."); this.$.countryCode.setContent("..."); this.$.quota.setContent("..."); this.$.privateBytes.setContent("..."); this.$.sharedBytes.setContent("..."); if (next) { next(); } }, displayAccountInfo: function(next) { this.log("accountInfo:", this.accountInfo); this.$.name.setContent(this.accountInfo.name); this.$.email.setContent(this.accountInfo.email); this.$.countryCode.setContent(this.accountInfo.countryCode); var quota = Math.floor(this.accountInfo.quota / (1024*1024)) + " MB"; this.$.quota.setContent(quota); var privateBytes = Math.floor(this.accountInfo.privateBytes / (1024*1024)) + " MB"; this.$.privateBytes.setContent(privateBytes); var sharedBytes = Math.floor(this.accountInfo.sharedBytes / (1024*1024)) + " MB"; this.$.sharedBytes.setContent(sharedBytes); if (next) { next(); } } });