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
synapse/cortex.py CHANGED
@@ -774,13 +774,17 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
774
774
  },
775
775
  'layer:lmdb:map_async': {
776
776
  'default': True,
777
- 'description': 'Set the default lmdb:map_async value in LMDB layers.',
778
- 'type': 'boolean'
777
+ 'description': 'Deprecated. This value is ignored.',
778
+ 'type': 'boolean',
779
+ 'hidecmdl': True,
780
+ 'hideconf': True,
779
781
  },
780
782
  'layer:lmdb:max_replay_log': {
781
783
  'default': 10000,
782
- 'description': 'Set the max size of the replay log for all layers.',
783
- 'type': 'integer'
784
+ 'description': 'Deprecated. This value is ignored.',
785
+ 'type': 'integer',
786
+ 'hidecmdl': True,
787
+ 'hideconf': True,
784
788
  },
785
789
  'layers:lockmemory': {
786
790
  'default': False,
@@ -1350,20 +1354,20 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
1350
1354
  'desc': 'Controls access to adding specific extended model properties.',
1351
1355
  'ex': 'model.prop.add._foo:bar'},
1352
1356
  {'perm': ('model', 'prop', 'del'), 'gate': 'cortex',
1353
- 'desc': 'Controls access to deleting extended model properties.'},
1357
+ 'desc': 'Controls access to deleting extended model properties and values.'},
1354
1358
  {'perm': ('model', 'prop', 'del', '<form>'), 'gate': 'cortex',
1355
- 'desc': 'Controls access to deleting specific extended model properties.',
1359
+ 'desc': 'Controls access to deleting specific extended model properties and values.',
1356
1360
  'ex': 'model.prop.del._foo:bar'},
1357
1361
 
1358
1362
  {'perm': ('model', 'tagprop', 'add'), 'gate': 'cortex',
1359
- 'desc': 'Controls access to adding extended model tag properties.'},
1363
+ 'desc': 'Controls access to adding extended model tag properties and values.'},
1360
1364
  {'perm': ('model', 'tagprop', 'del'), 'gate': 'cortex',
1361
- 'desc': 'Controls access to deleting extended model tag properties.'},
1365
+ 'desc': 'Controls access to deleting extended model tag properties and values.'},
1362
1366
 
1363
1367
  {'perm': ('model', 'univ', 'add'), 'gate': 'cortex',
1364
1368
  'desc': 'Controls access to adding extended model universal properties.'},
1365
1369
  {'perm': ('model', 'univ', 'del'), 'gate': 'cortex',
1366
- 'desc': 'Controls access to deleting extended model universal properties.'},
1370
+ 'desc': 'Controls access to deleting extended model universal properties and values.'},
1367
1371
 
1368
1372
  {'perm': ('node',), 'gate': 'layer',
1369
1373
  'desc': 'Controls all node edits in a layer.'},
@@ -1622,6 +1626,25 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
1622
1626
  if self.isactive:
1623
1627
  await self.finiStormPool()
1624
1628
 
1629
+ @s_nexus.Pusher.onPushAuto('model:lock:prop')
1630
+ async def setPropLocked(self, name, locked):
1631
+ prop = self.model.reqProp(name)
1632
+ self.modellocks.set(f'prop/{name}', locked)
1633
+ prop.locked = locked
1634
+
1635
+ @s_nexus.Pusher.onPushAuto('model:lock:univ')
1636
+ async def setUnivLocked(self, name, locked):
1637
+ prop = self.model.reqUniv(name)
1638
+ self.modellocks.set(f'univ/{name}', locked)
1639
+ for prop in self.model.getAllUnivs(name):
1640
+ prop.locked = locked
1641
+
1642
+ @s_nexus.Pusher.onPushAuto('model:lock:tagprop')
1643
+ async def setTagPropLocked(self, name, locked):
1644
+ prop = self.model.reqTagProp(name)
1645
+ self.modellocks.set(f'tagprop/{name}', locked)
1646
+ prop.locked = locked
1647
+
1625
1648
  @s_nexus.Pusher.onPushAuto('model:depr:lock')
1626
1649
  async def setDeprLock(self, name, locked):
1627
1650
 
@@ -2089,17 +2112,15 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
2089
2112
  async def onSetTrigDoc(node, prop, valu):
2090
2113
  valu = str(valu)
2091
2114
  iden = node.ndef[1]
2092
- trig = node.snap.view.triggers.get(iden)
2093
2115
  node.snap.user.confirm(('trigger', 'set', 'doc'), gateiden=iden)
2094
- await trig.set('doc', valu)
2116
+ await node.snap.view.setTriggerInfo(iden, 'doc', valu)
2095
2117
  node.props[prop.name] = valu
2096
2118
 
2097
2119
  async def onSetTrigName(node, prop, valu):
2098
2120
  valu = str(valu)
2099
2121
  iden = node.ndef[1]
2100
- trig = node.snap.view.triggers.get(iden)
2101
2122
  node.snap.user.confirm(('trigger', 'set', 'name'), gateiden=iden)
2102
- await trig.set('name', valu)
2123
+ await node.snap.view.setTriggerInfo(iden, 'name', valu)
2103
2124
  node.props[prop.name] = valu
2104
2125
 
2105
2126
  async def onSetCronDoc(node, prop, valu):
@@ -3433,7 +3454,7 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
3433
3454
 
3434
3455
  for layr in self.layers.values():
3435
3456
  async for item in layr.iterFormRows(formname):
3436
- mesg = f'Nodes still exist with form: {formname}'
3457
+ mesg = f'Nodes still exist with form: {formname} in layer {layr.iden}'
3437
3458
  raise s_exc.CantDelForm(mesg=mesg)
3438
3459
 
3439
3460
  self.model.delForm(formname)
@@ -3482,14 +3503,116 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
3482
3503
  await self.feedBeholder('model:prop:add', {'form': form, 'prop': prop.pack()})
3483
3504
 
3484
3505
  async def delFormProp(self, form, prop):
3506
+ self.reqExtProp(form, prop)
3507
+ return await self._push('model:prop:del', form, prop)
3508
+
3509
+ async def _delAllFormProp(self, formname, propname, meta):
3510
+ '''
3511
+ Delete all instances of a property from all layers.
3512
+
3513
+ NOTE: This does not fire triggers.
3514
+ '''
3515
+ self.reqExtProp(formname, propname)
3516
+
3517
+ fullname = f'{formname}:{propname}'
3518
+ prop = self.model.prop(fullname)
3519
+
3520
+ await self.setPropLocked(fullname, True)
3521
+
3522
+ for layr in list(self.layers.values()):
3523
+
3524
+ genr = layr.iterPropRows(formname, propname)
3525
+
3526
+ async for rows in s_coro.chunks(genr):
3527
+ nodeedits = []
3528
+ for buid, valu in rows:
3529
+ nodeedits.append((buid, prop.form.name, (
3530
+ (s_layer.EDIT_PROP_DEL, (prop.name, None, prop.type.stortype), ()),
3531
+ )))
3532
+
3533
+ await layr.saveNodeEdits(nodeedits, meta)
3534
+ await asyncio.sleep(0)
3535
+
3536
+ async def _delAllUnivProp(self, propname, meta):
3537
+ '''
3538
+ Delete all instances of a universal property from all layers.
3539
+
3540
+ NOTE: This does not fire triggers.
3541
+ '''
3542
+ self.reqExtUniv(propname)
3543
+
3544
+ full = f'.{propname}'
3545
+ prop = self.model.univ(full)
3546
+
3547
+ await self.setUnivLocked(full, True)
3548
+
3549
+ for layr in list(self.layers.values()):
3550
+
3551
+ genr = layr.iterUnivRows(full)
3552
+
3553
+ async for rows in s_coro.chunks(genr):
3554
+ nodeedits = []
3555
+ for buid, valu in rows:
3556
+ sode = await layr.getStorNode(buid)
3557
+ nodeedits.append((buid, sode.get('form'), (
3558
+ (s_layer.EDIT_PROP_DEL, (prop.name, None, prop.type.stortype), ()),
3559
+ )))
3560
+
3561
+ await layr.saveNodeEdits(nodeedits, meta)
3562
+ await asyncio.sleep(0)
3563
+
3564
+ async def _delAllTagProp(self, propname, meta):
3565
+ '''
3566
+ Delete all instances of a tag property from all layers.
3567
+
3568
+ NOTE: This does not fire triggers.
3569
+ '''
3570
+ self.reqExtTagProp(propname)
3571
+ prop = self.model.getTagProp(propname)
3572
+
3573
+ await self.setTagPropLocked(propname, True)
3574
+
3575
+ for layr in list(self.layers.values()):
3576
+
3577
+ for form, tag, tagprop in layr.getTagProps():
3578
+
3579
+ if tagprop != propname: # pragma: no cover
3580
+ await asyncio.sleep(0)
3581
+ continue
3582
+
3583
+ genr = layr.iterTagPropRows(tag, tagprop, form)
3584
+
3585
+ async for rows in s_coro.chunks(genr):
3586
+ nodeedits = []
3587
+ for buid, valu in rows:
3588
+ nodeedits.append((buid, form, (
3589
+ (s_layer.EDIT_TAGPROP_DEL, (tag, prop.name, None, prop.type.stortype), ()),
3590
+ )))
3591
+
3592
+ await layr.saveNodeEdits(nodeedits, meta)
3593
+ await asyncio.sleep(0)
3594
+
3595
+ def reqExtProp(self, form, prop):
3485
3596
  full = f'{form}:{prop}'
3486
3597
  pdef = self.extprops.get(full)
3487
-
3488
3598
  if pdef is None:
3489
3599
  mesg = f'No ext prop named {full}'
3490
3600
  raise s_exc.NoSuchProp(form=form, prop=prop, mesg=mesg)
3601
+ return pdef
3491
3602
 
3492
- return await self._push('model:prop:del', form, prop)
3603
+ def reqExtUniv(self, prop):
3604
+ udef = self.extunivs.get(prop)
3605
+ if udef is None:
3606
+ mesg = f'No ext univ named {prop}'
3607
+ raise s_exc.NoSuchUniv(name=prop, mesg=mesg)
3608
+ return udef
3609
+
3610
+ def reqExtTagProp(self, name):
3611
+ pdef = self.exttagprops.get(name)
3612
+ if pdef is None:
3613
+ mesg = f'No tag prop named {name}'
3614
+ raise s_exc.NoSuchTagProp(mesg=mesg, name=name)
3615
+ return pdef
3493
3616
 
3494
3617
  @s_nexus.Pusher.onPush('model:prop:del')
3495
3618
  async def _delFormProp(self, form, prop):
@@ -3504,22 +3627,19 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
3504
3627
 
3505
3628
  for layr in self.layers.values():
3506
3629
  async for item in layr.iterPropRows(form, prop):
3507
- mesg = f'Nodes still exist with prop: {form}:{prop}'
3630
+ mesg = f'Nodes still exist with prop: {form}:{prop} in layer {layr.iden}'
3508
3631
  raise s_exc.CantDelProp(mesg=mesg)
3509
3632
 
3510
3633
  self.model.delFormProp(form, prop)
3511
3634
  self.extprops.pop(full, None)
3635
+ self.modellocks.pop(f'prop/{full}', None)
3512
3636
  await self.fire('core:extmodel:change',
3513
3637
  form=form, prop=prop, act='del', type='formprop')
3514
3638
 
3515
3639
  await self.feedBeholder('model:prop:del', {'form': form, 'prop': prop})
3516
3640
 
3517
3641
  async def delUnivProp(self, prop):
3518
- udef = self.extunivs.get(prop)
3519
- if udef is None:
3520
- mesg = f'No ext univ named {prop}'
3521
- raise s_exc.NoSuchUniv(name=prop, mesg=mesg)
3522
-
3642
+ self.reqExtUniv(prop)
3523
3643
  return await self._push('model:univ:del', prop)
3524
3644
 
3525
3645
  @s_nexus.Pusher.onPush('model:univ:del')
@@ -3534,11 +3654,12 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
3534
3654
  univname = '.' + prop
3535
3655
  for layr in self.layers.values():
3536
3656
  async for item in layr.iterUnivRows(univname):
3537
- mesg = f'Nodes still exist with universal prop: {prop}'
3657
+ mesg = f'Nodes still exist with universal prop: {prop} in layer {layr.iden}'
3538
3658
  raise s_exc.CantDelUniv(mesg=mesg)
3539
3659
 
3540
3660
  self.model.delUnivProp(prop)
3541
3661
  self.extunivs.pop(prop, None)
3662
+ self.modellocks.pop(f'univ/{prop}', None)
3542
3663
  await self.fire('core:extmodel:change', name=prop, act='del', type='univ')
3543
3664
  await self.feedBeholder('model:univ:del', {'prop': univname})
3544
3665
 
@@ -3570,11 +3691,7 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
3570
3691
  await self.feedBeholder('model:tagprop:add', tagp.pack())
3571
3692
 
3572
3693
  async def delTagProp(self, name):
3573
- pdef = self.exttagprops.get(name)
3574
- if pdef is None:
3575
- mesg = f'No tag prop named {name}'
3576
- raise s_exc.NoSuchProp(mesg=mesg, name=name)
3577
-
3694
+ self.reqExtTagProp(name)
3578
3695
  return await self._push('model:tagprop:del', name)
3579
3696
 
3580
3697
  @s_nexus.Pusher.onPush('model:tagprop:del')
@@ -3585,12 +3702,13 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
3585
3702
 
3586
3703
  for layr in self.layers.values():
3587
3704
  if await layr.hasTagProp(name):
3588
- mesg = f'Nodes still exist with tagprop: {name}'
3705
+ mesg = f'Nodes still exist with tagprop: {name} in layer {layr.iden}'
3589
3706
  raise s_exc.CantDelProp(mesg=mesg)
3590
3707
 
3591
3708
  self.model.delTagProp(name)
3592
3709
 
3593
3710
  self.exttagprops.pop(name, None)
3711
+ self.modellocks.pop(f'tagprop/{name}', None)
3594
3712
  await self.fire('core:tagprop:change', name=name, act='del')
3595
3713
  await self.feedBeholder('model:tagprop:del', {'tagprop': name})
3596
3714
 
@@ -3937,7 +4055,10 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
3937
4055
  self.stormvars.set(s_stormlib_cell.runtime_fixes_key, s_stormlib_cell.getMaxHotFixes())
3938
4056
 
3939
4057
  async def _initDeprLocks(self):
4058
+
3940
4059
  self.deprlocks = self.cortexdata.getSubKeyVal('model:deprlocks:')
4060
+ self.modellocks = self.cortexdata.getSubKeyVal('model:locks:')
4061
+
3941
4062
  # TODO: 3.0.0 conversion will truncate this hive key
3942
4063
 
3943
4064
  if self.inaugural:
@@ -3963,6 +4084,24 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
3963
4084
  if _type is not None:
3964
4085
  _type.locked = locked
3965
4086
 
4087
+ for name, locked in self.modellocks.items():
4088
+
4089
+ prop = None
4090
+ elemtype, elemname = name.split('/', 1)
4091
+
4092
+ if elemtype == 'prop':
4093
+ prop = self.model.prop(elemname)
4094
+ elif elemtype == 'univ':
4095
+ prop = self.model.univ(elemname)
4096
+ if prop is not None:
4097
+ for univ in self.model.getAllUnivs(elemname):
4098
+ univ.locked = locked
4099
+ elif elemtype == 'tagprop':
4100
+ prop = self.model.getTagProp(elemname)
4101
+
4102
+ if prop is not None:
4103
+ prop.locked = locked
4104
+
3966
4105
  async def _initJsonStor(self):
3967
4106
 
3968
4107
  self.jsonurl = self.conf.get('jsonstor')
@@ -7068,6 +7207,7 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
7068
7207
  raise s_exc.BadArg('Only vault names and permissions can be changed.')
7069
7208
 
7070
7209
  vault = self.reqVault(iden)
7210
+ oldv = vault.get(key)
7071
7211
  vault[key] = valu
7072
7212
 
7073
7213
  s_schemas.reqValidVault(vault)
@@ -7075,8 +7215,7 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
7075
7215
  bidn = s_common.uhex(iden)
7076
7216
 
7077
7217
  if key == 'name':
7078
- name = vault.get('name')
7079
- self.slab.delete(name.encode(), db=self.vaultsbynamedb)
7218
+ self.slab.delete(oldv.encode(), db=self.vaultsbynamedb)
7080
7219
  self.slab.put(valu.encode(), bidn, db=self.vaultsbynamedb)
7081
7220
 
7082
7221
  self.slab.put(bidn, s_msgpack.en(vault), db=self.vaultsdb)
synapse/datamodel.py CHANGED
@@ -30,6 +30,7 @@ class TagProp:
30
30
  self.info = info
31
31
  self.tdef = tdef
32
32
  self.model = model
33
+ self.locked = False
33
34
 
34
35
  self.utf8 = name.encode()
35
36
  self.nenc = name.encode() + b'\x00'
@@ -127,6 +128,10 @@ class Prop:
127
128
  self.deprecated = self.info.get('deprecated', False)
128
129
 
129
130
  self.type = self.modl.getTypeClone(typedef)
131
+ self.typehash = self.type.typehash
132
+
133
+ if self.type.isarray:
134
+ self.arraytypehash = self.type.arraytype.typehash
130
135
 
131
136
  if form is not None:
132
137
  form.setProp(name, self)
@@ -269,6 +274,11 @@ class Form:
269
274
  if self.type is None:
270
275
  raise s_exc.NoSuchType(name=name)
271
276
 
277
+ self.typehash = self.type.typehash
278
+
279
+ if self.type.isarray:
280
+ self.arraytypehash = self.type.arraytype.typehash
281
+
272
282
  self.form = self
273
283
 
274
284
  self.props = {} # name: Prop()
@@ -462,6 +472,7 @@ class Model:
462
472
  self.modeldefs = []
463
473
 
464
474
  self.univs = {}
475
+ self.allunivs = collections.defaultdict(list)
465
476
 
466
477
  self.propsbytype = collections.defaultdict(dict) # name: Prop()
467
478
  self.arraysbytype = collections.defaultdict(dict)
@@ -620,6 +631,30 @@ class Model:
620
631
  self.formprefixcache[prefix] = forms
621
632
  return forms
622
633
 
634
+ def reqProp(self, name):
635
+ prop = self.prop(name)
636
+ if prop is not None:
637
+ return prop
638
+
639
+ mesg = f'No property named {name}.'
640
+ raise s_exc.NoSuchProp(mesg=mesg, name=name)
641
+
642
+ def reqUniv(self, name):
643
+ prop = self.univ(name)
644
+ if prop is not None:
645
+ return prop
646
+
647
+ mesg = f'No universal property named {name}.'
648
+ raise s_exc.NoSuchUniv(mesg=mesg, name=name)
649
+
650
+ def reqTagProp(self, name):
651
+ prop = self.getTagProp(name)
652
+ if prop is not None:
653
+ return prop
654
+
655
+ mesg = f'No tag property named {name}.'
656
+ raise s_exc.NoSuchTagProp(mesg=mesg, name=name)
657
+
623
658
  def reqFormsByPrefix(self, prefix, extra=None):
624
659
  forms = self.getFormsByPrefix(prefix)
625
660
  if not forms:
@@ -965,13 +1000,18 @@ class Model:
965
1000
 
966
1001
  def _addFormUniv(self, form, name, tdef, info):
967
1002
 
1003
+ univ = self.reqUniv(name)
1004
+
968
1005
  prop = Prop(self, form, name, tdef, info)
1006
+ prop.locked = univ.locked
969
1007
 
970
1008
  full = f'{form.name}{name}'
971
1009
 
972
1010
  self.props[full] = prop
973
1011
  self.props[(form.name, name)] = prop
974
1012
 
1013
+ self.allunivs[name].append(prop)
1014
+
975
1015
  def addUnivProp(self, name, tdef, info):
976
1016
 
977
1017
  base = '.' + name
@@ -985,8 +1025,13 @@ class Model:
985
1025
  self.props[base] = univ
986
1026
  self.univs[base] = univ
987
1027
 
1028
+ self.allunivs[base].append(univ)
1029
+
988
1030
  for form in self.forms.values():
989
- self._addFormUniv(form, base, tdef, info)
1031
+ prop = self._addFormUniv(form, base, tdef, info)
1032
+
1033
+ def getAllUnivs(self, name):
1034
+ return list(self.allunivs.get(name, ()))
990
1035
 
991
1036
  def addFormProp(self, formname, propname, tdef, info):
992
1037
  form = self.forms.get(formname)
@@ -1090,6 +1135,7 @@ class Model:
1090
1135
  raise s_exc.NoSuchUniv(name=propname)
1091
1136
 
1092
1137
  self.univs.pop(univname, None)
1138
+ self.allunivs.pop(univname, None)
1093
1139
 
1094
1140
  for form in self.forms.values():
1095
1141
  self.delFormProp(form.name, univname)
synapse/exc.py CHANGED
@@ -77,6 +77,7 @@ class BackupAlreadyRunning(SynErr):
77
77
  class StormPkgRequires(SynErr): pass
78
78
  class StormPkgConflicts(SynErr): pass
79
79
 
80
+ class BadName(SynErr): pass
80
81
  class BadPkgDef(SynErr): pass
81
82
  class BadCmdName(SynErr): pass
82
83
  class BadCmprValu(SynErr): pass