diff --git a/models/user.js b/models/user.js index 2e7d58e..d2c2a24 100644 --- a/models/user.js +++ b/models/user.js @@ -34,6 +34,11 @@ const structure = { gpgkey: { type: DataTypes.TEXT, allowNull: false + }, + accessLevel: { + type: DataTypes.SMALLINT, + allowNull: false, + defaultValue: 0 } } User.structure = structure diff --git a/scripts/create_admin_account.js b/scripts/create_admin_account.js new file mode 100644 index 0000000..d780fba --- /dev/null +++ b/scripts/create_admin_account.js @@ -0,0 +1,117 @@ +process.env.DEBUG = undefined; + +const db = require('../models'); +const args = require('args-parser')(process.argv); +const { authenticator } = require('otplib'); +const path = require('path'); +const crypto = require('crypto'); +const qrcode = require('qrcode-terminal'); +const prompt = require('prompt-sync')({ sigint: true, eot: true }); +const bcrypt = require('bcrypt') + +if (args['help']) { + console.log(path.basename(__filename) + ' [--pass] [--login] [--email] [--gpgkey] [--dry-run] [--no-interactive|-n] [--totp] [--set-totp] [--set-totp-recovery]'); + console.log( + ' --pass: Set password.\n' + + ' --login: Set login.\n' + + ' --email: Set email.\n' + + ' --gpgkey: Set path to your GPG key.\n' + + ' --dry-run: Don\'t do anything, but simulate like something is done.\n' + + ' --no-interactive -n: Disable prompts. Designed for script usage.\n' + + ' --totp: Add TOTP to the account. QR code and recovery codes would be printed to terminal.\n' + + ' --set-totp: Set TOTP key. Designed for script usage.\n' + + ' --set-totp-recovery: Set TOTP recovery keys (comma separated).' + ) + process.exit(0); +} + +function exit_error(error, exitCode) { + if (!exitCode) exitCode = -1; + console.error(error); + process.exit(exitCode); +} + +let interactive = true; +if (args['no-interactive']) interactive = false; + +// get data +let password, login, email, gpgkey; +let totpkey, totprecover, totpurl; +if (args['pass']) { + password = args['pass']; +} else if (interactive) { + // node does not provide a way to + // get a password with hidden stdin + password = prompt('Password: '); + console.clear(); +} else exit_error('No password supplied.'); + +if (args['login']) { + login = args['login']; +} else if (interactive) { + login = prompt('Login: '); +} else exit_error('No login supplied.'); + +if (args['email']) { + email = args['email']; +} else if (interactive) { + email = prompt('Email: '); +} else exit_error('No email supplied.'); + +if (args['gpgkey']) { + gpgkey = require('fs').readFileSync(args['gpgkey'], {encoding: 'utf-8'}).toString(); +} else if (interactive) { + gpgkey = prompt('Paste your GPG key:\n'); +} else exit_error('No GPG key supplied.') + +if (args['totp']) { + totpkey = authenticator.generateSecret(32); + totprecover = []; + for (let i = 0; i != 6; i++) { + totprecover[i] = crypto.randomInt(11111111, 99999999); + } + + totpkey = args['set-totp'] ? args['set-totp'] : totpkey; + totprecover = args['set-totp-recovery'] ? args['set-totp-recovery'].split(',') : totprecover; + + totpurl = `otpauth://totp/blek/${login}@blek.codes?secret=${totpkey}&issuer=blek`; + qrcode.generate(totpurl); + console.log('Totp URL: ' + totpurl); + console.log('Recovery keys:'); + for (let i = 0; i <= totprecover.length - 2; i+= 2) { + console.log(' ' + totprecover[i] + ', ' + totprecover[i+1]); + } +} +password = '' + password; + +// insert data +if (args['dry-run']) { + console.log('All done'); + process.exit(0); +} + +const salt = bcrypt.genSaltSync(12); +const pass = bcrypt.hashSync(password, salt); + + +async function create() { + const data = await db.User.findAndCountAll({where: {login}}); + if (data.count !== 0) { + console.error('User with this login already exists.'); + process.exit(0); + } + + const user = await db.User.create({ + login, + email, + pass, + accessLevel: 4, + gpgkey, + totp: totpkey, + totpRec: totprecover ? totprecover.join(',') : undefined + }); + console.log('All done'); + process.exit(0); +} + +create(); \ No newline at end of file diff --git a/scripts/package.json b/scripts/package.json index c43407b..63dfbb8 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -9,6 +9,8 @@ "author": "", "license": "ISC", "dependencies": { - "args-parser": "^1.3.0" + "args-parser": "^1.3.0", + "prompt-sync": "^4.2.0", + "qrcode-terminal": "^0.12.0" } }