#!/usr/bin/env node /* eslint no-console: 0*/ 'use strict'; // FUTURE FEATURE // this executable should generate and dispose access tokens for the API const pathlib = require('path'); process.env.NODE_CONFIG_DIR = pathlib.join(__dirname, '..', 'config'); const db = require('../lib/db'); const crypto = require('crypto'); const config = require('wild-config'); const yargs = require('yargs'); const util = require('util'); const dbconnect = util.promisify(db.connect); let argv = yargs .usage('Usage: $0 [options]') .command( 'provision ', 'Provision new access token', yargs => yargs .option('user', { alias: 'u', describe: 'Username', demandOption: true }) .option('role', { alias: 'r', describe: 'Choose role', choices: Object.keys(config.api.roles), demandOption: true }), async argv => { await dbconnect(); let accessToken = crypto.randomBytes(20).toString('hex'); let tokenHash = crypto .createHash('sha256') .update(accessToken) .digest('hex'); let key = 'tn:token:' + tokenHash; let tokenData = { user: argv.user, role: argv.role, created: Date.now(), // signature s: crypto .createHmac('sha256', config.api.accessControl.secret) .update( JSON.stringify({ token: accessToken, user: argv.user, role: argv.role }) ) .digest('hex') }; await db.redis .multi() .hmset(key, tokenData) .sadd('tn:user:' + argv.user, tokenHash) .exec(); console.error('Generated access token for %s[%s]:', tokenData.user, tokenData.role); process.stdout.write(accessToken); console.error(''); process.exit(); } ) .command( 'delete ', 'Delete existing access token', yargs => yargs.option('token', { alias: 't', describe: 'Access token', demandOption: true }), async argv => { await dbconnect(); let accessToken = argv.token; let tokenHash = crypto .createHash('sha256') .update(accessToken) .digest('hex'); let key = 'tn:token:' + tokenHash; let tokenData = await db.redis.hgetall(key); if (tokenData) { await db.redis.del(key); if (tokenData.user) { await db.redis.srem('tn:user:' + tokenData.user, tokenHash); } console.error('Token deleted'); } else { console.error('Token not found'); } process.exit(); } ) .command( 'clear ', 'Delete all tokens for a user', yargs => yargs.option('user', { alias: 'u', describe: 'User to de-provision', demandOption: true }), async argv => { await dbconnect(); let user = argv.user; let tokens = await db.redis.smembers('tn:user:' + user); if (!tokens || !tokens.length) { console.error('No tokens found for %s', user); process.exit(); } let query = await db.redis.multi().del('tn:user:' + user); tokens.forEach(tokenHash => { query = query.del('tn:token:' + tokenHash); }); await query.exec(); console.error('Deleted %s tokens for %s', tokens.length, user); process.exit(); } ) .help().argv; if (argv) { // ignore }