synapse 2.197.0__py311-none-any.whl → 2.199.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 (47) hide show
  1. synapse/axon.py +3 -0
  2. synapse/common.py +3 -0
  3. synapse/cortex.py +1 -3
  4. synapse/lib/aha.py +3 -0
  5. synapse/lib/ast.py +277 -165
  6. synapse/lib/auth.py +39 -11
  7. synapse/lib/cell.py +22 -4
  8. synapse/lib/hive.py +2 -1
  9. synapse/lib/hiveauth.py +10 -1
  10. synapse/lib/jsonstor.py +6 -5
  11. synapse/lib/layer.py +6 -5
  12. synapse/lib/node.py +10 -4
  13. synapse/lib/parser.py +46 -21
  14. synapse/lib/schemas.py +13 -0
  15. synapse/lib/snap.py +112 -36
  16. synapse/lib/storm.lark +13 -11
  17. synapse/lib/storm.py +11 -10
  18. synapse/lib/storm_format.py +3 -2
  19. synapse/lib/stormtypes.py +13 -4
  20. synapse/lib/version.py +2 -2
  21. synapse/lib/view.py +2 -1
  22. synapse/models/infotech.py +18 -0
  23. synapse/models/risk.py +9 -0
  24. synapse/models/syn.py +18 -2
  25. synapse/tests/files/stormpkg/badendpoints.yaml +7 -0
  26. synapse/tests/files/stormpkg/testpkg.yaml +8 -0
  27. synapse/tests/test_cortex.py +108 -0
  28. synapse/tests/test_datamodel.py +27 -5
  29. synapse/tests/test_lib_aha.py +22 -12
  30. synapse/tests/test_lib_ast.py +57 -0
  31. synapse/tests/test_lib_auth.py +143 -2
  32. synapse/tests/test_lib_grammar.py +54 -2
  33. synapse/tests/test_lib_lmdbslab.py +24 -0
  34. synapse/tests/test_lib_storm.py +24 -0
  35. synapse/tests/test_lib_stormlib_macro.py +3 -3
  36. synapse/tests/test_lib_stormtypes.py +14 -2
  37. synapse/tests/test_model_infotech.py +13 -0
  38. synapse/tests/test_model_risk.py +6 -0
  39. synapse/tests/test_model_syn.py +58 -0
  40. synapse/tests/test_tools_genpkg.py +10 -0
  41. synapse/tests/utils.py +17 -0
  42. synapse/tools/hive/load.py +1 -0
  43. {synapse-2.197.0.dist-info → synapse-2.199.0.dist-info}/METADATA +1 -1
  44. {synapse-2.197.0.dist-info → synapse-2.199.0.dist-info}/RECORD +47 -46
  45. {synapse-2.197.0.dist-info → synapse-2.199.0.dist-info}/LICENSE +0 -0
  46. {synapse-2.197.0.dist-info → synapse-2.199.0.dist-info}/WHEEL +0 -0
  47. {synapse-2.197.0.dist-info → synapse-2.199.0.dist-info}/top_level.txt +0 -0
@@ -1001,6 +1001,114 @@ class CortexTest(s_t_utils.SynTest):
1001
1001
  self.eq(cm.exception.get('mesg'),
1002
1002
  'walk operation expected a string or list. got: 0.')
1003
1003
 
1004
+ await core.nodes('[media:news=*]')
1005
+
1006
+ nodes = await core.nodes('$n = {[it:dev:str=foo]} media:news [ +(refs)> $n ]')
1007
+ self.len(1, nodes)
1008
+ self.eq(nodes[0].ndef[0], 'media:news')
1009
+
1010
+ nodes = await core.nodes('media:news -(refs)> it:dev:str')
1011
+ self.len(1, nodes)
1012
+
1013
+ q = '''
1014
+ function foo() {
1015
+ for $x in $lib.range(5) {
1016
+ [ it:dev:int=$x ]
1017
+ emit $node
1018
+ }
1019
+ }
1020
+ media:news [ +(refs)> $foo() ]
1021
+ '''
1022
+ nodes = await core.nodes(q)
1023
+ self.len(1, nodes)
1024
+ self.eq(nodes[0].ndef[0], 'media:news')
1025
+
1026
+ nodes = await core.nodes('media:news -(refs)> it:dev:int')
1027
+ self.len(5, nodes)
1028
+
1029
+ nodes = await core.nodes('$n = {[it:dev:str=foo]} media:news [ -(refs)> $n ]')
1030
+ self.len(1, nodes)
1031
+ self.eq(nodes[0].ndef[0], 'media:news')
1032
+
1033
+ nodes = await core.nodes('media:news -(refs)> it:dev:str')
1034
+ self.len(0, nodes)
1035
+
1036
+ q = '''
1037
+ function foo() {
1038
+ for $x in $lib.range(5) {
1039
+ [ it:dev:int=$x ]
1040
+ emit $node
1041
+ }
1042
+ }
1043
+ media:news [ -(refs)> $foo() ]
1044
+ '''
1045
+ nodes = await core.nodes(q)
1046
+ self.len(1, nodes)
1047
+ self.eq(nodes[0].ndef[0], 'media:news')
1048
+
1049
+ nodes = await core.nodes('media:news -(refs)> it:dev:int')
1050
+ self.len(0, nodes)
1051
+
1052
+ nodes = await core.nodes('$n = {[it:dev:str=foo]} media:news [ <(refs)+ $n ]')
1053
+ self.len(1, nodes)
1054
+ self.eq(nodes[0].ndef[0], 'media:news')
1055
+
1056
+ nodes = await core.nodes('media:news <(refs)- it:dev:str')
1057
+ self.len(1, nodes)
1058
+
1059
+ q = '''
1060
+ function foo() {
1061
+ for $x in $lib.range(5) {
1062
+ [ it:dev:int=$x ]
1063
+ emit $node
1064
+ }
1065
+ }
1066
+ media:news [ <(refs)+ $foo() ]
1067
+ '''
1068
+ nodes = await core.nodes(q)
1069
+ self.len(1, nodes)
1070
+ self.eq(nodes[0].ndef[0], 'media:news')
1071
+
1072
+ nodes = await core.nodes('media:news <(refs)- it:dev:int')
1073
+ self.len(5, nodes)
1074
+
1075
+ nodes = await core.nodes('$n = {[it:dev:str=foo]} media:news [ <(refs)- $n ]')
1076
+ self.len(1, nodes)
1077
+ self.eq(nodes[0].ndef[0], 'media:news')
1078
+
1079
+ nodes = await core.nodes('media:news <(refs)- it:dev:str')
1080
+ self.len(0, nodes)
1081
+
1082
+ q = '''
1083
+ function foo() {
1084
+ for $x in $lib.range(5) {
1085
+ [ it:dev:int=$x ]
1086
+ emit $node
1087
+ }
1088
+ }
1089
+ media:news [ <(refs)- $foo() ]
1090
+ '''
1091
+ nodes = await core.nodes(q)
1092
+ self.len(1, nodes)
1093
+ self.eq(nodes[0].ndef[0], 'media:news')
1094
+
1095
+ nodes = await core.nodes('media:news <(refs)- it:dev:int')
1096
+ self.len(0, nodes)
1097
+
1098
+ await core.nodes('[media:news=*]')
1099
+
1100
+ nodes = await core.nodes('$n = {[it:dev:str=foo]} $edge=refs media:news [ +($edge)> $n ]')
1101
+ self.len(2, nodes)
1102
+
1103
+ nodes = await core.nodes('media:news -(refs)> it:dev:str')
1104
+ self.len(2, nodes)
1105
+
1106
+ nodes = await core.nodes('$n = {[it:dev:str=foo]} $edge=refs media:news [ -($edge)> $n ]')
1107
+ self.len(2, nodes)
1108
+
1109
+ nodes = await core.nodes('media:news -(refs)> it:dev:str')
1110
+ self.len(0, nodes)
1111
+
1004
1112
  async def test_cortex_callstorm(self):
1005
1113
 
1006
1114
  async with self.getTestCore(conf={'auth:passwd': 'root'}) as core:
@@ -338,11 +338,33 @@ class DataModelTest(s_t_utils.SynTest):
338
338
 
339
339
  async def test_datamodel_locked_subs(self):
340
340
 
341
- async with self.getTestCore() as core:
342
- await core.setDeprLock('it:prod:softver:semver:major', True)
343
- nodes = await core.nodes('[ it:prod:softver=* :semver=3.1.0 ]')
344
- self.none(nodes[0].get('semver:major'))
345
- self.eq(1, nodes[0].get('semver:minor'))
341
+ conf = {'modules': [('synapse.tests.utils.DeprModule', {})]}
342
+ async with self.getTestCore(conf=conf) as core:
343
+
344
+ msgs = await core.stormlist('[ test:deprsub=bar :range=(1, 5) ]')
345
+ self.stormHasNoWarnErr(msgs)
346
+
347
+ msgs = await core.stormlist('[ test:deprsub2=(foo, (2, 6)) ]')
348
+ self.stormHasNoWarnErr(msgs)
349
+
350
+ nodes = await core.nodes('test:deprsub=bar')
351
+ self.eq(1, nodes[0].get('range:min'))
352
+ self.eq(5, nodes[0].get('range:max'))
353
+
354
+ nodes = await core.nodes('test:deprsub2=(foo, (2, 6))')
355
+ self.eq(2, nodes[0].get('range:min'))
356
+ self.eq(6, nodes[0].get('range:max'))
357
+
358
+ await core.setDeprLock('test:deprsub:range:min', True)
359
+ nodes = await core.nodes('[ test:deprsub=foo :range=(1, 5) ]')
360
+ self.none(nodes[0].get('range:min'))
361
+ self.eq(5, nodes[0].get('range:max'))
362
+
363
+ await core.nodes('test:deprsub2 | delnode')
364
+ await core.setDeprLock('test:deprsub2:range:max', True)
365
+ nodes = await core.nodes('[ test:deprsub2=(foo, (2, 6)) ]')
366
+ self.none(nodes[0].get('range:max'))
367
+ self.eq(2, nodes[0].get('range:min'))
346
368
 
347
369
  def test_datamodel_schema_basetypes(self):
348
370
  # N.B. This test is to keep synapse.lib.schemas.datamodel_basetypes const
@@ -133,13 +133,18 @@ class AhaTest(s_test.SynTest):
133
133
  with self.getTestDir() as dirn:
134
134
  cryo0_dirn = s_common.gendir(dirn, 'cryo0')
135
135
  async with self.getTestAha(dirn=dirn) as aha:
136
+
137
+ replaymult = 1
138
+ if s_common.envbool('SYNDEV_NEXUS_REPLAY'):
139
+ replaymult = 2
140
+
136
141
  purl = await aha.addAhaSvcProv('0.cryo')
137
142
 
138
- wait00 = aha.waiter(1, 'aha:svcadd')
143
+ wait00 = aha.waiter(1 * replaymult, 'aha:svcadd')
139
144
 
140
145
  conf = {'aha:provision': purl}
141
146
  async with self.getTestCryo(dirn=cryo0_dirn, conf=conf) as cryo:
142
- self.isin(len(await wait00.wait(timeout=6)), (1, 2))
147
+ self.len(1 * replaymult, await wait00.wait(timeout=6))
143
148
 
144
149
  svc = await aha.getAhaSvc('0.cryo...')
145
150
  linkiden = svc.get('svcinfo', {}).get('online')
@@ -147,16 +152,16 @@ class AhaTest(s_test.SynTest):
147
152
  # Tear down the Aha cell.
148
153
  await aha.__aexit__(None, None, None)
149
154
 
150
- async with self.getTestAha(dirn=dirn) as aha:
151
- wait01 = aha.waiter(1, 'aha:svcdown')
152
- await wait01.wait(timeout=6)
153
- svc = await aha.getAhaSvc('0.cryo...')
154
- self.notin('online', svc.get('svcinfo'))
155
+ with self.getAsyncLoggerStream('synapse.lib.aha', f'Set [0.cryo.synapse] offline.') as stream:
156
+ async with self.getTestAha(dirn=dirn) as aha:
157
+ self.true(await asyncio.wait_for(stream.wait(), timeout=12))
158
+ svc = await aha.getAhaSvc('0.cryo...')
159
+ self.notin('online', svc.get('svcinfo'))
155
160
 
156
- # Try setting something down a second time
157
- await aha.setAhaSvcDown('0.cryo', linkiden, network='synapse')
158
- svc = await aha.getAhaSvc('0.cryo...')
159
- self.notin('online', svc.get('svcinfo'))
161
+ # Try setting something down a second time
162
+ await aha.setAhaSvcDown('0.cryo', linkiden, network='synapse')
163
+ svc = await aha.getAhaSvc('0.cryo...')
164
+ self.notin('online', svc.get('svcinfo'))
160
165
 
161
166
  async def test_lib_aha_basics(self):
162
167
 
@@ -444,8 +449,13 @@ class AhaTest(s_test.SynTest):
444
449
 
445
450
  async with self.getTestAha() as aha:
446
451
 
452
+ replaymult = 1
453
+ if s_common.envbool('SYNDEV_NEXUS_REPLAY'):
454
+ replaymult = 2
455
+
447
456
  aha.testerr = True
448
457
  wait00 = aha.waiter(1, 'aha:svcadd')
458
+
449
459
  conf = {'aha:provision': await aha.addAhaSvcProv('0.cryo')}
450
460
  async with self.getTestCryo(conf=conf) as cryo:
451
461
 
@@ -454,7 +464,7 @@ class AhaTest(s_test.SynTest):
454
464
  svc = await aha.getAhaSvc('0.cryo...')
455
465
  self.none(svc)
456
466
 
457
- wait01 = aha.waiter(1, 'aha:svcadd')
467
+ wait01 = aha.waiter(1 * replaymult, 'aha:svcadd')
458
468
  aha.testerr = False
459
469
 
460
470
  self.nn(await wait01.wait(timeout=2))
@@ -492,6 +492,63 @@ class AstTest(s_test.SynTest):
492
492
  q = '$foo=newp [test:str=foo :hehe*$foo=heval]'
493
493
  nodes = await core.nodes(q)
494
494
 
495
+ async def test_ast_setmultioper(self):
496
+ async with self.getTestCore() as core:
497
+
498
+ nodes = await core.nodes('[ test:arrayprop="*" :ints=(1,) ]')
499
+ self.eq(nodes[0].get('ints'), (1,))
500
+
501
+ nodes = await core.nodes('test:arrayprop [ :ints++=([3, 4]) ]')
502
+ self.eq(nodes[0].get('ints'), (1, 3, 4))
503
+
504
+ nodes = await core.nodes('test:arrayprop [ :ints++=(null) ]')
505
+ self.eq(nodes[0].get('ints'), (1, 3, 4))
506
+
507
+ nodes = await core.nodes('test:arrayprop [ :ints--=(null) ]')
508
+ self.eq(nodes[0].get('ints'), (1, 3, 4))
509
+
510
+ nodes = await core.nodes('test:arrayprop [ :strs++=(foo, bar, baz) ]')
511
+ self.eq(nodes[0].get('strs'), ('foo', 'bar', 'baz'))
512
+
513
+ with self.raises(s_exc.BadTypeValu):
514
+ await core.nodes('test:arrayprop [ :ints++=(["newp", 5, 6]) ]')
515
+
516
+ nodes = await core.nodes('test:arrayprop [ :ints?++=(["newp", 5, 6]) ]')
517
+ self.eq(nodes[0].get('ints'), (1, 3, 4, 5, 6))
518
+
519
+ with self.raises(s_exc.BadTypeValu):
520
+ await core.nodes('test:arrayprop [ :ints--=(["newp", 5, 6]) ]')
521
+
522
+ nodes = await core.nodes('test:arrayprop [ :ints?--=(["newp", 5, 6, 7]) ]')
523
+ self.eq(nodes[0].get('ints'), (1, 3, 4))
524
+
525
+ nodes = await core.nodes('[ test:str=foo :ndefs++={[ test:str=bar ]} ]')
526
+ self.eq(nodes[0].get('ndefs'), (('test:str', 'bar'),))
527
+
528
+ nodes = await core.nodes('test:str=foo [ :ndefs++={[ test:str=baz test:str=faz ]} ]')
529
+ self.eq(nodes[0].get('ndefs'), (('test:str', 'bar'), ('test:str', 'baz'), ('test:str', 'faz')))
530
+
531
+ nodes = await core.nodes('test:str=foo [ :ndefs--={ test:str=baz test:str=faz } ]')
532
+ self.eq(nodes[0].get('ndefs'), (('test:str', 'bar'),))
533
+
534
+ with self.raises(s_exc.NoSuchProp):
535
+ await core.nodes('test:arrayprop [ :newp++=(["newp", 5, 6]) ]')
536
+
537
+ badq = [
538
+ 'test:str [ :hehe++=([3, 4]) ]',
539
+ 'test:str [ :hehe?++=([3, 4]) ]',
540
+ 'test:str [ :hehe--=([3, 4]) ]',
541
+ 'test:str [ :hehe?--=([3, 4]) ]',
542
+ 'test:arrayprop [ :ints++=(3) ]',
543
+ 'test:arrayprop [ :ints?++=(3) ]',
544
+ 'test:arrayprop [ :ints--=(3) ]',
545
+ 'test:arrayprop [ :ints?--=(3) ]',
546
+ ]
547
+
548
+ for q in badq:
549
+ with self.raises(s_exc.StormRuntimeError):
550
+ await core.nodes(q)
551
+
495
552
  async def test_ast_editparens(self):
496
553
 
497
554
  async with self.getTestCore() as core:
@@ -1,9 +1,15 @@
1
1
  import string
2
2
  import pathlib
3
3
 
4
+ from unittest import mock
5
+
4
6
  import synapse.exc as s_exc
7
+ import synapse.common as s_common
5
8
  import synapse.telepath as s_telepath
6
- import synapse.lib.hiveauth as s_hiveauth
9
+
10
+ import synapse.lib.auth as s_auth
11
+ import synapse.lib.cell as s_cell
12
+ import synapse.lib.lmdbslab as s_lmdbslab
7
13
 
8
14
  import synapse.tests.utils as s_test
9
15
 
@@ -426,6 +432,126 @@ class AuthTest(s_test.SynTest):
426
432
  with self.raises(s_exc.SchemaViolation):
427
433
  await core.auth.allrole.setRules([(True, )])
428
434
 
435
+ async def test_auth_archived_locked_interaction(self):
436
+
437
+ # Check that we can't unlock an archived user
438
+ async with self.getTestCore() as core:
439
+ lowuser = await core.addUser('lowuser')
440
+ useriden = lowuser.get('iden')
441
+
442
+ await core.setUserArchived(useriden, True)
443
+
444
+ udef = await core.getUserDef(useriden)
445
+ self.true(udef.get('archived'))
446
+ self.true(udef.get('locked'))
447
+
448
+ # Unlocking an archived user is invalid
449
+ with self.raises(s_exc.BadArg) as exc:
450
+ await core.setUserLocked(useriden, False)
451
+ self.eq(exc.exception.get('mesg'), 'Cannot unlock archived user.')
452
+ self.eq(exc.exception.get('user'), useriden)
453
+ self.eq(exc.exception.get('username'), 'lowuser')
454
+
455
+ # Check our cell migration that locks archived users
456
+ async with self.getRegrCore('unlocked-archived-users') as core:
457
+ for ii in range(10):
458
+ user = await core.getUserDefByName(f'lowuser{ii:02d}')
459
+ self.nn(user)
460
+ self.true(user.get('archived'))
461
+ self.true(user.get('locked'))
462
+
463
+ # Check behavior of upgraded mirrors and non-upgraded leader
464
+ async with self.getTestAha() as aha:
465
+
466
+ with self.getTestDir() as dirn:
467
+ path00 = s_common.gendir(dirn, 'cell00')
468
+ path01 = s_common.gendir(dirn, 'cell01')
469
+
470
+ with mock.patch('synapse.lib.cell.NEXUS_VERSION', (2, 177)):
471
+ async with self.addSvcToAha(aha, '00.cell', s_cell.Cell, dirn=path00) as cell00:
472
+ lowuser = await cell00.addUser('lowuser')
473
+ useriden = lowuser.get('iden')
474
+ await cell00.setUserArchived(useriden, True)
475
+
476
+ with mock.patch('synapse.lib.cell.NEXUS_VERSION', (2, 198)):
477
+ async with self.addSvcToAha(aha, '01.cell', s_cell.Cell, dirn=path01, provinfo={'mirror': 'cell'}) as cell01:
478
+ await cell01.sync()
479
+ udef = await cell01.getUserDef(useriden)
480
+ self.true(udef.get('locked'))
481
+ self.true(udef.get('archived'))
482
+
483
+ # Simulate a call to cell00.setUserLocked(useriden, False) to bypass
484
+ # the check in cell00.auth.setUserInfo()
485
+ await cell00.auth._push('user:info', useriden, 'locked', False)
486
+ await cell01.sync()
487
+
488
+ udef00 = await cell00.getUserDef(useriden)
489
+ self.true(udef00.get('archived'))
490
+ self.false(udef00.get('locked'))
491
+
492
+ udef01 = await cell01.getUserDef(useriden)
493
+ self.true(udef01.get('archived'))
494
+ self.false(udef01.get('locked'))
495
+
496
+ # Check that we don't blowup/schism if an upgraded mirror is behind a leader with a pending
497
+ # user:info event that unlocks an archived user
498
+ async with self.getTestAha() as aha:
499
+
500
+ with self.getTestDir() as dirn:
501
+ path00 = s_common.gendir(dirn, 'cell00')
502
+ path01 = s_common.gendir(dirn, 'cell01')
503
+
504
+ async with self.addSvcToAha(aha, '00.cell', s_cell.Cell, dirn=path00) as cell00:
505
+ lowuser = await cell00.addUser('lowuser')
506
+ useriden = lowuser.get('iden')
507
+ await cell00.setUserLocked(useriden, True)
508
+
509
+ async with self.addSvcToAha(aha, '01.cell', s_cell.Cell, dirn=path01, provinfo={'mirror': 'cell'}) as cell01:
510
+ await cell01.sync()
511
+ udef = await cell01.getUserDef(useriden)
512
+ self.true(udef.get('locked'))
513
+ self.false(udef.get('archived'))
514
+
515
+ # Set user locked while cell01 is offline so it will get the edit when it comes
516
+ # back
517
+ await cell00.setUserLocked(useriden, False)
518
+ await cell00.sync()
519
+
520
+ # Edit the slabs on both cells directly to archive the user
521
+ lmdb00 = s_common.genpath(path00, 'slabs', 'cell.lmdb')
522
+ lmdb01 = s_common.genpath(path01, 'slabs', 'cell.lmdb')
523
+
524
+ slab00 = await s_lmdbslab.Slab.anit(lmdb00, map_size=s_cell.SLAB_MAP_SIZE)
525
+ slab01 = await s_lmdbslab.Slab.anit(lmdb01, map_size=s_cell.SLAB_MAP_SIZE)
526
+
527
+ # Simulate the cell migration which locks archived users
528
+ for slab in (slab00, slab01):
529
+ authkv = slab.getSafeKeyVal('auth')
530
+ userkv = authkv.getSubKeyVal('user:info:')
531
+
532
+ info = userkv.get(useriden)
533
+ info['archived'] = True
534
+ info['locked'] = True
535
+ userkv.set(useriden, info)
536
+
537
+ await slab00.fini()
538
+ await slab01.fini()
539
+
540
+ # Spin the cells back up and wait for the edit to sync to cell01
541
+ async with self.addSvcToAha(aha, '00.cell', s_cell.Cell, dirn=path00) as cell00:
542
+ udef = await cell00.getUserDef(useriden)
543
+ self.true(udef.get('archived'))
544
+ self.true(udef.get('locked'))
545
+
546
+ async with self.addSvcToAha(aha, '01.cell', s_cell.Cell, dirn=path01, provinfo={'mirror': 'cell'}) as cell01:
547
+ await cell01.sync()
548
+ udef = await cell01.getUserDef(useriden)
549
+ self.true(udef.get('archived'))
550
+ self.true(udef.get('locked'))
551
+
552
+ self.ge(cell00.nexsvers, (2, 198))
553
+ self.ge(cell01.nexsvers, (2, 198))
554
+
429
555
  async def test_auth_password_policy(self):
430
556
  policy = {
431
557
  'complexity': {
@@ -446,6 +572,21 @@ class AuthTest(s_test.SynTest):
446
572
  pass3 = 'ZXN-pyv7ber-kzq2kgh'
447
573
 
448
574
  conf = {'auth:passwd:policy': policy}
575
+ async with self.getTestCore(conf=conf) as core:
576
+
577
+ user = await core.auth.addUser('blackout@vertex.link')
578
+
579
+ self.none(user.info.get('policy:previous'))
580
+ await user.setPasswd(pass1, nexs=False)
581
+ await user.setPasswd(pass2, nexs=False)
582
+ await user.setPasswd(pass3, nexs=False)
583
+ self.len(2, user.info.get('policy:previous'))
584
+
585
+ await user.tryPasswd('newp')
586
+ self.eq(1, user.info.get('policy:attempts'))
587
+ await user.setLocked(False, logged=False)
588
+ self.eq(0, user.info.get('policy:attempts'))
589
+
449
590
  async with self.getTestCore(conf=conf) as core:
450
591
  auth = core.auth
451
592
  self.nn(auth.policy)
@@ -708,7 +849,7 @@ class AuthTest(s_test.SynTest):
708
849
  await core.callStorm('auth.role.addrule ninjas --gate $gate another.rule',
709
850
  opts={'vars': {'gate': fork}})
710
851
 
711
- user = await core.auth.getUserByName('lowuser') # type: s_hiveauth.HiveUser
852
+ user = await core.auth.getUserByName('lowuser') # type: s_auth.User
712
853
  self.false(user.allowed(('hehe',)))
713
854
  self.false(user.allowed(('hehe',), deepdeny=True))
714
855
  self.true(user.allowed(('hehe', 'haha')))
@@ -735,6 +735,17 @@ Queries = [
735
735
  '[test:str=foo :$foo*$bar.baz=heval]',
736
736
  '[test:str=foo :$foo*$bar.("baz")=heval]',
737
737
  '[test:str=foo :$foo*$bar.baz()=heval]',
738
+ '[test:str=foo +(refs)> $n]',
739
+ '[test:str=foo +(refs)> $n.baz()]',
740
+ '[test:str=foo -(refs)> $n]',
741
+ '[test:str=foo <(refs)+ $n]',
742
+ '[test:str=foo <(refs)+ $n.baz()]',
743
+ '[test:str=foo <(refs)- $n]',
744
+ '[test:str=foo :bar++=([1, 2])]',
745
+ '[test:str=foo :$foo++=([1, 2])]',
746
+ '[test:str=foo :bar--=(foo, bar)]',
747
+ '[test:str=foo :bar?++=$baz]',
748
+ '[test:str=foo :bar?--={[it:dev:str=foo]}]',
738
749
  ]
739
750
 
740
751
  # Generated with print_parse_list below
@@ -778,7 +789,7 @@ _ParseResults = [
778
789
  'Query: [TryCatch: [Query: [LiftPropBy: [Const: inet:ipv4, Const: =, Const: asdf]], CatchBlock: [Const: TypeError, Const: err, Query: []]]]',
779
790
  'Query: [TryCatch: [Query: [LiftPropBy: [Const: inet:ipv4, Const: =, Const: asdf]], CatchBlock: [Const: FooBar, Const: err, Query: []], CatchBlock: [Const: *, Const: err, Query: []]]]',
780
791
  'Query: [LiftByArray: [Const: test:array, Const: =, Const: 1.2.3.4]]',
781
- 'Query: [CmdOper: [Const: macro.set, List: [Const: hehe, EmbedQuery: inet:ipv4]]]',
792
+ 'Query: [CmdOper: [Const: macro.set, List: [Const: hehe, EmbedQuery: inet:ipv4 ]]]',
782
793
  'Query: [SetVarOper: [Const: q, EmbedQuery: #foo.bar]]',
783
794
  'Query: [CmdOper: [Const: metrics.edits.byprop, List: [Const: inet:fqdn:domain, Const: --newv, VarDeref: [VarValue: [Const: lib], Const: null]]]]',
784
795
  'Query: [CmdOper: [Const: tee, Const: ()]]',
@@ -1364,7 +1375,7 @@ _ParseResults = [
1364
1375
  'Query: [SetVarOper: [Const: p, Const: names], LiftPropBy: [Const: ps:contact:name, Const: =, Const: foo], EditPropSet: [RelProp: [VarValue: [Const: p]], Const: ?-=, Const: bar]]',
1365
1376
  'Query: [SetVarOper: [Const: pvar, Const: stuff], LiftProp: [Const: test:arrayprop], FiltOper: [Const: +, ArrayCond: [RelProp: [VarValue: [Const: pvar]], Const: =, Const: neato]]]',
1366
1377
  'Query: [SetVarOper: [Const: pvar, Const: ints], LiftProp: [Const: test:arrayprop], FiltOper: [Const: +, ArrayCond: [RelProp: [VarValue: [Const: pvar]], Const: =, VarValue: [Const: othervar]]]]',
1367
- 'Query: [SetVarOper: [Const: foo, DollarExpr: [ExprDict: [Const: foo, EmbedQuery: inet:fqdn]]]]',
1378
+ 'Query: [SetVarOper: [Const: foo, DollarExpr: [ExprDict: [Const: foo, EmbedQuery: inet:fqdn ]]]]',
1368
1379
  'Query: [EditNodeAdd: [FormName: [Const: test:str], Const: =, Const: foo], EditCondPropSet: [RelProp: [Const: hehe], CondSetOper: [Const: unset], Const: heval]]',
1369
1380
  'Query: [EditNodeAdd: [FormName: [Const: test:str], Const: =, Const: foo], EditCondPropSet: [RelProp: [Const: hehe], CondSetOper: [VarValue: [Const: foo]], Const: heval]]',
1370
1381
  'Query: [EditNodeAdd: [FormName: [Const: test:str], Const: =, Const: foo], EditCondPropSet: [RelProp: [VarValue: [Const: foo]], CondSetOper: [Const: unset], Const: heval]]',
@@ -1372,6 +1383,17 @@ _ParseResults = [
1372
1383
  'Query: [EditNodeAdd: [FormName: [Const: test:str], Const: =, Const: foo], EditCondPropSet: [RelProp: [VarValue: [Const: foo]], CondSetOper: [VarDeref: [VarValue: [Const: bar], Const: baz]], Const: heval]]',
1373
1384
  'Query: [EditNodeAdd: [FormName: [Const: test:str], Const: =, Const: foo], EditCondPropSet: [RelProp: [VarValue: [Const: foo]], CondSetOper: [VarDeref: [VarValue: [Const: bar], DollarExpr: [Const: baz]]], Const: heval]]',
1374
1385
  'Query: [EditNodeAdd: [FormName: [Const: test:str], Const: =, Const: foo], EditCondPropSet: [RelProp: [VarValue: [Const: foo]], CondSetOper: [FuncCall: [VarDeref: [VarValue: [Const: bar], Const: baz], CallArgs: [], CallKwargs: []]], Const: heval]]',
1386
+ 'Query: [EditNodeAdd: [FormName: [Const: test:str], Const: =, Const: foo], EditEdgeAdd: [Const: refs, VarValue: [Const: n]]]',
1387
+ 'Query: [EditNodeAdd: [FormName: [Const: test:str], Const: =, Const: foo], EditEdgeAdd: [Const: refs, FuncCall: [VarDeref: [VarValue: [Const: n], Const: baz], CallArgs: [], CallKwargs: []]]]',
1388
+ 'Query: [EditNodeAdd: [FormName: [Const: test:str], Const: =, Const: foo], EditEdgeDel: [Const: refs, VarValue: [Const: n]]]',
1389
+ 'Query: [EditNodeAdd: [FormName: [Const: test:str], Const: =, Const: foo], EditEdgeAdd: [Const: refs, VarValue: [Const: n]]]',
1390
+ 'Query: [EditNodeAdd: [FormName: [Const: test:str], Const: =, Const: foo], EditEdgeAdd: [Const: refs, FuncCall: [VarDeref: [VarValue: [Const: n], Const: baz], CallArgs: [], CallKwargs: []]]]',
1391
+ 'Query: [EditNodeAdd: [FormName: [Const: test:str], Const: =, Const: foo], EditEdgeDel: [Const: refs, VarValue: [Const: n]]]',
1392
+ 'Query: [EditNodeAdd: [FormName: [Const: test:str], Const: =, Const: foo], EditPropSetMulti: [RelProp: [Const: bar], Const: ++=, DollarExpr: [ExprList: [Const: 1, Const: 2]]]]',
1393
+ 'Query: [EditNodeAdd: [FormName: [Const: test:str], Const: =, Const: foo], EditPropSetMulti: [RelProp: [VarValue: [Const: foo]], Const: ++=, DollarExpr: [ExprList: [Const: 1, Const: 2]]]]',
1394
+ 'Query: [EditNodeAdd: [FormName: [Const: test:str], Const: =, Const: foo], EditPropSetMulti: [RelProp: [Const: bar], Const: --=, List: [Const: foo, Const: bar]]]',
1395
+ 'Query: [EditNodeAdd: [FormName: [Const: test:str], Const: =, Const: foo], EditPropSetMulti: [RelProp: [Const: bar], Const: ?++=, VarValue: [Const: baz]]]',
1396
+ 'Query: [EditNodeAdd: [FormName: [Const: test:str], Const: =, Const: foo], EditPropSetMulti: [RelProp: [Const: bar], Const: ?--=, SubQuery: [Query: [EditNodeAdd: [FormName: [Const: it:dev:str], Const: =, Const: foo]]]]]',
1375
1397
  ]
1376
1398
 
1377
1399
  class GrammarTest(s_t_utils.SynTest):
@@ -1663,6 +1685,27 @@ class GrammarTest(s_t_utils.SynTest):
1663
1685
  errinfo = cm.exception.errinfo
1664
1686
  self.eq(1, errinfo.get('mesg').count('#'))
1665
1687
 
1688
+ query = '$q = ${ /* secret comment */ $lib.print([hello) } $lib.macro.set(hehe, $q)'
1689
+ parser = s_parser.Parser(query)
1690
+ with self.raises(s_exc.BadSyntax) as cm:
1691
+ _ = parser.query()
1692
+ info = cm.exception.errinfo.get('highlight')
1693
+ self.eq((40, 41), info['offsets'])
1694
+ self.eq((1, 1), info['lines'])
1695
+ self.eq((41, 42), info['columns'])
1696
+
1697
+ query = """function test(hello) {
1698
+ +'''asdf
1699
+ asdfasdf'''
1700
+ }"""
1701
+ parser = s_parser.Parser(query)
1702
+ with self.raises(s_exc.BadSyntax) as cm:
1703
+ _ = parser.query()
1704
+ info = cm.exception.errinfo.get('highlight')
1705
+ self.eq((44, 83), info['offsets'])
1706
+ self.eq((2, 3), info['lines'])
1707
+ self.eq((22, 31), info['columns'])
1708
+
1666
1709
  async def test_quotes(self):
1667
1710
 
1668
1711
  # Test vectors
@@ -1766,6 +1809,15 @@ class GrammarTest(s_t_utils.SynTest):
1766
1809
  self.false(s_grammar.isPropName('.hehe'))
1767
1810
  self.false(s_grammar.isPropName('testcmd'))
1768
1811
 
1812
+ async def test_embed_offsets(self):
1813
+
1814
+ embq = ' /* secret comment */ $lib.print(hello) /* haha */ $lib.print(goodbye) /*foo */ '
1815
+ query = f'$q = ${{{embq}}} $lib.print($q)'
1816
+ parser = s_parser.Parser(query)
1817
+ q = parser.query()
1818
+ embed = q.kids[0].kids[1]
1819
+ self.eq(embq, embed.getAstText())
1820
+
1769
1821
  def gen_parse_list():
1770
1822
  '''
1771
1823
  Prints out the Asts for a list of queries in order to compare ASTs between versions of parsers
@@ -371,6 +371,30 @@ class LmdbSlabTest(s_t_utils.SynTest):
371
371
  'vm.dirty_ratio',
372
372
  ], msgs[0].get('sysctls', {}).keys())
373
373
 
374
+ async def test_lmdbslab_commit_over_max_xactops(self):
375
+
376
+ # Make sure that we don't confuse the periodic commit with the max replay log commit
377
+ with (self.getTestDir() as dirn,
378
+ patch('synapse.lib.lmdbslab.Slab.WARN_COMMIT_TIME_MS', 1),
379
+ patch('synapse.lib.lmdbslab.Slab.COMMIT_PERIOD', 100)
380
+ ):
381
+ path = os.path.join(dirn, 'test.lmdb')
382
+
383
+ async with await s_lmdbslab.Slab.anit(path, max_replay_log=100, map_size=100_000_000) as slab:
384
+ foo = slab.initdb('foo', dupsort=True)
385
+
386
+ byts = b'\x00' * 256
387
+ for i in range(1000):
388
+ slab.put(b'\xff\xff\xff\xff' + s_common.guid(i).encode('utf8'), byts, db=foo)
389
+ await asyncio.sleep(0)
390
+
391
+ # Let the slab close and then grab its stats
392
+ stats = slab.statinfo()
393
+ commitstats = stats.get('commitstats', ())
394
+ self.gt(len(commitstats), 0)
395
+ commitstats = [x[1] for x in commitstats if x[1] != 0]
396
+ self.eq(commitstats, (100, 100, 100, 100, 100, 100, 100, 100, 100, 100))
397
+
374
398
  async def test_lmdbslab_max_replay(self):
375
399
  with self.getTestDir() as dirn:
376
400
  path = os.path.join(dirn, 'test.lmdb')
@@ -143,6 +143,26 @@ class StormTest(s_t_utils.SynTest):
143
143
  self.eq(props.get('name'), 'org name 77')
144
144
  self.eq(props.get('desc'), 'an org desc')
145
145
 
146
+ nodes = await core.nodes('ou:org=({"name": "the vertex project", "type": "lulz"})')
147
+ self.len(1, nodes)
148
+ orgn = nodes[0].ndef
149
+ self.eq(orgn, nodes11[0].ndef)
150
+
151
+ q = '[ ps:contact=* :org={ ou:org=({"name": "the vertex project", "type": "lulz"}) } ]'
152
+ nodes = await core.nodes(q)
153
+ self.len(1, nodes)
154
+ cont = nodes[0]
155
+ self.eq(cont.get('org'), orgn[1])
156
+
157
+ nodes = await core.nodes('ps:contact:org=({"name": "the vertex project", "type": "lulz"})')
158
+ self.len(1, nodes)
159
+ self.eq(nodes[0].ndef, cont.ndef)
160
+
161
+ self.len(0, await core.nodes('ps:contact:org=({"name": "vertex", "type": "newp"})'))
162
+
163
+ with self.raises(s_exc.BadTypeValu):
164
+ await core.nodes('inet:flow:from=({"name": "vertex", "type": "newp"})')
165
+
146
166
  async def test_lib_storm_jsonexpr(self):
147
167
  async with self.getTestCore() as core:
148
168
 
@@ -1737,6 +1757,10 @@ class StormTest(s_t_utils.SynTest):
1737
1757
  msgs = await core.stormlist('test:ro | merge', opts=altview)
1738
1758
  self.stormIsInWarn("Cannot merge read only property with conflicting value", msgs)
1739
1759
 
1760
+ await core.nodes('[ test:str=foo +(refs)> { for $i in $lib.range(1001) {[ test:int=$i ]}}]', opts=altview)
1761
+ await core.nodes('test:str=foo -(refs)+> * merge --apply', opts=altview)
1762
+ self.len(1001, await core.nodes('test:str=foo -(refs)> *'))
1763
+
1740
1764
  async def test_storm_merge_stricterr(self):
1741
1765
 
1742
1766
  conf = {'modules': [('synapse.tests.utils.DeprModule', {})]}
@@ -89,7 +89,7 @@ class MacroTest(s_test.SynTest):
89
89
  name = 'v' * 491
90
90
  q = '$lib.macro.set($name, ${ help }) return ( $lib.macro.get($name) )'
91
91
  mdef = await core.callStorm(q, opts={'vars': {'name': name}})
92
- self.eq(mdef.get('storm'), 'help')
92
+ self.eq(mdef.get('storm'), ' help ')
93
93
 
94
94
  badname = 'v' * 492
95
95
  with self.raises(s_exc.BadArg):
@@ -381,7 +381,7 @@ class MacroTest(s_test.SynTest):
381
381
  self.eq('storm:macro:add', addmesg['data']['event'])
382
382
  macro = addmesg['data']['info']['macro']
383
383
  self.eq(macro['name'], 'foobar')
384
- self.eq(macro['storm'], 'file:bytes | [+#neato]')
384
+ self.eq(macro['storm'], ' file:bytes | [+#neato] ')
385
385
  self.ne(visi.iden, macro['user'])
386
386
  self.ne(visi.iden, macro['creator'])
387
387
  self.nn(macro['iden'])
@@ -390,7 +390,7 @@ class MacroTest(s_test.SynTest):
390
390
  self.eq('storm:macro:mod', setmesg['data']['event'])
391
391
  event = setmesg['data']['info']
392
392
  self.nn(event['macro'])
393
- self.eq(event['info']['storm'], 'inet:ipv4 | [+#burrito]')
393
+ self.eq(event['info']['storm'], ' inet:ipv4 | [+#burrito] ')
394
394
  self.nn(event['info']['updated'])
395
395
 
396
396
  modmesg = await sock.receive_json()