joyent/node-triton#54 first pass at 'triton rbac policy' and 'triton rbac policies'
This commit is contained in:
		
							parent
							
								
									6b1065b24d
								
							
						
					
					
						commit
						c7daecc6f3
					
				@ -14,6 +14,8 @@
 | 
				
			|||||||
    - `triton rbac user ...` to show, create, edit and delete users.
 | 
					    - `triton rbac user ...` to show, create, edit and delete users.
 | 
				
			||||||
    - `triton rbac roles` to list all roles.
 | 
					    - `triton rbac roles` to list all roles.
 | 
				
			||||||
    - `triton rbac role ...` to show, create, edit and delete roles.
 | 
					    - `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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 2.1.4
 | 
					## 2.1.4
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										136
									
								
								lib/cloudapi2.js
									
									
									
									
									
								
							
							
						
						
									
										136
									
								
								lib/cloudapi2.js
									
									
									
									
									
								
							@ -460,7 +460,7 @@ CloudApi.prototype.getMachine = function getMachine(id, cb) {
 | 
				
			|||||||
 * delete a machine by id.
 | 
					 * delete a machine by id.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param {String} uuid (required) The machine 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, res)`
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
CloudApi.prototype.deleteMachine = function deleteMachine(uuid, callback) {
 | 
					CloudApi.prototype.deleteMachine = function deleteMachine(uuid, callback) {
 | 
				
			||||||
    var self = this;
 | 
					    var self = this;
 | 
				
			||||||
@ -471,8 +471,8 @@ CloudApi.prototype.deleteMachine = function deleteMachine(uuid, callback) {
 | 
				
			|||||||
        path: format('/%s/machines/%s', self.account, uuid),
 | 
					        path: format('/%s/machines/%s', self.account, uuid),
 | 
				
			||||||
        method: 'DELETE'
 | 
					        method: 'DELETE'
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    this._request(opts, function (err, req, res, body) {
 | 
					    this._request(opts, function (err, req, res) {
 | 
				
			||||||
        callback(err, body, res);
 | 
					        callback(err, res);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -809,9 +809,9 @@ CloudApi.prototype.updateUser = function updateUser(opts, cb) {
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * <http://apidocs.joyent.com/cloudapi/#DeleteUser>
 | 
					 * <http://apidocs.joyent.com/cloudapi/#DeleteUser>
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param {Object} opts (object) user object containing:
 | 
					 * @param {Object} opts (object)
 | 
				
			||||||
 *      - {String} id (required) for your user.
 | 
					 *      - {String} id (required) for your user.
 | 
				
			||||||
 * @param {Function} cb of the form `function (err, user, res)`
 | 
					 * @param {Function} cb of the form `function (err, res)`
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
CloudApi.prototype.deleteUser = function deleteUser(opts, cb) {
 | 
					CloudApi.prototype.deleteUser = function deleteUser(opts, cb) {
 | 
				
			||||||
    assert.object(opts, 'opts');
 | 
					    assert.object(opts, 'opts');
 | 
				
			||||||
@ -821,8 +821,8 @@ CloudApi.prototype.deleteUser = function deleteUser(opts, cb) {
 | 
				
			|||||||
    this._request({
 | 
					    this._request({
 | 
				
			||||||
        method: 'DELETE',
 | 
					        method: 'DELETE',
 | 
				
			||||||
        path: format('/%s/users/%s', this.account, opts.id)
 | 
					        path: format('/%s/users/%s', this.account, opts.id)
 | 
				
			||||||
    }, function (err, req, res, body) {
 | 
					    }, function (err, req, res) {
 | 
				
			||||||
        cb(err, body, res);
 | 
					        cb(err, res);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -850,7 +850,7 @@ CloudApi.prototype.listRoles = function listRoles(opts, cb) {
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param {Object} opts
 | 
					 * @param {Object} opts
 | 
				
			||||||
 *      - id {UUID|String} The role ID or name.
 | 
					 *      - id {UUID|String} The role ID or name.
 | 
				
			||||||
 * @param {Function} callback of the form `function (err, user, res)`
 | 
					 * @param {Function} callback of the form `function (err, role, res)`
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
CloudApi.prototype.getRole = function getRole(opts, cb) {
 | 
					CloudApi.prototype.getRole = function getRole(opts, cb) {
 | 
				
			||||||
    assert.object(opts, 'opts');
 | 
					    assert.object(opts, 'opts');
 | 
				
			||||||
@ -930,9 +930,9 @@ CloudApi.prototype.updateRole = function updateRole(opts, cb) {
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * <http://apidocs.joyent.com/cloudapi/#DeleteRole>
 | 
					 * <http://apidocs.joyent.com/cloudapi/#DeleteRole>
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param {Object} opts (object) user object containing:
 | 
					 * @param {Object} opts (object)
 | 
				
			||||||
 *      - {String} id (required) of the role to delete.
 | 
					 *      - {String} id (required) of the role to delete.
 | 
				
			||||||
 * @param {Function} cb of the form `function (err, user, res)`
 | 
					 * @param {Function} cb of the form `function (err, res)`
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
CloudApi.prototype.deleteRole = function deleteRole(opts, cb) {
 | 
					CloudApi.prototype.deleteRole = function deleteRole(opts, cb) {
 | 
				
			||||||
    assert.object(opts, 'opts');
 | 
					    assert.object(opts, 'opts');
 | 
				
			||||||
@ -942,11 +942,127 @@ CloudApi.prototype.deleteRole = function deleteRole(opts, cb) {
 | 
				
			|||||||
    this._request({
 | 
					    this._request({
 | 
				
			||||||
        method: 'DELETE',
 | 
					        method: 'DELETE',
 | 
				
			||||||
        path: format('/%s/roles/%s', this.account, opts.id)
 | 
					        path: format('/%s/roles/%s', this.account, opts.id)
 | 
				
			||||||
 | 
					    }, function (err, req, res) {
 | 
				
			||||||
 | 
					        cb(err, res);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * <http://apidocs.joyent.com/cloudapi/#ListPolicies>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param opts {Object} Options (optional)
 | 
				
			||||||
 | 
					 * @param cb {Function} Callback of the form `function (err, policies, res)`
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					CloudApi.prototype.listPolicies = function listPolicies(opts, cb) {
 | 
				
			||||||
 | 
					    if (cb === undefined) {
 | 
				
			||||||
 | 
					        cb = opts;
 | 
				
			||||||
 | 
					        opts = {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    assert.func(cb, 'cb');
 | 
				
			||||||
 | 
					    assert.object(opts, 'opts');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var endpoint = format('/%s/policies', this.account);
 | 
				
			||||||
 | 
					    this._passThrough(endpoint, opts, cb);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * <http://apidocs.joyent.com/cloudapi/#GetPolicy>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param {Object} opts
 | 
				
			||||||
 | 
					 *      - id {UUID|String} The policy ID or name.
 | 
				
			||||||
 | 
					 * @param {Function} callback of the form `function (err, policy, res)`
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					CloudApi.prototype.getPolicy = function getPolicy(opts, cb) {
 | 
				
			||||||
 | 
					    assert.object(opts, 'opts');
 | 
				
			||||||
 | 
					    assert.string(opts.id, 'opts.id');
 | 
				
			||||||
 | 
					    assert.func(cb, 'cb');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var endpoint = format('/%s/policies/%s', this.account, opts.id);
 | 
				
			||||||
 | 
					    this._passThrough(endpoint, {}, cb);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * <http://apidocs.joyent.com/cloudapi/#CreatePolicy>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param {Object} opts (object) policy object containing:
 | 
				
			||||||
 | 
					 *      - {String} name (required) for the policy.
 | 
				
			||||||
 | 
					 *      - {Array} description (optional) for the policy.
 | 
				
			||||||
 | 
					 *      - {Array} rules (optional) for the policy.
 | 
				
			||||||
 | 
					 * @param {Function} cb of the form `function (err, policy, res)`
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					CloudApi.prototype.createPolicy = function createPolicy(opts, cb) {
 | 
				
			||||||
 | 
					    assert.object(opts, 'opts');
 | 
				
			||||||
 | 
					    assert.string(opts.name, 'opts.name');
 | 
				
			||||||
 | 
					    // XXX strict on inputs
 | 
				
			||||||
 | 
					    assert.func(cb, 'cb');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var data = {
 | 
				
			||||||
 | 
					        name: opts.name,
 | 
				
			||||||
 | 
					        description: opts.description,
 | 
				
			||||||
 | 
					        rules: opts.rules
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this._request({
 | 
				
			||||||
 | 
					        method: 'POST',
 | 
				
			||||||
 | 
					        path: format('/%s/policies', this.account),
 | 
				
			||||||
 | 
					        data: data
 | 
				
			||||||
    }, function (err, req, res, body) {
 | 
					    }, function (err, req, res, body) {
 | 
				
			||||||
        cb(err, body, res);
 | 
					        cb(err, body, res);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * <http://apidocs.joyent.com/cloudapi/#UpdatePolicy>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param {Object} opts (object) policy object containing:
 | 
				
			||||||
 | 
					 *      - {UUID|String} id (required) The policy ID or name.
 | 
				
			||||||
 | 
					 *      - {String} name (optional)
 | 
				
			||||||
 | 
					 *      - {String} description (optional)
 | 
				
			||||||
 | 
					 *      - {Array} rules (optional)
 | 
				
			||||||
 | 
					 * @param {Function} cb of the form `function (err, policy, res)`
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					CloudApi.prototype.updatePolicy = function updatePolicy(opts, cb) {
 | 
				
			||||||
 | 
					    assert.object(opts, 'opts');
 | 
				
			||||||
 | 
					    assert.string(opts.id, 'opts.id');
 | 
				
			||||||
 | 
					    // XXX strict on inputs
 | 
				
			||||||
 | 
					    assert.func(cb, 'cb');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var update = {
 | 
				
			||||||
 | 
					        name: opts.name,
 | 
				
			||||||
 | 
					        description: opts.description,
 | 
				
			||||||
 | 
					        rules: opts.rules
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this._request({
 | 
				
			||||||
 | 
					        method: 'POST',
 | 
				
			||||||
 | 
					        path: format('/%s/policies/%s', this.account, opts.id),
 | 
				
			||||||
 | 
					        data: update
 | 
				
			||||||
 | 
					    }, function (err, req, res, body) {
 | 
				
			||||||
 | 
					        cb(err, body, res);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * <http://apidocs.joyent.com/cloudapi/#DeletePolicy>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param {Object} opts (object) user object containing:
 | 
				
			||||||
 | 
					 *      - {String} id (required) of the policy to delete.
 | 
				
			||||||
 | 
					 * @param {Function} cb of the form `function (err, res)`
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					CloudApi.prototype.deletePolicy = function deletePolicy(opts, cb) {
 | 
				
			||||||
 | 
					    assert.object(opts, 'opts');
 | 
				
			||||||
 | 
					    assert.string(opts.id, 'opts.id');
 | 
				
			||||||
 | 
					    assert.func(cb, 'cb');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this._request({
 | 
				
			||||||
 | 
					        method: 'DELETE',
 | 
				
			||||||
 | 
					        path: format('/%s/policies/%s', this.account, opts.id)
 | 
				
			||||||
 | 
					    }, function (err, req, res) {
 | 
				
			||||||
 | 
					        cb(err, res);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// --- Exports
 | 
					// --- Exports
 | 
				
			||||||
 | 
				
			|||||||
@ -686,6 +686,15 @@ function ansiStylize(str, color) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function indent(s, indentation) {
 | 
				
			||||||
 | 
					    if (!indentation) {
 | 
				
			||||||
 | 
					        indentation = '    ';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var lines = s.split(/\r?\n/g);
 | 
				
			||||||
 | 
					    return indentation + lines.join('\n' + indentation);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//---- exports
 | 
					//---- exports
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -709,6 +718,7 @@ module.exports = {
 | 
				
			|||||||
    promptEnter: promptEnter,
 | 
					    promptEnter: promptEnter,
 | 
				
			||||||
    promptField: promptField,
 | 
					    promptField: promptField,
 | 
				
			||||||
    editInEditor: editInEditor,
 | 
					    editInEditor: editInEditor,
 | 
				
			||||||
    ansiStylize: ansiStylize
 | 
					    ansiStylize: ansiStylize,
 | 
				
			||||||
 | 
					    indent: indent
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
// vim: set softtabstop=4 shiftwidth=4:
 | 
					// vim: set softtabstop=4 shiftwidth=4:
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										105
									
								
								lib/do_rbac/do_policies.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								lib/do_rbac/do_policies.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,105 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This Source Code Form is subject to the terms of the Mozilla Public
 | 
				
			||||||
 | 
					 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
				
			||||||
 | 
					 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2015 Joyent, Inc.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * `triton rbac policies ...`
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var tabula = require('tabula');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var common = require('../common');
 | 
				
			||||||
 | 
					var errors = require('../errors');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// columns default without -o
 | 
				
			||||||
 | 
					var columnsDefault = 'shortid,name,description,nrules';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// columns default with -l
 | 
				
			||||||
 | 
					var columnsDefaultLong = 'id,name,rules';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// sort default with -s
 | 
				
			||||||
 | 
					var sortDefault = 'name';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function do_policies(subcmd, opts, args, cb) {
 | 
				
			||||||
 | 
					    if (opts.help) {
 | 
				
			||||||
 | 
					        this.do_help('help', {}, [subcmd], cb);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    } else if (args.length !== 0) {
 | 
				
			||||||
 | 
					        cb(new errors.UsageError('invalid args: ' + args));
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var columns = columnsDefault;
 | 
				
			||||||
 | 
					    if (opts.o) {
 | 
				
			||||||
 | 
					        columns = opts.o;
 | 
				
			||||||
 | 
					    } else if (opts.long) {
 | 
				
			||||||
 | 
					        columns = columnsDefaultLong;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    columns = columns.split(',');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var sort = opts.s.split(',');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.top.tritonapi.cloudapi.listPolicies(function (err, policies) {
 | 
				
			||||||
 | 
					        if (err) {
 | 
				
			||||||
 | 
					            cb(err);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (opts.json) {
 | 
				
			||||||
 | 
					            common.jsonStream(policies);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            var i, j;
 | 
				
			||||||
 | 
					            // Add some convenience fields
 | 
				
			||||||
 | 
					            for (i = 0; i < policies.length; i++) {
 | 
				
			||||||
 | 
					                var role = policies[i];
 | 
				
			||||||
 | 
					                role.shortid = role.id.split('-', 1)[0];
 | 
				
			||||||
 | 
					                role.nrules = role.rules.length;
 | 
				
			||||||
 | 
					                role.rules = role.rules.sort().join('; ');
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            tabula(policies, {
 | 
				
			||||||
 | 
					                skipHeader: opts.H,
 | 
				
			||||||
 | 
					                columns: columns,
 | 
				
			||||||
 | 
					                sort: sort
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        cb();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					do_policies.options = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        names: ['help', 'h'],
 | 
				
			||||||
 | 
					        type: 'bool',
 | 
				
			||||||
 | 
					        help: 'Show this help.'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					].concat(common.getCliTableOptions({
 | 
				
			||||||
 | 
					    includeLong: true,
 | 
				
			||||||
 | 
					    sortDefault: sortDefault
 | 
				
			||||||
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					do_policies.help = (
 | 
				
			||||||
 | 
					    /* BEGIN JSSTYLED */
 | 
				
			||||||
 | 
					    'List RBAC policies.\n' +
 | 
				
			||||||
 | 
					    '\n' +
 | 
				
			||||||
 | 
					    'Usage:\n' +
 | 
				
			||||||
 | 
					    '    {{name}} policies [<options>]\n' +
 | 
				
			||||||
 | 
					    '\n' +
 | 
				
			||||||
 | 
					    '{{options}}' +
 | 
				
			||||||
 | 
					    '\n' +
 | 
				
			||||||
 | 
					    'Fields (most are self explanatory, the client adds some for convenience):\n' +
 | 
				
			||||||
 | 
					    '    shortid            A short ID prefix.\n' +
 | 
				
			||||||
 | 
					    '    nrules             The number of rules in this policy.\n'
 | 
				
			||||||
 | 
					    /* END JSSTYLED */
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = do_policies;
 | 
				
			||||||
							
								
								
									
										539
									
								
								lib/do_rbac/do_policy.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										539
									
								
								lib/do_rbac/do_policy.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,539 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This Source Code Form is subject to the terms of the Mozilla Public
 | 
				
			||||||
 | 
					 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
				
			||||||
 | 
					 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2015 Joyent, Inc.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * `triton rbac policy ...`
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var assert = require('assert-plus');
 | 
				
			||||||
 | 
					var format = require('util').format;
 | 
				
			||||||
 | 
					var fs = require('fs');
 | 
				
			||||||
 | 
					var strsplit = require('strsplit');
 | 
				
			||||||
 | 
					var vasync = require('vasync');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var common = require('../common');
 | 
				
			||||||
 | 
					var errors = require('../errors');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var UPDATABLE_ROLE_FIELDS = [
 | 
				
			||||||
 | 
					    {key: 'name', required: true},
 | 
				
			||||||
 | 
					    {key: 'description'},
 | 
				
			||||||
 | 
					    // Want 'rules' last for multiple yamlish repr, see below.
 | 
				
			||||||
 | 
					    {key: 'rules', array: true}
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var CREATE_ROLE_FIELDS = [
 | 
				
			||||||
 | 
					    {key: 'name', required: true},
 | 
				
			||||||
 | 
					    {key: 'description'},
 | 
				
			||||||
 | 
					    {key: 'rules', array: true}
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function _showPolicy(opts, cb) {
 | 
				
			||||||
 | 
					    assert.object(opts.cli, 'opts.cli');
 | 
				
			||||||
 | 
					    assert.string(opts.id, 'opts.id');
 | 
				
			||||||
 | 
					    assert.func(cb, 'cb');
 | 
				
			||||||
 | 
					    var cli = opts.cli;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cli.tritonapi.getPolicy({
 | 
				
			||||||
 | 
					        id: opts.id
 | 
				
			||||||
 | 
					    }, function onPolicy(err, policy) {
 | 
				
			||||||
 | 
					        if (err) {
 | 
				
			||||||
 | 
					            cb(err);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (opts.json) {
 | 
				
			||||||
 | 
					            console.log(JSON.stringify(policy));
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            console.log('name: %s', policy.name);
 | 
				
			||||||
 | 
					            delete policy.name;
 | 
				
			||||||
 | 
					            var rules = policy.rules;
 | 
				
			||||||
 | 
					            delete policy.rules;
 | 
				
			||||||
 | 
					            Object.keys(policy).forEach(function (key) {
 | 
				
			||||||
 | 
					                console.log('%s: %s', key, policy[key]);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            // Do rules last because it is the sole multiline field. The
 | 
				
			||||||
 | 
					            // rules can tend to be long, so we want to use multiline output.
 | 
				
			||||||
 | 
					            console.log('rules:');
 | 
				
			||||||
 | 
					            if (rules && rules.length) {
 | 
				
			||||||
 | 
					                console.log('    ' + rules.join('\n    '));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        cb();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function _yamlishFromPolicy(policy) {
 | 
				
			||||||
 | 
					    assert.object(policy, 'policy');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var lines = [];
 | 
				
			||||||
 | 
					    UPDATABLE_ROLE_FIELDS.forEach(function (field) {
 | 
				
			||||||
 | 
					        var key = field.key;
 | 
				
			||||||
 | 
					        var val = policy[key];
 | 
				
			||||||
 | 
					        if (key === 'rules') {
 | 
				
			||||||
 | 
					            lines.push('rules:');
 | 
				
			||||||
 | 
					            if (val && val.length) {
 | 
				
			||||||
 | 
					                lines.push('    ' + val.join('\n    '));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            lines.push(format('%s: %s', key, val));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    return lines.join('\n') + '\n';
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function _stripYamlishLine(line) {
 | 
				
			||||||
 | 
					    var commentIdx = line.indexOf('#');
 | 
				
			||||||
 | 
					    if (commentIdx !== -1) {
 | 
				
			||||||
 | 
					        line = line.slice(0, commentIdx);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return line.trim();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function _policyFromYamlish(yamlish) {
 | 
				
			||||||
 | 
					    assert.string(yamlish, 'yamlish');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var policy = {};
 | 
				
			||||||
 | 
					    var lines = yamlish.split(/\n/g);
 | 
				
			||||||
 | 
					    for (var i = 0; i < lines.length; i++) {
 | 
				
			||||||
 | 
					        var line = _stripYamlishLine(lines[i]);
 | 
				
			||||||
 | 
					        if (!line) {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        var parts = strsplit(line, ':', 2);
 | 
				
			||||||
 | 
					        var key = parts[0].trim();
 | 
				
			||||||
 | 
					        var value = parts[1].trim();
 | 
				
			||||||
 | 
					        if (key === 'rules') {
 | 
				
			||||||
 | 
					            rules = [];
 | 
				
			||||||
 | 
					            if (value) {
 | 
				
			||||||
 | 
					                rules.push(value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // Remaining lines are rules.
 | 
				
			||||||
 | 
					            for (var j = i+1; j < lines.length; j++) {
 | 
				
			||||||
 | 
					                var line = _stripYamlishLine(lines[j]);
 | 
				
			||||||
 | 
					                if (!line) {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                rules.push(line);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            policy['rules'] = rules;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            policy[key] = value;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return policy;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function _editPolicy(opts, cb) {
 | 
				
			||||||
 | 
					    assert.object(opts.cli, 'opts.cli');
 | 
				
			||||||
 | 
					    assert.string(opts.id, 'opts.id');
 | 
				
			||||||
 | 
					    assert.func(cb, 'cb');
 | 
				
			||||||
 | 
					    var cli = opts.cli;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var policy;
 | 
				
			||||||
 | 
					    var filename;
 | 
				
			||||||
 | 
					    var origText;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function offerRetry(afterText) {
 | 
				
			||||||
 | 
					        common.promptEnter(
 | 
				
			||||||
 | 
					            'Press <Enter> to re-edit, Ctrl+C to abort.',
 | 
				
			||||||
 | 
					            function (aborted) {
 | 
				
			||||||
 | 
					                if (aborted) {
 | 
				
			||||||
 | 
					                    console.log('\nAborting. No change made to policy.');
 | 
				
			||||||
 | 
					                    cb();
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    editAttempt(afterText);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function editAttempt(text) {
 | 
				
			||||||
 | 
					        common.editInEditor({
 | 
				
			||||||
 | 
					            text: text,
 | 
				
			||||||
 | 
					            filename: filename
 | 
				
			||||||
 | 
					        }, function (err, afterText, changed) {
 | 
				
			||||||
 | 
					            if (err) {
 | 
				
			||||||
 | 
					                return cb(new errors.TritonError(err));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // We don't use this `changed` in case it is a second attempt.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                var editedPolicy = _policyFromYamlish(afterText);
 | 
				
			||||||
 | 
					                editedPolicy.id = policy.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (_yamlishFromPolicy(editedPolicy) === origText) {
 | 
				
			||||||
 | 
					                    // This YAMLish is the closest to a canonical form we have.
 | 
				
			||||||
 | 
					                    console.log('No change to policy');
 | 
				
			||||||
 | 
					                    cb();
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } catch (textErr) {
 | 
				
			||||||
 | 
					                console.error('Error with your changes: %s', textErr);
 | 
				
			||||||
 | 
					                offerRetry(afterText);
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Save changes.
 | 
				
			||||||
 | 
					            cli.tritonapi.cloudapi.updatePolicy(editedPolicy,
 | 
				
			||||||
 | 
					                    function (uErr, updated) {
 | 
				
			||||||
 | 
					                if (uErr) {
 | 
				
			||||||
 | 
					                    var prefix = 'Error updating policy with your changes:'
 | 
				
			||||||
 | 
					                    var errmsg = uErr.toString();
 | 
				
			||||||
 | 
					                    if (errmsg.indexOf('\n') !== -1) {
 | 
				
			||||||
 | 
					                        console.error(prefix + '\n' + common.indent(errmsg));
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        console.error(prefix + ' ' + errmsg);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    offerRetry(afterText);
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                console.log('Updated policy "%s" (%s)',
 | 
				
			||||||
 | 
					                    updated.name, updated.id);
 | 
				
			||||||
 | 
					                cb();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cli.tritonapi.getPolicy({
 | 
				
			||||||
 | 
					        id: opts.id
 | 
				
			||||||
 | 
					    }, function onPolicy(err, policy_) {
 | 
				
			||||||
 | 
					        if (err) {
 | 
				
			||||||
 | 
					            return cb(err);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        policy = policy_;
 | 
				
			||||||
 | 
					        filename = format('%s-policy-%s.txt', cli.tritonapi.profile.account,
 | 
				
			||||||
 | 
					            policy.name);
 | 
				
			||||||
 | 
					        origText = _yamlishFromPolicy(policy);
 | 
				
			||||||
 | 
					        editAttempt(origText);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function _deletePolicies(opts, cb) {
 | 
				
			||||||
 | 
					    assert.object(opts.cli, 'opts.cli');
 | 
				
			||||||
 | 
					    assert.arrayOfString(opts.ids, 'opts.ids');
 | 
				
			||||||
 | 
					    assert.optionalBool(opts.yes, 'opts.yes');
 | 
				
			||||||
 | 
					    assert.func(cb, 'cb');
 | 
				
			||||||
 | 
					    var cli = opts.cli;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (opts.ids.length === 0) {
 | 
				
			||||||
 | 
					        cb();
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vasync.pipeline({funcs: [
 | 
				
			||||||
 | 
					        function confirm(_, next) {
 | 
				
			||||||
 | 
					            if (opts.yes) {
 | 
				
			||||||
 | 
					                return next();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            var msg;
 | 
				
			||||||
 | 
					            if (opts.ids.length === 1) {
 | 
				
			||||||
 | 
					                msg = 'Delete policy "' + opts.ids[0] + '"? [y/n] ';
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                msg = format('Delete %d policies (%s)? [y/n] ',
 | 
				
			||||||
 | 
					                    opts.ids.length, opts.ids.join(', '));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            common.promptYesNo({msg: msg}, function (answer) {
 | 
				
			||||||
 | 
					                if (answer !== 'y') {
 | 
				
			||||||
 | 
					                    console.error('Aborting');
 | 
				
			||||||
 | 
					                    next(true); // early abort signal
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    next();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        function deleteThem(_, next) {
 | 
				
			||||||
 | 
					            vasync.forEachPipeline({
 | 
				
			||||||
 | 
					                inputs: opts.ids,
 | 
				
			||||||
 | 
					                func: function deleteOne(id, nextId) {
 | 
				
			||||||
 | 
					                    cli.tritonapi.deletePolicy({id: id}, function (err) {
 | 
				
			||||||
 | 
					                        if (err) {
 | 
				
			||||||
 | 
					                            nextId(err);
 | 
				
			||||||
 | 
					                            return;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        console.log('Deleted policy "%s"', id);
 | 
				
			||||||
 | 
					                        nextId();
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }, next);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ]}, function (err) {
 | 
				
			||||||
 | 
					        if (err === true) {
 | 
				
			||||||
 | 
					            err = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        cb(err);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function _addPolicy(opts, cb) {
 | 
				
			||||||
 | 
					    assert.object(opts.cli, 'opts.cli');
 | 
				
			||||||
 | 
					    assert.optionalString(opts.file, 'opts.file');
 | 
				
			||||||
 | 
					    assert.func(cb, 'cb');
 | 
				
			||||||
 | 
					    var cli = opts.cli;
 | 
				
			||||||
 | 
					    var log = cli.log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vasync.pipeline({funcs: [
 | 
				
			||||||
 | 
					        function gatherDataStdin(_, next) {
 | 
				
			||||||
 | 
					            if (opts.file !== '-') {
 | 
				
			||||||
 | 
					                return next();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            var stdin = '';
 | 
				
			||||||
 | 
					            process.stdin.resume();
 | 
				
			||||||
 | 
					            process.stdin.on('data', function (chunk) {
 | 
				
			||||||
 | 
					                stdin += chunk;
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            process.stdin.on('end', function () {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    data = JSON.parse(stdin);
 | 
				
			||||||
 | 
					                } catch (err) {
 | 
				
			||||||
 | 
					                    log.trace({stdin: stdin}, 'invalid policy JSON on stdin');
 | 
				
			||||||
 | 
					                    return next(new errors.TritonError(
 | 
				
			||||||
 | 
					                        format('invalid policy JSON on stdin: %s', err)));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                next();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        function gatherDataFile(_, next) {
 | 
				
			||||||
 | 
					            if (!opts.file || opts.file === '-') {
 | 
				
			||||||
 | 
					                return next();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            var input = fs.readFileSync(opts.file);
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                data = JSON.parse(input);
 | 
				
			||||||
 | 
					            } catch (err) {
 | 
				
			||||||
 | 
					                return next(new errors.TritonError(format(
 | 
				
			||||||
 | 
					                    'invalid policy JSON in "%s": %s', opts.file, err)));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            next();
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        function gatherDataInteractive(_, next) {
 | 
				
			||||||
 | 
					            if (opts.file) {
 | 
				
			||||||
 | 
					                return next();
 | 
				
			||||||
 | 
					            } else if (!process.stdin.isTTY) {
 | 
				
			||||||
 | 
					                return next(new errors.UsageError('cannot interactively ' +
 | 
				
			||||||
 | 
					                    'create a policy: stdin is not a TTY'));
 | 
				
			||||||
 | 
					            } else if (!process.stdout.isTTY) {
 | 
				
			||||||
 | 
					                return next(new errors.UsageError('cannot interactively ' +
 | 
				
			||||||
 | 
					                    'create a policy: stdout is not a TTY'));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // TODO: better validation of name, rules
 | 
				
			||||||
 | 
					            // TODO: retries on failure
 | 
				
			||||||
 | 
					            // TODO: on failure write out to a tmp file with cmd to add it
 | 
				
			||||||
 | 
					            data = {};
 | 
				
			||||||
 | 
					            vasync.forEachPipeline({
 | 
				
			||||||
 | 
					                inputs: CREATE_ROLE_FIELDS,
 | 
				
			||||||
 | 
					                func: function getField(field, nextField) {
 | 
				
			||||||
 | 
					                    if (field.key === 'rules') {
 | 
				
			||||||
 | 
					                        var rules = [];
 | 
				
			||||||
 | 
					                        var rulePrompt = {
 | 
				
			||||||
 | 
					                            key: 'rule',
 | 
				
			||||||
 | 
					                            desc: 'Enter one rule per line. Enter an empty ' +
 | 
				
			||||||
 | 
					                                'rule to finish rules. See ' +
 | 
				
			||||||
 | 
					                                // JSSTYLED
 | 
				
			||||||
 | 
					                                '<https://docs.joyent.com/public-cloud/rbac/rules> ' +
 | 
				
			||||||
 | 
					                                'for rule syntax and examples.'
 | 
				
			||||||
 | 
					                        };
 | 
				
			||||||
 | 
					                        var promptAnotherRule = function () {
 | 
				
			||||||
 | 
					                            common.promptField(rulePrompt, function (err, val) {
 | 
				
			||||||
 | 
					                                delete rulePrompt.desc; // only want first time
 | 
				
			||||||
 | 
					                                if (err) {
 | 
				
			||||||
 | 
					                                    nextField(err);
 | 
				
			||||||
 | 
					                                } else if (!val) {
 | 
				
			||||||
 | 
					                                    // Done rules.
 | 
				
			||||||
 | 
					                                    data.rules = rules;
 | 
				
			||||||
 | 
					                                    nextField();
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					                                    rules.push(val);
 | 
				
			||||||
 | 
					                                    promptAnotherRule();
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                        };
 | 
				
			||||||
 | 
					                        promptAnotherRule();
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        common.promptField(field, function (err, value) {
 | 
				
			||||||
 | 
					                            if (value) {
 | 
				
			||||||
 | 
					                                data[field.key] = value;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            nextField(err);
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }, function (err) {
 | 
				
			||||||
 | 
					                console.log();
 | 
				
			||||||
 | 
					                next(err);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        function validateData(_, next) {
 | 
				
			||||||
 | 
					            var missing = [];
 | 
				
			||||||
 | 
					            var dataCopy = common.objCopy(data);
 | 
				
			||||||
 | 
					            CREATE_ROLE_FIELDS.forEach(function (field) {
 | 
				
			||||||
 | 
					                if (dataCopy.hasOwnProperty(field.key)) {
 | 
				
			||||||
 | 
					                    delete dataCopy[field.key];
 | 
				
			||||||
 | 
					                } else if (field.required) {
 | 
				
			||||||
 | 
					                    missing.push(field.key);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            var extra = Object.keys(dataCopy);
 | 
				
			||||||
 | 
					            var issues = [];
 | 
				
			||||||
 | 
					            if (missing.length) {
 | 
				
			||||||
 | 
					                issues.push(format('%s missing required field%s: %s',
 | 
				
			||||||
 | 
					                    missing.length, (missing.length === 1 ? '' : 's'),
 | 
				
			||||||
 | 
					                    missing.join(', ')));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (extra.length) {
 | 
				
			||||||
 | 
					                issues.push(format('extraneous field%s: %s',
 | 
				
			||||||
 | 
					                    (extra.length === 1 ? '' : 's'), extra.join(', ')));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (issues.length) {
 | 
				
			||||||
 | 
					                next(new errors.TritonError(
 | 
				
			||||||
 | 
					                    'invalid policy data: ' + issues.join('; ')));
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                next();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        function createIt(_, next) {
 | 
				
			||||||
 | 
					            cli.tritonapi.cloudapi.createPolicy(data, function (err, policy) {
 | 
				
			||||||
 | 
					                if (err) {
 | 
				
			||||||
 | 
					                    next(err);
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                console.log('Created policy "%s"', policy.name);
 | 
				
			||||||
 | 
					                next();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ]}, cb);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function do_policy(subcmd, opts, args, cb) {
 | 
				
			||||||
 | 
					    if (opts.help) {
 | 
				
			||||||
 | 
					        this.do_help('help', {}, [subcmd], cb);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Which action?
 | 
				
			||||||
 | 
					    var actions = [];
 | 
				
			||||||
 | 
					    if (opts.add) { actions.push('add'); }
 | 
				
			||||||
 | 
					    if (opts.edit) { actions.push('edit'); }
 | 
				
			||||||
 | 
					    if (opts['delete']) { actions.push('delete'); }
 | 
				
			||||||
 | 
					    var action;
 | 
				
			||||||
 | 
					    if (actions.length === 0) {
 | 
				
			||||||
 | 
					        action = 'show';
 | 
				
			||||||
 | 
					    } else if (actions.length > 1) {
 | 
				
			||||||
 | 
					        return cb(new errors.UsageError(
 | 
				
			||||||
 | 
					            'only one action option may be used at once'));
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        action = actions[0];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Arg count validation.
 | 
				
			||||||
 | 
					    if (args.length === 0 && ['show', 'edit'].indexOf(action) !== -1) {
 | 
				
			||||||
 | 
					        return cb(new errors.UsageError('POLICY argument is required'));
 | 
				
			||||||
 | 
					    } else if (action !== 'delete' && args.length > 1) {
 | 
				
			||||||
 | 
					        return cb(new errors.UsageError('too many arguments'));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (action) {
 | 
				
			||||||
 | 
					    case 'show':
 | 
				
			||||||
 | 
					        _showPolicy({
 | 
				
			||||||
 | 
					            cli: this.top,
 | 
				
			||||||
 | 
					            id: args[0],
 | 
				
			||||||
 | 
					            json: opts.json
 | 
				
			||||||
 | 
					        }, cb);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case 'edit':
 | 
				
			||||||
 | 
					        _editPolicy({
 | 
				
			||||||
 | 
					            cli: this.top,
 | 
				
			||||||
 | 
					            id: args[0]
 | 
				
			||||||
 | 
					        }, cb);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case 'delete':
 | 
				
			||||||
 | 
					        _deletePolicies({
 | 
				
			||||||
 | 
					            cli: this.top,
 | 
				
			||||||
 | 
					            ids: args,
 | 
				
			||||||
 | 
					            yes: opts.yes
 | 
				
			||||||
 | 
					        }, cb);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case 'add':
 | 
				
			||||||
 | 
					        _addPolicy({cli: this.top, file: args[0]}, cb);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        return cb(new errors.InternalError('unknown action: ' + action));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					do_policy.options = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        names: ['help', 'h'],
 | 
				
			||||||
 | 
					        type: 'bool',
 | 
				
			||||||
 | 
					        help: 'Show this help.'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        names: ['json', 'j'],
 | 
				
			||||||
 | 
					        type: 'bool',
 | 
				
			||||||
 | 
					        help: 'JSON stream output.'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        names: ['yes', 'y'],
 | 
				
			||||||
 | 
					        type: 'bool',
 | 
				
			||||||
 | 
					        help: 'Answer yes to confirmations, e.g. confirmation of deletion.'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        group: 'Action Options'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        names: ['edit', 'e'],
 | 
				
			||||||
 | 
					        type: 'bool',
 | 
				
			||||||
 | 
					        help: 'Edit the named policy in your $EDITOR.'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        names: ['add', 'a'],
 | 
				
			||||||
 | 
					        type: 'bool',
 | 
				
			||||||
 | 
					        help: 'Add a new policy.'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        names: ['delete', 'd'],
 | 
				
			||||||
 | 
					        type: 'bool',
 | 
				
			||||||
 | 
					        help: 'Delete the named policy.'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					do_policy.help = [
 | 
				
			||||||
 | 
					    /* BEGIN JSSTYLED */
 | 
				
			||||||
 | 
					    'Show, add, edit and delete RBAC policies.',
 | 
				
			||||||
 | 
					    '',
 | 
				
			||||||
 | 
					    'Usage:',
 | 
				
			||||||
 | 
					    '     {{name}} policy POLICY                   # show policy POLICY',
 | 
				
			||||||
 | 
					    '     {{name}} policy -e|--edit POLICY         # edit policy POLICY in $EDITOR',
 | 
				
			||||||
 | 
					    '     {{name}} policy -d|--delete [POLICY...]  # delete policy POLICY',
 | 
				
			||||||
 | 
					    '',
 | 
				
			||||||
 | 
					    '     {{name}} policy -a|--add [FILE]',
 | 
				
			||||||
 | 
					    '             # Add a new policy. FILE must be a file path to a JSON file',
 | 
				
			||||||
 | 
					    '             # with the policy data or "-" to pass the policy in on stdin.',
 | 
				
			||||||
 | 
					    '             # Or exclude FILE to interactively add.',
 | 
				
			||||||
 | 
					    '',
 | 
				
			||||||
 | 
					    '{{options}}',
 | 
				
			||||||
 | 
					    'Where "POLICY" is a full policy "id", the policy "login" name or a "shortid", i.e.',
 | 
				
			||||||
 | 
					    'an id prefix.',
 | 
				
			||||||
 | 
					    '',
 | 
				
			||||||
 | 
					    'Fields for creating a policy:',
 | 
				
			||||||
 | 
					    CREATE_ROLE_FIELDS.map(function (field) {
 | 
				
			||||||
 | 
					        return '    ' + field.key + (field.required ? ' (required)' : '');
 | 
				
			||||||
 | 
					    }).join('\n')
 | 
				
			||||||
 | 
					    /* END JSSTYLED */
 | 
				
			||||||
 | 
					].join('\n');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = do_policy;
 | 
				
			||||||
@ -49,13 +49,11 @@ function _arrayFromCSV(csv) {
 | 
				
			|||||||
function _showRole(opts, cb) {
 | 
					function _showRole(opts, cb) {
 | 
				
			||||||
    assert.object(opts.cli, 'opts.cli');
 | 
					    assert.object(opts.cli, 'opts.cli');
 | 
				
			||||||
    assert.string(opts.id, 'opts.id');
 | 
					    assert.string(opts.id, 'opts.id');
 | 
				
			||||||
    assert.optionalBool(opts.roles, 'opts.roles');
 | 
					 | 
				
			||||||
    assert.func(cb, 'cb');
 | 
					    assert.func(cb, 'cb');
 | 
				
			||||||
    var cli = opts.cli;
 | 
					    var cli = opts.cli;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    cli.tritonapi.getRole({
 | 
					    cli.tritonapi.getRole({
 | 
				
			||||||
        id: opts.id,
 | 
					        id: opts.id
 | 
				
			||||||
        roles: opts.roles
 | 
					 | 
				
			||||||
    }, function onRole(err, role) {
 | 
					    }, function onRole(err, role) {
 | 
				
			||||||
        if (err) {
 | 
					        if (err) {
 | 
				
			||||||
            cb(err);
 | 
					            cb(err);
 | 
				
			||||||
@ -222,8 +220,8 @@ function _deleteRoles(opts, cb) {
 | 
				
			|||||||
            if (opts.ids.length === 1) {
 | 
					            if (opts.ids.length === 1) {
 | 
				
			||||||
                msg = 'Delete role "' + opts.ids[0] + '"? [y/n] ';
 | 
					                msg = 'Delete role "' + opts.ids[0] + '"? [y/n] ';
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                msg = 'Delete %d roles (' + opts.ids.join(', ') + ')? [y/n] ';
 | 
					                msg = format('Delete %d roles (%s)? [y/n] ',
 | 
				
			||||||
 | 
					                    opts.ids.length, opts.ids.join(', '));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            common.promptYesNo({msg: msg}, function (answer) {
 | 
					            common.promptYesNo({msg: msg}, function (answer) {
 | 
				
			||||||
                if (answer !== 'y') {
 | 
					                if (answer !== 'y') {
 | 
				
			||||||
@ -405,12 +403,10 @@ function do_role(subcmd, opts, args, cb) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Arg count validation.
 | 
					    // Arg count validation.
 | 
				
			||||||
    if (args.length > 1) {
 | 
					    if (args.length === 0 && ['show', 'edit'].indexOf(action) !== -1) {
 | 
				
			||||||
        return cb(new errors.UsageError('too many arguments'));
 | 
					 | 
				
			||||||
    } else if (args.length === 0 &&
 | 
					 | 
				
			||||||
        ['show', 'edit'].indexOf(action) !== -1)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return cb(new errors.UsageError('ROLE argument is required'));
 | 
					        return cb(new errors.UsageError('ROLE argument is required'));
 | 
				
			||||||
 | 
					    } else if (action !== 'delete' && args.length > 1) {
 | 
				
			||||||
 | 
					        return cb(new errors.UsageError('too many arguments'));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (action) {
 | 
					    switch (action) {
 | 
				
			||||||
@ -453,19 +449,6 @@ do_role.options = [
 | 
				
			|||||||
        type: 'bool',
 | 
					        type: 'bool',
 | 
				
			||||||
        help: 'JSON stream output.'
 | 
					        help: 'JSON stream output.'
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        names: ['roles', 'r'],
 | 
					 | 
				
			||||||
        type: 'bool',
 | 
					 | 
				
			||||||
        help: 'Include "roles" and "default_roles" this user has.'
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        names: ['membership'],
 | 
					 | 
				
			||||||
        type: 'bool',
 | 
					 | 
				
			||||||
        help: 'Include "roles" and "default_roles" this user has. Included ' +
 | 
					 | 
				
			||||||
            'for backward compat with `sdc-user get --membership ...` from ' +
 | 
					 | 
				
			||||||
            'node-smartdc.',
 | 
					 | 
				
			||||||
        hidden: true
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        names: ['yes', 'y'],
 | 
					        names: ['yes', 'y'],
 | 
				
			||||||
        type: 'bool',
 | 
					        type: 'bool',
 | 
				
			||||||
@ -477,17 +460,17 @@ do_role.options = [
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        names: ['edit', 'e'],
 | 
					        names: ['edit', 'e'],
 | 
				
			||||||
        type: 'bool',
 | 
					        type: 'bool',
 | 
				
			||||||
        help: 'Edit the named user in your $EDITOR.'
 | 
					        help: 'Edit the named role in your $EDITOR.'
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        names: ['add', 'a'],
 | 
					        names: ['add', 'a'],
 | 
				
			||||||
        type: 'bool',
 | 
					        type: 'bool',
 | 
				
			||||||
        help: 'Add a new user.'
 | 
					        help: 'Add a new role.'
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        names: ['delete', 'd'],
 | 
					        names: ['delete', 'd'],
 | 
				
			||||||
        type: 'bool',
 | 
					        type: 'bool',
 | 
				
			||||||
        help: 'Delete the named user.'
 | 
					        help: 'Delete the named role.'
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
do_role.help = [
 | 
					do_role.help = [
 | 
				
			||||||
 | 
				
			|||||||
@ -211,8 +211,8 @@ function _deleteUsers(opts, cb) {
 | 
				
			|||||||
            if (opts.ids.length === 1) {
 | 
					            if (opts.ids.length === 1) {
 | 
				
			||||||
                msg = 'Delete user "' + opts.ids[0] + '"? [y/n] ';
 | 
					                msg = 'Delete user "' + opts.ids[0] + '"? [y/n] ';
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                msg = 'Delete %d users (' + opts.ids.join(', ') + ')? [y/n] ';
 | 
					                msg = format('Delete %d users (%s)? [y/n] ',
 | 
				
			||||||
 | 
					                    opts.ids.length, opts.ids.join(', '));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            common.promptYesNo({msg: msg}, function (answer) {
 | 
					            common.promptYesNo({msg: msg}, function (answer) {
 | 
				
			||||||
                if (answer !== 'y') {
 | 
					                if (answer !== 'y') {
 | 
				
			||||||
@ -385,12 +385,10 @@ function do_user(subcmd, opts, args, cb) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Arg count validation.
 | 
					    // Arg count validation.
 | 
				
			||||||
    if (args.length > 1) {
 | 
					    if (args.length === 0 && ['show', 'edit'].indexOf(action) !== -1) {
 | 
				
			||||||
        return cb(new errors.UsageError('too many arguments'));
 | 
					 | 
				
			||||||
    } else if (args.length === 0 &&
 | 
					 | 
				
			||||||
        ['show', 'edit'].indexOf(action) !== -1)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return cb(new errors.UsageError('USER argument is required'));
 | 
					        return cb(new errors.UsageError('USER argument is required'));
 | 
				
			||||||
 | 
					    } else if (action !== 'delete' && args.length > 1) {
 | 
				
			||||||
 | 
					        return cb(new errors.UsageError('too many arguments'));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    switch (action) {
 | 
					    switch (action) {
 | 
				
			||||||
 | 
				
			|||||||
@ -47,4 +47,7 @@ RbacCLI.prototype.do_user = require('./do_user');
 | 
				
			|||||||
RbacCLI.prototype.do_roles = require('./do_roles');
 | 
					RbacCLI.prototype.do_roles = require('./do_roles');
 | 
				
			||||||
RbacCLI.prototype.do_role = require('./do_role');
 | 
					RbacCLI.prototype.do_role = require('./do_role');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RbacCLI.prototype.do_policies = require('./do_policies');
 | 
				
			||||||
 | 
					RbacCLI.prototype.do_policy = require('./do_policy');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = RbacCLI;
 | 
					module.exports = RbacCLI;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										145
									
								
								lib/tritonapi.js
									
									
									
									
									
								
							
							
						
						
									
										145
									
								
								lib/tritonapi.js
									
									
									
									
									
								
							@ -811,7 +811,7 @@ TritonApi.prototype.getRole = function getRole(opts, cb) {
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param {Object} opts
 | 
					 * @param {Object} opts
 | 
				
			||||||
 *      - id {UUID|String} The role id (a UUID), name or short id.
 | 
					 *      - id {UUID|String} The role id (a UUID), name or short id.
 | 
				
			||||||
 * @param {Function} callback of the form `function (err, user)`
 | 
					 * @param {Function} callback of the form `function (err)`
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
TritonApi.prototype.deleteRole = function deleteRole(opts, cb) {
 | 
					TritonApi.prototype.deleteRole = function deleteRole(opts, cb) {
 | 
				
			||||||
    var self = this;
 | 
					    var self = this;
 | 
				
			||||||
@ -832,8 +832,12 @@ TritonApi.prototype.deleteRole = function deleteRole(opts, cb) {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.getRole({id: opts.id}, function (err, role) {
 | 
					            self.getRole({id: opts.id}, function (err, role) {
 | 
				
			||||||
                ctx.id = role.id;
 | 
					                if (err) {
 | 
				
			||||||
                    next(err);
 | 
					                    next(err);
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                ctx.id = role.id;
 | 
				
			||||||
 | 
					                next();
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -846,6 +850,143 @@ TritonApi.prototype.deleteRole = function deleteRole(opts, cb) {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Get an RBAC policy by ID, name, or short ID, in that order.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param {Object} opts
 | 
				
			||||||
 | 
					 *      - id {UUID|String} The RBAC policy id (a UUID), name or short id.
 | 
				
			||||||
 | 
					 * @param {Function} callback of the form `function (err, policy)`
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					TritonApi.prototype.getPolicy = function getPolicy(opts, cb) {
 | 
				
			||||||
 | 
					    var self = this;
 | 
				
			||||||
 | 
					    assert.object(opts, 'opts');
 | 
				
			||||||
 | 
					    assert.string(opts.id, 'opts.id');
 | 
				
			||||||
 | 
					    assert.func(cb, 'cb');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * CloudAPI GetPolicy supports a UUID or name, so we try that first.
 | 
				
			||||||
 | 
					     * If that is a 404 and `opts.id` a valid shortid, then try to lookup
 | 
				
			||||||
 | 
					     * via `listPolicies`.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    var context = {};
 | 
				
			||||||
 | 
					    vasync.pipeline({arg: context, funcs: [
 | 
				
			||||||
 | 
					        function tryGetIt(ctx, next) {
 | 
				
			||||||
 | 
					            self.cloudapi.getPolicy({id: opts.id}, function (err, policy) {
 | 
				
			||||||
 | 
					                if (err) {
 | 
				
			||||||
 | 
					                    if (err.restCode === 'ResourceNotFound') {
 | 
				
			||||||
 | 
					                        ctx.notFoundErr = err;
 | 
				
			||||||
 | 
					                        next();
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        next(err);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    ctx.policy = policy;
 | 
				
			||||||
 | 
					                    next();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        function tryShortId(ctx, next) {
 | 
				
			||||||
 | 
					            if (ctx.policy) {
 | 
				
			||||||
 | 
					                next();
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            var shortId = common.normShortId(opts.id);
 | 
				
			||||||
 | 
					            if (!shortId) {
 | 
				
			||||||
 | 
					                next();
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.cloudapi.listRoles(function (err, policies) {
 | 
				
			||||||
 | 
					                if (err) {
 | 
				
			||||||
 | 
					                    next(err);
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var shortIdMatches = [];
 | 
				
			||||||
 | 
					                for (var i = 0; i < policies.length; i++) {
 | 
				
			||||||
 | 
					                    var policy = policies[i];
 | 
				
			||||||
 | 
					                    if (policy.id.slice(0, shortId.length) === shortId) {
 | 
				
			||||||
 | 
					                        shortIdMatches.push(policy);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (shortIdMatches.length === 1) {
 | 
				
			||||||
 | 
					                    ctx.policy = shortIdMatches[0];
 | 
				
			||||||
 | 
					                    next();
 | 
				
			||||||
 | 
					                } else if (shortIdMatches.length === 0) {
 | 
				
			||||||
 | 
					                    next(new errors.ResourceNotFoundError(format(
 | 
				
			||||||
 | 
					                        'policy with id or name matching "%s" was not found',
 | 
				
			||||||
 | 
					                        opts.id)));
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    next(new errors.ResourceNotFoundError(
 | 
				
			||||||
 | 
					                        format('policy with name "%s" was not found '
 | 
				
			||||||
 | 
					                        + 'and "%s" is an ambiguous short id', opts.id)));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        function raiseEarlierNotFoundErrIfNotFound(ctx, next) {
 | 
				
			||||||
 | 
					            if (!ctx.policy) {
 | 
				
			||||||
 | 
					                // We must have gotten the `notFoundErr` above.
 | 
				
			||||||
 | 
					                next(new errors.ResourceNotFoundError(ctx.notFoundErr, format(
 | 
				
			||||||
 | 
					                    'policy with name or id "%s" was not found', opts.id)));
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                next();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ]}, function (err) {
 | 
				
			||||||
 | 
					        cb(err, context.policy);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Delete an RBAC policy by ID, name, or short ID, in that order.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param {Object} opts
 | 
				
			||||||
 | 
					 *      - id {UUID|String} The policy id (a UUID), name or short id.
 | 
				
			||||||
 | 
					 * @param {Function} callback of the form `function (err)`
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					TritonApi.prototype.deletePolicy = function deletePolicy(opts, cb) {
 | 
				
			||||||
 | 
					    var self = this;
 | 
				
			||||||
 | 
					    assert.object(opts, 'opts');
 | 
				
			||||||
 | 
					    assert.string(opts.id, 'opts.id');
 | 
				
			||||||
 | 
					    assert.func(cb, 'cb');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * CloudAPI DeletePolicy only accepts a policy id (UUID).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    var context = {};
 | 
				
			||||||
 | 
					    vasync.pipeline({arg: context, funcs: [
 | 
				
			||||||
 | 
					        function getId(ctx, next) {
 | 
				
			||||||
 | 
					            if (common.isUUID(opts.id)) {
 | 
				
			||||||
 | 
					                ctx.id = opts.id;
 | 
				
			||||||
 | 
					                next();
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.getPolicy({id: opts.id}, function (err, policy) {
 | 
				
			||||||
 | 
					                if (err) {
 | 
				
			||||||
 | 
					                    next(err);
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                ctx.id = policy.id;
 | 
				
			||||||
 | 
					                next();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        function deleteIt(ctx, next) {
 | 
				
			||||||
 | 
					            self.cloudapi.deletePolicy({id: ctx.id}, next);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ]}, function (err) {
 | 
				
			||||||
 | 
					        cb(err);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//---- exports
 | 
					//---- exports
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports.createClient = function (options) {
 | 
					module.exports.createClient = function (options) {
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user