synapse 2.191.0__py311-none-any.whl → 2.193.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.
- synapse/axon.py +54 -23
- synapse/common.py +15 -0
- synapse/cortex.py +18 -20
- synapse/exc.py +6 -1
- synapse/lib/agenda.py +0 -2
- synapse/lib/ast.py +30 -12
- synapse/lib/cell.py +79 -85
- synapse/lib/cli.py +20 -11
- synapse/lib/nexus.py +2 -1
- synapse/lib/parser.py +1 -1
- synapse/lib/snap.py +4 -4
- synapse/lib/storm.py +34 -17
- synapse/lib/stormhttp.py +32 -35
- synapse/lib/stormlib/json.py +5 -2
- synapse/lib/stormtypes.py +121 -20
- synapse/lib/version.py +2 -2
- synapse/models/inet.py +17 -1
- synapse/models/infotech.py +14 -4
- synapse/models/risk.py +16 -2
- synapse/tests/test_axon.py +10 -0
- synapse/tests/test_cortex.py +55 -3
- synapse/tests/test_exc.py +3 -0
- synapse/tests/test_lib_agenda.py +157 -1
- synapse/tests/test_lib_ast.py +49 -1
- synapse/tests/test_lib_cell.py +106 -1
- synapse/tests/test_lib_httpapi.py +9 -2
- synapse/tests/test_lib_storm.py +72 -30
- synapse/tests/test_lib_stormhttp.py +57 -12
- synapse/tests/test_lib_stormlib_json.py +20 -0
- synapse/tests/test_lib_stormlib_scrape.py +2 -2
- synapse/tests/test_model_inet.py +40 -5
- synapse/tests/test_model_risk.py +2 -0
- synapse/tests/test_servers_univ.py +0 -12
- synapse/tests/test_tools_apikey.py +227 -0
- synapse/tests/test_tools_storm.py +95 -0
- synapse/tests/test_utils_getrefs.py +1 -1
- synapse/tools/apikey.py +93 -0
- synapse/utils/getrefs.py +14 -3
- synapse/vendor/cpython/lib/http/__init__.py +0 -0
- synapse/vendor/cpython/lib/http/cookies.py +59 -0
- synapse/vendor/cpython/lib/test/test_http_cookies.py +49 -0
- {synapse-2.191.0.dist-info → synapse-2.193.0.dist-info}/METADATA +2 -2
- {synapse-2.191.0.dist-info → synapse-2.193.0.dist-info}/RECORD +46 -41
- {synapse-2.191.0.dist-info → synapse-2.193.0.dist-info}/WHEEL +1 -1
- {synapse-2.191.0.dist-info → synapse-2.193.0.dist-info}/LICENSE +0 -0
- {synapse-2.191.0.dist-info → synapse-2.193.0.dist-info}/top_level.txt +0 -0
synapse/lib/cell.py
CHANGED
|
@@ -459,6 +459,43 @@ class CellApi(s_base.Base):
|
|
|
459
459
|
async def delRole(self, iden):
|
|
460
460
|
return await self.cell.delRole(iden)
|
|
461
461
|
|
|
462
|
+
async def addUserApiKey(self, name, duration=None, useriden=None):
|
|
463
|
+
if useriden is None:
|
|
464
|
+
useriden = self.user.iden
|
|
465
|
+
|
|
466
|
+
if self.user.iden == useriden:
|
|
467
|
+
self.user.confirm(('auth', 'self', 'set', 'apikey'), default=True)
|
|
468
|
+
else:
|
|
469
|
+
self.user.confirm(('auth', 'user', 'set', 'apikey'))
|
|
470
|
+
|
|
471
|
+
return await self.cell.addUserApiKey(useriden, name, duration=duration)
|
|
472
|
+
|
|
473
|
+
async def listUserApiKeys(self, useriden=None):
|
|
474
|
+
if useriden is None:
|
|
475
|
+
useriden = self.user.iden
|
|
476
|
+
|
|
477
|
+
if self.user.iden == useriden:
|
|
478
|
+
self.user.confirm(('auth', 'self', 'set', 'apikey'), default=True)
|
|
479
|
+
else:
|
|
480
|
+
self.user.confirm(('auth', 'user', 'set', 'apikey'))
|
|
481
|
+
|
|
482
|
+
return await self.cell.listUserApiKeys(useriden)
|
|
483
|
+
|
|
484
|
+
async def delUserApiKey(self, iden):
|
|
485
|
+
apikey = await self.cell.getUserApiKey(iden)
|
|
486
|
+
if apikey is None:
|
|
487
|
+
mesg = f'User API key with {iden=} does not exist.'
|
|
488
|
+
raise s_exc.NoSuchIden(mesg=mesg, iden=iden)
|
|
489
|
+
|
|
490
|
+
useriden = apikey.get('user')
|
|
491
|
+
|
|
492
|
+
if self.user.iden == useriden:
|
|
493
|
+
self.user.confirm(('auth', 'self', 'set', 'apikey'), default=True)
|
|
494
|
+
else:
|
|
495
|
+
self.user.confirm(('auth', 'user', 'set', 'apikey'))
|
|
496
|
+
|
|
497
|
+
return await self.cell.delUserApiKey(iden)
|
|
498
|
+
|
|
462
499
|
@adminapi()
|
|
463
500
|
async def dyncall(self, iden, todo, gatekeys=()):
|
|
464
501
|
return await self.cell.dyncall(iden, todo, gatekeys=gatekeys)
|
|
@@ -1095,6 +1132,8 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1095
1132
|
}
|
|
1096
1133
|
SYSCTL_CHECK_FREQ = 60.0
|
|
1097
1134
|
|
|
1135
|
+
LOGGED_HTTPAPI_HEADERS = ('User-Agent',)
|
|
1136
|
+
|
|
1098
1137
|
async def __anit__(self, dirn, conf=None, readonly=False, parent=None):
|
|
1099
1138
|
|
|
1100
1139
|
# phase 1
|
|
@@ -2584,18 +2623,12 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
2584
2623
|
walkpath(self.backdirn)
|
|
2585
2624
|
return backups
|
|
2586
2625
|
|
|
2587
|
-
async def
|
|
2588
|
-
|
|
2589
|
-
success = False
|
|
2590
|
-
loglevel = logging.WARNING
|
|
2591
|
-
|
|
2592
|
-
path = self._reqBackDirn(name)
|
|
2593
|
-
cellguid = os.path.join(path, 'cell.guid')
|
|
2594
|
-
if not os.path.isfile(cellguid):
|
|
2595
|
-
mesg = 'Specified backup path has no cell.guid file.'
|
|
2596
|
-
raise s_exc.BadArg(mesg=mesg, arg='path', valu=path)
|
|
2597
|
-
|
|
2626
|
+
async def _streamBackupArchive(self, path, user, name):
|
|
2598
2627
|
link = s_scope.get('link')
|
|
2628
|
+
if link is None:
|
|
2629
|
+
mesg = 'Link not found in scope. This API must be called via a CellApi.'
|
|
2630
|
+
raise s_exc.SynErr(mesg=mesg)
|
|
2631
|
+
|
|
2599
2632
|
linkinfo = await link.getSpawnInfo()
|
|
2600
2633
|
linkinfo['logconf'] = await self._getSpawnLogConf()
|
|
2601
2634
|
|
|
@@ -2603,42 +2636,42 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
2603
2636
|
|
|
2604
2637
|
ctx = multiprocessing.get_context('spawn')
|
|
2605
2638
|
|
|
2606
|
-
proc = None
|
|
2607
|
-
mesg = 'Streaming complete'
|
|
2608
|
-
|
|
2609
2639
|
def getproc():
|
|
2610
2640
|
proc = ctx.Process(target=_iterBackupProc, args=(path, linkinfo))
|
|
2611
2641
|
proc.start()
|
|
2612
2642
|
return proc
|
|
2613
2643
|
|
|
2644
|
+
mesg = 'Streaming complete'
|
|
2645
|
+
proc = await s_coro.executor(getproc)
|
|
2646
|
+
cancelled = False
|
|
2614
2647
|
try:
|
|
2615
|
-
proc = await s_coro.executor(getproc)
|
|
2616
|
-
|
|
2617
2648
|
await s_coro.executor(proc.join)
|
|
2649
|
+
self.backlastuploaddt = datetime.datetime.now()
|
|
2650
|
+
logger.debug(f'Backup streaming completed successfully for {name}')
|
|
2618
2651
|
|
|
2619
|
-
except
|
|
2652
|
+
except asyncio.CancelledError:
|
|
2653
|
+
logger.warning('Backup streaming was cancelled.')
|
|
2654
|
+
cancelled = True
|
|
2655
|
+
raise
|
|
2620
2656
|
|
|
2621
|
-
|
|
2622
|
-
# could be the result of a remote link terminating due to the
|
|
2623
|
-
# backup stream being completed, prior to this function
|
|
2624
|
-
# finishing.
|
|
2657
|
+
except Exception as e:
|
|
2625
2658
|
logger.exception('Error during backup streaming.')
|
|
2626
|
-
|
|
2627
|
-
if proc:
|
|
2628
|
-
proc.terminate()
|
|
2629
|
-
|
|
2630
2659
|
mesg = repr(e)
|
|
2631
2660
|
raise
|
|
2632
2661
|
|
|
2633
|
-
else:
|
|
2634
|
-
success = True
|
|
2635
|
-
loglevel = logging.DEBUG
|
|
2636
|
-
self.backlastuploaddt = datetime.datetime.now()
|
|
2637
|
-
|
|
2638
2662
|
finally:
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2663
|
+
proc.terminate()
|
|
2664
|
+
|
|
2665
|
+
if not cancelled:
|
|
2666
|
+
raise s_exc.DmonSpawn(mesg=mesg)
|
|
2667
|
+
|
|
2668
|
+
async def iterBackupArchive(self, name, user):
|
|
2669
|
+
path = self._reqBackDirn(name)
|
|
2670
|
+
cellguid = os.path.join(path, 'cell.guid')
|
|
2671
|
+
if not os.path.isfile(cellguid):
|
|
2672
|
+
mesg = 'Specified backup path has no cell.guid file.'
|
|
2673
|
+
raise s_exc.BadArg(mesg=mesg, arg='path', valu=path)
|
|
2674
|
+
await self._streamBackupArchive(path, user, name)
|
|
2642
2675
|
|
|
2643
2676
|
async def iterNewBackupArchive(self, user, name=None, remove=False):
|
|
2644
2677
|
|
|
@@ -2649,9 +2682,6 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
2649
2682
|
if remove:
|
|
2650
2683
|
self.backupstreaming = True
|
|
2651
2684
|
|
|
2652
|
-
success = False
|
|
2653
|
-
loglevel = logging.WARNING
|
|
2654
|
-
|
|
2655
2685
|
if name is None:
|
|
2656
2686
|
name = time.strftime('%Y%m%d%H%M%S', datetime.datetime.now().timetuple())
|
|
2657
2687
|
|
|
@@ -2660,10 +2690,6 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
2660
2690
|
mesg = 'Backup with name already exists'
|
|
2661
2691
|
raise s_exc.BadArg(mesg=mesg)
|
|
2662
2692
|
|
|
2663
|
-
link = s_scope.get('link')
|
|
2664
|
-
linkinfo = await link.getSpawnInfo()
|
|
2665
|
-
linkinfo['logconf'] = await self._getSpawnLogConf()
|
|
2666
|
-
|
|
2667
2693
|
try:
|
|
2668
2694
|
await self.runBackup(name)
|
|
2669
2695
|
except Exception:
|
|
@@ -2673,54 +2699,13 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
2673
2699
|
logger.debug(f'Removed {path}')
|
|
2674
2700
|
raise
|
|
2675
2701
|
|
|
2676
|
-
await self.
|
|
2677
|
-
|
|
2678
|
-
ctx = multiprocessing.get_context('spawn')
|
|
2679
|
-
|
|
2680
|
-
proc = None
|
|
2681
|
-
mesg = 'Streaming complete'
|
|
2682
|
-
|
|
2683
|
-
def getproc():
|
|
2684
|
-
proc = ctx.Process(target=_iterBackupProc, args=(path, linkinfo))
|
|
2685
|
-
proc.start()
|
|
2686
|
-
return proc
|
|
2687
|
-
|
|
2688
|
-
try:
|
|
2689
|
-
proc = await s_coro.executor(getproc)
|
|
2690
|
-
|
|
2691
|
-
await s_coro.executor(proc.join)
|
|
2692
|
-
|
|
2693
|
-
except (asyncio.CancelledError, Exception) as e:
|
|
2694
|
-
|
|
2695
|
-
# We want to log all exceptions here, an asyncio.CancelledError
|
|
2696
|
-
# could be the result of a remote link terminating due to the
|
|
2697
|
-
# backup stream being completed, prior to this function
|
|
2698
|
-
# finishing.
|
|
2699
|
-
logger.exception('Error during backup streaming.')
|
|
2700
|
-
|
|
2701
|
-
if proc:
|
|
2702
|
-
proc.terminate()
|
|
2703
|
-
|
|
2704
|
-
mesg = repr(e)
|
|
2705
|
-
raise
|
|
2706
|
-
|
|
2707
|
-
else:
|
|
2708
|
-
success = True
|
|
2709
|
-
loglevel = logging.DEBUG
|
|
2710
|
-
self.backlastuploaddt = datetime.datetime.now()
|
|
2711
|
-
|
|
2712
|
-
finally:
|
|
2713
|
-
if remove:
|
|
2714
|
-
logger.debug(f'Removing {path}')
|
|
2715
|
-
await s_coro.executor(shutil.rmtree, path, ignore_errors=True)
|
|
2716
|
-
logger.debug(f'Removed {path}')
|
|
2717
|
-
|
|
2718
|
-
phrase = 'successfully' if success else 'with failure'
|
|
2719
|
-
logger.log(loglevel, f'iterNewBackupArchive completed {phrase} for {name}')
|
|
2720
|
-
raise s_exc.DmonSpawn(mesg=mesg)
|
|
2702
|
+
await self._streamBackupArchive(path, user, name)
|
|
2721
2703
|
|
|
2722
2704
|
finally:
|
|
2723
2705
|
if remove:
|
|
2706
|
+
logger.debug(f'Removing {path}')
|
|
2707
|
+
await s_coro.executor(shutil.rmtree, path, ignore_errors=True)
|
|
2708
|
+
logger.debug(f'Removed {path}')
|
|
2724
2709
|
self.backupstreaming = False
|
|
2725
2710
|
|
|
2726
2711
|
async def isUserAllowed(self, iden, perm, gateiden=None, default=False):
|
|
@@ -3248,6 +3233,15 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3248
3233
|
'remoteip': remote_ip,
|
|
3249
3234
|
}
|
|
3250
3235
|
|
|
3236
|
+
headers = {}
|
|
3237
|
+
|
|
3238
|
+
for header in self.LOGGED_HTTPAPI_HEADERS:
|
|
3239
|
+
if (valu := handler.request.headers.get(header)) is not None:
|
|
3240
|
+
headers[header.lower()] = valu
|
|
3241
|
+
|
|
3242
|
+
if headers:
|
|
3243
|
+
enfo['headers'] = headers
|
|
3244
|
+
|
|
3251
3245
|
extra = {'synapse': enfo}
|
|
3252
3246
|
|
|
3253
3247
|
# It is possible that a Cell implementor may register handlers which
|
synapse/lib/cli.py
CHANGED
|
@@ -281,18 +281,26 @@ class Cli(s_base.Base):
|
|
|
281
281
|
|
|
282
282
|
await self.fini()
|
|
283
283
|
|
|
284
|
-
async def addSignalHandlers(self):
|
|
284
|
+
async def addSignalHandlers(self): # pragma: no cover
|
|
285
285
|
'''
|
|
286
286
|
Register SIGINT signal handler with the ioloop to cancel the currently running cmdloop task.
|
|
287
|
+
Removes the handler when the cli is fini'd.
|
|
287
288
|
'''
|
|
288
|
-
|
|
289
289
|
def sigint():
|
|
290
|
-
self.printf('<ctrl-c>')
|
|
291
290
|
if self.cmdtask is not None:
|
|
292
291
|
self.cmdtask.cancel()
|
|
293
292
|
|
|
294
293
|
self.loop.add_signal_handler(signal.SIGINT, sigint)
|
|
295
294
|
|
|
295
|
+
def onfini():
|
|
296
|
+
# N.B. This is reaches into some loop / handle internals but
|
|
297
|
+
# prevents us from removing a handler that overwrote our own.
|
|
298
|
+
hndl = self.loop._signal_handlers.get(signal.SIGINT, None) # type: asyncio.Handle
|
|
299
|
+
if hndl is not None and hndl._callback is sigint:
|
|
300
|
+
self.loop.remove_signal_handler(signal.SIGINT)
|
|
301
|
+
|
|
302
|
+
self.onfini(onfini)
|
|
303
|
+
|
|
296
304
|
def get(self, name, defval=None):
|
|
297
305
|
return self.locs.get(name, defval)
|
|
298
306
|
|
|
@@ -324,8 +332,12 @@ class Cli(s_base.Base):
|
|
|
324
332
|
if text is None:
|
|
325
333
|
text = self.cmdprompt
|
|
326
334
|
|
|
327
|
-
with patch_stdout():
|
|
328
|
-
retn = await self.sess.prompt_async(text,
|
|
335
|
+
with patch_stdout(): # pragma: no cover
|
|
336
|
+
retn = await self.sess.prompt_async(text,
|
|
337
|
+
vi_mode=self.vi_mode,
|
|
338
|
+
enable_open_in_editor=True,
|
|
339
|
+
handle_sigint=False # We handle sigint in the loop
|
|
340
|
+
)
|
|
329
341
|
return retn
|
|
330
342
|
|
|
331
343
|
def printf(self, mesg, addnl=True, color=None):
|
|
@@ -390,7 +402,7 @@ class Cli(s_base.Base):
|
|
|
390
402
|
self.cmdtask = self.schedCoro(coro)
|
|
391
403
|
await self.cmdtask
|
|
392
404
|
|
|
393
|
-
except KeyboardInterrupt:
|
|
405
|
+
except (KeyboardInterrupt, asyncio.CancelledError):
|
|
394
406
|
|
|
395
407
|
if self.isfini:
|
|
396
408
|
return
|
|
@@ -408,11 +420,8 @@ class Cli(s_base.Base):
|
|
|
408
420
|
if self.cmdtask is not None:
|
|
409
421
|
self.cmdtask.cancel()
|
|
410
422
|
try:
|
|
411
|
-
self.cmdtask.
|
|
412
|
-
except asyncio.CancelledError:
|
|
413
|
-
# Wait a beat to let any remaining nodes to print out before we print the prompt
|
|
414
|
-
await asyncio.sleep(1)
|
|
415
|
-
except Exception:
|
|
423
|
+
await asyncio.wait_for(self.cmdtask, timeout=0.1)
|
|
424
|
+
except (asyncio.CancelledError, asyncio.TimeoutError):
|
|
416
425
|
pass
|
|
417
426
|
|
|
418
427
|
async def runCmdLine(self, line):
|
synapse/lib/nexus.py
CHANGED
|
@@ -505,6 +505,7 @@ class NexsRoot(s_base.Base):
|
|
|
505
505
|
if features.get('dynmirror'):
|
|
506
506
|
await proxy.readyToMirror()
|
|
507
507
|
|
|
508
|
+
synvers = cellinfo['synapse']['version']
|
|
508
509
|
cellvers = cellinfo['cell']['version']
|
|
509
510
|
if cellvers > self.cell.VERSION:
|
|
510
511
|
logger.error('Leader is a higher version than we are. Mirrors must be updated first. Entering read-only mode.')
|
|
@@ -537,7 +538,7 @@ class NexsRoot(s_base.Base):
|
|
|
537
538
|
offs = self.nexslog.index()
|
|
538
539
|
|
|
539
540
|
opts = {}
|
|
540
|
-
if
|
|
541
|
+
if synvers >= (2, 95, 0):
|
|
541
542
|
opts['tellready'] = True
|
|
542
543
|
|
|
543
544
|
genr = proxy.getNexusChanges(offs, **opts)
|
synapse/lib/parser.py
CHANGED
|
@@ -507,7 +507,7 @@ class Parser:
|
|
|
507
507
|
origexc = e.orig_exc
|
|
508
508
|
if not isinstance(origexc, s_exc.SynErr):
|
|
509
509
|
raise e.orig_exc # pragma: no cover
|
|
510
|
-
origexc.
|
|
510
|
+
origexc.set('text', self.text)
|
|
511
511
|
return s_exc.BadSyntax(**origexc.errinfo)
|
|
512
512
|
|
|
513
513
|
elif isinstance(e, lark.exceptions.UnexpectedCharacters): # pragma: no cover
|
synapse/lib/snap.py
CHANGED
|
@@ -363,9 +363,9 @@ class ProtoNode:
|
|
|
363
363
|
valu, norminfo = prop.type.norm(valu)
|
|
364
364
|
except s_exc.BadTypeValu as e:
|
|
365
365
|
oldm = e.errinfo.get('mesg')
|
|
366
|
-
e.
|
|
367
|
-
|
|
368
|
-
|
|
366
|
+
e.update({'prop': prop.name,
|
|
367
|
+
'form': prop.form.name,
|
|
368
|
+
'mesg': f'Bad prop value {prop.full}={valu!r} : {oldm}'})
|
|
369
369
|
if self.ctx.snap.strict:
|
|
370
370
|
raise e
|
|
371
371
|
await self.ctx.snap.warn(e)
|
|
@@ -493,7 +493,7 @@ class SnapEditor:
|
|
|
493
493
|
try:
|
|
494
494
|
valu, norminfo = form.type.norm(valu)
|
|
495
495
|
except s_exc.BadTypeValu as e:
|
|
496
|
-
e.
|
|
496
|
+
e.set('form', form.name)
|
|
497
497
|
if self.snap.strict: raise e
|
|
498
498
|
await self.snap.warn(f'addNode() BadTypeValu {form.name}={valu} {e}')
|
|
499
499
|
return None
|
synapse/lib/storm.py
CHANGED
|
@@ -5344,6 +5344,12 @@ class ParallelCmd(Cmd):
|
|
|
5344
5344
|
inet:ipv4#foo | parallel { $place = $lib.import(foobar).lookup(:latlong) [ :place=$place ] }
|
|
5345
5345
|
|
|
5346
5346
|
NOTE: Storm variables set within the parallel query pipelines do not interact.
|
|
5347
|
+
|
|
5348
|
+
NOTE: If there are inbound nodes to the parallel command, parallel pipelines will be created as each node
|
|
5349
|
+
is processed, up to the number specified by --size. If the number of nodes in the pipeline is less
|
|
5350
|
+
than the value specified by --size, additional pipelines with no inbound node will not be created.
|
|
5351
|
+
If there are no inbound nodes to the parallel command, the number of pipelines specified by --size
|
|
5352
|
+
will always be created.
|
|
5347
5353
|
'''
|
|
5348
5354
|
name = 'parallel'
|
|
5349
5355
|
readonly = True
|
|
@@ -5400,19 +5406,33 @@ class ParallelCmd(Cmd):
|
|
|
5400
5406
|
inq = asyncio.Queue(maxsize=size)
|
|
5401
5407
|
outq = asyncio.Queue(maxsize=size)
|
|
5402
5408
|
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5409
|
+
tsks = 0
|
|
5410
|
+
try:
|
|
5411
|
+
while tsks < size:
|
|
5412
|
+
await inq.put(await genr.__anext__())
|
|
5413
|
+
base.schedCoro(self.pipeline(runt, query, inq, outq))
|
|
5414
|
+
tsks += 1
|
|
5415
|
+
except StopAsyncIteration:
|
|
5416
|
+
[await inq.put(None) for i in range(tsks)]
|
|
5417
|
+
|
|
5418
|
+
# If a full set of tasks were created, keep pumping nodes into the queue
|
|
5419
|
+
if tsks == size:
|
|
5420
|
+
async def pump():
|
|
5421
|
+
try:
|
|
5422
|
+
async for pumpitem in genr:
|
|
5423
|
+
await inq.put(pumpitem)
|
|
5424
|
+
[await inq.put(None) for i in range(size)]
|
|
5425
|
+
except Exception as e:
|
|
5426
|
+
await outq.put(e)
|
|
5427
|
+
|
|
5428
|
+
base.schedCoro(pump())
|
|
5429
|
+
|
|
5430
|
+
# If no tasks were created, make a full set
|
|
5431
|
+
elif tsks == 0:
|
|
5432
|
+
tsks = size
|
|
5433
|
+
for i in range(size):
|
|
5434
|
+
base.schedCoro(self.pipeline(runt, query, inq, outq))
|
|
5435
|
+
[await inq.put(None) for i in range(tsks)]
|
|
5416
5436
|
|
|
5417
5437
|
exited = 0
|
|
5418
5438
|
while True:
|
|
@@ -5423,7 +5443,7 @@ class ParallelCmd(Cmd):
|
|
|
5423
5443
|
|
|
5424
5444
|
if item is None:
|
|
5425
5445
|
exited += 1
|
|
5426
|
-
if exited ==
|
|
5446
|
+
if exited == tsks:
|
|
5427
5447
|
return
|
|
5428
5448
|
continue
|
|
5429
5449
|
|
|
@@ -5566,9 +5586,6 @@ class TeeCmd(Cmd):
|
|
|
5566
5586
|
|
|
5567
5587
|
await outq.put(None)
|
|
5568
5588
|
|
|
5569
|
-
except asyncio.CancelledError: # pragma: no cover
|
|
5570
|
-
raise
|
|
5571
|
-
|
|
5572
5589
|
except Exception as e:
|
|
5573
5590
|
await outq.put(e)
|
|
5574
5591
|
|
synapse/lib/stormhttp.py
CHANGED
|
@@ -91,12 +91,19 @@ class LibHttp(s_stormtypes.Lib):
|
|
|
91
91
|
|
|
92
92
|
For APIs that accept an ssl_opts argument, the dictionary may contain the following values::
|
|
93
93
|
|
|
94
|
-
{
|
|
94
|
+
({
|
|
95
95
|
'verify': <bool> - Perform SSL/TLS verification. Is overridden by the ssl_verify argument.
|
|
96
96
|
'client_cert': <str> - PEM encoded full chain certificate for use in mTLS.
|
|
97
97
|
'client_key': <str> - PEM encoded key for use in mTLS. Alternatively, can be included in client_cert.
|
|
98
98
|
'ca_cert': <str> - A PEM encoded full chain CA certificate for use when verifying the request.
|
|
99
|
-
}
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
For APIs that accept a proxy argument, the following values are supported::
|
|
102
|
+
|
|
103
|
+
$lib.null: Deprecated - Use the proxy defined by the http:proxy configuration option if set.
|
|
104
|
+
$lib.true: Use the proxy defined by the http:proxy configuration option if set.
|
|
105
|
+
$lib.false: Do not use the proxy defined by the http:proxy configuration option if set.
|
|
106
|
+
<str>: A proxy URL string.
|
|
100
107
|
'''
|
|
101
108
|
_storm_locals = (
|
|
102
109
|
{'name': 'get', 'desc': 'Get the contents of a given URL.',
|
|
@@ -113,8 +120,8 @@ class LibHttp(s_stormtypes.Lib):
|
|
|
113
120
|
'default': 300},
|
|
114
121
|
{'name': 'allow_redirects', 'type': 'bool', 'desc': 'If set to false, do not follow redirects.',
|
|
115
122
|
'default': True},
|
|
116
|
-
{'name': 'proxy', 'type': ['bool', '
|
|
117
|
-
'desc': '
|
|
123
|
+
{'name': 'proxy', 'type': ['bool', 'str'],
|
|
124
|
+
'desc': 'Configure proxy usage. See $lib.inet.http help for additional details.', 'default': True},
|
|
118
125
|
{'name': 'ssl_opts', 'type': 'dict',
|
|
119
126
|
'desc': 'Optional SSL/TLS options. See $lib.inet.http help for additional details.',
|
|
120
127
|
'default': None},
|
|
@@ -145,8 +152,8 @@ class LibHttp(s_stormtypes.Lib):
|
|
|
145
152
|
'and the corresponding file will be uploaded as the value for '
|
|
146
153
|
'the field.',
|
|
147
154
|
'default': None},
|
|
148
|
-
{'name': 'proxy', 'type': ['bool', '
|
|
149
|
-
'desc': '
|
|
155
|
+
{'name': 'proxy', 'type': ['bool', 'str'],
|
|
156
|
+
'desc': 'Configure proxy usage. See $lib.inet.http help for additional details.', 'default': True},
|
|
150
157
|
{'name': 'ssl_opts', 'type': 'dict',
|
|
151
158
|
'desc': 'Optional SSL/TLS options. See $lib.inet.http help for additional details.',
|
|
152
159
|
'default': None},
|
|
@@ -167,8 +174,8 @@ class LibHttp(s_stormtypes.Lib):
|
|
|
167
174
|
'default': 300, },
|
|
168
175
|
{'name': 'allow_redirects', 'type': 'bool', 'desc': 'If set to true, follow redirects.',
|
|
169
176
|
'default': False},
|
|
170
|
-
{'name': 'proxy', 'type': ['bool', '
|
|
171
|
-
'desc': '
|
|
177
|
+
{'name': 'proxy', 'type': ['bool', 'str'],
|
|
178
|
+
'desc': 'Configure proxy usage. See $lib.inet.http help for additional details.', 'default': True},
|
|
172
179
|
{'name': 'ssl_opts', 'type': 'dict',
|
|
173
180
|
'desc': 'Optional SSL/TLS options. See $lib.inet.http help for additional details.',
|
|
174
181
|
'default': None},
|
|
@@ -200,8 +207,8 @@ class LibHttp(s_stormtypes.Lib):
|
|
|
200
207
|
'and the corresponding file will be uploaded as the value for '
|
|
201
208
|
'the field.',
|
|
202
209
|
'default': None},
|
|
203
|
-
{'name': 'proxy', 'type': ['bool', '
|
|
204
|
-
'desc': '
|
|
210
|
+
{'name': 'proxy', 'type': ['bool', 'str'],
|
|
211
|
+
'desc': 'Configure proxy usage. See $lib.inet.http help for additional details.', 'default': True},
|
|
205
212
|
{'name': 'ssl_opts', 'type': 'dict',
|
|
206
213
|
'desc': 'Optional SSL/TLS options. See $lib.inet.http help for additional details.',
|
|
207
214
|
'default': None},
|
|
@@ -221,8 +228,8 @@ class LibHttp(s_stormtypes.Lib):
|
|
|
221
228
|
'default': 300},
|
|
222
229
|
{'name': 'params', 'type': 'dict', 'desc': 'Optional parameters which may be passed to the connection request.',
|
|
223
230
|
'default': None},
|
|
224
|
-
{'name': 'proxy', 'type': ['bool', '
|
|
225
|
-
'desc': '
|
|
231
|
+
{'name': 'proxy', 'type': ['bool', 'str'],
|
|
232
|
+
'desc': 'Configure proxy usage. See $lib.inet.http help for additional details.', 'default': True},
|
|
226
233
|
{'name': 'ssl_opts', 'type': 'dict',
|
|
227
234
|
'desc': 'Optional SSL/TLS options. See $lib.inet.http help for additional details.',
|
|
228
235
|
'default': None},
|
|
@@ -308,23 +315,23 @@ class LibHttp(s_stormtypes.Lib):
|
|
|
308
315
|
return s_common.httpcodereason(code)
|
|
309
316
|
|
|
310
317
|
async def _httpEasyHead(self, url, headers=None, ssl_verify=True, params=None, timeout=300,
|
|
311
|
-
allow_redirects=False, proxy=
|
|
318
|
+
allow_redirects=False, proxy=True, ssl_opts=None):
|
|
312
319
|
return await self._httpRequest('HEAD', url, headers=headers, ssl_verify=ssl_verify, params=params,
|
|
313
320
|
timeout=timeout, allow_redirects=allow_redirects, proxy=proxy, ssl_opts=ssl_opts)
|
|
314
321
|
|
|
315
322
|
async def _httpEasyGet(self, url, headers=None, ssl_verify=True, params=None, timeout=300,
|
|
316
|
-
allow_redirects=True, proxy=
|
|
323
|
+
allow_redirects=True, proxy=True, ssl_opts=None):
|
|
317
324
|
return await self._httpRequest('GET', url, headers=headers, ssl_verify=ssl_verify, params=params,
|
|
318
325
|
timeout=timeout, allow_redirects=allow_redirects, proxy=proxy, ssl_opts=ssl_opts)
|
|
319
326
|
|
|
320
327
|
async def _httpPost(self, url, headers=None, json=None, body=None, ssl_verify=True,
|
|
321
|
-
params=None, timeout=300, allow_redirects=True, fields=None, proxy=
|
|
328
|
+
params=None, timeout=300, allow_redirects=True, fields=None, proxy=True, ssl_opts=None):
|
|
322
329
|
return await self._httpRequest('POST', url, headers=headers, json=json, body=body,
|
|
323
330
|
ssl_verify=ssl_verify, params=params, timeout=timeout,
|
|
324
331
|
allow_redirects=allow_redirects, fields=fields, proxy=proxy, ssl_opts=ssl_opts)
|
|
325
332
|
|
|
326
333
|
async def inetHttpConnect(self, url, headers=None, ssl_verify=True, timeout=300,
|
|
327
|
-
params=None, proxy=
|
|
334
|
+
params=None, proxy=True, ssl_opts=None):
|
|
328
335
|
|
|
329
336
|
url = await s_stormtypes.tostr(url)
|
|
330
337
|
headers = await s_stormtypes.toprim(headers)
|
|
@@ -338,15 +345,9 @@ class LibHttp(s_stormtypes.Lib):
|
|
|
338
345
|
|
|
339
346
|
sock = await WebSocket.anit()
|
|
340
347
|
|
|
341
|
-
if proxy is not None:
|
|
342
|
-
self.runt.confirm(('storm', 'lib', 'inet', 'http', 'proxy'))
|
|
343
|
-
|
|
344
|
-
if proxy is None:
|
|
345
|
-
proxy = await self.runt.snap.core.getConfOpt('http:proxy')
|
|
346
|
-
|
|
347
348
|
connector = None
|
|
348
|
-
if proxy:
|
|
349
|
-
connector = aiohttp_socks.ProxyConnector.from_url(
|
|
349
|
+
if proxyurl := await s_stormtypes.resolveCoreProxyUrl(proxy):
|
|
350
|
+
connector = aiohttp_socks.ProxyConnector.from_url(proxyurl)
|
|
350
351
|
|
|
351
352
|
timeout = aiohttp.ClientTimeout(total=timeout)
|
|
352
353
|
kwargs = {'timeout': timeout}
|
|
@@ -387,7 +388,7 @@ class LibHttp(s_stormtypes.Lib):
|
|
|
387
388
|
|
|
388
389
|
async def _httpRequest(self, meth, url, headers=None, json=None, body=None,
|
|
389
390
|
ssl_verify=True, params=None, timeout=300, allow_redirects=True,
|
|
390
|
-
fields=None, proxy=
|
|
391
|
+
fields=None, proxy=True, ssl_opts=None):
|
|
391
392
|
meth = await s_stormtypes.tostr(meth)
|
|
392
393
|
url = await s_stormtypes.tostr(url)
|
|
393
394
|
json = await s_stormtypes.toprim(json)
|
|
@@ -407,19 +408,18 @@ class LibHttp(s_stormtypes.Lib):
|
|
|
407
408
|
|
|
408
409
|
headers = s_stormtypes.strifyHttpArg(headers)
|
|
409
410
|
|
|
410
|
-
if proxy is not None:
|
|
411
|
-
self.runt.confirm(('storm', 'lib', 'inet', 'http', 'proxy'))
|
|
412
|
-
|
|
413
411
|
if fields:
|
|
414
412
|
if any(['sha256' in field for field in fields]):
|
|
415
413
|
self.runt.confirm(('storm', 'lib', 'axon', 'wput'))
|
|
416
414
|
|
|
417
415
|
kwargs = {}
|
|
418
|
-
|
|
419
|
-
|
|
416
|
+
|
|
417
|
+
ok, proxy = await s_stormtypes.resolveAxonProxyArg(proxy)
|
|
418
|
+
if ok:
|
|
420
419
|
kwargs['proxy'] = proxy
|
|
421
420
|
|
|
422
421
|
if ssl_opts is not None:
|
|
422
|
+
axonvers = self.runt.snap.core.axoninfo['synapse']['version']
|
|
423
423
|
mesg = f'The ssl_opts argument requires an Axon Synapse version {s_stormtypes.AXON_MINVERS_SSLOPTS}, ' \
|
|
424
424
|
f'but the Axon is running {axonvers}'
|
|
425
425
|
s_version.reqVersion(axonvers, s_stormtypes.AXON_MINVERS_SSLOPTS, mesg=mesg)
|
|
@@ -432,12 +432,9 @@ class LibHttp(s_stormtypes.Lib):
|
|
|
432
432
|
|
|
433
433
|
kwargs['ssl'] = self.runt.snap.core.getCachedSslCtx(opts=ssl_opts, verify=ssl_verify)
|
|
434
434
|
|
|
435
|
-
if proxy is None:
|
|
436
|
-
proxy = await self.runt.snap.core.getConfOpt('http:proxy')
|
|
437
|
-
|
|
438
435
|
connector = None
|
|
439
|
-
if proxy:
|
|
440
|
-
connector = aiohttp_socks.ProxyConnector.from_url(
|
|
436
|
+
if proxyurl := await s_stormtypes.resolveCoreProxyUrl(proxy):
|
|
437
|
+
connector = aiohttp_socks.ProxyConnector.from_url(proxyurl)
|
|
441
438
|
|
|
442
439
|
timeout = aiohttp.ClientTimeout(total=timeout)
|
|
443
440
|
|
synapse/lib/stormlib/json.py
CHANGED
|
@@ -93,6 +93,7 @@ class JsonLib(s_stormtypes.Lib):
|
|
|
93
93
|
'type': {'type': 'function', '_funcname': '_jsonSave',
|
|
94
94
|
'args': (
|
|
95
95
|
{'name': 'item', 'type': 'any', 'desc': 'The item to be serialized as a JSON string.', },
|
|
96
|
+
{'name': 'indent', 'type': 'int', 'desc': 'Specify a number of spaces to indent with.', 'default': None},
|
|
96
97
|
),
|
|
97
98
|
'returns': {'type': 'str', 'desc': 'The JSON serialized object.', }}},
|
|
98
99
|
{'name': 'schema', 'desc': 'Get a JS schema validation object.',
|
|
@@ -115,10 +116,12 @@ class JsonLib(s_stormtypes.Lib):
|
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
@s_stormtypes.stormfunc(readonly=True)
|
|
118
|
-
async def _jsonSave(self, item):
|
|
119
|
+
async def _jsonSave(self, item, indent=None):
|
|
120
|
+
indent = await s_stormtypes.toint(indent, noneok=True)
|
|
121
|
+
|
|
119
122
|
try:
|
|
120
123
|
item = await s_stormtypes.toprim(item)
|
|
121
|
-
return json.dumps(item)
|
|
124
|
+
return json.dumps(item, indent=indent)
|
|
122
125
|
except Exception as e:
|
|
123
126
|
mesg = f'Argument is not JSON compatible: {item}'
|
|
124
127
|
raise s_exc.MustBeJsonSafe(mesg=mesg)
|