joyent/node-triton#54 first pass at 'triton rbac key' and 'triton rbac keys'
This commit is contained in:
parent
4491a55093
commit
dd0a70820b
@ -16,6 +16,8 @@
|
||||
- `triton rbac role ...` to show, create, edit and delete roles.
|
||||
- `triton rbac policies` to list all policies.
|
||||
- `triton rbac policy ...` to show, create, edit and delete policies.
|
||||
- `triton rbac keys` to list all RBAC user SSH keys.
|
||||
- `triton rbac key ...` to show, create, edit and delete user keys.
|
||||
|
||||
|
||||
## 2.1.4
|
||||
|
129
lib/cloudapi2.js
129
lib/cloudapi2.js
@ -294,7 +294,7 @@ CloudApi.prototype._passThrough = function _passThrough(endpoint, opts, cb) {
|
||||
/**
|
||||
* Get network information
|
||||
*
|
||||
* @param {Function} callback of the form `function (err, networks, response)`
|
||||
* @param {Function} callback of the form `function (err, networks, res)`
|
||||
*/
|
||||
CloudApi.prototype.listNetworks = function listNetworks(opts, cb) {
|
||||
var endpoint = format('/%s/networks', this.account);
|
||||
@ -324,7 +324,7 @@ CloudApi.prototype.getNetwork = function getNetwork(id, cb) {
|
||||
/**
|
||||
* Get services information
|
||||
*
|
||||
* @param {Function} callback of the form `function (err, services, response)`
|
||||
* @param {Function} callback of the form `function (err, services, res)`
|
||||
*/
|
||||
CloudApi.prototype.listServices = function listServices(opts, cb) {
|
||||
var endpoint = format('/%s/services', this.account);
|
||||
@ -334,8 +334,7 @@ CloudApi.prototype.listServices = function listServices(opts, cb) {
|
||||
/**
|
||||
* Get datacenters information
|
||||
*
|
||||
* @param {Function} callback of the form
|
||||
* `function (err, datacenters, response)`
|
||||
* @param {Function} callback of the form `function (err, datacenters, res)`
|
||||
*/
|
||||
CloudApi.prototype.listDatacenters = function listDatacenters(opts, cb) {
|
||||
var endpoint = format('/%s/datacenters', this.account);
|
||||
@ -348,7 +347,7 @@ CloudApi.prototype.listDatacenters = function listDatacenters(opts, cb) {
|
||||
/**
|
||||
* Get account information
|
||||
*
|
||||
* @param {Function} callback of the form `function (err, account, response)`
|
||||
* @param {Function} callback of the form `function (err, account, res)`
|
||||
*/
|
||||
CloudApi.prototype.getAccount = function getAccount(opts, cb) {
|
||||
var endpoint = format('/%s', this.account);
|
||||
@ -356,9 +355,9 @@ CloudApi.prototype.getAccount = function getAccount(opts, cb) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Get public key information
|
||||
* List account's SSH keys.
|
||||
*
|
||||
* @param {Function} callback of the form `function (err, keys, response)`
|
||||
* @param {Function} callback of the form `function (err, keys, res)`
|
||||
*/
|
||||
CloudApi.prototype.listKeys = function listKeys(opts, cb) {
|
||||
var endpoint = format('/%s/keys', this.account);
|
||||
@ -444,7 +443,7 @@ CloudApi.prototype.getPackage = function getPackage(opts, cb) {
|
||||
* XXX cloudapi docs don't doc the credentials=true option
|
||||
*
|
||||
* @param {String} uuid (required) The machine id.
|
||||
* @param {Function} callback of the form `function (err, machine, response)`
|
||||
* @param {Function} callback of the form `function (err, machine, res)`
|
||||
*/
|
||||
CloudApi.prototype.getMachine = function getMachine(id, cb) {
|
||||
assert.uuid(id, 'id');
|
||||
@ -480,7 +479,7 @@ CloudApi.prototype.deleteMachine = function deleteMachine(uuid, callback) {
|
||||
* start a machine by id.
|
||||
*
|
||||
* @param {String} uuid (required) The machine id.
|
||||
* @param {Function} callback of the form `function (err, machine, response)`
|
||||
* @param {Function} callback of the form `function (err, machine, res)`
|
||||
*/
|
||||
CloudApi.prototype.startMachine = function startMachine(uuid, callback) {
|
||||
return this._doMachine('start', uuid, callback);
|
||||
@ -490,7 +489,7 @@ CloudApi.prototype.startMachine = function startMachine(uuid, callback) {
|
||||
* stop a machine by id.
|
||||
*
|
||||
* @param {String} uuid (required) The machine id.
|
||||
* @param {Function} callback of the form `function (err, machine, response)`
|
||||
* @param {Function} callback of the form `function (err, machine, res)`
|
||||
*/
|
||||
CloudApi.prototype.stopMachine = function stopMachine(uuid, callback) {
|
||||
return this._doMachine('stop', uuid, callback);
|
||||
@ -500,7 +499,7 @@ CloudApi.prototype.stopMachine = function stopMachine(uuid, callback) {
|
||||
* reboot a machine by id.
|
||||
*
|
||||
* @param {String} uuid (required) The machine id.
|
||||
* @param {Function} callback of the form `function (err, machine, response)`
|
||||
* @param {Function} callback of the form `function (err, machine, res)`
|
||||
*/
|
||||
CloudApi.prototype.rebootMachine = function rebootMachine(uuid, callback) {
|
||||
return this._doMachine('reboot', uuid, callback);
|
||||
@ -658,7 +657,7 @@ CloudApi.prototype.createMachine = function createMachine(options, callback) {
|
||||
* XXX IMO this endpoint should be called ListMachineAudit in cloudapi.
|
||||
*
|
||||
* @param {String} id (required) The machine id.
|
||||
* @param {Function} callback of the form `function (err, audit, response)`
|
||||
* @param {Function} callback of the form `function (err, audit, res)`
|
||||
*/
|
||||
CloudApi.prototype.machineAudit = function machineAudit(id, cb) {
|
||||
assert.uuid(id, 'id');
|
||||
@ -827,6 +826,112 @@ CloudApi.prototype.deleteUser = function deleteUser(opts, cb) {
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* List RBAC user's SSH keys.
|
||||
*
|
||||
* @param {Object} opts (object)
|
||||
* - {String} userId (required) The user id or login.
|
||||
* @param {Function} callback of the form `function (err, userKeys, res)`
|
||||
*/
|
||||
CloudApi.prototype.listUserKeys = function listUserKeys(opts, cb) {
|
||||
assert.object(opts, 'opts');
|
||||
assert.string(opts.userId, 'opts.userId');
|
||||
assert.func(cb, 'cb');
|
||||
|
||||
var endpoint = format('/%s/users/%s/keys', this.account, opts.userId);
|
||||
this._passThrough(endpoint, opts, cb);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get a RBAC user's SSH key.
|
||||
*
|
||||
* @param {Object} opts (object)
|
||||
* - {String} userId (required) The user id or login.
|
||||
* - {String} fingerprint (required*) The SSH key fingerprint. One of
|
||||
* 'fingerprint' or 'name' is required.
|
||||
* - {String} name (required*) The SSH key name. One of 'fingerprint'
|
||||
* or 'name' is required.
|
||||
* @param {Function} callback of the form `function (err, userKey, res)`
|
||||
*/
|
||||
CloudApi.prototype.getUserKey = function getUserKey(opts, cb) {
|
||||
assert.object(opts, 'opts');
|
||||
assert.string(opts.userId, 'opts.userId');
|
||||
assert.optionalString(opts.fingerprint, 'opts.fingerprint');
|
||||
assert.optionalString(opts.name, 'opts.name');
|
||||
assert.ok(opts.fingerprint || opts.name,
|
||||
'one of "fingerprint" or "name" is require');
|
||||
assert.func(cb, 'cb');
|
||||
|
||||
var endpoint = format('/%s/users/%s/keys/%s', this.account, opts.userId,
|
||||
encodeURIComponent(opts.fingerprint || opts.name));
|
||||
this._passThrough(endpoint, {}, cb);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create/upload a new RBAC user SSH public key.
|
||||
*
|
||||
* @param {Object} opts (object)
|
||||
* - {String} userId (required) The user id or login.
|
||||
* - {String} key (required) The SSH public key content.
|
||||
* - {String} name (optional) A name for the key. If not given, the
|
||||
* key fingerprint will be used.
|
||||
* @param {Function} callback of the form `function (err, userKey, res)`
|
||||
*/
|
||||
CloudApi.prototype.createUserKey = function createUserKey(opts, cb) {
|
||||
assert.object(opts, 'opts');
|
||||
assert.string(opts.userId, 'opts.userId');
|
||||
assert.string(opts.key, 'opts.key');
|
||||
assert.optionalString(opts.name, 'opts.name');
|
||||
assert.func(cb, 'cb');
|
||||
|
||||
var data = {
|
||||
name: opts.name,
|
||||
key: opts.key
|
||||
};
|
||||
|
||||
this._request({
|
||||
method: 'POST',
|
||||
path: format('/%s/users/%s/keys', this.account, opts.userId),
|
||||
data: data
|
||||
}, function (err, req, res, body) {
|
||||
cb(err, body, res);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Delete a RBAC user's SSH key.
|
||||
*
|
||||
* @param {Object} opts (object)
|
||||
* - {String} userId (required) The user id or login.
|
||||
* - {String} fingerprint (required*) The SSH key fingerprint. One of
|
||||
* 'fingerprint' or 'name' is required.
|
||||
* - {String} name (required*) The SSH key name. One of 'fingerprint'
|
||||
* or 'name' is required.
|
||||
* @param {Function} callback of the form `function (err, res)`
|
||||
*/
|
||||
CloudApi.prototype.deleteUserKey = function deleteUserKey(opts, cb) {
|
||||
assert.object(opts, 'opts');
|
||||
assert.string(opts.userId, 'opts.userId');
|
||||
assert.optionalString(opts.fingerprint, 'opts.fingerprint');
|
||||
assert.optionalString(opts.name, 'opts.name');
|
||||
assert.ok(opts.fingerprint || opts.name,
|
||||
'one of "fingerprint" or "name" is require');
|
||||
assert.func(cb, 'cb');
|
||||
|
||||
this._request({
|
||||
method: 'DELETE',
|
||||
path: format('/%s/users/%s/keys/%s', this.account, opts.userId,
|
||||
encodeURIComponent(opts.fingerprint || opts.name))
|
||||
}, function (err, req, res) {
|
||||
cb(err, res);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* <http://apidocs.joyent.com/cloudapi/#ListRoles>
|
||||
*
|
||||
|
@ -695,6 +695,17 @@ function indent(s, indentation) {
|
||||
}
|
||||
|
||||
|
||||
// http://perldoc.perl.org/functions/chomp.html
|
||||
function chomp(s) {
|
||||
if (s.length) {
|
||||
while (s.slice(-1) === '\n') {
|
||||
s = s.slice(0, -1);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---- exports
|
||||
|
||||
@ -719,6 +730,7 @@ module.exports = {
|
||||
promptField: promptField,
|
||||
editInEditor: editInEditor,
|
||||
ansiStylize: ansiStylize,
|
||||
indent: indent
|
||||
indent: indent,
|
||||
chomp: chomp
|
||||
};
|
||||
// vim: set softtabstop=4 shiftwidth=4:
|
||||
|
@ -11,19 +11,21 @@
|
||||
*/
|
||||
|
||||
var common = require('./common');
|
||||
var errors = require('./errors');
|
||||
|
||||
function do_keys(subcmd, opts, args, callback) {
|
||||
|
||||
function do_keys(subcmd, opts, args, cb) {
|
||||
if (opts.help) {
|
||||
this.do_help('help', {}, [subcmd], callback);
|
||||
this.do_help('help', {}, [subcmd], cb);
|
||||
return;
|
||||
} else if (args.length !== 0) {
|
||||
callback(new Error('invalid args: ' + args));
|
||||
cb(new errors.UsageError('invalid args: ' + args));
|
||||
return;
|
||||
}
|
||||
|
||||
this.tritonapi.cloudapi.listKeys(function (err, keys) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
cb(err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -31,13 +33,10 @@ function do_keys(subcmd, opts, args, callback) {
|
||||
common.jsonStream(keys);
|
||||
} else {
|
||||
keys.forEach(function (key) {
|
||||
process.stdout.write(key.key);
|
||||
if (key.key && key.key.slice(-1) !== '\n') {
|
||||
process.stdout.write('\n');
|
||||
}
|
||||
console.log(common.chomp(key.key));
|
||||
});
|
||||
}
|
||||
callback();
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -55,9 +55,8 @@ function do_policies(subcmd, opts, args, cb) {
|
||||
if (opts.json) {
|
||||
common.jsonStream(policies);
|
||||
} else {
|
||||
var i, j;
|
||||
// Add some convenience fields
|
||||
for (i = 0; i < policies.length; i++) {
|
||||
for (var i = 0; i < policies.length; i++) {
|
||||
var role = policies[i];
|
||||
role.shortid = role.id.split('-', 1)[0];
|
||||
role.nrules = role.rules.length;
|
||||
|
@ -98,10 +98,11 @@ function _stripYamlishLine(line) {
|
||||
function _policyFromYamlish(yamlish) {
|
||||
assert.string(yamlish, 'yamlish');
|
||||
|
||||
var line;
|
||||
var policy = {};
|
||||
var lines = yamlish.split(/\n/g);
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var line = _stripYamlishLine(lines[i]);
|
||||
line = _stripYamlishLine(lines[i]);
|
||||
if (!line) {
|
||||
continue;
|
||||
}
|
||||
@ -109,13 +110,13 @@ function _policyFromYamlish(yamlish) {
|
||||
var key = parts[0].trim();
|
||||
var value = parts[1].trim();
|
||||
if (key === 'rules') {
|
||||
rules = [];
|
||||
var rules = [];
|
||||
if (value) {
|
||||
rules.push(value);
|
||||
}
|
||||
// Remaining lines are rules.
|
||||
for (var j = i+1; j < lines.length; j++) {
|
||||
var line = _stripYamlishLine(lines[j]);
|
||||
line = _stripYamlishLine(lines[j]);
|
||||
if (!line) {
|
||||
continue;
|
||||
}
|
||||
@ -185,7 +186,7 @@ function _editPolicy(opts, cb) {
|
||||
cli.tritonapi.cloudapi.updatePolicy(editedPolicy,
|
||||
function (uErr, updated) {
|
||||
if (uErr) {
|
||||
var prefix = 'Error updating policy with your changes:'
|
||||
var prefix = 'Error updating policy with your changes:';
|
||||
var errmsg = uErr.toString();
|
||||
if (errmsg.indexOf('\n') !== -1) {
|
||||
console.error(prefix + '\n' + common.indent(errmsg));
|
||||
@ -526,7 +527,7 @@ do_policy.help = [
|
||||
' # Or exclude FILE to interactively add.',
|
||||
'',
|
||||
'{{options}}',
|
||||
'Where "POLICY" is a full policy "id", the policy "login" name or a "shortid", i.e.',
|
||||
'Where "POLICY" is a full policy "id", the policy "name" or a "shortid", i.e.',
|
||||
'an id prefix.',
|
||||
'',
|
||||
'Fields for creating a policy:',
|
||||
|
@ -488,7 +488,7 @@ do_role.help = [
|
||||
' # Or exclude FILE to interactively add.',
|
||||
'',
|
||||
'{{options}}',
|
||||
'Where "ROLE" is a full role "id", the role "login" name or a "shortid", i.e.',
|
||||
'Where "ROLE" is a full role "id", the role "name" or a "shortid", i.e.',
|
||||
'an id prefix.',
|
||||
'',
|
||||
'Fields for creating a role:',
|
||||
|
@ -53,12 +53,14 @@ function _showUser(opts, cb) {
|
||||
assert.object(opts.cli, 'opts.cli');
|
||||
assert.string(opts.id, 'opts.id');
|
||||
assert.optionalBool(opts.roles, 'opts.roles');
|
||||
assert.optionalBool(opts.keys, 'opts.keys');
|
||||
assert.func(cb, 'cb');
|
||||
var cli = opts.cli;
|
||||
|
||||
cli.tritonapi.getUser({
|
||||
id: opts.id,
|
||||
roles: opts.roles
|
||||
roles: opts.roles,
|
||||
keys: opts.keys
|
||||
}, function onUser(err, user) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
@ -68,8 +70,20 @@ function _showUser(opts, cb) {
|
||||
console.log(JSON.stringify(user));
|
||||
} else {
|
||||
Object.keys(user).forEach(function (key) {
|
||||
if (key === 'keys') {
|
||||
return;
|
||||
}
|
||||
console.log('%s: %s', key, user[key]);
|
||||
});
|
||||
if (opts.keys) {
|
||||
console.log('keys:');
|
||||
user.keys.forEach(function (key) {
|
||||
process.stdout.write(' ' + key.key);
|
||||
if (key.key && key.key.slice(-1) !== '\n') {
|
||||
process.stdout.write('\n');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
cb();
|
||||
});
|
||||
@ -397,6 +411,7 @@ function do_user(subcmd, opts, args, cb) {
|
||||
cli: this.top,
|
||||
id: args[0],
|
||||
roles: opts.roles || opts.membership,
|
||||
keys: opts.keys,
|
||||
json: opts.json
|
||||
}, cb);
|
||||
break;
|
||||
@ -436,7 +451,7 @@ do_user.options = [
|
||||
{
|
||||
names: ['roles', 'r'],
|
||||
type: 'bool',
|
||||
help: 'Include "roles" and "default_roles" this user has.'
|
||||
help: 'Include "roles" and "default_roles" fields for this user.'
|
||||
},
|
||||
{
|
||||
names: ['membership'],
|
||||
@ -446,6 +461,11 @@ do_user.options = [
|
||||
'node-smartdc.',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
names: ['keys', 'k'],
|
||||
type: 'bool',
|
||||
help: 'Include SSH keys (the "keys" field) for this user.'
|
||||
},
|
||||
{
|
||||
names: ['yes', 'y'],
|
||||
type: 'bool',
|
||||
|
@ -50,4 +50,7 @@ RbacCLI.prototype.do_role = require('./do_role');
|
||||
RbacCLI.prototype.do_policies = require('./do_policies');
|
||||
RbacCLI.prototype.do_policy = require('./do_policy');
|
||||
|
||||
RbacCLI.prototype.do_keys = require('./do_keys');
|
||||
RbacCLI.prototype.do_key = require('./do_key');
|
||||
|
||||
module.exports = RbacCLI;
|
||||
|
@ -596,6 +596,8 @@ TritonApi.prototype.getInstance = function getInstance(name, cb) {
|
||||
* - id {UUID|String} The user ID (a UUID), login or short id.
|
||||
* - roles {Boolean} Optional. Whether to includes roles of which this
|
||||
* user is a member. Default false.
|
||||
* - keys {Boolean} Optional. Set to `true` to also (with a separate
|
||||
* request) retrieve the `keys` for this user. Default is false.
|
||||
* @param {Function} callback of the form `function (err, user)`
|
||||
*/
|
||||
TritonApi.prototype.getUser = function getUser(opts, cb) {
|
||||
@ -603,6 +605,7 @@ TritonApi.prototype.getUser = function getUser(opts, cb) {
|
||||
assert.object(opts, 'opts');
|
||||
assert.string(opts.id, 'opts.id');
|
||||
assert.optionalBool(opts.roles, 'opts.roles');
|
||||
assert.optionalBool(opts.keys, 'opts.keys');
|
||||
assert.func(cb, 'cb');
|
||||
|
||||
/*
|
||||
@ -705,6 +708,21 @@ TritonApi.prototype.getUser = function getUser(opts, cb) {
|
||||
next();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
function getKeys(ctx, next) {
|
||||
if (!opts.keys) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
self.cloudapi.listUserKeys({id: ctx.user.id}, function (err, keys) {
|
||||
if (err) {
|
||||
next(err);
|
||||
return;
|
||||
}
|
||||
ctx.user.keys = keys;
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
]}, function (err) {
|
||||
|
Reference in New Issue
Block a user