Source

global/LoggedUser.js

/**
 * @module LoggedUser
 * @category Global
 * @author Alexis L. <alexis.lecomte@supinfo.com>
 */

import { isArray } from "lodash-es";
import { Permission } from "../api/interfaces/interfaces.js";

/**
 * A user object stored in a JWT token
 * @typedef {Object} LoggedUser~JWTObject
 * @property {string|int} sub - User ID
 * @property {string} uuid
 * @property {string} given_name - First name
 * @property {string} family_name - Last name
 * @property {string|null} picture - Link to the user's avatar
 * @property {string} email
 * @property {string} gender
 * @property {string} birthdate
 * @property {string} groups - Groups of the user (CURRENTLY NOT USED)
 * @property {Study} study
 */

/**
 * LoggedUser is a wrapper for the basic user object. It adds multiple functions such as permission checkers, ...
 * @class
 *
 * @example
 * const user = new LoggedUser({ ... });
 */
export default class LoggedUser {
	permissions = null;

	/** @param {LoggedUser~JWTObject} userObj */
	constructor(userObj) {
		const propMapping = { sub: "userID", first_name: "firstName", last_name: "lastName" };

		for (const [key, value] of Object.entries(userObj)) {
			const targetKey = propMapping[key] ?? key;
			this[targetKey] = value;
		}

		return this;
	}

	/* ---- Functions ------------------------------- */
	/**
	 * Updates the user permissions.
	 * @function
	 * @async
	 *
	 * @return {LoggedUser} - Instance of current user
	 */
	async updatePermissions() {
		if (this.uuid) {
			const resp = await Permission.getByUUID(this.uuid);
			this.permissions = resp.data.permissions;
		}

		return this;
	}

	/**
	 * Makes sure that the value of `permission` is an array
	 * @function
	 * @private
	 *
	 * @param {Array<string>|string} permission
	 * @return {Array<string>}
	 */
	_toArray(permission) {
		return isArray(permission) ? permission : [permission];
	}

	/**
	 * Returns true if the user has this permission.
	 * @function
	 * @private
	 *
	 * @param {string} permission
	 * @return {boolean}
	 */
	_hasPermission(permission) {
		return this.permissions.includes(permission);
	}

	/**
	 * Check if the user has the given permission(s). Return an object with true/false for each permission.
	 * @function
	 * @async
	 *
	 * @param {Array<string>|string} permissions
	 *
	 * @example
	 * const user = new LoggedUser({ ... });
	 * console.log(user.hasPermissions([ "SYNC_DATA", "READ_STUDENTS" ])); // ->
	 * // {
	 * //   "SYNC_DATA": false,
	 * //   "READ_STUDENTS": true
	 * // }
	 *
	 * @return {Promise<Object<boolean>>}
	 */
	async hasPermissions(permissions) {
		if (!this.permissions) await this.updatePermissions();
		const perms = {};

		for (const permission of this._toArray(permissions)) {
			perms[permission] = this._hasPermission(permission);
		}

		return perms;
	}

	/**
	 * Returns true if the user has **all** the given permission(s).
	 * @function
	 * @async
	 *
	 * @param {Array<string>|string} permissions
	 *
	 * @example
	 * const user = new LoggedUser({ ... });
	 * console.log(user.hasAllPermissions([ "SYNC_DATA", "READ_STUDENTS" ])); // -> false
	 * console.log(user.hasAllPermissions([ "READ_STUDENTS", "READ_OLD_STUDENTS" ])); // -> true
	 *
	 * @return {Promise<boolean>}
	 */
	async hasAllPermissions(permissions) {
		if (!this.permissions) await this.updatePermissions();
		return this._toArray(permissions).every(p => this._hasPermission(p));
	}
}