synapse 2.177.0__py311-none-any.whl → 2.179.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 (73) hide show
  1. synapse/cortex.py +170 -31
  2. synapse/datamodel.py +47 -1
  3. synapse/exc.py +1 -0
  4. synapse/lib/aha.py +362 -88
  5. synapse/lib/ast.py +26 -22
  6. synapse/lib/base.py +39 -12
  7. synapse/lib/cell.py +315 -119
  8. synapse/lib/config.py +15 -11
  9. synapse/lib/coro.py +27 -0
  10. synapse/lib/drive.py +551 -0
  11. synapse/lib/layer.py +0 -5
  12. synapse/lib/link.py +1 -1
  13. synapse/lib/lmdbslab.py +3 -3
  14. synapse/lib/nexus.py +24 -12
  15. synapse/lib/schemas.py +39 -0
  16. synapse/lib/snap.py +17 -7
  17. synapse/lib/storm.py +3 -1
  18. synapse/lib/stormhttp.py +1 -0
  19. synapse/lib/stormlib/imap.py +6 -2
  20. synapse/lib/stormlib/modelext.py +29 -3
  21. synapse/lib/stormlib/smtp.py +12 -2
  22. synapse/lib/stormlib/stix.py +40 -17
  23. synapse/lib/stormlib/vault.py +2 -2
  24. synapse/lib/stormtypes.py +1 -1
  25. synapse/lib/types.py +9 -0
  26. synapse/lib/version.py +2 -2
  27. synapse/lookup/pe.py +303 -38
  28. synapse/models/dns.py +24 -1
  29. synapse/models/geospace.py +4 -1
  30. synapse/models/infotech.py +26 -1
  31. synapse/telepath.py +32 -17
  32. synapse/tests/files/aha/certs/cas/synapse.crt +28 -0
  33. synapse/tests/files/aha/certs/cas/synapse.key +51 -0
  34. synapse/tests/files/aha/certs/hosts/00.aha.loop.vertex.link.crt +30 -0
  35. synapse/tests/files/aha/certs/hosts/00.aha.loop.vertex.link.key +51 -0
  36. synapse/tests/files/aha/certs/users/root@synapse.crt +29 -0
  37. synapse/tests/files/aha/certs/users/root@synapse.key +51 -0
  38. synapse/tests/files/rstorm/testsvc.py +1 -1
  39. synapse/tests/test_axon.py +1 -1
  40. synapse/tests/test_cortex.py +67 -60
  41. synapse/tests/test_lib_agenda.py +3 -3
  42. synapse/tests/test_lib_aha.py +353 -490
  43. synapse/tests/test_lib_base.py +20 -0
  44. synapse/tests/test_lib_cell.py +273 -22
  45. synapse/tests/test_lib_config.py +4 -3
  46. synapse/tests/test_lib_coro.py +12 -0
  47. synapse/tests/test_lib_nexus.py +8 -0
  48. synapse/tests/test_lib_stormhttp.py +40 -0
  49. synapse/tests/test_lib_stormlib_aha.py +35 -35
  50. synapse/tests/test_lib_stormlib_cell.py +4 -15
  51. synapse/tests/test_lib_stormlib_imap.py +14 -3
  52. synapse/tests/test_lib_stormlib_modelext.py +55 -3
  53. synapse/tests/test_lib_stormlib_smtp.py +51 -0
  54. synapse/tests/test_lib_stormlib_stix.py +15 -0
  55. synapse/tests/test_lib_stormlib_vault.py +11 -1
  56. synapse/tests/test_lib_stormtypes.py +5 -0
  57. synapse/tests/test_lib_types.py +9 -0
  58. synapse/tests/test_model_dns.py +8 -0
  59. synapse/tests/test_model_geospace.py +3 -1
  60. synapse/tests/test_model_infotech.py +47 -0
  61. synapse/tests/test_model_syn.py +11 -0
  62. synapse/tests/test_tools_aha.py +78 -101
  63. synapse/tests/test_utils_stormcov.py +1 -1
  64. synapse/tests/utils.py +86 -120
  65. synapse/tools/aha/clone.py +50 -0
  66. synapse/tools/aha/enroll.py +2 -1
  67. synapse/tools/backup.py +2 -2
  68. synapse/tools/changelog.py +31 -1
  69. {synapse-2.177.0.dist-info → synapse-2.179.0.dist-info}/METADATA +48 -48
  70. {synapse-2.177.0.dist-info → synapse-2.179.0.dist-info}/RECORD +73 -65
  71. {synapse-2.177.0.dist-info → synapse-2.179.0.dist-info}/WHEEL +1 -1
  72. {synapse-2.177.0.dist-info → synapse-2.179.0.dist-info}/LICENSE +0 -0
  73. {synapse-2.177.0.dist-info → synapse-2.179.0.dist-info}/top_level.txt +0 -0
@@ -60,6 +60,21 @@ class BaseTest(s_t_utils.SynTest):
60
60
  event = await base.fire('woot', x=3, y=5, ret=[])
61
61
  self.eq(event[1]['ret'], 8)
62
62
 
63
+ base00 = await s_base.Base.anit()
64
+ base01 = await s_base.Base.anit()
65
+ data = {}
66
+ async def onfini():
67
+ data['woot'] = True
68
+
69
+ await base00.fini()
70
+ base00.onfini(onfini)
71
+ await asyncio.sleep(0)
72
+ self.true(data.get('woot'))
73
+
74
+ base00.onfini(base01)
75
+ await asyncio.sleep(0)
76
+ self.true(base01.isfini)
77
+
63
78
  async def test_base_anit(self):
64
79
 
65
80
  afoo = await Hehe.anit(20)
@@ -201,6 +216,7 @@ class BaseTest(s_t_utils.SynTest):
201
216
  self.eq(data['count'], 1)
202
217
 
203
218
  async def test_base_waiter(self):
219
+
204
220
  base0 = await s_base.Base.anit()
205
221
 
206
222
  wait0 = base0.waiter(3, 'foo:bar')
@@ -224,6 +240,10 @@ class BaseTest(s_t_utils.SynTest):
224
240
  evts = await wait2.wait(1)
225
241
  self.len(2, evts)
226
242
 
243
+ with self.raises(s_exc.TimeOut):
244
+ async with base0.waiter(1, 'newp', 'nuuh', timeout=0.01):
245
+ pass
246
+
227
247
  async def test_baseref(self):
228
248
 
229
249
  bref = await s_base.BaseRef.anit()
@@ -28,6 +28,7 @@ import synapse.lib.base as s_base
28
28
  import synapse.lib.cell as s_cell
29
29
  import synapse.lib.coro as s_coro
30
30
  import synapse.lib.link as s_link
31
+ import synapse.lib.drive as s_drive
31
32
  import synapse.lib.nexus as s_nexus
32
33
  import synapse.lib.certdir as s_certdir
33
34
  import synapse.lib.msgpack as s_msgpack
@@ -155,8 +156,217 @@ async def altAuthCtor(cell):
155
156
  cell.onfini(auth.fini)
156
157
  return auth
157
158
 
159
+ testDataSchema_v0 = {
160
+ 'type': 'object',
161
+ 'properties': {
162
+ 'type': {'type': 'string'},
163
+ 'size': {'type': 'number'},
164
+ },
165
+ 'required': ['type', 'size'],
166
+ 'additionalProperties': False,
167
+ }
168
+
169
+ testDataSchema_v1 = {
170
+ 'type': 'object',
171
+ 'properties': {
172
+ 'type': {'type': 'string'},
173
+ 'size': {'type': 'number'},
174
+ 'woot': {'type': 'string'},
175
+ },
176
+ 'required': ['type', 'size', 'woot'],
177
+ 'additionalProperties': False,
178
+ }
179
+
158
180
  class CellTest(s_t_utils.SynTest):
159
181
 
182
+ async def test_cell_drive(self):
183
+
184
+ async with self.getTestCell() as cell:
185
+
186
+ with self.raises(s_exc.BadName):
187
+ s_drive.reqValidName('A' * 512)
188
+
189
+ info = {'name': 'users'}
190
+ pathinfo = await cell.addDriveItem(info)
191
+
192
+ info = {'name': 'root'}
193
+ pathinfo = await cell.addDriveItem(info, path='users')
194
+
195
+ with self.raises(s_exc.DupIden):
196
+ await cell.drive.addItemInfo(pathinfo[-1], path='users')
197
+
198
+ rootdir = pathinfo[-1].get('iden')
199
+ self.eq(0, pathinfo[-1].get('kids'))
200
+
201
+ info = {'name': 'win32k.sys', 'type': 'hehe'}
202
+ with self.raises(s_exc.NoSuchType):
203
+ info = await cell.addDriveItem(info, reldir=rootdir)
204
+
205
+ infos = [i async for i in cell.getDriveKids(s_drive.rootdir)]
206
+ self.len(1, infos)
207
+ self.eq(1, infos[0].get('kids'))
208
+ self.eq('users', infos[0].get('name'))
209
+
210
+ # TODO how to handle iden match with additional property mismatch
211
+
212
+ await cell.drive.setTypeSchema('woot', testDataSchema_v0)
213
+
214
+ info = {'name': 'win32k.sys', 'type': 'woot'}
215
+ info = await cell.addDriveItem(info, reldir=rootdir)
216
+
217
+ iden = info[-1].get('iden')
218
+
219
+ tick = s_common.now()
220
+ rootuser = cell.auth.rootuser.iden
221
+
222
+ with self.raises(s_exc.SchemaViolation):
223
+ versinfo = {'version': (1, 0, 0), 'updated': tick, 'updater': rootuser}
224
+ await cell.setDriveData(iden, versinfo, {'newp': 'newp'})
225
+
226
+ versinfo = {'version': (1, 1, 0), 'updated': tick + 10, 'updater': rootuser}
227
+ info, versinfo = await cell.setDriveData(iden, versinfo, {'type': 'haha', 'size': 20})
228
+ self.eq(info.get('version'), (1, 1, 0))
229
+ self.eq(versinfo.get('version'), (1, 1, 0))
230
+
231
+ versinfo = {'version': (1, 0, 0), 'updated': tick, 'updater': rootuser}
232
+ info, versinfo = await cell.setDriveData(iden, versinfo, {'type': 'hehe', 'size': 0})
233
+ self.eq(info.get('version'), (1, 1, 0))
234
+ self.eq(versinfo.get('version'), (1, 0, 0))
235
+
236
+ versinfo10, data10 = await cell.getDriveData(iden, vers=(1, 0, 0))
237
+ self.eq(versinfo10.get('updated'), tick)
238
+ self.eq(versinfo10.get('updater'), rootuser)
239
+ self.eq(versinfo10.get('version'), (1, 0, 0))
240
+
241
+ versinfo11, data11 = await cell.getDriveData(iden, vers=(1, 1, 0))
242
+ self.eq(versinfo11.get('updated'), tick + 10)
243
+ self.eq(versinfo11.get('updater'), rootuser)
244
+ self.eq(versinfo11.get('version'), (1, 1, 0))
245
+
246
+ versions = [vers async for vers in cell.getDriveDataVersions(iden)]
247
+ self.len(2, versions)
248
+ self.eq(versions[0], versinfo11)
249
+ self.eq(versions[1], versinfo10)
250
+
251
+ info = await cell.delDriveData(iden, vers=(0, 0, 0))
252
+
253
+ versions = [vers async for vers in cell.getDriveDataVersions(iden)]
254
+ self.len(2, versions)
255
+ self.eq(versions[0], versinfo11)
256
+ self.eq(versions[1], versinfo10)
257
+
258
+ info = await cell.delDriveData(iden, vers=(1, 1, 0))
259
+ self.eq(info.get('updated'), tick)
260
+ self.eq(info.get('version'), (1, 0, 0))
261
+
262
+ info = await cell.delDriveData(iden, vers=(1, 0, 0))
263
+ self.eq(info.get('size'), 0)
264
+ self.eq(info.get('version'), (0, 0, 0))
265
+ self.none(info.get('updated'))
266
+ self.none(info.get('updater'))
267
+
268
+ # repopulate a couple data versions to test migration and delete
269
+ versinfo = {'version': (1, 0, 0), 'updated': tick, 'updater': rootuser}
270
+ info, versinfo = await cell.setDriveData(iden, versinfo, {'type': 'hehe', 'size': 0})
271
+ versinfo = {'version': (1, 1, 0), 'updated': tick + 10, 'updater': rootuser}
272
+ info, versinfo = await cell.setDriveData(iden, versinfo, {'type': 'haha', 'size': 17})
273
+ self.eq(versinfo, (await cell.getDriveData(iden))[0])
274
+
275
+ # This will be done by the cell in a cell storage version migration...
276
+ async def migrate_v1(info, versinfo, data):
277
+ data['woot'] = 'woot'
278
+ return data
279
+
280
+ await cell.drive.setTypeSchema('woot', testDataSchema_v1, migrate_v1)
281
+
282
+ versinfo, data = await cell.getDriveData(iden, vers=(1, 0, 0))
283
+ self.eq('woot', data.get('woot'))
284
+
285
+ versinfo, data = await cell.getDriveData(iden, vers=(1, 1, 0))
286
+ self.eq('woot', data.get('woot'))
287
+
288
+ self.nn(await cell.getDriveInfo(iden))
289
+ self.len(2, [vers async for vers in cell.getDriveDataVersions(iden)])
290
+
291
+ await cell.delDriveData(iden)
292
+ self.len(1, [vers async for vers in cell.getDriveDataVersions(iden)])
293
+
294
+ await cell.delDriveInfo(iden)
295
+
296
+ self.none(await cell.getDriveInfo(iden))
297
+ self.len(0, [vers async for vers in cell.getDriveDataVersions(iden)])
298
+
299
+ with self.raises(s_exc.NoSuchPath):
300
+ await cell.getDrivePath('users/root/win32k.sys')
301
+
302
+ pathinfo = await cell.addDrivePath('foo/bar/baz')
303
+ self.len(3, pathinfo)
304
+ self.eq('foo', pathinfo[0].get('name'))
305
+ self.eq(1, pathinfo[0].get('kids'))
306
+ self.eq('bar', pathinfo[1].get('name'))
307
+ self.eq(1, pathinfo[1].get('kids'))
308
+ self.eq('baz', pathinfo[2].get('name'))
309
+ self.eq(0, pathinfo[2].get('kids'))
310
+
311
+ self.eq(pathinfo, await cell.addDrivePath('foo/bar/baz'))
312
+
313
+ baziden = pathinfo[2].get('iden')
314
+ self.eq(pathinfo, await cell.drive.getItemPath(baziden))
315
+
316
+ info = await cell.setDriveInfoPerm(baziden, {'users': {rootuser: 3}, 'roles': {}})
317
+ self.eq(3, info['perm']['users'][rootuser])
318
+
319
+ with self.raises(s_exc.NoSuchIden):
320
+ # s_drive.rootdir is all 00s... ;)
321
+ await cell.setDriveInfoPerm(s_drive.rootdir, {'users': {}, 'roles': {}})
322
+
323
+ await cell.addDrivePath('hehe/haha')
324
+ pathinfo = await cell.setDriveInfoPath(baziden, 'hehe/haha/hoho')
325
+
326
+ self.eq('hoho', pathinfo[-1].get('name'))
327
+ self.eq(baziden, pathinfo[-1].get('iden'))
328
+
329
+ self.true(await cell.drive.hasPathInfo('hehe/haha/hoho'))
330
+ self.false(await cell.drive.hasPathInfo('foo/bar/baz'))
331
+
332
+ pathinfo = await cell.getDrivePath('foo/bar')
333
+ self.eq(0, pathinfo[-1].get('kids'))
334
+
335
+ pathinfo = await cell.getDrivePath('hehe/haha')
336
+ self.eq(1, pathinfo[-1].get('kids'))
337
+
338
+ with self.raises(s_exc.DupName):
339
+ iden = pathinfo[-2].get('iden')
340
+ name = pathinfo[-1].get('name')
341
+ cell.drive.reqFreeStep(iden, name)
342
+
343
+ walks = [item async for item in cell.drive.walkPathInfo('hehe')]
344
+ self.len(3, walks)
345
+ # confirm walked paths are yielded depth first...
346
+ self.eq('hoho', walks[0].get('name'))
347
+ self.eq('haha', walks[1].get('name'))
348
+ self.eq('hehe', walks[2].get('name'))
349
+
350
+ iden = walks[2].get('iden')
351
+ walks = [item async for item in cell.drive.walkItemInfo(iden)]
352
+ self.len(3, walks)
353
+ self.eq('hoho', walks[0].get('name'))
354
+ self.eq('haha', walks[1].get('name'))
355
+ self.eq('hehe', walks[2].get('name'))
356
+
357
+ self.none(cell.drive.getTypeSchema('newp'))
358
+
359
+ cell.drive.validators.pop('woot')
360
+ self.nn(cell.drive.getTypeValidator('woot'))
361
+
362
+ # move to root dir
363
+ pathinfo = await cell.setDriveInfoPath(baziden, 'zipzop')
364
+ self.len(1, pathinfo)
365
+ self.eq(s_drive.rootdir, pathinfo[-1].get('parent'))
366
+
367
+ pathinfo = await cell.setDriveInfoPath(baziden, 'hehe/haha/hoho')
368
+ self.len(3, pathinfo)
369
+
160
370
  async def test_cell_auth(self):
161
371
 
162
372
  with self.getTestDir() as dirn:
@@ -635,7 +845,6 @@ class CellTest(s_t_utils.SynTest):
635
845
  async with self.getTestCell(s_cell.Cell, dirn=dir0, conf=conf) as cell00, \
636
846
  cell00.getLocalProxy() as prox00:
637
847
 
638
- self.true(cell00.nexsroot.map_async)
639
848
  self.true(cell00.nexsroot.donexslog)
640
849
 
641
850
  await prox00.addUser('test')
@@ -932,7 +1141,7 @@ class CellTest(s_t_utils.SynTest):
932
1141
  pass
933
1142
  stream.seek(0)
934
1143
  buf = stream.read()
935
- self.isin(f'...cell API (telepath): cell://root@{dirn}:*', buf)
1144
+ self.isin(f'...cell API (telepath): tcp://0.0.0.0:27492', buf)
936
1145
  self.isin('...cell API (https): disabled', buf)
937
1146
 
938
1147
  async def test_cell_initargv_conf(self):
@@ -954,11 +1163,11 @@ class CellTest(s_t_utils.SynTest):
954
1163
  # 1) cmdline
955
1164
  # 2) envars
956
1165
  # 3) cell.yaml
957
- self.true(cell.conf.reqConfValu('nexslog:en'))
958
- self.true(cell.conf.reqConfValu('nexslog:async'))
959
- self.none(cell.conf.reqConfValu('dmon:listen'))
960
- self.none(cell.conf.reqConfValu('https:port'))
961
- self.eq(cell.conf.reqConfValu('aha:name'), 'some:cell')
1166
+ self.true(cell.conf.req('nexslog:en'))
1167
+ self.true(cell.conf.req('nexslog:async'))
1168
+ self.none(cell.conf.req('dmon:listen'))
1169
+ self.none(cell.conf.req('https:port'))
1170
+ self.eq(cell.conf.req('aha:name'), 'some:cell')
962
1171
  root = cell.auth.rootuser
963
1172
  self.true(await root.tryPasswd('secret'))
964
1173
 
@@ -966,7 +1175,7 @@ class CellTest(s_t_utils.SynTest):
966
1175
  with self.getTestDir() as dirn:
967
1176
  s_common.yamlsave({'nexslog:en': False}, dirn, 'cell.mods.yaml')
968
1177
  async with await s_cell.Cell.initFromArgv([dirn]) as cell:
969
- self.false(cell.conf.reqConfValu('nexslog:en'))
1178
+ self.false(cell.conf.req('nexslog:en'))
970
1179
  # We can remove the valu from the overrides file with the pop API
971
1180
  # This is NOT reactive API which causes the whole behavior
972
1181
  # of the cell to suddenly change. This is intended to be used with
@@ -1038,6 +1247,7 @@ class CellTest(s_t_utils.SynTest):
1038
1247
  errinfo = info.get('lastexception')
1039
1248
  laststart1 = info['laststart']
1040
1249
  self.eq(errinfo['err'], 'SynErr')
1250
+ self.eq(errinfo['errinfo']['mesg'], 'backup subprocess start timed out')
1041
1251
 
1042
1252
  # Test runners can take an unusually long time to spawn a process
1043
1253
  with mock.patch.object(s_cell.Cell, 'BACKUP_SPAWN_TIMEOUT', 8.0):
@@ -1050,15 +1260,17 @@ class CellTest(s_t_utils.SynTest):
1050
1260
  self.ne(laststart1, laststart2)
1051
1261
  errinfo = info.get('lastexception')
1052
1262
  self.eq(errinfo['err'], 'SynErr')
1263
+ self.eq(errinfo['errinfo']['mesg'], 'backup subprocess start timed out')
1053
1264
 
1054
- with mock.patch.object(s_cell.Cell, '_backupProc', staticmethod(_exiterProc)):
1055
- await self.asyncraises(s_exc.SpawnExit, proxy.runBackup('_exiterProc'))
1265
+ with mock.patch.object(s_cell.Cell, '_backupProc', staticmethod(_exiterProc)):
1266
+ await self.asyncraises(s_exc.SpawnExit, proxy.runBackup('_exiterProc'))
1056
1267
 
1057
- info = await proxy.getBackupInfo()
1058
- laststart3 = info['laststart']
1059
- self.ne(laststart2, laststart3)
1060
- errinfo = info.get('lastexception')
1061
- self.eq(errinfo['err'], 'SpawnExit')
1268
+ info = await proxy.getBackupInfo()
1269
+ laststart3 = info['laststart']
1270
+ self.ne(laststart2, laststart3)
1271
+ errinfo = info.get('lastexception')
1272
+ self.eq(errinfo['err'], 'SpawnExit')
1273
+ self.eq(errinfo['errinfo']['code'], 1)
1062
1274
 
1063
1275
  # Create rando slabs inside cell dir
1064
1276
  slabpath = s_common.genpath(coredirn, 'randoslab')
@@ -1741,7 +1953,7 @@ class CellTest(s_t_utils.SynTest):
1741
1953
  'has different iden') as stream:
1742
1954
  async with self.getTestCell(s_cell.Cell, dirn=path01, conf=conf01) as cell01:
1743
1955
  await stream.wait(timeout=2)
1744
- self.true(await cell01.waitfini(6))
1956
+ self.true(await cell01.nexsroot.waitfini(6))
1745
1957
 
1746
1958
  async def test_backup_restore_base(self):
1747
1959
 
@@ -1836,15 +2048,42 @@ class CellTest(s_t_utils.SynTest):
1836
2048
  self.true(s_common.isguid(second_doneiden))
1837
2049
  self.ne(doneiden, second_doneiden)
1838
2050
 
2051
+ async def test_cell_mirrorboot_failure(self):
2052
+ async with self.getTestAha() as aha: # type: s_aha.AhaCell
2053
+
2054
+ with self.getTestDir() as dirn:
2055
+ cdr0 = s_common.genpath(dirn, 'cell00')
2056
+ cdr1 = s_common.genpath(dirn, 'cell01')
2057
+
2058
+ async with self.addSvcToAha(aha, '00.cell', s_cell.Cell, dirn=cdr0) as cell00:
2059
+
2060
+ conf = {'mirror': 'aha://cell...'}
2061
+ with self.raises(s_exc.FatalErr) as cm:
2062
+ async with self.getTestCell(conf=conf, dirn=cdr1) as cell01:
2063
+ self.fail('Cell01 should never boot')
2064
+ self.isin('No aha:provision configuration has been provided to allow the service to bootstrap',
2065
+ cm.exception.get('mesg'))
2066
+
2067
+ provurl = await aha.addAhaSvcProv('01.cell', provinfo={'mirror': 'cell'})
2068
+ conf = self.getCellConf({'aha:provision': provurl})
2069
+ async with self.getTestCell(conf=conf, dirn=cdr1) as cell01:
2070
+ await cell01.sync()
2071
+ os.unlink(s_common.genpath(cdr1, 'cell.guid'))
2072
+
2073
+ conf = self.getCellConf({'aha:provision': provurl})
2074
+ with self.raises(s_exc.FatalErr) as cm:
2075
+ async with self.getTestCell(conf=conf, dirn=cdr1) as cell01:
2076
+ self.fail('Cell01 should never boot')
2077
+ self.isin('The aha:provision URL guid matches the service prov.done guid',
2078
+ cm.exception.get('mesg'))
2079
+
1839
2080
  async def test_backup_restore_aha(self):
1840
2081
  # do a mirror provisioning of a Cell
1841
2082
  # promote the mirror to being a leader
1842
2083
  # ensure the mirror has a
1843
2084
  # backup the mirror
1844
2085
  # restore the backup
1845
- async with self.getTestAhaProv(conf={'auth:passwd': 'secret'}) as aha: # type: s_aha.AhaCell
1846
- root = await aha.auth.getUserByName('root')
1847
- self.true(await root.tryPasswd('secret'))
2086
+ async with self.getTestAha() as aha: # type: s_aha.AhaCell
1848
2087
 
1849
2088
  with self.getTestDir() as dirn:
1850
2089
  cdr0 = s_common.genpath(dirn, 'core00')
@@ -1927,9 +2166,7 @@ class CellTest(s_t_utils.SynTest):
1927
2166
  # ensure the mirror has a
1928
2167
  # backup the mirror
1929
2168
  # restore the backup
1930
- async with self.getTestAhaProv(conf={'auth:passwd': 'secret'}) as aha: # type: s_aha.AhaCell
1931
- root = await aha.auth.getUserByName('root')
1932
- self.true(await root.tryPasswd('secret'))
2169
+ async with self.getTestAha() as aha: # type: s_aha.AhaCell
1933
2170
 
1934
2171
  with self.getTestDir() as dirn:
1935
2172
  cdr0 = s_common.genpath(dirn, 'core00')
@@ -2887,3 +3124,17 @@ class CellTest(s_t_utils.SynTest):
2887
3124
 
2888
3125
  async with self.getTestCell(s_cell.Cell, dirn=dirn):
2889
3126
  pass
3127
+
3128
+ async def test_cell_initslab_fini(self):
3129
+ class SlabCell(s_cell.Cell):
3130
+ async def initServiceStorage(self):
3131
+ self.long_lived_slab = await self._initSlabFile(os.path.join(self.dirn, 'slabs', 'long.lmdb'))
3132
+ short_slab = await self._initSlabFile(os.path.join(self.dirn, 'slabs', 'short.lmdb'), ephemeral=True)
3133
+ self.short_slab_path = short_slab.lenv.path()
3134
+ await short_slab.fini()
3135
+
3136
+ async with self.getTestCell(SlabCell) as cell:
3137
+ self.true(os.path.isdir(cell.short_slab_path))
3138
+ self.isin(cell.long_lived_slab.fini, cell._fini_funcs)
3139
+ slabs = [s for s in cell.tofini if isinstance(s, s_lmdbslab.Slab) and s.lenv.path() == cell.short_slab_path]
3140
+ self.len(0, slabs)
@@ -31,7 +31,7 @@ class SchemaCell(s_cell.Cell):
31
31
  await s_cell.Cell.__anit__(self, dirn, conf, readonly, *args, **kwargs)
32
32
  # This captures a design pattern that reduces boilerplate
33
33
  # code used by Cell implementators.
34
- self.conf.reqConfValu('apikey')
34
+ self.conf.req('apikey')
35
35
 
36
36
 
37
37
  class ConfTest(s_test.SynTest):
@@ -162,7 +162,7 @@ class ConfTest(s_test.SynTest):
162
162
  })
163
163
 
164
164
  # We can ensure that certain vars are loaded
165
- self.eq('Funky string time!', conf.reqConfValu('key:string'))
165
+ self.eq('Funky string time!', conf.req('key:string'))
166
166
  # And throw if they are not, or if the requested key isn't even schema valid
167
167
  self.raises(s_exc.NeedConfValu, conf.reqConfValu, 'key:bool:nodefval')
168
168
  self.raises(s_exc.BadArg, conf.reqConfValu, 'key:newp')
@@ -286,7 +286,8 @@ class ConfTest(s_test.SynTest):
286
286
  # Trying to make a cell with a missing key it wants fails
287
287
  async with await SchemaCell.anit(dirn, conf={}) as cell:
288
288
  pass
289
- self.eq(cm.exception.get('key'), 'apikey')
289
+
290
+ self.eq(cm.exception.get('name'), 'apikey')
290
291
 
291
292
  def test_hideconf(self):
292
293
  hide_schema = {
@@ -41,6 +41,18 @@ def nopickle():
41
41
 
42
42
  class CoroTest(s_t_utils.SynTest):
43
43
 
44
+ async def test_coro_chunks(self):
45
+ async def agen():
46
+ for i in range(101):
47
+ yield i
48
+
49
+ chunks = []
50
+ async for chunk in s_coro.chunks(agen()):
51
+ chunks.append(chunk)
52
+
53
+ self.len(1, chunks[1])
54
+ self.len(100, chunks[0])
55
+
44
56
  async def test_coro_event(self):
45
57
 
46
58
  evnt = s_coro.Event()
@@ -111,6 +111,14 @@ class NexusTest(s_t_utils.SynTest):
111
111
  stream.seek(0)
112
112
  self.isin('while replaying log', stream.read())
113
113
 
114
+ async def test_nexus_modroot(self):
115
+
116
+ async with self.getTestCell() as cell:
117
+ await cell.sync()
118
+ async with cell.nexslock:
119
+ await cell.modNexsRoot(cell._ctorNexsRoot)
120
+ await cell.sync()
121
+
114
122
  async def test_nexus_mixin(self):
115
123
  with self.getTestDir() as dirn:
116
124
  dir1 = s_common.genpath(dirn, 'nexus1')
@@ -692,6 +692,9 @@ class StormHttpTest(s_test.SynTest):
692
692
  tlscadir = s_common.gendir(dirn, 'cadir')
693
693
  cacertpath = shutil.copyfile(os.path.join(cadir, 'somelocalca.crt'), os.path.join(tlscadir, 'somelocalca.crt'))
694
694
 
695
+ with s_common.genfile(cacertpath) as fd:
696
+ ca_cert = fd.read().decode()
697
+
695
698
  pkey, cert = tdir.genUserCert('someuser', signas='somelocalca')
696
699
  user_pkey = tdir._pkeyToByts(pkey).decode()
697
700
  user_cert = tdir._certToByts(cert).decode()
@@ -835,3 +838,40 @@ class StormHttpTest(s_test.SynTest):
835
838
  ## bad cert
836
839
  sslopts['client_cert'] = 'not-gonna-work'
837
840
  await self.asyncraises(s_exc.BadArg, core.callStorm(q, opts=opts))
841
+
842
+ # Provide a CA certificate directly
843
+ async with self.getTestCore(dirn=dirn) as core:
844
+
845
+ sslctx = core.initSslCtx(certpath, pkeypath)
846
+ sslctx.load_verify_locations(cafile=cacertpath)
847
+
848
+ addr, port = await core.addHttpsPort(0, sslctx=sslctx)
849
+ root = await core.auth.getUserByName('root')
850
+ await root.setPasswd('root')
851
+
852
+ core.addHttpApi('/api/v0/test', s_test.HttpReflector, {'cell': core})
853
+
854
+ sslopts = {}
855
+
856
+ opts = {
857
+ 'vars': {
858
+ 'url': f'https://root:root@localhost:{port}/api/v0/test',
859
+ 'verify': True,
860
+ 'sslopts': sslopts,
861
+ },
862
+ }
863
+
864
+ q = 'return($lib.inet.http.get($url, ssl_verify=$verify, ssl_opts=$sslopts))'
865
+
866
+ size, sha256 = await core.callStorm('return($lib.bytes.put($lib.base64.decode(Zm9v)))')
867
+ opts['vars']['sha256'] = sha256
868
+
869
+ ## no cert provided
870
+ resp = await core.callStorm(q, opts=opts)
871
+ self.eq(-1, resp['code'])
872
+ self.isin('certificate verify failed', resp['reason'])
873
+
874
+ ## provide just the CA Certificate
875
+ sslopts['ca_cert'] = ca_cert
876
+ resp = await core.callStorm(q, opts=opts)
877
+ self.eq(200, resp['code'])