Skip to content

Commit

Permalink
Merge branch 'master' into scankeys-nodup
Browse files Browse the repository at this point in the history
  • Loading branch information
Cisphyx authored Jun 17, 2024
2 parents 5e8217c + da323b9 commit 3db94a2
Show file tree
Hide file tree
Showing 12 changed files with 288 additions and 208 deletions.
77 changes: 77 additions & 0 deletions synapse/cortex.py
Original file line number Diff line number Diff line change
Expand Up @@ -6912,6 +6912,83 @@ async def delVault(self, iden):
self.slab.delete(name.encode(), db=self.vaultsbynamedb)
self.slab.delete(bidn, db=self.vaultsdb)

def _propAllowedReason(self, user, perms, gateiden=None, default=None):
'''
Similar to allowed, but always prefer the default value specified by the caller.
Default values are still pulled from permdefs if there is a match there; but still prefer caller default.
This results in a ternary response that can be used to know if a rule had a positive/negative or no match.
The matching reason metadata is also returned.
'''
if default is None:
permdef = self.getPermDef(perms)
if permdef:
default = permdef.get('default', default)

return user.getAllowedReason(perms, gateiden=gateiden, default=default)

def confirmPropSet(self, user, prop, layriden):
meta0 = self._propAllowedReason(user, prop.setperms[0], gateiden=layriden)

if meta0.isadmin:
return

allowed0 = meta0.value

meta1 = self._propAllowedReason(user, prop.setperms[1], gateiden=layriden)
allowed1 = meta1.value

if allowed0:
if allowed1:
return
elif allowed1 is False:
# This is a allow-with-precedence case.
# Inspect meta to determine if the rule a0 is more specific than rule a1
if len(meta0.rule) >= len(meta1.rule):
return
user.raisePermDeny(prop.setperms[0], gateiden=layriden)
return

if allowed1:
if allowed0 is None:
return
# allowed0 here is False. This is a deny-with-precedence case.
# Inspect meta to determine if the rule a1 is more specific than rule a0
if len(meta1.rule) > len(meta0.rule):
return

user.raisePermDeny(prop.setperms[0], gateiden=layriden)

def confirmPropDel(self, user, prop, layriden):
meta0 = self._propAllowedReason(user, prop.delperms[0], gateiden=layriden)

if meta0.isadmin:
return

allowed0 = meta0.value
meta1 = self._propAllowedReason(user, prop.delperms[1], gateiden=layriden)
allowed1 = meta1.value

if allowed0:
if allowed1:
return
elif allowed1 is False:
# This is a allow-with-precedence case.
# Inspect meta to determine if the rule a0 is more specific than rule a1
if len(meta0.rule) >= len(meta1.rule):
return
user.raisePermDeny(prop.delperms[0], gateiden=layriden)
return

if allowed1:
if allowed0 is None:
return
# allowed0 here is False. This is a deny-with-precedence case.
# Inspect meta to determine if the rule a1 is more specific than rule a0
if len(meta1.rule) > len(meta0.rule):
return

user.raisePermDeny(prop.delperms[0], gateiden=layriden)

@contextlib.asynccontextmanager
async def getTempCortex(mods=None):
'''
Expand Down
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
169 changes: 99 additions & 70 deletions synapse/lib/layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4094,91 +4094,120 @@ async def iterNodeDataKeys(self, buid):
prop = self.getAbrvProp(abrv)
yield prop[0]

async def iterLayerAddPerms(self):
async def confirmLayerEditPerms(self, user, gateiden, delete=False):
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: # pragma: no cover
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:
realform = self.core.model.form(form)
if not realform: # pragma: no cover
mesg = f'Invalid form: {form}'
raise s_exc.NoSuchForm(mesg=mesg, form=form)

realprop = realform.prop(prop)
if not realprop: # pragma: no cover
mesg = f'Invalid prop: {form}:{prop}'
raise s_exc.NoSuchProp(mesg=mesg, form=form, prop=prop)

if delete:
self.core.confirmPropDel(user, realprop, gateiden)
else:
self.core.confirmPropSet(user, realprop, 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, nodup=True)):
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, nodup=True)):
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, nodup=True)):
yield ('node', 'edge', 'add', verb.decode())
if not allow_edges:
async for verb in s_coro.pause(self.layrslab.scanKeys(db=self.byverb, nodup=True)):
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
78 changes: 7 additions & 71 deletions synapse/lib/storm.py
Original file line number Diff line number Diff line change
Expand Up @@ -2195,92 +2195,28 @@ def allowed(self, perms, gateiden=None, default=None):
return self.user.allowed(perms, gateiden=gateiden, default=default)

def allowedReason(self, perms, gateiden=None, default=None):
'''
Similar to allowed, but always prefer the default value specified by the caller.
Default values are still pulled from permdefs if there is a match there; but still prefer caller default.
This results in a ternary response that can be used to know if a rule had a positive/negative or no match.
The matching reason metadata is also returned.
'''
if self.asroot:
return self._admin_reason

if default is None:
permdef = self.snap.core.getPermDef(perms)
if permdef:
default = permdef.get('default', default)

return self.user.getAllowedReason(perms, gateiden=gateiden, default=default)
return self.snap.core._propAllowedReason(self.user, perms, gateiden=gateiden, default=default)

def confirmPropSet(self, prop, layriden=None):
if self.asroot:
return

if layriden is None:
layriden = self.snap.wlyr.iden

meta0 = self.allowedReason(prop.setperms[0], gateiden=layriden)

if meta0.isadmin:
return

allowed0 = meta0.value

meta1 = self.allowedReason(prop.setperms[1], gateiden=layriden)
allowed1 = meta1.value

if allowed0:
if allowed1:
return
elif allowed1 is False:
# This is a allow-with-precedence case.
# Inspect meta to determine if the rule a0 is more specific than rule a1
if len(meta0.rule) >= len(meta1.rule):
return
self.user.raisePermDeny(prop.setperms[0], gateiden=layriden)
return

if allowed1:
if allowed0 is None:
return
# allowed0 here is False. This is a deny-with-precedence case.
# Inspect meta to determine if the rule a1 is more specific than rule a0
if len(meta1.rule) > len(meta0.rule):
return

self.user.raisePermDeny(prop.setperms[0], gateiden=layriden)
return self.snap.core.confirmPropSet(self.user, prop, layriden=layriden)

def confirmPropDel(self, prop, layriden=None):
if self.asroot:
return

if layriden is None:
layriden = self.snap.wlyr.iden

meta0 = self.allowedReason(prop.delperms[0], gateiden=layriden)

if meta0.isadmin:
return

allowed0 = meta0.value
meta1 = self.allowedReason(prop.delperms[1], gateiden=layriden)
allowed1 = meta1.value

if allowed0:
if allowed1:
return
elif allowed1 is False:
# This is a allow-with-precedence case.
# Inspect meta to determine if the rule a0 is more specific than rule a1
if len(meta0.rule) >= len(meta1.rule):
return
self.user.raisePermDeny(prop.delperms[0], gateiden=layriden)
return

if allowed1:
if allowed0 is None:
return
# allowed0 here is False. This is a deny-with-precedence case.
# Inspect meta to determine if the rule a1 is more specific than rule a0
if len(meta1.rule) > len(meta0.rule):
return

self.user.raisePermDeny(prop.delperms[0], gateiden=layriden)
return self.snap.core.confirmPropDel(self.user, prop, layriden=layriden)

def confirmEasyPerm(self, item, perm, mesg=None):
if not self.asroot:
Expand Down
Loading

0 comments on commit 3db94a2

Please sign in to comment.