export const OWNER = 'owners'; export const EDITOR = 'editors'; export const VIEWER = 'viewers'; export const GUEST = 'guests'; export const MEMBER = 'members'; // Roles ordered from most to least permissive. const roleOrder: Array = [OWNER, EDITOR, VIEWER, MEMBER, GUEST, null]; export type BasicRole = 'owners'|'editors'|'viewers'; export type NonMemberRole = BasicRole|'guests'; export type NonGuestRole = BasicRole|'members'; export type Role = NonMemberRole|'members'; // Returns the BasicRole (or null) with the same effective access as the given role. export function getEffectiveRole(role: Role|null): BasicRole|null { if (role === GUEST || role === MEMBER) { return VIEWER; } else { return role; } } export function canEditAccess(role: string|null): boolean { return role === OWNER; } // Note that while canEdit has the same return value as canDelete, the functions are // kept separate as they may diverge in the future. export function canEdit(role: string|null): boolean { return role === OWNER || role === EDITOR; } export function canDelete(role: string|null): boolean { return role === OWNER || role === EDITOR; } export function canView(role: string|null): boolean { return role !== null; } // Returns true if the role string is a valid role or null. export function isValidRole(role: string|null): role is Role|null { return (roleOrder as Array).includes(role); } // Returns true if the role string is a valid non-Guest, non-Member, non-null role. export function isBasicRole(role: string|null): role is BasicRole { return Boolean(role && role !== GUEST && role !== MEMBER && isValidRole(role)); } // Returns true if the role string is a valid non-Guest, non-null role. export function isNonGuestRole(role: string|null): role is NonGuestRole { return Boolean(role && role !== GUEST && isValidRole(role)); } /** * Returns out of any number of group role names the one that offers more permissions. The function * is overloaded so that the output type matches the specificity of the input values. */ export function getStrongestRole(...args: T[]): T { return getFirstMatchingRole(roleOrder, args); } /** * Returns out of any number of group role names the one that offers fewer permissions. The function * is overloaded so that the output type matches the specificity of the input values. */ export function getWeakestRole(...args: T[]): T { return getFirstMatchingRole(roleOrder.slice().reverse(), args); } // Returns which of the `anyOf` args comes first in `array`. Helper for getStrongestRole // and getWeakestRole. function getFirstMatchingRole(array: Array, anyOf: T[]): T { if (anyOf.length === 0) { throw new Error(`getFirstMatchingRole: No roles given`); } for (const role of anyOf) { if (!isValidRole(role)) { throw new Error(`getFirstMatchingRole: Invalid role ${role}`); } } return array.find((item) => anyOf.includes(item as T)) as T; }