cloudron 4.12.2 → 4.12.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.jshintrc CHANGED
@@ -3,5 +3,6 @@
3
3
  "browser": true,
4
4
  "unused": true,
5
5
  "globalstrict": true,
6
+ "esversion": 8,
6
7
  "predef": [ "angular", "$" ]
7
8
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudron",
3
- "version": "4.12.2",
3
+ "version": "4.12.6",
4
4
  "license": "MIT",
5
5
  "description": "Cloudron Commandline Tool",
6
6
  "main": "main.js",
@@ -17,23 +17,23 @@
17
17
  },
18
18
  "author": "Cloudron Developers <support@cloudron.io>",
19
19
  "dependencies": {
20
- "async": "^3.2.0",
20
+ "async": "^3.2.1",
21
21
  "cloudron-manifestformat": "^5.10.2",
22
22
  "colors": "^1.4.0",
23
23
  "commander": "^6.1.0",
24
- "debug": "^4.3.1",
24
+ "debug": "^4.3.2",
25
25
  "easy-table": "^1.1.1",
26
26
  "ejs": "^3.1.6",
27
27
  "eventsource": "^1.1.0",
28
28
  "micromatch": "^4.0.4",
29
29
  "mkdirp": "^1.0.4",
30
30
  "once": "^1.4.0",
31
- "open": "^8.2.0",
31
+ "open": "^8.2.1",
32
32
  "progress": "^2.0.3",
33
33
  "progress-stream": "^2.0.0",
34
34
  "readline-sync": "^1.4.10",
35
35
  "request": "^2.88.2",
36
- "safetydance": "^2.0.1",
36
+ "safetydance": "^2.2.0",
37
37
  "split": "^1.0.1",
38
38
  "superagent": "^6.1.0",
39
39
  "superagent-sync": "^0.2.1",
@@ -42,12 +42,12 @@
42
42
  "underscore": "^1.13.1"
43
43
  },
44
44
  "engines": {
45
- "node": ">= 4.x.x"
45
+ "node": ">= 14.x.x"
46
46
  },
47
47
  "devDependencies": {
48
48
  "expect.js": "^0.3.1",
49
49
  "memorystream": "^0.3.1",
50
- "mocha": "^9.0.0",
50
+ "mocha": "^9.1.1",
51
51
  "rimraf": "^3.0.2"
52
52
  }
53
53
  }
package/src/actions.js CHANGED
@@ -20,6 +20,7 @@ const assert = require('assert'),
20
20
  safe = require('safetydance'),
21
21
  spawn = require('child_process').spawn,
22
22
  split = require('split'),
23
+ superagent = require('superagent'),
23
24
  Table = require('easy-table'),
24
25
  tar = require('tar-fs'),
25
26
  util = require('util'),
@@ -81,6 +82,8 @@ function requestOptions(options) {
81
82
 
82
83
  // error for the request module
83
84
  function requestError(response) {
85
+ if (response.statusCode === 401) return 'Invalid token. Use cloudron login again.';
86
+
84
87
  return `${response.statusCode} message: ${response.body.message || response.body}`; // body is sometimes just a string like in 401
85
88
  }
86
89
 
@@ -246,81 +249,73 @@ function startApp(app, options, callback) {
246
249
  });
247
250
  }
248
251
 
249
- function login(adminFqdn, options) {
252
+ async function authenticate(adminFqdn, username, password, options) {
253
+ let totpToken;
254
+
255
+ const { rejectUnauthorized, askForTotpToken } = options;
256
+ if (askForTotpToken) totpToken = readlineSync.question('2FA Token: ', {});
257
+
258
+ const request = superagent.post(`https://${adminFqdn}/api/v1/cloudron/login`)
259
+ .timeout(10000)
260
+ .send({ username, password, totpToken })
261
+ .ok(() => true)
262
+ .set('User-Agent', 'cloudron-cli');
263
+ if (!rejectUnauthorized) request.disableTLSCerts();
264
+ const response = await request; // this triggers the request
265
+ if (response.status === 401) {
266
+ if (response.body.message === 'A totpToken must be provided') {
267
+ return await authenticate(username, password, { rejectUnauthorized, askForTotpToken: true });
268
+ } else if (response.body.message === 'Invalid totpToken') {
269
+ console.log('Invalid 2FA Token'.red);
270
+ return await authenticate(username, password, { rejectUnauthorized, askForTotpToken: true });
271
+ } else {
272
+ throw new Error('Invalid credentials');
273
+ }
274
+ }
275
+
276
+ if (response.statusCode !== 200) throw new Error(`Login failed: Status code: ${requestError(response)}`);
277
+
278
+ return response.body.accessToken;
279
+ }
280
+
281
+ async function login(adminFqdn, options) {
250
282
  if (!adminFqdn) adminFqdn = readlineSync.question('Cloudron Domain (e.g. my.example.com): ', {});
251
283
  if (!adminFqdn) return exit('');
252
284
 
253
285
  if (adminFqdn.indexOf('https://') === 0) adminFqdn = adminFqdn.slice('https://'.length);
254
286
  if (adminFqdn.indexOf('/') !== -1) adminFqdn = adminFqdn.slice(0, adminFqdn.indexOf('/'));
255
287
 
256
- function checkIfAlreadyLoggedIn(callback) {
257
- config.setActive(adminFqdn);
258
-
259
- const token = config.token();
260
- if (!token) return callback('no token');
261
-
262
- request.get(`https://${adminFqdn}/api/v1/profile?access_token=${token}`, {}, function (error, response) {
263
- if (error) return callback(error);
264
- if (response.statusCode !== 200) return callback(`Failed to get profile: ${requestError(response)}`);
288
+ config.setActive(adminFqdn);
265
289
 
290
+ let token = config.token();
291
+ if (token) { // check if the token is not expired
292
+ const [error, response] = await safe(superagent.get(`https://${adminFqdn}/api/v1/profile?access_token=${token}`)
293
+ .timeout(10000)
294
+ .ok(() => true));
295
+ if (error) return exit(error);
296
+ if (response.status === 200) {
266
297
  console.log('Existing token still valid.'.green);
267
-
268
- callback(null, token);
269
- });
298
+ } else {
299
+ token = null;
300
+ console.log(`Existing token possibly expired: ${requestError(response)}`.red);
301
+ }
270
302
  }
271
303
 
272
- function authenticate(username, password, askForTotpToken) {
273
- var totpToken;
274
-
275
- if (askForTotpToken) totpToken = readlineSync.question('2FA Token: ', {});
276
-
304
+ if (!token) {
305
+ const username = options.username || readlineSync.question('Username: ', {});
306
+ const password = options.password || readlineSync.question('Password: ', { noEchoBack: true });
277
307
  const rejectUnauthorized = !(options.parent.allowSelfsigned || options.parent.acceptSelfsigned);
278
- request.post(`https://${adminFqdn}/api/v1/cloudron/login`, {
279
- headers: {
280
- 'User-Agent': 'cloudron-cli'
281
- },
282
- rejectUnauthorized,
283
- json: {
284
- username,
285
- password,
286
- totpToken
287
- }
288
- }, function (error, response) {
289
- if (error) return exit(error);
290
- if (response.statusCode === 401) {
291
- if (response.body.message === 'A totpToken must be provided') {
292
- return authenticate(username, password, true);
293
- } else if (response.body.message === 'Invalid totpToken') {
294
- console.log('Invalid 2FA Token'.red);
295
- return authenticate(username, password, true);
296
- } else {
297
- exit('Invalid credentials');
298
- }
299
- }
300
- if (response.statusCode !== 200) return exit(new Error(`Login failed: Status code: ${requestError(response)}`));
301
-
302
- console.log('Login successful.'.green);
303
-
304
- done(response.body.accessToken);
305
- });
306
- }
307
308
 
308
- function done(token) {
309
- config.setActive(adminFqdn);
310
- config.setApiEndpoint(adminFqdn);
311
- config.setToken(token);
312
- config.setAllowSelfsigned(options.parent.allowSelfsigned || options.parent.acceptSelfsigned);
313
- config.set('cloudrons.default', adminFqdn);
309
+ const [error, result] = await safe(authenticate(adminFqdn, username, password, { rejectUnauthorized, askForTotpToken: false }));
310
+ if (error) return exit(`Failed to login: ${error.message}`);
311
+ token = result;
314
312
  }
315
313
 
316
- checkIfAlreadyLoggedIn(function (error, token) {
317
- if (!error) return done(token);
318
-
319
- let username = options.username || readlineSync.question('Username: ', {});
320
- let password = options.password || readlineSync.question('Password: ', { noEchoBack: true });
321
-
322
- authenticate(username, password, false);
323
- });
314
+ config.setActive(adminFqdn);
315
+ config.setApiEndpoint(adminFqdn);
316
+ config.setToken(token);
317
+ config.setAllowSelfsigned(options.parent.allowSelfsigned || options.parent.acceptSelfsigned);
318
+ config.set('cloudrons.default', adminFqdn);
324
319
  }
325
320
 
326
321
  function logout() {
@@ -1162,7 +1157,7 @@ function backupList(options) {
1162
1157
  response.body.backups.forEach(function (backup) {
1163
1158
  t.cell('Id', backup.id);
1164
1159
  t.cell('Creation Time', backup.creationTime);
1165
- t.cell('Version', backup.version);
1160
+ t.cell('Version', backup.packageVersion);
1166
1161
 
1167
1162
  t.newRow();
1168
1163
  });
@@ -1387,7 +1382,7 @@ function exec(cmd, options) {
1387
1382
  if (app.installationState !== 'installed') exit('App is not yet running. Try again later.');
1388
1383
 
1389
1384
  if (cmd.length === 0) {
1390
- cmd = [ '/bin/bash', '--rcfile', '/app/data/.bashrc' ];
1385
+ cmd = [ '/bin/bash' ];
1391
1386
  tty = true; // override
1392
1387
  }
1393
1388
 
package/src/helper.js CHANGED
@@ -2,15 +2,15 @@
2
2
 
3
3
  'use strict';
4
4
 
5
- let fs = require('fs'),
5
+ const fs = require('fs'),
6
6
  path = require('path'),
7
7
  util = require('util');
8
8
 
9
9
  exports = module.exports = {
10
- exit: exit,
10
+ exit,
11
11
 
12
- locateManifest: locateManifest,
13
- verifyArguments: verifyArguments
12
+ locateManifest,
13
+ verifyArguments
14
14
  };
15
15
 
16
16
  function exit(error) {
@@ -21,9 +21,9 @@ function exit(error) {
21
21
  }
22
22
 
23
23
  function locateManifest() {
24
- var curdir = process.cwd();
24
+ let curdir = process.cwd();
25
25
  do {
26
- var candidate = path.join(curdir, 'CloudronManifest.json');
26
+ const candidate = path.join(curdir, 'CloudronManifest.json');
27
27
  if (fs.existsSync(candidate)) return candidate;
28
28
 
29
29
  // check if we can't go further up (the previous check for '/' breaks on windows)