Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Adds User Keys #309

Closed
wants to merge 9 commits into from

3 participants

@dscape
Collaborator

As per @chjj.

@jfhbrook

yo @chjj are there any issues I should be aware about before I merge this? For example, master api changes, etc?

@chjj

@jesusabdullah, yeah. This branch probably won't work with http-users. To make this work we would most likely have to add a new set of commands to jitsu for managing api keys specifically. Originally, the old branch (that this was originally designed for) was supposed to handle both public keys and api keys, but those are going to be separated from each other now. There's a bunch of other smaller differences I'm sure. This branch shouldn't be merged until a few changes are made.

To make this happen:

  • The keys/tokens api in http-users needs to be solidified.
  • "tokens" commands need to be added to jitsu. The code below should be used for public keys only.
  • The code below needs to be changed slightly depending on whether http-users expects different fields.
  • nodejitsu-api needs to be updated to correspond to the new http-users api.
@jfhbrook

Okay.

I'll leave this open until you think it's ready. Lemme know.

@dscape dscape closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
30 lib/jitsu.js
@@ -34,7 +34,7 @@ jitsu.use(flatiron.plugins.cli, {
string: true
},
jitsuconf: {
- alias: 'j',
+ alias: 'j',
description: 'specify file to load configuration from',
string: true
},
@@ -69,7 +69,7 @@ jitsu.options.log = {
// Setup config, users, command aliases and prompt settings
//
jitsu.prompt.properties = flatiron.common.mixin(
- jitsu.prompt.properties,
+ jitsu.prompt.properties,
require('./jitsu/properties')
);
jitsu.prompt.override = jitsu.argv;
@@ -95,6 +95,7 @@ jitsu.api.Databases = require('nodejitsu-api').Databases;
jitsu.api.Logs = require('nodejitsu-api').Logs;
jitsu.api.Snapshots = require('nodejitsu-api').Snapshots;
jitsu.api.Users = require('nodejitsu-api').Users;
+jitsu.api.Keys = require('nodejitsu-api').Keys;
//
// ### function welcome ()
@@ -111,7 +112,7 @@ jitsu.welcome = function () {
// #### @callback {function} Continuation to pass control to when complete.
// Starts the jitsu CLI and runs the specified command.
//
-jitsu.start = function (callback) {
+jitsu.start = function (callback) {
//
// Check for --no-colors/--colors option, without hitting the config file
// yet
@@ -124,7 +125,7 @@ jitsu.start = function (callback) {
if (err) {
return callback();
}
-
+
jitsu.init(function (err) {
if (err) {
jitsu.welcome();
@@ -217,12 +218,13 @@ jitsu.exec = function (command, callback) {
// Sets up the instances of the Resource clients for jitsu.
// there is no io here, yet this function is ASYNC.
//
-jitsu.setup = function (callback) {
+jitsu.setup = function (callback) {
if (jitsu.started === true) {
return callback();
}
- ['Users', 'Apps', 'Snapshots', 'Databases', 'Logs'].forEach(function (key) {
+ ['Users', 'Apps', 'Snapshots',
+ 'Databases', 'Logs', 'Keys'].forEach(function (key) {
var k = key.toLowerCase();
jitsu[k] = new jitsu.api[key](jitsu.config);
jitsu[k].on('debug::request', debug);
@@ -258,12 +260,12 @@ jitsu.showError = function (command, err, shallow, skip) {
if (username) {
jitsu.log.error('Unable to authenticate as: ' + username.magenta);
}
-
- jitsu.log.error('403 ' + err.result.error);
+
+ jitsu.log.error('403 ' + err.result.error);
}
else if (!skip) {
jitsu.log.error('Error running command ' + command.magenta);
-
+
if (!jitsu.config.get('nolog')) {
jitsu.logFile.log(err);
}
@@ -287,7 +289,7 @@ jitsu.showError = function (command, err, shallow, skip) {
jitsu.log.error('');
jitsu.log.error('This type of error is usually a ' + err.result.result.error.blame.type + ' error.');
}
-
+
jitsu.log.error('Error output from your application:');
jitsu.log.error('');
if (err.result.result.error.stdout) {
@@ -295,7 +297,7 @@ jitsu.showError = function (command, err, shallow, skip) {
jitsu.log.error(line);
});
}
-
+
if (err.result.result.error.stderr) {
err.result.result.error.stderr.split('\n').forEach(function (line) {
jitsu.log.error(line);
@@ -306,13 +308,13 @@ jitsu.showError = function (command, err, shallow, skip) {
jitsu.log.error('There was an error while attempting to deploy your application.');
jitsu.log.error('');
jitsu.log.error(err.result.result.error.message);
-
+
if (err.result.result.error.blame) {
jitsu.log.error(err.result.result.error.blame.message);
jitsu.log.error('');
jitsu.log.error('This type of error is usually a ' + err.result.result.error.blame.type + ' error.');
- }
-
+ }
+
jitsu.log.error('Error output from Haibu:');
jitsu.log.error('');
stack = err.result.result.error.result || err.result.result.error.stack;
View
256 lib/jitsu/commands/keys.js
@@ -0,0 +1,256 @@
+/*
+ * keys.js: Commands related to user keys
+ *
+ * (C) 2010, Nodejitsu Inc.
+ *
+ */
+
+var jitsu = require('../../jitsu'),
+ fs = require('fs');
+
+var keys = exports;
+
+//
+// ### function create (keyName, file, callback)
+// #### @keyName {string} Desired key name
+// #### @file {string} Path to public key file
+// #### @callback {function} Continuation to pass control to when complete.
+// Attempt to create a key on the user's behalf.
+//
+keys.create = function (keyName, file, callback) {
+ var user = jitsu.config.get('username');
+ var data = { name: keyName };
+
+ function checkType() {
+ jitsu.log.info('Type of key? ' + '(api/ssh)'.magenta);
+ jitsu.prompt.get(['type'], function (err, result) {
+ if (result.type === 'ssh') {
+ jitsu.log.info('Please enter location of public key.');
+ jitsu.log.info(
+ '~/.ssh/id_rsa.pub'.magenta
+ + ' will be used by default.');
+ return jitsu.prompt.get(['file'], function (err, result) {
+ if (err) return callback(err);
+ file = result.file || jitsu.config.get('root') + '/.ssh/id_rsa.pub';
+ return checkTTL(readKey);
+ });
+ }
+ return checkTTL(function() {
+ jitsu.log.info('Generating API key.');
+ return createKey();
+ });
+ });
+ }
+
+ function checkTTL(next) {
+ if (data.ttl == null) {
+ jitsu.log.info('Time-to-live? Enter a date.');
+ return jitsu.prompt.get(['ttl'], function(err, result) {
+ if (err) return callback(err);
+
+ var ttl = new Date(result.ttl);
+ if (ttl + '' === 'Invalid Date'
+ || isNaN(+ttl)
+ || ttl < new Date) {
+ return next();
+ }
+
+ data.ttl = ttl - new Date;
+
+ return next();
+ });
+ }
+ return next();
+ }
+
+ function readKey() {
+ fs.readFile(file, 'utf8', function(err, val) {
+ if (err) return callback(err);
+
+ if (/private key/i.test(val)) {
+ jitsu.log.error(
+ 'Private key detected. Stopping.'
+ + ' Are you sure this is a'
+ + ' public'.bold + ' key?');
+ return callback(new Error(), true);
+ }
+
+ data.value = val;
+
+ jitsu.log.info('Uploading public key.');
+ return createKey();
+ });
+ }
+
+ function createKey() {
+ jitsu.keys.create(user + '/keys/' + keyName, data, function (err, response) {
+ if (err) return callback(err);
+ jitsu.log.info('Key successfully created.');
+ return callback();
+ });
+ }
+
+ if (!user) {
+ return jitsu.commands.users.login(function (err) {
+ if (err) return callback(err, true);
+ return keys.create(keyName, file, callback);
+ });
+ }
+
+ if (!keyName) {
+ jitsu.log.info('Please enter a name for your key.');
+ return jitsu.prompt.get(['name'], function(err, result) {
+ return err
+ ? callback(err)
+ : keys.create(result.name, file, callback);
+ });
+ }
+
+ // If there is a file path,
+ // we can assume it is a public key.
+ return file
+ ? readKey()
+ : checkType();
+};
+
+//
+// Usage for `jitsu keys create`.
+//
+keys.create.usage = [
+ 'Adds a key to jitsu, if no file is specified, generate random API key.',
+ '',
+ 'jitsu keys create',
+ 'jitsu keys create <name>',
+ 'jitsu keys create <name> <file>'
+];
+
+//
+// ### function list (callback)
+// #### @callback {function} Continuation to pass control to when complete.
+// Attempt to list all keys bound to the user.
+//
+keys.list = function (callback) {
+ var user = jitsu.config.get('username');
+
+ if (!user) {
+ return jitsu.commands.users.login(function (err) {
+ if (err) return callback(err, true);
+ return keys.list(keyName, callback);
+ });
+ }
+
+ jitsu.keys.list(user, function (err, keys) {
+ if (err) return callback(err);
+
+ var rows = [['name', 'type', 'value', 'ttl']],
+ colors = ['underline', 'underline', 'underline', 'underline'];
+
+ Object.keys(keys).forEach(function (k) {
+ var key = keys[k];
+ rows.push([
+ k,
+ key.type,
+ key.value.substring(0, 40),
+ key.ttl || 'none'
+ ]);
+ });
+
+ jitsu.inspect.putRows('data', rows, colors);
+
+ return callback();
+ });
+};
+
+//
+// Usage for `jitsu keys list`.
+//
+keys.list.usage = [
+ 'List all API and SSH keys.',
+ '',
+ 'jitsu keys list'
+];
+
+//
+// ### function view (keyName callback)
+// #### @keyName {string} Name of the key
+// #### @callback {function} Continuation to pass control to when complete.
+// Attempt to return a key.
+//
+keys.view = function (keyName, callback) {
+ var user = jitsu.config.get('username');
+
+ if (!user) {
+ return jitsu.commands.users.login(function (err) {
+ if (err) return callback(err, true);
+ return keys.view(keyName, callback);
+ });
+ }
+
+ if (!keyName) {
+ jitsu.log.info('Please enter a name for your key.');
+ return jitsu.prompt.get(['name'], function(err, result) {
+ return err
+ ? callback(err)
+ : keys.view(result.name, callback);
+ });
+ }
+
+ jitsu.keys.view(user + '/keys/' + keyName, function (err, key) {
+ if (err) return callback(err);
+ jitsu.inspect.putObject(key);
+ return callback();
+ });
+};
+
+//
+// Usage for `jitsu keys view`.
+//
+keys.view.usage = [
+ 'View a specific API or SSH key by name.',
+ '',
+ 'jitsu keys view',
+ 'jitsu keys view <name>'
+];
+
+//
+// ### function delete (keyName, callback)
+// #### @keyName {string} Name of the key
+// #### @callback {function} Continuation to pass control to when complete.
+// Attempt to return a key.
+//
+keys.delete = function (keyName, callback) {
+ var user = jitsu.config.get('username');
+
+ if (!user) {
+ return jitsu.commands.users.login(function (err) {
+ if (err) return callback(err, true);
+ return keys.delete(keyName, callback);
+ });
+ }
+
+ if (!keyName) {
+ jitsu.log.info('Please enter a name for your key.');
+ return jitsu.prompt.get(['name'], function(err, result) {
+ return err
+ ? callback(err)
+ : keys.delete(result.name, callback);
+ });
+ }
+
+ // fix this ins nodejitsu-api
+ jitsu.keys.destroy(user + '/keys/' + keyName, function (err) {
+ if (err) return callback(err);
+ jitsu.log.help('Key deleted successfully.');
+ return callback();
+ });
+};
+
+//
+// Usage for `jitsu keys delete`.
+//
+keys.delete.usage = [
+ 'Delete the specified API or SSH key.',
+ '',
+ 'jitsu keys delete',
+ 'jitsu keys delete <name>'
+];
View
13 lib/jitsu/completion.js
@@ -73,6 +73,12 @@ var complete = require('complete'),
// jitsu help users
// jitsu help wizard
+// jitsu keys [<commands>]
+// jitsu keys create <name> <file>
+// jitsu keys list
+// jitsu keys view <name>
+// jitsu keys delete <name>
+
// jitsu signup
// jitsu login
// jitsu logout
@@ -328,6 +334,13 @@ var commands = {
'wizard': {}
},
+ 'keys': {
+ 'create': {},
+ 'list': {},
+ 'view': {},
+ 'delete': {}
+ },
+
'signup': {},
'login': {},
'logout': {},
View
95 test/commands/keys-test.js
@@ -0,0 +1,95 @@
+/*
+ * apps-test.js: Tests for `jitsu apps *` command(s).
+ *
+ * (C) 2010, Nodejitsu Inc.
+ *
+ */
+
+var assert = require('assert'),
+ fs = require('fs'),
+ path = require('path'),
+ nock = require('nock'),
+ vows = require('vows'),
+ jitsu = require('../../lib/jitsu'),
+ macros = require('../helpers/macros');
+
+var shouldNodejitsuOk = macros.shouldNodejitsuOk,
+ useAppFixture = macros.useAppFixture;
+
+var mainDirectory = process.cwd();
+
+vows.describe('jitsu/commands/keys').addBatch({
+ 'keys list': shouldNodejitsuOk(function setup() {
+ jitsu.config.stores.file.file =
+ path.join(__dirname, '..', 'fixtures', 'logged-out-jitsuconf');
+ //path.join(__dirname, '..', 'fixtures', 'dot-jitsuconf');
+ jitsu.config.stores.file.loadSync();
+
+ jitsu.prompt.override.username = 'tester';
+ jitsu.prompt.override.password = 'EXAMPLE-PASSWORD';
+
+ nock('http://api.mockjitsu.com')
+ .get('/users/tester/keys')
+ .reply(200, {
+ 'hello': { type: 'api', value: 'asdas90d8208rhkhfh92yur3932' },
+ 'world': { type: 'ssh', value: 'asdasd8028390328iffusrehfh439' },
+ 'testing': { type: 'api', value: 'asdasdasdasdasdasdasdasdas' }
+ }, { 'x-powered-by': 'Nodejitsu' })
+ })
+}).addBatch({
+ 'keys view test1': shouldNodejitsuOk(function setup() {
+ jitsu.config.stores.file.file =
+ path.join(__dirname, '..', 'fixtures', 'logged-out-jitsuconf');
+ jitsu.config.stores.file.loadSync();
+
+ jitsu.prompt.override.username = 'tester';
+ jitsu.prompt.override.password = 'EXAMPLE-PASSWORD';
+ jitsu.prompt.override.name = 'test1';
+
+ nock('http://api.mockjitsu.com')
+ .get('/users/tester/keys/test1')
+ .reply(200,
+ { type: 'api', name: 'test1', value: 'asdas90d8208rhkhfh92yur3932' }
+ , { 'x-powered-by': 'Nodejitsu' })
+ })
+}).addBatch({
+ 'keys create test2': shouldNodejitsuOk(function setup() {
+ jitsu.config.stores.file.file =
+ path.join(__dirname, '..', 'fixtures', 'logged-out-jitsuconf');
+ jitsu.config.stores.file.loadSync();
+
+ jitsu.prompt.override.username = 'tester';
+ jitsu.prompt.override.password = 'EXAMPLE-PASSWORD';
+ jitsu.prompt.override.type = 'api';
+ jitsu.prompt.override.name = 'test2';
+ jitsu.prompt.override.ttl = '0';
+
+ nock('http://api.mockjitsu.com')
+ .post('/users/tester/keys/test2', { name: 'test2' })
+ .reply(200,
+ { type: 'api', name: 'test2', value: 'asdas90d8208rhkhfh92yur3932' }
+ , { 'x-powered-by': 'Nodejitsu' })
+ .get('/users/tester/keys/test2')
+ .reply(200,
+ { type: 'api', name: 'test2', value: 'asdas90d8208rhkhfh92yur3932' }
+ , { 'x-powered-by': 'Nodejitsu' })
+ })
+}).addBatch({
+ 'keys delete test3': shouldNodejitsuOk(function setup() {
+ jitsu.config.stores.file.file =
+ path.join(__dirname, '..', 'fixtures', 'logged-out-jitsuconf');
+ jitsu.config.stores.file.loadSync();
+
+ jitsu.prompt.override.username = 'tester';
+ jitsu.prompt.override.password = 'EXAMPLE-PASSWORD';
+ jitsu.prompt.override.name = 'test3';
+
+ nock('http://api.mockjitsu.com')
+ .delete('/users/tester/keys/test3', {})
+ .reply(200, '', { 'x-powered-by': 'Nodejitsu' })
+ .get('/users/tester/keys/test3')
+ .reply(500,
+ { error: 'not_found', reason: 'missing' }
+ , { 'x-powered-by': 'Nodejitsu' })
+ })
+}).export(module);
Something went wrong with that request. Please try again.