@bdelab/roar-firekit
Version:
A library to facilitate Firebase authentication and Cloud Firestore interaction for ROAR apps
110 lines (109 loc) • 5.37 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PermissionsService = void 0;
const roles_1 = require("../constants/roles");
const user_roles_1 = require("../constants/user-roles");
const permissions_1 = require("../constants/permissions");
const flattenObjectValues_util_1 = require("../utils/flattenObjectValues.util");
const jwt_decode_1 = require("jwt-decode");
exports.PermissionsService = (() => {
/**
* This function takes a the JWT token of the user and a permission, and validates if the user is allowed to perform the action associated with the permission.
* @param {string} token A JWT token string from Firestore User.
* @param {string} permission The permission to check.
* @returns {Boolean} True if the user has the permission, false otherwise.
*/
const canUser = (token, permission) => {
var _a;
if (!isValidPermission(permission)) {
console.error(`[ROAR Permissions Service] Invalid permission "${permission}".`);
return false;
}
try {
const userRole = (_a = getRoleFromToken(token)) === null || _a === void 0 ? void 0 : _a.toLowerCase();
// If the user is a super admin, grant permission.
if (userRole === user_roles_1.UserRoles.SUPER_ADMIN.toLowerCase())
return true;
const config = roles_1.roles[userRole];
// If the user role doesn't exist in our config, flag and deny.
if (!config) {
console.error(`[ROAR Permissions Service] Invalid user role "${userRole}".`);
return false;
}
return checkPermissionList(config.permissions, permission);
}
catch (error) {
console.error('[ROAR Permissions Service] Error checking permissions:', error);
return false;
}
};
/**
* This function returns a boolean indicating whether the provided permission is present
* in the Permissions object.
*
* @param {any} permission A permission string to check.
* @returns {Boolean} True if the permission is valid, false otherwise.
*/
const isValidPermission = (permission) => {
if (typeof permission !== 'string')
return false;
const allPermissions = (0, flattenObjectValues_util_1.flattenObjectValues)(permissions_1.Permissions);
return allPermissions.includes(permission);
};
/**
* This function takes a JWT token and returns the user's role. If the token is invalid or missing the role claim,
* return the GUEST role.
*
* @param {string} token JWT token string from Firestore User.
* @returns {string} The user's role based on the provided JWT token.
*/
const getRoleFromToken = (token) => {
var _a;
const decodedToken = (0, jwt_decode_1.jwtDecode)(token);
const userRole = (_a = decodedToken.role) !== null && _a !== void 0 ? _a : user_roles_1.FallbackRole;
// Retrieve the user's role from the token's claims. If the claim is missing or invalid, default to the GUEST role.
if (!decodedToken.role) {
console.error(`[ROAR Permissions Service] Missing role claim in user's custom claims. Defaulting to the ${userRole} role.`);
}
return userRole;
};
/**
* This function checks if a permission is included in a list of permissions.
*
* @param {string[]} permissionsList List of permissions to check against.
* @param {string} permission Permission to check.
* @returns {Boolean} True if the permission is in the list, false otherwise.
*/
const checkPermissionList = (permissionsList, permission) => {
// Check if the literal permission is in the list
if (permissionsList.includes(permission))
return true;
// Check if the permission matches a wildcard permission
return permissionsList.some((rolePermission) => matchWildcardPermission(rolePermission, permission));
};
/**
* This function checks if a permission matches a wildcard permission within a permission list.
* ex. 'app.users.create' matches 'app.user.*' and 'app.*'
*
* @param {string} permission Permisssion from the
* @param {string} userPermission Permission to check. This will be from the user.
* @returns {Boolean} True if the permissions match considering wildcards. False otherwise.
*/
const matchWildcardPermission = (pattern, permission) => {
const patternParts = pattern.split('.');
const permissionParts = permission.split('.');
// Check if the pattern has a wildcard
const wildcardIndex = patternParts.indexOf('*');
// If there's no wildcard, the parts should match exactly
if (wildcardIndex === -1) {
return (patternParts.length === permissionParts.length &&
patternParts.every((part, index) => part === permissionParts[index]));
}
// If there's a wildcard, it must be the last part
if (wildcardIndex !== patternParts.length - 1)
return false;
// Check if all parts before the wildcard match
return patternParts.slice(0, wildcardIndex).every((part, index) => part === permissionParts[index]);
};
return { canUser };
})();