From ec10e8dc1e51092228e9dee69c4a797aa159118d Mon Sep 17 00:00:00 2001 From: jash Date: Sun, 21 Oct 2018 16:15:01 +0200 Subject: [PATCH] added support for api key generation --- .../generators/app/index.js | 61 ++++++++++++++- .../generators/app/lib/apikey.js | 76 +++++++++++++++++++ .../generators/app/prompters/010_authapi.js | 37 +++++++++ .../templates/authentication/keys.properties | 1 + 4 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 install/generator-cyphernode/generators/app/lib/apikey.js create mode 100644 install/generator-cyphernode/generators/app/prompters/010_authapi.js create mode 100644 install/generator-cyphernode/generators/app/templates/authentication/keys.properties diff --git a/install/generator-cyphernode/generators/app/index.js b/install/generator-cyphernode/generators/app/index.js index 8e97764..129d8ad 100644 --- a/install/generator-cyphernode/generators/app/index.js +++ b/install/generator-cyphernode/generators/app/index.js @@ -8,6 +8,7 @@ const path = require("path"); const featureChoices = require(path.join(__dirname, "features.json")); const coinstring = require('coinstring'); const Archive = require('./lib/archive.js'); +const ApiKey = require('./lib/apikey.js'); const uaCommentRegexp = /^[a-zA-Z0-9 \.,:_\-\?\/@]+$/; // TODO: look for spec of unsafe chars const userRegexp = /^[a-zA-Z0-9\._\-]+$/; @@ -163,6 +164,9 @@ module.exports = class extends Generator { return; } + // save auth key password to check if it changed + this.auth_clientkeyspassword = this.props.auth_clientkeyspassword; + let prompts = []; for( let m of prompters ) { prompts = prompts.concat(m.prompts(this)); @@ -173,12 +177,46 @@ module.exports = class extends Generator { }); } - writing() { - const configJsonString = JSON.stringify(this.props); + + async configuring() { + if( this.props.auth_recreatekeys || !this.props.auth_keys ) { + delete this.props.auth_recreatekeys; + const apikey = new ApiKey(); + + let configEntries = []; + let clientInformation = []; + + apikey.setId('001'); + apikey.setGroups(['watcher']); + await apikey.randomiseKey(); + configEntries.push(apikey.getConfigEntry()); + clientInformation.push(apikey.getClientInformation()); + + apikey.setId('002'); + apikey.setGroups(['watcher','spender']); + await apikey.randomiseKey(); + configEntries.push(apikey.getConfigEntry()); + clientInformation.push(apikey.getClientInformation()); + + apikey.setId('003'); + apikey.setGroups(['watcher','spender','admin']); + await apikey.randomiseKey(); + configEntries.push(apikey.getConfigEntry()); + clientInformation.push(apikey.getClientInformation()); + + this.props.auth_keys = { + configEntries: configEntries, + clientInformation: clientInformation + } + } + } + + async writing() { + const configJsonString = JSON.stringify(this.props, null, 4); const archive = new Archive( this.destinationPath('config.7z'), this.configurationPassword ); if( !archive.writeEntry( 'config.json', configJsonString ) ) { - console.log(chalk.bold.red( 'error! config archive was not written' )); + console.log(chalk.bold.red( 'error! Config archive was not written' )); } for( let m of prompters ) { @@ -192,6 +230,19 @@ module.exports = class extends Generator { ); } } + + if( this.props.auth_keys && this.props.auth_keys.clientInformation ) { + + if( this.auth_clientkeyspassword !== this.props.auth_clientkeyspassword ) { + fs.unlinkSync( this.destinationPath('clientKeys.7z') ); + } + + const archive = new Archive( this.destinationPath('clientKeys.7z'), this.props.auth_clientkeyspassword ); + if( !archive.writeEntry( 'keys.txt', this.props.auth_keys.clientInformation.join('\n') ) ) { + console.log(chalk.bold.red( 'error! Client auth key archive was not written' )); + } + } + } install() { @@ -199,6 +250,10 @@ module.exports = class extends Generator { /* some utils */ + _clientAuthKeysArchiveExists() { + return fs.existsSync( this.destinationPath('clientKeys.7z') ); + } + _assignConfigDefaults( props ) { props.derivation_path = this.props.derivation_path || '0/n'; props.installer = this.props.installer ||  'docker'; diff --git a/install/generator-cyphernode/generators/app/lib/apikey.js b/install/generator-cyphernode/generators/app/lib/apikey.js new file mode 100644 index 0000000..602d1ed --- /dev/null +++ b/install/generator-cyphernode/generators/app/lib/apikey.js @@ -0,0 +1,76 @@ +const spawn = require('child_process').spawn; + +module.exports = class ApiKey { + constructor( id, groups, key, script ) { + this.setId(id || '001'); + this.setGroups(groups || ['admin'] ); + this.setScript(script || 'eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}' ); + this.setKey(key); + } + + setGroups( groups ) { + this.groups = groups; + } + + setId( id ) { + this.id = id; + } + + setScript( script ) { + this.script = script; + } + + setKey( key ) { + this.key = key; + } + + async randomiseKey() { + try { + + //const dd = spawn('/bin/dd if=/dev/urandom bs=32 count=1 | /usr/bin/xxd -pc 32'); + const dd = spawn("dd if=/dev/urandom bs=32 count=1 | xxd -pc32", [], {stdio: ['ignore', 'pipe', 'ignore' ], shell: true} ); + + const result = await new Promise( function(resolve, reject ) { + + let result = ''; + dd.stdout.on('data', function( a,b,c) { + let chunk = a.toString().trim(); + result += chunk; + }); + + dd.stdout.on('end', function() { + result = result.replace(/[^a-zA-Z0-9]/,''); + resolve(result); + }); + + dd.stdout.on('error', function(err) { + console.log(err); + reject(err); + }) + }); + this.key = result; + + } catch( err ) { + console.log( err ); + return; + } + } + + getKey() { + return this.key; + } + + getConfigEntry() { + if( !this.key ) { + return; + } + return `kapi_id="${this.id}";kapi_key="${this.key}";kapi_groups="${this.groups.join(',')}";${this.script}`; + } + + getClientInformation() { + return `${this.id}=${this.key}`; + } + +} + +//dd if=/dev/urandom bs=32 count=1 2> /dev/null | xxd -pc 32 \ No newline at end of file diff --git a/install/generator-cyphernode/generators/app/prompters/010_authapi.js b/install/generator-cyphernode/generators/app/prompters/010_authapi.js new file mode 100644 index 0000000..3c0ac93 --- /dev/null +++ b/install/generator-cyphernode/generators/app/prompters/010_authapi.js @@ -0,0 +1,37 @@ +const chalk = require('chalk'); + +const name = 'authentication'; + +const capitalise = function( txt ) { + return txt.charAt(0).toUpperCase() + txt.substr(1); +}; + +const prefix = function() { + return chalk.bold.red(capitalise(name)+': '); +}; + +module.exports = { + name: function() { + return name; + }, + prompts: function( utils ) { + // TODO: delete clientKeys archive when password chnages + return [{ + type: 'password', + name: 'auth_clientkeyspassword', + default: utils._getDefault( 'auth_clientkeyspassword' ), + message: prefix()+'Enter a password to protect your client keys with'+'\n', + filter: utils._trimFilter, + validate: utils._notEmptyValidator + }, + { + type: 'confirm', + name: 'auth_recreatekeys', + default: false, + message: prefix()+'Recreate auth keys?'+'\n' + }]; + }, + templates: function( props ) { + return [ 'keys.properties' ]; + } +}; \ No newline at end of file diff --git a/install/generator-cyphernode/generators/app/templates/authentication/keys.properties b/install/generator-cyphernode/generators/app/templates/authentication/keys.properties new file mode 100644 index 0000000..8144e69 --- /dev/null +++ b/install/generator-cyphernode/generators/app/templates/authentication/keys.properties @@ -0,0 +1 @@ +<%- auth_keys.configEntries.join('\n') %>