wildduck/bin/access-tokens
2019-07-08 18:14:55 +02:00

152 lines
4.2 KiB
JavaScript
Executable file

#!/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 <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 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
}