updated config handler to autogenerate secrets and default user structure
Co-authored-by: Spacelord <Spacelord09@users.noreply.github.com>
This commit is contained in:
		@@ -1,5 +1,6 @@
 | 
			
		||||
import fs from 'node:fs';
 | 
			
		||||
import _ from 'lodash';
 | 
			
		||||
import { randomUUID, randomBytes } from 'crypto';
 | 
			
		||||
 | 
			
		||||
export type configObject = Record<any, any>;
 | 
			
		||||
 | 
			
		||||
@@ -14,6 +15,7 @@ export default class config {
 | 
			
		||||
	#configPath: string;
 | 
			
		||||
	//global = {[key: string] : string}
 | 
			
		||||
	global: configObject;
 | 
			
		||||
	replaceSecrets: boolean;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates an instance of config.
 | 
			
		||||
@@ -22,9 +24,10 @@ export default class config {
 | 
			
		||||
	 * @param {string} configPath Path to config file.
 | 
			
		||||
	 * @param {object} configPreset Default config object with default values.
 | 
			
		||||
	 */
 | 
			
		||||
	constructor(configPath: string, configPreset: object) {
 | 
			
		||||
	constructor(configPath: string, replaceSecrets: boolean, configPreset: object) {
 | 
			
		||||
		this.#configPath = configPath;
 | 
			
		||||
		this.global = configPreset;
 | 
			
		||||
		this.replaceSecrets = replaceSecrets;
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			// Read config
 | 
			
		||||
@@ -52,6 +55,12 @@ export default class config {
 | 
			
		||||
	 */
 | 
			
		||||
	save_config() {
 | 
			
		||||
		try {
 | 
			
		||||
			// If enabled replace tokens defines as "gen" with random token
 | 
			
		||||
			if (this.replaceSecrets) {
 | 
			
		||||
				// Replace tokens with value "gen"
 | 
			
		||||
				this.generate_secrets(this.global, 'gen')
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			fs.writeFileSync(this.#configPath, JSON.stringify(this.global, null, 8));
 | 
			
		||||
		} catch (err) {
 | 
			
		||||
			console.error(`Could not write config file at ${this.#configPath} due to: ${err}`);
 | 
			
		||||
@@ -59,31 +68,73 @@ export default class config {
 | 
			
		||||
		}
 | 
			
		||||
		console.log(`Successfully written config file to ${this.#configPath}`);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Replaces each item matching the value of placeholder with a random UUID.
 | 
			
		||||
	 * Thanks to https://stackoverflow.com/questions/8085004/iterate-through-nested-javascript-objects
 | 
			
		||||
	 * @param {configObject} obj
 | 
			
		||||
	 */
 | 
			
		||||
	generate_secrets(obj: configObject, placeholder: string) {
 | 
			
		||||
		const stack = [obj];
 | 
			
		||||
		while (stack?.length > 0) {
 | 
			
		||||
			const currentObj = stack.pop();
 | 
			
		||||
			Object.keys(currentObj).forEach((key) => {
 | 
			
		||||
 | 
			
		||||
				if (currentObj[key] === placeholder) {
 | 
			
		||||
					console.log('Generating secret: ' + key);
 | 
			
		||||
					currentObj[key] = randomBytes(48).toString('base64').replace(/\W/g, '');
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (typeof currentObj[key] === 'object' && currentObj[key] !== null) {
 | 
			
		||||
					stack.push(currentObj[key]);
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 | 
			
		||||
**** Example ****
 | 
			
		||||
 | 
			
		||||
import configHandler from './assets/configHandler.js';
 | 
			
		||||
import ConfigHandlerNG from './assets/configHandlerNG.js';
 | 
			
		||||
 | 
			
		||||
// Create a new config instance.
 | 
			
		||||
export const config = new ConfigHandler(__path + '/config.json', {
 | 
			
		||||
	db_connection_string: 'mysql://USER:PASSWORD@HOST:3306/DATABASE',
 | 
			
		||||
	http_listen_address: '127.0.0.1',
 | 
			
		||||
	http_port: 3000,
 | 
			
		||||
	sentry_dsn: 'https://ID@sentry.example.com/PROJECTID',
 | 
			
		||||
	debug: false
 | 
			
		||||
});
 | 
			
		||||
export const config = new ConfigHandler(__path + '/config.json', true, {
 | 
			
		||||
	test1: 't1',
 | 
			
		||||
	test2: 't2',
 | 
			
		||||
	test3: 'gen',
 | 
			
		||||
	test4: 't4',
 | 
			
		||||
	test5: 'gen',
 | 
			
		||||
	testObj: {
 | 
			
		||||
		local: {
 | 
			
		||||
			active: true,
 | 
			
		||||
			users: {
 | 
			
		||||
				user1: 'gen',
 | 
			
		||||
				user2: 'gen',
 | 
			
		||||
				user3: 'gen',
 | 
			
		||||
				user4: 'gen',
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		oidc: {
 | 
			
		||||
			active: false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
console.log('Base Config:');
 | 
			
		||||
console.log(config.global);
 | 
			
		||||
 | 
			
		||||
console.log('Add some new key to config and call save_config.');
 | 
			
		||||
console.log('Add some new key to config and call save_config().');
 | 
			
		||||
config.global.NewKey = 'ThisIsANewKey!'
 | 
			
		||||
config.save_config()
 | 
			
		||||
 | 
			
		||||
console.log('This will add a new key with value gen, but gen gets replaced with a random UUID when save_config() is called.');
 | 
			
		||||
config.global.someSecret = 'gen'
 | 
			
		||||
config.save_config() // global.someSecret is getting replaced with some random UUID since it was set to 'gen'.
 | 
			
		||||
 | 
			
		||||
console.log('Complete Config:');
 | 
			
		||||
console.log(config.global);
 | 
			
		||||
*/
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								src/index.ts
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								src/index.ts
									
									
									
									
									
								
							@@ -7,6 +7,7 @@ import * as eta from 'eta';
 | 
			
		||||
import bodyParser from 'body-parser';
 | 
			
		||||
import session from 'express-session';
 | 
			
		||||
import passport from 'passport';
 | 
			
		||||
import _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
// Sentry
 | 
			
		||||
import * as Sentry from '@sentry/node';
 | 
			
		||||
@@ -35,19 +36,18 @@ export const log = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Create a new config instance.
 | 
			
		||||
export const config = new ConfigHandler(__path + '/config.json', {
 | 
			
		||||
export const config = new ConfigHandler(__path + '/config.json', true, {
 | 
			
		||||
	db_connection_string: 'mysql://USER:PASSWORD@HOST:3306/DATABASE',
 | 
			
		||||
	http_listen_address: '127.0.0.1',
 | 
			
		||||
	http_port: 3000,
 | 
			
		||||
	sentry_dsn: 'https://ID@sentry.example.com/PROJECTID',
 | 
			
		||||
	debug: false,
 | 
			
		||||
	auth: {
 | 
			
		||||
		cookie_secret: 'gen',
 | 
			
		||||
		cookie_secure: true,
 | 
			
		||||
		local: {
 | 
			
		||||
			active: true,
 | 
			
		||||
			users: {
 | 
			
		||||
				user: 'password',
 | 
			
		||||
				user1: 'password'
 | 
			
		||||
			}
 | 
			
		||||
			users: {}
 | 
			
		||||
		},
 | 
			
		||||
		oidc: {
 | 
			
		||||
			active: false
 | 
			
		||||
@@ -55,6 +55,15 @@ export const config = new ConfigHandler(__path + '/config.json', {
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// If no local User exists, create the default with a generated password
 | 
			
		||||
if (_.isEqual(config.global.auth.local.users, {})) {
 | 
			
		||||
	config.global.auth.local.users = {
 | 
			
		||||
		'flowAdmin': 'gen',
 | 
			
		||||
	};
 | 
			
		||||
	config.save_config();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// TODO: Add errorhandling with some sort of message.
 | 
			
		||||
export const prisma = new PrismaClient({
 | 
			
		||||
	datasources: {
 | 
			
		||||
@@ -113,10 +122,10 @@ app.use(bodyParser.json());
 | 
			
		||||
// TODO: Move secret to config -> Autogenerate.
 | 
			
		||||
app.use(
 | 
			
		||||
	session({
 | 
			
		||||
		secret: 'keyboard cat',
 | 
			
		||||
		secret: config.global.auth.cookie_secret,
 | 
			
		||||
		resave: false,
 | 
			
		||||
		saveUninitialized: false,
 | 
			
		||||
		cookie: { secure: false }
 | 
			
		||||
		cookie: { secure: config.global.auth.cookie_secure }
 | 
			
		||||
	})
 | 
			
		||||
);
 | 
			
		||||
app.use(passport.authenticate('session'));
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user