synapse 2.178.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 (44) hide show
  1. synapse/cortex.py +162 -27
  2. synapse/datamodel.py +47 -1
  3. synapse/exc.py +1 -0
  4. synapse/lib/aha.py +2 -1
  5. synapse/lib/ast.py +26 -22
  6. synapse/lib/base.py +12 -3
  7. synapse/lib/cell.py +150 -11
  8. synapse/lib/coro.py +14 -0
  9. synapse/lib/drive.py +551 -0
  10. synapse/lib/schemas.py +39 -0
  11. synapse/lib/snap.py +17 -7
  12. synapse/lib/storm.py +3 -1
  13. synapse/lib/stormhttp.py +1 -0
  14. synapse/lib/stormlib/modelext.py +29 -3
  15. synapse/lib/stormlib/stix.py +40 -17
  16. synapse/lib/stormlib/vault.py +2 -2
  17. synapse/lib/stormtypes.py +1 -1
  18. synapse/lib/types.py +9 -0
  19. synapse/lib/version.py +2 -2
  20. synapse/lookup/pe.py +303 -38
  21. synapse/models/dns.py +24 -1
  22. synapse/models/geospace.py +4 -1
  23. synapse/models/infotech.py +26 -1
  24. synapse/tests/test_cortex.py +45 -1
  25. synapse/tests/test_lib_aha.py +17 -0
  26. synapse/tests/test_lib_cell.py +224 -0
  27. synapse/tests/test_lib_coro.py +12 -0
  28. synapse/tests/test_lib_stormhttp.py +40 -0
  29. synapse/tests/test_lib_stormlib_modelext.py +55 -3
  30. synapse/tests/test_lib_stormlib_stix.py +15 -0
  31. synapse/tests/test_lib_stormlib_vault.py +11 -1
  32. synapse/tests/test_lib_stormtypes.py +5 -0
  33. synapse/tests/test_lib_types.py +9 -0
  34. synapse/tests/test_model_dns.py +8 -0
  35. synapse/tests/test_model_geospace.py +3 -1
  36. synapse/tests/test_model_infotech.py +47 -0
  37. synapse/tests/test_model_syn.py +11 -0
  38. synapse/tests/test_utils_stormcov.py +1 -1
  39. synapse/tools/changelog.py +28 -0
  40. {synapse-2.178.0.dist-info → synapse-2.179.0.dist-info}/METADATA +1 -1
  41. {synapse-2.178.0.dist-info → synapse-2.179.0.dist-info}/RECORD +44 -43
  42. {synapse-2.178.0.dist-info → synapse-2.179.0.dist-info}/WHEEL +1 -1
  43. {synapse-2.178.0.dist-info → synapse-2.179.0.dist-info}/LICENSE +0 -0
  44. {synapse-2.178.0.dist-info → synapse-2.179.0.dist-info}/top_level.txt +0 -0
synapse/lib/drive.py ADDED
@@ -0,0 +1,551 @@
1
+ import regex
2
+ import asyncio
3
+
4
+ import synapse.exc as s_exc
5
+ import synapse.common as s_common
6
+
7
+ import synapse.lib.base as s_base
8
+ import synapse.lib.config as s_config
9
+ import synapse.lib.msgpack as s_msgpack
10
+ import synapse.lib.schemas as s_schemas
11
+
12
+ nameregex = regex.compile(s_schemas.re_drivename)
13
+ def reqValidName(name):
14
+ if nameregex.match(name) is None:
15
+ mesg = f'Name {name} is invalid. It must match: {s_schemas.re_drivename}.'
16
+ raise s_exc.BadName(mesg=mesg)
17
+ return name
18
+
19
+ LKEY_TYPE = b'\x00' # <type> = <schema>
20
+ LKEY_DIRN = b'\x01' # <bidn> <name> = <kid>
21
+ LKEY_INFO = b'\x02' # <bidn> = <info>
22
+ LKEY_DATA = b'\x03' # <bidn> <vers> = <data>
23
+ LKEY_VERS = b'\x04' # <bidn> <vers> = <versinfo>
24
+ LKEY_INFO_BYTYPE = b'\x05' # <type> 00 <bidn> = 01
25
+
26
+ rootdir = '00000000000000000000000000000000'
27
+
28
+ def getVersIndx(vers):
29
+ maji = vers[0].to_bytes(3, 'big')
30
+ mini = vers[1].to_bytes(3, 'big')
31
+ pati = vers[2].to_bytes(3, 'big')
32
+ return maji + mini + pati
33
+
34
+ class Drive(s_base.Base):
35
+ '''
36
+ Drive is a hierarchical storage abstraction which:
37
+
38
+ * Provides enveloping which includes meta data for each item:
39
+ * creator iden / time
40
+ * updated iden / time / version
41
+ * number of children
42
+ * data type for the item
43
+ * easy perms (enforcement is up to the caller)
44
+
45
+ * Enforces schemas for data
46
+ * Allows storage of historical versions of data
47
+ * Provides a "path traversal" based API
48
+ * Provides an iden based API that does not require traversal
49
+ '''
50
+ async def __anit__(self, slab, name):
51
+ await s_base.Base.__anit__(self)
52
+ self.slab = slab
53
+ self.dbname = slab.initdb(f'drive:{name}')
54
+ self.validators = {}
55
+
56
+ def getPathNorm(self, path):
57
+
58
+ if isinstance(path, str):
59
+ path = path.strip().strip('/').split('/')
60
+
61
+ return [reqValidName(p.strip().lower()) for p in path]
62
+
63
+ def getItemInfo(self, iden):
64
+ return self._getItemInfo(s_common.uhex(iden))
65
+
66
+ def _getItemInfo(self, bidn):
67
+ byts = self.slab.get(LKEY_INFO + bidn, db=self.dbname)
68
+ if byts is not None:
69
+ return s_msgpack.un(byts)
70
+
71
+ def reqItemInfo(self, iden):
72
+ return self._reqItemInfo(s_common.uhex(iden))
73
+
74
+ def _reqItemInfo(self, bidn):
75
+ info = self._getItemInfo(bidn)
76
+ if info is not None:
77
+ return info
78
+
79
+ mesg = f'No drive item with ID {s_common.ehex(bidn)}.'
80
+ raise s_exc.NoSuchIden(mesg=mesg)
81
+
82
+ async def setItemPath(self, iden, path):
83
+ '''
84
+ Move an existing item to the given path.
85
+ '''
86
+ return await self._setItemPath(s_common.uhex(iden), path)
87
+
88
+ async def getItemPath(self, iden):
89
+ pathinfo = []
90
+ while iden is not None:
91
+
92
+ info = self.reqItemInfo(iden)
93
+
94
+ pathinfo.append(info)
95
+ iden = info.get('parent')
96
+ if iden == rootdir:
97
+ break
98
+
99
+ pathinfo.reverse()
100
+ return pathinfo
101
+
102
+ async def _setItemPath(self, bidn, path, reldir=rootdir):
103
+
104
+ path = self.getPathNorm(path)
105
+
106
+ # new parent iden / bidn
107
+ parinfo = None
108
+ pariden = reldir
109
+
110
+ pathinfo = await self.getPathInfo(path[:-1], reldir=reldir)
111
+ if pathinfo:
112
+ parinfo = pathinfo[-1]
113
+ pariden = parinfo.get('iden')
114
+
115
+ parbidn = s_common.uhex(pariden)
116
+
117
+ self._reqFreeStep(parbidn, path[-1])
118
+
119
+ info = self._reqItemInfo(bidn)
120
+
121
+ oldp = info.get('parent')
122
+ oldb = s_common.uhex(oldp)
123
+ oldname = info.get('name')
124
+
125
+ name = path[-1]
126
+
127
+ info['name'] = name
128
+ info['parent'] = pariden
129
+
130
+ s_schemas.reqValidDriveInfo(info)
131
+
132
+ rows = [
133
+ (LKEY_INFO + bidn, s_msgpack.en(info)),
134
+ (LKEY_DIRN + parbidn + name.encode(), bidn),
135
+ ]
136
+
137
+ if parinfo is not None:
138
+ parinfo['kids'] += 1
139
+ s_schemas.reqValidDriveInfo(parinfo)
140
+ rows.append((LKEY_INFO + parbidn, s_msgpack.en(parinfo)))
141
+
142
+ # if old parent is rootdir this may be None
143
+ oldpinfo = self._getItemInfo(oldb)
144
+ if oldpinfo is not None:
145
+ oldpinfo['kids'] -= 1
146
+ s_schemas.reqValidDriveInfo(oldpinfo)
147
+ rows.append((LKEY_INFO + oldb, s_msgpack.en(oldpinfo)))
148
+
149
+ self.slab.delete(LKEY_DIRN + oldb + oldname.encode(), db=self.dbname)
150
+ self.slab.putmulti(rows, db=self.dbname)
151
+
152
+ pathinfo.append(info)
153
+ return pathinfo
154
+
155
+ def _hasStepItem(self, bidn, name):
156
+ return self.slab.has(LKEY_DIRN + bidn + name.encode(), db=self.dbname)
157
+
158
+ def getStepInfo(self, iden, name):
159
+ return self._getStepInfo(s_common.uhex(iden), name)
160
+
161
+ def _getStepInfo(self, bidn, name):
162
+ step = self.slab.get(LKEY_DIRN + bidn + name.encode(), db=self.dbname)
163
+ if step is None:
164
+ return None
165
+
166
+ byts = self.slab.get(LKEY_INFO + step, db=self.dbname)
167
+ if byts is not None:
168
+ return s_msgpack.un(byts)
169
+
170
+ def _addStepInfo(self, parbidn, parinfo, info):
171
+
172
+ newbidn = s_common.uhex(info.get('iden'))
173
+
174
+ # name must already be normalized
175
+ name = info.get('name')
176
+ typename = info.get('type')
177
+
178
+ self._reqFreeStep(parbidn, name)
179
+
180
+ rows = [
181
+ (LKEY_DIRN + parbidn + name.encode(), newbidn),
182
+ (LKEY_INFO + newbidn, s_msgpack.en(info)),
183
+ ]
184
+
185
+ if parinfo is not None:
186
+ parinfo['kids'] += 1
187
+ rows.append((LKEY_INFO + parbidn, s_msgpack.en(parinfo)))
188
+
189
+ if typename is not None:
190
+ typekey = LKEY_INFO_BYTYPE + typename.encode() + b'\x00' + newbidn
191
+ rows.append((typekey, b'\x01'))
192
+
193
+ self.slab.putmulti(rows, db=self.dbname)
194
+
195
+ def setItemPerm(self, iden, perm):
196
+ return self._setItemPerm(s_common.uhex(iden), perm)
197
+
198
+ def _setItemPerm(self, bidn, perm):
199
+ info = self._reqItemInfo(bidn)
200
+ info['perm'] = perm
201
+ s_schemas.reqValidDriveInfo(info)
202
+ self.slab.put(LKEY_INFO + bidn, s_msgpack.en(info), db=self.dbname)
203
+ return info
204
+
205
+ async def getPathInfo(self, path, reldir=rootdir):
206
+ '''
207
+ Return a list of item info for each step in the given path
208
+ relative to rootdir.
209
+
210
+ This API is designed to allow the caller to retrieve the path info
211
+ and potentially check permissions on each level to control access.
212
+ '''
213
+
214
+ path = self.getPathNorm(path)
215
+ parbidn = s_common.uhex(reldir)
216
+
217
+ pathinfo = []
218
+ for part in path:
219
+ await asyncio.sleep(0)
220
+
221
+ info = self._getStepInfo(parbidn, part)
222
+ if info is None:
223
+ mesg = f'Path step not found: {part}'
224
+ raise s_exc.NoSuchPath(mesg=mesg)
225
+
226
+ pathinfo.append(info)
227
+ parbidn = s_common.uhex(info.get('iden'))
228
+
229
+ return pathinfo
230
+
231
+ def hasItemInfo(self, iden):
232
+ return self._hasItemInfo(s_common.uhex(iden))
233
+
234
+ def _hasItemInfo(self, bidn):
235
+ return self.slab.has(LKEY_INFO + bidn, db=self.dbname)
236
+
237
+ async def hasPathInfo(self, path, reldir=rootdir):
238
+ '''
239
+ Check for a path existing relative to reldir.
240
+ '''
241
+ path = self.getPathNorm(path)
242
+ parbidn = s_common.uhex(reldir)
243
+
244
+ for part in path:
245
+
246
+ await asyncio.sleep(0)
247
+
248
+ info = self._getStepInfo(parbidn, part)
249
+ if info is None:
250
+ return False
251
+
252
+ parbidn = s_common.uhex(info.get('iden'))
253
+
254
+ return True
255
+
256
+ async def addItemInfo(self, info, path=None, reldir=rootdir):
257
+ '''
258
+ Add a new item at the specified path relative to reldir.
259
+ '''
260
+ pariden = reldir
261
+ pathinfo = []
262
+
263
+ if path is not None:
264
+ path = self.getPathNorm(path)
265
+ pathinfo = await self.getPathInfo(path, reldir=reldir)
266
+ if pathinfo:
267
+ pariden = pathinfo[-1].get('iden')
268
+
269
+ parbidn = s_common.uhex(pariden)
270
+ parinfo = self._getItemInfo(parbidn)
271
+
272
+ info['size'] = 0
273
+ info['kids'] = 0
274
+ info['parent'] = pariden
275
+
276
+ info.setdefault('perm', {'users': {}, 'roles': {}})
277
+ info.setdefault('version', (0, 0, 0))
278
+
279
+ s_schemas.reqValidDriveInfo(info)
280
+
281
+ iden = info.get('iden')
282
+ typename = info.get('type')
283
+
284
+ bidn = s_common.uhex(iden)
285
+
286
+ if typename is not None:
287
+ self.reqTypeValidator(typename)
288
+
289
+ if self._getItemInfo(bidn) is not None:
290
+ mesg = f'A drive entry with ID {iden} already exists.'
291
+ raise s_exc.DupIden(mesg=mesg)
292
+
293
+ self._addStepInfo(parbidn, parinfo, info)
294
+
295
+ pathinfo.append(info)
296
+ return pathinfo
297
+
298
+ def reqFreeStep(self, iden, name):
299
+ return self._reqFreeStep(s_common.uhex(iden), name)
300
+
301
+ def _reqFreeStep(self, bidn, name):
302
+ if self._hasStepItem(bidn, name):
303
+ mesg = f'A drive entry with name {name} already exists in parent {s_common.ehex(bidn)}.'
304
+ raise s_exc.DupName(mesg=mesg)
305
+
306
+ async def delItemInfo(self, iden):
307
+ '''
308
+ Recursively remove the info and all associated data versions.
309
+ '''
310
+ return await self._delItemInfo(s_common.uhex(iden))
311
+
312
+ async def _delItemInfo(self, bidn):
313
+ async for info in self._walkItemInfo(bidn):
314
+ await self._delOneInfo(info)
315
+
316
+ async def _delOneInfo(self, info):
317
+ iden = info.get('iden')
318
+ parent = info.get('parent')
319
+
320
+ bidn = s_common.uhex(iden)
321
+ parbidn = s_common.uhex(parent)
322
+
323
+ name = info.get('name').encode()
324
+
325
+ self.slab.delete(LKEY_INFO + bidn, db=self.dbname)
326
+ self.slab.delete(LKEY_DIRN + parbidn + name, db=self.dbname)
327
+
328
+ pref = LKEY_VERS + bidn
329
+ for lkey in self.slab.scanKeysByPref(pref, db=self.dbname):
330
+ self.slab.delete(lkey, db=self.dbname)
331
+ await asyncio.sleep(0)
332
+
333
+ pref = LKEY_DATA + bidn
334
+ for lkey in self.slab.scanKeysByPref(pref, db=self.dbname):
335
+ self.slab.delete(lkey, db=self.dbname)
336
+ await asyncio.sleep(0)
337
+
338
+ async def walkItemInfo(self, iden):
339
+ async for item in self._walkItemInfo(s_common.uhex(iden)):
340
+ yield item
341
+
342
+ async def _walkItemInfo(self, bidn):
343
+ async for knfo in self._walkItemKids(bidn):
344
+ yield knfo
345
+ yield self._getItemInfo(bidn)
346
+
347
+ async def walkPathInfo(self, path, reldir=rootdir):
348
+
349
+ path = self.getPathNorm(path)
350
+ pathinfo = await self.getPathInfo(path, reldir=reldir)
351
+
352
+ bidn = s_common.uhex(pathinfo[-1].get('iden'))
353
+ async for info in self._walkItemKids(bidn):
354
+ yield info
355
+
356
+ yield pathinfo[-1]
357
+
358
+ async def getItemKids(self, iden):
359
+ '''
360
+ Yield each of the children of the specified item.
361
+ '''
362
+ bidn = s_common.uhex(iden)
363
+ for lkey, bidn in self.slab.scanByPref(LKEY_DIRN + bidn, db=self.dbname):
364
+ await asyncio.sleep(0)
365
+
366
+ info = self._getItemInfo(bidn)
367
+ if info is None: # pragma no cover
368
+ continue
369
+
370
+ yield info
371
+
372
+ async def _walkItemKids(self, bidn):
373
+
374
+ for lkey, bidn in self.slab.scanByPref(LKEY_DIRN + bidn, db=self.dbname):
375
+ await asyncio.sleep(0)
376
+
377
+ info = self._getItemInfo(bidn)
378
+ if info is None: # pragma: no cover
379
+ continue
380
+
381
+ nidn = s_common.uhex(info.get('iden'))
382
+ async for item in self._walkItemKids(nidn):
383
+ yield item
384
+
385
+ yield info
386
+
387
+ def setItemData(self, iden, versinfo, data):
388
+ return self._setItemData(s_common.uhex(iden), versinfo, data)
389
+
390
+ def _setItemData(self, bidn, versinfo, data):
391
+
392
+ info = self._reqItemInfo(bidn)
393
+
394
+ typename = info.get('type')
395
+
396
+ self.reqValidData(typename, data)
397
+
398
+ byts = s_msgpack.en(data)
399
+
400
+ size = len(byts)
401
+
402
+ versinfo['size'] = size
403
+
404
+ s_schemas.reqValidDriveDataVers(versinfo)
405
+
406
+ curvers = info.get('version')
407
+ datavers = versinfo.get('version')
408
+
409
+ versindx = getVersIndx(datavers)
410
+
411
+ rows = [
412
+ (LKEY_DATA + bidn + versindx, s_msgpack.en(data)),
413
+ (LKEY_VERS + bidn + versindx, s_msgpack.en(versinfo)),
414
+ ]
415
+
416
+ # if new version is greater than the one we have stored
417
+ # update the info with the newest version info...
418
+ if datavers >= curvers:
419
+ info.update(versinfo)
420
+ rows.append((LKEY_INFO + bidn, s_msgpack.en(info)))
421
+
422
+ self.slab.putmulti(rows, db=self.dbname)
423
+
424
+ return info, versinfo
425
+
426
+ def getItemData(self, iden, vers=None):
427
+ '''
428
+ Return a (versinfo, data) tuple for the given iden. If
429
+ version is not specified, the current version is returned.
430
+ '''
431
+ return self._getItemData(s_common.uhex(iden), vers=vers)
432
+
433
+ def _getItemData(self, bidn, vers=None):
434
+
435
+ if vers is None:
436
+ info = self._getItemInfo(bidn)
437
+ vers = info.get('version')
438
+
439
+ versindx = getVersIndx(vers)
440
+ versbyts = self.slab.get(LKEY_VERS + bidn + versindx, db=self.dbname)
441
+ if versbyts is None: # pragma: no cover
442
+ return None
443
+
444
+ databyts = self.slab.get(LKEY_DATA + bidn + versindx, db=self.dbname)
445
+ if databyts is None: # pragma: no cover
446
+ return None
447
+
448
+ return s_msgpack.un(versbyts), s_msgpack.un(databyts)
449
+
450
+ def delItemData(self, iden, vers=None):
451
+ return self._delItemData(s_common.uhex(iden), vers=vers)
452
+
453
+ def _delItemData(self, bidn, vers=None):
454
+
455
+ info = self._reqItemInfo(bidn)
456
+ if vers is None:
457
+ vers = info.get('version')
458
+
459
+ versindx = getVersIndx(vers)
460
+
461
+ self.slab.delete(LKEY_VERS + bidn + versindx, db=self.dbname)
462
+ self.slab.delete(LKEY_DATA + bidn + versindx, db=self.dbname)
463
+
464
+ # back down or revert to 0.0.0
465
+ if vers == info.get('version'):
466
+ versinfo = self._getLastDataVers(bidn)
467
+ if versinfo is None:
468
+ info['size'] = 0
469
+ info['version'] = (0, 0, 0)
470
+ info.pop('updated', None)
471
+ info.pop('updater', None)
472
+ else:
473
+ info.update(versinfo)
474
+
475
+ self.slab.put(LKEY_INFO + bidn, s_msgpack.en(info), db=self.dbname)
476
+ return info
477
+
478
+ def _getLastDataVers(self, bidn):
479
+ for lkey, byts in self.slab.scanByPrefBack(LKEY_VERS + bidn, db=self.dbname):
480
+ return s_msgpack.un(byts)
481
+
482
+ async def getItemDataVersions(self, iden):
483
+ '''
484
+ Yield data version info in reverse created order.
485
+ '''
486
+ bidn = s_common.uhex(iden)
487
+ pref = LKEY_VERS + bidn
488
+ for lkey, byts in self.slab.scanByPrefBack(pref, db=self.dbname):
489
+ yield s_msgpack.un(byts)
490
+ await asyncio.sleep(0)
491
+
492
+ def getTypeSchema(self, typename):
493
+ byts = self.slab.get(LKEY_TYPE + typename.encode(), db=self.dbname)
494
+ if byts is not None:
495
+ return s_msgpack.un(byts)
496
+
497
+ async def setTypeSchema(self, typename, schema, callback=None):
498
+
499
+ reqValidName(typename)
500
+
501
+ vtor = s_config.getJsValidator(schema)
502
+
503
+ self.validators[typename] = vtor
504
+
505
+ lkey = LKEY_TYPE + typename.encode()
506
+
507
+ self.slab.put(lkey, s_msgpack.en(schema), db=self.dbname)
508
+
509
+ if callback is not None:
510
+ async for info in self.getItemsByType(typename):
511
+ bidn = s_common.uhex(info.get('iden'))
512
+ for lkey, byts in self.slab.scanByPref(LKEY_VERS + bidn, db=self.dbname):
513
+ versindx = lkey[-9:]
514
+ databyts = self.slab.get(LKEY_DATA + bidn + versindx, db=self.dbname)
515
+ data = await callback(info, s_msgpack.un(byts), s_msgpack.un(databyts))
516
+ vtor(data)
517
+ self.slab.put(LKEY_DATA + bidn + versindx, s_msgpack.en(data), db=self.dbname)
518
+ await asyncio.sleep(0)
519
+
520
+ async def getItemsByType(self, typename):
521
+ tkey = typename.encode() + b'\x00'
522
+ for lkey in self.slab.scanKeysByPref(LKEY_INFO_BYTYPE + tkey, db=self.dbname):
523
+ bidn = lkey[-16:]
524
+ info = self._getItemInfo(bidn)
525
+ if info is not None:
526
+ yield info
527
+
528
+ def getTypeValidator(self, typename):
529
+ vtor = self.validators.get(typename)
530
+ if vtor is not None:
531
+ return vtor
532
+
533
+ schema = self.getTypeSchema(typename)
534
+ if schema is None:
535
+ return None
536
+
537
+ vtor = s_config.getJsValidator(schema)
538
+ self.validators[typename] = vtor
539
+
540
+ return vtor
541
+
542
+ def reqTypeValidator(self, typename):
543
+ vtor = self.getTypeValidator(typename)
544
+ if vtor is not None:
545
+ return vtor
546
+
547
+ mesg = f'No schema registered with name: {typename}'
548
+ raise s_exc.NoSuchType(mesg=mesg)
549
+
550
+ def reqValidData(self, typename, item):
551
+ self.reqTypeValidator(typename)(item)
synapse/lib/schemas.py CHANGED
@@ -274,6 +274,7 @@ reqValidSslCtxOpts = s_config.getJsValidator({
274
274
  'verify': {'type': 'boolean', 'default': True},
275
275
  'client_cert': {'type': ['string', 'null'], 'default': None},
276
276
  'client_key': {'type': ['string', 'null'], 'default': None},
277
+ 'ca_cert': {'type': ['string', 'null'], 'default': None},
277
278
  },
278
279
  'additionalProperties': False,
279
280
  })
@@ -454,3 +455,41 @@ tabularConfSchema = {
454
455
  }
455
456
 
456
457
  reqValidTabularConf = s_config.getJsValidator(tabularConfSchema)
458
+
459
+ emptySchema = {'object': {}, 'additionalProperties': False}
460
+ re_drivename = r'^[\w_.-]{1,128}$'
461
+
462
+ driveInfoSchema = {
463
+ 'type': 'object',
464
+ 'properties': {
465
+ 'iden': {'type': 'string', 'pattern': s_config.re_iden},
466
+ 'parent': {'type': 'string', 'pattern': s_config.re_iden},
467
+ 'type': {'type': 'string', 'pattern': re_drivename},
468
+ 'name': {'type': 'string', 'pattern': re_drivename},
469
+ 'perm': s_msgpack.deepcopy(easyPermSchema),
470
+ 'kids': {'type': 'number', 'minimum': 0},
471
+ 'created': {'type': 'number'},
472
+ 'creator': {'type': 'string', 'pattern': s_config.re_iden},
473
+ # these are also data version info...
474
+ 'size': {'type': 'number', 'minimum': 0},
475
+ 'updated': {'type': 'number'},
476
+ 'updater': {'type': 'string', 'pattern': s_config.re_iden},
477
+ 'version': {'type': 'array', 'items': {'type': 'number', 'minItems': 3, 'maxItems': 3}},
478
+ },
479
+ 'required': ('iden', 'parent', 'name', 'created', 'creator', 'kids'),
480
+ 'additionalProperties': False,
481
+ }
482
+ reqValidDriveInfo = s_config.getJsValidator(driveInfoSchema)
483
+
484
+ driveDataVersSchema = {
485
+ 'type': 'object',
486
+ 'properties': {
487
+ 'size': {'type': 'number', 'minimum': 0},
488
+ 'updated': {'type': 'number'},
489
+ 'updater': {'type': 'string', 'pattern': s_config.re_iden},
490
+ 'version': {'type': 'array', 'items': {'type': 'number', 'minItems': 3, 'maxItems': 3}},
491
+ },
492
+ 'required': ('size', 'version', 'updated', 'updater'),
493
+ 'additionalProperties': False,
494
+ }
495
+ reqValidDriveDataVers = s_config.getJsValidator(driveDataVersSchema)
synapse/lib/snap.py CHANGED
@@ -316,6 +316,10 @@ class ProtoNode:
316
316
  mesg = f'Tagprop {name} does not exist in this Cortex.'
317
317
  return await self.ctx.snap._raiseOnStrict(s_exc.NoSuchTagProp, mesg)
318
318
 
319
+ if prop.locked:
320
+ mesg = f'Tagprop {name} is locked.'
321
+ return await self.ctx.snap._raiseOnStrict(s_exc.IsDeprLocked, mesg)
322
+
319
323
  try:
320
324
  norm, info = prop.type.norm(valu)
321
325
  except s_exc.BadTypeValu as e:
@@ -1029,7 +1033,7 @@ class Snap(s_base.Base):
1029
1033
  if node is not None:
1030
1034
  yield node
1031
1035
 
1032
- async def nodesByPropValu(self, full, cmpr, valu, reverse=False):
1036
+ async def nodesByPropValu(self, full, cmpr, valu, reverse=False, norm=True):
1033
1037
  if cmpr == 'type=':
1034
1038
  if reverse:
1035
1039
  async for node in self.nodesByPropTypeValu(full, valu, reverse=reverse):
@@ -1050,10 +1054,13 @@ class Snap(s_base.Base):
1050
1054
  mesg = f'No property named "{full}".'
1051
1055
  raise s_exc.NoSuchProp(mesg=mesg)
1052
1056
 
1053
- cmprvals = prop.type.getStorCmprs(cmpr, valu)
1054
- # an empty return probably means ?= with invalid value
1055
- if not cmprvals:
1056
- return
1057
+ if norm:
1058
+ cmprvals = prop.type.getStorCmprs(cmpr, valu)
1059
+ # an empty return probably means ?= with invalid value
1060
+ if not cmprvals:
1061
+ return
1062
+ else:
1063
+ cmprvals = ((cmpr, valu, prop.type.stortype),)
1057
1064
 
1058
1065
  if prop.isrunt:
1059
1066
  for storcmpr, storvalu, _ in cmprvals:
@@ -1108,7 +1115,7 @@ class Snap(s_base.Base):
1108
1115
  async for node in self.nodesByPropArray(prop.full, '=', valu, reverse=reverse):
1109
1116
  yield node
1110
1117
 
1111
- async def nodesByPropArray(self, full, cmpr, valu, reverse=False):
1118
+ async def nodesByPropArray(self, full, cmpr, valu, reverse=False, norm=True):
1112
1119
 
1113
1120
  prop = self.core.model.prop(full)
1114
1121
  if prop is None:
@@ -1119,7 +1126,10 @@ class Snap(s_base.Base):
1119
1126
  mesg = f'Array syntax is invalid on non array type: {prop.type.name}.'
1120
1127
  raise s_exc.BadTypeValu(mesg=mesg)
1121
1128
 
1122
- cmprvals = prop.type.arraytype.getStorCmprs(cmpr, valu)
1129
+ if norm:
1130
+ cmprvals = prop.type.arraytype.getStorCmprs(cmpr, valu)
1131
+ else:
1132
+ cmprvals = ((cmpr, valu, prop.type.arraytype.stortype),)
1123
1133
 
1124
1134
  if prop.isform:
1125
1135
  async for (buid, sodes) in self.core._liftByPropArray(prop.name, None, cmprvals, self.layers, reverse=reverse):
synapse/lib/storm.py CHANGED
@@ -53,7 +53,9 @@ When condition is tag:add or tag:del, you may optionally provide a form name
53
53
  to restrict the trigger to fire only on tags added or deleted from nodes of
54
54
  those forms.
55
55
 
56
- The added tag is provided to the query as an embedded variable '$tag'.
56
+ The added tag is provided to the query in the ``$auto`` dictionary variable under
57
+ ``$auto.opts.tag``. Usage of the ``$tag`` variable is deprecated and it will no longer
58
+ be populated in Synapse v3.0.0.
57
59
 
58
60
  Simple one level tag globbing is supported, only at the end after a period,
59
61
  that is aka.* matches aka.foo and aka.bar but not aka.foo.bar. aka* is not
synapse/lib/stormhttp.py CHANGED
@@ -95,6 +95,7 @@ class LibHttp(s_stormtypes.Lib):
95
95
  'verify': <bool> - Perform SSL/TLS verification. Is overridden by the ssl_verify argument.
96
96
  'client_cert': <str> - PEM encoded full chain certificate for use in mTLS.
97
97
  'client_key': <str> - PEM encoded key for use in mTLS. Alternatively, can be included in client_cert.
98
+ 'ca_cert': <str> - A PEM encoded full chain CA certificate for use when verifying the request.
98
99
  }
99
100
  '''
100
101
  _storm_locals = (