synapse 2.196.0__py311-none-any.whl → 2.198.0__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.

Files changed (61) hide show
  1. synapse/axon.py +3 -0
  2. synapse/common.py +3 -0
  3. synapse/cortex.py +13 -11
  4. synapse/cryotank.py +2 -2
  5. synapse/lib/aha.py +3 -0
  6. synapse/lib/ast.py +277 -165
  7. synapse/lib/auth.py +39 -11
  8. synapse/lib/cell.py +24 -6
  9. synapse/lib/config.py +3 -3
  10. synapse/lib/hive.py +2 -1
  11. synapse/lib/hiveauth.py +10 -1
  12. synapse/lib/jsonstor.py +6 -5
  13. synapse/lib/layer.py +6 -5
  14. synapse/lib/multislabseqn.py +2 -2
  15. synapse/lib/node.py +10 -4
  16. synapse/lib/parser.py +46 -21
  17. synapse/lib/schemas.py +491 -1
  18. synapse/lib/snap.py +68 -26
  19. synapse/lib/storm.lark +13 -11
  20. synapse/lib/storm.py +13 -395
  21. synapse/lib/storm_format.py +3 -2
  22. synapse/lib/stormlib/graph.py +0 -61
  23. synapse/lib/stormlib/index.py +52 -0
  24. synapse/lib/stormtypes.py +16 -5
  25. synapse/lib/task.py +13 -2
  26. synapse/lib/urlhelp.py +1 -1
  27. synapse/lib/version.py +2 -2
  28. synapse/models/doc.py +62 -0
  29. synapse/models/infotech.py +18 -0
  30. synapse/models/orgs.py +6 -4
  31. synapse/models/risk.py +9 -0
  32. synapse/models/syn.py +18 -2
  33. synapse/tests/files/stormpkg/badendpoints.yaml +7 -0
  34. synapse/tests/files/stormpkg/testpkg.yaml +8 -0
  35. synapse/tests/test_cortex.py +108 -0
  36. synapse/tests/test_datamodel.py +7 -0
  37. synapse/tests/test_lib_aha.py +12 -42
  38. synapse/tests/test_lib_ast.py +57 -0
  39. synapse/tests/test_lib_auth.py +143 -2
  40. synapse/tests/test_lib_boss.py +15 -6
  41. synapse/tests/test_lib_cell.py +43 -0
  42. synapse/tests/test_lib_grammar.py +54 -2
  43. synapse/tests/test_lib_lmdbslab.py +24 -0
  44. synapse/tests/test_lib_storm.py +20 -0
  45. synapse/tests/test_lib_stormlib_index.py +39 -0
  46. synapse/tests/test_lib_stormlib_macro.py +3 -3
  47. synapse/tests/test_lib_stormtypes.py +14 -2
  48. synapse/tests/test_lib_task.py +31 -13
  49. synapse/tests/test_model_doc.py +38 -0
  50. synapse/tests/test_model_infotech.py +13 -0
  51. synapse/tests/test_model_orgs.py +7 -0
  52. synapse/tests/test_model_risk.py +6 -0
  53. synapse/tests/test_model_syn.py +58 -0
  54. synapse/tests/test_tools_genpkg.py +10 -0
  55. synapse/tools/genpkg.py +2 -2
  56. synapse/tools/hive/load.py +1 -0
  57. {synapse-2.196.0.dist-info → synapse-2.198.0.dist-info}/METADATA +1 -1
  58. {synapse-2.196.0.dist-info → synapse-2.198.0.dist-info}/RECORD +61 -58
  59. {synapse-2.196.0.dist-info → synapse-2.198.0.dist-info}/LICENSE +0 -0
  60. {synapse-2.196.0.dist-info → synapse-2.198.0.dist-info}/WHEEL +0 -0
  61. {synapse-2.196.0.dist-info → synapse-2.198.0.dist-info}/top_level.txt +0 -0
synapse/lib/cell.py CHANGED
@@ -62,7 +62,7 @@ import synapse.tools.backup as s_t_backup
62
62
 
63
63
  logger = logging.getLogger(__name__)
64
64
 
65
- NEXUS_VERSION = (2, 177)
65
+ NEXUS_VERSION = (2, 198)
66
66
 
67
67
  SLAB_MAP_SIZE = 128 * s_const.mebibyte
68
68
  SSLCTX_CACHE_SIZE = 64
@@ -1625,16 +1625,34 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1625
1625
  pass
1626
1626
 
1627
1627
  async def setNexsVers(self, vers):
1628
- if self.nexsvers < NEXUS_VERSION:
1629
- await self._push('nexs:vers:set', NEXUS_VERSION)
1628
+ if self.nexsvers < vers:
1629
+ await self._push('nexs:vers:set', vers)
1630
1630
 
1631
1631
  @s_nexus.Pusher.onPush('nexs:vers:set')
1632
1632
  async def _setNexsVers(self, vers):
1633
1633
  if vers > self.nexsvers:
1634
- self.cellvers.set('nexus:version', vers)
1634
+ await self._migrNexsVers(vers)
1635
+ self.cellinfo.set('nexus:version', vers)
1635
1636
  self.nexsvers = vers
1636
1637
  await self.configNexsVers()
1637
1638
 
1639
+ async def _migrNexsVers(self, newvers):
1640
+ if self.nexsvers < (2, 198) and newvers >= (2, 198) and self.conf.get('auth:ctor') is None:
1641
+ # This "migration" will lock all archived users. Once the nexus version is bumped to
1642
+ # >=2.198, then the bottom-half nexus handler for user:info (Auth._setUserInfo()) will
1643
+ # begin rejecting unlock requests for archived users.
1644
+
1645
+ authkv = self.slab.getSafeKeyVal('auth')
1646
+ userkv = authkv.getSubKeyVal('user:info:')
1647
+
1648
+ for iden, info in userkv.items():
1649
+ if info.get('archived') and not info.get('locked'):
1650
+ info['locked'] = True
1651
+ userkv.set(iden, info)
1652
+
1653
+ # Clear the auth caches so the changes get picked up by the already running auth subsystem
1654
+ self.auth.clearAuthCache()
1655
+
1638
1656
  async def configNexsVers(self):
1639
1657
  for meth, orig in self.nexspatches:
1640
1658
  setattr(self, meth, orig)
@@ -4478,7 +4496,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
4478
4496
 
4479
4497
  for task in self.boss.ps():
4480
4498
 
4481
- item = task.pack()
4499
+ item = task.packv2()
4482
4500
  item['service'] = self.ahasvcname
4483
4501
 
4484
4502
  yield item
@@ -4500,7 +4518,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
4500
4518
 
4501
4519
  task = self.boss.get(iden)
4502
4520
  if task is not None:
4503
- item = task.pack()
4521
+ item = task.packv2()
4504
4522
  item['service'] = self.ahasvcname
4505
4523
  return item
4506
4524
 
synapse/lib/config.py CHANGED
@@ -32,16 +32,16 @@ def localSchemaRefHandler(uri):
32
32
  try:
33
33
  parts = urllib.parse.urlparse(uri)
34
34
  except ValueError:
35
- raise s_exc.BadUrl(f'Malformed URI: {uri}.') from None
35
+ raise s_exc.BadUrl(mesg=f'Malformed URI: {uri}.') from None
36
36
 
37
37
  filename = s_data.path('jsonschemas', parts.hostname, *parts.path.split('/'))
38
38
 
39
39
  # Check for path traversal. Unlikely, but still check
40
40
  if not filename.startswith(s_data.path('jsonschemas', parts.hostname)):
41
- raise s_exc.BadArg(f'Path traversal in schema URL: {uri}.')
41
+ raise s_exc.BadArg(mesg=f'Path traversal in schema URL: {uri}.')
42
42
 
43
43
  if not os.path.exists(filename) or not os.path.isfile(filename):
44
- raise s_exc.NoSuchFile(f'Local JSON schema not found for {uri}.')
44
+ raise s_exc.NoSuchFile(mesg=f'Local JSON schema not found for {uri}.')
45
45
 
46
46
  with open(filename, 'r') as fp:
47
47
  return json.load(fp)
synapse/lib/hive.py CHANGED
@@ -424,7 +424,7 @@ class Hive(s_nexus.Pusher, s_telepath.Aware):
424
424
  return node.valu
425
425
 
426
426
  async def getTeleApi(self, link, mesg, path):
427
-
427
+ s_common.deprecated('Hive.getTeleApi', curv='2.198.0', eolv='2.199.0')
428
428
  auth = await self.getHiveAuth()
429
429
 
430
430
  if not self.conf.get('auth:en'):
@@ -759,6 +759,7 @@ def iterpath(path):
759
759
  yield path[:i + 1]
760
760
 
761
761
  async def openurl(url, **opts):
762
+ s_common.deprecated('synapse.lib.hive.openurl()', curv='2.198.0', eolv='2.199.0')
762
763
  prox = await s_telepath.openurl(url, **opts)
763
764
  return await TeleHive.anit(prox)
764
765
 
synapse/lib/hiveauth.py CHANGED
@@ -1,3 +1,9 @@
1
+ # pragma: no cover
2
+
3
+ ###
4
+ ### THIS WHOLE MODULE IS DEPRECATED AND EXPECTED TO BE REMOVED O/A v2.198.0
5
+ ###
6
+
1
7
  import logging
2
8
  import dataclasses
3
9
 
@@ -30,12 +36,13 @@ reqValidRules = s_config.getJsValidator({
30
36
 
31
37
  def getShadow(passwd): # pragma: no cover
32
38
  '''This API is deprecated.'''
33
- s_common.deprecated('hiveauth.getShadow()', curv='2.110.0')
39
+ s_common.deprecated('hiveauth.getShadow()', curv='2.110.0', eolv='2.199.0')
34
40
  salt = s_common.guid()
35
41
  hashed = s_common.guid((salt, passwd))
36
42
  return (salt, hashed)
37
43
 
38
44
  def textFromRule(rule):
45
+ s_common.deprecated('hiveauth.textFromRule()', curv='2.198.0', eolv='2.199.0') # pragma: no cover
39
46
  text = '.'.join(rule[1])
40
47
  if not rule[0]:
41
48
  text = '!' + text
@@ -43,6 +50,7 @@ def textFromRule(rule):
43
50
 
44
51
  @dataclasses.dataclass(slots=True)
45
52
  class _allowedReason:
53
+ s_common.deprecated('hiveauth._allowedReason()', curv='2.198.0', eolv='2.199.0')
46
54
  value: Union[bool | None]
47
55
  default: bool = False
48
56
  isadmin: bool = False
@@ -135,6 +143,7 @@ class Auth(s_nexus.Pusher):
135
143
  Args:
136
144
  node (HiveNode): The root of the persistent storage for auth
137
145
  '''
146
+ s_common.deprecated('Auth.__anit__()', curv='2.198.0', eolv='2.199.0')
138
147
  # Derive an iden from the parent
139
148
  iden = 'auth:' + ':'.join(node.full)
140
149
  await s_nexus.Pusher.__anit__(self, iden, nexsroot=nexsroot)
synapse/lib/jsonstor.py CHANGED
@@ -46,7 +46,7 @@ class JsonStor(s_base.Base):
46
46
  self.dirty.pop(buid, None)
47
47
  await asyncio.sleep(0)
48
48
 
49
- def _incRefObj(self, buid, valu=1):
49
+ async def _incRefObj(self, buid, valu=1):
50
50
 
51
51
  refs = 0
52
52
 
@@ -62,6 +62,7 @@ class JsonStor(s_base.Base):
62
62
  # remove the meta entries
63
63
  for lkey, byts in self.slab.scanByPref(buid, db=self.metadb):
64
64
  self.slab.pop(lkey, db=self.metadb)
65
+ await asyncio.sleep(0)
65
66
 
66
67
  # remove the item data
67
68
  self.slab.pop(buid, db=self.itemdb)
@@ -88,7 +89,7 @@ class JsonStor(s_base.Base):
88
89
 
89
90
  oldb = self.slab.replace(pkey, buid, db=self.pathdb)
90
91
  if oldb is not None:
91
- self._incRefObj(oldb, -1)
92
+ await self._incRefObj(oldb, -1)
92
93
 
93
94
  self.slab.put(buid + b'refs', s_msgpack.en(1), db=self.metadb)
94
95
 
@@ -124,7 +125,7 @@ class JsonStor(s_base.Base):
124
125
  pkey = self._pathToPkey(path)
125
126
  buid = self.slab.pop(pkey, db=self.pathdb)
126
127
  if buid is not None:
127
- self._incRefObj(buid, valu=-1)
128
+ await self._incRefObj(buid, valu=-1)
128
129
 
129
130
  async def setPathLink(self, srcpath, dstpath):
130
131
  '''
@@ -140,9 +141,9 @@ class JsonStor(s_base.Base):
140
141
 
141
142
  oldb = self.slab.pop(srcpkey, db=self.pathdb)
142
143
  if oldb is not None:
143
- self._incRefObj(oldb, valu=-1)
144
+ await self._incRefObj(oldb, valu=-1)
144
145
 
145
- self._incRefObj(buid, valu=1)
146
+ await self._incRefObj(buid, valu=1)
146
147
  self.slab.put(srcpkey, buid, db=self.pathdb)
147
148
 
148
149
  async def getPathObjProp(self, path, prop):
synapse/lib/layer.py CHANGED
@@ -3543,9 +3543,8 @@ class Layer(s_nexus.Pusher):
3543
3543
  if self.nodeDelHook is not None:
3544
3544
  self.nodeDelHook()
3545
3545
 
3546
- self._wipeNodeData(buid)
3547
- # TODO edits to become async so we can sleep(0) on large deletes?
3548
- self._delNodeEdges(buid)
3546
+ await self._wipeNodeData(buid)
3547
+ await self._delNodeEdges(buid)
3549
3548
 
3550
3549
  self.buidcache.pop(buid, None)
3551
3550
 
@@ -3957,13 +3956,14 @@ class Layer(s_nexus.Pusher):
3957
3956
  for _, lval in self.layrslab.scanByDups(verb.encode(), db=self.byverb):
3958
3957
  yield (s_common.ehex(lval[:32]), verb, s_common.ehex(lval[32:]))
3959
3958
 
3960
- def _delNodeEdges(self, buid):
3959
+ async def _delNodeEdges(self, buid):
3961
3960
  for lkey, n2buid in self.layrslab.scanByPref(buid, db=self.edgesn1):
3962
3961
  venc = lkey[32:]
3963
3962
  self.layrslab.delete(venc, buid + n2buid, db=self.byverb)
3964
3963
  self.layrslab.delete(lkey, n2buid, db=self.edgesn1)
3965
3964
  self.layrslab.delete(n2buid + venc, buid, db=self.edgesn2)
3966
3965
  self.layrslab.delete(buid + n2buid, venc, db=self.edgesn1n2)
3966
+ await asyncio.sleep(0)
3967
3967
 
3968
3968
  def getStorIndx(self, stortype, valu):
3969
3969
 
@@ -4473,7 +4473,7 @@ class Layer(s_nexus.Pusher):
4473
4473
 
4474
4474
  await self.waitfini(1)
4475
4475
 
4476
- def _wipeNodeData(self, buid):
4476
+ async def _wipeNodeData(self, buid):
4477
4477
  '''
4478
4478
  Remove all node data for a buid
4479
4479
  '''
@@ -4482,6 +4482,7 @@ class Layer(s_nexus.Pusher):
4482
4482
  buid = lkey[:32]
4483
4483
  self.dataslab.delete(lkey, db=self.nodedata)
4484
4484
  self.dataslab.delete(abrv, buid, db=self.dataname)
4485
+ await asyncio.sleep(0)
4485
4486
 
4486
4487
  async def getModelVers(self):
4487
4488
  return self.layrinfo.get('model:version', (-1, -1, -1))
@@ -150,7 +150,7 @@ class MultiSlabSeqn(s_base.Base):
150
150
  firstidx = firstitem[0] # might not match the separately stored first index due to culling
151
151
 
152
152
  if firstidx < fnstartidx:
153
- raise s_exc.BadCoreStore('Multislab: filename inconsistent with contents')
153
+ raise s_exc.BadCoreStore(mesg='Multislab: filename inconsistent with contents')
154
154
 
155
155
  lastidx = seqn.index() - 1
156
156
 
@@ -161,7 +161,7 @@ class MultiSlabSeqn(s_base.Base):
161
161
  self.firstindx = lowindx
162
162
 
163
163
  if self.firstindx > self.indx:
164
- raise s_exc.BadCoreStore('Invalid firstindx value')
164
+ raise s_exc.BadCoreStore(mesg='Invalid firstindx value')
165
165
 
166
166
  await self._initTailSlab(fnstartidx)
167
167
 
synapse/lib/node.py CHANGED
@@ -63,18 +63,24 @@ class Node:
63
63
  def __repr__(self):
64
64
  return f'Node{{{self.pack()}}}'
65
65
 
66
- async def addEdge(self, verb, n2iden):
66
+ async def addEdge(self, verb, n2iden, extra=None):
67
67
  if self.form.isrunt:
68
68
  mesg = f'Edges cannot be used with runt nodes: {self.form.full}'
69
- raise s_exc.IsRuntForm(mesg=mesg, form=self.form.full)
69
+ exc = s_exc.IsRuntForm(mesg=mesg, form=self.form.full)
70
+ if extra is not None:
71
+ exc = extra(exc)
72
+ raise exc
70
73
 
71
74
  async with self.snap.getNodeEditor(self) as editor:
72
75
  return await editor.addEdge(verb, n2iden)
73
76
 
74
- async def delEdge(self, verb, n2iden):
77
+ async def delEdge(self, verb, n2iden, extra=None):
75
78
  if self.form.isrunt:
76
79
  mesg = f'Edges cannot be used with runt nodes: {self.form.full}'
77
- raise s_exc.IsRuntForm(mesg=mesg, form=self.form.full)
80
+ exc = s_exc.IsRuntForm(mesg=mesg, form=self.form.full)
81
+ if extra is not None:
82
+ exc = extra(exc)
83
+ raise exc
78
84
 
79
85
  async with self.snap.getNodeEditor(self) as editor:
80
86
  return await editor.delEdge(verb, n2iden)
synapse/lib/parser.py CHANGED
@@ -6,6 +6,7 @@ import lark # type: ignore
6
6
  import regex # type: ignore
7
7
 
8
8
  import synapse.exc as s_exc
9
+ import synapse.common as s_common
9
10
 
10
11
  import synapse.lib.ast as s_ast
11
12
  import synapse.lib.coro as s_coro
@@ -72,6 +73,7 @@ terminalEnglishMap = {
72
73
  'LSQB': '[',
73
74
  'MCASEBARE': 'case multi-value',
74
75
  'MODSET': '+= or -=',
76
+ 'MODSETMULTI': '++= or --=',
75
77
  'NONQUOTEWORD': 'unquoted value',
76
78
  'NOT': 'not',
77
79
  'NULL': 'null',
@@ -92,8 +94,8 @@ terminalEnglishMap = {
92
94
  'TRY': 'try',
93
95
  'TRIPLEQUOTEDSTRING': 'triple-quoted string',
94
96
  'TRYSET': '?=',
95
- 'TRYSETPLUS': '?+=',
96
- 'TRYSETMINUS': '?-=',
97
+ 'TRYMODSET': '?+= or ?-=',
98
+ 'TRYMODSETMULTI': '?++= or ?--=',
97
99
  'UNIVNAME': 'universal property',
98
100
  'UNSET': 'unset',
99
101
  'EXPRUNIVNAME': 'universal property',
@@ -158,7 +160,7 @@ class AstConverter(lark.Transformer):
158
160
 
159
161
  # Keep the original text for error printing and weird subquery argv parsing
160
162
  self.text = text
161
- self.texthash = hashlib.md5(text.encode(errors='surrogatepass'), usedforsecurity=False).hexdigest()
163
+ self.texthash = s_common.queryhash(text)
162
164
 
163
165
  def metaToAstInfo(self, meta, isterm=False):
164
166
  if isinstance(meta, lark.tree.Meta) and meta.empty:
@@ -248,9 +250,14 @@ class AstConverter(lark.Transformer):
248
250
  @lark.v_args(meta=True)
249
251
  def embedquery(self, meta, kids):
250
252
  assert len(kids) == 1
251
- astinfo = self.metaToAstInfo(meta)
252
- text = kids[0].getAstText()
253
- return s_ast.EmbedQuery(kids[0].astinfo, text, kids=kids)
253
+ astinfo = AstInfo(self.text,
254
+ meta.start_pos + 2, meta.end_pos - 1,
255
+ meta.line, meta.end_line,
256
+ meta.column, meta.end_column, False)
257
+
258
+ kids[0].astinfo = astinfo
259
+
260
+ return s_ast.EmbedQuery(astinfo, kids[0].getAstText(), kids=kids)
254
261
 
255
262
  @lark.v_args(meta=True)
256
263
  def funccall(self, meta, kids):
@@ -489,18 +496,28 @@ class Parser:
489
496
  Convert lark exception to synapse BadSyntax exception
490
497
  '''
491
498
  mesg = regex.split('[\n]', str(e))[0]
492
- at = len(self.text)
493
- line = None
494
- column = None
499
+ soff = eoff = len(self.text)
500
+ sline = eline = None
501
+ scol = ecol = None
495
502
  token = None
496
503
  if isinstance(e, lark.exceptions.UnexpectedToken):
497
504
  expected = sorted(set(terminalEnglishMap[t] for t in e.expected))
498
- at = e.pos_in_stream
499
- line = e.line
500
- column = e.column
501
505
  token = e.token.value
502
- valu = terminalEnglishMap.get(e.token.type, e.token.value)
503
- mesg = f"Unexpected token '{valu}' at line {line}, column {column}," \
506
+ soff = e.pos_in_stream
507
+ eoff = soff + len(token)
508
+
509
+ lines = token.splitlines()
510
+ sline = e.line
511
+ eline = sline + len(lines) - 1
512
+
513
+ scol = e.column
514
+ if len(lines) > 1:
515
+ ecol = len(lines[-1])
516
+ else:
517
+ ecol = scol + len(token)
518
+
519
+ valu = terminalEnglishMap.get(e.token.type, token)
520
+ mesg = f"Unexpected token '{valu}' at line {sline}, column {scol}," \
504
521
  f' expecting one of: {", ".join(expected)}'
505
522
 
506
523
  elif isinstance(e, lark.exceptions.VisitError):
@@ -514,16 +531,23 @@ class Parser:
514
531
  elif isinstance(e, lark.exceptions.UnexpectedCharacters): # pragma: no cover
515
532
  expected = sorted(set(terminalEnglishMap[t] for t in e.allowed))
516
533
  mesg += f'. Expecting one of: {", ".join(expected)}'
517
- at = e.pos_in_stream
518
- line = e.line
519
- column = e.column
534
+ soff = eoff = e.pos_in_stream
535
+ sline = eline = e.line
536
+ scol = ecol = e.column
520
537
  elif isinstance(e, lark.exceptions.UnexpectedEOF): # pragma: no cover
521
538
  expected = sorted(set(terminalEnglishMap[t] for t in e.expected))
522
539
  mesg += ' ' + ', '.join(expected)
523
- line = e.line
524
- column = e.column
525
-
526
- return s_exc.BadSyntax(at=at, text=self.text, mesg=mesg, line=line, column=column, token=token)
540
+ sline = eline = e.line
541
+ scol = ecol = e.column
542
+
543
+ highlight = {
544
+ 'hash': s_common.queryhash(self.text),
545
+ 'lines': (sline, eline),
546
+ 'columns': (scol, ecol),
547
+ 'offsets': (soff, eoff),
548
+ }
549
+ return s_exc.BadSyntax(at=soff, text=self.text, mesg=mesg, line=sline,
550
+ column=scol, token=token, highlight=highlight)
527
551
 
528
552
  def eval(self):
529
553
  try:
@@ -661,6 +685,7 @@ ruleClassMap = {
661
685
  'editpropdel': lambda astinfo, kids: s_ast.EditPropDel(astinfo, kids[1:]),
662
686
  'editpropset': s_ast.EditPropSet,
663
687
  'editcondpropset': s_ast.EditCondPropSet,
688
+ 'editpropsetmulti': s_ast.EditPropSetMulti,
664
689
  'edittagadd': s_ast.EditTagAdd,
665
690
  'edittagdel': lambda astinfo, kids: s_ast.EditTagDel(astinfo, kids[1:]),
666
691
  'edittagpropset': s_ast.EditTagPropSet,