synapse 2.172.0__py311-none-any.whl → 2.173.1__py311-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of synapse might be problematic. Click here for more details.

synapse/lib/hiveauth.py CHANGED
@@ -1,7 +1,7 @@
1
1
  import logging
2
2
  import dataclasses
3
3
 
4
- from typing import Union
4
+ from typing import Optional, Union
5
5
 
6
6
  import synapse.exc as s_exc
7
7
  import synapse.common as s_common
@@ -941,16 +941,38 @@ class HiveUser(HiveRuler):
941
941
  if not self.allowed(perm):
942
942
  await self.addRule((True, perm), indx=0)
943
943
 
944
- def allowed(self, perm, default=None, gateiden=None):
944
+ def allowed(self,
945
+ perm: tuple[str, ...],
946
+ default: Optional[str] = None,
947
+ gateiden: Optional[str] = None,
948
+ deepdeny: bool = False) -> Union[bool, None]:
949
+ '''
950
+ Check if a user is allowed a given permission.
951
+
952
+ Args:
953
+ perm: The permission tuple to check.
954
+ default: The default rule value if there is no match.
955
+ gateiden: The gate iden to check against.
956
+ deepdeny: If True, give precedence for checking deny rules which are more specific than the requested
957
+ permission.
958
+
959
+ Notes:
960
+ The use of the deepdeny argument is intended for checking a less-specific part of a permissions tree, in
961
+ order to know about possible short circuit options. Using it to check a more specific part may have
962
+ unintended results.
963
+
964
+ Returns:
965
+ The allowed value of the permission.
966
+ '''
945
967
  perm = tuple(perm)
946
- return self.permcache.get((perm, default, gateiden))
968
+ return self.permcache.get((perm, default, gateiden, deepdeny))
947
969
 
948
970
  def _allowed(self, pkey):
949
971
  '''
950
972
  NOTE: This must remain in sync with any changes to _getAllowedReason()!
951
973
  '''
952
974
 
953
- perm, default, gateiden = pkey
975
+ perm, default, gateiden, deepdeny = pkey
954
976
 
955
977
  if self.info.get('locked'):
956
978
  return False
@@ -958,6 +980,9 @@ class HiveUser(HiveRuler):
958
980
  if self.info.get('admin'):
959
981
  return True
960
982
 
983
+ if deepdeny and self._hasDeepDeny(perm, gateiden):
984
+ return False
985
+
961
986
  # 1. check authgate user rules
962
987
  if gateiden is not None:
963
988
 
@@ -1055,6 +1080,58 @@ class HiveUser(HiveRuler):
1055
1080
 
1056
1081
  return _allowedReason(default, default=True)
1057
1082
 
1083
+ def _hasDeepDeny(self, perm, gateiden):
1084
+
1085
+ permlen = len(perm)
1086
+
1087
+ # 1. check authgate user rules
1088
+ if gateiden is not None:
1089
+
1090
+ info = self.authgates.get(gateiden)
1091
+ if info is not None:
1092
+
1093
+ if info.get('admin'):
1094
+ return False
1095
+
1096
+ for allow, path in info.get('rules', ()):
1097
+ if allow:
1098
+ continue
1099
+ if path[:permlen] == perm and len(path) > permlen:
1100
+ return True
1101
+
1102
+ # 2. check user rules
1103
+ for allow, path in self.info.get('rules', ()):
1104
+ if allow:
1105
+ continue
1106
+
1107
+ if path[:permlen] == perm and len(path) > permlen:
1108
+ return True
1109
+
1110
+ # 3. check authgate role rules
1111
+ if gateiden is not None:
1112
+
1113
+ for role in self.getRoles():
1114
+
1115
+ info = role.authgates.get(gateiden)
1116
+ if info is None:
1117
+ continue
1118
+
1119
+ for allow, path in info.get('rules', ()):
1120
+ if allow:
1121
+ continue
1122
+ if path[:permlen] == perm and len(path) > permlen:
1123
+ return True
1124
+
1125
+ # 4. check role rules
1126
+ for role in self.getRoles():
1127
+ for allow, path in role.info.get('rules', ()):
1128
+ if allow:
1129
+ continue
1130
+ if path[:permlen] == perm and len(path) > permlen:
1131
+ return True
1132
+
1133
+ return False
1134
+
1058
1135
  def clearAuthCache(self):
1059
1136
  self.permcache.clear()
1060
1137
  self.allowedcache.clear()
synapse/lib/layer.py CHANGED
@@ -4095,9 +4095,6 @@ class Layer(s_nexus.Pusher):
4095
4095
  yield prop[0]
4096
4096
 
4097
4097
  async def confirmLayerEditPerms(self, user, gateiden, delete=False):
4098
- if user.allowed(('node',), gateiden=gateiden):
4099
- return
4100
-
4101
4098
  if delete:
4102
4099
  perm_forms = ('node', 'del')
4103
4100
  perm_props = ('node', 'prop', 'del')
@@ -4111,11 +4108,14 @@ class Layer(s_nexus.Pusher):
4111
4108
  perm_ndata = ('node', 'data', 'set')
4112
4109
  perm_edges = ('node', 'edge', 'add')
4113
4110
 
4114
- allow_forms = user.allowed(perm_forms, gateiden=gateiden)
4115
- allow_props = user.allowed(perm_props, gateiden=gateiden)
4116
- allow_tags = user.allowed(perm_tags, gateiden=gateiden)
4117
- allow_ndata = user.allowed(perm_ndata, gateiden=gateiden)
4118
- allow_edges = user.allowed(perm_edges, gateiden=gateiden)
4111
+ if user.allowed(('node',), gateiden=gateiden, deepdeny=True):
4112
+ return
4113
+
4114
+ allow_forms = user.allowed(perm_forms, gateiden=gateiden, deepdeny=True)
4115
+ allow_props = user.allowed(perm_props, gateiden=gateiden, deepdeny=True)
4116
+ allow_tags = user.allowed(perm_tags, gateiden=gateiden, deepdeny=True)
4117
+ allow_ndata = user.allowed(perm_ndata, gateiden=gateiden, deepdeny=True)
4118
+ allow_edges = user.allowed(perm_edges, gateiden=gateiden, deepdeny=True)
4119
4119
 
4120
4120
  if all((allow_forms, allow_props, allow_tags, allow_ndata, allow_edges)):
4121
4121
  return
synapse/lib/version.py CHANGED
@@ -223,6 +223,6 @@ def reqVersion(valu, reqver,
223
223
  ##############################################################################
224
224
  # The following are touched during the release process by bumpversion.
225
225
  # Do not modify these directly.
226
- version = (2, 172, 0)
226
+ version = (2, 173, 1)
227
227
  verstring = '.'.join([str(x) for x in version])
228
- commit = '3e33d8a8cbdfd0f4f6f9a31d664578d817d9ccb8'
228
+ commit = '06b04fd5f58964b7897442d4c88cbfed145093d9'
synapse/models/orgs.py CHANGED
@@ -1026,8 +1026,11 @@ class OuModule(s_module.CoreModule):
1026
1026
  }),
1027
1027
  ('name', ('str', {'lower': True}), {
1028
1028
  'doc': 'The full name of the conference.',
1029
- 'ex': 'decfon 2017',
1030
- }),
1029
+ 'ex': 'defcon 2017'}),
1030
+
1031
+ ('names', ('array', {'type': 'str', 'typeopts': {'lower': True}, 'uniq': True, 'sorted': True}), {
1032
+ 'doc': 'An array of alternate names for the conference.'}),
1033
+
1031
1034
  ('desc', ('str', {'lower': True}), {
1032
1035
  'doc': 'A description of the conference.',
1033
1036
  'ex': 'annual cybersecurity conference',
synapse/models/person.py CHANGED
@@ -356,8 +356,11 @@ class PsModule(s_module.CoreModule):
356
356
  'doc': 'A description of this contact.',
357
357
  }),
358
358
  ('title', ('ou:jobtitle', {}), {
359
- 'doc': 'The job/org title listed for this contact.',
360
- }),
359
+ 'doc': 'The job/org title listed for this contact.'}),
360
+
361
+ ('titles', ('array', {'type': 'ou:jobtitle', 'sorted': True, 'uniq': True}), {
362
+ 'doc': 'An array of alternate titles for the contact.'}),
363
+
361
364
  ('photo', ('file:bytes', {}), {
362
365
  'doc': 'The photo listed for this contact.',
363
366
  }),
@@ -28,6 +28,11 @@ class PlanModule(s_module.CoreModule):
28
28
  'doc': 'A link between steps in a procedure.'}),
29
29
  ),
30
30
 
31
+ 'edges': (
32
+ (('plan:procedure:step', 'uses', None), {
33
+ 'doc': 'The step in the procedure makes use of the target node.'}),
34
+ ),
35
+
31
36
  'forms': (
32
37
  ('plan:system', {}, (
33
38
 
@@ -2,9 +2,10 @@ import pathlib
2
2
 
3
3
  import synapse.exc as s_exc
4
4
  import synapse.telepath as s_telepath
5
+ import synapse.lib.hiveauth as s_hiveauth
5
6
 
6
7
  import synapse.tests.utils as s_test
7
- from synapse.tests.utils import alist
8
+
8
9
 
9
10
  class AuthTest(s_test.SynTest):
10
11
 
@@ -371,3 +372,140 @@ class AuthTest(s_test.SynTest):
371
372
  await core.auth.allrole.setRules([(True, ('hehe', 'haha'), 'newp')])
372
373
  with self.raises(s_exc.SchemaViolation):
373
374
  await core.auth.allrole.setRules([(True, )])
375
+
376
+ async def test_hive_auth_deepdeny(self):
377
+ async with self.getTestCore() as core:
378
+
379
+ # Create an authgate we can later test against
380
+ fork = await core.callStorm('return( $lib.view.get().fork().iden )')
381
+ await core.callStorm('auth.user.add lowuser')
382
+ await core.callStorm('auth.user.addrule lowuser "!hehe.haha.specific"')
383
+ await core.callStorm('auth.user.addrule lowuser "hehe.something.else"')
384
+ await core.callStorm('auth.user.addrule lowuser "hehe.haha"')
385
+ await core.callStorm('auth.user.addrule lowuser "some.perm"')
386
+ await core.callStorm('auth.role.add ninjas')
387
+ await core.callStorm('auth.role.add clowns')
388
+ await core.callStorm('auth.user.grant --index 0 lowuser ninjas')
389
+ await core.callStorm('auth.user.grant --index 1 lowuser clowns')
390
+ await core.callStorm('auth.role.addrule ninjas some.rule')
391
+ await core.callStorm('auth.role.addrule ninjas --gate $gate another.rule',
392
+ opts={'vars': {'gate': fork}})
393
+
394
+ user = await core.auth.getUserByName('lowuser') # type: s_hiveauth.HiveUser
395
+ self.false(user.allowed(('hehe',)))
396
+ self.false(user.allowed(('hehe',), deepdeny=True))
397
+ self.true(user.allowed(('hehe', 'haha')))
398
+ self.false(user.allowed(('hehe', 'haha'), deepdeny=True))
399
+ self.true(user.allowed(('hehe', 'haha', 'wow')))
400
+ self.true(user.allowed(('hehe', 'haha', 'wow'), deepdeny=True))
401
+ self.true(user.allowed(('some', 'perm')))
402
+ self.true(user.allowed(('some', 'perm'), deepdeny=True))
403
+ self.true(user.allowed(('some', 'perm', 'here')))
404
+ self.true(user.allowed(('some', 'perm', 'here'), deepdeny=True))
405
+
406
+ await core.callStorm('auth.user.delrule lowuser hehe.haha')
407
+ await core.callStorm('auth.user.addrule lowuser hehe')
408
+ self.true(user.allowed(('hehe',)))
409
+ self.false(user.allowed(('hehe',), deepdeny=True))
410
+ self.true(user.allowed(('hehe', 'haha')))
411
+ self.false(user.allowed(('hehe', 'haha'), deepdeny=True))
412
+ self.true(user.allowed(('hehe', 'haha', 'wow')))
413
+ self.true(user.allowed(('hehe', 'haha', 'wow'), deepdeny=True))
414
+ self.false(user.allowed(('weee',)))
415
+ self.false(user.allowed(('weee',), deepdeny=True))
416
+ await core.callStorm('auth.user.delrule lowuser hehe')
417
+
418
+ await core.callStorm('auth.role.addrule all "!hehe.something.else.very.specific"')
419
+ self.false(user.allowed(('hehe',)))
420
+ self.false(user.allowed(('hehe',), deepdeny=True))
421
+
422
+ self.false(user.allowed(('hehe', 'something')))
423
+ self.true(user.allowed(('hehe', 'something', 'else')))
424
+ self.true(user.allowed(('hehe', 'something', 'else', 'very')))
425
+ self.true(user.allowed(('hehe', 'something', 'else', 'very', 'specific')))
426
+
427
+ self.false(user.allowed(('hehe', 'something')))
428
+ self.false(user.allowed(('hehe', 'something', 'else'), deepdeny=True))
429
+ self.false(user.allowed(('hehe', 'something', 'else', 'very'), deepdeny=True))
430
+
431
+ # There is NOT a deeper permission here, even though there is a deny rule on the role.
432
+ self.true(user.allowed(('hehe', 'something', 'else', 'very', 'specific'), deepdeny=True))
433
+ self.true(user.allowed(('hehe', 'something', 'else', 'very', 'specific', 'more')))
434
+ self.true(user.allowed(('hehe', 'something', 'else', 'very', 'specific', 'more'), deepdeny=True))
435
+ await core.callStorm('auth.role.delrule all "!hehe.something.else.very.specific"')
436
+
437
+ await core.callStorm('auth.role.addrule --gate $gate all "beep.boop"',
438
+ opts={'vars': {'gate': fork}})
439
+ await core.callStorm('auth.role.addrule --gate $gate all "!hehe.something.else.very.specific"',
440
+ opts={'vars': {'gate': fork}})
441
+ self.false(user.allowed(('hehe',), gateiden=fork))
442
+ self.false(user.allowed(('hehe', 'something'), gateiden=fork))
443
+ self.true(user.allowed(('hehe', 'something', 'else'), gateiden=fork))
444
+ self.true(user.allowed(('hehe', 'something', 'else', 'very'), gateiden=fork))
445
+ self.true(user.allowed(('hehe', 'something', 'else', 'very', 'specific'), gateiden=fork))
446
+ self.false(user.allowed(('hehe',), gateiden=fork, deepdeny=True))
447
+ self.false(user.allowed(('hehe', 'something'), gateiden=fork, deepdeny=True))
448
+ self.false(user.allowed(('hehe', 'something', 'else'), gateiden=fork, deepdeny=True))
449
+ self.false(user.allowed(('hehe', 'something', 'else', 'very'), gateiden=fork, deepdeny=True))
450
+ self.true(user.allowed(('hehe', 'something', 'else', 'very', 'specific'), gateiden=fork, deepdeny=True))
451
+ await core.callStorm('auth.role.delrule --gate $gate all "!hehe.something.else.very.specific"',
452
+ opts={'vars': {'gate': fork}})
453
+ await core.callStorm('auth.role.delrule --gate $gate all "beep.boop"',
454
+ opts={'vars': {'gate': fork}})
455
+
456
+ await core.callStorm('auth.user.addrule --gate $gate lowuser "beep.boop"',
457
+ opts={'vars': {'gate': fork}})
458
+ await core.callStorm('auth.user.addrule --gate $gate lowuser "!hehe.something.else.very.specific"',
459
+ opts={'vars': {'gate': fork}})
460
+ self.false(user.allowed(('hehe',), gateiden=fork))
461
+ self.false(user.allowed(('hehe', 'something'), gateiden=fork))
462
+ self.true(user.allowed(('hehe', 'something', 'else'), gateiden=fork))
463
+ self.true(user.allowed(('hehe', 'something', 'else', 'very'), gateiden=fork))
464
+ self.false(user.allowed(('hehe', 'something', 'else', 'very', 'specific'), gateiden=fork))
465
+ self.false(user.allowed(('hehe',), gateiden=fork, deepdeny=True))
466
+ self.false(user.allowed(('hehe', 'something'), gateiden=fork, deepdeny=True))
467
+ self.false(user.allowed(('hehe', 'something', 'else'), gateiden=fork, deepdeny=True))
468
+ self.false(user.allowed(('hehe', 'something', 'else', 'very'), gateiden=fork, deepdeny=True))
469
+ # This differs from earlier check as the dd is false; but the user authgate deny is earlier in precedence
470
+ # than the user specific allow
471
+ self.false(user.allowed(('hehe', 'something', 'else', 'very', 'specific'), gateiden=fork, deepdeny=True))
472
+
473
+ await core.callStorm('auth.user.delrule --gate $gate lowuser "!hehe.something.else.very.specific"',
474
+ opts={'vars': {'gate': fork}})
475
+ await core.callStorm('auth.user.delrule --gate $gate lowuser "beep.boop"',
476
+ opts={'vars': {'gate': fork}})
477
+
478
+ await core.callStorm('auth.user.mod --admin (true) lowuser --gate $gate', opts={'vars': {'gate': fork}})
479
+ self.true(user.allowed(('hehe',), gateiden=fork))
480
+ self.true(user.allowed(('hehe', 'something'), gateiden=fork))
481
+ self.true(user.allowed(('hehe', 'something', 'else'), gateiden=fork))
482
+ self.true(user.allowed(('hehe', 'something', 'else', 'very'), gateiden=fork))
483
+ self.true(user.allowed(('hehe', 'something', 'else', 'very', 'specific'), gateiden=fork))
484
+
485
+ self.true(user.allowed(('hehe',), gateiden=fork, deepdeny=True))
486
+ self.true(user.allowed(('hehe', 'something'), gateiden=fork, deepdeny=True))
487
+ self.true(user.allowed(('hehe', 'something', 'else'), gateiden=fork, deepdeny=True))
488
+ self.true(user.allowed(('hehe', 'something', 'else', 'very'), gateiden=fork, deepdeny=True))
489
+ self.true(user.allowed(('hehe', 'something', 'else', 'very', 'specific'), gateiden=fork, deepdeny=True))
490
+
491
+ await core.callStorm('auth.user.mod --admin (false) lowuser --gate $gate', opts={'vars': {'gate': fork}})
492
+
493
+ await core.callStorm('auth.user.mod --admin (true) lowuser')
494
+ self.true(user.allowed(('hehe',)))
495
+ self.true(user.allowed(('hehe', 'something')))
496
+ self.true(user.allowed(('hehe', 'something', 'else')))
497
+ self.true(user.allowed(('hehe', 'something', 'else', 'very')))
498
+ self.true(user.allowed(('hehe', 'something', 'else', 'very', 'specific')))
499
+ self.true(user.allowed(('hehe',), deepdeny=True))
500
+ self.true(user.allowed(('hehe', 'something')))
501
+ self.true(user.allowed(('hehe', 'something', 'else'), deepdeny=True))
502
+ self.true(user.allowed(('hehe', 'something', 'else', 'very'), deepdeny=True))
503
+ self.true(user.allowed(('hehe', 'something', 'else', 'very', 'specific'), deepdeny=True))
504
+ await core.callStorm('auth.user.mod --admin (false) lowuser')
505
+
506
+ await core.callStorm('auth.user.mod --locked (true) lowuser')
507
+ self.false(user.allowed(('hehe',), deepdeny=True))
508
+ self.false(user.allowed(('hehe', 'something'), deepdeny=True))
509
+ self.false(user.allowed(('hehe', 'something', 'else'), deepdeny=True))
510
+ self.false(user.allowed(('hehe', 'something', 'else', 'very'), deepdeny=True))
511
+ self.false(user.allowed(('hehe', 'something', 'else', 'very', 'specific'), deepdeny=True))
@@ -1920,6 +1920,19 @@ class LayerTest(s_t_utils.SynTest):
1920
1920
  async def __anit__(self, dirn=None, size=1, cell=None):
1921
1921
  await super().__anit__(dirn=dirn, size=size, cell=cell)
1922
1922
 
1923
+ seen = set()
1924
+ def confirm(self, perm, default=None, gateiden=None):
1925
+ seen.add(perm)
1926
+ return True
1927
+
1928
+ def confirmPropSet(self, user, prop, layriden):
1929
+ seen.add(prop.setperms[0])
1930
+ seen.add(prop.setperms[1])
1931
+
1932
+ def confirmPropDel(self, user, prop, layriden):
1933
+ seen.add(prop.delperms[0])
1934
+ seen.add(prop.delperms[1])
1935
+
1923
1936
  with mock.patch('synapse.lib.spooled.Dict', Dict):
1924
1937
  async with self.getTestCore() as core:
1925
1938
 
@@ -1952,19 +1965,7 @@ class LayerTest(s_t_utils.SynTest):
1952
1965
 
1953
1966
  parent = core.view.layers[0]
1954
1967
 
1955
- seen = set()
1956
- def confirm(self, perm, default=None, gateiden=None):
1957
- seen.add(perm)
1958
- return True
1959
-
1960
- def confirmPropSet(self, user, prop, layriden):
1961
- seen.add(prop.setperms[0])
1962
- seen.add(prop.setperms[1])
1963
-
1964
- def confirmPropDel(self, user, prop, layriden):
1965
- seen.add(prop.delperms[0])
1966
- seen.add(prop.delperms[1])
1967
-
1968
+ seen.clear()
1968
1969
  with mock.patch.object(s_hiveauth.HiveUser, 'confirm', confirm):
1969
1970
  with mock.patch.object(s_cortex.Cortex, 'confirmPropSet', confirmPropSet):
1970
1971
  with mock.patch.object(s_cortex.Cortex, 'confirmPropDel', confirmPropDel):
@@ -2075,6 +2076,59 @@ class LayerTest(s_t_utils.SynTest):
2075
2076
  ('node', 'tag', 'del', 'foo', 'bar'),
2076
2077
  })
2077
2078
 
2079
+ async with self.getTestCore() as core:
2080
+
2081
+ user = await core.auth.addUser('blackout@vertex.link')
2082
+ await user.addRule((False, ('node', 'edge', 'add', 'haha')))
2083
+ await user.addRule((False, ('node', 'data', 'set', 'hehe')))
2084
+ await user.addRule((True, ('node',)))
2085
+
2086
+ viewiden = await core.callStorm('''
2087
+ $lyr = $lib.layer.add()
2088
+ $view = $lib.view.add(($lyr.iden,))
2089
+ return($view.iden)
2090
+ ''')
2091
+
2092
+ layr = core.views[viewiden].layers[0]
2093
+
2094
+ opts = {'view': viewiden}
2095
+
2096
+ await core.nodes('[ test:str=bar +#foo.bar ]', opts=opts)
2097
+
2098
+ await core.nodes('''
2099
+ [ test:str=foo
2100
+ :hehe=bar
2101
+ +#foo.bar.baz
2102
+ <(refs)+ { test:str=bar }
2103
+ ]
2104
+ $node.data.set(foo, bar)
2105
+ ''', opts=opts)
2106
+
2107
+ parent = core.view.layers[0]
2108
+
2109
+ seen.clear()
2110
+ with mock.patch.object(s_hiveauth.HiveUser, 'confirm', confirm):
2111
+ with mock.patch.object(s_cortex.Cortex, 'confirmPropSet', confirmPropSet):
2112
+ with mock.patch.object(s_cortex.Cortex, 'confirmPropDel', confirmPropDel):
2113
+ await layr.confirmLayerEditPerms(user, parent.iden)
2114
+
2115
+ self.eq(seen, {
2116
+ # node.edge.add.* and node.data.set.* because of the deny rules
2117
+ ('node', 'edge', 'add', 'refs'),
2118
+ ('node', 'data', 'set', 'foo'),
2119
+ })
2120
+
2121
+ await user.delRule((False, ('node', 'edge', 'add', 'haha')))
2122
+ await user.delRule((False, ('node', 'data', 'set', 'hehe')))
2123
+
2124
+ seen.clear()
2125
+ with mock.patch.object(s_hiveauth.HiveUser, 'confirm', confirm):
2126
+ with mock.patch.object(s_cortex.Cortex, 'confirmPropSet', confirmPropSet):
2127
+ with mock.patch.object(s_cortex.Cortex, 'confirmPropDel', confirmPropDel):
2128
+ await layr.confirmLayerEditPerms(user, parent.iden)
2129
+
2130
+ self.eq(seen, set())
2131
+
2078
2132
  async def test_layer_v9(self):
2079
2133
  async with self.getRegrCore('2.101.1-hugenum-indxprec') as core:
2080
2134
 
@@ -375,19 +375,25 @@ class OuModelTest(s_t_utils.SynTest):
375
375
  props = {
376
376
  'org': guid0,
377
377
  'name': 'arrowcon 2018',
378
+ 'names': ('Arrow Conference 2018', 'ArrCon18', 'ArrCon18'),
378
379
  'base': 'arrowcon',
379
380
  'start': '20180301',
380
381
  'end': '20180303',
381
382
  'place': place0,
382
383
  'url': 'http://arrowcon.org/2018',
383
384
  }
384
- q = '''[(ou:conference=$valu :org=$p.org :name=$p.name :base=$p.base
385
- :start=$p.start :end=$p.end :place=$p.place :url=$p.url)]'''
385
+ q = '''[
386
+ ou:conference=$valu
387
+ :org=$p.org :name=$p.name :names=$p.names
388
+ :base=$p.base :start=$p.start :end=$p.end
389
+ :place=$p.place :url=$p.url
390
+ ]'''
386
391
  nodes = await core.nodes(q, opts={'vars': {'valu': c0, 'p': props}})
387
392
  self.len(1, nodes)
388
393
  node = nodes[0]
389
394
  self.eq(node.ndef[1], c0)
390
395
  self.eq(node.get('name'), 'arrowcon 2018')
396
+ self.eq(node.get('names'), ('arrcon18', 'arrow conference 2018',))
391
397
  self.eq(node.get('base'), 'arrowcon')
392
398
  self.eq(node.get('org'), guid0)
393
399
  self.eq(node.get('start'), 1519862400000)
@@ -153,6 +153,7 @@ class PsModelTest(s_t_utils.SynTest):
153
153
  :org=$p.org :asof=$p.asof :person=$p.person
154
154
  :place=$p.place :place:name=$p."place:name" :name=$p.name
155
155
  :title=$p.title :orgname=$p.orgname :user=$p.user
156
+ :titles=('hehe', 'hehe', 'haha')
156
157
  :web:acct=$p."web:acct" :web:group=$p."web:group"
157
158
  :dob=$p.dob :dod=$p.dod :url=$p.url
158
159
  :email=$p.email :email:work=$p."email:work"
@@ -177,6 +178,7 @@ class PsModelTest(s_t_utils.SynTest):
177
178
  self.eq(node.get('place:name'), 'the shire')
178
179
  self.eq(node.get('name'), 'tony stark')
179
180
  self.eq(node.get('title'), 'ceo')
181
+ self.eq(node.get('titles'), ('haha', 'hehe'))
180
182
  self.eq(node.get('orgname'), 'stark industries, inc')
181
183
  self.eq(node.get('user'), 'ironman')
182
184
  self.eq(node.get('web:acct'), ('twitter.com', 'ironman'))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: synapse
3
- Version: 2.172.0
3
+ Version: 2.173.1
4
4
  Summary: Synapse Intelligence Analysis Framework
5
5
  Author-email: The Vertex Project LLC <root@vertex.link>
6
6
  License: Apache License 2.0
@@ -108,13 +108,13 @@ synapse/lib/hashitem.py,sha256=3115F7E1hIR97hrJ7QY6j1amasm7uXy63SmpRD1nB_I,658
108
108
  synapse/lib/hashset.py,sha256=6R9q6iNfxZ5iGGEmoN7ODDCUnba1XBn7w2PPJh4Yz9o,1468
109
109
  synapse/lib/health.py,sha256=wkVZObjo3quEmxA0VOUnYHapnRinOVZdTFOVobjoTH4,1730
110
110
  synapse/lib/hive.py,sha256=7nrh6-is0qy4lZPg23M61wf9Jq7eDcL3D4EEbaFWKJM,19755
111
- synapse/lib/hiveauth.py,sha256=J7a9LGwLdsVr6EBLoVG4DXC2ML2ahOYq8XihNfNhS7c,40054
111
+ synapse/lib/hiveauth.py,sha256=gH6EtmRVqTl_iIouaVjB6VaxcKEibCzilPUXlXFLRPk,42601
112
112
  synapse/lib/httpapi.py,sha256=i0E2dFl54ovWjEPSq7VQ5kNs6FThjXDUkD7ml9XNLOI,43186
113
113
  synapse/lib/ingest.py,sha256=HNW1xs215c_UXVjKaxjipKBmVL4ujrjmarHBRvLPLkE,40
114
114
  synapse/lib/interval.py,sha256=PqpEhMGG6LD9mJxEms0oQWC-NB01H6gwsmLSc5LrDFk,1175
115
115
  synapse/lib/jsonstor.py,sha256=QQVf7Kl90sI_v97UZZAoa6stizDeMyy0ykR3RsHpmi8,19359
116
116
  synapse/lib/jupyter.py,sha256=EGAE1D-zlv1YVSDrTkr4bNMFsXvZuzBfIo5FhId2xSI,16358
117
- synapse/lib/layer.py,sha256=Uhu-YiORe1n5psXpBoOJL2PcimOpk08c6SBEjLXidyc,149709
117
+ synapse/lib/layer.py,sha256=VlPfVdjIeAGBr_grD7KcvBQ9cpVgdfrnwYndsibYXbY,149799
118
118
  synapse/lib/link.py,sha256=YiiAiFe1HTkEBeAtxKjNzWMH6i73Md93sZ5SKxtZylg,9802
119
119
  synapse/lib/lmdbslab.py,sha256=vFok5SUaRdQPN0V1nBzoFzQVq-7B1SpPldqJroalBLE,55307
120
120
  synapse/lib/modelrev.py,sha256=otBcP_q_bVgjfVLupFrB66u1I2zF0n0EedEzKkUrfDY,40887
@@ -156,7 +156,7 @@ synapse/lib/time.py,sha256=FKTYwpdvpuAj8p8sSodRjOxoA7Vu67CIbbXz55gtghk,9231
156
156
  synapse/lib/trigger.py,sha256=cqKW0e2KmeiPsU3d3vwpo0PLLHUs6ZPVWZyxAxU6gMA,20849
157
157
  synapse/lib/types.py,sha256=wSq8kpuIUzllx3j86kIg0M7QtYSg95OM2zfOKmnG8ek,68988
158
158
  synapse/lib/urlhelp.py,sha256=j-DvWGi-xH0TcO0NbCuwG7guUuiV8wxIxfMyJOzDygo,2523
159
- synapse/lib/version.py,sha256=giG3CvXyACJkQW1O-5r7BI6xvvK_ssN7coS6wjb1fbE,7162
159
+ synapse/lib/version.py,sha256=aczXBBErejzWJmCclNE1agmC5fp9CtUeOohoLcDC9gM,7162
160
160
  synapse/lib/view.py,sha256=gwiAmFyQNAatJcthjc98kHARz6TkiwjUhuYjeX6lbYM,57888
161
161
  synapse/lib/crypto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
162
162
  synapse/lib/crypto/coin.py,sha256=_dhlkzIrHT8BvHdJOWK7PDThz3sK3dDRnWAUqjRpZJc,4910
@@ -237,9 +237,9 @@ synapse/models/infotech.py,sha256=GW0DuwiiuJYrLMqMKJe9lNDU3D7H65SoyeJcDxQOVMk,14
237
237
  synapse/models/language.py,sha256=D2gyMrCQmKY_QjxN7OwJsCmh_pR9fjjZnxcKoJ9NItM,3430
238
238
  synapse/models/material.py,sha256=d-nonZKyJpHc32ebghtkm5tSifwVFN_ctx2kb8N4NqI,3214
239
239
  synapse/models/media.py,sha256=uIsayKqdDM36eGyrJwz2UqOa_QQ9MpMJ2uHdgGYDLa4,3605
240
- synapse/models/orgs.py,sha256=z3eQSHpVJnbM8-hYy9ayOmwJO3ch84ZHlAUNA0H3mAw,62908
241
- synapse/models/person.py,sha256=w6lihRSU_6wW9UOj_dogR1c7BfMAmUPXb3S9_LKk-zI,26847
242
- synapse/models/planning.py,sha256=kuBjIvU2juZvOYJ0DxE-JQRNF6ee1il5iKsIBZx3OLQ,7117
240
+ synapse/models/orgs.py,sha256=YDhzivAI3MHejAEU6BZI0K1LDlJj6qAibHJezb2uhWU,63091
241
+ synapse/models/person.py,sha256=dVoROHMNPfdE_xnB43T6a4GKNUPXw-OO_Ip81VqthHk,27008
242
+ synapse/models/planning.py,sha256=vmrY4d3WRxizrNU1YBe36NGZTuuu4lhGS8KI5lCZ5yQ,7302
243
243
  synapse/models/proj.py,sha256=eYY2bY7H8HJGvmuVQOrn4rQQWZQ_7noKtjLiq3QzgUU,9035
244
244
  synapse/models/risk.py,sha256=kzMooC8prYNf2yJq0o0G6GhjRSrQQMk2lmlOm6xIaZI,51197
245
245
  synapse/models/science.py,sha256=oSkKbjmVncYcVkVgx8rhM08XtcsgDgjf3mpunz5kL30,4329
@@ -300,12 +300,12 @@ synapse/tests/test_lib_hashitem.py,sha256=IyyueviwK8g-MpCkXU-jLfMDRFMuO8Bl3870Iu
300
300
  synapse/tests/test_lib_hashset.py,sha256=HwFsohiEzLyQ3evpvcezlj2iM7Li5IrN4rWh1jZnnPQ,1329
301
301
  synapse/tests/test_lib_health.py,sha256=yqNw6rXBm_2UBqPlWxeLpFhawKocdS1VitOCD3Lv8gE,2265
302
302
  synapse/tests/test_lib_hive.py,sha256=n8xuwhuLPLiqfO_OHcsDSMYIWpIpzVklHYpM-kZHq3U,6379
303
- synapse/tests/test_lib_hiveauth.py,sha256=srcgsVS-Zs1zpNnFSLfVrUfRRZeOLLTTYod0kOJ1XAs,15129
303
+ synapse/tests/test_lib_hiveauth.py,sha256=qISnELUaVfbutCR147CFh5cMD4TFewHlGBqAl-Elrnw,24640
304
304
  synapse/tests/test_lib_httpapi.py,sha256=iwHIQzY4x9CJqik436sEYIlFlYIbCrlZnbyuNmouuto,90691
305
305
  synapse/tests/test_lib_interval.py,sha256=PNEU24XXEGdlW7WkiYJGbhGljwBJpAWen9yTOqlNikQ,839
306
306
  synapse/tests/test_lib_jsonstor.py,sha256=1AebkkDAqFhd_mUyjpGviAUq_n0XFUzeuBxPevOu1vc,5866
307
307
  synapse/tests/test_lib_jupyter.py,sha256=GZ3jPpm9mUCF4bznZgLD8CDbuJFXiKvtXrtgevbkrGA,8738
308
- synapse/tests/test_lib_layer.py,sha256=vqy_CBTFuqJURvKwNowxg06kM1hfUtRr63qr1G2Hi7A,90475
308
+ synapse/tests/test_lib_layer.py,sha256=NUEBkrSBDzfQDOqFZrvyxw4UM80JJfcz88T0sNEVqKY,92526
309
309
  synapse/tests/test_lib_link.py,sha256=VtRFH8GEksxSmPXhEKr8GjuPBVprVjpHEecAuHdpYsI,8270
310
310
  synapse/tests/test_lib_lmdbslab.py,sha256=S9PRoXDC94nhFxy5LyAwkGs2WxwhZDdPl3PbTDcZZNc,62793
311
311
  synapse/tests/test_lib_modelrev.py,sha256=naTkrBGUW0dChVYgsPWr9xUEqOXOIxkenV4axCbIoDk,21345
@@ -399,8 +399,8 @@ synapse/tests/test_model_infotech.py,sha256=g0TLvnc2Em6TEEuNNtB5SmNrhySUGSpb56Rm
399
399
  synapse/tests/test_model_language.py,sha256=LQFF-hya6WsGnkebC8FwguDvYn5t3sXSIDeCZ_coJ54,2883
400
400
  synapse/tests/test_model_material.py,sha256=M7ACDCuMtavm-xtwAIZa1M-bHVq5PCUedDfR-Ty9_F4,1975
401
401
  synapse/tests/test_model_media.py,sha256=RQVhYjOZ4rkNPBSzz05rle1M7F6-Yxw545s7k-YBwzc,2627
402
- synapse/tests/test_model_orgs.py,sha256=JYlMxsHg9JWwRvrLd4wHmAzBl1aSQRYb2bs3D_BCDNc,39766
403
- synapse/tests/test_model_person.py,sha256=pRt9ZA9F11V-5pTsw6M43Ktsh3RnejikJPwPOYogIGo,17068
402
+ synapse/tests/test_model_orgs.py,sha256=v-dH3zhJ6t3_hUqq6wkRr3UnGggPVsDl1ORfM-BsGhs,40009
403
+ synapse/tests/test_model_person.py,sha256=mO6xFZgqz7SIePp0KnGfjfTlSFFlETxpF6gDq6esFWo,17179
404
404
  synapse/tests/test_model_planning.py,sha256=U2kkE0uBO6CqtTfy7wlnhEIu_NFdSri4I_I5b-mRjBE,5615
405
405
  synapse/tests/test_model_proj.py,sha256=bBPNzvcbd1jZReeJ7X-AQdH7F7ZMugZVgaCTvS-QNFM,22849
406
406
  synapse/tests/test_model_risk.py,sha256=jPhygdQSEob1lJ2RaNl7kR_t4GzPFmn2nHJPxd7HhWg,27736
@@ -567,8 +567,8 @@ synapse/vendor/xrpl/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
567
567
  synapse/vendor/xrpl/tests/test_codec.py,sha256=Zwq6A5uZUK_FWDL3BA932c5b-rL3hnC6efobWHSLC4o,6651
568
568
  synapse/vendor/xrpl/tests/test_main.py,sha256=kZQwWk7I6HrP-PMvLdsUUN4POvWD9I-iXDHOwdeF090,4299
569
569
  synapse/vendor/xrpl/tests/test_main_test_cases.py,sha256=vTlUM4hJD2Hd2wCIdd9rfsvcMZZZQmNHWdCTTFeGz2Y,4221
570
- synapse-2.172.0.dist-info/LICENSE,sha256=xllut76FgcGL5zbIRvuRc7aezPbvlMUTWJPsVr2Sugg,11358
571
- synapse-2.172.0.dist-info/METADATA,sha256=snBg3X0ZQ_tP0Eg4sratZG_ihTUMS3wC0Usz4RcbrkE,4921
572
- synapse-2.172.0.dist-info/WHEEL,sha256=edw-scxvibQFxgJJiYS4o0nVbA742tS_tqIs8D1-V3Q,93
573
- synapse-2.172.0.dist-info/top_level.txt,sha256=v_1YsqjmoSCzCKs7oIhzTNmWtSYoORiBMv1TJkOhx8A,8
574
- synapse-2.172.0.dist-info/RECORD,,
570
+ synapse-2.173.1.dist-info/LICENSE,sha256=xllut76FgcGL5zbIRvuRc7aezPbvlMUTWJPsVr2Sugg,11358
571
+ synapse-2.173.1.dist-info/METADATA,sha256=UIkCgZxhQU3hIV5ERAzQUbQOMaz-B79HL3fjWPJcsFA,4921
572
+ synapse-2.173.1.dist-info/WHEEL,sha256=_92Lck2xgIdXE3aYlakoaV5b8A5Iy6trBZ87W_vipAQ,93
573
+ synapse-2.173.1.dist-info/top_level.txt,sha256=v_1YsqjmoSCzCKs7oIhzTNmWtSYoORiBMv1TJkOhx8A,8
574
+ synapse-2.173.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (70.1.0)
2
+ Generator: setuptools (70.1.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py311-none-any
5
5