synapse 2.164.0__py311-none-any.whl → 2.166.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 +3 -3
- synapse/cmds/cortex.py +1 -6
- synapse/common.py +7 -1
- synapse/cortex.py +145 -192
- synapse/datamodel.py +36 -1
- synapse/lib/agenda.py +87 -97
- synapse/lib/aha.py +51 -0
- synapse/lib/ast.py +22 -23
- synapse/lib/base.py +0 -6
- synapse/lib/boss.py +3 -0
- synapse/lib/cell.py +70 -39
- synapse/lib/certdir.py +9 -0
- synapse/lib/hiveauth.py +65 -12
- synapse/lib/httpapi.py +1 -0
- synapse/lib/modelrev.py +121 -33
- synapse/lib/modules.py +1 -0
- synapse/lib/nexus.py +64 -26
- synapse/lib/parser.py +2 -0
- synapse/lib/schemas.py +14 -0
- synapse/lib/snap.py +50 -4
- synapse/lib/storm.lark +4 -3
- synapse/lib/storm.py +96 -22
- synapse/lib/storm_format.py +1 -0
- synapse/lib/stormlib/aha.py +7 -1
- synapse/lib/stormlib/auth.py +13 -5
- synapse/lib/stormlib/cache.py +202 -0
- synapse/lib/stormlib/cortex.py +147 -8
- synapse/lib/stormlib/gen.py +53 -6
- synapse/lib/stormlib/math.py +1 -1
- synapse/lib/stormlib/model.py +11 -1
- synapse/lib/stormlib/spooled.py +109 -0
- synapse/lib/stormlib/vault.py +1 -1
- synapse/lib/stormtypes.py +113 -17
- synapse/lib/trigger.py +36 -47
- synapse/lib/types.py +29 -2
- synapse/lib/version.py +2 -2
- synapse/lib/view.py +80 -53
- synapse/models/economic.py +174 -5
- synapse/models/files.py +2 -0
- synapse/models/inet.py +77 -2
- synapse/models/infotech.py +12 -12
- synapse/models/orgs.py +72 -21
- synapse/models/person.py +40 -11
- synapse/models/risk.py +78 -24
- synapse/models/science.py +102 -0
- synapse/telepath.py +117 -35
- synapse/tests/test_cortex.py +84 -158
- synapse/tests/test_datamodel.py +22 -0
- synapse/tests/test_lib_agenda.py +52 -96
- synapse/tests/test_lib_aha.py +126 -4
- synapse/tests/test_lib_ast.py +412 -6
- synapse/tests/test_lib_cell.py +24 -8
- synapse/tests/test_lib_certdir.py +32 -0
- synapse/tests/test_lib_grammar.py +9 -1
- synapse/tests/test_lib_httpapi.py +0 -1
- synapse/tests/test_lib_jupyter.py +0 -1
- synapse/tests/test_lib_modelrev.py +41 -0
- synapse/tests/test_lib_nexus.py +38 -0
- synapse/tests/test_lib_storm.py +95 -5
- synapse/tests/test_lib_stormlib_cache.py +272 -0
- synapse/tests/test_lib_stormlib_cortex.py +71 -0
- synapse/tests/test_lib_stormlib_gen.py +37 -2
- synapse/tests/test_lib_stormlib_model.py +2 -0
- synapse/tests/test_lib_stormlib_spooled.py +190 -0
- synapse/tests/test_lib_stormlib_vault.py +12 -3
- synapse/tests/test_lib_stormsvc.py +0 -10
- synapse/tests/test_lib_stormtypes.py +60 -8
- synapse/tests/test_lib_trigger.py +20 -2
- synapse/tests/test_lib_types.py +17 -1
- synapse/tests/test_model_economic.py +114 -0
- synapse/tests/test_model_files.py +2 -0
- synapse/tests/test_model_inet.py +73 -1
- synapse/tests/test_model_infotech.py +2 -2
- synapse/tests/test_model_orgs.py +10 -1
- synapse/tests/test_model_risk.py +30 -2
- synapse/tests/test_model_science.py +59 -0
- synapse/tests/test_model_syn.py +0 -1
- synapse/tests/test_telepath.py +30 -7
- synapse/tests/test_tools_modrole.py +81 -0
- synapse/tests/test_tools_moduser.py +105 -0
- synapse/tools/modrole.py +59 -7
- synapse/tools/moduser.py +78 -10
- {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/METADATA +2 -2
- {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/RECORD +87 -83
- {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/WHEEL +1 -1
- synapse/lib/provenance.py +0 -111
- synapse/tests/test_lib_provenance.py +0 -37
- {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/LICENSE +0 -0
- {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/top_level.txt +0 -0
synapse/tests/test_model_risk.py
CHANGED
|
@@ -394,6 +394,7 @@ class RiskModelTest(s_t_utils.SynTest):
|
|
|
394
394
|
:sophistication=high
|
|
395
395
|
:merged:time = 20230111
|
|
396
396
|
:merged:isnow = {[ risk:threat=* ]}
|
|
397
|
+
:mitre:attack:group=G0001
|
|
397
398
|
]
|
|
398
399
|
''')
|
|
399
400
|
self.len(1, nodes)
|
|
@@ -414,10 +415,12 @@ class RiskModelTest(s_t_utils.SynTest):
|
|
|
414
415
|
self.eq(1673395200000, nodes[0].get('merged:time'))
|
|
415
416
|
self.eq(1643673600000, nodes[0].get('reporter:discovered'))
|
|
416
417
|
self.eq(1675209600000, nodes[0].get('reporter:published'))
|
|
418
|
+
self.eq('G0001', nodes[0].get('mitre:attack:group'))
|
|
417
419
|
|
|
418
420
|
self.len(1, nodes[0].get('goals'))
|
|
419
421
|
self.len(1, nodes[0].get('techniques'))
|
|
420
422
|
self.len(1, await core.nodes('risk:threat:merged:isnow -> risk:threat'))
|
|
423
|
+
self.len(1, await core.nodes('risk:threat -> it:mitre:attack:group'))
|
|
421
424
|
|
|
422
425
|
nodes = await core.nodes('''[ risk:leak=*
|
|
423
426
|
:name="WikiLeaks ACME Leak"
|
|
@@ -432,6 +435,8 @@ class RiskModelTest(s_t_utils.SynTest):
|
|
|
432
435
|
:public:url=https://wikileaks.org/acme
|
|
433
436
|
:reporter={ gen.ou.org vertex }
|
|
434
437
|
:reporter:name=vertex
|
|
438
|
+
:size:bytes=99
|
|
439
|
+
:extortion=*
|
|
435
440
|
]''')
|
|
436
441
|
self.len(1, nodes)
|
|
437
442
|
self.eq('wikileaks acme leak', nodes[0].get('name'))
|
|
@@ -439,9 +444,11 @@ class RiskModelTest(s_t_utils.SynTest):
|
|
|
439
444
|
self.eq(1698883200000, nodes[0].get('disclosed'))
|
|
440
445
|
self.eq('public.', nodes[0].get('type'))
|
|
441
446
|
self.eq(1, nodes[0].get('public'))
|
|
447
|
+
self.eq(99, nodes[0].get('size:bytes'))
|
|
442
448
|
self.eq('https://wikileaks.org/acme', nodes[0].get('public:url'))
|
|
443
449
|
self.eq('vertex', nodes[0].get('reporter:name'))
|
|
444
450
|
|
|
451
|
+
self.len(1, await core.nodes('risk:leak -> risk:extortion'))
|
|
445
452
|
self.len(1, await core.nodes('risk:leak -> risk:leak:type:taxonomy'))
|
|
446
453
|
self.len(1, await core.nodes('risk:leak :owner -> ps:contact +:orgname=acme'))
|
|
447
454
|
self.len(1, await core.nodes('risk:leak :leaker -> ps:contact +:orgname=wikileaks'))
|
|
@@ -451,6 +458,7 @@ class RiskModelTest(s_t_utils.SynTest):
|
|
|
451
458
|
|
|
452
459
|
nodes = await core.nodes('''[ risk:extortion=*
|
|
453
460
|
:demanded=20231102
|
|
461
|
+
:deadline=20240329
|
|
454
462
|
:name="APT99 Extorted ACME"
|
|
455
463
|
:desc="APT99 extorted ACME for a zillion vertex coins."
|
|
456
464
|
:type=fingain
|
|
@@ -471,6 +479,7 @@ class RiskModelTest(s_t_utils.SynTest):
|
|
|
471
479
|
self.eq('apt99 extorted acme', nodes[0].get('name'))
|
|
472
480
|
self.eq('APT99 extorted ACME for a zillion vertex coins.', nodes[0].get('desc'))
|
|
473
481
|
self.eq(1698883200000, nodes[0].get('demanded'))
|
|
482
|
+
self.eq(1711670400000, nodes[0].get('deadline'))
|
|
474
483
|
self.eq('fingain.', nodes[0].get('type'))
|
|
475
484
|
self.eq(1, nodes[0].get('public'))
|
|
476
485
|
self.eq(1, nodes[0].get('success'))
|
|
@@ -501,25 +510,41 @@ class RiskModelTest(s_t_utils.SynTest):
|
|
|
501
510
|
self.len(1, await core.nodes('risk:technique:masquerade :node -> * +inet:fqdn=microsoft-verify.com'))
|
|
502
511
|
self.len(1, await core.nodes('risk:technique:masquerade :target -> * +inet:fqdn=microsoft.com'))
|
|
503
512
|
|
|
513
|
+
nodes = await core.nodes('''
|
|
514
|
+
[ risk:vulnerable=*
|
|
515
|
+
:period=(2022, ?)
|
|
516
|
+
:node=(inet:fqdn, vertex.link)
|
|
517
|
+
:vuln={[ risk:vuln=* :name=redtree ]}
|
|
518
|
+
]
|
|
519
|
+
''')
|
|
520
|
+
self.len(1, nodes)
|
|
521
|
+
self.nn(nodes[0].get('vuln'))
|
|
522
|
+
self.eq((1640995200000, 9223372036854775807), nodes[0].get('period'))
|
|
523
|
+
self.eq(('inet:fqdn', 'vertex.link'), nodes[0].get('node'))
|
|
524
|
+
self.len(1, await core.nodes('risk:vulnerable -> risk:vuln'))
|
|
525
|
+
self.len(1, await core.nodes('risk:vuln:name=redtree -> risk:vulnerable :node -> *'))
|
|
526
|
+
|
|
504
527
|
async def test_model_risk_mitigation(self):
|
|
505
528
|
async with self.getTestCore() as core:
|
|
506
529
|
nodes = await core.nodes('''[
|
|
507
530
|
risk:mitigation=*
|
|
508
531
|
:vuln=*
|
|
509
|
-
:name=FooBar
|
|
532
|
+
:name=" FooBar "
|
|
510
533
|
:desc=BazFaz
|
|
511
534
|
:hardware=*
|
|
512
535
|
:software=*
|
|
513
536
|
:reporter:name=vertex
|
|
514
537
|
:reporter = { gen.ou.org vertex }
|
|
538
|
+
:mitre:attack:mitigation=M1036
|
|
515
539
|
]''')
|
|
516
|
-
self.eq('
|
|
540
|
+
self.eq('foobar', nodes[0].props['name'])
|
|
517
541
|
self.eq('BazFaz', nodes[0].props['desc'])
|
|
518
542
|
self.eq('vertex', nodes[0].get('reporter:name'))
|
|
519
543
|
self.nn(nodes[0].get('reporter'))
|
|
520
544
|
self.len(1, await core.nodes('risk:mitigation -> risk:vuln'))
|
|
521
545
|
self.len(1, await core.nodes('risk:mitigation -> it:prod:softver'))
|
|
522
546
|
self.len(1, await core.nodes('risk:mitigation -> it:prod:hardware'))
|
|
547
|
+
self.len(1, await core.nodes('risk:mitigation -> it:mitre:attack:mitigation'))
|
|
523
548
|
|
|
524
549
|
async def test_model_risk_tool_software(self):
|
|
525
550
|
|
|
@@ -536,6 +561,7 @@ class RiskModelTest(s_t_utils.SynTest):
|
|
|
536
561
|
:reporter:published=202302
|
|
537
562
|
:techniques=(*,)
|
|
538
563
|
:tag=cno.mal.cobaltstrike
|
|
564
|
+
:mitre:attack:software=S0001
|
|
539
565
|
|
|
540
566
|
:sophistication=high
|
|
541
567
|
:availability=public
|
|
@@ -551,6 +577,7 @@ class RiskModelTest(s_t_utils.SynTest):
|
|
|
551
577
|
self.eq((1325376000000, 9223372036854775807), nodes[0].get('used'))
|
|
552
578
|
self.eq(1643673600000, nodes[0].get('reporter:discovered'))
|
|
553
579
|
self.eq(1675209600000, nodes[0].get('reporter:published'))
|
|
580
|
+
self.eq('S0001', nodes[0].get('mitre:attack:software'))
|
|
554
581
|
|
|
555
582
|
self.eq('cobaltstrike', nodes[0].get('soft:name'))
|
|
556
583
|
self.eq(('beacon',), nodes[0].get('soft:names'))
|
|
@@ -560,6 +587,7 @@ class RiskModelTest(s_t_utils.SynTest):
|
|
|
560
587
|
self.len(1, await core.nodes('risk:tool:software -> it:prod:soft'))
|
|
561
588
|
self.len(1, await core.nodes('risk:tool:software -> ou:technique'))
|
|
562
589
|
self.len(1, await core.nodes('risk:tool:software -> syn:tag'))
|
|
590
|
+
self.len(1, await core.nodes('risk:tool:software -> it:mitre:attack:software'))
|
|
563
591
|
|
|
564
592
|
nodes = await core.nodes('''
|
|
565
593
|
[ risk:vuln:soft:range=*
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import synapse.exc as s_exc
|
|
2
|
+
import synapse.common as s_common
|
|
3
|
+
import synapse.tests.utils as s_t_utils
|
|
4
|
+
|
|
5
|
+
class SciModelTest(s_t_utils.SynTest):
|
|
6
|
+
|
|
7
|
+
async def test_model_sci(self):
|
|
8
|
+
|
|
9
|
+
async with self.getTestCore() as core:
|
|
10
|
+
|
|
11
|
+
nodes = await core.nodes('''
|
|
12
|
+
[ sci:hypothesis=*
|
|
13
|
+
:name="Light travels as a WAVE"
|
|
14
|
+
:type=physics.quantum
|
|
15
|
+
:summary="Light travels as a wave not a particle."
|
|
16
|
+
]
|
|
17
|
+
''')
|
|
18
|
+
self.len(1, nodes)
|
|
19
|
+
self.eq('physics.quantum.', nodes[0].get('type'))
|
|
20
|
+
self.eq('light travels as a wave', nodes[0].get('name'))
|
|
21
|
+
self.eq('Light travels as a wave not a particle.', nodes[0].get('summary'))
|
|
22
|
+
|
|
23
|
+
nodes = await core.nodes('''
|
|
24
|
+
[ sci:experiment=*
|
|
25
|
+
:name=double-slit
|
|
26
|
+
:time=2024-03-19
|
|
27
|
+
:type=lab.light
|
|
28
|
+
:summary="Foo bar baz."
|
|
29
|
+
]
|
|
30
|
+
''')
|
|
31
|
+
self.len(1, nodes)
|
|
32
|
+
self.eq(1710806400000, nodes[0].get('time'))
|
|
33
|
+
self.eq('lab.light.', nodes[0].get('type'))
|
|
34
|
+
self.eq('double-slit', nodes[0].get('name'))
|
|
35
|
+
self.eq('Foo bar baz.', nodes[0].get('summary'))
|
|
36
|
+
|
|
37
|
+
nodes = await core.nodes('''
|
|
38
|
+
[ sci:evidence=*
|
|
39
|
+
:observation={[ sci:observation=*
|
|
40
|
+
:time=2024-03-19
|
|
41
|
+
:experiment={sci:experiment:name=double-slit}
|
|
42
|
+
:summary="Shadows cast on the wall in a diffusion pattern."
|
|
43
|
+
]}
|
|
44
|
+
:hypothesis={ sci:hypothesis:name="light travels as a wave" }
|
|
45
|
+
:summary="Shadows in wave diffusion pattern support the hypothesis."
|
|
46
|
+
:refutes=(false)
|
|
47
|
+
]
|
|
48
|
+
''')
|
|
49
|
+
self.len(1, nodes)
|
|
50
|
+
self.nn(nodes[0].get('hypothesis'))
|
|
51
|
+
self.nn(nodes[0].get('observation'))
|
|
52
|
+
self.eq(False, nodes[0].get('refutes'))
|
|
53
|
+
self.eq("Shadows in wave diffusion pattern support the hypothesis.", nodes[0].get('summary'))
|
|
54
|
+
|
|
55
|
+
nodes = await core.nodes('sci:observation')
|
|
56
|
+
self.len(1, nodes)
|
|
57
|
+
self.nn(nodes[0].get('experiment'))
|
|
58
|
+
self.eq(1710806400000, nodes[0].get('time'))
|
|
59
|
+
self.eq("Shadows cast on the wall in a diffusion pattern.", nodes[0].get('summary'))
|
synapse/tests/test_model_syn.py
CHANGED
synapse/tests/test_telepath.py
CHANGED
|
@@ -831,15 +831,17 @@ class TeleTest(s_t_utils.SynTest):
|
|
|
831
831
|
|
|
832
832
|
await targ.waitready()
|
|
833
833
|
|
|
834
|
-
|
|
834
|
+
prox00 = await targ.proxy(timeout=12)
|
|
835
|
+
self.eq(110, await prox00.dostuff(100))
|
|
836
|
+
|
|
835
837
|
self.eq(1, fail0.count)
|
|
836
838
|
self.eq(0, fail1.count)
|
|
837
839
|
|
|
838
|
-
_prox = await targ.proxy()
|
|
839
840
|
await dmon0.fini()
|
|
840
|
-
self.true(await
|
|
841
|
+
self.true(await prox00.waitfini(10))
|
|
841
842
|
|
|
842
|
-
|
|
843
|
+
prox01 = await targ.proxy(timeout=12)
|
|
844
|
+
self.eq(110, await prox01.dostuff(100))
|
|
843
845
|
self.eq(1, fail0.count)
|
|
844
846
|
self.eq(1, fail1.count)
|
|
845
847
|
|
|
@@ -855,15 +857,36 @@ class TeleTest(s_t_utils.SynTest):
|
|
|
855
857
|
mesgs = stream.read()
|
|
856
858
|
self.notin('password', mesgs)
|
|
857
859
|
|
|
858
|
-
|
|
860
|
+
prox00 = await targ.proxy(timeout=12)
|
|
861
|
+
self.eq(110, await prox00.dostuff(100))
|
|
859
862
|
|
|
860
863
|
self.eq(1, fail0.count)
|
|
861
864
|
self.eq(2, fail1.count)
|
|
862
865
|
|
|
863
866
|
async with await s_telepath.open(url1) as targ:
|
|
864
|
-
await targ.waitready()
|
|
865
|
-
|
|
867
|
+
await targ.waitready(timeout=12)
|
|
868
|
+
prox00 = await targ.proxy(timeout=12)
|
|
869
|
+
self.eq(110, await prox00.dostuff(100))
|
|
870
|
+
|
|
871
|
+
async def onlink(proxy, urlinfo):
|
|
872
|
+
self.eq(110, await proxy.dostuff(100))
|
|
873
|
+
_url = s_telepath.zipurl(urlinfo)
|
|
874
|
+
logger.info(f'Connected to url={_url}')
|
|
875
|
+
|
|
876
|
+
with self.getAsyncLoggerStream('synapse.tests.test_telepath',
|
|
877
|
+
f'Connected to url=tcp://127.0.0.1:{addr1[1]}/foo') as stream:
|
|
878
|
+
async with await s_telepath.open(url1, onlink=onlink) as targ:
|
|
879
|
+
self.true(await stream.wait(timeout=12))
|
|
880
|
+
|
|
881
|
+
# Coverage
|
|
882
|
+
async def badonlink(proxy, urlinfo):
|
|
883
|
+
raise ValueError('oopsie')
|
|
884
|
+
|
|
885
|
+
with self.getAsyncLoggerStream('synapse.telepath', 'onlink: ') as stream:
|
|
886
|
+
async with await s_telepath.open(url1, onlink=badonlink) as targ:
|
|
887
|
+
self.true(await stream.wait(timeout=12))
|
|
866
888
|
|
|
889
|
+
await dmon0.fini()
|
|
867
890
|
await dmon1.fini()
|
|
868
891
|
|
|
869
892
|
async def test_telepath_poolsize(self):
|
|
@@ -2,6 +2,26 @@ import synapse.lib.output as s_output
|
|
|
2
2
|
import synapse.tests.utils as s_test
|
|
3
3
|
import synapse.tools.modrole as s_t_modrole
|
|
4
4
|
|
|
5
|
+
rolelist = s_test.deguidify('''
|
|
6
|
+
Roles:
|
|
7
|
+
ce6928dfe88cace918c405bbef51e72f - all
|
|
8
|
+
0f316b3f3e6ec970fcdb9a085fcd5b77 - visi
|
|
9
|
+
'''.strip())
|
|
10
|
+
|
|
11
|
+
roleinfo = s_test.deguidify('''
|
|
12
|
+
Role: visi (145c3321a0cd0cd06de19174415a7aeb)
|
|
13
|
+
|
|
14
|
+
Rules:
|
|
15
|
+
[0 ] - !foo.bar.baz
|
|
16
|
+
[1 ] - foo.bar
|
|
17
|
+
|
|
18
|
+
Gates:
|
|
19
|
+
3ad191c63a201df3246c6d6ff81763ad
|
|
20
|
+
Admin: False
|
|
21
|
+
[0 ] - !bar.baz.faz
|
|
22
|
+
[1 ] - bar.baz
|
|
23
|
+
'''.strip())
|
|
24
|
+
|
|
5
25
|
class ModRoleTest(s_test.SynTest):
|
|
6
26
|
|
|
7
27
|
async def test_tools_modrole(self):
|
|
@@ -40,6 +60,60 @@ class ModRoleTest(s_test.SynTest):
|
|
|
40
60
|
self.true(bool(visi.allowed('foo.bar.gaz'.split('.'))))
|
|
41
61
|
self.false(bool(visi.allowed('foo.bar.baz'.split('.'))))
|
|
42
62
|
|
|
63
|
+
gateiden = core.getLayer().iden
|
|
64
|
+
argv = (
|
|
65
|
+
'--svcurl', svcurl,
|
|
66
|
+
'visi',
|
|
67
|
+
'--allow', 'bar.baz',
|
|
68
|
+
'--deny', 'bar.baz.faz',
|
|
69
|
+
'--gate', gateiden,
|
|
70
|
+
)
|
|
71
|
+
outp = s_output.OutPutStr()
|
|
72
|
+
self.eq(0, await s_t_modrole.main(argv, outp=outp))
|
|
73
|
+
self.isin(f'...adding allow rule: bar.baz on gate {gateiden}', str(outp))
|
|
74
|
+
self.isin(f'...adding deny rule: bar.baz.faz on gate {gateiden}', str(outp))
|
|
75
|
+
|
|
76
|
+
gate = await core.getAuthGate(gateiden)
|
|
77
|
+
for role in gate['roles']:
|
|
78
|
+
if role['iden'] == visi.iden:
|
|
79
|
+
self.isin((True, ('bar', 'baz')), role['rules'])
|
|
80
|
+
self.isin((False, ('bar', 'baz', 'faz')), role['rules'])
|
|
81
|
+
|
|
82
|
+
argv = (
|
|
83
|
+
'--svcurl', svcurl,
|
|
84
|
+
'--list',
|
|
85
|
+
)
|
|
86
|
+
outp = s_output.OutPutStr()
|
|
87
|
+
self.eq(0, await s_t_modrole.main(argv, outp=outp))
|
|
88
|
+
self.isin(rolelist, s_test.deguidify(str(outp)))
|
|
89
|
+
|
|
90
|
+
argv = (
|
|
91
|
+
'--svcurl', svcurl,
|
|
92
|
+
'--list',
|
|
93
|
+
'visi',
|
|
94
|
+
)
|
|
95
|
+
outp = s_output.OutPutStr()
|
|
96
|
+
self.eq(0, await s_t_modrole.main(argv, outp=outp))
|
|
97
|
+
self.isin(roleinfo, s_test.deguidify(str(outp)))
|
|
98
|
+
|
|
99
|
+
argv = (
|
|
100
|
+
'--svcurl', svcurl,
|
|
101
|
+
'--list',
|
|
102
|
+
'newprole',
|
|
103
|
+
)
|
|
104
|
+
outp = s_output.OutPutStr()
|
|
105
|
+
self.eq(1, await s_t_modrole.main(argv, outp=outp))
|
|
106
|
+
self.isin('ERROR: Role not found: newprole', str(outp))
|
|
107
|
+
|
|
108
|
+
argv = (
|
|
109
|
+
'--svcurl', svcurl,
|
|
110
|
+
'visi',
|
|
111
|
+
'--gate', 'newp',
|
|
112
|
+
)
|
|
113
|
+
outp = s_output.OutPutStr()
|
|
114
|
+
self.eq(1, await s_t_modrole.main(argv, outp=outp))
|
|
115
|
+
self.isin('ERROR: No auth gate found with iden: newp', str(outp))
|
|
116
|
+
|
|
43
117
|
argv = (
|
|
44
118
|
'--svcurl', svcurl,
|
|
45
119
|
'--add',
|
|
@@ -59,3 +133,10 @@ class ModRoleTest(s_test.SynTest):
|
|
|
59
133
|
self.eq(0, await s_t_modrole.main(argv, outp=outp))
|
|
60
134
|
self.isin('...deleting role: visi', str(outp))
|
|
61
135
|
self.none(await core.auth.getRoleByName('visi'))
|
|
136
|
+
|
|
137
|
+
argv = (
|
|
138
|
+
'--svcurl', svcurl,
|
|
139
|
+
)
|
|
140
|
+
outp = s_output.OutPutStr()
|
|
141
|
+
self.eq(1, await s_t_modrole.main(argv, outp=outp))
|
|
142
|
+
self.isin('ERROR: A rolename argument is required when --list is not specified.', str(outp))
|
|
@@ -2,6 +2,32 @@ import synapse.lib.output as s_output
|
|
|
2
2
|
import synapse.tests.utils as s_test
|
|
3
3
|
import synapse.tools.moduser as s_t_moduser
|
|
4
4
|
|
|
5
|
+
userlist = '''
|
|
6
|
+
Users:
|
|
7
|
+
root
|
|
8
|
+
visi
|
|
9
|
+
'''.strip()
|
|
10
|
+
|
|
11
|
+
userinfo = s_test.deguidify('''
|
|
12
|
+
User: visi (04dddd4ff39e4ce00b36c7d526b9eac7)
|
|
13
|
+
|
|
14
|
+
Locked: False
|
|
15
|
+
Admin: False
|
|
16
|
+
Email: visi@test.com
|
|
17
|
+
Rules:
|
|
18
|
+
[0 ] - !foo.bar.baz
|
|
19
|
+
[1 ] - foo.bar
|
|
20
|
+
|
|
21
|
+
Roles:
|
|
22
|
+
576a948f9944c58d3953f0d36bc2da81 - all
|
|
23
|
+
|
|
24
|
+
Gates:
|
|
25
|
+
c7b276154c0c799430668cb3c4cd259d
|
|
26
|
+
Admin: False
|
|
27
|
+
[0 ] - !bar.baz.faz
|
|
28
|
+
[1 ] - bar.baz
|
|
29
|
+
'''.strip())
|
|
30
|
+
|
|
5
31
|
class ModUserTest(s_test.SynTest):
|
|
6
32
|
|
|
7
33
|
async def test_tools_moduser(self):
|
|
@@ -124,6 +150,78 @@ class ModUserTest(s_test.SynTest):
|
|
|
124
150
|
self.true(bool(visi.allowed('foo.bar.gaz'.split('.'))))
|
|
125
151
|
self.false(bool(visi.allowed('foo.bar.baz'.split('.'))))
|
|
126
152
|
|
|
153
|
+
gateiden = core.getLayer().iden
|
|
154
|
+
argv = (
|
|
155
|
+
'--svcurl', svcurl,
|
|
156
|
+
'visi',
|
|
157
|
+
'--admin', 'true',
|
|
158
|
+
'--gate', gateiden,
|
|
159
|
+
)
|
|
160
|
+
outp = s_output.OutPutStr()
|
|
161
|
+
self.eq(0, await s_t_moduser.main(argv, outp=outp))
|
|
162
|
+
self.isin(f'...setting admin: true on gate {gateiden}', str(outp))
|
|
163
|
+
|
|
164
|
+
gate = await core.getAuthGate(gateiden)
|
|
165
|
+
for user in gate['users']:
|
|
166
|
+
if user['iden'] == visi.iden:
|
|
167
|
+
self.true(user['admin'])
|
|
168
|
+
|
|
169
|
+
gateiden = core.getLayer().iden
|
|
170
|
+
argv = (
|
|
171
|
+
'--svcurl', svcurl,
|
|
172
|
+
'visi',
|
|
173
|
+
'--admin', 'false',
|
|
174
|
+
'--allow', 'bar.baz',
|
|
175
|
+
'--deny', 'bar.baz.faz',
|
|
176
|
+
'--gate', gateiden,
|
|
177
|
+
)
|
|
178
|
+
outp = s_output.OutPutStr()
|
|
179
|
+
self.eq(0, await s_t_moduser.main(argv, outp=outp))
|
|
180
|
+
self.isin(f'...setting admin: false on gate {gateiden}', str(outp))
|
|
181
|
+
self.isin(f'...adding allow rule: bar.baz on gate {gateiden}', str(outp))
|
|
182
|
+
self.isin(f'...adding deny rule: bar.baz.faz on gate {gateiden}', str(outp))
|
|
183
|
+
|
|
184
|
+
gate = await core.getAuthGate(gateiden)
|
|
185
|
+
for user in gate['users']:
|
|
186
|
+
if user['iden'] == visi.iden:
|
|
187
|
+
self.isin((True, ('bar', 'baz')), user['rules'])
|
|
188
|
+
self.isin((False, ('bar', 'baz', 'faz')), user['rules'])
|
|
189
|
+
|
|
190
|
+
argv = (
|
|
191
|
+
'--svcurl', svcurl,
|
|
192
|
+
'--list',
|
|
193
|
+
)
|
|
194
|
+
outp = s_output.OutPutStr()
|
|
195
|
+
self.eq(0, await s_t_moduser.main(argv, outp=outp))
|
|
196
|
+
self.isin(userlist, str(outp))
|
|
197
|
+
|
|
198
|
+
argv = (
|
|
199
|
+
'--svcurl', svcurl,
|
|
200
|
+
'--list',
|
|
201
|
+
'visi',
|
|
202
|
+
)
|
|
203
|
+
outp = s_output.OutPutStr()
|
|
204
|
+
self.eq(0, await s_t_moduser.main(argv, outp=outp))
|
|
205
|
+
self.isin(userinfo, s_test.deguidify(str(outp)))
|
|
206
|
+
|
|
207
|
+
argv = (
|
|
208
|
+
'--svcurl', svcurl,
|
|
209
|
+
'--list',
|
|
210
|
+
'newpuser',
|
|
211
|
+
)
|
|
212
|
+
outp = s_output.OutPutStr()
|
|
213
|
+
self.eq(1, await s_t_moduser.main(argv, outp=outp))
|
|
214
|
+
self.isin('ERROR: User not found: newpuser', str(outp))
|
|
215
|
+
|
|
216
|
+
argv = (
|
|
217
|
+
'--svcurl', svcurl,
|
|
218
|
+
'visi',
|
|
219
|
+
'--gate', 'newp',
|
|
220
|
+
)
|
|
221
|
+
outp = s_output.OutPutStr()
|
|
222
|
+
self.eq(1, await s_t_moduser.main(argv, outp=outp))
|
|
223
|
+
self.isin('ERROR: No auth gate found with iden: newp', str(outp))
|
|
224
|
+
|
|
127
225
|
argv = (
|
|
128
226
|
'--svcurl', svcurl,
|
|
129
227
|
'visi',
|
|
@@ -151,3 +249,10 @@ class ModUserTest(s_test.SynTest):
|
|
|
151
249
|
outp = s_output.OutPutStr()
|
|
152
250
|
self.eq(1, await s_t_moduser.main(argv, outp=outp))
|
|
153
251
|
self.isin('ERROR: User not found (need --add?): visi', str(outp))
|
|
252
|
+
|
|
253
|
+
argv = (
|
|
254
|
+
'--svcurl', svcurl,
|
|
255
|
+
)
|
|
256
|
+
outp = s_output.OutPutStr()
|
|
257
|
+
self.eq(1, await s_t_moduser.main(argv, outp=outp))
|
|
258
|
+
self.isin('ERROR: A username argument is required when --list is not specified.', str(outp))
|
synapse/tools/modrole.py
CHANGED
|
@@ -11,15 +11,34 @@ descr = '''
|
|
|
11
11
|
Add or modify a role in a Synapse service.
|
|
12
12
|
'''
|
|
13
13
|
|
|
14
|
+
def printrole(role, outp):
|
|
15
|
+
|
|
16
|
+
outp.printf(f'Role: {role.get("name")} ({role.get("iden")})')
|
|
17
|
+
outp.printf('')
|
|
18
|
+
outp.printf(' Rules:')
|
|
19
|
+
for indx, rule in enumerate(role.get('rules')):
|
|
20
|
+
outp.printf(f' [{str(indx).ljust(3)}] - {s_common.reprauthrule(rule)}')
|
|
21
|
+
|
|
22
|
+
outp.printf('')
|
|
23
|
+
outp.printf(' Gates:')
|
|
24
|
+
for gateiden, gateinfo in role.get('authgates', {}).items():
|
|
25
|
+
outp.printf(f' {gateiden}')
|
|
26
|
+
outp.printf(f' Admin: {gateinfo.get("admin") == True}')
|
|
27
|
+
for indx, rule in enumerate(gateinfo.get('rules', ())):
|
|
28
|
+
outp.printf(f' [{str(indx).ljust(3)}] - {s_common.reprauthrule(rule)}')
|
|
29
|
+
|
|
14
30
|
async def main(argv, outp=s_output.stdout):
|
|
15
31
|
|
|
16
32
|
pars = argparse.ArgumentParser(prog='modrole', description=descr)
|
|
17
33
|
pars.add_argument('--svcurl', default='cell:///vertex/storage', help='The telepath URL of the Synapse service.')
|
|
18
34
|
pars.add_argument('--add', default=False, action='store_true', help='Add the role if they do not already exist.')
|
|
19
35
|
pars.add_argument('--del', dest='delete', default=False, action='store_true', help='Delete the role if it exists.')
|
|
36
|
+
pars.add_argument('--list', default=False, action='store_true',
|
|
37
|
+
help='List existing roles, or rules of a specific role.')
|
|
20
38
|
pars.add_argument('--allow', default=[], action='append', help='A permission string to allow for the role.')
|
|
21
39
|
pars.add_argument('--deny', default=[], action='append', help='A permission string to deny for the role.')
|
|
22
|
-
pars.add_argument('
|
|
40
|
+
pars.add_argument('--gate', default=None, help='The iden of an auth gate to add/del rules on.')
|
|
41
|
+
pars.add_argument('rolename', nargs='?', help='The rolename to add/edit.')
|
|
23
42
|
|
|
24
43
|
opts = pars.parse_args(argv)
|
|
25
44
|
|
|
@@ -31,6 +50,31 @@ async def main(argv, outp=s_output.stdout):
|
|
|
31
50
|
|
|
32
51
|
async with await s_telepath.openurl(opts.svcurl) as cell:
|
|
33
52
|
|
|
53
|
+
if opts.list:
|
|
54
|
+
if opts.rolename:
|
|
55
|
+
role = await cell.getRoleDefByName(opts.rolename)
|
|
56
|
+
if role is None:
|
|
57
|
+
outp.printf(f'ERROR: Role not found: {opts.rolename}')
|
|
58
|
+
return 1
|
|
59
|
+
|
|
60
|
+
printrole(role, outp)
|
|
61
|
+
|
|
62
|
+
else:
|
|
63
|
+
outp.printf('Roles:')
|
|
64
|
+
for role in await cell.getRoleDefs():
|
|
65
|
+
outp.printf(f' {role.get("iden")} - {role.get("name")}')
|
|
66
|
+
|
|
67
|
+
return 0
|
|
68
|
+
elif opts.rolename is None:
|
|
69
|
+
outp.printf(f'ERROR: A rolename argument is required when --list is not specified.')
|
|
70
|
+
return 1
|
|
71
|
+
|
|
72
|
+
if opts.gate:
|
|
73
|
+
gate = await cell.getAuthGate(opts.gate)
|
|
74
|
+
if gate is None:
|
|
75
|
+
outp.printf(f'ERROR: No auth gate found with iden: {opts.gate}')
|
|
76
|
+
return 1
|
|
77
|
+
|
|
34
78
|
role = await cell.getRoleDefByName(opts.rolename)
|
|
35
79
|
if role is not None:
|
|
36
80
|
outp.printf(f'Modifying role: {opts.rolename}')
|
|
@@ -52,15 +96,23 @@ async def main(argv, outp=s_output.stdout):
|
|
|
52
96
|
|
|
53
97
|
for allow in opts.allow:
|
|
54
98
|
perm = allow.lower().split('.')
|
|
55
|
-
|
|
56
|
-
if
|
|
57
|
-
|
|
99
|
+
mesg = f'...adding allow rule: {allow}'
|
|
100
|
+
if opts.gate:
|
|
101
|
+
mesg += f' on gate {opts.gate}'
|
|
102
|
+
|
|
103
|
+
outp.printf(mesg)
|
|
104
|
+
if not await cell.isRoleAllowed(roleiden, perm, gateiden=opts.gate):
|
|
105
|
+
await cell.addRoleRule(roleiden, (True, perm), indx=0, gateiden=opts.gate)
|
|
58
106
|
|
|
59
107
|
for deny in opts.deny:
|
|
60
108
|
perm = deny.lower().split('.')
|
|
61
|
-
|
|
62
|
-
if
|
|
63
|
-
|
|
109
|
+
mesg = f'...adding deny rule: {deny}'
|
|
110
|
+
if opts.gate:
|
|
111
|
+
mesg += f' on gate {opts.gate}'
|
|
112
|
+
|
|
113
|
+
outp.printf(mesg)
|
|
114
|
+
if await cell.isRoleAllowed(roleiden, perm, gateiden=opts.gate):
|
|
115
|
+
await cell.addRoleRule(roleiden, (False, perm), indx=0, gateiden=opts.gate)
|
|
64
116
|
return 0
|
|
65
117
|
|
|
66
118
|
if __name__ == '__main__': # pragma: no cover
|