firebase-rules-parser
Version:
Parser for Firebase rule files
834 lines • 67.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Tree_1 = require("antlr4/tree/Tree");
const merge = require("deepmerge");
const __1 = require("..");
const FirebaseRulesListener_1 = require("../parser/FirebaseRulesListener");
const FirebaseRulesParser_1 = require("../parser/FirebaseRulesParser");
const FirestoreRuleClosure_1 = require("./FirestoreRuleClosure");
const MockFirestoreRequest_1 = require("./MockFirestoreRequest");
const MockFirestoreResource_1 = require("./MockFirestoreResource");
const system_1 = require("./system");
const patternMatch_1 = require("./utils/patternMatch");
var StackItemType;
(function (StackItemType) {
StackItemType["ALLOW"] = "allow";
StackItemType["ARITHMETIC"] = "arithmetic";
StackItemType["ARRAY"] = "array";
StackItemType["ARRAY_CELL_REF"] = "array-cell-ref";
StackItemType["BINARY"] = "binary";
StackItemType["COMPARE"] = "compare";
StackItemType["CLOSURE"] = "closure";
StackItemType["EXPRESSION"] = "expression";
StackItemType["FUNCTION_CALL"] = "function-call";
StackItemType["FUNCTION_DECLARATION"] = "fun-dec";
StackItemType["GET"] = "get";
StackItemType["MEMBER_FIELD_REF"] = "identifier-field-ref";
StackItemType["LOGICAL"] = "logical";
StackItemType["OBJECT_REFERENCE"] = "objectref";
StackItemType["PARENTHESIS"] = "parenthesis";
StackItemType["RESOLVED"] = "resolved";
StackItemType["UNARY"] = "unary";
StackItemType["VALUE"] = "value";
})(StackItemType || (StackItemType = {}));
exports.defaultFirebaseRulesContext = {
auth: merge(MockFirestoreRequest_1.defaultFirestoreRequest.auth, {}),
resource: merge(MockFirestoreResource_1.defaultFirestoreResource, {}),
};
// tslint:disable-next-line: jsdoc-format
/**
* Create a default firebare rule context to be used when calling rules rights.
*
* Function uses deep merge, so you can set needed values in sub objects like,
* ```typescript
* createFirebaseRulesContext({
* auth: {
* uid: '123'
* }
* });
* ```
* This will override only the uid property and will leave other properties intact.
*
* @export
* @param {Partial<FirebaseRulesContext>} [overrides] Values, to be overrided from default values.
* @param {boolean} authenticated When true, a default mock user info is given for context. Default value is `false`.
* @returns {FirebaseRulesContext}
*/
function createFirebaseRulesContext(overrides, authenticated = false) {
const context = merge(exports.defaultFirebaseRulesContext, authenticated
? {}
: {
auth: {
uid: null,
email: null,
},
});
return overrides ? merge(context, overrides) : context;
}
exports.createFirebaseRulesContext = createFirebaseRulesContext;
/**
* Firebase Rules Intepreter testing user rights based on rules script
*/
class FirebaseRulesIntepreter {
constructor() {
this.init = (rulesFile) => {
this._parser.init(rulesFile);
return this;
};
/**
* Elaborate access rights for given path within given context
*
* @memberof RulesParser
*/
this.hasAccess = (path, context) => {
return this._parser.hasAccess(path, context);
};
this._parser = new _FirebaseRulesIntepreter();
}
get request() {
return this._parser.request;
}
set request(value) {
this._parser.request = value;
}
get resource() {
return this._parser.resource;
}
set resource(value) {
this._parser.resource = value;
}
/**
* Get the namespace used in rules -file
*
* @readonly
* @memberof RulesParser
*/
get namespace() {
return this._parser.namespace;
}
}
exports.FirebaseRulesIntepreter = FirebaseRulesIntepreter;
class _FirebaseRulesIntepreter extends FirebaseRulesListener_1.FirebaseRulesListener {
constructor() {
super();
this._request = MockFirestoreRequest_1.defaultFirestoreRequest;
this._resource = MockFirestoreResource_1.defaultFirestoreResource;
this.allowRules = [];
this._stack = [];
// private closure: FirestoreRulesClosureContext = new FirestoreRulesClosureContext();
this.pathElements = [];
this.init = (rulesFile) => {
this._stack = [];
this._parser = __1.parseFirebaseRulesFromString(rulesFile);
this._parser.addErrorListener({
syntaxError: (recognizer, offendingSymbol, line, column, msg, e) => {
// tslint:disable-next-line: no-console
console.error(`${msg} at line ${line} column ${column}`);
const source = this.parseSourceToLines(rulesFile);
// tslint:disable-next-line: no-console
console.error(source[line - 1]);
const spaces = ' '.repeat(column - 1);
// tslint:disable-next-line: no-console
console.error(spaces + '^');
},
reportAmbiguity: (recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs) => {
// tslint:disable-next-line: no-console
console.error(`Ambiguity: ' + ${ambigAlts} at line ${startIndex} column ${stopIndex}`);
},
reportAttemptingFullContext: (recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs) => {
// tslint:disable-next-line: no-console
console.error(conflictingAlts);
},
reportContextSensitivity: (recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs) => {
// tslint:disable-next-line: no-console
console.error(conflictingAlts);
},
});
this.walkAST(this._parser);
return this;
};
/**
* Elaborate access rights for given path within given context
*
* @memberof RulesParser
*/
this.hasAccess = (path, context) => {
const moduleClosure = this.newClosure();
const internalContext = Object.assign({}, context, { hasAccess: {}, exit: false, path });
this._stack[0].callback(internalContext, moduleClosure);
return internalContext.hasAccess;
};
this.enterService = (ctx) => {
const callbacks = [];
this._namespace = ctx.getChild(1).getText();
this._stack.push({
type: StackItemType.CLOSURE,
debug: 'service',
obj: callbacks,
callback: (context, closure) => this.executeClosure(context, closure, callbacks),
});
};
this.enterMatcher = (ctx) => {
if (ctx.getChildCount() > 0) {
const path = ctx.getChild(1);
const callbacks = [];
const pathElement = this.generatePath(path);
this._stack.push({
type: StackItemType.CLOSURE,
debug: ctx.getText(),
obj: callbacks,
callback: (context, closure) => {
const newClosure = closure.open();
newClosure.path = pathElement;
const matchPattern = new patternMatch_1.MatchPattern(newClosure.getPath()); // TODO optimize
const variables = matchPattern.matchPrefix(context.path);
if (variables) {
newClosure.addValues(variables);
this.executeClosure(context, newClosure, callbacks);
}
},
});
}
else {
throw new Error('Internal error: match -element without child elements.');
}
};
this.exitMatcher = (ctx) => {
// this.pathElements.pop();
// this.closure.close();
const closure = this._stack.pop();
const parentClosure = this.peek();
parentClosure.obj.push(closure.callback);
};
this.enterAllow = (ctx) => {
const path = this.getCurrentPath();
const pattern = new patternMatch_1.MatchPattern(path);
const allowKeys = [];
const currentAllowRule = {
pattern,
allowKeys,
if: (context, closure) => false,
};
this._stack.push({
type: StackItemType.ALLOW,
obj: currentAllowRule,
debug: ctx.getText(),
callback: (context, closure) => {
const hasAccess = currentAllowRule.if(context, closure);
for (const key of allowKeys) {
context.hasAccess[key] = context.hasAccess[key] || hasAccess;
}
},
});
// this.allowRules.push(currentAllowRule);
};
this.exitAllow = (ctx) => {
const item = this._stack.pop();
if (!item) {
throwError('Internal error', ctx);
return;
}
if (item.type !== StackItemType.ALLOW) {
const allowItem = this._stack.pop();
if (!allowItem) {
throwError('Internal error', ctx);
return;
}
allowItem.obj.if = item.callback;
const closure = this.peek();
closure.obj.push(allowItem.callback);
}
else {
// No expression is give, so allow will treated as true for all values
const closure = this.peek();
closure.obj.push(() => true);
}
};
this.enterAllowKey = (ctx) => {
const item = this.peek();
const currentAllowRule = item.obj;
if (!item.obj) {
throwError('Allow key defined while no allow operation is active.', ctx);
return;
}
currentAllowRule.allowKeys.push(ctx.getText());
};
this.exitCompareExpression = (ctx) => {
this.handleBinaryOperation(StackItemType.COMPARE, ctx);
};
this.exitInExpression = (ctx) => {
this.handleBinaryOperation(StackItemType.ARITHMETIC, ctx);
};
this.exitArithmeticExpression = (ctx) => {
this.handleBinaryOperation(StackItemType.ARITHMETIC, ctx);
};
this.exitLogicalExpression = (ctx) => {
this.handleBinaryOperation(StackItemType.LOGICAL, ctx);
};
this.exitBinaryExpression = (ctx) => {
this.handleBinaryOperation(StackItemType.BINARY, ctx);
};
this.exitUnaryExpression = (ctx) => {
const value = this._stack.pop();
// toto undefined
const operator = ctx.getChild(0).symbol.text;
this._stack.push({
type: StackItemType.UNARY,
debug: ctx.getText(),
callback: this.resolveUnaryOperation(operator, value, ctx),
});
};
this.enterArrayExpression = (ctx) => {
const callback = (context, closure) => {
return arrayItem.items.map(expression => expression(context, closure));
};
const arrayItem = {
type: StackItemType.ARRAY,
debug: ctx.getText(),
items: [],
callback,
};
this.push(arrayItem);
};
this.exitArrayExpression = (ctx) => {
const items = [];
while (this.peek().type !== StackItemType.ARRAY) {
items.unshift(this.pop().callback);
}
const item = this.peek();
item.items = items;
};
this.exitMemberReferenceExpression = (ctx) => {
const expression = this._stack.pop();
const fieldName = ctx.getChild(2).getText();
const item = {
type: StackItemType.MEMBER_FIELD_REF,
debug: ctx.getText(),
callback: (context, closure) => {
const value = expression.callback(context, closure);
return value[fieldName];
},
};
this.push(item);
};
this.exitRangeExpression = (ctx) => {
const arrayExpression = this.pop();
const expression = this.pop();
const item = {
type: StackItemType.ARRAY_CELL_REF,
debug: ctx.getText(),
callback: (context, closure) => {
const index = arrayExpression.callback(context, closure);
const value = expression.callback(context, closure);
return value[index];
},
};
this.push(item);
};
this.enterMemberFunctionExpression = (ctx) => {
const functionName = ctx.getChild(2).getText();
const argCallbacks = [];
// const closure = this.closure.current;
const itemContext = {
callbacks: argCallbacks,
expression: undefined,
};
this._stack.push({
type: StackItemType.FUNCTION_CALL,
debug: ctx.getText(),
obj: itemContext,
callback: (context, closure) => {
const fun = closure.getValue(functionName);
if (!fun) {
throwError(`Function with name ${functionName} was not found. Please, make sure that the function name is correctly spelled and it is available in this scope.`, ctx);
}
const self = itemContext.expression.callback(context, closure);
// const args: any[] = [];
let index = 0;
const funClosure = closure.open();
for (const callback of argCallbacks) {
const callbackResult = callback(context, funClosure);
// args.push(callbackResult);
funClosure.self[fun.argNames[index++]] = callbackResult;
}
const value = fun.callback(context, funClosure, self);
funClosure.close();
return value;
},
});
};
this.exitMemberFunctionExpression = (ctx) => {
const expression = this._stack.pop();
const item = this.peek();
item.obj.expression = expression;
};
this.enterFunctionCall = (ctx) => {
const functionName = ctx.getChild(0).getText();
const argExpressions = [];
const fieldRefs = [];
const functionCallItem = {
type: StackItemType.FUNCTION_CALL,
debug: ctx.getText(),
argExpressions,
callback: (context, closure) => {
const fun = closure.getValue(functionName);
if (!fun) {
throwError(`Function with name ${functionName} was not found. Please, make sure that the function name is correctly spelled and it is available in this scope.`, ctx);
}
let index = 0;
const funClosure = closure.open();
for (const callback of argExpressions) {
const callbackResult = callback(context, closure);
funClosure.self[fun.argNames[index++]] = callbackResult;
}
let value = fun.callback(context, funClosure);
funClosure.close();
if (fieldRefs.length > 0) {
value = this.getFieldValueFromObject(context, value, fieldRefs, 0, closure);
if (value) {
return value;
}
throwError(`Null value error, field ${ctx.getText()} do not exists`, ctx);
}
return value;
},
};
this.push(functionCallItem);
};
this.exitArg = (ctx) => {
const expression = this.pop();
const item = this.peek();
if (item.type !== StackItemType.FUNCTION_CALL) {
throw new Error('Expecting a function call but found ' + expression.type);
}
const argCallbacks = item.argExpressions;
argCallbacks.push(expression.callback);
};
this.exitMemberArg = (ctx) => {
const expression = this._stack.pop();
const item = this.peek(1);
const argCallbacks = item.obj.callbacks;
if (!item.obj) {
throwError('Allow key defined while no allow operation is active.', ctx);
return;
}
argCallbacks.push(expression.callback);
};
this.enterFunctionDeclaration = (ctx) => {
this._stack.push({
type: StackItemType.FUNCTION_DECLARATION,
callback: () => true,
obj: [],
debug: '',
});
};
this.exitArgDeclaration = (ctx) => {
const item = this.peek();
item.obj.push(ctx.getText());
};
this.exitFunctionDeclaration = (ctx) => {
const funcBody = this._stack.pop();
const funDec = this._stack.pop();
const parentClosure = this.peek();
const argNames = funDec.obj;
if (!funcBody) {
throwError(`Function not found from stack`, ctx);
return;
}
const functionName = ctx.getChild(1).symbol.text;
// TODO check from closure item
// if (this.closure.current.self[functionName]) {
// throwError(`Function with name ${functionName} already exists`, ctx);
// return;
// }
parentClosure.obj.push((context, closure) => {
const desc = {
callback: funcBody.callback,
argNames,
};
closure.addValues({
[functionName]: desc,
});
return funcBody.callback(context, closure);
});
};
this.exitNumberExpression = (ctx) => {
this.handleValueExpression(ctx);
};
this.exitStringExpression = (ctx) => {
let value = ctx.getText();
value = value.substr(1, value.length - 2);
this._stack.push({
type: StackItemType.VALUE,
debug: ctx.getText(),
callback: () => value,
});
};
this.exitBooleanExpression = (ctx) => {
this._stack.push({
type: StackItemType.VALUE,
callback: () => JSON.parse(ctx.getText()),
debug: ctx.getText(),
});
};
this.exitNullExpression = (ctx) => {
this._stack.push({
type: StackItemType.VALUE,
callback: () => null,
debug: ctx.getText(),
});
};
this.enterObjectReference = (ctx) => {
const identifier = ctx.getChild(0).getText();
const fieldRefs = [identifier];
// const closure = this.closure.current;
this._stack.push({
type: StackItemType.OBJECT_REFERENCE,
obj: fieldRefs,
debug: ctx.getText(),
callback: (context, closure) => {
const objectIdentifier = this.refValue(context, fieldRefs[0], closure);
let obj = closure.getValue(objectIdentifier);
obj = this.getFieldValueFromObject(context, obj, fieldRefs, 1, closure);
if (obj || obj === '') {
return obj;
}
throwError(`Null value error, field ${ctx.getText()} do not exists`, ctx);
},
});
};
this.exitObjectReferenceExpression = (ctx) => {
const fieldName = ctx.getText();
this._stack.push({
type: StackItemType.OBJECT_REFERENCE,
debug: ctx.getText(),
callback: (context, closure) => {
const objectIdentifier = this.refValue(context, fieldName, closure);
return closure.getValue(objectIdentifier);
},
});
};
this.enterRuleFunctionCall = (ctx) => {
const functionName = ctx.getChild(0).getText();
const argCallbacks = [];
const fieldRefs = [];
// const closure = this.closure.current;
this._stack.push({
type: StackItemType.FUNCTION_CALL,
debug: ctx.getText(),
obj: {
callbacks: argCallbacks,
fieldRefs,
},
callback: (context, closure) => {
let fun;
switch (functionName) {
case 'get':
fun =
context.onGetCall ||
(() => {
// tslint:disable-next-line
throwError('get -function called but there is no onGetCall handler degined on context. Please override onGetCall trigger on context.', ctx);
});
break;
case 'exists':
fun =
context.onExistsCall ||
// tslint:disable-next-line
(() => {
throwError('exists -function called but there is no onGetCall handler degined on context. Please override onExistCall trigger on context.', ctx);
});
break;
default:
throwError(`Unidentified function ${functionName}`, ctx);
return;
}
let path = '';
for (const callback of argCallbacks) {
const callbackResult = typeof callback === 'string' ? callback : callback(context, closure);
path += '/' + callbackResult;
}
let value = fun(path);
if (fieldRefs.length > 0) {
value = this.getFieldValueFromObject(context, value, fieldRefs, 0, closure);
if (value) {
return value;
}
throwError(`Null value error, field ${ctx.getText()} do not exists`, ctx);
}
return value;
},
});
};
this.exitGetPathExpressionVariable = (ctx) => {
const expression = this._stack.pop();
const item = this.peek();
if (!item.obj) {
throwError('Allow key defined while no allow operation is active.', ctx);
return;
}
const argCallbacks = item.obj.callbacks;
argCallbacks.push(expression.callback);
};
this.exitGetPathVariable = (ctx) => {
const item = this.peek();
const value = ctx.getText();
const argCallbacks = item.obj.callbacks;
if (!item.obj) {
throwError('Allow key defined while no allow operation is active.', ctx);
return;
}
argCallbacks.push(value);
};
this.exitFieldReferenceWithIdentifier = (ctx) => {
const fieldName = ctx.getChild(1).getText();
const expressionItem = this.pop();
const fieldRefItem = {
type: StackItemType.MEMBER_FIELD_REF,
debug: ctx.getText(),
callback: (context, closure) => {
const value = expressionItem.callback(context, closure);
if (typeof value !== 'object') {
throwError(`Unidentfied type ${typeof value} for field ref.`, ctx);
return;
}
return value[fieldName];
},
};
this.push(fieldRefItem);
};
this.exitFieldReferenceWithMemberRef = (ctx) => {
const expression = this._stack.pop();
if (!expression) {
throwError('Internal error, no expression found in memberRef', ctx);
return;
}
const item = this.peek();
item.obj.push(expression.callback);
};
this.executeClosure = (context, closure, callbacks) => {
if (context.exit) {
return;
}
const subClosure = closure.open();
for (const callback of callbacks) {
callback(context, subClosure);
if (context.exit) {
return;
}
}
};
this.refValue = (context, value, closure) => {
if (typeof value === 'string') {
return value;
}
return value(context, closure);
};
this.getFieldValueFromObject = (context, obj, fieldRefs, startIndex, closure) => {
for (let i = startIndex; i < fieldRefs.length; i++) {
if (!obj) {
break;
}
const value = this.refValue(context, fieldRefs[i], closure);
obj = obj[value];
}
return obj;
};
this.parseSourceToLines = (source) => source.split('\n');
this.generatePath = (path) => {
let result = '';
for (let i = 0; i < path.getChildCount(); i++) {
const child = path.getChild(i);
if (child instanceof FirebaseRulesParser_1.PathVariableContext) {
for (let j = 0; j < child.getChildCount(); j++) {
result += child.getChild(j).symbol.text;
}
}
else {
result += child.symbol.text;
}
}
return result;
};
this.resolveUnaryOperation = (operation, expression, ctx) => {
if (!(expression && operation)) {
throwError(`Internal error`, ctx);
}
return (context, closure) => {
const value = expression.callback(context, closure);
switch (operation) {
case '!':
return !value;
case '-':
return -value;
default:
throw new Error(`Unidentified operator: ${operation} at line ${ctx.start.line} column ${ctx.start.start}.`);
}
};
};
this.resolveBinaryOperation = (left, operator, right, ctx) => {
if (!(right && left)) {
throwError(`Internal error`, ctx);
}
return (context, closure) => {
const leftValue = left.callback(context, closure);
const rightValue = right.callback(context, closure);
switch (operator) {
case '<':
return leftValue < rightValue;
case '<=':
return leftValue <= rightValue;
case 'in': {
if (Array.isArray(rightValue)) {
// List in operator => check if an array has a value
return rightValue.includes(leftValue);
}
if (typeof rightValue === 'object') {
// Map in operator => check if the object has a key
return rightValue.hasOwnProperty(leftValue);
}
throwError('In operatation must have a list or and map as target, but found type of ' + typeof rightValue, ctx);
return false;
}
case '==':
if (Array.isArray(leftValue)) {
if (Array.isArray(rightValue)) {
if (leftValue.length !== rightValue.length) {
return false;
}
for (let i = 0; i < leftValue.length; i++) {
if (leftValue[i] !== rightValue[i]) {
return false;
}
}
return true;
}
return false;
}
if (Array.isArray(rightValue)) {
return false;
}
return leftValue === rightValue;
case '!=':
return leftValue !== rightValue;
case '>':
return leftValue > rightValue;
case '>=':
return leftValue >= rightValue;
case '+':
return leftValue + rightValue;
case '-':
return leftValue - rightValue;
case '*':
return leftValue * rightValue;
case '/':
return leftValue / rightValue;
case '%':
return leftValue % rightValue;
case '&&':
return leftValue && rightValue;
case '||':
return leftValue || rightValue;
case '^':
return leftValue ^ rightValue;
default:
throw new Error(`Unidentified operator: ${operator} at line ${ctx.start.line} column ${ctx.start.start}.`);
}
};
};
this.getCurrentPath = () => {
let result = '';
for (const pathElement of this.pathElements) {
result += pathElement;
}
return result;
};
/**
* Map rules in file to paths to be accessed
*
* @private
* @memberof RulesParser
*/
this.walkAST = (parser) => {
const service = parser.service();
Tree_1.ParseTreeWalker.DEFAULT.walk(this, service);
};
this.initGlobalClosure = () => {
this._globalClosure = new FirestoreRuleClosure_1.FirestoreRulesClosure();
system_1.default(this._globalClosure);
};
this.initGlobalClosure();
}
get request() {
return this._request;
}
set request(value) {
this._request = value;
}
get resource() {
return this._resource;
}
set resource(value) {
this._resource = value;
}
/**
* Get the namespace used in rules -file
*
* @readonly
* @memberof RulesParser
*/
get namespace() {
return this._namespace;
}
defaultResult() {
return 0;
}
push(item) {
this._stack.push(item);
}
pop() {
const item = this._stack.pop();
return this.stackItemWithType(item);
}
stackItemWithType(item) {
switch (item.type) {
case StackItemType.FUNCTION_CALL:
return item;
case StackItemType.CLOSURE:
return item;
default:
return item;
}
}
newClosure() {
const closure = new FirestoreRuleClosure_1.FirestoreRulesClosure(this._globalClosure);
closure.self.request = this.request;
closure.self.resource = this.resource;
return closure;
}
peek(distance = 0) {
return this.stackItemWithType(this._stack[this._stack.length - 1 - distance]);
}
handleValueExpression(ctx) {
const value = JSON.parse(ctx.getText());
this._stack.push({
type: StackItemType.VALUE,
debug: ctx.getText(),
callback: () => value,
});
}
handleBinaryOperation(type, ctx) {
const right = this._stack.pop();
const left = this._stack.pop();
const operator = ctx.getChild(1).getText();
this._stack.push({
type,
debug: operator,
callback: this.resolveBinaryOperation(left, operator, right, ctx),
});
}
}
function throwError(message, ctx) {
throw new Error(`${message} at line ${ctx.start.line} column ${ctx.start.start}. ${ctx.getSourceInterval().toString()}`);
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW50ZXByZXRlci9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUNBLDJDQUFtRDtBQUNuRCxtQ0FBbUM7QUFFbkMsMEJBQWtEO0FBQ2xELDJFQUF3RTtBQUN4RSx1RUFnQ3VDO0FBR3ZDLGlFQUErRDtBQUMvRCxpRUFBdUY7QUFDdkYsbUVBQTBGO0FBQzFGLHFDQUErQztBQUMvQyx1REFBb0Q7QUFZcEQsSUFBSyxhQW1CSjtBQW5CRCxXQUFLLGFBQWE7SUFDaEIsZ0NBQWUsQ0FBQTtJQUNmLDBDQUF5QixDQUFBO0lBQ3pCLGdDQUFlLENBQUE7SUFDZixrREFBaUMsQ0FBQTtJQUNqQyxrQ0FBaUIsQ0FBQTtJQUNqQixvQ0FBbUIsQ0FBQTtJQUNuQixvQ0FBbUIsQ0FBQTtJQUNuQiwwQ0FBeUIsQ0FBQTtJQUN6QixnREFBK0IsQ0FBQTtJQUMvQixpREFBZ0MsQ0FBQTtJQUNoQyw0QkFBVyxDQUFBO0lBQ1gsMERBQXlDLENBQUE7SUFDekMsb0NBQW1CLENBQUE7SUFDbkIsK0NBQThCLENBQUE7SUFDOUIsNENBQTJCLENBQUE7SUFDM0Isc0NBQXFCLENBQUE7SUFDckIsZ0NBQWUsQ0FBQTtJQUNmLGdDQUFlLENBQUE7QUFDakIsQ0FBQyxFQW5CSSxhQUFhLEtBQWIsYUFBYSxRQW1CakI7QUErRFksUUFBQSwyQkFBMkIsR0FBa0M7SUFDeEUsSUFBSSxFQUFFLEtBQUssQ0FBQyw4Q0FBdUIsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDO0lBQzdDLFFBQVEsRUFBRSxLQUFLLENBQUMsZ0RBQXdCLEVBQUUsRUFBRSxDQUFDO0NBQzlDLENBQUM7QUFNRix5Q0FBeUM7QUFDekM7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBaUJHO0FBQ0gsU0FBZ0IsMEJBQTBCLENBQ3hDLFNBQXlDLEVBQ3pDLGdCQUF5QixLQUFLO0lBRTlCLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FDbkIsbUNBQTJC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