synapse 2.165.0__py311-none-any.whl → 2.167.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 (77) hide show
  1. synapse/axon.py +4 -10
  2. synapse/cmds/cortex.py +1 -6
  3. synapse/common.py +6 -0
  4. synapse/cortex.py +104 -57
  5. synapse/datamodel.py +32 -0
  6. synapse/exc.py +1 -0
  7. synapse/lib/agenda.py +81 -51
  8. synapse/lib/aha.py +2 -0
  9. synapse/lib/ast.py +21 -23
  10. synapse/lib/base.py +11 -10
  11. synapse/lib/cell.py +24 -24
  12. synapse/lib/hive.py +11 -0
  13. synapse/lib/httpapi.py +1 -0
  14. synapse/lib/nexus.py +3 -2
  15. synapse/lib/node.py +4 -2
  16. synapse/lib/schemas.py +3 -1
  17. synapse/lib/snap.py +50 -0
  18. synapse/lib/storm.py +19 -17
  19. synapse/lib/stormlib/aha.py +370 -17
  20. synapse/lib/stormlib/auth.py +11 -4
  21. synapse/lib/stormlib/cache.py +202 -0
  22. synapse/lib/stormlib/cortex.py +69 -7
  23. synapse/lib/stormlib/macro.py +11 -18
  24. synapse/lib/stormlib/spooled.py +109 -0
  25. synapse/lib/stormlib/stix.py +1 -1
  26. synapse/lib/stormtypes.py +61 -17
  27. synapse/lib/trigger.py +10 -12
  28. synapse/lib/types.py +3 -1
  29. synapse/lib/version.py +2 -2
  30. synapse/lib/view.py +16 -3
  31. synapse/models/base.py +8 -0
  32. synapse/models/files.py +3 -0
  33. synapse/models/inet.py +74 -2
  34. synapse/models/orgs.py +52 -8
  35. synapse/models/person.py +30 -11
  36. synapse/models/risk.py +44 -3
  37. synapse/telepath.py +115 -32
  38. synapse/tests/files/stormpkg/dotstorm/dotstorm.yaml +3 -0
  39. synapse/tests/test_cortex.py +79 -8
  40. synapse/tests/test_datamodel.py +22 -0
  41. synapse/tests/test_lib_agenda.py +8 -1
  42. synapse/tests/test_lib_aha.py +19 -6
  43. synapse/tests/test_lib_cell.py +6 -2
  44. synapse/tests/test_lib_grammar.py +62 -64
  45. synapse/tests/test_lib_httpapi.py +1 -1
  46. synapse/tests/test_lib_rstorm.py +4 -4
  47. synapse/tests/test_lib_storm.py +98 -7
  48. synapse/tests/test_lib_stormlib_aha.py +196 -0
  49. synapse/tests/test_lib_stormlib_cache.py +272 -0
  50. synapse/tests/test_lib_stormlib_compression.py +12 -12
  51. synapse/tests/test_lib_stormlib_cortex.py +71 -0
  52. synapse/tests/test_lib_stormlib_macro.py +94 -0
  53. synapse/tests/test_lib_stormlib_spooled.py +190 -0
  54. synapse/tests/test_lib_stormtypes.py +71 -37
  55. synapse/tests/test_lib_view.py +50 -3
  56. synapse/tests/test_model_files.py +3 -0
  57. synapse/tests/test_model_inet.py +67 -0
  58. synapse/tests/test_model_risk.py +6 -0
  59. synapse/tests/test_telepath.py +30 -7
  60. synapse/tests/test_tools_genpkg.py +26 -0
  61. synapse/tests/test_tools_hiveload.py +1 -0
  62. synapse/tests/test_tools_hivesave.py +1 -0
  63. synapse/tests/test_tools_modrole.py +81 -0
  64. synapse/tests/test_tools_moduser.py +105 -0
  65. synapse/tests/utils.py +22 -3
  66. synapse/tools/autodoc.py +1 -1
  67. synapse/tools/hive/load.py +3 -0
  68. synapse/tools/hive/save.py +3 -0
  69. synapse/tools/modrole.py +59 -7
  70. synapse/tools/moduser.py +78 -10
  71. {synapse-2.165.0.dist-info → synapse-2.167.0.dist-info}/METADATA +3 -3
  72. {synapse-2.165.0.dist-info → synapse-2.167.0.dist-info}/RECORD +75 -72
  73. synapse/lib/provenance.py +0 -111
  74. synapse/tests/test_lib_provenance.py +0 -37
  75. {synapse-2.165.0.dist-info → synapse-2.167.0.dist-info}/LICENSE +0 -0
  76. {synapse-2.165.0.dist-info → synapse-2.167.0.dist-info}/WHEEL +0 -0
  77. {synapse-2.165.0.dist-info → synapse-2.167.0.dist-info}/top_level.txt +0 -0
synapse/lib/storm.py CHANGED
@@ -29,7 +29,6 @@ import synapse.lib.version as s_version
29
29
  import synapse.lib.hashitem as s_hashitem
30
30
  import synapse.lib.hiveauth as s_hiveauth
31
31
  import synapse.lib.stormctrl as s_stormctrl
32
- import synapse.lib.provenance as s_provenance
33
32
  import synapse.lib.stormtypes as s_stormtypes
34
33
 
35
34
  import synapse.lib.stormlib.graph as s_stormlib_graph
@@ -1208,6 +1207,8 @@ stormcmds = (
1208
1207
  'descr': addcrondescr,
1209
1208
  'cmdargs': (
1210
1209
  ('query', {'help': 'Query for the cron job to execute.'}),
1210
+ ('--pool', {'action': 'store_true', 'default': False,
1211
+ 'help': 'Allow the cron job to be run by a mirror from the query pool.'}),
1211
1212
  ('--minute', {'help': 'Minute value for job or recurrence period.'}),
1212
1213
  ('--name', {'help': 'An optional name for the cron job.'}),
1213
1214
  ('--doc', {'help': 'An optional doc string for the cron job.'}),
@@ -1227,6 +1228,7 @@ stormcmds = (
1227
1228
  minute=$cmdopts.minute,
1228
1229
  hour=$cmdopts.hour,
1229
1230
  day=$cmdopts.day,
1231
+ pool=$cmdopts.pool,
1230
1232
  month=$cmdopts.month,
1231
1233
  year=$cmdopts.year,
1232
1234
  hourly=$cmdopts.hourly,
@@ -1374,6 +1376,7 @@ stormcmds = (
1374
1376
  $lib.print('iden: {iden}', iden=$job.iden)
1375
1377
  $lib.print('user: {user}', user=$job.user)
1376
1378
  $lib.print('enabled: {enabled}', enabled=$job.enabled)
1379
+ $lib.print(`pool: {$job.pool}`)
1377
1380
  $lib.print('recurring: {isrecur}', isrecur=$job.isrecur)
1378
1381
  $lib.print('# starts: {startcount}', startcount=$job.startcount)
1379
1382
  $lib.print('# errors: {errcount}', errcount=$job.errcount)
@@ -2310,27 +2313,26 @@ class Runtime(s_base.Base):
2310
2313
 
2311
2314
  async def execute(self, genr=None):
2312
2315
  try:
2313
- with s_provenance.claim('storm', q=self.query.text, user=self.user.iden):
2314
2316
 
2315
- async with contextlib.aclosing(self.query.iterNodePaths(self, genr=genr)) as nodegenr:
2316
- nodegenr, empty = await s_ast.pullone(nodegenr)
2317
+ async with contextlib.aclosing(self.query.iterNodePaths(self, genr=genr)) as nodegenr:
2318
+ nodegenr, empty = await s_ast.pullone(nodegenr)
2317
2319
 
2318
- if empty:
2319
- return
2320
+ if empty:
2321
+ return
2320
2322
 
2321
- rules = self.opts.get('graph')
2322
- if rules not in (False, None):
2323
- if rules is True:
2324
- rules = {'degrees': None, 'refs': True}
2325
- elif isinstance(rules, str):
2326
- rules = await self.snap.core.getStormGraph(rules)
2323
+ rules = self.opts.get('graph')
2324
+ if rules not in (False, None):
2325
+ if rules is True:
2326
+ rules = {'degrees': None, 'refs': True}
2327
+ elif isinstance(rules, str):
2328
+ rules = await self.snap.core.getStormGraph(rules)
2327
2329
 
2328
- subgraph = s_ast.SubGraph(rules)
2329
- nodegenr = subgraph.run(self, nodegenr)
2330
+ subgraph = s_ast.SubGraph(rules)
2331
+ nodegenr = subgraph.run(self, nodegenr)
2330
2332
 
2331
- async for item in nodegenr:
2332
- self.tick()
2333
- yield item
2333
+ async for item in nodegenr:
2334
+ self.tick()
2335
+ yield item
2334
2336
 
2335
2337
  except RecursionError:
2336
2338
  mesg = 'Maximum Storm pipeline depth exceeded.'
@@ -1,42 +1,169 @@
1
- import synapse.common as s_common
1
+ import synapse.exc as s_exc
2
2
  import synapse.lib.stormtypes as s_stormtypes
3
3
 
4
+ @s_stormtypes.registry.registerLib
5
+ class AhaLib(s_stormtypes.Lib):
6
+ '''
7
+ A Storm Library for interacting with AHA.
8
+ '''
9
+
10
+ _storm_locals = (
11
+ {'name': 'del', 'desc': '''Delete a service from AHA.
12
+
13
+ Examples:
14
+ Deleting a service with its relative name::
15
+
16
+ $lib.aha.del(00.mysvc...)
17
+
18
+ Deleting a service with its full name::
19
+
20
+ $lib.aha.del(00.mysvc.loop.vertex.link)
21
+ ''',
22
+ 'type': {'type': 'function', '_funcname': '_methAhaDel',
23
+ 'args': (
24
+ {'name': 'svcname', 'type': 'str',
25
+ 'desc': 'The name of the service to delete. It is easiest to use the relative name of a service, ending with "...".', },
26
+ ),
27
+ 'returns': {'type': 'null'}}},
28
+ {'name': 'get', 'desc': '''Get information about an AHA service.
29
+
30
+ Examples:
31
+ Getting service information with a relative name::
32
+
33
+ $lib.aha.get(00.cortex...)
34
+
35
+ Getting service information with its full name::
36
+
37
+ $lib.aha.get(00.cortex.loop.vertex.link)
38
+ ''',
39
+ 'type': {'type': 'function', '_funcname': '_methAhaGet',
40
+ 'args': (
41
+ {'name': 'svcname', 'type': 'str',
42
+ 'desc': 'The name of the AHA service to look up. It is easiest to use the relative name of a service, ending with "...".', },
43
+ {'name': 'filters', 'type': 'dict', 'default': None,
44
+ 'desc': 'An optional dictionary of filters to use when resolving the AHA service.'}
45
+ ),
46
+ 'returns': {'type': ('null', 'dict'),
47
+ 'desc': 'The AHA service information dictionary, or $lib.null.', }}},
48
+ {'name': 'list', 'desc': 'Enumerate all of the AHA services.',
49
+ 'type': {'type': 'function', '_funcname': '_methAhaList', 'args': (),
50
+ 'returns': {'name': 'Yields', 'type': 'list',
51
+ 'desc': 'The AHA service dictionaries.', }}},
52
+ )
53
+ _storm_lib_path = ('aha',)
54
+ def getObjLocals(self):
55
+ return {
56
+ 'del': self._methAhaDel,
57
+ 'get': self._methAhaGet,
58
+ 'list': self._methAhaList,
59
+ }
60
+
61
+ @s_stormtypes.stormfunc(readonly=True)
62
+ async def _methAhaList(self):
63
+ self.runt.reqAdmin()
64
+ proxy = await self.runt.snap.core.reqAhaProxy()
65
+ async for info in proxy.getAhaSvcs():
66
+ yield info
67
+
68
+ async def _methAhaDel(self, svcname):
69
+ self.runt.reqAdmin()
70
+ svcname = await s_stormtypes.tostr(svcname)
71
+ proxy = await self.runt.snap.core.reqAhaProxy()
72
+ svc = await proxy.getAhaSvc(svcname)
73
+ if svc is None:
74
+ raise s_exc.NoSuchName(mesg=f'No AHA service for {svcname=}')
75
+ if svc.get('services'): # It is an AHA Pool!
76
+ mesg = f'Cannot use $lib.aha.del() to remove an AHA Pool. Use $lib.aha.pool.del(); {svcname=}'
77
+ raise s_exc.BadArg(mesg=mesg)
78
+ return await proxy.delAhaSvc(svc.get('svcname'), network=svc.get('svcnetw'))
79
+
80
+ @s_stormtypes.stormfunc(readonly=True)
81
+ async def _methAhaGet(self, svcname, filters=None):
82
+ self.runt.reqAdmin()
83
+ svcname = await s_stormtypes.tostr(svcname)
84
+ filters = await s_stormtypes.toprim(filters)
85
+ proxy = await self.runt.snap.core.reqAhaProxy()
86
+ return await proxy.getAhaSvc(svcname, filters=filters)
87
+
4
88
  @s_stormtypes.registry.registerLib
5
89
  class AhaPoolLib(s_stormtypes.Lib):
6
90
  '''
7
91
  A Storm Library for interacting with AHA service pools.
8
92
  '''
93
+
94
+ _storm_locals = (
95
+ {'name': 'add', 'desc': '''Add a new AHA service pool.
96
+
97
+ Examples:
98
+ Add a pool via its relative name::
99
+
100
+ $lib.aha.pool.add(pool00.cortex...)
101
+ ''',
102
+ 'type': {'type': 'function', '_funcname': '_methPoolAdd',
103
+ 'args': (
104
+ {'name': 'name', 'type': 'str',
105
+ 'desc': 'The name of the pool to add. It is easiest to use the relative name of a pool, ending with "...".', },
106
+ ),
107
+ 'returns': {'type': 'aha:pool'}}},
108
+ {'name': 'del', 'desc': '''Delete an existing AHA service pool.
109
+
110
+ Examples:
111
+ Delete a pool via its relative name::
112
+
113
+ $lib.aha.pool.del(pool00.cortex...)
114
+ ''',
115
+ 'type': {'type': 'function', '_funcname': '_methPoolDel',
116
+ 'args': (
117
+ {'name': 'name', 'type': 'str',
118
+ 'desc': 'The name of the pool to delete. It is easiest to use the relative name of a pool, ending with "...".', },
119
+ ),
120
+ 'returns': {'type': 'dict', 'desc': 'The AHA pool definition that was deleted.'}}},
121
+ {'name': 'get', 'desc': 'Get an existing AHA service pool.',
122
+ 'type': {'type': 'function', '_funcname': '_methPoolGet',
123
+ 'args': (
124
+ {'name': 'name', 'type': 'str',
125
+ 'desc': 'The name of the pool to get. It is easiest to use the relative name of a pool, ending with "...".', },
126
+ ),
127
+ 'returns': {'type': ['null', 'aha:pool'], 'desc': 'The pool if it exists, or $lib.null.'}}},
128
+ {'name': 'list', 'desc': 'Enumerate all of the AHA service pools.',
129
+ 'type': {'type': 'function', '_funcname': '_methPoolList',
130
+ 'returns': {'name': 'yields', 'type': 'aha:pool'}}},
131
+ )
9
132
  _storm_lib_path = ('aha', 'pool')
10
133
 
11
134
  def getObjLocals(self):
12
135
  return {
13
- 'add': self.add,
14
- 'del': self._del,
15
- 'get': self.get,
16
- 'list': self.list,
136
+ 'add': self._methPoolAdd,
137
+ 'del': self._methPoolDel,
138
+ 'get': self._methPoolGet,
139
+ 'list': self._methPoolList,
17
140
  }
18
141
 
19
- async def add(self, name):
142
+ async def _methPoolAdd(self, name):
20
143
  self.runt.reqAdmin()
144
+ name = await s_stormtypes.tostr(name)
21
145
  proxy = await self.runt.snap.core.reqAhaProxy()
22
146
  poolinfo = {'creator': self.runt.user.iden}
23
147
  poolinfo = await proxy.addAhaPool(name, poolinfo)
24
148
  return AhaPool(self.runt, poolinfo)
25
149
 
26
- async def _del(self, name):
150
+ async def _methPoolDel(self, name):
27
151
  self.runt.reqAdmin()
152
+ name = await s_stormtypes.tostr(name)
28
153
  proxy = await self.runt.snap.core.reqAhaProxy()
29
154
  return await proxy.delAhaPool(name)
30
155
 
31
- async def get(self, name):
156
+ @s_stormtypes.stormfunc(readonly=True)
157
+ async def _methPoolGet(self, name):
32
158
  self.runt.reqAdmin()
159
+ name = await s_stormtypes.tostr(name)
33
160
  proxy = await self.runt.snap.core.reqAhaProxy()
34
161
  poolinfo = await proxy.getAhaPool(name)
35
162
  if poolinfo is not None:
36
163
  return AhaPool(self.runt, poolinfo)
37
164
 
38
- async def list(self):
39
-
165
+ @s_stormtypes.stormfunc(readonly=True)
166
+ async def _methPoolList(self):
40
167
  self.runt.reqAdmin()
41
168
  proxy = await self.runt.snap.core.reqAhaProxy()
42
169
 
@@ -48,6 +175,36 @@ class AhaPool(s_stormtypes.StormType):
48
175
  '''
49
176
  Implements the Storm API for an AHA pool.
50
177
  '''
178
+ _storm_locals = (
179
+ {'name': 'add', 'desc': '''Add a service to the AHA pool
180
+
181
+ Examples:
182
+ Add a service to a pool with its relative name::
183
+
184
+ $pool = $lib.aha.pool.get(pool00.cortex...)
185
+ $pool.add(00.cortex...)
186
+ ''',
187
+ 'type': {'type': 'function', '_funcname': '_methPoolSvcAdd',
188
+ 'args': (
189
+ {'name': 'svcname', 'type': 'str',
190
+ 'desc': 'The name of the AHA service to add. It is easiest to use the relative name of a service, ending with "...".', },
191
+ ),
192
+ 'returns': {'type': 'null', }}},
193
+ {'name': 'add', 'desc': '''Remove a service from the AHA pool.
194
+
195
+ Examples:
196
+ Remove a service from a pool with its relative name::
197
+
198
+ $pool = $lib.aha.pool.get(pool00.cortex...)
199
+ $pool.del(00.cortex...)
200
+ ''',
201
+ 'type': {'type': 'function', '_funcname': '_methPoolSvcDel',
202
+ 'args': (
203
+ {'name': 'svcname', 'type': 'str',
204
+ 'desc': 'The name of the AHA service to remove. It is easiest to use the relative name of a service, ending with "...".', },
205
+ ),
206
+ 'returns': {'type': 'null', }}},
207
+ )
51
208
  _storm_typename = 'aha:pool'
52
209
 
53
210
  def __init__(self, runt, poolinfo):
@@ -56,14 +213,17 @@ class AhaPool(s_stormtypes.StormType):
56
213
  self.poolinfo = poolinfo
57
214
 
58
215
  self.locls.update({
59
- 'add': self.add,
60
- 'del': self._del,
216
+ 'add': self._methPoolSvcAdd,
217
+ 'del': self._methPoolSvcDel,
61
218
  })
62
219
 
220
+ async def stormrepr(self):
221
+ return f'{self._storm_typename}: {self.poolinfo.get("name")}'
222
+
63
223
  async def _derefGet(self, name):
64
224
  return self.poolinfo.get(name)
65
225
 
66
- async def add(self, svcname):
226
+ async def _methPoolSvcAdd(self, svcname):
67
227
  self.runt.reqAdmin()
68
228
  svcname = await s_stormtypes.tostr(svcname)
69
229
 
@@ -76,7 +236,7 @@ class AhaPool(s_stormtypes.StormType):
76
236
 
77
237
  self.poolinfo.update(poolinfo)
78
238
 
79
- async def _del(self, svcname):
239
+ async def _methPoolSvcDel(self, svcname):
80
240
  self.runt.reqAdmin()
81
241
  svcname = await s_stormtypes.tostr(svcname)
82
242
 
@@ -97,7 +257,6 @@ stormcmds = (
97
257
  for $pool in $lib.aha.pool.list() {
98
258
  $count = ($count + 1)
99
259
  $lib.print(`Pool: {$pool.name}`)
100
- $lib.print($pool)
101
260
  for ($svcname, $svcinfo) in $pool.services {
102
261
  $lib.print(` {$svcname}`)
103
262
  }
@@ -143,7 +302,7 @@ stormcmds = (
143
302
  ),
144
303
  'storm': '''
145
304
  $pool = $lib.aha.pool.get($cmdopts.poolname)
146
- if (not $pool) { $lib.exit(`No AHA serivce pool named: {$cmdopts.poolname}`) }
305
+ if (not $pool) { $lib.exit(`No AHA service pool named: {$cmdopts.poolname}`) }
147
306
 
148
307
  $pool.add($cmdopts.svcname)
149
308
  $lib.print(`AHA service ({$cmdopts.svcname}) added to service pool ({$pool.name})`)
@@ -158,10 +317,204 @@ stormcmds = (
158
317
  ),
159
318
  'storm': '''
160
319
  $pool = $lib.aha.pool.get($cmdopts.poolname)
161
- if (not $pool) { $lib.exit(`No AHA serivce pool named: {$cmdopts.poolname}`) }
320
+ if (not $pool) { $lib.exit(`No AHA service pool named: {$cmdopts.poolname}`) }
162
321
 
163
322
  $pool.del($cmdopts.svcname)
164
323
  $lib.print(`AHA service ({$cmdopts.svcname}) removed from service pool ({$pool.name})`)
165
324
  ''',
166
325
  },
326
+ {
327
+ 'name': 'aha.svc.stat',
328
+ 'descr': '''Show all information for a specific AHA service.
329
+
330
+ If the --nexus argument is given, the Cortex will attempt to connect the service and report the Nexus offset of the service.
331
+
332
+ The ready value indicates that a service has entered into the realtime change window for synchronizing changes from its leader.
333
+ ''',
334
+ 'cmdargs': (
335
+ ('svc', {'help': 'The service to inspect.'}),
336
+ ('--nexus', {'help': 'Try to connect to online services and report their nexus offset.',
337
+ 'default': False, 'action': 'store_true'}),
338
+ ),
339
+ 'storm': '''
340
+ function _getNexus(svcname) {
341
+ $_url = `aha://{$svcname}/`
342
+ try {
343
+ $_prox = $lib.telepath.open($_url)
344
+ $_info = $_prox.getCellInfo()
345
+ return ( $_info.cell.nexsindx )
346
+ } catch * as _err {
347
+ $_emsg = $_err.mesg
348
+ if ($_emsg = null ) {
349
+ $_emsg = `{$_err}`
350
+ }
351
+ return ( $_emsg )
352
+ }
353
+ }
354
+
355
+ $svc = $lib.aha.get($cmdopts.svc)
356
+ if ($svc = null) {
357
+ $lib.print(`No service found for: "{$cmdopts.svc}"`)
358
+ } else {
359
+ $services = $svc.services
360
+ if $services {
361
+ $lib.print(`Resolved {$cmdopts.svc} to an AHA Pool.\n`)
362
+ $lib.print(`The pool currently has {$lib.len($services)} members.`)
363
+
364
+ $lib.print(`AHA Pool: {$svc.name}`)
365
+ for ($_svcname, $_svcinfo) in $services {
366
+ $lib.print(`Member: {$_svcname}`)
367
+ }
368
+ } else {
369
+ $lib.print(`Resolved {$cmdopts.svc} to an AHA Service.\n`)
370
+ $svcinfo = $svc.svcinfo
371
+ $leader = $svcinfo.leader
372
+ if ($leader = null) {
373
+ $leader = 'Service did not register itself with a leader name.'
374
+ }
375
+ $online = false
376
+ if $svcinfo.online {
377
+ $online = true
378
+ }
379
+ $ready = 'null'
380
+ if $lib.dict.has($svcinfo, ready) {
381
+ $ready = `{$svcinfo.ready}`
382
+ }
383
+ $lib.print(`Name: {$svc.name}`)
384
+ $lib.print(`Online: {$online}`)
385
+ $lib.print(`Ready: {$ready}`)
386
+ $lib.print(`Run iden: {$svcinfo.run}`)
387
+ $lib.print(`Cell iden: {$svcinfo.iden}`)
388
+ $lib.print(`Leader: {$leader}`)
389
+
390
+ if $cmdopts.nexus {
391
+ if $svcinfo.online {
392
+ $nexusOffset = $_getNexus($svc.name)
393
+ } else {
394
+ $nexusOffset = 'Service is not online. Will not attempt to retrieve its nexus offset.'
395
+ }
396
+ $lib.print(`Nexus: {$nexusOffset}`)
397
+ }
398
+
399
+ $lib.print('Connection information:')
400
+ $urlinfo = $svcinfo.urlinfo
401
+ $keys = $lib.dict.keys($urlinfo)
402
+ $keys.sort()
403
+ for $k in $keys {
404
+ $dk = `{$k}:`
405
+ $dk = $dk.ljust(12)
406
+ $lib.print(` {$dk}{$urlinfo.$k}`)
407
+ }
408
+ }
409
+ }
410
+ '''
411
+ },
412
+ {
413
+ 'name': 'aha.svc.list',
414
+ 'descr': '''List AHA services.
415
+
416
+ If the --nexus argument is given, the Cortex will attempt to connect to each service and report the Nexus offset of the service.
417
+
418
+ The ready column indicates that a service has entered into the realtime change window for synchronizing changes from its leader.''',
419
+ 'cmdargs': (
420
+ ('--nexus', {'help': 'Try to connect to online services and report their nexus offset.',
421
+ 'default': False, 'action': 'store_true'}),
422
+ ),
423
+ 'storm': '''
424
+ function _getNexus(svcname) {
425
+ $_url = `aha://{$svcname}/`
426
+ try {
427
+ $_prox = $lib.telepath.open($_url)
428
+ $_info = $_prox.getCellInfo()
429
+ return ( $_info.cell.nexsindx )
430
+ } catch * as _err {
431
+ $_emsg = $_err.mesg
432
+ if ($_emsg = null ) {
433
+ $_emsg = `{$_err}`
434
+ }
435
+ return ( $_emsg )
436
+ }
437
+ }
438
+
439
+ $svcs = ()
440
+ for $svc in $lib.aha.list() {
441
+ $svcs.append($svc)
442
+ }
443
+
444
+ if ($lib.len($svcs) = 0) {
445
+ $lib.print('No AHA services registered.')
446
+ }
447
+ else {
448
+ $columns = 'Name Leader Online Ready Host Port '
449
+ if $cmdopts.nexus {
450
+ $columns = `{$columns} Nexus`
451
+ }
452
+
453
+ $leaders = $lib.set()
454
+ for $info in $svcs {
455
+ $svcinfo = $info.svcinfo
456
+ if $svcinfo {
457
+ if ($info.svcname = $svcinfo.leader) {
458
+ $leaders.add($svcinfo.run)
459
+ }
460
+ }
461
+ }
462
+
463
+ $lib.print($columns)
464
+
465
+ for $info in $svcs {
466
+ $name = $info.name
467
+ $nexusOffset = (null)
468
+ $svcinfo = $info.svcinfo
469
+
470
+ if $cmdopts.nexus {
471
+ if $svcinfo.online {
472
+ $nexusOffset = $_getNexus($name)
473
+ } else {
474
+ $nexusOffset = 'Service is not online. Will not attempt to retrieve its nexus offset.'
475
+ }
476
+ }
477
+ $name=$name.ljust(45)
478
+
479
+ $online = false
480
+ if $svcinfo.online {
481
+ $online = true
482
+ }
483
+ $online = $online.ljust(6)
484
+
485
+ $urlinfo = $svcinfo.urlinfo
486
+
487
+ $host = $urlinfo.host
488
+ $host = $host.ljust(15)
489
+
490
+ $port = $lib.cast(str, $urlinfo.port) // Cast to str
491
+ $port = $port.ljust(5)
492
+
493
+ $ready = 'null'
494
+ if $lib.dict.has($svcinfo, ready) {
495
+ $ready = `{$svcinfo.ready}`
496
+ }
497
+ $ready = $ready.ljust(5)
498
+
499
+ $leader = null
500
+ if ( $svcinfo.leader != null ) {
501
+ if $leaders.has($svcinfo.run) {
502
+ $leader = true
503
+ } else {
504
+ $leader = false
505
+ }
506
+ }
507
+ $leader = $leader.ljust(6)
508
+
509
+ if $info {
510
+ $s = `{$name} {$leader} {$online} {$ready} {$host} {$port}`
511
+ if ($nexusOffset != null) {
512
+ $s = `{$s} {$nexusOffset}`
513
+ }
514
+ $lib.print($s)
515
+ }
516
+ }
517
+ }
518
+ '''
519
+ }
167
520
  )
@@ -1227,6 +1227,11 @@ class User(s_stormtypes.Prim):
1227
1227
  indx = await s_stormtypes.toint(indx, noneok=True)
1228
1228
  gateiden = await s_stormtypes.tostr(gateiden, noneok=True)
1229
1229
  self.runt.confirm(('auth', 'user', 'set', 'rules'), gateiden=gateiden)
1230
+ # TODO: Remove me in 3.0.0
1231
+ if gateiden == 'cortex':
1232
+ mesg = f'Adding rule on the "cortex" authgate. This authgate is not used ' \
1233
+ f'for permission checks and will be removed in Synapse v3.0.0.'
1234
+ await self.runt.snap.warn(mesg, log=False)
1230
1235
  await self.runt.snap.core.addUserRule(self.valu, rule, indx=indx, gateiden=gateiden)
1231
1236
 
1232
1237
  async def _methUserDelRule(self, rule, gateiden=None):
@@ -1480,6 +1485,11 @@ class Role(s_stormtypes.Prim):
1480
1485
  indx = await s_stormtypes.toint(indx, noneok=True)
1481
1486
  gateiden = await s_stormtypes.tostr(gateiden, noneok=True)
1482
1487
  self.runt.confirm(('auth', 'role', 'set', 'rules'), gateiden=gateiden)
1488
+ # TODO: Remove me in 3.0.0
1489
+ if gateiden == 'cortex':
1490
+ mesg = f'Adding rule on the "cortex" authgate. This authgate is not used ' \
1491
+ f'for permission checks and will be removed in Synapse v3.0.0.'
1492
+ await self.runt.snap.warn(mesg, log=False)
1483
1493
  await self.runt.snap.core.addRoleRule(self.valu, rule, indx=indx, gateiden=gateiden)
1484
1494
 
1485
1495
  async def _methRoleDelRule(self, rule, gateiden=None):
@@ -1558,10 +1568,7 @@ class LibAuth(s_stormtypes.Lib):
1558
1568
  @s_stormtypes.stormfunc(readonly=True)
1559
1569
  async def textFromRule(self, rule):
1560
1570
  rule = await s_stormtypes.toprim(rule)
1561
- text = '.'.join(rule[1])
1562
- if not rule[0]:
1563
- text = '!' + text
1564
- return text
1571
+ return s_common.reprauthrule(rule)
1565
1572
 
1566
1573
  @s_stormtypes.stormfunc(readonly=True)
1567
1574
  async def getPermDefs(self):