mashafrancis/butternut-user

View on GitHub
src/app/security/access-decision/access-decision-manager.ts

Summary

Maintainability
A
1 hr
Test Coverage
/* tslint:disable */
import { ucfirst } from '../../_helpers';
import { AccessEnum, Voter } from '../voter';
import { AccessDecisionManagerInterface } from './access-decision-manager.interface';
import { AccessDecisionStrategyEnum } from './access-decision-strategy.enum';

export class AccessDecisionManager implements AccessDecisionManagerInterface {
    private strategyMethod: string;

    constructor(
        private voters: IterableIterator<Voter>,
        private strategy: AccessDecisionStrategyEnum = AccessDecisionStrategyEnum.STRATEGY_AFFIRMATIVE,
        private allowIfAllAbstainDecisions: boolean = false,
        private allowIfEqualGrantedDeniedDecisions: boolean = true,
    ) {
        const strategyMethod = `decide${ucfirst(this.strategy)}`;
        if (typeof this[strategyMethod] !== 'function') {
            throw new Error(`'The strategy "${strategyMethod}" is not supported.'`);
        }
        this.strategyMethod = strategyMethod;
    }

    public async decide(token, attributes: any[], object: any): Promise<boolean> {
        return this[this.strategyMethod].call(this, token, attributes, object);
    }

    // @ts-ignore
    private async decideAffirmative(token, attributes, object): Promise<boolean> {
        let deny = 0;
        for (const voter of this.voters) {
            const result = await voter.vote(token, object, attributes);
            switch (result) {
                case AccessEnum.ACCESS_GRANTED:
                    return true;
                case AccessEnum.ACCESS_DENIED:
                    ++deny;
                    break;
                default:
                    break;
            }
        }
        if (deny > 0) {
            return false;
        }
        return this.allowIfAllAbstainDecisions;
    }

    // @ts-ignore
    private async decideConsensus(
        token,
        attributes,
        object = null,
    ): Promise<boolean> {
        let grant = 0;
        let deny = 0;
        for (const voter of this.voters) {
            const result = await voter.vote(token, object, attributes);
            switch (result) {
                case AccessEnum.ACCESS_GRANTED:
                    ++grant;
                    break;
                case AccessEnum.ACCESS_DENIED:
                    ++deny;
                    break;
            }
        }
        if (grant > deny) {
            return true;
        }
        if (deny > grant) {
            return false;
        }
        if (grant > 0) {
            return this.allowIfEqualGrantedDeniedDecisions;
        }
        return this.allowIfAllAbstainDecisions;
    }

    // @ts-ignore
    private async decideUnanimous(
        token,
        attributes,
        object = null,
    ): Promise<boolean> {
        let grant = 0;
        for (const voter of this.voters) {
            for (const attribute of attributes) {
                const result = await voter.vote(token, object, [attribute]);
                switch (result) {
                    case AccessEnum.ACCESS_GRANTED:
                        ++grant;
                        break;
                    case AccessEnum.ACCESS_DENIED:
                        return false;
                    default:
                        break;
                }
            }
        }
        if (grant > 0) {
            return true;
        }
        return this.allowIfAllAbstainDecisions;
    }
}