opa-compile-response-parser
Version:
An Open Policy Agent Compile Response Parser
705 lines (704 loc) • 20.7 kB
TypeScript
export declare type RegoValue = string | boolean | number | Array<any> | Record<string, any>;
export interface RegoRuleOptions {
name: string;
fullName: string;
isDefault: boolean;
value: RegoValue;
expressions: RegoExp[];
isCompleteEvaluated?: boolean;
parser: OpaCompileResponseParser;
}
/**
* @class RegoRule
* @export
*
* RegoRule represents [Rule](https://www.openpolicyagent.org/docs/latest/how-do-i-write-policies/#rules) concept in Rego language.
* - A simple rule is made up of Rule head, Rule value & Rule body
* - The Rule value defines the final result of the rule if the rule is matched (i.e. all expressions in rule body are true)
* - If you didn't sepcify the rule value, it will be assume as boolean value `true`
* - The rule body is made up of one of more rego expressions (see @class RegoExp) and each of the expression is made up of terms (see @class RegoTerm)
* - The rule is considered as matched if all expressions in rule body are `true`
* You can opt to define a `default` rule. A default rule has no rule body and will only considered as matched if all other rules are not matched.
*/
export declare class RegoRule {
/**
* the local name of the rule. i.e. doesn't include full package path
* e.g. `allow`
*
* @type {string}
* @memberof RegoRule
*/
name: string;
/**
* Full name of the rule. Includes fulle package path
* e.g. `data.object.content.allowRead`
*
* @type {string}
* @memberof RegoRule
*/
fullName: string;
/**
* Whether a rule is a default Rule
* Its value only be used if any other residual rules are not matched (or no other residual rules)
*
* @type {boolean}
* @memberof RegoRule
*/
isDefault: boolean;
/**
* Rule value. Rule value is this value if all expression in rule body are true
* It can be any type. e.g. can be object or array etc. But a simple policy normally outputs a boolean true or false
*
* @type {RegoValue}
* @memberof RegoRule
*/
value: RegoValue;
/**
* Whether the rule contains any expressions that has any resolvable references.
* reference start with `input.` should be considered as non-resolvable in context of partial evaluation.
* When this field is set to `true`, we should not attempt to evaluate this expression.
* i.e. evaluate() method should return immediately.
* This will speed up evaluation process.
*
* @type {boolean}
* @memberof RegoRule
*/
hasNoResolvableRef: boolean;
/**
* All Rego expressions in this rule's rule body. @see RegoExp
*
* @type {RegoExp[]}
* @memberof RegoRule
*/
expressions: RegoExp[];
/**
* If the rule is matched or not
* Default to undefined
* Its value is only set when `isCompleteEvaluated` is true
*
* @type {boolean}
* @memberof RegoRule
*/
isMatched?: boolean;
/**
* If the rule is fully evaluate
* Default to false
*
* @type {boolean}
* @memberof RegoRule
*/
isCompleteEvaluated: boolean;
/**
* Reference to OpaParser
*
* @private
* @type {OpaCompileResponseParser}
* @memberof RegoRule
*/
private parser;
constructor(options: RegoRuleOptions);
/**
* OPA PE result might contain duplicate expressions.
* https://github.com/open-policy-agent/opa/issues/4516
* This method will remove those duplication by simply string comparison.
*
* @return {*}
* @memberof RegoRule
*/
removeDuplicateExpressions(): void;
/**
* Test whether the rule is an "impossible" rule.
* If so, the rule should be simply discarded.
* See: https://github.com/open-policy-agent/opa/issues/4516
*
* @return {*} {boolean}
* @memberof RegoRule
*/
isImpossible(): boolean;
clone(options?: Partial<RegoRuleOptions>): RegoRule;
/**
* Re-evaluate this rule
* If fully evaluated, this.isCompleteEvaluated will be set to true
*
* @returns
* @memberof RegoRule
*/
evaluate(): this;
/**
* Whether or not the rule is resolvable (i.e. we can tell whether it's matched or not) now.
*
* @return {*} {boolean}
* @memberof RegoRule
*/
isResolvable(): boolean;
/**
* Generate Human Readable string of this rule
* If it's fully evaluated, the output will be true or false (or actual rule value)
* Otherwise, will generate expressions concate with `AND`
*
* @returns {string}
* @memberof RegoRule
*/
toHumanReadableString(): string;
toData(): {
default: boolean;
value: RegoValue;
fullName: string;
name: string;
expressions: ({
negated: boolean;
terms: {
type: string;
value: RegoTermValue;
}[];
index?: undefined;
} | {
negated: boolean;
index: number;
terms: {
type: string;
value: RegoTermValue;
}[];
} | {
terms: {
type: string;
value: RegoTermValue;
}[];
negated?: undefined;
index?: undefined;
} | {
index: number;
terms: {
type: string;
value: RegoTermValue;
}[];
negated?: undefined;
})[];
};
toJson(): string;
toConciseData(): {
default: boolean;
value: RegoValue;
fullName: string;
name: string;
expressions: {
negated: boolean;
operator: string;
operands: {
isRef: boolean;
value: RegoTermValue;
}[];
}[];
};
toConciseJSON(): string;
/**
* Create RegoRule from Opa response data
*
* @static
* @param {*} r
* @param {string} packageName
* @param {OpaCompileResponseParser} parser
* @returns {RegoRule}
* @memberof RegoRule
*/
static parseFromData(r: any, packageName: string, parser: OpaCompileResponseParser): RegoRule;
static createExpressionsFromRuleBodyData(data: any, parser: OpaCompileResponseParser): RegoExp[];
static randomRuleName(prefix: string): string;
static createFromValue(val: RegoValue, parser: OpaCompileResponseParser): RegoRule;
}
export interface RegoRefPart {
type: string;
value: string;
}
export declare const RegoOperators: {
readonly eq: "=";
readonly equal: "=";
readonly neq: "!=";
readonly lt: "<";
readonly gt: ">";
readonly lte: "<=";
readonly gte: ">=";
};
export declare type RegoOperatorAstString = keyof typeof RegoOperators;
export declare type RegoOperatorString = typeof RegoOperators[RegoOperatorAstString];
export declare type RegoTermValue = RegoRef | RegoValue;
/**
* RegoTerm represent the basic elements that creates an expressions.
* e.g. An expression `a > 4` is made up of 3 terms
* - Reference Term: `a`
* - Operator Term `>`
* - Value Term: `4`
*
* @export
* @class RegoTerm
*/
export declare class RegoTerm {
type: string;
value: RegoTermValue;
private parser;
/**
* Whether the expression contains any resolvable references.
* reference start with `input.` should be considered as non-resolvable in context of partial evaluation.
* When this field is set to `true`, we should not attempt to evaluate this expression.
* i.e. evaluate() method should return immediately.
* This will speed up evaluation process.
*
* @type {boolean}
* @memberof RegoTerm
*/
hasNoResolvableRef: boolean;
constructor(type: string, value: RegoTermValue, parser: OpaCompileResponseParser);
clone(): RegoTerm;
/**
* If it's a reference term, return its full string representation
*
* @returns
* @memberof RegoTerm
*/
asString(): string;
/**
* If it's a reference term. A operator is an Reference term as well
*
* @returns {boolean}
* @memberof RegoTerm
*/
isRef(): boolean;
/**
* Return RegoRef instance if this term is a RegoRef.
*
* @returns {RegoRef}
* @memberof RegoTerm
*/
getRef(): RegoRef;
/**
* If the term is a reference and it contains any collection lookup
* e.g.
* - objectA.propB.collectionC[_]
* - objectA.propB.collectionC[_].ABC[_].name
* - objectA.propB.collectionC[_].id
*
* @returns {boolean}
* @memberof RegoTerm
*/
hasCollectionLookup(): boolean;
/**
* The term is not only a Reference but a reference contains simple collection lookup
* i.e. only contains one collection lookup and the whole ref ends with the only collection lookup
* e.g. objectA.propB.collectionC[_]
* Note: objectA.propB.collectionC[_].name is not a simple collection lookup as it resolve to single value (`name` property)
* rather than a collection
*
* @returns {boolean}
* @memberof RegoTerm
*/
isSimpleCollectionLookup(): boolean;
/**
*
*
* @returns {boolean}
* @memberof RegoTerm
*/
isResolveAsCollectionValue(): boolean;
/**
* If it's a reference term, return its full string representation
* Otherwise, throw exception
*
* @param {string[]} [removalPrefixs=[]]
* @returns {string}
* @memberof RegoTerm
*/
fullRefString(removalPrefixs?: string[]): string;
/**
* If it's a reference term, return its string representation (not include ending [_])
* Otherwise, throw exception
*
* @param {string[]} [removalPrefixs=[]]
* @returns {string}
* @memberof RegoTerm
*/
refString(removalPrefixs?: string[]): string;
/**
* Return term as operator string e.g. `=`, `>=` etc.
*
* @returns {string}
* @memberof RegoTerm
*/
asOperator(): RegoOperatorString;
/**
* If it's a operator term
*
* @returns {boolean}
* @memberof RegoTerm
*/
isOperator(): boolean;
/**
* Tried to determine the value of the term
*
* @returns {RegoValue}
* @memberof RegoTerm
*/
getValue(): RegoValue;
/**
* Whether or not the RegoTerm is resolvable
*
* @return {*} {boolean}
* @memberof RegoTerm
*/
isValueResolvable(): boolean;
toData(): {
type: string;
value: RegoTermValue;
};
toJson(): string;
toConciseData(): {
isRef: boolean;
value: RegoTermValue;
};
toConciseJSON(): string;
static parseFromData(data: any, parser: OpaCompileResponseParser): RegoTerm;
}
/**
* Represents Rego expression
*
* @export
* @class RegoExp
*/
export declare class RegoExp {
/**
* All RegoTerms belongs to this expression
*
* @type {RegoTerm[]}
* @memberof RegoExp
*/
terms: RegoTerm[];
/**
* Whether the expression contains any resolvable references.
* reference start with `input.` should be considered as non-resolvable in context of partial evaluation.
* When this field is set to `true`, we should not attempt to evaluate this expression.
* i.e. evaluate() method should return immediately.
* This will speed up evaluation process.
*
* @type {boolean}
* @memberof RegoExp
*/
hasNoResolvableRef: boolean;
/**
* Whether this expression is a negative expression
* i.e. it's final evaluation result should be `false` if result is `true`
*
* @type {boolean}
* @memberof RegoExp
*/
isNegated: boolean;
/**
* If it's complete evaluated
*
* @type {boolean}
* @memberof RegoExp
*/
isCompleteEvaluated: boolean;
/**
* The value of the expression
*
* @type {RegoValue}
* @memberof RegoExp
*/
value: RegoValue;
/**
* Ref to Opa Parser
*
* @private
* @type {OpaCompileResponseParser}
* @memberof RegoExp
*/
private parser;
constructor(terms: RegoTerm[], isNegated: boolean, isCompleteEvaluated: boolean, value: RegoValue, parser: OpaCompileResponseParser);
clone(): RegoExp;
/**
* For debug usage, print terms as easy to ready short string
*
* @returns
* @memberof RegoExp
*/
termsAsString(): string;
/**
* Print concise format expression string presentation.
* Can be used for debugging
*
* @return {*}
* @memberof RegoExp
*/
asString(): string;
/**
* Output human readable string
*
* @returns {string}
* @memberof RegoExp
*/
toHumanReadableString(): string;
/**
* Try to determins its value
*
* @returns
* @memberof RegoExp
*/
getValue(): RegoValue;
/**
* Whether or not a expression should be considered as "matched".
* If all expressions of a rule are "matched", the rule will be considered as "matched".
* Thus, the rule has a value.
*
* Please note: if an expression's value is `0`, empty string "", null etc, the expression is considered as "matched".
* We only consider an expression as "Not Matched" when the expression has value `false` or is undefined.
*
* @return {boolean}
* @memberof RegoExp
*/
isMatched(): boolean;
/**
* Whether or not the expression is resolvable now.
*
* @return {boolean}
* @memberof RegoExp
*/
isResolvable(): boolean;
/**
* Convert operator term to string and put rest operands into an array.
* And then return a [Operator, Operands] structure
*
* @returns {[string, RegoTerm[]]}
* @memberof RegoExp
*/
toOperatorOperandsArray(): [RegoOperatorString, RegoTerm[]];
/**
* Try to evaluate the expression
*
* @returns
* @memberof RegoExp
*/
evaluate(): this;
toData(index?: number, ignoreIndex?: boolean, ignoreNegated?: boolean): {
negated: boolean;
terms: {
type: string;
value: RegoTermValue;
}[];
index?: undefined;
} | {
negated: boolean;
index: number;
terms: {
type: string;
value: RegoTermValue;
}[];
} | {
terms: {
type: string;
value: RegoTermValue;
}[];
negated?: undefined;
index?: undefined;
} | {
index: number;
terms: {
type: string;
value: RegoTermValue;
}[];
negated?: undefined;
};
toJSON(index?: number, ignoreIndex?: boolean, ignoreNegated?: boolean): string;
toConciseData(): {
negated: boolean;
operator: string;
operands: {
isRef: boolean;
value: RegoTermValue;
}[];
};
toConciseJSON(): string;
static parseFromData(expData: any, parser: OpaCompileResponseParser): RegoExp;
}
/**
* Represents a special Rego Term type: reference term
* You shouldn't use this class directly
*
* @export
* @class RegoRef
*/
export declare class RegoRef {
parts: RegoRefPart[];
/**
* Whether the expression contains any resolvable references.
* reference start with `input.` should be considered as non-resolvable in context of partial evaluation.
* When this field is set to `true`, we should not attempt to evaluate this expression.
* i.e. evaluate() method should return immediately.
* This will speed up evaluation process.
*
* @type {boolean}
* @memberof RegoRef
*/
hasNoResolvableRef: boolean;
constructor(parts: RegoRefPart[]);
clone(): RegoRef;
toData(): {
type: string;
value: RegoRefPart[];
};
toJson(): string;
static parseFromData(data: any): RegoRef;
static convertToFullRefString(parts: RegoRefPart[]): string;
removeAllPrefixs(str: string, removalPrefixs?: string[]): string;
fullRefString(removalPrefixs?: string[]): string;
refString(removalPrefixs?: string[]): string;
asCollectionRefs(removalPrefixs?: string[]): string[];
isOperator(): boolean;
hasCollectionLookup(): boolean;
isSimpleCollectionLookup(): boolean;
isResolveAsCollectionValue(): boolean;
asOperator(): RegoOperatorString | null;
}
export interface CompleteRuleResult {
fullName: string;
name: string;
value: RegoValue;
isCompleteEvaluated: boolean;
residualRules?: RegoRule[];
}
export declare function value2String(value: RegoValue): string;
export declare class RegoRuleSet {
fullName: string;
name: string;
rules: RegoRule[];
defaultRule: RegoRule | null;
value?: any;
isCompleteEvaluated: boolean;
parser: OpaCompileResponseParser;
/**
* Whether the ruleSet contains any rules that has any resolvable references.
* reference start with `input.` should be considered as non-resolvable in context of partial evaluation.
* When this field is set to `true`, we should not attempt to evaluate this expression.
* i.e. evaluate() method should return immediately.
* This will speed up evaluation process.
*
* @type {boolean}
* @memberof RegoRuleSet
*/
hasNoResolvableRef: boolean;
constructor(parser: OpaCompileResponseParser, rules: RegoRule[], fullName?: string, name?: string);
evaluate(): RegoRuleSet;
isResolvable(): boolean;
getResidualRules(): RegoRule[];
}
/**
* OPA result Parser
*
* @export
* @class OpaCompileResponseParser
*/
export default class OpaCompileResponseParser {
/**
* If a warning is produced during the parsing
*
* @type {boolean}
* @memberof OpaCompileResponseParser
*/
hasWarns: boolean;
/**
* Any warnings produced during the parsing
*
* @type {string[]}
* @memberof OpaCompileResponseParser
*/
warns: string[];
private data;
/**
* Inital Rules parsed from result
* Only for debug purpose
*
* @type {RegoRule[]}
* @memberof OpaCompileResponseParser
*/
originalRules: RegoRule[];
/**
* Parsed, compressed & evaluated rules
*
* @type {RegoRule[]}
* @memberof OpaCompileResponseParser
*/
rules: RegoRule[];
/**
* Parsed, compressed & evaluated rule sets
*
* @type {RegoRuleSet[]}
* @memberof OpaCompileResponseParser
*/
ruleSets: {
[fullName: string]: RegoRuleSet;
};
queries: RegoExp[];
/**
* A cache of all resolved rule result
*
* @type {{
* [fullName: string]: CompleteRuleResult;
* }}
* @memberof OpaCompileResponseParser
*/
completeRuleResults: {
[fullName: string]: CompleteRuleResult;
};
/**
* The pseudo query rule name
* The parser will assign a random pseudo rule name to the query expressions you submit.
*
* @type {string}
* @memberof OpaCompileResponseParser
*/
readonly pseudoQueryRuleName: string;
private ruleDuplicationCheckCache;
private setQueryRuleResult;
/**
* Parse OPA result Response
*
* @param {*} json
* @returns {RegoRule[]}
* @memberof OpaCompileResponseParser
*/
parse(json: any): RegoRule[];
addRule(rule: RegoRule): void;
isRefResolvable(fullName: string): boolean;
getRefValue(fullName: string): any;
private resolveAllRuleSets;
/**
* Call to evaluate a rule
*
* @param {string} fullName
* @returns {CompleteRuleResult}
* @memberof OpaCompileResponseParser
*/
evaluateRule(fullName: string): CompleteRuleResult | null;
/**
* Shortcut to evalute query result directly
*
* @returns {CompleteRuleResult}
* @memberof OpaCompileResponseParser
*/
evaluate(): CompleteRuleResult;
/**
* evaluate a rule and returned as human readable string
*
* @param {string} fullName
* @returns {string}
* @memberof OpaCompileResponseParser
*/
evaluateRuleAsHumanReadableString(fullName: string): string;
/**
* Shortcut to evalute query result directly and returned as human readable string
*
* @returns {string}
* @memberof OpaCompileResponseParser
*/
evaluateAsHumanReadableString(): string;
reportWarns(msg: string): void;
}
export declare function unknown2Ref(unknown: string): string;