signalling server update

This commit is contained in:
2023-05-19 01:59:50 +03:00
parent a0a4ca1cc0
commit d3260522f4
95 changed files with 0 additions and 0 deletions
@@ -0,0 +1,2 @@
// Copyright Epic Games, Inc. All Rights Reserved.
exports.users = require('./users');
@@ -0,0 +1,80 @@
// Copyright Epic Games, Inc. All Rights Reserved.
//
// Usage: npm run store_password -- --username <USERNAME> --password <PASSWORD>
// or from ./modules/authentication/db dir: node store_password.js --username <USERNAME> --password <PASSWORD>
//
// --usersFile is an optional parameter that can be used to specify a different location for the users database file
// use this if running the command from a different working dir. The default location is './users.json'
// e.g. If running from the SignallingWebServer dir use: --usersFile ./modules/authentication/db/users.json
const argv = require('yargs').argv;
const fs = require('fs');
const bcrypt = require('bcryptjs');
var username, password;
var usersFile = './users.json'
const STORE_PLAINTEXT_PASSWORD = false;
try {
if(typeof argv.username != 'undefined'){
username = argv.username.toString();
}
if(typeof argv.password != 'undefined'){
password = argv.password;
}
if(typeof argv.usersFile != 'undefined'){
usersFile = argv.usersFile;
}
} catch (e) {
console.error(e);
process.exit(2);
}
if(username && password){
let existingAccounts = [];
if (fs.existsSync(usersFile)) {
console.log(`File '${usersFile}' exists, reading file`)
var content = fs.readFileSync(usersFile, 'utf8');
try{
existingAccounts = JSON.parse(content);
}
catch(e){
console.error(`Existing file '${usersFile}', has invalid JSON: ${e}`);
}
}
var existingUser = existingAccounts.find( u => u.username == username)
if(existingUser){
console.log(`User '${username}', already exists, updating password`)
existingUser.passwordHash = generatePasswordHash(password)
if(STORE_PLAINTEXT_PASSWORD)
existingUser.password = password;
else if (existingUser.password)
delete existingUser.password;
} else {
console.log(`Adding new user '${username}'`)
let newUser = {
id: existingAccounts.length + 1,
username: username,
passwordHash: generatePasswordHash(password)
}
if(STORE_PLAINTEXT_PASSWORD)
newUser.password = password;
existingAccounts.push(newUser);
}
console.log(`Writing updated users to '${usersFile}'`);
var newContent = JSON.stringify(existingAccounts);
fs.writeFileSync(usersFile, newContent);
} else {
console.log(`Please pass in both username (${username}) and password (${password}) please`);
}
function generatePasswordHash(pass){
return bcrypt.hashSync(pass, 12)
}
@@ -0,0 +1,35 @@
// Copyright Epic Games, Inc. All Rights Reserved.
const fs = require('fs');
const path = require('path');
// Read in users from file
let records = [];
let usersFile = path.join(__dirname, './users.json');
if (fs.existsSync(usersFile)) {
console.log(`Reading users from '${usersFile}'`)
var content = fs.readFileSync(usersFile, 'utf8');
try {
records = JSON.parse(content);
} catch(e) {
console.log(`ERROR: Failed to parse users from file '${usersFile}'`)
}
}
exports.findById = function(id, cb) {
var idx = id - 1;
if (records[idx]) {
cb(null, records[idx]);
} else {
cb(new Error('User ' + id + ' does not exist'));
}
}
exports.findByUsername = function(username, cb) {
for (var i = 0, len = records.length; i < len; i++) {
var record = records[i];
if (record.username === username) {
return cb(null, record);
}
}
return cb(null, null);
}
@@ -0,0 +1,4 @@
// Copyright Epic Games, Inc. All Rights Reserved.
module.exports = {
init: require('./init')
}
@@ -0,0 +1,109 @@
// Copyright Epic Games, Inc. All Rights Reserved.
// Adapted from
// * https://blog.risingstack.com/node-hero-node-js-authentication-passport-js/
// * https://github.com/RisingStack/nodehero-authentication/tree/master/app
// * https://github.com/passport/express-4.x-local-example
const passport = require('passport');
const session = require('express-session');
const bcrypt = require('bcryptjs');
const LocalStrategy = require('passport-local').Strategy;
const path = require('path');
const fs = require('fs');
var db = require('./db');
function initPassport (app) {
// Generate session secret if it doesn't already exist and save it to file for use next time
let config = {};
let configPath = path.join(__dirname, './config.json');
if (fs.existsSync(configPath)) {
let content = fs.readFileSync(configPath, 'utf8');
try {
config = JSON.parse(content);
} catch (e) {
console.log(`Error with config file '${configPath}': ${e}`);
}
}
if(!config.sessionSecret){
config.sessionSecret = bcrypt.genSaltSync(12);
let content = JSON.stringify(config);
fs.writeFileSync(configPath, content);
}
// Setup session id settings
app.use(session({
secret: config.sessionSecret,
resave: false,
saveUninitialized: false,
cookie: {
secure: true,
maxAge: 24 * 60 * 60 * 1000 /* 1 day */
//maxAge: 5 * 1000 /* 5 seconds */
}
}));
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, cb) {
cb(null, user.id);
});
passport.deserializeUser(function(id, cb) {
db.users.findById(id, function (err, user) {
if (err) { return cb(err); }
cb(null, user);
});
});
console.log('Setting up auth');
passport.use(new LocalStrategy(
(username, password, callback) => {
db.users.findByUsername(username, (err, user) => {
if (err) {
console.log(`Unable to login '${username}', error ${err}`);
return callback(err);
}
// User not found
if (!user) {
console.log(`User '${username}' not found`);
return callback(null, false);
}
// Always use hashed passwords and fixed time comparison
bcrypt.compare(password, user.passwordHash, (err, isValid) => {
if (err) {
console.log(`Error comparing password for user '${username}': ${err}`);
return callback(err);
}
if (!isValid) {
console.log(`Password incorrect for user '${username}'`)
return callback(null, false);
}
console.log(`User '${username}' logged in`);
return callback(null, user);
});
})
}
));
passport.authenticationMiddleware = function authenticationMiddleware (redirectUrl) {
return function (req, res, next) {
if (req.isAuthenticated()) {
return next();
}
// Set redirectTo property so that user can be redirected back there after logging in
//console.log(`Original request path '${req.originalUrl}'`);
req.session.redirectTo = req.originalUrl;
res.redirect(redirectUrl);
}
}
}
module.exports = initPassport;
+56
View File
@@ -0,0 +1,56 @@
// Copyright Epic Games, Inc. All Rights Reserved.
//-- Provides configuration information from file and combines it with default values and command line arguments --//
//-- Hierachy of values: Default Values < Config File < Command Line arguments --//
const fs = require('fs');
const path = require('path');
const argv = require('yargs').argv;
function initConfig(configFile, defaultConfig){
defaultConfig = defaultConfig || {};
// Using object spread syntax: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals
let config = {...defaultConfig};
try {
let configData = fs.readFileSync(configFile, 'UTF8');
fileConfig = JSON.parse(configData);
config = {...config, ...fileConfig}
try {
accessSync('configFile', constants.W_OK);
// Update config file with any additional defaults (does not override existing values if default has changed)
fs.writeFileSync(configFile, JSON.stringify(config, null, '\t'), 'UTF8');
} catch (err) {
console.log("Config file is readonly, skipping writing config...");
}
} catch(err) {
if (err.code === 'ENOENT') {
console.log("No config file found, writing defaults to log file " + configFile);
fs.writeFileSync(configFile, JSON.stringify(config, null, '\t'), 'UTF8');
} else if (err instanceof SyntaxError) {
console.log(`ERROR: Invalid JSON in ${configFile}, ignoring file config, ${err}`)
} else {
console.log(`ERROR: ${err}`);
}
}
try {
//Make a copy of the command line args and remove the unneccessary ones
//The _ value is an array of any elements without a key
let commandLineConfig = {...argv}
delete commandLineConfig._;
delete commandLineConfig.help;
delete commandLineConfig.version;
delete commandLineConfig['$0'];
config = {...config, ...commandLineConfig}
} catch(err) {
console.log(`ERROR: ${err}`);
}
return config;
}
module.exports = {
init: initConfig
}
@@ -0,0 +1,95 @@
// Copyright Epic Games, Inc. All Rights Reserved.
var querystring = require('querystring')
const https = require('https');
const assert = require('assert');
function cleanUrl(aUrl){
let url = aUrl;
if(url.startsWith("https://"))
url = url.substring("https://".length);
return url
}
function createOptions(requestType, url){
let index = url.indexOf('/');
let urlParts = url.split('/', 2)
return {
hostname: (index === -1) ? url.substring(0) : url.substring(0, index),
port: 443,
path: (index === -1) ? '' : url.substring(index),
method: requestType,
timeout: 30000,
};
}
function makeHttpsCall(options, aCallback, aError){
//console.log(JSON.stringify(options));
const req = https.request(options, function(response){
let data = '';
//console.log('statusCode:', response.statusCode);
//console.log('headers:', response.headers);
// A chunk of data has been received.
response.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
response.on('end', () => {
if(typeof aCallback != "undefined")
aCallback(response, data);
});
});
req.on('timeout', function () {
console.log("Request timed out. " + (options.timeout / 1000) + " seconds expired");
// Source: https://github.com/nodejs/node/blob/master/test/parallel/test-http-client-timeout-option.js#L27
req.destroy();
});
req.on("error", (err) => {
if(typeof aError != "undefined") {
aError(err);
} else {
console.log("Error: " + err.message);
}
});
return req;
}
module.exports = class HttpClient {
get(aUrl, aCallback, aError) {
let url = cleanUrl(aUrl);
let options = createOptions('GET', url);
const req = makeHttpsCall(options, aCallback, aError);
req.end();
}
post(aUrl, body, aCallback, aError) {
let url = cleanUrl(aUrl);
let options = createOptions('POST', url);
let postBody = querystring.stringify(body);
//Add extra options for POST request type
options.headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': postBody.length
};
const req = makeHttpsCall(options, aCallback, aError);
req.write(postBody);
req.end();
}
}
+108
View File
@@ -0,0 +1,108 @@
// Copyright Epic Games, Inc. All Rights Reserved.
const fs = require('fs');
const { Console } = require('console');
var loggers=[];
var logFunctions=[];
var logColorFunctions=[];
console.log = function(msg, ...args) {
logFunctions.forEach((logFunction) => {
logFunction(msg, ...args);
});
}
console.logColor = function(color, msg, ...args) {
logColorFunctions.forEach((logColorFunction) => {
logColorFunction(color, msg, ...args);
});
}
const AllAttributesOff = '\x1b[0m';
const BoldOn = '\x1b[1m';
const Black = '\x1b[30m';
const Red = '\x1b[31m';
const Green = '\x1b[32m';
const Yellow = '\x1b[33m';
const Blue = '\x1b[34m';
const Magenta = '\x1b[35m';
const Cyan = '\x1b[36m';
const White = '\x1b[37m';
/**
* Pad the start of the given number with zeros so it takes up the number of digits.
* e.g. zeroPad(5, 3) = '005' and zeroPad(23, 2) = '23'.
*/
function zeroPad(number, digits) {
let string = number.toString();
while (string.length < digits) {
string = '0' + string;
}
return string;
}
/**
* Create a string of the form 'YEAR.MONTH.DATE.HOURS.MINUTES.SECONDS'.
*/
function dateTimeToString() {
let date = new Date();
return `${date.getFullYear()}.${zeroPad(date.getMonth(), 2)}.${zeroPad(date.getDate(), 2)}.${zeroPad(date.getHours(), 2)}.${zeroPad(date.getMinutes(), 2)}.${zeroPad(date.getSeconds(), 2)}`;
}
/**
* Create a string of the form 'HOURS.MINUTES.SECONDS.MILLISECONDS'.
*/
function timeToString() {
let date = new Date();
return `${zeroPad(date.getHours(), 2)}:${zeroPad(date.getMinutes(), 2)}:${zeroPad(date.getSeconds(), 2)}.${zeroPad(date.getMilliseconds(), 3)}`;
}
function RegisterFileLogger(path) {
if(path == null)
path = './logs/';
if (!fs.existsSync(path))
fs.mkdirSync(path);
var output = fs.createWriteStream(`${path}${dateTimeToString()}.log`);
var fileLogger = new Console(output);
logFunctions.push(function(msg, ...args) {
fileLogger.log(`${timeToString()} ${msg}`, ...args);
});
logColorFunctions.push(function(color, msg, ...args) {
fileLogger.log(`${timeToString()} ${msg}`, ...args);
});
loggers.push(fileLogger);
}
function RegisterConsoleLogger() {
var consoleLogger = new Console(process.stdout, process.stderr)
logFunctions.push(function(msg, ...args) {
consoleLogger.log(`${timeToString()} ${msg}`, ...args);
});
logColorFunctions.push(function(color, msg, ...args) {
consoleLogger.log(`${BoldOn}${color}${timeToString()} ${msg}${AllAttributesOff}`, ...args);
});
loggers.push(consoleLogger);
}
module.exports = {
//Functions
RegisterFileLogger,
RegisterConsoleLogger,
//Variables
AllAttributesOff,
BoldOn,
Black,
Red,
Green,
Yellow,
Blue,
Magenta,
Cyan,
White
}