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.

Files changed (89) hide show
  1. synapse/axon.py +3 -3
  2. synapse/cmds/cortex.py +1 -6
  3. synapse/common.py +7 -1
  4. synapse/cortex.py +145 -192
  5. synapse/datamodel.py +36 -1
  6. synapse/lib/agenda.py +87 -97
  7. synapse/lib/aha.py +51 -0
  8. synapse/lib/ast.py +22 -23
  9. synapse/lib/base.py +0 -6
  10. synapse/lib/boss.py +3 -0
  11. synapse/lib/cell.py +70 -39
  12. synapse/lib/certdir.py +9 -0
  13. synapse/lib/hiveauth.py +65 -12
  14. synapse/lib/httpapi.py +1 -0
  15. synapse/lib/modelrev.py +121 -33
  16. synapse/lib/modules.py +1 -0
  17. synapse/lib/nexus.py +64 -26
  18. synapse/lib/parser.py +2 -0
  19. synapse/lib/schemas.py +14 -0
  20. synapse/lib/snap.py +50 -4
  21. synapse/lib/storm.lark +4 -3
  22. synapse/lib/storm.py +96 -22
  23. synapse/lib/storm_format.py +1 -0
  24. synapse/lib/stormlib/aha.py +7 -1
  25. synapse/lib/stormlib/auth.py +13 -5
  26. synapse/lib/stormlib/cache.py +202 -0
  27. synapse/lib/stormlib/cortex.py +147 -8
  28. synapse/lib/stormlib/gen.py +53 -6
  29. synapse/lib/stormlib/math.py +1 -1
  30. synapse/lib/stormlib/model.py +11 -1
  31. synapse/lib/stormlib/spooled.py +109 -0
  32. synapse/lib/stormlib/vault.py +1 -1
  33. synapse/lib/stormtypes.py +113 -17
  34. synapse/lib/trigger.py +36 -47
  35. synapse/lib/types.py +29 -2
  36. synapse/lib/version.py +2 -2
  37. synapse/lib/view.py +80 -53
  38. synapse/models/economic.py +174 -5
  39. synapse/models/files.py +2 -0
  40. synapse/models/inet.py +77 -2
  41. synapse/models/infotech.py +12 -12
  42. synapse/models/orgs.py +72 -21
  43. synapse/models/person.py +40 -11
  44. synapse/models/risk.py +78 -24
  45. synapse/models/science.py +102 -0
  46. synapse/telepath.py +117 -35
  47. synapse/tests/test_cortex.py +84 -158
  48. synapse/tests/test_datamodel.py +22 -0
  49. synapse/tests/test_lib_agenda.py +52 -96
  50. synapse/tests/test_lib_aha.py +126 -4
  51. synapse/tests/test_lib_ast.py +412 -6
  52. synapse/tests/test_lib_cell.py +24 -8
  53. synapse/tests/test_lib_certdir.py +32 -0
  54. synapse/tests/test_lib_grammar.py +9 -1
  55. synapse/tests/test_lib_httpapi.py +0 -1
  56. synapse/tests/test_lib_jupyter.py +0 -1
  57. synapse/tests/test_lib_modelrev.py +41 -0
  58. synapse/tests/test_lib_nexus.py +38 -0
  59. synapse/tests/test_lib_storm.py +95 -5
  60. synapse/tests/test_lib_stormlib_cache.py +272 -0
  61. synapse/tests/test_lib_stormlib_cortex.py +71 -0
  62. synapse/tests/test_lib_stormlib_gen.py +37 -2
  63. synapse/tests/test_lib_stormlib_model.py +2 -0
  64. synapse/tests/test_lib_stormlib_spooled.py +190 -0
  65. synapse/tests/test_lib_stormlib_vault.py +12 -3
  66. synapse/tests/test_lib_stormsvc.py +0 -10
  67. synapse/tests/test_lib_stormtypes.py +60 -8
  68. synapse/tests/test_lib_trigger.py +20 -2
  69. synapse/tests/test_lib_types.py +17 -1
  70. synapse/tests/test_model_economic.py +114 -0
  71. synapse/tests/test_model_files.py +2 -0
  72. synapse/tests/test_model_inet.py +73 -1
  73. synapse/tests/test_model_infotech.py +2 -2
  74. synapse/tests/test_model_orgs.py +10 -1
  75. synapse/tests/test_model_risk.py +30 -2
  76. synapse/tests/test_model_science.py +59 -0
  77. synapse/tests/test_model_syn.py +0 -1
  78. synapse/tests/test_telepath.py +30 -7
  79. synapse/tests/test_tools_modrole.py +81 -0
  80. synapse/tests/test_tools_moduser.py +105 -0
  81. synapse/tools/modrole.py +59 -7
  82. synapse/tools/moduser.py +78 -10
  83. {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/METADATA +2 -2
  84. {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/RECORD +87 -83
  85. {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/WHEEL +1 -1
  86. synapse/lib/provenance.py +0 -111
  87. synapse/tests/test_lib_provenance.py +0 -37
  88. {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/LICENSE +0 -0
  89. {synapse-2.164.0.dist-info → synapse-2.166.0.dist-info}/top_level.txt +0 -0
@@ -186,8 +186,8 @@ class AhaTest(s_test.SynTest):
186
186
  self.nn(await proxy.getCellIden())
187
187
 
188
188
  with self.raises(s_exc.BadArg):
189
- await cryo.ahaclient.waitready(timeout=2)
190
- await cryo.ahaclient.modAhaSvcInfo('cryo.mynet', {'newp': 'newp'})
189
+ _proxy = await cryo.ahaclient.proxy(timeout=2)
190
+ await _proxy.modAhaSvcInfo('cryo.mynet', {'newp': 'newp'})
191
191
 
192
192
  async with await s_telepath.openurl('aha://root:secret@0.cryo.mynet') as proxy:
193
193
  self.nn(await proxy.getCellIden())
@@ -885,6 +885,27 @@ class AhaTest(s_test.SynTest):
885
885
  self.isin('Provisioning aha:network must be equal to the Aha servers network',
886
886
  cm.exception.get('mesg'))
887
887
 
888
+ # We can generate urls and then drop them en-mass. They will not usable.
889
+ provurls = []
890
+ enrlursl = []
891
+ async with aha.getLocalProxy() as proxy:
892
+ provurls.append(await proxy.addAhaSvcProv('00.cell'))
893
+ provurls.append(await proxy.addAhaSvcProv('01.cell', {'mirror': 'cell'}))
894
+ enrlursl.append(await proxy.addAhaUserEnroll('bob'))
895
+ enrlursl.append(await proxy.addAhaUserEnroll('alice'))
896
+
897
+ await proxy.clearAhaSvcProvs()
898
+ await proxy.clearAhaUserEnrolls()
899
+
900
+ for url in provurls:
901
+ with self.raises(s_exc.NoSuchName) as cm:
902
+ async with await s_telepath.openurl(url) as client:
903
+ self.fail(f'Connected to an expired provisioning URL {url}') # pragma: no cover
904
+ for url in enrlursl:
905
+ with self.raises(s_exc.NoSuchName) as cm:
906
+ async with await s_telepath.openurl(url) as prox:
907
+ self.fail(f'Connected to an expired enrollment URL {url}') # pragma: no cover
908
+
888
909
  async def test_aha_httpapi(self):
889
910
 
890
911
  conf = {
@@ -1148,13 +1169,24 @@ class AhaTest(s_test.SynTest):
1148
1169
  self.stormHasNoWarnErr(msgs)
1149
1170
  self.stormIsInPrint('Created AHA service pool: pool00.loop.vertex.link', msgs)
1150
1171
 
1151
- with self.raises(s_exc.BadArg):
1152
- await s_telepath.open('aha://pool00...')
1172
+ # Pool has no members....
1173
+ pool = await s_telepath.open('aha://pool00...')
1174
+ self.eq(0, pool.size())
1175
+ waiter = pool.waiter(0, 'svc:add')
1153
1176
 
1154
1177
  msgs = await core00.stormlist('aha.pool.svc.add pool00... 00...')
1155
1178
  self.stormHasNoWarnErr(msgs)
1156
1179
  self.stormIsInPrint('AHA service (00...) added to service pool (pool00.loop.vertex.link)', msgs)
1157
1180
 
1181
+ self.len(1, await waiter.wait(timeout=12))
1182
+ prox = await pool.proxy(timeout=12)
1183
+ info = await prox.getCellInfo()
1184
+ self.eq('00', info.get('cell').get('aha').get('name'))
1185
+ self.eq(1, pool.size())
1186
+ await pool.fini()
1187
+ self.eq(0, pool.size())
1188
+ self.true(prox.isfini)
1189
+
1158
1190
  poolinfo = await aha.getAhaPool('pool00...')
1159
1191
  self.len(1, poolinfo['services'])
1160
1192
 
@@ -1163,6 +1195,9 @@ class AhaTest(s_test.SynTest):
1163
1195
  self.stormIsInPrint(' 00.loop.vertex.link', msgs)
1164
1196
  self.stormIsInPrint('1 pools', msgs)
1165
1197
 
1198
+ msgs = await core00.stormlist('$lib.print($lib.aha.pool.get(pool00.loop.vertex.link))')
1199
+ self.stormIsInPrint('aha:pool: pool00.loop.vertex.link', msgs)
1200
+
1166
1201
  async with await s_telepath.open('aha://pool00...') as pool:
1167
1202
 
1168
1203
  replay = s_common.envbool('SYNDEV_NEXUS_REPLAY')
@@ -1320,3 +1355,90 @@ class AhaTest(s_test.SynTest):
1320
1355
  svcinfo = snfo.get('svcinfo')
1321
1356
  ready = svcinfo.get('ready')
1322
1357
  self.true(ready)
1358
+
1359
+ async def test_aha_provision_longname(self):
1360
+ # Run a long network name and try provisioning with values that would exceed CSR
1361
+ # and certificate functionality.
1362
+ with self.withNexusReplay() as stack:
1363
+
1364
+ with self.getTestDir() as dirn:
1365
+ aha00dirn = s_common.gendir(dirn, 'aha00')
1366
+ svc0dirn = s_common.gendir(dirn, 'svc00')
1367
+ async with await s_base.Base.anit() as cm:
1368
+ # Add enough space to allow aha CA bootstraping.
1369
+ basenet = 'loop.vertex.link'
1370
+ networkname = f'{"x" * (64 - 7 - len(basenet))}.{basenet}'
1371
+ aconf = {
1372
+ 'aha:name': 'aha',
1373
+ 'aha:network': networkname,
1374
+ 'provision:listen': f'ssl://aha.{networkname}:0'
1375
+ }
1376
+ name = aconf.get('aha:name')
1377
+ netw = aconf.get('aha:network')
1378
+ dnsname = f'{name}.{netw}'
1379
+
1380
+ aha = await s_aha.AhaCell.anit(aha00dirn, conf=aconf)
1381
+ await cm.enter_context(aha)
1382
+
1383
+ addr, port = aha.provdmon.addr
1384
+ # update the config to reflect the dynamically bound port
1385
+ aha.conf['provision:listen'] = f'ssl://{dnsname}:{port}'
1386
+
1387
+ # do this config ex-post-facto due to port binding...
1388
+ host, ahaport = await aha.dmon.listen(f'ssl://0.0.0.0:0?hostname={dnsname}&ca={netw}')
1389
+ aha.conf['aha:urls'] = (f'ssl://{dnsname}:{ahaport}',)
1390
+
1391
+ with self.raises(s_exc.BadArg) as errcm:
1392
+ await aha.addAhaSvcProv('00.svc', provinfo=None)
1393
+ self.isin('Hostname value must not exceed 64 characters in length.',
1394
+ errcm.exception.get('mesg'))
1395
+ self.isin('len=65', errcm.exception.get('mesg'))
1396
+
1397
+ # We can generate a 64 character names though.
1398
+ onetime = await aha.addAhaSvcProv('00.sv', provinfo=None)
1399
+ sconf = {'aha:provision': onetime}
1400
+ s_common.yamlsave(sconf, svc0dirn, 'cell.yaml')
1401
+ svc0 = await s_cell.Cell.anit(svc0dirn, conf=sconf)
1402
+ await cm.enter_context(svc0)
1403
+
1404
+ # Cannot generate a user cert that would be a problem for signing
1405
+ with self.raises(s_exc.BadArg) as errcm:
1406
+ await aha.addAhaUserEnroll('ruhroh')
1407
+ self.isin('Username value must not exceed 64 characters in length.',
1408
+ errcm.exception.get('mesg'))
1409
+ self.isin('len=65', errcm.exception.get('mesg'))
1410
+
1411
+ # We can generate a name that is 64 characters in length and have its csr signed
1412
+ onetime = await aha.addAhaUserEnroll('vvvvv')
1413
+ async with await s_telepath.openurl(onetime) as prox:
1414
+ userinfo = await prox.getUserInfo()
1415
+ ahauser = userinfo.get('aha:user')
1416
+ ahanetw = userinfo.get('aha:network')
1417
+ username = f'{ahauser}@{ahanetw}'
1418
+ byts = aha.certdir.genUserCsr(username)
1419
+ byts = await prox.signUserCsr(byts)
1420
+ self.nn(byts)
1421
+
1422
+ # 0 length inputs
1423
+ with self.raises(s_exc.BadArg) as errcm:
1424
+ await aha.addAhaSvcProv('')
1425
+ self.isin('Empty name values are not allowed for provisioning.', errcm.exception.get('mesg'))
1426
+ with self.raises(s_exc.BadArg) as errcm:
1427
+ await aha.addAhaUserEnroll('')
1428
+ self.isin('Empty name values are not allowed for provisioning.', errcm.exception.get('mesg'))
1429
+
1430
+ # add an aha bootstrapping test failure
1431
+ with self.getTestDir() as dirn:
1432
+ aha00dirn = s_common.gendir(dirn, 'aha00')
1433
+ async with await s_base.Base.anit() as cm:
1434
+ # Make the network too long that we cannot bootstrap the CA
1435
+ basenet = 'loop.vertex.link'
1436
+ networkname = f'{"x" * (64 - len(basenet))}.{basenet}'
1437
+ aconf = {
1438
+ 'aha:name': 'aha',
1439
+ 'aha:network': networkname,
1440
+ 'provision:listen': f'ssl://aha.{networkname}:0'
1441
+ }
1442
+ with self.raises(s_exc.CryptoErr) as errcm:
1443
+ await s_aha.AhaCell.anit(aha00dirn, conf=aconf)
1444
+ self.isin('Certificate name values must be between 1-64 characters', errcm.exception.get('mesg'))
@@ -6,6 +6,7 @@ from unittest import mock
6
6
 
7
7
  import synapse.exc as s_exc
8
8
  import synapse.common as s_common
9
+ import synapse.datamodel as s_datamodel
9
10
 
10
11
  import synapse.lib.ast as s_ast
11
12
  import synapse.lib.snap as s_snap
@@ -16,7 +17,6 @@ foo_stormpkg = {
16
17
  'name': 'foo',
17
18
  'desc': 'The Foo Module',
18
19
  'version': (0, 0, 1),
19
- 'synapse_minversion': [2, 144, 0],
20
20
  'synapse_version': '>=2.8.0,<3.0.0',
21
21
  'modules': [
22
22
  {
@@ -713,6 +713,8 @@ class AstTest(s_test.SynTest):
713
713
  self.len(3, await core.nodes('$form=(inet:dns:a, inet:dns:mx) inet:fqdn=vertex.link -+> $form'))
714
714
 
715
715
  self.len(2, await core.nodes('inet:fqdn=vertex.link :zone -> (inet:dns:a:fqdn, inet:dns:mx:fqdn)'))
716
+ self.len(2, await core.nodes('$prop=fqdn $targ=inet:fqdn inet:dns:a* :$prop -> $targ'))
717
+
716
718
  self.len(1, await core.nodes('$form=inet:dns:a:fqdn inet:fqdn=vertex.link :zone -> $form'))
717
719
 
718
720
  self.len(3, await core.nodes('inet:fqdn=vertex.link :zone -+> (inet:dns:a:fqdn, inet:dns:mx:fqdn)'))
@@ -1216,14 +1218,12 @@ class AstTest(s_test.SynTest):
1216
1218
  otherpkg = {
1217
1219
  'name': 'foosball',
1218
1220
  'version': '0.0.1',
1219
- 'synapse_minversion': [2, 144, 0],
1220
1221
  'synapse_version': '>=2.8.0,<3.0.0',
1221
1222
  }
1222
1223
 
1223
1224
  stormpkg = {
1224
1225
  'name': 'stormpkg',
1225
1226
  'version': '1.2.3',
1226
- 'synapse_minversion': [2, 144, 0],
1227
1227
  'synapse_version': '>=2.8.0,<3.0.0',
1228
1228
  'commands': (
1229
1229
  {
@@ -1236,7 +1236,6 @@ class AstTest(s_test.SynTest):
1236
1236
  stormpkgnew = {
1237
1237
  'name': 'stormpkg',
1238
1238
  'version': '1.2.4',
1239
- 'synapse_minversion': [2, 144, 0],
1240
1239
  'synapse_version': '>=2.8.0,<3.0.0',
1241
1240
  'commands': (
1242
1241
  {
@@ -1249,7 +1248,6 @@ class AstTest(s_test.SynTest):
1249
1248
  jsonpkg = {
1250
1249
  'name': 'jsonpkg',
1251
1250
  'version': '1.2.3',
1252
- 'synapse_minversion': [2, 144, 0],
1253
1251
  'synapse_version': '>=2.8.0,<3.0.0',
1254
1252
  'docs': (
1255
1253
  {
@@ -2365,6 +2363,12 @@ class AstTest(s_test.SynTest):
2365
2363
  nodes = await core.nodes('if (false) { [inet:ipv4=1.2.3.4] }')
2366
2364
  self.len(0, nodes)
2367
2365
 
2366
+ nodes = await core.nodes('if (null) { [inet:ipv4=1.2.3.4] }')
2367
+ self.len(0, nodes)
2368
+
2369
+ self.none(await core.callStorm('return((null))'))
2370
+ self.eq({'foo': None}, await core.callStorm('return(({"foo": null}))'))
2371
+
2368
2372
  nodes = await core.nodes('[ test:int=(18 + 2) ]')
2369
2373
  self.len(1, nodes)
2370
2374
  self.eq(nodes[0].ndef, ('test:int', 20))
@@ -3549,7 +3553,17 @@ class AstTest(s_test.SynTest):
3549
3553
 
3550
3554
  async def test_ast_prop_perms(self):
3551
3555
 
3552
- async with self.getTestCore() as core:
3556
+ async with self.getTestCore() as core: # type: s_cortex.Cortex
3557
+
3558
+ # TODO: This goes away in 3.0.0 when we remove old style permissions.
3559
+ for key, prop in core.model.props.items():
3560
+ if not isinstance(prop, s_datamodel.Prop):
3561
+ continue
3562
+ if prop.isuniv:
3563
+ continue
3564
+ self.len(2, prop.delperms)
3565
+ self.len(2, prop.setperms)
3566
+
3553
3567
  visi = (await core.addUser('visi'))['iden']
3554
3568
 
3555
3569
  self.len(1, await core.nodes('[ inet:ipv4=1.2.3.4 :asn=10 ]'))
@@ -3571,3 +3585,395 @@ class AstTest(s_test.SynTest):
3571
3585
  self.stormHasNoWarnErr(msgs)
3572
3586
 
3573
3587
  self.len(1, await core.nodes('inet:ipv4=1.2.3.4 [ -:asn ]', opts={'user': visi}))
3588
+
3589
+ # When evaluating the property set permissions:
3590
+ #
3591
+ # node.prop.del.<form>.<prop>
3592
+ # node.prop.del.<fullprop>
3593
+ # node.prop.set.<form>.<prop>
3594
+ # node.prop.set.<fullprop>
3595
+ #
3596
+ # We have to consider cases of no-match ( None ) results when interpreting
3597
+ # the rules matches, in order to grant the permission. Since we decide
3598
+ # the precedence order is the newer-style, we do not allow a mixed match
3599
+ # where is an deny on the new style and an allow on the old style.
3600
+ #
3601
+ # Implementing this can be done by short-circuiting the a0 ( representing
3602
+ # the new style permission matching ) where possible, and allowing the
3603
+ # one undefined a0 + a1 case. All other results can then be left to raise
3604
+ # a s_exc.AuthDeny error.
3605
+ #
3606
+ # a0 a1 action
3607
+ # None None Deny
3608
+ # None True Allow
3609
+ # None False Deny
3610
+ # True None Allow
3611
+ # True True Allow
3612
+ # True False Allow with precedence
3613
+ # False None Deny
3614
+ # False True Deny with precedence
3615
+ # False False Deny
3616
+
3617
+ # These tests assume that only positive permissions are present to grant node.add / node.prop.set
3618
+ # and then denies on node.prop.set.
3619
+
3620
+ async with self.getTestCore() as core: # type: s_cortex.Cortex
3621
+ q = '[media:news=* :published=2020]'
3622
+
3623
+ # test 0
3624
+ # None None Deny
3625
+ name = s_common.guid()
3626
+ unfo = await core.addUser(name)
3627
+ opts = {'vars': {'name': name}}
3628
+ await core.callStorm('auth.user.addrule $name node.add', opts=opts)
3629
+ aslow = {'user': unfo.get('iden')}
3630
+
3631
+ msgs = await core.stormlist(q, opts=aslow)
3632
+ self.stormIsInErr('must have permission node.prop.set.media:news.published', msgs)
3633
+
3634
+ # test 1
3635
+ # None True Allow
3636
+ name = s_common.guid()
3637
+ unfo = await core.addUser(name)
3638
+ opts = {'vars': {'name': name}}
3639
+ await core.callStorm('auth.user.addrule $name node.prop.set.media:news:published', opts=opts)
3640
+ await core.callStorm('auth.user.addrule $name node.add', opts=opts)
3641
+ aslow = {'user': unfo.get('iden')}
3642
+ msgs = await core.stormlist(q, opts=aslow)
3643
+ self.stormHasNoErr(msgs)
3644
+
3645
+ # test 2
3646
+ # None False Deny
3647
+ name = s_common.guid()
3648
+ unfo = await core.addUser(name)
3649
+ opts = {'vars': {'name': name}}
3650
+ await core.callStorm('auth.user.addrule $name "!node.prop.set.media:news:published"', opts=opts)
3651
+ await core.callStorm('auth.user.addrule $name node.add', opts=opts)
3652
+ aslow = {'user': unfo.get('iden')}
3653
+ msgs = await core.stormlist(q, opts=aslow)
3654
+ self.stormIsInErr('must have permission node.prop.set.media:news.published', msgs)
3655
+
3656
+ # test 3
3657
+ # True None Allow
3658
+ name = s_common.guid()
3659
+ unfo = await core.addUser(name)
3660
+ opts = {'vars': {'name': name}}
3661
+ await core.callStorm('auth.user.addrule $name "node.prop.set.media:news.published"', opts=opts)
3662
+ await core.callStorm('auth.user.addrule $name node.add', opts=opts)
3663
+ aslow = {'user': unfo.get('iden')}
3664
+ msgs = await core.stormlist(q, opts=aslow)
3665
+ self.stormHasNoWarnErr(msgs)
3666
+
3667
+ # test 4
3668
+ # True True Allow
3669
+ name = s_common.guid()
3670
+ unfo = await core.addUser(name)
3671
+ opts = {'vars': {'name': name}}
3672
+ await core.callStorm('auth.user.addrule $name "node.prop.set.media:news.published"', opts=opts)
3673
+ await core.callStorm('auth.user.addrule $name "node.prop.set.media:news:published"', opts=opts)
3674
+ await core.callStorm('auth.user.addrule $name node.add', opts=opts)
3675
+ aslow = {'user': unfo.get('iden')}
3676
+ msgs = await core.stormlist(q, opts=aslow)
3677
+ self.stormHasNoWarnErr(msgs)
3678
+
3679
+ # test 5
3680
+ # True False Allow with precedence
3681
+ name = s_common.guid()
3682
+ unfo = await core.addUser(name)
3683
+ opts = {'vars': {'name': name}}
3684
+ await core.callStorm('auth.user.addrule $name "node.prop.set.media:news.published"', opts=opts)
3685
+ await core.callStorm('auth.user.addrule $name "!node.prop.set.media:news:published"', opts=opts)
3686
+ await core.callStorm('auth.user.addrule $name node.add', opts=opts)
3687
+ aslow = {'user': unfo.get('iden')}
3688
+ msgs = await core.stormlist(q, opts=aslow)
3689
+ self.stormHasNoWarnErr(msgs)
3690
+
3691
+ # test 6
3692
+ # False None Deny
3693
+ name = s_common.guid()
3694
+ unfo = await core.addUser(name)
3695
+ opts = {'vars': {'name': name}}
3696
+ await core.callStorm('auth.user.addrule $name "!node.prop.set.media:news.published"', opts=opts)
3697
+ await core.callStorm('auth.user.addrule $name node.add', opts=opts)
3698
+ aslow = {'user': unfo.get('iden')}
3699
+ msgs = await core.stormlist(q, opts=aslow)
3700
+ self.stormIsInErr('must have permission node.prop.set.media:news.published', msgs)
3701
+
3702
+ # test 7
3703
+ # False True Deny with precedence
3704
+ name = s_common.guid()
3705
+ unfo = await core.addUser(name)
3706
+ opts = {'vars': {'name': name}}
3707
+ await core.callStorm('auth.user.addrule $name "!node.prop.set.media:news.published"', opts=opts)
3708
+ await core.callStorm('auth.user.addrule $name "node.prop.set.media:news:published"', opts=opts)
3709
+ await core.callStorm('auth.user.addrule $name node.add', opts=opts)
3710
+ aslow = {'user': unfo.get('iden')}
3711
+ msgs = await core.stormlist(q, opts=aslow)
3712
+ self.stormIsInErr('must have permission node.prop.set.media:news.published', msgs)
3713
+
3714
+ # test 8
3715
+ # False False Deny
3716
+ name = s_common.guid()
3717
+ unfo = await core.addUser(name)
3718
+ opts = {'vars': {'name': name}}
3719
+ await core.callStorm('auth.user.addrule $name "!node.prop.set.media:news.published"', opts=opts)
3720
+ await core.callStorm('auth.user.addrule $name "!node.prop.set.media:news:published"', opts=opts)
3721
+ await core.callStorm('auth.user.addrule $name node.add', opts=opts)
3722
+ aslow = {'user': unfo.get('iden')}
3723
+ msgs = await core.stormlist(q, opts=aslow)
3724
+ self.stormIsInErr('must have permission node.prop.set.media:news.published', msgs)
3725
+
3726
+ # Exhaustive test for node.prop.del behaviors
3727
+ async with self.getTestCore() as core: # type: s_cortex.Cortex
3728
+ q = 'inet:asn=$valu [ -:name ]'
3729
+
3730
+ # test 0
3731
+ # None None Deny
3732
+ name = s_common.guid()
3733
+ unfo = await core.addUser(name)
3734
+ varz = {'valu': 0}
3735
+ aslow = {'user': unfo.get('iden'), 'vars': varz}
3736
+ self.len(1, await core.nodes('[inet:asn=$valu :name=name]', opts={'vars': varz}))
3737
+ msgs = await core.stormlist(q, opts=aslow)
3738
+ self.stormIsInErr('must have permission node.prop.del.inet:asn.name', msgs)
3739
+
3740
+ # test 1
3741
+ # None True Allow
3742
+ name = s_common.guid()
3743
+ unfo = await core.addUser(name)
3744
+ opts = {'vars': {'name': name}}
3745
+ await core.callStorm('auth.user.addrule $name node.prop.del.inet:asn:name', opts=opts)
3746
+
3747
+ varz = {'valu': 1}
3748
+ aslow = {'user': unfo.get('iden'), 'vars': varz}
3749
+ self.len(1, await core.nodes('[inet:asn=$valu :name=name]', opts={'vars': varz}))
3750
+ msgs = await core.stormlist(q, opts=aslow)
3751
+ self.stormHasNoErr(msgs)
3752
+
3753
+ # test 2
3754
+ # None False Deny
3755
+ name = s_common.guid()
3756
+ unfo = await core.addUser(name)
3757
+ opts = {'vars': {'name': name}}
3758
+ await core.callStorm('auth.user.addrule $name "!node.prop.del:inet:asn:name"', opts=opts)
3759
+ varz = {'valu': 2}
3760
+ aslow = {'user': unfo.get('iden'), 'vars': varz}
3761
+ self.len(1, await core.nodes('[inet:asn=$valu :name=name]', opts={'vars': varz}))
3762
+ msgs = await core.stormlist(q, opts=aslow)
3763
+ self.stormIsInErr('must have permission node.prop.del.inet:asn.name', msgs)
3764
+
3765
+ # test 3
3766
+ # True None Allow
3767
+ name = s_common.guid()
3768
+ unfo = await core.addUser(name)
3769
+ opts = {'vars': {'name': name}}
3770
+ await core.callStorm('auth.user.addrule $name "node.prop.del.inet:asn.name"', opts=opts)
3771
+
3772
+ varz = {'valu': 3}
3773
+ aslow = {'user': unfo.get('iden'), 'vars': varz}
3774
+ self.len(1, await core.nodes('[inet:asn=$valu :name=name]', opts={'vars': varz}))
3775
+ msgs = await core.stormlist(q, opts=aslow)
3776
+ self.stormHasNoWarnErr(msgs)
3777
+
3778
+ # test 4
3779
+ # True True Allow
3780
+ name = s_common.guid()
3781
+ unfo = await core.addUser(name)
3782
+ opts = {'vars': {'name': name}}
3783
+ await core.callStorm('auth.user.addrule $name "node.prop.del.inet:asn.name"', opts=opts)
3784
+ await core.callStorm('auth.user.addrule $name "node.prop.del.inet:asn:name"', opts=opts)
3785
+ varz = {'valu': 4}
3786
+ aslow = {'user': unfo.get('iden'), 'vars': varz}
3787
+ self.len(1, await core.nodes('[inet:asn=$valu :name=name]', opts={'vars': varz}))
3788
+ msgs = await core.stormlist(q, opts=aslow)
3789
+ self.stormHasNoWarnErr(msgs)
3790
+
3791
+ # test 5
3792
+ # True False Allow with precedence
3793
+ name = s_common.guid()
3794
+ unfo = await core.addUser(name)
3795
+ opts = {'vars': {'name': name}}
3796
+ await core.callStorm('auth.user.addrule $name "node.prop.del.inet:asn.name"', opts=opts)
3797
+ await core.callStorm('auth.user.addrule $name "!node.prop.del.inet:asn:name"', opts=opts)
3798
+ varz = {'valu': 5}
3799
+ aslow = {'user': unfo.get('iden'), 'vars': varz}
3800
+ self.len(1, await core.nodes('[inet:asn=$valu :name=name]', opts={'vars': varz}))
3801
+ msgs = await core.stormlist(q, opts=aslow)
3802
+ self.stormHasNoWarnErr(msgs)
3803
+
3804
+ # test 6
3805
+ # False None Deny
3806
+ name = s_common.guid()
3807
+ unfo = await core.addUser(name)
3808
+ opts = {'vars': {'name': name}}
3809
+ await core.callStorm('auth.user.addrule $name "!node.prop.del.inet:asn.name"', opts=opts)
3810
+ varz = {'valu': 6}
3811
+ aslow = {'user': unfo.get('iden'), 'vars': varz}
3812
+ self.len(1, await core.nodes('[inet:asn=$valu :name=name]', opts={'vars': varz}))
3813
+ msgs = await core.stormlist(q, opts=aslow)
3814
+ self.stormIsInErr('must have permission node.prop.del.inet:asn.name', msgs)
3815
+
3816
+ # test 7
3817
+ # False True Deny with precedence
3818
+ name = s_common.guid()
3819
+ unfo = await core.addUser(name)
3820
+ opts = {'vars': {'name': name}}
3821
+ await core.callStorm('auth.user.addrule $name "!node.prop.del.inet:asn.name"', opts=opts)
3822
+ await core.callStorm('auth.user.addrule $name "node.prop.del.inet:asn:name"', opts=opts)
3823
+ varz = {'valu': 7}
3824
+ aslow = {'user': unfo.get('iden'), 'vars': varz}
3825
+ self.len(1, await core.nodes('[inet:asn=$valu :name=name]', opts={'vars': varz}))
3826
+ msgs = await core.stormlist(q, opts=aslow)
3827
+ self.stormIsInErr('must have permission node.prop.del.inet:asn.name', msgs)
3828
+
3829
+ # test 8
3830
+ # False False Deny
3831
+ name = s_common.guid()
3832
+ unfo = await core.addUser(name)
3833
+ opts = {'vars': {'name': name}}
3834
+ await core.callStorm('auth.user.addrule $name "!node.prop.del.inet:asn.name"', opts=opts)
3835
+ await core.callStorm('auth.user.addrule $name "!node.prop.del.inet:asn:name"', opts=opts)
3836
+ varz = {'valu': 8}
3837
+ aslow = {'user': unfo.get('iden'), 'vars': varz}
3838
+ self.len(1, await core.nodes('[inet:asn=$valu :name=name]', opts={'vars': varz}))
3839
+ msgs = await core.stormlist(q, opts=aslow)
3840
+ self.stormIsInErr('must have permission node.prop.del.inet:asn.name', msgs)
3841
+
3842
+ # Negative permission tests
3843
+ # These tests confirm the behavior when a deny rule is used to deny the permission
3844
+ # but may still have an underlying allow rule present.
3845
+
3846
+ async with self.getTestCore() as core: # type: s_cortex.Cortex
3847
+ unfo = await core.addUser('lowuser')
3848
+
3849
+ await core.callStorm('auth.user.addrule lowuser "!node.prop.set.media:news.published"')
3850
+ await core.callStorm('auth.user.addrule lowuser "!node.prop.set.media:news:published"')
3851
+ await core.callStorm('auth.user.addrule lowuser node')
3852
+ aslow = {'user': unfo.get('iden')}
3853
+ q = '[media:news=(m0,) .seen=2020 :published=2020]'
3854
+ msgs = await core.stormlist(q, opts=aslow)
3855
+ self.stormIsInErr('must have permission node.prop.set.media:news.published', msgs)
3856
+
3857
+ # New style permission being deny, blanket node allowed
3858
+ async with self.getTestCore() as core: # type: s_cortex.Cortex
3859
+ unfo = await core.addUser('lowuser')
3860
+
3861
+ await core.callStorm('auth.user.addrule lowuser "!node.prop.set.media:news.published"')
3862
+ await core.callStorm('auth.user.addrule lowuser node')
3863
+ aslow = {'user': unfo.get('iden')}
3864
+ q = '[media:news=(m0,) .seen=2021 :published=2021]'
3865
+ msgs = await core.stormlist(q, opts=aslow)
3866
+ self.stormIsInErr('must have permission node.prop.set.media:news.published', msgs)
3867
+
3868
+ # Old style permission being deny, blanket node allowed
3869
+ async with self.getTestCore() as core: # type: s_cortex.Cortex
3870
+ unfo = await core.addUser('lowuser')
3871
+
3872
+ await core.callStorm('auth.user.addrule lowuser "!node.prop.set.media:news:published"')
3873
+ await core.callStorm('auth.user.addrule lowuser node')
3874
+ aslow = {'user': unfo.get('iden')}
3875
+ q = '[media:news=(m0,) .seen=2022 :published=2022]'
3876
+ msgs = await core.stormlist(q, opts=aslow)
3877
+ self.stormIsInErr('must have permission node.prop.set.media:news.published', msgs)
3878
+
3879
+ # Now with del - new style perm
3880
+ async with self.getTestCore() as core: # type: s_cortex.Cortex
3881
+ unfo = await core.addUser('lowuser')
3882
+ await core.callStorm('auth.user.addrule lowuser "!node.prop.del.media:news.published"')
3883
+ await core.callStorm('auth.user.addrule lowuser "node"')
3884
+ self.len(1, await core.nodes('[media:news=(m0,) :published=2022]'))
3885
+ aslow = {'user': unfo.get('iden')}
3886
+ q = 'media:news=(m0,) [-:published]'
3887
+ msgs = await core.stormlist(q, opts=aslow)
3888
+ self.stormIsInErr('must have permission node.prop.del.media:news.published', msgs)
3889
+
3890
+ # Now with del - old style perm
3891
+ async with self.getTestCore() as core: # type: s_cortex.Cortex
3892
+ unfo = await core.addUser('lowuser')
3893
+ await core.callStorm('auth.user.addrule lowuser "!node.prop.del.media:news:published"')
3894
+ await core.callStorm('auth.user.addrule lowuser "node"')
3895
+ self.len(1, await core.nodes('[media:news=(m0,) :published=2022]'))
3896
+ aslow = {'user': unfo.get('iden')}
3897
+ q = 'media:news=(m0,) [-:published]'
3898
+ msgs = await core.stormlist(q, opts=aslow)
3899
+ self.stormIsInErr('must have permission node.prop.del.media:news.published', msgs)
3900
+
3901
+ # This is a legal mix which has a logical equivalence to test case #7
3902
+ async with self.getTestCore() as core: # type: s_cortex.Cortex
3903
+ unfo = await core.addUser('lowuser')
3904
+ await core.callStorm('auth.user.addrule lowuser "!node.prop.set.media:news:published"')
3905
+ await core.callStorm('auth.user.addrule lowuser node.prop.set')
3906
+ await core.callStorm('auth.user.addrule lowuser node.add')
3907
+ aslow = {'user': unfo.get('iden')}
3908
+ q = '[media:news=(m0,) .seen=2022 :published=2022]'
3909
+ msgs = await core.stormlist(q, opts=aslow)
3910
+ self.stormIsInErr('must have permission node.prop.set.media:news.published', msgs)
3911
+
3912
+ # "Don't do this in production" example. Since the r1 DENY permission is not more precise
3913
+ # than the R0 allow permission, we allow the action.
3914
+ async with self.getTestCore() as core: # type: s_cortex.Cortex
3915
+ unfo = await core.addUser('lowuser')
3916
+ await core.callStorm('auth.user.addrule lowuser "node.prop.set.media:news"')
3917
+ await core.callStorm('auth.user.addrule lowuser "!node.prop.set.media:news:published"')
3918
+ await core.callStorm('auth.user.addrule lowuser node')
3919
+ aslow = {'user': unfo.get('iden')}
3920
+ q = '[media:news=(m0,) .seen=2022 :published=2022]'
3921
+ msgs = await core.stormlist(q, opts=aslow)
3922
+ self.stormHasNoWarnErr(msgs)
3923
+
3924
+ # A valid construction - the user is granted one a new style prop set perm but denied others.
3925
+ async with self.getTestCore() as core: # type: s_cortex.Cortex
3926
+ unfo = await core.addUser('lowuser')
3927
+ await core.callStorm('auth.user.addrule lowuser "node.prop.set.media:news.published"')
3928
+ await core.callStorm('auth.user.addrule lowuser "!node.prop.set"')
3929
+ await core.callStorm('auth.user.addrule lowuser node.add')
3930
+ aslow = {'user': unfo.get('iden')}
3931
+ q = '[media:news=(m0,) :published=2022]'
3932
+ msgs = await core.stormlist(q, opts=aslow)
3933
+ self.stormHasNoWarnErr(msgs)
3934
+
3935
+ # A valid construction - the user is granted one a old style prop set perm but denied others.
3936
+ # This is a deny with precedence.
3937
+ async with self.getTestCore() as core: # type: s_cortex.Cortex
3938
+ unfo = await core.addUser('lowuser')
3939
+ await core.callStorm('auth.user.addrule lowuser "node.prop.set.media:news:published"')
3940
+ await core.callStorm('auth.user.addrule lowuser "!node.prop.set"')
3941
+ await core.callStorm('auth.user.addrule lowuser node.add')
3942
+ aslow = {'user': unfo.get('iden')}
3943
+ q = '[media:news=(m0,) :published=2022]'
3944
+ msgs = await core.stormlist(q, opts=aslow)
3945
+ self.stormHasNoWarnErr(msgs)
3946
+
3947
+ # Same but with deletion
3948
+ async with self.getTestCore() as core: # type: s_cortex.Cortex
3949
+ unfo = await core.addUser('lowuser')
3950
+ await core.callStorm('auth.user.addrule lowuser "node.prop.del.media:news:published"')
3951
+ await core.callStorm('auth.user.addrule lowuser "!node.prop.del"')
3952
+ self.len(1, await core.nodes('[media:news=(m0,) :published=2022]'))
3953
+ aslow = {'user': unfo.get('iden')}
3954
+ q = 'media:news=(m0,) [-:published]'
3955
+ msgs = await core.stormlist(q, opts=aslow)
3956
+ self.stormHasNoWarnErr(msgs)
3957
+
3958
+ # "Don't do this in production" example. Since the r1 ALLOW permission is not more precise
3959
+ # than the R0 allow permission, we deny the action.
3960
+ async with self.getTestCore() as core: # type: s_cortex.Cortex
3961
+ unfo = await core.addUser('lowuser')
3962
+ await core.callStorm('auth.user.addrule lowuser "node.prop.set.media:news:published"')
3963
+ await core.callStorm('auth.user.addrule lowuser "!node.prop.set.media:news"')
3964
+ await core.callStorm('auth.user.addrule lowuser node.add')
3965
+ aslow = {'user': unfo.get('iden')}
3966
+ q = '[media:news=(m0,) :published=2022]'
3967
+ msgs = await core.stormlist(q, opts=aslow)
3968
+ self.stormIsInErr('must have permission node.prop.set.media:news.published', msgs)
3969
+
3970
+ # Same but with deletion
3971
+ async with self.getTestCore() as core: # type: s_cortex.Cortex
3972
+ unfo = await core.addUser('lowuser')
3973
+ await core.callStorm('auth.user.addrule lowuser "node.prop.del.media:news:published"')
3974
+ await core.callStorm('auth.user.addrule lowuser "!node.prop.del.media:news"')
3975
+ self.len(1, await core.nodes('[media:news=(m0,) :published=2022]'))
3976
+ aslow = {'user': unfo.get('iden')}
3977
+ q = 'media:news=(m0,) [-:published]'
3978
+ msgs = await core.stormlist(q, opts=aslow)
3979
+ self.stormIsInErr('must have permission node.prop.del.media:news.published', msgs)