Skip to content

Commit

Permalink
Add support for syn:user and syn:role to repr/norm by name (#3959)
Browse files Browse the repository at this point in the history
Co-authored-by: Cisphyx <[email protected]>
  • Loading branch information
invisig0th and Cisphyx authored Oct 18, 2024
1 parent 1f7b69d commit b1dff23
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 30 deletions.
5 changes: 5 additions & 0 deletions changes/8567a1e3122f571054749b76873466dc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
desc: Added support for ``syn:user`` and ``syn:role`` types to be converted to/from names.
prs: []
type: feat
...
5 changes: 5 additions & 0 deletions changes/ef5318e3f581ea3b4f60caf7c5c76c00.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
desc: Added ``$lib.repr()`` to convert a system mode value to a display mode string.
prs: []
type: feat
...
2 changes: 1 addition & 1 deletion synapse/cortex.py
Original file line number Diff line number Diff line change
Expand Up @@ -946,7 +946,7 @@ async def initServiceStorage(self):
self._exthttpapicache = s_cache.FixedCache(self._getHttpExtApiByPath, size=1000)
self._initCortexExtHttpApi()

self.model = s_datamodel.Model()
self.model = s_datamodel.Model(core=self)

await self._bumpCellVers('cortex:extmodel', (
(1, self._migrateTaxonomyIface),
Expand Down
3 changes: 2 additions & 1 deletion synapse/datamodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -482,8 +482,9 @@ class Model:
'''
The data model used by a Cortex hypergraph.
'''
def __init__(self):
def __init__(self, core=None):

self.core = core
self.types = {} # name: Type()
self.forms = {} # name: Form()
self.props = {} # (form,name): Prop() and full: Prop()
Expand Down
13 changes: 13 additions & 0 deletions synapse/lib/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,19 @@ async def getRoleByName(self, name):
def _getRoleIden(self, name):
return self.roleidenbyname.get(name)

# TODO convert getUserByName() and getRoleByName()
# back from async? These were plumbed to avoid infecting
# type norm/repr functions with async...
def _getRoleByName(self, name):
roleiden = self.roleidenbynamecache.get(name)
if roleiden is not None:
return self.role(roleiden)

def _getUserByName(self, name):
useriden = self.useridenbynamecache.get(name)
if useriden is not None:
return self.user(useriden)

@s_nexus.Pusher.onPushAuto('user:profile:set')
async def setUserProfileValu(self, iden, name, valu):
user = await self.reqUser(iden)
Expand Down
61 changes: 40 additions & 21 deletions synapse/lib/stormtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1328,6 +1328,22 @@ class LibBase(Lib):
),
'returns': {'type': 'list',
'desc': 'A list of (<bool>, <prim>) for status and normalized value.', }}},
{'name': 'repr', 'desc': '''
Attempt to convert a system mode value to a display mode string.
Examples:
Print the Synapse user name for an iden::
$lib.print($lib.repr(syn:user, $iden))
''',
'type': {'type': 'function', '_funcname': '_repr',
'args': (
{'name': 'name', 'type': 'str', 'desc': 'The name of the model type.'},
{'name': 'valu', 'type': 'any', 'desc': 'The value to convert.'},
),
'returns': {'type': 'str', 'desc': 'A display mode representation of the value.'}}},

{'name': 'debug', 'desc': '''
True if the current runtime has debugging enabled.
Expand Down Expand Up @@ -1400,6 +1416,7 @@ def getObjLocals(self):
'false': False,
'text': self._text,
'cast': self._cast,
'repr': self._repr,
'warn': self._warn,
'print': self._print,
'raise': self._raise,
Expand Down Expand Up @@ -1489,22 +1506,26 @@ async def _copy(self, item):
mesg = 'Nested type does not support being copied!'
raise s_exc.BadArg(mesg=mesg) from None

def _reqTypeByName(self, name):
typeitem = self.runt.snap.core.model.type(name)
if typeitem is not None:
return typeitem

# If a type cannot be found for the form, see if name is a property
# that has a type we can use
propitem = self.runt.snap.core.model.prop(name)
if propitem is not None:
return propitem.type

mesg = f'No type or prop found for name {name}.'
raise s_exc.NoSuchType(mesg=mesg)

@stormfunc(readonly=True)
async def _cast(self, name, valu):
name = await toprim(name)
valu = await toprim(valu)

typeitem = self.runt.snap.core.model.type(name)
if typeitem is None:
# If a type cannot be found for the form, see if name is a property
# that has a type we can use
propitem = self.runt.snap.core.model.prop(name)
if propitem is None:
mesg = f'No type or prop found for name {name}.'
raise s_exc.NoSuchType(mesg=mesg)

typeitem = propitem.type

typeitem = self._reqTypeByName(name)
# TODO an eventual mapping between model types and storm prims

norm, info = typeitem.norm(valu)
Expand All @@ -1515,23 +1536,21 @@ async def trycast(self, name, valu):
name = await toprim(name)
valu = await toprim(valu)

typeitem = self.runt.snap.core.model.type(name)
if typeitem is None:
# If a type cannot be found for the form, see if name is a property
# that has a type we can use
propitem = self.runt.snap.core.model.prop(name)
if propitem is None:
mesg = f'No type or prop found for name {name}.'
raise s_exc.NoSuchType(mesg=mesg)

typeitem = propitem.type
typeitem = self._reqTypeByName(name)

try:
norm, info = typeitem.norm(valu)
return (True, fromprim(norm, basetypes=False))
except s_exc.BadTypeValu:
return (False, None)

@stormfunc(readonly=True)
async def _repr(self, name, valu):
name = await toprim(name)
valu = await toprim(valu)

return self._reqTypeByName(name).repr(valu)

@stormfunc(readonly=True)
async def _exit(self, mesg=None, **kwargs):
if mesg:
Expand Down
1 change: 0 additions & 1 deletion synapse/models/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ def getModelDefs(self):
('publisher', ('ou:org', {}), {
'doc': 'The organization which published the news.'}),


('publisher:name', ('ou:name', {}), {
'doc': 'The name of the publishing org used to publish the news.'}),

Expand Down
70 changes: 64 additions & 6 deletions synapse/models/syn.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,67 @@

import synapse.exc as s_exc

import synapse.lib.types as s_types
import synapse.lib.module as s_module

logger = logging.getLogger(__name__)

class SynUser(s_types.Guid):

def _normPyStr(self, text):

core = self.modl.core
if core is not None:

# direct use of an iden takes precedence...
user = core.auth.user(text)
if user is not None:
return user.iden, {}

user = core.auth._getUserByName(text)
if user is not None:
return user.iden, {}

return s_types.Guid._normPyStr(self, text)

def repr(self, iden):

core = self.modl.core
if core is not None:
user = core.auth.user(iden)
if user is not None:
return user.name

return iden

class SynRole(s_types.Guid):

def _normPyStr(self, text):

core = self.modl.core
if core is not None:

# direct use of an iden takes precedence...
role = core.auth.role(text)
if role is not None:
return role.iden, {}

role = core.auth._getRoleByName(text)
if role is not None:
return role.iden, {}

return s_types.Guid._normPyStr(self, text)

def repr(self, iden):

core = self.modl.core
if core is not None:
role = core.auth.role(iden)
if role is not None:
return role.name

return iden

class SynModule(s_module.CoreModule):

def initCoreModule(self):
Expand Down Expand Up @@ -105,6 +162,13 @@ def getModelDefs(self):

return (('syn', {

'ctors': (
('syn:user', 'synapse.models.syn.SynUser', {}, {
'doc': 'A Synapse user.'}),

('syn:role', 'synapse.models.syn.SynRole', {}, {
'doc': 'A Synapse role.'}),
),
'types': (
('syn:type', ('str', {'strip': True}), {
'doc': 'A Synapse type used for normalizing nodes and properties.',
Expand All @@ -130,12 +194,6 @@ def getModelDefs(self):
('syn:nodedata', ('comp', {'fields': (('key', 'str'), ('form', 'syn:form'))}), {
'doc': 'A nodedata key and the form it may be present on.',
}),
('syn:user', ('guid', {'strip': True}), {
'doc': 'A Synapse user GUID.'
}),
('syn:role', ('guid', {'strip': True}), {
'doc': 'A Synapse role GUID.'
}),
),

'forms': (
Expand Down
43 changes: 43 additions & 0 deletions synapse/tests/test_model_syn.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import synapse.exc as s_exc
import synapse.common as s_common
import synapse.cortex as s_cortex
import synapse.datamodel as s_datamodel

import synapse.lib.stormsvc as s_stormsvc

Expand Down Expand Up @@ -46,6 +48,47 @@ class TestService(s_stormsvc.StormSvc):

class SynModelTest(s_t_utils.SynTest):

async def test_syn_userrole(self):

async with self.getTestCore() as core:

(ok, iden) = await core.callStorm('return($lib.trycast(syn:user, root))')
self.true(ok)
self.eq(iden, core.auth.rootuser.iden)

# coverage for iden taking precedence
(ok, iden) = await core.callStorm(f'return($lib.trycast(syn:user, {iden}))')
self.true(ok)
self.eq(iden, core.auth.rootuser.iden)

self.eq('root', await core.callStorm(f'return($lib.repr(syn:user, {iden}))'))

(ok, iden) = await core.callStorm('return($lib.trycast(syn:role, all))')
self.true(ok)
self.eq(iden, core.auth.allrole.iden)

# coverage for iden taking precedence
(ok, iden) = await core.callStorm(f'return($lib.trycast(syn:role, {iden}))')
self.true(ok)
self.eq(iden, core.auth.allrole.iden)

self.eq('all', await core.callStorm(f'return($lib.repr(syn:role, {iden}))'))

# coverage for DataModel without a cortex reference
iden = s_common.guid()

model = core.model
model.core = None

synuser = model.type('syn:user')
synrole = model.type('syn:user')

self.eq(iden, synuser.repr(iden))
self.eq(iden, synrole.repr(iden))

self.eq(iden, synuser.norm(iden)[0])
self.eq(iden, synrole.norm(iden)[0])

async def test_syn_tag(self):

async with self.getTestCore() as core:
Expand Down

0 comments on commit b1dff23

Please sign in to comment.