Skip to content

Commit

Permalink
Add more information about the account to auth contexts (#168)
Browse files Browse the repository at this point in the history
  • Loading branch information
itai-codefresh committed Mar 7, 2018
1 parent 073ceec commit 02b91c7
Show file tree
Hide file tree
Showing 18 changed files with 6,604 additions and 720 deletions.
10 changes: 5 additions & 5 deletions codefresh.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ steps:
commands:
- yarn eslint

# unit-tests:
# title: 'Running unit tests'
# image: codefresh/node-tester-image:8.8.0
# commands:
# - yarn test
unit-tests:
title: 'Running unit tests'
image: codefresh/node-tester-image:8.8.0
commands:
- yarn test

build_step:
type: build
Expand Down
10 changes: 6 additions & 4 deletions lib/interface/cli/commands/auth/current-context.cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ const CFError = require('cf-errors');
const { auth } = require('../../../../logic');
const authManager = auth.manager;
const authRoot = require('../root/auth.cmd');

const { printTableForAuthContexts } = require('../../helpers/auth');
const _ = require('lodash');

const command = new Command({
command: 'current-context',
Expand All @@ -21,11 +22,12 @@ const command = new Command({
},
handler: async (argv) => {
const currentContext = authManager.getCurrentContext();
if (currentContext) {
console.log(currentContext.getName());
} else {
if (_.isUndefined(currentContext)) {
throw new CFError('There are no contexts in cfconfig file');
}
if (currentContext) {
await printTableForAuthContexts({ filter: 'current' });
}
},
});

Expand Down
2 changes: 1 addition & 1 deletion lib/interface/cli/commands/auth/get-contexts.cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const command = new Command({
.example('codefresh auth get-contexts', 'List all existing authentication contexts');
},
handler: async (argv) => {
printTableForAuthContexts();
await printTableForAuthContexts({ filter: 'all' });
},
});

Expand Down
68 changes: 47 additions & 21 deletions lib/interface/cli/helpers/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,57 @@ const _ = require('lodash');
const columnify = require('columnify');
const { auth } = require('../../../logic');
const authManager = auth.manager;
const kefir = require('kefir');
const assert = require('assert');
const debug = require('debug')('codefresh:cli:helpers:auth');
const colors = require('colors');

const printTableForAuthContexts = () => {
const currentContext = authManager.getCurrentContext();
if (!currentContext) {
console.log('No authentication contexts.');
return;
const printTableForAuthContexts = ({ filter = 'all' }) => {
let contexts;
if (filter === 'all') {
contexts = _.keys(authManager.getAllContexts());
}
const contexts = authManager.getAllContexts();
const keys = currentContext.defaultColumns;
if (filter === 'current') {
contexts = [_.get(authManager.getCurrentContext(), 'name', {})];
}


const res = [];
let obj = [];
_.forEach(contexts, (context) => {
obj = [];
_.forEach(keys, (key) => {
if (key === 'current') {
(context === currentContext) ? obj[key.toUpperCase()] = '*' : obj[key.toUpperCase()] = '';
}
else {
obj[key.toUpperCase()] = context[key];
}
let mergedKeys = [];
let contextsInfo = {};
return kefir.sequentially(0, contexts)
.flatMap((contextName) => {
let context = authManager.getContextByName(contextName);
debug(`context to check ${context.name}`);
return kefir.fromPromise(context.addAccountInfo()
.then(() => {
mergedKeys = _.union(mergedKeys, context.defaultColumns);
let ret = _.chain(context)
.pick(context.defaultColumns)
.mapValues((value, key) => {
if (key === 'current') return '*'.green;
return _.isString(value) ? value : JSON.stringify(value);
})
.value();

return ret;
}));
})
.scan((prev, context) => {
prev.push(context);
return prev;
}, [])
.flatMap((info) => {
info = _.sortBy(info, ['name']);
output = columnify(info, { columns: mergedKeys });
return kefir.constant(output);
})
.ignoreErrors()
.toPromise()
.then((output) => {
console.log(output);
});
res.push(obj);
});
const columns = columnify(res);
console.log(columns);

};

module.exports = { printTableForAuthContexts };
18 changes: 12 additions & 6 deletions lib/logic/api/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const { printError } = require('../../interface/cli/helpers/general');

const { version } = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../../../package.json')));

const sendHttpRequest = async (httpOptions, authContext) => {
const sendHttpRequest = async (httpOptions, authContext, throwOnUnauthorized) => {
let finalAuthContext;
if (!authContext) {
const authManager = require('../auth').manager; // eslint-disable-line
Expand All @@ -34,16 +34,22 @@ const sendHttpRequest = async (httpOptions, authContext) => {
response = await rp(finalOptions);
} catch (err) {
if (_.isEqual(err.statusCode, 401)) {
printError(new CFError({
const error = new CFError({
cause: err,
message: 'Error: Please create or update your authentication context'
}));
process.exit(1);
message: 'Error: Please create or update your authentication context',
});

if (!throwOnUnauthorized) {
printError();
process.exit(1);
} else {
throw error;
}
}
if (_.isEqual(err.statusCode, 403)) {
printError(new CFError({
cause: err,
message: 'Error: You do not have permissions to perform this action'
message: 'Error: You do not have permissions to perform this action',
}));
process.exit(1);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/logic/auth/contexts/APIKeyContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class APIKeyContext extends Context {
this.type = TYPE;
this.token = options.token;
this.name = options.name || this.name;
this.defaultColumns = ['current', 'name', 'url'];
this.defaultColumns = ['current', 'name', 'url', 'account', 'status'];
this.beta = options.beta || this.beta;
}

Expand Down
12 changes: 12 additions & 0 deletions lib/logic/auth/contexts/Context.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const CFError = require('cf-errors');
const uuidv4 = require('uuid/v4');
const { whoami } = require('./whoami');

class Context {
constructor(options) {
Expand All @@ -12,6 +13,17 @@ class Context {
return this.beta;
}

async addAccountInfo() {
try {
const { name, runtimeEnvironment } = await whoami(this);
this.account = name;
this.runtimeEnvironment = runtimeEnvironment;
this.status = 'Valid';
} catch (err) {
this.status = 'Revoked';
}
}

setName(name) {
this.name = name;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/logic/auth/contexts/JWTContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class JWTContext extends Context {
this.accountName = options.accountName;
this.expires = new Date(options.expires * 1000);
this.name = options.name || this.name;
this.defaultColumns = ['current', 'name', 'url'];
this.defaultColumns = ['current', 'name', 'url', 'account', 'status'];
this.beta = options.beta || this.beta;
}

Expand Down
25 changes: 25 additions & 0 deletions lib/logic/auth/contexts/whoami.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const { sendHttpRequest } = require('../../api/helper');
const _ = require('lodash');
const debug = require('debug')('codefresh:logic:auth:whoami');

const whoami = async (context) => {
debug(`context is ${context} for ${context.name}`);
const userOptions = {
url: '/api/user/',
method: 'GET',
};
const user = await sendHttpRequest(userOptions, context, true);
const accounts = _.get(user, 'account', {});
const accountInfo = _.chain(accounts)
.filter(account => account.name === user.activeAccountName)
.get('[0]', {})
.pick('name', 'runtimeEnvironment')
.value();

debug(`account info ${JSON.stringify(accountInfo)}`);
debug(`current account name is : ${JSON.stringify(_.get(user, 'activeAccountName'))}`);

return accountInfo;
};

module.exports = { whoami };
130 changes: 130 additions & 0 deletions lib/logic/auth/contexts/whoami.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/* eslint-disable */

const context = require('../index'); // eslint-disable-line
const { whoami } = require('./whoami');
const { APIKeyContext } = require('./index');

jest.mock('../../api/helper', () => { // eslint-disable-line
const apiHelper = require.requireActual('../../api/helper');

let response;
let validateParams;

const __setResponse = (x) => {
response = x;
};

const __setValidateParams = (x) => {
validateParams = x;
};

const __reset = () => {
response = null;
validateParams = null;
};

const sendHttpRequest = (userOptions, context, flag) => { // eslint-disable-line
if (validateParams) {
validateParams(userOptions, context, flag);
}

if (response) {
return response();
} else { // eslint-disable-line
return apiHelper.sendHttpRequest(userOptions, context, flag);
}
};

return {
__setResponse,
__setValidateParams,
__reset,
sendHttpRequest,
};
});

const apiHelper = require('../../api/helper');

beforeEach(() => {
apiHelper.__reset();
});

describe('whoami tests', () => {

describe('api key context', () => {

describe('positive', () => {

it('should return account information in case context is valid', () => {
const context = new APIKeyContext({
name: 'test-context',
url: 'http://test',
token: 'test-token',
});

apiHelper.__setValidateParams((userOptions, context, flag) => {
expect(userOptions)
.toEqual({
'method': 'GET',
'url': '/api/user/',
});
expect(flag)
.toEqual(true);
});

apiHelper.__setResponse(() => {
return Promise.resolve({
activeAccountName: 'account-name-1',
account: [
{
name: 'account-name-1',
runtimeEnvironment: 'runtime-environment-1',
},
{
name: 'account-name-2',
runtimeEnvironment: 'runtime-environment-2',
},
],
});
});

return whoami(context)
.then((accountInfo) => {
expect(accountInfo)
.toEqual({
name: 'account-name-1',
runtimeEnvironment: 'runtime-environment-1',
});
});

});

});

describe('negative', () => {

it('should fail in case context is not valid', () => {
const context = new APIKeyContext({
name: 'test-context',
url: 'http://test',
token: 'test-token',
});

apiHelper.__setResponse(() => {
return Promise.reject(new Error('http request error'));
});

return whoami(context)
.then(() => {
throw new Error('should have failed');
}, (err) => {
expect(err.toString())
.toEqual('Error: http request error');
});

});

});
});

});
5 changes: 3 additions & 2 deletions lib/logic/auth/manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class Manager {
* @returns {*}
*/
getContextByName(name) {
return this.contexts[name];
return _.get(this.contexts, name, undefined);
}

/**
Expand All @@ -37,14 +37,15 @@ class Manager {
this.addContext(context);
this.currentContextName = context.name;
}
context.current = 'true'; // eslint-disable-line
}

/**
* returns the current active context
* @returns {*}
*/
getCurrentContext() {
return this.contexts[this.currentContextName];
return _.get(this.contexts, this.currentContextName, undefined);
}

addContext(context) {
Expand Down
Loading

0 comments on commit 02b91c7

Please sign in to comment.