mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) Support for $ syntax in ACL rules
Summary: Adding support for the "$" syntax in ACL rules. Test Plan: Updated Reviewers: georgegevoian, dsagal Reviewed By: georgegevoian, dsagal Differential Revision: https://phab.getgrist.com/D3692
This commit is contained in:
parent
b29ce996b6
commit
101450262c
@ -1155,6 +1155,7 @@ class ObsRulePart extends Disposable {
|
||||
private _completions = Computed.create<string[]>(this, (use) => [
|
||||
...use(this._ruleSet.accessRules.userAttrChoices).map(opt => opt.value),
|
||||
...this._ruleSet.getValidColIds().map(colId => `rec.${colId}`),
|
||||
...this._ruleSet.getValidColIds().map(colId => `$${colId}`),
|
||||
...this._ruleSet.getValidColIds().map(colId => `newRec.${colId}`),
|
||||
]);
|
||||
|
||||
|
@ -7,6 +7,7 @@ from collections import namedtuple
|
||||
import asttokens
|
||||
import six
|
||||
|
||||
from codebuilder import replace_dollar_attrs
|
||||
|
||||
def parse_acl_formula(acl_formula):
|
||||
"""
|
||||
@ -31,6 +32,7 @@ def parse_acl_formula(acl_formula):
|
||||
if isinstance(acl_formula, six.binary_type):
|
||||
acl_formula = acl_formula.decode('utf8')
|
||||
try:
|
||||
acl_formula = replace_dollar_attrs(acl_formula)
|
||||
tree = ast.parse(acl_formula, mode='eval')
|
||||
result = _TreeConverter().visit(tree)
|
||||
for part in tokenize.generate_tokens(io.StringIO(acl_formula).readline):
|
||||
|
@ -122,6 +122,26 @@ def make_formula_body(formula, default_value, assoc_value=None):
|
||||
return final_formula
|
||||
|
||||
|
||||
def replace_dollar_attrs(formula):
|
||||
"""
|
||||
Translates formula "$" expression into rec. expression. This is extracted from the
|
||||
make_formula_body function.
|
||||
"""
|
||||
formula_builder_text = textbuilder.Text(formula)
|
||||
tmp_patches = textbuilder.make_regexp_patches(formula, DOLLAR_REGEX, 'DOLLAR')
|
||||
tmp_formula = textbuilder.Replacer(formula_builder_text, tmp_patches)
|
||||
atok = asttokens.ASTTokens(tmp_formula.get_text(), parse=True)
|
||||
patches = []
|
||||
for node in ast.walk(atok.tree):
|
||||
if isinstance(node, ast.Name) and node.id.startswith('DOLLAR'):
|
||||
input_pos = tmp_formula.map_back_offset(node.first_token.startpos)
|
||||
m = DOLLAR_REGEX.match(formula, input_pos)
|
||||
if m:
|
||||
patches.append(textbuilder.make_patch(formula, m.start(0), m.end(0), 'rec.'))
|
||||
final_formula = textbuilder.Replacer(formula_builder_text, patches)
|
||||
return final_formula.get_text()
|
||||
|
||||
|
||||
def _create_syntax_error_code(builder, input_text, err):
|
||||
"""
|
||||
Returns the text for a function that raises the given SyntaxError and includes the offending
|
||||
|
@ -33,6 +33,15 @@ class TestACLFormula(unittest.TestCase):
|
||||
['List', ['Const', 'sally@'], ['Const', 'xie@']]
|
||||
]])
|
||||
|
||||
self.assertEqual(parse_acl_formula(
|
||||
"$office == 'Seattle' and user.email in ['sally@', 'xie@']"),
|
||||
['And',
|
||||
['Eq', ['Attr', ['Name', 'rec'], 'office'], ['Const', 'Seattle']],
|
||||
['In',
|
||||
['Attr', ['Name', 'user'], 'email'],
|
||||
['List', ['Const', 'sally@'], ['Const', 'xie@']]
|
||||
]])
|
||||
|
||||
self.assertEqual(parse_acl_formula(
|
||||
"user.IsAdmin or rec.assigned is None or (not newRec.HasDuplicates and rec.StatusIndex <= newRec.StatusIndex)"),
|
||||
['Or',
|
||||
@ -44,6 +53,17 @@ class TestACLFormula(unittest.TestCase):
|
||||
]
|
||||
])
|
||||
|
||||
self.assertEqual(parse_acl_formula(
|
||||
"user.IsAdmin or $assigned is None or (not newRec.HasDuplicates and $StatusIndex <= newRec.StatusIndex)"),
|
||||
['Or',
|
||||
['Attr', ['Name', 'user'], 'IsAdmin'],
|
||||
['Is', ['Attr', ['Name', 'rec'], 'assigned'], ['Const', None]],
|
||||
['And',
|
||||
['Not', ['Attr', ['Name', 'newRec'], 'HasDuplicates']],
|
||||
['LtE', ['Attr', ['Name', 'rec'], 'StatusIndex'], ['Attr', ['Name', 'newRec'], 'StatusIndex']]
|
||||
]
|
||||
])
|
||||
|
||||
self.assertEqual(parse_acl_formula(
|
||||
"r.A <= n.A + 1 or r.A >= n.A - 1 or r.B < n.B * 2.5 or r.B > n.B / 2.5 or r.C % 2 != 0"),
|
||||
['Or',
|
||||
@ -71,6 +91,13 @@ class TestACLFormula(unittest.TestCase):
|
||||
['IsNot', ['Attr', ['Name', 'rec'], 'A'], ['Const', False]]
|
||||
])
|
||||
|
||||
self.assertEqual(parse_acl_formula(
|
||||
"$A is True or $A is not False"),
|
||||
['Or',
|
||||
['Is', ['Attr', ['Name', 'rec'], 'A'], ['Const', True]],
|
||||
['IsNot', ['Attr', ['Name', 'rec'], 'A'], ['Const', False]]
|
||||
])
|
||||
|
||||
self.assertEqual(parse_acl_formula(
|
||||
"user.Office.City == 'Seattle' and user.Status.IsActive"),
|
||||
['And',
|
||||
@ -171,7 +198,7 @@ class TestACLFormulaUserActions(test_engine.EngineTestCase):
|
||||
"aclFormula": ["not user.IsGood", ""],
|
||||
}])
|
||||
self.assertPartialOutActions(out_actions, { "stored": [
|
||||
[ 'BulkUpdateRecord', '_grist_ACLRules', [2, 3], {
|
||||
['BulkUpdateRecord', '_grist_ACLRules', [2, 3], {
|
||||
"aclFormula": ["not user.IsGood", ""],
|
||||
"aclFormulaParsed": ['["Not", ["Attr", ["Name", "user"], "IsGood"]]', ''],
|
||||
}],
|
||||
|
Loading…
Reference in New Issue
Block a user