Skip to content

Commit

Permalink
Merge pull request #428 from particle-iot/feature/login-params
Browse files Browse the repository at this point in the history
feature/login-params
  • Loading branch information
busticated authored Jun 25, 2018
2 parents 32b88f1 + 59993a3 commit 3f54b08
Show file tree
Hide file tree
Showing 4 changed files with 311 additions and 49 deletions.
26 changes: 25 additions & 1 deletion src/cli/cloud.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,33 @@ export default ({ commandProcessor, root }) => {
});

commandProcessor.createCommand(cloud, 'login', 'Login to the cloud and store an access token locally', {
examples: {
'$0 $command': 'prompt for credentials and log in',
'$0 $command --username user@example.com --password test': 'log in with credentials provided on the command line',
'$0 $command --token <my-api-token>': 'log in with an access token provided on the command line',
'$0 $command --token <my-api-token> --username user@example.com': 'log in with an access token provided on the command line and set your username'
},
options: {
u: {
description: 'your username',
alias: 'username',
nargs: 1
},
p: {
description: 'your password',
alias: 'password',
nargs: 1
},
t: {
description: '',
alias: 'token',
nargs: 1
}
},
handler: (args) => {
const CloudCommands = require('../cmd/cloud');
return new CloudCommands().login();
const { username, password, token } = args;
return new CloudCommands().login(username, password, token);
}
});

Expand Down
99 changes: 52 additions & 47 deletions src/cmd/cloud.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,6 @@ const chalk = require('chalk');
const arrow = chalk.green('>');
const alert = chalk.yellow('!');

class EarlyReturnError extends VError {
constructor(...args) {
super(...args);
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
}
}

// Use known platforms and add shortcuts
const PLATFORMS = extend(utilities.knownPlatforms(), {
'c': 0,
Expand Down Expand Up @@ -165,11 +157,6 @@ class CloudCommand {
return this._doFlash({ api, deviceId, fileMapping, version });
});
}).catch((err) => {
if (VError.hasCauseWithName(err, EarlyReturnError.name)) {
console.log(err.message);
return;
}

throw new VError(ensureError(err), 'Flash device failed');
});
}
Expand Down Expand Up @@ -351,43 +338,61 @@ class CloudCommand {
});
}

login(username, password) {
if (this.tries >= (password ? 1 : 3)) {
throw new VError("It seems we're having trouble with logging in.");
}
login(username, password, token) {
const shouldRetry = !((username && password) || token && !this.tries);

return Promise.resolve().then(() => {
//prompt for creds
if (password) {
return { username: username, password: password };
}
return prompts.getCredentials(username, password);
}).then(creds => {
//login to the server
const api = new ApiClient();
username = creds.username;
this.newSpin('Sending login details...').start();
return api.login(settings.clientId, creds.username, creds.password);
}).then(accessToken => {

this.stopSpin();
console.log(arrow, 'Successfully completed login!');
settings.override(null, 'access_token', accessToken);
if (username) {
settings.override(null, 'username', username);
}
this.tries = 0;
return accessToken;
}).catch((err) => {
this.stopSpin();
console.log(alert, "There was an error logging you in! Let's try again.");
console.error(alert, err);
this.tries = (this.tries || 0) + 1;
return Promise.resolve()
.then(() => {
if (token){
return { token, username, password };
}
if (username && password){
return { username, password };
}
return prompts.getCredentials(username, password);
})
.then(credentials => {
const { token, username, password } = credentials;
const api = new ApiClient();

return this.login(username);
});
}
this.newSpin('Sending login details...').start();
this._usernameProvided = username;

if (token){
return api.getUser(token).then(() => ({ token, username, password }));
}
return api.login(settings.clientId, username, password)
.then(token => ({ token, username, password }));
})
.then(credentials => {
const { token, username } = credentials;

this.stopSpin();
console.log(arrow, 'Successfully completed login!');

settings.override(null, 'access_token', token);

if (username) {
settings.override(null, 'username', username);
}

this._usernameProvided = null;
this.tries = 0;

return token;
})
.catch(error => {
this.stopSpin();
console.log(alert, `There was an error logging you in! ${shouldRetry ? "Let's try again." : ''}`);
console.error(alert, error);
this.tries = (this.tries || 0) + 1;

if (shouldRetry && this.tries < 3){
return this.login(this._usernameProvided);
}
throw new VError("It seems we're having trouble with logging in.");
});
}

doLogout(keep, password) {
const api = new ApiClient();
Expand Down
34 changes: 33 additions & 1 deletion src/lib/ApiClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ class ApiClient {
});
}
ready() {
let hasToken = !!this._access_token;
let hasToken = this.hasToken();

if (!hasToken) {
console.log("You're not logged in. Please login using", chalk.bold.cyan('particle cloud login'), 'before using this command');
}
Expand All @@ -89,6 +90,10 @@ class ApiClient {
}
}

hasToken() {
return !!this._access_token;
}

clearToken() {
this._access_token = null;
}
Expand Down Expand Up @@ -148,6 +153,32 @@ class ApiClient {
return dfd.promise;
}

getUser(token){
const { request, hasBadToken } = this;
token = token || this._access_token;

return when.promise((resolve, reject) => {
request({
uri: '/v1/user',
method: 'GET',
json: true,
headers: {
Authorization: `Bearer ${token}`
}
}, (error, response, body) => {
if (error) {
return reject(error);
}
if (hasBadToken(body)) {
// TODO (mirande): throw a real error and supress the logging
// done within hasBadToken();
return reject('Invalid token');
}
return resolve(body);
});
});
}

/**
* Login and update the access token on this instance. Doesn't update the global settings.
* Outputs failure to the console.
Expand Down Expand Up @@ -1039,3 +1070,4 @@ class ApiClient {
}

module.exports = ApiClient;

Loading

0 comments on commit 3f54b08

Please sign in to comment.