Skip to content

Commit

Permalink
SYN-7128: view.mergeAllowed/wipeAllowed to pull required perms from i…
Browse files Browse the repository at this point in the history
…ndex data
  • Loading branch information
MichaelSquires committed Jun 11, 2024
1 parent dfc97d0 commit 1b587b2
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 130 deletions.
2 changes: 1 addition & 1 deletion synapse/lib/coro.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
163 changes: 93 additions & 70 deletions synapse/lib/layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Check warning on line 4131 in synapse/lib/layer.py

View check run for this annotation

Codecov / codecov/patch

synapse/lib/layer.py#L4131

Added line #L4131 was not covered by tests

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):
'''
Expand Down
9 changes: 2 additions & 7 deletions synapse/lib/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
'''
Expand All @@ -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):

Expand Down
54 changes: 3 additions & 51 deletions synapse/tests/test_lib_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion synapse/tests/test_lib_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down

0 comments on commit 1b587b2

Please sign in to comment.