From 1b587b2652d26e67eefcbafca3560c2c1d003a09 Mon Sep 17 00:00:00 2001 From: blackout Date: Tue, 11 Jun 2024 15:15:11 -0400 Subject: [PATCH] SYN-7128: view.mergeAllowed/wipeAllowed to pull required perms from index data --- synapse/lib/coro.py | 2 +- synapse/lib/layer.py | 163 ++++++++++++++++++-------------- synapse/lib/view.py | 9 +- synapse/tests/test_lib_layer.py | 54 +---------- synapse/tests/test_lib_view.py | 2 +- 5 files changed, 100 insertions(+), 130 deletions(-) diff --git a/synapse/lib/coro.py b/synapse/lib/coro.py index 6097093c01..fc4706aaca 100644 --- a/synapse/lib/coro.py +++ b/synapse/lib/coro.py @@ -39,7 +39,7 @@ async def agen(item): for x in item: yield x -async def pause(genr, iterations=1000): +async def pause(genr, iterations=10): idx = 0 async for out in agen(genr): diff --git a/synapse/lib/layer.py b/synapse/lib/layer.py index 52b78f4b91..590212a1b0 100644 --- a/synapse/lib/layer.py +++ b/synapse/lib/layer.py @@ -4094,91 +4094,114 @@ async def iterNodeDataKeys(self, buid): prop = self.getAbrvProp(abrv) yield prop[0] - async def iterLayerAddPerms(self): + async def confirmLayerEditPerms(self, user, gateiden=None, delete=False): + if gateiden is None: + gateiden = self.iden + + if user.allowed(('node',), gateiden=gateiden): + return + + if delete: + perm_forms = ('node', 'del') + perm_props = ('node', 'prop', 'del') + perm_tags = ('node', 'tag', 'del') + perm_ndata = ('node', 'data', 'pop') + perm_edges = ('node', 'edge', 'del') + else: + perm_forms = ('node', 'add') + perm_props = ('node', 'prop', 'set') + perm_tags = ('node', 'tag', 'add') + perm_ndata = ('node', 'data', 'set') + perm_edges = ('node', 'edge', 'add') + + allow_forms = user.allowed(perm_forms, gateiden=gateiden) + allow_props = user.allowed(perm_props, gateiden=gateiden) + allow_tags = user.allowed(perm_tags, gateiden=gateiden) + allow_ndata = user.allowed(perm_ndata, gateiden=gateiden) + allow_edges = user.allowed(perm_edges, gateiden=gateiden) + + if all((allow_forms, allow_props, allow_tags, allow_ndata, allow_edges)): + return # nodes & props - async for byts, abrv in s_coro.pause(self.propabrv.slab.scanByFull(db=self.propabrv.name2abrv)): - form, prop = s_msgpack.un(byts) - if form is None: - continue + if not allow_forms or not allow_props: + async for byts, abrv in s_coro.pause(self.propabrv.slab.scanByFull(db=self.propabrv.name2abrv)): + form, prop = s_msgpack.un(byts) + if form is None: + continue - if self.layrslab.prefexists(abrv, db=self.byprop): - if prop: - yield ('node', 'prop', 'set', f'{form}:{prop}') - else: - yield ('node', 'add', form) + if self.layrslab.prefexists(abrv, db=self.byprop): + if prop and not allow_props: + oldperm = perm_props + (f'{form}:{prop}',) + newperm = perm_props + (form, prop) + + if not user.allowed(oldperm, gateiden=gateiden): + user.confirm(newperm, gateiden=gateiden) + + elif not prop and not allow_forms: + user.confirm(perm_forms + (form,), gateiden=gateiden) # tagprops - async for byts, abrv in s_coro.pause(self.tagpropabrv.slab.scanByFull(db=self.tagpropabrv.name2abrv)): - info = s_msgpack.un(byts) - if None in info or len(info) != 3: - continue + if not allow_tags: + async for byts, abrv in s_coro.pause(self.tagpropabrv.slab.scanByFull(db=self.tagpropabrv.name2abrv)): + info = s_msgpack.un(byts) + if None in info or len(info) != 3: + continue - if self.layrslab.prefexists(abrv, db=self.bytagprop): - yield ('node', 'tag', 'add', *info[1].split('.')) + if self.layrslab.prefexists(abrv, db=self.bytagprop): + perm = perm_tags + tuple(info[1].split('.')) + user.confirm(perm, gateiden=gateiden) # nodedata - async for abrv in s_coro.pause(self.dataslab.scanKeys(db=self.dataname)): - name, _ = self.getAbrvProp(abrv) - yield ('node', 'data', 'set', name) + if not allow_ndata: + async for abrv in s_coro.pause(self.dataslab.scanKeys(db=self.dataname)): + name, _ = self.getAbrvProp(abrv) + perm = perm_ndata + (name,) + user.confirm(perm, gateiden=gateiden) # edges - async for verb in s_coro.pause(self.layrslab.scanKeys(db=self.byverb)): - yield ('node', 'edge', 'add', verb.decode()) + if not allow_edges: + async for verb in s_coro.pause(self.layrslab.scanKeys(db=self.byverb)): + perm = perm_edges + (verb.decode(),) + user.confirm(perm, gateiden=gateiden) # tags # NB: tag perms should be yielded for every leaf on every node in the layer - async with self.core.getSpooledDict() as tags: - - # Collect all tag abrvs for all nodes in the layer - async for lkey, buid in s_coro.pause(self.layrslab.scanByFull(db=self.bytag)): - abrv = lkey[:8] - abrvs = list(tags.get(buid, [])) - abrvs.append(abrv) - await tags.set(buid, abrvs) - - # Iterate over each node and it's tags - async for buid, abrvs in s_coro.pause(tags.items()): - seen = {} - - if len(abrvs) == 1: - # Easy optimization: If there's only one tag abrv, then it's a - # leaf by default - name = self.tagabrv.abrvToName(abrv) - key = tuple(name.split('.')) - yield ('node', 'tag', 'add', *key) - - else: - for abrv in abrvs: + if not allow_tags: + async with self.core.getSpooledDict() as tags: + + # Collect all tag abrvs for all nodes in the layer + async for lkey, buid in s_coro.pause(self.layrslab.scanByFull(db=self.bytag)): + abrv = lkey[:8] + abrvs = list(tags.get(buid, [])) + abrvs.append(abrv) + await tags.set(buid, abrvs) + + # Iterate over each node and it's tags + async for buid, abrvs in s_coro.pause(tags.items()): + seen = {} + + if len(abrvs) == 1: + # Easy optimization: If there's only one tag abrv, then it's a + # leaf by default name = self.tagabrv.abrvToName(abrv) - parts = tuple(name.split('.')) - for idx in range(1, len(parts) + 1): - key = tuple(parts[:idx]) - seen.setdefault(key, 0) - seen[key] += 1 - - for key, count in seen.items(): - if count == 1: - yield ('node', 'tag', 'add', *key) - - async def iterLayerDelPerms(self): - async for perm in self.iterLayerAddPerms(): - if perm[:2] == ('node', 'add'): - yield ('node', 'del', *perm[2:]) - continue - - match perm[:3]: - case ('node', 'prop', 'set'): - yield ('node', 'prop', 'del', *perm[3:]) - - case ('node', 'tag', 'add'): - yield ('node', 'tag', 'del', *perm[3:]) + key = tuple(name.split('.')) + perm = perm_tags + key + user.confirm(perm, gateiden=gateiden) - case ('node', 'data', 'set'): - yield ('node', 'data', 'pop', *perm[3:]) - - case ('node', 'edge', 'add'): - yield ('node', 'edge', 'del', *perm[3:]) + else: + for abrv in abrvs: + name = self.tagabrv.abrvToName(abrv) + parts = tuple(name.split('.')) + for idx in range(1, len(parts) + 1): + key = tuple(parts[:idx]) + seen.setdefault(key, 0) + seen[key] += 1 + + for key, count in seen.items(): + if count == 1: + perm = perm_tags + key + user.confirm(perm, gateiden=gateiden) async def iterLayerNodeEdits(self): ''' diff --git a/synapse/lib/view.py b/synapse/lib/view.py index ea8743b831..73034af1a2 100644 --- a/synapse/lib/view.py +++ b/synapse/lib/view.py @@ -1444,10 +1444,7 @@ async def mergeAllowed(self, user=None, force=False): if user is None or user.isAdmin() or user.isAdmin(gateiden=parentlayr.iden): return - async with await self.parent.snap(user=user) as snap: - async for perm in fromlayr.iterLayerAddPerms(): - self.parent._confirm(user, perm) - await asyncio.sleep(0) + await fromlayr.confirmLayerEditPerms(user, gateiden=parentlayr.iden) async def wipeAllowed(self, user=None): ''' @@ -1457,9 +1454,7 @@ async def wipeAllowed(self, user=None): return layer = self.layers[0] - async for perm in layer.iterLayerDelPerms(): - self._confirm(user, perm) - await asyncio.sleep(0) + await layer.confirmLayerEditPerms(user, delete=True) async def runTagAdd(self, node, tag, valu): diff --git a/synapse/tests/test_lib_layer.py b/synapse/tests/test_lib_layer.py index 0260b2d2da..5a2c7f249f 100644 --- a/synapse/tests/test_lib_layer.py +++ b/synapse/tests/test_lib_layer.py @@ -1932,28 +1932,7 @@ async def __anit__(self, dirn=None, size=1, cell=None): $node.data.set(foo, bar) ''', opts=opts) - perms = [perm async for perm in layr.iterLayerAddPerms()] - self.eq({ - ('node', 'add', 'syn:tag'), - ('node', 'add', 'test:str'), - - ('node', 'prop', 'set', 'test:str:hehe'), - ('node', 'prop', 'set', 'test:str:.created'), - - ('node', 'prop', 'set', 'syn:tag:up'), - ('node', 'prop', 'set', 'syn:tag:base'), - ('node', 'prop', 'set', 'syn:tag:depth'), - ('node', 'prop', 'set', 'syn:tag:.created'), - - ('node', 'tag', 'add', 'foo'), - ('node', 'tag', 'add', 'bar'), - ('node', 'tag', 'add', 'foo', 'bar'), - ('node', 'tag', 'add', 'foo', 'bar', 'baz'), - - ('node', 'data', 'set', 'foo'), - - ('node', 'edge', 'add', 'refs'), - }, set(perms)) + await layr.confirmLayerEditPerms(core.auth.rootuser) await core.nodes(''' test:str=foo @@ -1962,35 +1941,8 @@ async def __anit__(self, dirn=None, size=1, cell=None): | delnode ''', opts=opts) - perms = [perm async for perm in layr.iterLayerAddPerms()] - self.eq({ - ('node', 'add', 'syn:tag'), - ('node', 'add', 'test:str'), - - ('node', 'prop', 'set', 'test:str:.created'), - - ('node', 'prop', 'set', 'syn:tag:up'), - ('node', 'prop', 'set', 'syn:tag:base'), - ('node', 'prop', 'set', 'syn:tag:depth'), - ('node', 'prop', 'set', 'syn:tag:.created'), - - ('node', 'tag', 'add', 'foo', 'bar'), - }, set(perms)) - - perms = [perm async for perm in layr.iterLayerDelPerms()] - self.eq({ - ('node', 'del', 'syn:tag'), - ('node', 'del', 'test:str'), - - ('node', 'prop', 'del', 'test:str:.created'), - - ('node', 'prop', 'del', 'syn:tag:up'), - ('node', 'prop', 'del', 'syn:tag:base'), - ('node', 'prop', 'del', 'syn:tag:depth'), - ('node', 'prop', 'del', 'syn:tag:.created'), - - ('node', 'tag', 'del', 'foo', 'bar'), - }, set(perms)) + await layr.confirmLayerEditPerms(core.auth.rootuser) + await layr.confirmLayerEditPerms(core.auth.rootuser, delete=True) async def test_layer_v9(self): async with self.getRegrCore('2.101.1-hugenum-indxprec') as core: diff --git a/synapse/tests/test_lib_view.py b/synapse/tests/test_lib_view.py index 2dd5179038..101baa2992 100644 --- a/synapse/tests/test_lib_view.py +++ b/synapse/tests/test_lib_view.py @@ -769,7 +769,7 @@ async def test_lib_view_merge_perms(self): with self.raises(s_exc.AuthDeny) as cm: await core.nodes('$lib.view.get().merge()', opts=viewopts) - self.eq('node.prop.set.syn:tag:base', cm.exception.errinfo['perm']) + self.eq('node.prop.set.syn:tag.base', cm.exception.errinfo['perm']) await core.addUserRule(useriden, (True, ('node', 'prop', 'set')), gateiden=baselayr)