Merge branch 'master' into prettier
This commit is contained in:
		
							
								
								
									
										102
									
								
								core/auth.js
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								core/auth.js
									
									
									
									
									
								
							@@ -5,9 +5,15 @@ let gDatabase = new Database('auth');
 | 
			
		||||
 | 
			
		||||
const kRefreshInterval = 1 * 7 * 24 * 60 * 60 * 1000;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Makes a Base64 value URL safe
 | 
			
		||||
 * @param {string} value
 | 
			
		||||
 * @returns TODOC
 | 
			
		||||
 */
 | 
			
		||||
function b64url(value) {
 | 
			
		||||
	value = value.replaceAll('+', '-').replaceAll('/', '_');
 | 
			
		||||
	let equals = value.indexOf('=');
 | 
			
		||||
 | 
			
		||||
	if (equals !== -1) {
 | 
			
		||||
		return value.substring(0, equals);
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -15,9 +21,15 @@ function b64url(value) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * TODOC
 | 
			
		||||
 * @param {string} value
 | 
			
		||||
 * @returns
 | 
			
		||||
 */
 | 
			
		||||
function unb64url(value) {
 | 
			
		||||
	value = value.replaceAll('-', '+').replaceAll('_', '/');
 | 
			
		||||
	let remainder = value.length % 4;
 | 
			
		||||
 | 
			
		||||
	if (remainder == 3) {
 | 
			
		||||
		return value + '=';
 | 
			
		||||
	} else if (remainder == 2) {
 | 
			
		||||
@@ -27,16 +39,22 @@ function unb64url(value) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Creates a JSON Web Token
 | 
			
		||||
 * @param {object} payload Object: {"name": "username"}
 | 
			
		||||
 * @returns the JWT
 | 
			
		||||
 */
 | 
			
		||||
function makeJwt(payload) {
 | 
			
		||||
	let ids = ssb.getIdentities(':auth');
 | 
			
		||||
	const ids = ssb.getIdentities(':auth');
 | 
			
		||||
	let id;
 | 
			
		||||
 | 
			
		||||
	if (ids?.length) {
 | 
			
		||||
		id = ids[0];
 | 
			
		||||
	} else {
 | 
			
		||||
		id = ssb.createIdentity(':auth');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	let final_payload = b64url(
 | 
			
		||||
	const final_payload = b64url(
 | 
			
		||||
		base64Encode(
 | 
			
		||||
			JSON.stringify(
 | 
			
		||||
				Object.assign({}, payload, {
 | 
			
		||||
@@ -45,7 +63,7 @@ function makeJwt(payload) {
 | 
			
		||||
			)
 | 
			
		||||
		)
 | 
			
		||||
	);
 | 
			
		||||
	let jwt = [
 | 
			
		||||
	const jwt = [
 | 
			
		||||
		b64url(base64Encode(JSON.stringify({alg: 'HS256', typ: 'JWT'}))),
 | 
			
		||||
		final_payload,
 | 
			
		||||
		b64url(ssb.hmacsha256sign(final_payload, ':auth', id)),
 | 
			
		||||
@@ -53,17 +71,26 @@ function makeJwt(payload) {
 | 
			
		||||
	return jwt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Validates a JWT ?
 | 
			
		||||
 * @param {*} session TODOC
 | 
			
		||||
 * @returns
 | 
			
		||||
 */
 | 
			
		||||
function readSession(session) {
 | 
			
		||||
	let jwt_parts = session?.split('.');
 | 
			
		||||
 | 
			
		||||
	if (jwt_parts?.length === 3) {
 | 
			
		||||
		let [header, payload, signature] = jwt_parts;
 | 
			
		||||
		header = JSON.parse(utf8Decode(base64Decode(unb64url(header))));
 | 
			
		||||
 | 
			
		||||
		if (header.typ === 'JWT' && header.alg === 'HS256') {
 | 
			
		||||
			signature = unb64url(signature);
 | 
			
		||||
			let id = ssb.getIdentities(':auth');
 | 
			
		||||
 | 
			
		||||
			if (id?.length && ssb.hmacsha256verify(id[0], payload, signature)) {
 | 
			
		||||
				let result = JSON.parse(utf8Decode(base64Decode(unb64url(payload))));
 | 
			
		||||
				let now = new Date().valueOf();
 | 
			
		||||
				const result = JSON.parse(utf8Decode(base64Decode(unb64url(payload))));
 | 
			
		||||
				const now = new Date().valueOf();
 | 
			
		||||
 | 
			
		||||
				if (now < result.exp) {
 | 
			
		||||
					print(`JWT valid for another ${(result.exp - now) / 1000} seconds.`);
 | 
			
		||||
					return result;
 | 
			
		||||
@@ -79,15 +106,30 @@ function readSession(session) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Check the provided password matches the hash
 | 
			
		||||
 * @param {string} password
 | 
			
		||||
 * @param {string} hash bcrypt hash
 | 
			
		||||
 * @returns true if the password matches the hash
 | 
			
		||||
 */
 | 
			
		||||
function verifyPassword(password, hash) {
 | 
			
		||||
	return bCrypt.hashpw(password, hash) == hash;
 | 
			
		||||
	return bCrypt.hashpw(password, hash) === hash;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Hashes a password
 | 
			
		||||
 * @param {string} password
 | 
			
		||||
 * @returns {string} TODOC
 | 
			
		||||
 */
 | 
			
		||||
function hashPassword(password) {
 | 
			
		||||
	let salt = bCrypt.gensalt(12);
 | 
			
		||||
	return bCrypt.hashpw(password, salt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Check if there is an administrator on the instance
 | 
			
		||||
 * @returns TODOC
 | 
			
		||||
 */
 | 
			
		||||
function noAdministrator() {
 | 
			
		||||
	return (
 | 
			
		||||
		!core.globalSettings ||
 | 
			
		||||
@@ -100,6 +142,10 @@ function noAdministrator() {
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Makes a user an administrator
 | 
			
		||||
 * @param {string} name the user's name
 | 
			
		||||
 */
 | 
			
		||||
function makeAdministrator(name) {
 | 
			
		||||
	if (!core.globalSettings.permissions) {
 | 
			
		||||
		core.globalSettings.permissions = {};
 | 
			
		||||
@@ -110,9 +156,15 @@ function makeAdministrator(name) {
 | 
			
		||||
	if (core.globalSettings.permissions[name].indexOf('administration') == -1) {
 | 
			
		||||
		core.globalSettings.permissions[name].push('administration');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	core.setGlobalSettings(core.globalSettings);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * TODOC
 | 
			
		||||
 * @param {*} headers most likely an object
 | 
			
		||||
 * @returns
 | 
			
		||||
 */
 | 
			
		||||
function getCookies(headers) {
 | 
			
		||||
	let cookies = {};
 | 
			
		||||
 | 
			
		||||
@@ -129,7 +181,13 @@ function getCookies(headers) {
 | 
			
		||||
	return cookies;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Validates a username
 | 
			
		||||
 * @param {string} name
 | 
			
		||||
 * @returns false | boolean[] ?
 | 
			
		||||
 */
 | 
			
		||||
function isNameValid(name) {
 | 
			
		||||
	// TODO(tasiaiso): convert this into a regex
 | 
			
		||||
	let c = name.charAt(0);
 | 
			
		||||
	return (
 | 
			
		||||
		((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) &&
 | 
			
		||||
@@ -144,8 +202,16 @@ function isNameValid(name) {
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Request handler ?
 | 
			
		||||
 * @param {*} request TODOC
 | 
			
		||||
 * @param {*} response
 | 
			
		||||
 * @returns
 | 
			
		||||
 */
 | 
			
		||||
function handler(request, response) {
 | 
			
		||||
	// TODO(tasiaiso): split this function
 | 
			
		||||
	let session = getCookies(request.headers).session;
 | 
			
		||||
 | 
			
		||||
	if (request.uri == '/login') {
 | 
			
		||||
		let formData = form.decodeForm(request.query);
 | 
			
		||||
		if (query(request.headers)?.permissions?.authenticated) {
 | 
			
		||||
@@ -285,6 +351,11 @@ function handler(request, response) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Gets a user's permissions based on it's session ?
 | 
			
		||||
 * @param {*} session TODOC
 | 
			
		||||
 * @returns
 | 
			
		||||
 */
 | 
			
		||||
function getPermissions(session) {
 | 
			
		||||
	let permissions;
 | 
			
		||||
	let entry = readSession(session);
 | 
			
		||||
@@ -295,6 +366,11 @@ function getPermissions(session) {
 | 
			
		||||
	return permissions || {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get a user's permissions ?
 | 
			
		||||
 * @param {string} userName TODOC
 | 
			
		||||
 * @returns
 | 
			
		||||
 */
 | 
			
		||||
function getPermissionsForUser(userName) {
 | 
			
		||||
	let permissions = {};
 | 
			
		||||
	if (
 | 
			
		||||
@@ -309,6 +385,11 @@ function getPermissionsForUser(userName) {
 | 
			
		||||
	return permissions;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * TODOC
 | 
			
		||||
 * @param {*} headers
 | 
			
		||||
 * @returns
 | 
			
		||||
 */
 | 
			
		||||
function query(headers) {
 | 
			
		||||
	let session = getCookies(headers).session;
 | 
			
		||||
	let entry;
 | 
			
		||||
@@ -323,7 +404,12 @@ function query(headers) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function make_refresh(credentials) {
 | 
			
		||||
/**
 | 
			
		||||
 * Refreshes a JWT ?
 | 
			
		||||
 * @param {*} credentials TODOC
 | 
			
		||||
 * @returns
 | 
			
		||||
 */
 | 
			
		||||
function makeRefresh(credentials) {
 | 
			
		||||
	if (credentials?.session?.name) {
 | 
			
		||||
		return {
 | 
			
		||||
			token: makeJwt({name: credentials.session.name}),
 | 
			
		||||
@@ -332,4 +418,4 @@ function make_refresh(credentials) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export {handler, query, make_refresh};
 | 
			
		||||
export {handler, query, makeRefresh};
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user