mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Update ACL resources/rules when tables/columns get renamed
Summary: - Placed rule-updating functions in acl.py. - Reset UI when rules update externally, or alert the user to reset if there are pending local changes. - Removed some unused and distracting bits from client-side DocModel. A few improvements related to poor error handling: - In case of missing DocActions (tickled by broken ACL rule handling), don't add to confusion by attempting to process bad actions - In case of missing attributes in ACL formulas, return undefined rather than fail; the latter creates more problems. - In case in invalid rules, fail rather than skip; this feels more correct now that we have error checking and recovery option, and helps avoid invalid rules. - Prevent saving invalid rules with an empty ACL formula. - Fix bug with rule positions. Test Plan: Added a python and browser test for table/column renames. Reviewers: paulfitz Reviewed By: paulfitz Differential Revision: https://phab.getgrist.com/D2698
This commit is contained in:
@@ -2,7 +2,15 @@
|
||||
# It now retains only the minimum needed to keep new documents openable by old code,
|
||||
# and to produce the ActionBundles expected by other code.
|
||||
|
||||
import json
|
||||
|
||||
from acl_formula import parse_acl_grist_entities
|
||||
import action_obj
|
||||
import logger
|
||||
import textbuilder
|
||||
|
||||
log = logger.Logger(__name__, logger.INFO)
|
||||
|
||||
|
||||
class Permissions(object):
|
||||
# Permission types and their combination are represented as bits of a single integer.
|
||||
@@ -36,3 +44,93 @@ def acl_read_split(action_group):
|
||||
bundle.undo.extend((0, da) for da in action_group.undo)
|
||||
bundle.retValues = action_group.retValues
|
||||
return bundle
|
||||
|
||||
|
||||
def prepare_acl_table_renames(docmodel, useractions, table_renames_dict):
|
||||
"""
|
||||
Given a dict of table renames of the form {table_id: new_table_id}, returns a callback
|
||||
that will apply updates to the affected ACL rules and resources.
|
||||
"""
|
||||
# If there are ACLResources that refer to the renamed table, prepare updates for those.
|
||||
resource_updates = []
|
||||
for resource_rec in docmodel.aclResources.all:
|
||||
if resource_rec.tableId in table_renames_dict:
|
||||
resource_updates.append((resource_rec, {'tableId': table_renames_dict[resource_rec.tableId]}))
|
||||
|
||||
# Collect updates for any ACLRules with UserAttributes that refer to the renamed table.
|
||||
rule_updates = []
|
||||
for rule_rec in docmodel.aclRules.all:
|
||||
if rule_rec.userAttributes:
|
||||
try:
|
||||
rule_info = json.loads(rule_rec.userAttributes)
|
||||
if rule_info.get("tableId") in table_renames_dict:
|
||||
rule_info["tableId"] = table_renames_dict[rule_info.get("tableId")]
|
||||
rule_updates.append((rule_rec, {'userAttributes': json.dumps(rule_info)}))
|
||||
except Exception, e:
|
||||
log.warn("Error examining aclRule: %s" % (e,))
|
||||
|
||||
def do_renames():
|
||||
useractions.doBulkUpdateFromPairs('_grist_ACLResources', resource_updates)
|
||||
useractions.doBulkUpdateFromPairs('_grist_ACLRules', rule_updates)
|
||||
return do_renames
|
||||
|
||||
|
||||
def prepare_acl_col_renames(docmodel, useractions, col_renames_dict):
|
||||
"""
|
||||
Given a dict of column renames of the form {(table_id, col_id): new_col_id}, returns a callback
|
||||
that will apply updates to the affected ACL rules and resources.
|
||||
"""
|
||||
# Collect updates for ACLResources that refer to the renamed columns.
|
||||
resource_updates = []
|
||||
for resource_rec in docmodel.aclResources.all:
|
||||
t = resource_rec.tableId
|
||||
if resource_rec.colIds and resource_rec.colIds != '*':
|
||||
new_col_ids = ','.join((col_renames_dict.get((t, c)) or c)
|
||||
for c in resource_rec.colIds.split(','))
|
||||
if new_col_ids != resource_rec.colIds:
|
||||
resource_updates.append((resource_rec, {'colIds': new_col_ids}))
|
||||
|
||||
# Collect updates for any ACLRules with UserAttributes that refer to the renamed column.
|
||||
rule_updates = []
|
||||
user_attr_tables = {} # Maps name of user attribute to its lookup table
|
||||
for rule_rec in docmodel.aclRules.all:
|
||||
if rule_rec.userAttributes:
|
||||
try:
|
||||
rule_info = json.loads(rule_rec.userAttributes)
|
||||
user_attr_tables[rule_info.get('name')] = rule_info.get('tableId')
|
||||
new_col_id = col_renames_dict.get((rule_info.get("tableId"), rule_info.get("lookupColId")))
|
||||
if new_col_id:
|
||||
rule_info["lookupColId"] = new_col_id
|
||||
rule_updates.append((rule_rec, {'userAttributes': json.dumps(rule_info)}))
|
||||
except Exception, e:
|
||||
log.warn("Error examining aclRule: %s" % (e,))
|
||||
|
||||
# Go through again checking if anything in ACL formulas is affected by the rename.
|
||||
for rule_rec in docmodel.aclRules.all:
|
||||
if rule_rec.aclFormula:
|
||||
# Positions are obtained from unicode version of formulas, so that's what we must patch
|
||||
formula = rule_rec.aclFormula.decode('utf8')
|
||||
patches = []
|
||||
|
||||
for entity in parse_acl_grist_entities(rule_rec.aclFormula):
|
||||
if entity.type == 'recCol':
|
||||
table_id = docmodel.aclResources.table.get_record(int(rule_rec.resource)).tableId
|
||||
elif entity.type == 'userAttrCol':
|
||||
table_id = user_attr_tables.get(entity.extra)
|
||||
else:
|
||||
continue
|
||||
col_id = entity.name
|
||||
new_col_id = col_renames_dict.get((table_id, col_id))
|
||||
if not new_col_id:
|
||||
continue
|
||||
patch = textbuilder.make_patch(
|
||||
formula, entity.start_pos, entity.start_pos + len(entity.name), new_col_id)
|
||||
patches.append(patch)
|
||||
|
||||
replacer = textbuilder.Replacer(textbuilder.Text(formula), patches)
|
||||
rule_updates.append((rule_rec, {'aclFormula': replacer.get_text().encode('utf8')}))
|
||||
|
||||
def do_renames():
|
||||
useractions.doBulkUpdateFromPairs('_grist_ACLResources', resource_updates)
|
||||
useractions.doBulkUpdateFromPairs('_grist_ACLRules', rule_updates)
|
||||
return do_renames
|
||||
|
||||
Reference in New Issue
Block a user