diff --git a/src/core/accessController.ts b/src/core/accessController.ts index a76e405..bda3a4c 100644 --- a/src/core/accessController.ts +++ b/src/core/accessController.ts @@ -30,17 +30,23 @@ export class AccessController { resourceAdapter: ResourceAdapter; redisClient: RedisClientType; userTopic: Topic; - waiting: any[]; + waiting: any; cfg: any; userService: UserServiceClient; - constructor(private logger: Logger, opts: AccessControlConfiguration, - userTopic: Topic, cfg: any, userService: UserServiceClient) { + + constructor( + private logger: Logger, + opts: AccessControlConfiguration, + userTopic: Topic, + cfg: any, + userService: UserServiceClient + ) { this.policySets = new Map(); this.combiningAlgorithms = new Map(); logger.info('Parsing combining algorithms from access control configuration...'); // parsing URNs and mapping them to functions - const combiningAlgorithms: CombiningAlgorithm[] = opts?.combiningAlgorithms || []; + const combiningAlgorithms: CombiningAlgorithm[] = opts?.combiningAlgorithms ?? []; for (let ca of combiningAlgorithms) { const urn = ca.urn; const method = ca.method; @@ -115,14 +121,17 @@ export class AccessController { // policyEffect needed to evalute if the properties should be PERMIT / DENY let policyEffect: Effect; - if ((!!policySet.target && await this.targetMatches(policySet.target, request, 'isAllowed', obligations)) - || !policySet.target) { + if ( + !policySet.target + || await this.targetMatches(policySet.target, request, 'isAllowed', obligations) + ) { let exactMatch = false; for (let [, policyValue] of policySet.combinables) { const policy: Policy = policyValue; if (policy.effect) { policyEffect = policy.effect; - } else if (policy.combining_algorithm) { + } + else if (policy.combining_algorithm) { const method = this.combiningAlgorithms.get(policy.combining_algorithm); if (method === 'permitOverrides') { policyEffect = Effect.PERMIT; @@ -130,7 +139,11 @@ export class AccessController { policyEffect = Effect.DENY; } } - if (!!policy.target && await this.targetMatches(policy.target, request, 'isAllowed', obligations, policyEffect)) { + + if ( + policy.target + && await this.targetMatches(policy.target, request, 'isAllowed', obligations, policyEffect) + ) { exactMatch = true; break; } @@ -151,11 +164,18 @@ export class AccessController { continue; } const ruleEffects: EffectEvaluation[] = []; - if ((!!policy.target && exactMatch && await this.targetMatches(policy.target, request, 'isAllowed', obligations, policyEffect)) + if ( + !policy.target + || ( + exactMatch + && await this.targetMatches(policy.target, request, 'isAllowed', obligations, policyEffect) + ) // regex match - || (!!policy.target && !exactMatch && await this.targetMatches(policy.target, request, 'isAllowed', obligations, policyEffect, true)) - || !policy.target) { - + || ( + !exactMatch + && await this.targetMatches(policy.target, request, 'isAllowed', obligations, policyEffect, true) + ) + ) { const rules: Map = policy.combinables; this.logger.verbose(`Checking policy ${policy.name}`); // only apply a policy effect if there are no rules @@ -184,19 +204,26 @@ export class AccessController { } if (matches) { - this.logger.verbose(`Checking rule ${rule.name}`); + this.logger.verbose(`Checking rule HR Scope for ${rule.name}`); if (matches && rule.target) { matches = await checkHierarchicalScope(rule.target, request, this.urns, this, this.logger); } try { - if (matches && !_.isEmpty(rule.condition)) { + if (matches && rule.condition?.length) { // context query is only checked when a rule exists let context: any; - if (!_.isEmpty(rule.context_query) && this.resourceAdapter) { + if ( + this.resourceAdapter + && ( + rule.context_query?.filters?.length + || rule.context_query?.query?.length + ) + ) { context = await this.pullContextResources(rule.context_query, request); if (_.isNil(context)) { + this.logger.debug('Context query response is empty!'); return { // deny by default decision: Response_Decision.DENY, obligations, @@ -209,12 +236,12 @@ export class AccessController { } } - request.context = context || request.context; + request.context = context ?? request.context; this.logger.debug('Validating rule condition', { name: rule.name, condition: rule.condition }); matches = conditionMatches(rule.condition, request); this.logger.debug('condition validation response', { matches }); } - } catch (err) { + } catch (err: any) { this.logger.error('Caught an exception while applying rule condition to request', { code: err.code, message: err.message, stack: err.stack }); return { // if an exception is caught deny by default decision: Response_Decision.DENY, @@ -296,7 +323,10 @@ export class AccessController { let obligations: Attribute[] = []; for (let [, value] of this.policySets) { let pSet: PolicySetRQ; - if (_.isEmpty(value.target) || await this.targetMatches(value.target, request, 'whatIsAllowed', obligations)) { + if ( + _.isEmpty(value.target) + || await this.targetMatches(value.target, request, 'whatIsAllowed', obligations) + ) { pSet = _.merge({}, { combining_algorithm: value.combining_algorithm }, _.pick(value, ['id', 'target', 'effect'])) as any; pSet.policies = []; diff --git a/src/core/hierarchicalScope.ts b/src/core/hierarchicalScope.ts index 36b39ea..ef87b95 100644 --- a/src/core/hierarchicalScope.ts +++ b/src/core/hierarchicalScope.ts @@ -148,7 +148,7 @@ export const checkHierarchicalScope = async (ruleTarget: Target, } if (_.isNil(entityOrOperation) || _.isEmpty(entityOrOperation)) { - logger.debug('No Entity or operation name found'); + logger.debug('No entity or operation name found'); // return false; // no entity found } @@ -179,7 +179,7 @@ export const checkHierarchicalScope = async (ruleTarget: Target, for (let roleScopeInstObj of attribute.attributes) { // role-attributes-attributes -> roleScopingInstance if (roleScopeInstObj.id == urns.get('roleScopingInstance') && !!scopingEntity) { // if scoping instance is found within the attributes const instances = entities.get(scopingEntity); - if (!_.isEmpty(_.remove(instances, i => i == roleScopeInstObj.value))) { // if any element was removed + if (!_.isEmpty(_.remove(instances, (i: string) => i == roleScopeInstObj.value))) { // if any element was removed if (_.isEmpty(instances)) { entities.delete(scopingEntity); if (entities.size == 0) { @@ -217,11 +217,11 @@ export const checkHierarchicalScope = async (ruleTarget: Target, if (!check && hierarchicalRoleScopeCheck && hierarchicalRoleScopeCheck === 'true') { const hierarchicalScopes = context.subject.hierarchical_scopes; for (let hierarchicalScope of hierarchicalScopes) { - let subTreeRole = null; + let subTreeRole: string = null; let level = -1; traverse(hierarchicalScope).forEach(function (node: any): void { // depth-first search let subtreeFound = false; - if (!!node.id) { + if (node.id) { if (level > -1 && this.level >= level) { subTreeRole = null; level = -1; @@ -239,7 +239,7 @@ export const checkHierarchicalScope = async (ruleTarget: Target, } if (subtreeFound) { const entities = scopedRoles.get(subTreeRole); - let eligibleOrgScopes = []; + let eligibleOrgScopes: string[] = []; getAllValues(node, eligibleOrgScopes); if (entities) { for (let [entity, instances] of entities) {