2018-08-25 14:38:20 +08:00
|
|
|
#!/usr/bin/env node
|
|
|
|
|
2018-08-29 18:15:38 +08:00
|
|
|
/* eslint no-console: 0*/
|
|
|
|
|
2018-08-25 14:38:20 +08:00
|
|
|
'use strict';
|
|
|
|
|
2018-08-28 14:42:42 +08:00
|
|
|
// FUTURE FEATURE
|
|
|
|
// this executable should generate and dispose access tokens for the API
|
|
|
|
|
2018-09-28 16:30:30 +08:00
|
|
|
const pathlib = require('path');
|
|
|
|
process.env.NODE_CONFIG_DIR = pathlib.join(__dirname, '..', 'config');
|
|
|
|
|
2018-08-25 14:38:20 +08:00
|
|
|
const db = require('../lib/db');
|
2018-08-29 18:15:38 +08:00
|
|
|
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 <command> [options]')
|
|
|
|
.command(
|
|
|
|
'provision <user> <role>',
|
|
|
|
'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 <token>',
|
|
|
|
'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 <user>',
|
|
|
|
'Delete all tokens for an 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();
|
|
|
|
}
|
|
|
|
|
2018-11-23 15:53:22 +08:00
|
|
|
let query = await db.redis.multi().del('tn:user:' + user);
|
2018-08-29 18:15:38 +08:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|