Compare commits

..

No commits in common. "c23b1b306c04c36c930c3e2e2e901def51bbc163" and "6fa27979032be09025fa176a17b5cc12b7873c5f" have entirely different histories.

5 changed files with 45 additions and 100 deletions

View File

@ -1,6 +1,5 @@
import fs from 'node:fs'; import fs from 'node:fs';
import _ from 'lodash'; import _ from 'lodash';
import { randomUUID, randomBytes } from 'crypto';
export type configObject = Record<any, any>; export type configObject = Record<any, any>;
@ -15,7 +14,6 @@ export default class config {
#configPath: string; #configPath: string;
//global = {[key: string] : string} //global = {[key: string] : string}
global: configObject; global: configObject;
replaceSecrets: boolean;
/** /**
* Creates an instance of config. * Creates an instance of config.
@ -24,10 +22,9 @@ export default class config {
* @param {string} configPath Path to config file. * @param {string} configPath Path to config file.
* @param {object} configPreset Default config object with default values. * @param {object} configPreset Default config object with default values.
*/ */
constructor(configPath: string, replaceSecrets: boolean, configPreset: object) { constructor(configPath: string, configPreset: object) {
this.#configPath = configPath; this.#configPath = configPath;
this.global = configPreset; this.global = configPreset;
this.replaceSecrets = replaceSecrets;
try { try {
// Read config // Read config
@ -55,12 +52,6 @@ export default class config {
*/ */
save_config() { save_config() {
try { 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)); fs.writeFileSync(this.#configPath, JSON.stringify(this.global, null, 8));
} catch (err) { } catch (err) {
console.error(`Could not write config file at ${this.#configPath} due to: ${err}`); console.error(`Could not write config file at ${this.#configPath} due to: ${err}`);
@ -68,73 +59,31 @@ export default class config {
} }
console.log(`Successfully written config file to ${this.#configPath}`); 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 **** **** Example ****
import ConfigHandlerNG from './assets/configHandlerNG.js'; import configHandler from './assets/configHandler.js';
// Create a new config instance. // Create a new config instance.
export const config = new ConfigHandler(__path + '/config.json', true, { export const config = new ConfigHandler(__path + '/config.json', {
test1: 't1', db_connection_string: 'mysql://USER:PASSWORD@HOST:3306/DATABASE',
test2: 't2', http_listen_address: '127.0.0.1',
test3: 'gen', http_port: 3000,
test4: 't4', sentry_dsn: 'https://ID@sentry.example.com/PROJECTID',
test5: 'gen', debug: false
testObj: {
local: {
active: true,
users: {
user1: 'gen',
user2: 'gen',
user3: 'gen',
user4: 'gen',
}
},
oidc: {
active: false
}
}
}); });
console.log('Base Config:'); console.log('Base Config:');
console.log(config.global); 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.global.NewKey = 'ThisIsANewKey!'
config.save_config() 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('Complete Config:');
console.log(config.global); console.log(config.global);
*/ */

View File

@ -7,7 +7,6 @@ import * as eta from 'eta';
import bodyParser from 'body-parser'; import bodyParser from 'body-parser';
import session from 'express-session'; import session from 'express-session';
import passport from 'passport'; import passport from 'passport';
import _ from 'lodash';
// Sentry // Sentry
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
@ -36,18 +35,19 @@ export const log = {
}; };
// Create a new config instance. // Create a new config instance.
export const config = new ConfigHandler(__path + '/config.json', true, { export const config = new ConfigHandler(__path + '/config.json', {
db_connection_string: 'mysql://USER:PASSWORD@HOST:3306/DATABASE', db_connection_string: 'mysql://USER:PASSWORD@HOST:3306/DATABASE',
http_listen_address: '127.0.0.1', http_listen_address: '127.0.0.1',
http_port: 3000, http_port: 3000,
sentry_dsn: 'https://ID@sentry.example.com/PROJECTID', sentry_dsn: 'https://ID@sentry.example.com/PROJECTID',
debug: false, debug: false,
auth: { auth: {
cookie_secret: 'gen',
cookie_secure: true,
local: { local: {
active: true, active: true,
users: {} users: {
user: 'password',
user1: 'password'
}
}, },
oidc: { oidc: {
active: false active: false
@ -55,15 +55,6 @@ export const config = new ConfigHandler(__path + '/config.json', true, {
} }
}); });
// 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. // TODO: Add errorhandling with some sort of message.
export const prisma = new PrismaClient({ export const prisma = new PrismaClient({
datasources: { datasources: {
@ -122,10 +113,10 @@ app.use(bodyParser.json());
// TODO: Move secret to config -> Autogenerate. // TODO: Move secret to config -> Autogenerate.
app.use( app.use(
session({ session({
secret: config.global.auth.cookie_secret, secret: 'keyboard cat',
resave: false, resave: false,
saveUninitialized: false, saveUninitialized: false,
cookie: { secure: config.global.auth.cookie_secure } cookie: { secure: false }
}) })
); );
app.use(passport.authenticate('session')); app.use(passport.authenticate('session'));

View File

@ -1,4 +1,5 @@
export function checkAuthentication(req: any, res: any, next: Function) { /*
function checkAuthentication(req: any, res: any, next: Function) {
if (req.isAuthenticated()) { if (req.isAuthenticated()) {
//req.isAuthenticated() will return true if user is logged in //req.isAuthenticated() will return true if user is logged in
next(); next();
@ -7,15 +8,16 @@ export function checkAuthentication(req: any, res: any, next: Function) {
} }
} }
// const checkIsInRole = (...roles) => (req, res, next) => { const checkIsInRole = (...roles) => (req, res, next) => {
// if (!req.user) { if (!req.user) {
// return res.redirect('/login') return res.redirect('/login')
// } }
// const hasRole = roles.find(role => req.user.role === role) const hasRole = roles.find(role => req.user.role === role)
// if (!hasRole) { if (!hasRole) {
// return res.redirect('/login') return res.redirect('/login')
// } }
// return next() return next()
// } }
*/

View File

@ -3,9 +3,6 @@ import { Strategy as LocalStrategy } from 'passport-local';
import express, { Request, Response } from 'express'; import express, { Request, Response } from 'express';
import { prisma, __path, log, config, app } from '../../index.js'; import { prisma, __path, log, config, app } from '../../index.js';
// Middleware Imports
import { checkAuthentication } from '../../middleware/auth.mw.js'
/* Configure password authentication strategy. /* Configure password authentication strategy.
* *
* The `LocalStrategy` authenticates users by verifying a username and password. * The `LocalStrategy` authenticates users by verifying a username and password.
@ -25,7 +22,7 @@ passport.use(
//log.auth.debug('Loop(REQ):', username, password); //log.auth.debug('Loop(REQ):', username, password);
//log.auth.debug('Loop(CFG):', user, pass); //log.auth.debug('Loop(CFG):', user, pass);
if (user.toLowerCase() === username.toLowerCase() && pass === password) { if (user === username && pass === password) {
log.auth.debug('LocalStrategy: success'); log.auth.debug('LocalStrategy: success');
return cb(null, { username: username }); // This is the user object. return cb(null, { username: username }); // This is the user object.
} }
@ -59,8 +56,8 @@ passport.use(
*/ */
passport.serializeUser(function (user: any, cb) { passport.serializeUser(function (user: any, cb) {
process.nextTick(function () { process.nextTick(function () {
// log.auth.debug('Called seriealizeUser'); log.auth.debug('Called seriealizeUser');
// log.auth.debug('user:', user); log.auth.debug('user:', user);
return cb(null, { return cb(null, {
username: user.username username: user.username
}); });
@ -69,7 +66,7 @@ passport.serializeUser(function (user: any, cb) {
passport.deserializeUser(function (user, cb) { passport.deserializeUser(function (user, cb) {
process.nextTick(function () { process.nextTick(function () {
// log.auth.debug('Called deseriealizeUser'); log.auth.debug('Called deseriealizeUser');
return cb(null, user); return cb(null, user);
}); });
}); });
@ -88,3 +85,12 @@ Router.route('/login').post(passport.authenticate('local', { successRedirect: '/
Router.route('/test').get(checkAuthentication, testRoute.get); Router.route('/test').get(checkAuthentication, testRoute.get);
export default Router; export default Router;
function checkAuthentication(req: Request, res: Response, next: Function) {
if (req.isAuthenticated()) {
//req.isAuthenticated() will return true if user is logged in
next();
} else {
res.redirect('/auth/login');
}
}

View File

@ -2,9 +2,6 @@ import express, { Express } from 'express';
import { __path, prisma } from '../index.js'; import { __path, prisma } from '../index.js';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
// Middleware Imports
import { checkAuthentication } from '../middleware/auth.mw.js'
// Route imports // Route imports
import frontend_routes from './frontend/index.js'; import frontend_routes from './frontend/index.js';
import static_routes from './static/index.js'; import static_routes from './static/index.js';
@ -14,9 +11,9 @@ import auth_routes from './auth/index.js';
const Router = express.Router({ strict: false }); const Router = express.Router({ strict: false });
Router.use('/static', static_routes); Router.use('/static', static_routes);
Router.use('/api', checkAuthentication, api_routes); Router.use('/api', api_routes);
Router.use('/auth', auth_routes); Router.use('/auth', auth_routes);
Router.use('/', checkAuthentication, frontend_routes); Router.use('/', frontend_routes);
// The error handler must be before any other error middleware and after all controllers // The error handler must be before any other error middleware and after all controllers
Router.use(Sentry.Handlers.errorHandler()); Router.use(Sentry.Handlers.errorHandler());