Skip to content

Commit

Permalink
Merge pull request #1181 from dbauszus-glx/nano-adieu
Browse files Browse the repository at this point in the history
remove nanoid
  • Loading branch information
dbauszus-glx authored Mar 12, 2024
2 parents 44c3af3 + 8bf9e57 commit 26b54a1
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 69 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ Node.js v18+

[jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken) - A Node implementation of JSON Web Token.

[nanoid](https://www.npmjs.com/package/nanoid) - A tiny, secure, URL-friendly, unique string ID generator for JavaScript.

[Node-Postgres](https://github.com/brianc/node-postgres) - PostgreSQL client for Node.

[nodemailer](https://github.com/nodemailer/nodemailer) - Send e-mails with Node – easy as cake!
Expand Down
18 changes: 16 additions & 2 deletions mod/user/auth.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/**
## User Auth
This module handles user authentication and authorization.
@module /user/auth
*/

Expand All @@ -10,6 +12,18 @@ const fromACL = require('./fromACL')

const user_sessions = {}

/**
* Authenticates the user based on the provided token or authorization header.
* @function auth
* @async
* @param {Object} req - The request object.
* @param {string} [req.headers.authorization] - The authorization header.
* @param {string} [req.params.token] - The token provided as a query parameter.
* @param {Object} [req.cookies] - The cookies object.
* @param {string} [req.cookies[process.env.TITLE]] - The cookie containing the token.
* @param {Object} res - The response object.
* @returns {Promise<Object|Error>} A Promise that resolves with the user object or an Error if authentication fails.
*/
module.exports = async (req, res) => {

if (req.headers.authorization) {
Expand All @@ -32,8 +46,8 @@ module.exports = async (req, res) => {
// Return error if verification fails.
if (err) return err

// user [nano] sessions are enabled in the env.
if (process.env.NANO_SESSION && user.session) {
// user sessions are enabled in the env.
if (process.env.USER_SESSION && user.session) {

// The session token is stored in the user_session object.
if (Object.hasOwn(user_sessions, user.email)) {
Expand Down
67 changes: 42 additions & 25 deletions mod/user/fromACL.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
### fromACL
## fromACL
This module exports a function to authenticate a user based on a provided email and password.
Expand All @@ -18,13 +18,18 @@ const languageTemplates = require('../utils/languageTemplates')

const acl = require('./acl')

const { nanoid } = require('nanoid')

/**
* Exported function fromACL that will authenticate user.
* @function fromACL
* @param {Object} req
* @returns {Obect} user
* Authenticates a user based on the provided email and password.
@function fromACL
@async
@param {Object} req - The request object.
@param {string} [req.body.email] - The email address of the user.
@param {string} [req.body.password] - The password of the user.
@param {string} [req.params.language] - The language for the user.
@param {Object} req.headers - The request headers.
@param {string} [req.headers.authorization] - The authorization header containing the email and password.
@param {string} [req.headers['x-forwarded-for']] - The IP address of the client.
@returns {Promise<Object|Error>} A Promise that resolves with the user object or an Error if authentication fails.
*/
module.exports = async (req) => {

Expand Down Expand Up @@ -76,10 +81,16 @@ module.exports = async (req) => {
}

/**
* Function that will get the User from the ACL and update the access_log property.
* @function getUser
* @param {Object} request
* @returns {Object} user
* Retrieves the user from the ACL and updates the access_log property.
@function getUser
@async
@param {Object} request - The request object.
@param {string} request.email - The email address of the user.
@param {Date} request.date - The current date and time.
@param {string} request.remote_address - The IP address of the client.
@param {string} request.language - The language for the user.
@param {string} request.host - The host for the account verification email.
@returns {Promise<Object|Error>} A Promise that resolves with the user object or an Error if the user is not found or authentication fails.
*/
async function getUser(request) {

Expand Down Expand Up @@ -139,15 +150,13 @@ async function getUser(request) {
// password must be removed after check
delete user.password

if (process.env.NANO_SESSION) {

const nano_session = nanoid()
if (process.env.USER_SESSION) {

user.session = nano_session
user.session = crypto.randomBytes(10).toString('hex')

rows = await acl(`
UPDATE acl_schema.acl_table
SET session = '${nano_session}'
SET session = '${user.session}'
WHERE lower(email) = lower($1)`,
[request.email])

Expand All @@ -166,11 +175,14 @@ async function getUser(request) {
}

/**
* Function to check the exiry of the user.
* @function userExpiry
* @param {Object} user
* @param {Obejct} request
* @returns {boolean}
* Checks if the user account has expired.
@function userExpiry
@async
@param {Object} user - The user object.
@param {Object} request - The request object.
@param {string} request.email - The email address of the user.
@param {string} request.language - The language for the user.
@returns {Promise<boolean>} A Promise that resolves with a boolean indicating if the user account has expired.
*/
async function userExpiry(user, request) {

Expand Down Expand Up @@ -199,10 +211,15 @@ async function userExpiry(user, request) {
}

/**
* Function to fail the login attempt of a user and increase the failed attempts on the ACL.
* @function failedLogin
* @param {Object} request
* @returns {Error} auth_failed
* Handles a failed login attempt and increases the failed attempts on the ACL.
@function failedLogin
@async
@param {Object} request - The request object.
@param {string} request.email - The email address of the user.
@param {string} request.language - The language for the user.
@param {string} request.host - The host for the account verification email.
@param {string} request.remote_address - The IP address of the client.
@returns {Promise<Error>} A Promise that resolves with an Error indicating that authentication failed.
*/
async function failedLogin(request) {

Expand Down
90 changes: 50 additions & 40 deletions mod/utils/logger.js
Original file line number Diff line number Diff line change
@@ -1,75 +1,85 @@
/**
@module /utils/logger
*/
## logger 🪵
This module provides a logging utility for the xyz.
* @module /utils/logger
*/

const logs = new Set(process.env.LOGS?.split(',') || [])
const crypto = require('crypto');

// Errors should always be logged.
logs.add('err')
const logs = new Set(process.env.LOGS?.split(',') || []);

const { nanoid } = require('nanoid')
// Errors should always be logged.
logs.add('err');

const process_nanoid = nanoid(6)
const process_id = crypto.randomBytes(3).toString('hex');

const logout = {
logflare,
postgresql
}
};

// Required to initialse PostgreSQL logger.
const { Pool } = require('pg');

const logger = process.env.LOGGER
&& Object.hasOwn(logout, process.env.LOGGER.split(':')[0])
&& logout[process.env.LOGGER.split(':')[0]]()
&& logout[process.env.LOGGER.split(':')[0]]();

// The default key is 'err' which should always be logged.
/**
* Logs a message to the configured logger or console.
* @function logger
* @param {string|Object} log - The message or object to log.
* @param {string} [key='err'] - The log level or key.
* @returns {void}
*/
module.exports = (log, key = 'err') => {

// Check whether the log for the key should be logged.
if (!logs.has(key)) return;

// Write log to logger if configured.
logger?.(log, key);

if (key === 'err') {

// Log errors as such.
console.error(log)
return
console.error(log);
return;
}

// Log to stdout.
console.log(log)
}
console.log(log);
};

/**
* Configures the Logflare logger.
* @function logflare
* @returns {Function} A function that logs messages to Logflare.
*/
function logflare() {

const params = Object.fromEntries(new URLSearchParams(process.env.LOGGER.split(':')[1]).entries())
const params = Object.fromEntries(new URLSearchParams(process.env.LOGGER.split(':')[1]).entries());

return (log, key) => {

fetch(`https://api.logflare.app/logs/json?source=${params.source}`,
{
method: 'post',
headers: {
'Content-Type': 'application/json',
'X-API-KEY': params.apikey,
},
body: JSON.stringify({
[process_nanoid]: log,
key
})
}).catch(err => {
console.error(err)
fetch(`https://api.logflare.app/logs/json?source=${params.source}`, {
method: 'post',
headers: {
'Content-Type': 'application/json',
'X-API-KEY': params.apikey,
},
body: JSON.stringify({
[process_id]: log,
key
})

}

}).catch(err => {
console.error(err);
});
};
}

/**
* Configures the PostgreSQL logger.
* @function postgresql
* @returns {Function} A function that logs messages to a PostgreSQL database.
*/
function postgresql() {

const params = Object.fromEntries(new URLSearchParams(process.env.LOGGER.split(':')[1]).entries());

const connectionString = process.env[`DBS_${params.dbs}`];
Expand All @@ -85,7 +95,6 @@ function postgresql() {
});

return async (log, key) => {

//Sanitize the params.table to ensure no SQL injection
const table = params.table.replace(/[^a-zA-Z0-9_.]/g, '');
// Log messages can be string or objects
Expand All @@ -99,12 +108,13 @@ function postgresql() {
try {
await client.query(
`INSERT INTO ${table} (process, datetime, key, log, message)
VALUES ($1, $2, $3, $4, $5)`,
[process_nanoid, parseInt(Date.now() / 1000), key, logstring, errorMessage]);
VALUES ($1, $2, $3, $4, $5)`,
[process_id, parseInt(Date.now() / 1000), key, logstring, errorMessage]
);
} catch (error) {
console.error('Error while logging to database:', error);
} finally {
client.release();
}
};
}
}

0 comments on commit 26b54a1

Please sign in to comment.