UNPKG

@serenity-is/corelib

Version:
243 lines (207 loc) 8.14 kB
import { localText } from "./localtext"; import { notifyError } from "./notify"; import { getRemoteData, getRemoteDataAsync } from "./scriptdata"; import { UserDefinition } from "./userdefinition"; const andOrRegex = /[|&]/; /** * Contains permission related functions. * * ## Note * We use a namespace here both for compatibility and for allowing users to override * these functions easily in ES modules environment, which is normally hard to do. */ export namespace Authorization { /** * Checks if the current user has the permission specified. * This should only be used for UI purposes and it is strongly recommended to check permissions server side. * * > Please prefer the `hasPermissionAsync` variant as this may block the UI thread if the `UserData` script is not already loaded. * @param permission Permission key. It may contain logical operators like A&B|C. * @returns `false` for "null or undefined", true for "*", `IsLoggedIn` for "?". For other permissions, * if the user has the permission or if the user has the `IsAdmin` flag (super admin) `true`, otherwise `false`. */ export function hasPermission(permission: string) { if (permission == null) return false; if (permission == "*") return true; // normally in server side empty permission would return false // here we are more tolerant for compatibility reasons and // as it is less risky if (permission == "" || permission == "?") return !!Authorization.isLoggedIn; var ud = Authorization.userDefinition; if (!ud) return false; if (ud.IsAdmin) return true; return isPermissionInSet(ud.Permissions, permission); } /** * Checks if the current user has the permission specified. * This should only be used for UI purposes and it is strongly recommended to check permissions server side. * * @param permission Permission key. It may contain logical operators like A&B|C. * @returns `false` for "null or undefined", true for "*", `IsLoggedIn` for "?". For other permissions, * if the user has the permission or if the user has the `IsAdmin` flag (super admin) `true`, otherwise `false`. */ export async function hasPermissionAsync(permission: string): Promise<boolean> { if (permission == null) return false; if (permission == "*") return true; if (permission == "" || permission == "?") return await Authorization.isLoggedInAsync; var ud = await Authorization.userDefinitionAsync; if (!ud) return false; if (ud.IsAdmin) return true; return isPermissionInSet(ud.Permissions, permission); } /** * Checks if the hashset contains the specified permission, also handling logical "|" and "&" operators * @param permissionSet Set of permissions * @param permission Permission key or a permission expression containing & | operators * @returns true if set contains permission */ export function isPermissionInSet(permissionSet: { [key: string]: boolean }, permission: string) { if (!permissionSet || permission == null) return false; if (permissionSet[permission]) return true; if (!andOrRegex.test(permission)) return false; var orParts = permission.split('|'); for (var r of orParts) { if (!r.length) continue; var andParts = r.split('&'); let anyFalse = false; for (var n of andParts) { if (!n || !permissionSet[n]) { anyFalse = true; break; } } if (!anyFalse) return true; } return false; } /** * Throws an error if the current user does not have the specified permission. * Prefer `await validatePermissionAsync()` as this one might block the UI if the `UserData` * is not already loaded. * @param permission Permission key. It may contain logical operators like A&B|C. */ export function validatePermission(permission: string) { if (!hasPermission(permission)) { notifyError(localText("Authorization.AccessDenied")); throw new Error(localText("Authorization.AccessDenied")); } } /** * Throws an error if the current user does not have the specified permission. * @param permission Permission key. It may contain logical operators like A&B|C. * @example * await Authorization.validatePermissionAsync("A&B|C"); */ export async function validatePermissionAsync(permission: string): Promise<void> { if (!(await hasPermissionAsync(permission))) { notifyError(localText("Authorization.AccessDenied")); throw new Error(localText("Authorization.AccessDenied")); } } } export declare namespace Authorization { /** * Checks if the current user is logged in. Prefer `isLoggedInAsync` as this one might block the UI if the `UserData` * is not already loaded. * @returns `true` if the user is logged in, `false` otherwise. * @example * if (Authorization.isLoggedIn) { * // do something * } */ export let isLoggedIn: boolean; /** * Checks if the current user is logged in. * @returns `true` if the user is logged in, `false` otherwise. * @example * if (await Authorization.isLoggedInAsync) { * // do something * } */ export let isLoggedInAsync: Promise<boolean>; /** Returns the username for currently logged user. Prefer `usernameAsync` as this one might block the UI if the `UserData` * is not already loaded. * @returns Username for currently logged user. * @example * if (Authorization.username) { * // do something * } */ export let username: string; /** Returns the username for currently logged user. * @returns Username for currently logged user. * @example * if (await Authorization.usernameAsync) { * // do something * } */ export let usernameAsync: Promise<string>; /** Returns the user data for currently logged user. Prefer `userDefinitionAsync` as this one might block the UI if the `UserData` * is not already loaded. * @returns User data for currently logged user. * @example * if (Authorization.userDefinition.IsAdmin) { * // do something * } */ export let userDefinition: UserDefinition; /** Returns the user data for currently logged user. * @returns User data for currently logged user. * @example * if ((await Authorization.userDefinitionAsync).IsAdmin) { * // do something * } */ export let userDefinitionAsync: Promise<UserDefinition>; } Object.defineProperty(Authorization, "isLoggedIn", { get: function () { return !!(Authorization.userDefinition?.Username); }, configurable: true }); Object.defineProperty(Authorization, "isLoggedInAsync", { get: async function () { return !!((await Authorization.userDefinitionAsync)?.Username); }, configurable: true }); Object.defineProperty(Authorization, "userDefinition", { get: function () { return getRemoteData<UserDefinition>("UserData"); }, configurable: true }); Object.defineProperty(Authorization, "userDefinitionAsync", { get: async function () { return await getRemoteDataAsync<UserDefinition>("UserData"); }, configurable: true }); Object.defineProperty(Authorization, "username", { get: function () { return Authorization.userDefinition?.Username; }, configurable: true }); Object.defineProperty(Authorization, "usernameAsync", { get: async function () { return (await Authorization.userDefinitionAsync)?.Username; }, configurable: true });