commit b308e5d6b879206608f9cee444b84ac539742e33 Author: Girish Ramakrishnan Date: Sat Jun 13 21:33:42 2015 -0700 Initial version diff --git a/appstore.js b/appstore.js new file mode 100644 index 0000000..27dc2ab --- /dev/null +++ b/appstore.js @@ -0,0 +1,80 @@ +'use strict'; + +var debug = require('debug')('e2e:appstore'), + request = require('superagent-sync'), + sleep = require('sleep').sleep, + +exports = module.exports = AppStore; + +function AppStore(origin) { + this._origin = origin; + + // credentials for api calls + this._credentials = { + password: null, + accessToken: null + }; +} + +function verifyResponse(res, errorMessage) { + if (res.statusCode < 200 || res.statusCode > 299) { + debug('Response error statusCode:%s error:%s body:%s', res.statusCode, res.error, res.body); + debug(errorMessage.red); + throw new Error(errorMessage); + } +} + +AppStore.prototype.getAccessToken = function (user) { + var res = request.get(this._origin + '/api/v1/login').auth(user.email, user.password).end(); + verifyResponse(res, 'Could not login'); + return res.body.accessToken; +}; + +AppStore.prototype.setCredentials = function (password, accessToken) { + this._credentials = { password: password, accessToken: accessToken }; +}; + +AppStore.prototype.createCloudron = function (box) { + var accessToken = this._credentials.accessToken; + var res = request.post(this._origin + '/api/v1/cloudrons').send(box).query({ accessToken: accessToken }).end(); + verifyResponse(res, 'Could not create cloudron'); + var boxId = res.body.box.id; + debug('Cloudron %s created'.green, box.name); + + ////////// wait for cloudron to come up + var creationTime = new Date(); + process.stdout.write('Waiting for cloudron to come up.'); + + while (true) { + sleep(10); + process.stdout.write('.'); + res = request.get(this._origin + '/api/v1/cloudrons/' + boxId).query({ accessToken: accessToken }).end(); + verifyResponse(res, 'Could not query cloudron status'); + + if (res.body.box.status === 'ready') { + debug(); + break; + } + } + + debug('Box created in %s minutes'.green, (new Date() - creationTime) / 60000); + + return res.body.box; +}; + +AppStore.prototype.deleteCloudron = function (box) { + debug('Deleting cloudron'); + var res = request.post(this._origin + '/api/v1/cloudrons/' + box.id) + .query({ accessToken: this._credentials.accessToken }) + .set('X-HTTP-Method-Override', 'DELETE') + .send({ password: this._credentials.password }) + .end(); + verifyResponse(res, 'Could not delete cloudron'); +}; + +AppStore.prototype.getManifest = function (appId, version) { + var res = request.get(this._origin + '/api/v1/apps/' + appId + '/versions/' + version).end(); + verifyResponse(res, 'Could not get get app manifest'); + return res.body.manifest; +}; + diff --git a/cloudron.js b/cloudron.js new file mode 100644 index 0000000..29761a6 --- /dev/null +++ b/cloudron.js @@ -0,0 +1,176 @@ +#!/usr/bin/env node + +'use strict'; + +var assert = require('assert'), + debug = require('debug')('e2e:cloudron'), + querystring = require('querystring'), + request = require('superagent-sync'), + sleep = require('sleep').sleep, + url = require('url'); + +exports = module.exports = Cloudron; + +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + +function Cloudron(box) { + this._box = box; + this._origin = 'https://my-' + box.domain; + + this._credentials = { + password: null, + accessToken: null + }; +} + +function verifyResponse(res, errorMessage) { + if (res.statusCode < 200 || res.statusCode > 299) { + debug('Response error statusCode:%s error:%s body:%s', res.statusCode, res.error, res.body); + debug(errorMessage.red); + throw new Error(errorMessage); + } +} + +// get oauth token for logged in as certain user { username, password, email } +Cloudron.prototype.getOauthToken = function (user) { + var username = user.username; + var password = user.password; + + ////////// try to authorize without a session + var res = request.get(this._origin + '/api/v1/oauth/dialog/authorize').query({ redirect_uri: 'https://self', client_id: 'cid-webadmin', response_type: 'token', scope: 'root,profile,apps,roleAdmin' }).end(); + var sessionCookies = res.headers['set-cookie']; // always an array + + ///////// should get redirected to login form with a script tag (to workaround chrome issue with redirects+cookies) + var urlp = url.parse(res.text.match(/window.location.href = "(.*)"/)[1]); + + ////////// get the login form (api/v1/session/login) + res = request.get(this._origin + urlp.pathname).set('cookie', sessionCookies[0]).query(urlp.query).end(); + var csrf = res.text.match(/name="_csrf" value="(.*)"/)[1]; + sessionCookies = res.headers['set-cookie']; // always an array + + ////////// submit the login form with credentials + res = request.post(this._origin + urlp.pathname).set('cookie', sessionCookies[0]).send({ _csrf: csrf, username: username, password: password }).redirects(0).end(); + if (res.statusCode !== 302) return null; + sessionCookies = res.headers['set-cookie']; // always an array + + ////////// authorize now with cookies + res = request.get(this._origin + '/api/v1/oauth/dialog/authorize').set('cookie', sessionCookies[0]).query({ redirect_uri: 'https://self', client_id: 'cid-webadmin', response_type: 'token', scope: 'root,profile,apps,roleAdmin' }).redirects(0).end(); + if (res.statusCode !== 302) return null; + sessionCookies = res.headers['set-cookie']; // always an array + + ////////// success will get redirect to callback?redirectURI=xx#access_token=yy&token_type=Bearer' (content is a