@taukala/xs-ctrl
Version:
A flexible and powerful access control library for JavaScript applications with dynamic validation support
106 lines (102 loc) • 3.76 kB
JavaScript
/**
* Creates an access rule builder for constructing authorization rules.
* The builder follows a fluent interface pattern allowing method chaining
* for creating complex access conditions with both static and dynamic validations.
*
* Rules can be:
* 1. Static only (single/multiple conditions)
* 2. Dynamic only (single/multiple conditions)
* 3. Mixed (combination of static and dynamic conditions)
*
* @returns {Object} Rule builder object with methods for building access rules
*
* @example
* // Static only - single condition
* const adminRule = createAccessRule()
* .addCondition('role', 'admin')
* .build();
*
* @example
* // Static only - multiple conditions
* const managerRule = createAccessRule()
* .addCondition('role', 'manager')
* .addCondition('department', 'IT', 'HR')
* .build();
*
* @example
* // Dynamic only - single condition
* const businessRule = createAccessRule()
* .addDynamicCondition(({ claims, resources }) => {
* const businessIds = claims.businessOwner || [];
* return businessIds.includes(resources.business?.id);
* })
* .build();
*
* @example
* // Mixed - static and dynamic conditions
* const complexRule = createAccessRule()
* .addCondition('role', 'business')
* .addDynamicCondition(({ claims, resources }) => {
* const businessIds = claims.businessOwner || [];
* return businessIds.includes(resources.business?.id);
* })
* .addDynamicCondition(({ claims, resources }) => {
* return resources.business?.status === 'active';
* })
* .build();
*/
export function createAccessRule() {
const conditions = [];
const dynamicValidators = [];
return {
/**
* Adds a static condition to the access rule. Each condition consists of a key and one or more
* acceptable values. When evaluating access, the user must have at least one matching
* value for each condition key.
*
* @param {string} key - The condition key (e.g., 'role', 'department')
* @param {...(string|number)} values - One or more acceptable values for the condition
* @returns {Object} The builder instance for method chaining
* @throws {Error} If key is empty or no values are provided
*/
addCondition(key, ...values) {
if (!key || values.length === 0) {
throw new Error('Condition key and values must be non-empty.');
}
conditions.push([key, values]);
return this;
},
/**
* Adds a dynamic condition to the access rule. Multiple dynamic conditions can be added
* and all must pass for the rule to be valid. Dynamic conditions receive context including
* claims and resources for complex validation logic.
*
* @param {Function} validator - Function that performs dynamic validation
* @param {Object} validator.context - Context passed to the validator
* @param {Object} validator.context.claims - User claims
* @param {Object} validator.context.resources - Resource objects for validation
* @param {Object} validator.context.session - User session data
* @returns {Object} The builder instance for method chaining
* @throws {Error} If validator is not a function
*/
addDynamicCondition(validator) {
if (typeof validator !== 'function') {
throw new Error('Dynamic condition must be a function');
}
dynamicValidators.push(validator);
return this;
},
/**
* Builds and returns the final rule object containing both static conditions
* and dynamic validators if present.
*
* @returns {Object} Rule object containing conditions and dynamic validators
*/
build() {
return {
conditions,
dynamicValidators
};
}
};
}