synapse 2.221.0__py311-none-any.whl → 2.223.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 (46) hide show
  1. synapse/cortex.py +143 -44
  2. synapse/cryotank.py +1 -1
  3. synapse/data/lark/storm.lark +9 -6
  4. synapse/lib/ast.py +13 -5
  5. synapse/lib/layer.py +18 -11
  6. synapse/lib/nexus.py +1 -1
  7. synapse/lib/parser.py +1 -0
  8. synapse/lib/rstorm.py +19 -1
  9. synapse/lib/schemas.py +4 -0
  10. synapse/lib/snap.py +15 -9
  11. synapse/lib/storm.py +0 -190
  12. synapse/lib/stormlib/auth.py +1 -1
  13. synapse/lib/stormlib/cortex.py +1 -1
  14. synapse/lib/stormlib/mime.py +15 -5
  15. synapse/lib/stormlib/pkg.py +598 -0
  16. synapse/lib/stormlib/task.py +115 -0
  17. synapse/lib/stormtypes.py +42 -178
  18. synapse/lib/trigger.py +16 -14
  19. synapse/lib/version.py +2 -2
  20. synapse/lib/view.py +17 -14
  21. synapse/models/files.py +1 -1
  22. synapse/models/orgs.py +3 -0
  23. synapse/tests/test_cortex.py +1 -1
  24. synapse/tests/test_lib_aha.py +68 -53
  25. synapse/tests/test_lib_ast.py +3 -0
  26. synapse/tests/test_lib_cell.py +12 -12
  27. synapse/tests/test_lib_grammar.py +4 -4
  28. synapse/tests/test_lib_rstorm.py +55 -7
  29. synapse/tests/test_lib_storm.py +105 -249
  30. synapse/tests/test_lib_stormlib_auth.py +84 -0
  31. synapse/tests/test_lib_stormlib_cortex.py +1 -0
  32. synapse/tests/test_lib_stormlib_mime.py +24 -0
  33. synapse/tests/test_lib_stormlib_pkg.py +456 -0
  34. synapse/tests/test_lib_stormlib_task.py +98 -0
  35. synapse/tests/test_lib_stormtypes.py +25 -100
  36. synapse/tests/test_lib_trigger.py +66 -3
  37. synapse/tests/test_lib_view.py +53 -0
  38. synapse/tests/test_model_files.py +11 -0
  39. synapse/tests/test_model_orgs.py +6 -1
  40. synapse/tools/cryo/cat.py +2 -1
  41. synapse/tools/cryo/list.py +2 -0
  42. {synapse-2.221.0.dist-info → synapse-2.223.0.dist-info}/METADATA +1 -1
  43. {synapse-2.221.0.dist-info → synapse-2.223.0.dist-info}/RECORD +46 -42
  44. {synapse-2.221.0.dist-info → synapse-2.223.0.dist-info}/WHEEL +0 -0
  45. {synapse-2.221.0.dist-info → synapse-2.223.0.dist-info}/licenses/LICENSE +0 -0
  46. {synapse-2.221.0.dist-info → synapse-2.223.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,115 @@
1
+ import asyncio
2
+ import logging
3
+
4
+ import synapse.exc as s_exc
5
+ import synapse.lib.stormtypes as s_stormtypes
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+ stormcmds = [
10
+ {
11
+ 'name': 'task.list',
12
+ 'descr': 'List running tasks on the Cortex and any mirrors.',
13
+ 'cmdargs': (
14
+ ('--verbose', {'default': False, 'action': 'store_true', 'help': 'Enable verbose output.'}),
15
+ ),
16
+ 'storm': '''
17
+ $tcnt = (0)
18
+ for $task in $lib.task.list() {
19
+ $lib.print(`task iden: {$task.iden}`)
20
+ $lib.print(` name: {$task.name}`)
21
+ $lib.print(` user: {$task.username}`)
22
+ $lib.print(` service: {$task.service}`)
23
+ $lib.print(` start time: {$lib.time.format($task.tick, '%Y-%m-%d %H:%M:%S')}`)
24
+ $lib.print(' metadata:')
25
+ if $cmdopts.verbose {
26
+ $lib.pprint($task.info, prefix=' ')
27
+ } else {
28
+ $lib.pprint($task.info, prefix=' ', clamp=120)
29
+ }
30
+ $tcnt = ($tcnt + 1)
31
+ }
32
+
33
+ if ($tcnt = 1) {
34
+ $lib.print('1 task found.')
35
+ } else {
36
+ $lib.print(`{$tcnt} tasks found.`)
37
+ }
38
+ ''',
39
+ },
40
+ {
41
+ 'name': 'task.kill',
42
+ 'descr': 'Kill a running task on the Cortex or a mirror.',
43
+ 'cmdargs': (
44
+ ('iden', {'help': 'Any prefix that matches exactly one valid task iden is accepted.'}),
45
+ ),
46
+ 'storm': '''
47
+ $kild = $lib.task.kill($cmdopts.iden)
48
+ $lib.print("kill status: {kild}", kild=$kild)
49
+ ''',
50
+ },
51
+ ]
52
+
53
+ @s_stormtypes.registry.registerLib
54
+ class LibTask(s_stormtypes.Lib):
55
+ '''
56
+ A Storm Library for interacting with tasks on a Cortex and its mirrors.
57
+ '''
58
+ _storm_locals = (
59
+ {'name': 'list', 'desc': 'List tasks the current user can access on the Cortex and its mirrors.',
60
+ 'type': {'type': 'function', '_funcname': '_methTaskList', 'args': (),
61
+ 'returns': {'name': 'yields', 'type': 'dict', 'desc': 'Task definitions.'}}},
62
+ {'name': 'kill', 'desc': 'Stop a running task on the Cortex or a mirror.',
63
+ 'type': {'type': 'function', '_funcname': '_methTaskKill', 'args': (
64
+ {'name': 'prefix', 'type': 'str',
65
+ 'desc': 'The prefix of the task to stop. '
66
+ 'Tasks will only be stopped if there is a single prefix match.'},),
67
+ 'returns': {'type': 'boolean', 'desc': 'True if the task was cancelled, False otherwise.'}}},
68
+ )
69
+ _storm_lib_path = ('task',)
70
+ _storm_lib_perms = (
71
+ {'perm': ('task', 'get'), 'gate': 'cortex',
72
+ 'desc': 'Permits a user to view tasks owned by other users.'},
73
+ {'perm': ('task', 'del'), 'gate': 'cortex',
74
+ 'desc': 'Permits a user to kill tasks owned by other users.'},
75
+ )
76
+
77
+ def getObjLocals(self):
78
+ return {
79
+ 'list': self._methTaskList,
80
+ 'kill': self._methTaskKill,
81
+ }
82
+
83
+ @s_stormtypes.stormfunc(readonly=True)
84
+ async def _methTaskList(self):
85
+ useriden = self.runt.user.iden
86
+ isallowed = self.runt.allowed(('task', 'get'))
87
+
88
+ async for task in self.runt.snap.core.getTasks():
89
+ if isallowed or task['user'] == useriden:
90
+ yield task
91
+
92
+ async def _methTaskKill(self, prefix):
93
+ useriden = self.runt.user.iden
94
+ isallowed = self.runt.allowed(('task', 'del'))
95
+
96
+ prefix = await s_stormtypes.tostr(prefix)
97
+ if not prefix:
98
+ mesg = 'An empty task iden prefix is not allowed.'
99
+ raise s_exc.StormRuntimeError(mesg=mesg, iden=prefix)
100
+
101
+ iden = None
102
+ async for task in self.runt.snap.core.getTasks():
103
+ taskiden = task['iden']
104
+ if (isallowed or task['user'] == useriden) and taskiden.startswith(prefix):
105
+ if iden is None:
106
+ iden = taskiden
107
+ else:
108
+ mesg = 'Provided iden matches more than one task.'
109
+ raise s_exc.StormRuntimeError(mesg=mesg, iden=prefix)
110
+
111
+ if iden is None:
112
+ mesg = 'Provided iden does not match any tasks.'
113
+ raise s_exc.StormRuntimeError(mesg=mesg, iden=prefix)
114
+
115
+ return await self.runt.snap.core.killTask(iden)
synapse/lib/stormtypes.py CHANGED
@@ -699,117 +699,6 @@ class Lib(StormType):
699
699
  async for item in self.runt.dyniter(iden, todo, gatekeys=gatekeys):
700
700
  yield item
701
701
 
702
- @registry.registerLib
703
- class LibPkg(Lib):
704
- '''
705
- A Storm Library for interacting with Storm Packages.
706
- '''
707
- _storm_locals = (
708
- {'name': 'add', 'desc': 'Add a Storm Package to the Cortex.',
709
- 'type': {'type': 'function', '_funcname': '_libPkgAdd',
710
- 'args': (
711
- {'name': 'pkgdef', 'type': 'dict', 'desc': 'A Storm Package definition.', },
712
- {'name': 'verify', 'type': 'boolean', 'default': False,
713
- 'desc': 'Verify storm package signature.', },
714
- ),
715
- 'returns': {'type': 'null', }}},
716
- {'name': 'get', 'desc': 'Get a Storm Package from the Cortex.',
717
- 'type': {'type': 'function', '_funcname': '_libPkgGet',
718
- 'args': (
719
- {'name': 'name', 'type': 'str', 'desc': 'A Storm Package name.', },
720
- ),
721
- 'returns': {'type': 'dict', 'desc': 'The Storm package definition.', }}},
722
- {'name': 'has', 'desc': 'Check if a Storm Package is available in the Cortex.',
723
- 'type': {'type': 'function', '_funcname': '_libPkgHas',
724
- 'args': (
725
- {'name': 'name', 'type': 'str',
726
- 'desc': 'A Storm Package name to check for the existence of.', },
727
- ),
728
- 'returns': {'type': 'boolean',
729
- 'desc': 'True if the package exists in the Cortex, False if it does not.', }}},
730
- {'name': 'del', 'desc': 'Delete a Storm Package from the Cortex.',
731
- 'type': {'type': 'function', '_funcname': '_libPkgDel',
732
- 'args': (
733
- {'name': 'name', 'type': 'str', 'desc': 'The name of the package to delete.', },
734
- ),
735
- 'returns': {'type': 'null', }}},
736
- {'name': 'list', 'desc': 'Get a list of Storm Packages loaded in the Cortex.',
737
- 'type': {'type': 'function', '_funcname': '_libPkgList',
738
- 'returns': {'type': 'list', 'desc': 'A list of Storm Package definitions.', }}},
739
- {'name': 'deps', 'desc': 'Verify the dependencies for a Storm Package.',
740
- 'type': {'type': 'function', '_funcname': '_libPkgDeps',
741
- 'args': (
742
- {'name': 'pkgdef', 'type': 'dict', 'desc': 'A Storm Package definition.', },
743
- ),
744
- 'returns': {'type': 'dict', 'desc': 'A dictionary listing dependencies and if they are met.', }}},
745
- {'name': 'vars',
746
- 'desc': "Get a dictionary representing the package's persistent variables.",
747
- 'type': {'type': 'function', '_funcname': '_libPkgVars',
748
- 'args': (
749
- {'name': 'name', 'type': 'str',
750
- 'desc': 'A Storm Package name to get vars for.', },
751
- ),
752
- 'returns': {'type': 'pkg:vars', 'desc': 'A dictionary representing the package variables.', }}},
753
- )
754
- _storm_lib_perms = (
755
- {'perm': ('power-ups', '<name>', 'admin'), 'gate': 'cortex',
756
- 'desc': 'Controls the ability to interact with the vars for a Storm Package by name.'},
757
- )
758
- _storm_lib_path = ('pkg',)
759
-
760
- def getObjLocals(self):
761
- return {
762
- 'add': self._libPkgAdd,
763
- 'get': self._libPkgGet,
764
- 'has': self._libPkgHas,
765
- 'del': self._libPkgDel,
766
- 'list': self._libPkgList,
767
- 'deps': self._libPkgDeps,
768
- 'vars': self._libPkgVars,
769
- }
770
-
771
- async def _libPkgAdd(self, pkgdef, verify=False):
772
- self.runt.confirm(('pkg', 'add'), None)
773
- pkgdef = await toprim(pkgdef)
774
- verify = await tobool(verify)
775
- await self.runt.snap.core.addStormPkg(pkgdef, verify=verify)
776
-
777
- @stormfunc(readonly=True)
778
- async def _libPkgGet(self, name):
779
- name = await tostr(name)
780
- pkgdef = await self.runt.snap.core.getStormPkg(name)
781
- if pkgdef is None:
782
- return None
783
-
784
- return Dict(pkgdef)
785
-
786
- @stormfunc(readonly=True)
787
- async def _libPkgHas(self, name):
788
- name = await tostr(name)
789
- pkgdef = await self.runt.snap.core.getStormPkg(name)
790
- if pkgdef is None:
791
- return False
792
- return True
793
-
794
- async def _libPkgDel(self, name):
795
- self.runt.confirm(('pkg', 'del'), None)
796
- await self.runt.snap.core.delStormPkg(name)
797
-
798
- @stormfunc(readonly=True)
799
- async def _libPkgList(self):
800
- pkgs = await self.runt.snap.core.getStormPkgs()
801
- return list(sorted(pkgs, key=lambda x: x.get('name')))
802
-
803
- @stormfunc(readonly=True)
804
- async def _libPkgDeps(self, pkgdef):
805
- pkgdef = await toprim(pkgdef)
806
- return await self.runt.snap.core.verifyStormPkgDeps(pkgdef)
807
-
808
- async def _libPkgVars(self, name):
809
- name = await tostr(name)
810
- confirm(('power-ups', name, 'admin'))
811
- return PkgVars(self.runt, name)
812
-
813
702
  @registry.registerLib
814
703
  class LibDmon(Lib):
815
704
  '''
@@ -1595,8 +1484,16 @@ class LibBase(Lib):
1595
1484
 
1596
1485
  if not asroot:
1597
1486
  permtext = ' or '.join(('.'.join(p) for p in rootperms))
1598
- mesg = f'Module ({name}) requires permission: {permtext}'
1599
- raise s_exc.AuthDeny(mesg=mesg, user=self.runt.user.iden, username=self.runt.user.name)
1487
+
1488
+ match mdef.get('asroot:ondeny:import', 'deny'):
1489
+ case 'allow':
1490
+ pass
1491
+ case 'warn':
1492
+ mesg = f'Module ({name}) permissions will not be elevated. Missing permission: {permtext}.'
1493
+ await self.runt.warnonce(mesg, log=False)
1494
+ case _:
1495
+ mesg = f'Module ({name}) requires permission: {permtext}'
1496
+ raise s_exc.AuthDeny(mesg=mesg, user=self.runt.user.iden, username=self.runt.user.name)
1600
1497
 
1601
1498
  else:
1602
1499
  perm = ('storm', 'asroot', 'mod') + tuple(name.split('.'))
@@ -3948,22 +3845,22 @@ class LibQueue(Lib):
3948
3845
  {'name': 'add', 'desc': 'Add a Queue to the Cortex with a given name.',
3949
3846
  'type': {'type': 'function', '_funcname': '_methQueueAdd',
3950
3847
  'args': (
3951
- {'name': 'name', 'type': 'str', 'desc': 'The name of the queue to add.', },
3848
+ {'name': 'name', 'type': 'str', 'desc': 'The name of the Queue to add.', },
3952
3849
  ),
3953
3850
  'returns': {'type': 'queue', }}},
3954
- {'name': 'gen', 'desc': 'Add or get a Storm Queue in a single operation.',
3851
+ {'name': 'gen', 'desc': 'Add or get a Queue in a single operation.',
3955
3852
  'type': {'type': 'function', '_funcname': '_methQueueGen',
3956
3853
  'args': (
3957
3854
  {'name': 'name', 'type': 'str', 'desc': 'The name of the Queue to add or get.', },
3958
3855
  ),
3959
3856
  'returns': {'type': 'queue', }}},
3960
- {'name': 'del', 'desc': 'Delete a given named Queue.',
3857
+ {'name': 'del', 'desc': 'Delete a given Queue.',
3961
3858
  'type': {'type': 'function', '_funcname': '_methQueueDel',
3962
3859
  'args': (
3963
- {'name': 'name', 'type': 'str', 'desc': 'The name of the queue to delete.', },
3860
+ {'name': 'name', 'type': 'str', 'desc': 'The name of the Queue to delete.', },
3964
3861
  ),
3965
3862
  'returns': {'type': 'null', }}},
3966
- {'name': 'get', 'desc': 'Get an existing Storm Queue object.',
3863
+ {'name': 'get', 'desc': 'Get an existing Queue.',
3967
3864
  'type': {'type': 'function', '_funcname': '_methQueueGet',
3968
3865
  'args': (
3969
3866
  {'name': 'name', 'type': 'str', 'desc': 'The name of the Queue to get.', },
@@ -3972,17 +3869,17 @@ class LibQueue(Lib):
3972
3869
  {'name': 'list', 'desc': 'Get a list of the Queues in the Cortex.',
3973
3870
  'type': {'type': 'function', '_funcname': '_methQueueList',
3974
3871
  'returns': {'type': 'list',
3975
- 'desc': 'A list of queue definitions the current user is allowed to interact with.', }}},
3872
+ 'desc': 'A list of Queue definitions the current user is allowed to interact with.', }}},
3976
3873
  )
3977
3874
  _storm_lib_perms = (
3978
3875
  {'perm': ('queue', 'add'), 'gate': 'cortex',
3979
- 'desc': 'Permits a user to create a named queue.'},
3876
+ 'desc': 'Permits a user to create a Queue.'},
3980
3877
  {'perm': ('queue', 'get'), 'gate': 'queue',
3981
- 'desc': 'Permits a user to access a queue. This allows the user to read from the queue and remove items from it.'},
3878
+ 'desc': 'Permits a user to access a Queue. This allows the user to read from the Queue and remove items from it.'},
3982
3879
  {'perm': ('queue', 'put'), 'gate': 'queue',
3983
- 'desc': 'Permits a user to put items into a queue.'},
3880
+ 'desc': 'Permits a user to put items into a Queue.'},
3984
3881
  {'perm': ('queue', 'del'), 'gate': 'queue',
3985
- 'desc': 'Permits a user to delete a queue.'},
3882
+ 'desc': 'Permits a user to delete a Queue.'},
3986
3883
  )
3987
3884
  _storm_lib_path = ('queue',)
3988
3885
 
@@ -4045,7 +3942,7 @@ class LibQueue(Lib):
4045
3942
  @registry.registerType
4046
3943
  class Queue(StormType):
4047
3944
  '''
4048
- A StormLib API instance of a named channel in the Cortex multiqueue.
3945
+ A StormLib API instance of a named channel in the Cortex MultiQueue.
4049
3946
  '''
4050
3947
  _storm_locals = (
4051
3948
  {'name': 'name', 'desc': 'The name of the Queue.', 'type': 'str', },
@@ -4059,48 +3956,48 @@ class Queue(StormType):
4059
3956
  'desc': 'Wait for the offset to be available before returning the item.', },
4060
3957
  ),
4061
3958
  'returns': {'type': 'list',
4062
- 'desc': 'A tuple of the offset and the item from the queue. If wait is false and '
3959
+ 'desc': 'A tuple of the offset and the item from the Queue. If wait is false and '
4063
3960
  'the offset is not present, null is returned.', }}},
4064
- {'name': 'pop', 'desc': 'Pop a item from the Queue at a specific offset.',
3961
+ {'name': 'pop', 'desc': 'Pop an item from the Queue at a specific offset.',
4065
3962
  'type': {'type': 'function', '_funcname': '_methQueuePop',
4066
3963
  'args': (
4067
3964
  {'name': 'offs', 'type': 'int', 'default': None,
4068
- 'desc': 'Offset to pop the item from. If not specified, the first item in the queue will be'
3965
+ 'desc': 'Offset to pop the item from. If not specified, the first item in the Queue will be'
4069
3966
  ' popped.', },
4070
3967
  {'name': 'wait', 'type': 'boolean', 'default': False,
4071
3968
  'desc': 'Wait for an item to be available to pop.'},
4072
3969
  ),
4073
3970
  'returns': {'type': 'list',
4074
- 'desc': 'The offset and item popped from the queue. If there is no item at the '
4075
- 'offset or the queue is empty and wait is false, it returns null.', }}},
4076
- {'name': 'put', 'desc': 'Put an item into the queue.',
3971
+ 'desc': 'The offset and item popped from the Queue. If there is no item at the '
3972
+ 'offset or the Queue is empty and wait is false, it returns null.', }}},
3973
+ {'name': 'put', 'desc': 'Put an item into the Queue.',
4077
3974
  'type': {'type': 'function', '_funcname': '_methQueuePut',
4078
3975
  'args': (
4079
- {'name': 'item', 'type': 'prim', 'desc': 'The item being put into the queue.', },
3976
+ {'name': 'item', 'type': 'prim', 'desc': 'The item being put into the Queue.', },
4080
3977
  ),
4081
- 'returns': {'type': 'int', 'desc': 'The queue offset of the item.'}}},
3978
+ 'returns': {'type': 'int', 'desc': 'The Queue offset of the item.'}}},
4082
3979
  {'name': 'puts', 'desc': 'Put multiple items into the Queue.',
4083
3980
  'type': {'type': 'function', '_funcname': '_methQueuePuts',
4084
3981
  'args': (
4085
3982
  {'name': 'items', 'type': 'list', 'desc': 'The items to put into the Queue.', },
4086
3983
  ),
4087
- 'returns': {'type': 'int', 'desc': 'The queue offset of the first item.'}}},
3984
+ 'returns': {'type': 'int', 'desc': 'The Queue offset of the first item.'}}},
4088
3985
  {'name': 'gets', 'desc': 'Get multiple items from the Queue as a iterator.',
4089
3986
  'type': {'type': 'function', '_funcname': '_methQueueGets',
4090
3987
  'args': (
4091
- {'name': 'offs', 'type': 'int', 'desc': 'The offset to retrieve an items from.', 'default': 0, },
3988
+ {'name': 'offs', 'type': 'int', 'desc': 'The offset to retrieve items from.', 'default': 0, },
4092
3989
  {'name': 'wait', 'type': 'boolean', 'default': True,
4093
3990
  'desc': 'Wait for the offset to be available before returning the item.', },
4094
3991
  {'name': 'cull', 'type': 'boolean', 'default': False,
4095
3992
  'desc': 'Culls items up to, but not including, the specified offset.', },
4096
- {'name': 'size', 'type': 'int', 'desc': 'The maximum number of items to yield',
3993
+ {'name': 'size', 'type': 'int', 'desc': 'The maximum number of items to yield.',
4097
3994
  'default': None, },
4098
3995
  ),
4099
3996
  'returns': {'name': 'Yields', 'type': 'list', 'desc': 'Yields tuples of the offset and item.', }}},
4100
- {'name': 'cull', 'desc': 'Remove items from the queue up to, and including, the offset.',
3997
+ {'name': 'cull', 'desc': 'Remove items from the Queue up to, and including, the offset.',
4101
3998
  'type': {'type': 'function', '_funcname': '_methQueueCull',
4102
3999
  'args': (
4103
- {'name': 'offs', 'type': 'int', 'desc': 'The offset which to cull records from the queue.', },
4000
+ {'name': 'offs', 'type': 'int', 'desc': 'The offset which to cull records from the Queue.', },
4104
4001
  ),
4105
4002
  'returns': {'type': 'null', }}},
4106
4003
  {'name': 'size', 'desc': 'Get the number of items in the Queue.',
@@ -6088,45 +5985,6 @@ class LibVars(Lib):
6088
5985
  async def _libVarsType(self, valu):
6089
5986
  return await totype(valu)
6090
5987
 
6091
- @registry.registerType
6092
- class PkgVars(Prim):
6093
- '''
6094
- The Storm deref/setitem/iter convention on top of pkg vars information.
6095
- '''
6096
- _storm_typename = 'pkg:vars'
6097
- _ismutable = True
6098
-
6099
- def __init__(self, runt, valu, path=None):
6100
- Prim.__init__(self, valu, path=path)
6101
- self.runt = runt
6102
-
6103
- def _reqPkgAdmin(self):
6104
- confirm(('power-ups', self.valu, 'admin'))
6105
-
6106
- @stormfunc(readonly=True)
6107
- async def deref(self, name):
6108
- self._reqPkgAdmin()
6109
- name = await tostr(name)
6110
- return await self.runt.snap.core.getStormPkgVar(self.valu, name)
6111
-
6112
- async def setitem(self, name, valu):
6113
- self._reqPkgAdmin()
6114
- name = await tostr(name)
6115
-
6116
- if valu is undef:
6117
- await self.runt.snap.core.popStormPkgVar(self.valu, name)
6118
- return
6119
-
6120
- valu = await toprim(valu)
6121
- await self.runt.snap.core.setStormPkgVar(self.valu, name, valu)
6122
-
6123
- @stormfunc(readonly=True)
6124
- async def iter(self):
6125
- self._reqPkgAdmin()
6126
- async for name, valu in self.runt.snap.core.iterStormPkgVars(self.valu):
6127
- yield name, valu
6128
- await asyncio.sleep(0)
6129
-
6130
5988
  @registry.registerType
6131
5989
  class Query(Prim):
6132
5990
  '''
@@ -7837,7 +7695,8 @@ class Layer(Prim):
7837
7695
 
7838
7696
  layr = self.runt.snap.core.reqLayer(self.valu.get('iden'))
7839
7697
 
7840
- self.runt.confirm(('layer', 'edits', 'read'), gateiden=layr.iden)
7698
+ if not self.runt.allowed(('layer', 'edits', 'read'), gateiden=layr.iden):
7699
+ self.runt.confirm(('layer', 'read'), gateiden=layr.iden)
7841
7700
 
7842
7701
  if reverse:
7843
7702
  wait = False
@@ -10439,5 +10298,10 @@ async def totype(valu, basetypes=False) -> str:
10439
10298
  async def typeerr(name, reqt):
10440
10299
  if not isinstance(name, reqt):
10441
10300
  styp = await totype(name, basetypes=True)
10442
- mesg = f"Expected value of type '{reqt}', got '{styp}' with value {name}."
10301
+
10302
+ reqtname = str(reqt)
10303
+ if (clsname := getattr(reqt, '__name__')):
10304
+ reqtname = clsname
10305
+
10306
+ mesg = f"Expected value of type '{reqtname}', got '{styp}' with value {name}."
10443
10307
  return s_exc.StormRuntimeError(mesg=mesg, name=name, type=styp)
synapse/lib/trigger.py CHANGED
@@ -136,26 +136,28 @@ class Triggers:
136
136
  finally:
137
137
  RecursionDepth.reset(token)
138
138
 
139
- async def runNodeAdd(self, node):
139
+ async def runNodeAdd(self, node, useriden):
140
+ vars = {'auto': {'opts': {'user': useriden}}}
140
141
  with self._recursion_check():
141
- [await trig.execute(node) for trig in self.nodeadd.get(node.form.name, ())]
142
+ [await trig.execute(node, vars=vars) for trig in self.nodeadd.get(node.form.name, ())]
142
143
 
143
- async def runNodeDel(self, node):
144
+ async def runNodeDel(self, node, useriden):
145
+ vars = {'auto': {'opts': {'user': useriden}}}
144
146
  with self._recursion_check():
145
- [await trig.execute(node) for trig in self.nodedel.get(node.form.name, ())]
147
+ [await trig.execute(node, vars=vars) for trig in self.nodedel.get(node.form.name, ())]
146
148
 
147
- async def runPropSet(self, node, prop, oldv):
149
+ async def runPropSet(self, node, prop, oldv, useriden):
148
150
  vars = {'propname': prop.name, 'propfull': prop.full,
149
- 'auto': {'opts': {'propname': prop.name, 'propfull': prop.full, }},
151
+ 'auto': {'opts': {'propname': prop.name, 'propfull': prop.full, 'user': useriden}},
150
152
  }
151
153
  with self._recursion_check():
152
154
  [await trig.execute(node, vars=vars) for trig in self.propset.get(prop.full, ())]
153
155
  if prop.univ is not None:
154
156
  [await trig.execute(node, vars=vars) for trig in self.propset.get(prop.univ.full, ())]
155
157
 
156
- async def runTagAdd(self, node, tag):
158
+ async def runTagAdd(self, node, tag, useriden):
157
159
 
158
- vars = {'tag': tag, 'auto': {'opts': {'tag': tag}}}
160
+ vars = {'tag': tag, 'auto': {'opts': {'tag': tag, 'user': useriden}}}
159
161
  with self._recursion_check():
160
162
 
161
163
  for trig in self.tagadd.get((node.form.name, tag), ()):
@@ -176,10 +178,10 @@ class Triggers:
176
178
  for _, trig in globs.get(tag):
177
179
  await trig.execute(node, vars=vars)
178
180
 
179
- async def runTagDel(self, node, tag):
181
+ async def runTagDel(self, node, tag, useriden):
180
182
 
181
183
  vars = {'tag': tag,
182
- 'auto': {'opts': {'tag': tag}},
184
+ 'auto': {'opts': {'tag': tag, 'user': useriden}},
183
185
  }
184
186
  with self._recursion_check():
185
187
 
@@ -201,11 +203,11 @@ class Triggers:
201
203
  for _, trig in globs.get(tag):
202
204
  await trig.execute(node, vars=vars)
203
205
 
204
- async def runEdgeAdd(self, n1, verb, n2):
206
+ async def runEdgeAdd(self, n1, verb, n2, useriden):
205
207
  n1form = n1.form.name if n1 else None
206
208
  n2form = n2.form.name if n2 else None
207
209
  n2iden = n2.iden() if n2 else None
208
- varz = {'auto': {'opts': {'verb': verb, 'n2iden': n2iden}}}
210
+ varz = {'auto': {'opts': {'verb': verb, 'n2iden': n2iden, 'user': useriden}}}
209
211
  with self._recursion_check():
210
212
  cachekey = (n1form, verb, n2form)
211
213
  cached = self.edgeaddcache.get(cachekey)
@@ -251,11 +253,11 @@ class Triggers:
251
253
  for trig in cached:
252
254
  await trig.execute(n1, vars=varz)
253
255
 
254
- async def runEdgeDel(self, n1, verb, n2):
256
+ async def runEdgeDel(self, n1, verb, n2, useriden):
255
257
  n1form = n1.form.name if n1 else None
256
258
  n2form = n2.form.name if n2 else None
257
259
  n2iden = n2.iden() if n2 else None
258
- varz = {'auto': {'opts': {'verb': verb, 'n2iden': n2iden}}}
260
+ varz = {'auto': {'opts': {'verb': verb, 'n2iden': n2iden, 'user': useriden}}}
259
261
  with self._recursion_check():
260
262
  cachekey = (n1form, verb, n2form)
261
263
  cached = self.edgedelcache.get(cachekey)
synapse/lib/version.py CHANGED
@@ -223,6 +223,6 @@ def reqVersion(valu, reqver,
223
223
  ##############################################################################
224
224
  # The following are touched during the release process by bumpversion.
225
225
  # Do not modify these directly.
226
- version = (2, 221, 0)
226
+ version = (2, 223, 0)
227
227
  verstring = '.'.join([str(x) for x in version])
228
- commit = '6ae7cd60524df7dc861bb91fe25309999eba6d70'
228
+ commit = 'b40e64b75fa2820856ad138b8ad9f8c4b6e9547e'
synapse/lib/view.py CHANGED
@@ -58,6 +58,9 @@ class ViewApi(s_cell.CellApi):
58
58
  @s_cell.adminapi()
59
59
  async def saveNodeEdits(self, edits, meta):
60
60
  meta['link:user'] = self.user.iden
61
+ user = meta.get('user', '')
62
+ if not s_common.isguid(user):
63
+ raise s_exc.BadArg(mesg=f'Meta argument requires user key to be a guid, got {user=}')
61
64
  async with await self.view.snap(user=self.user) as snap:
62
65
  return await snap.saveNodeEdits(edits, meta)
63
66
 
@@ -1584,57 +1587,57 @@ class View(s_nexus.Pusher): # type: ignore
1584
1587
  layer = self.layers[0]
1585
1588
  await layer.confirmLayerEditPerms(user, layer.iden, delete=True)
1586
1589
 
1587
- async def runTagAdd(self, node, tag, valu):
1590
+ async def runTagAdd(self, node, tag, valu, useriden):
1588
1591
 
1589
1592
  if self.core.migration or self.core.safemode:
1590
1593
  return
1591
1594
 
1592
1595
  # Run any trigger handlers
1593
- await self.triggers.runTagAdd(node, tag)
1596
+ await self.triggers.runTagAdd(node, tag, useriden)
1594
1597
 
1595
- async def runTagDel(self, node, tag, valu):
1598
+ async def runTagDel(self, node, tag, valu, useriden):
1596
1599
 
1597
1600
  if self.core.migration or self.core.safemode:
1598
1601
  return
1599
1602
 
1600
- await self.triggers.runTagDel(node, tag)
1603
+ await self.triggers.runTagDel(node, tag, useriden)
1601
1604
 
1602
- async def runNodeAdd(self, node):
1605
+ async def runNodeAdd(self, node, useriden):
1603
1606
 
1604
1607
  if self.core.migration or self.core.safemode:
1605
1608
  return
1606
1609
 
1607
- await self.triggers.runNodeAdd(node)
1610
+ await self.triggers.runNodeAdd(node, useriden)
1608
1611
 
1609
- async def runNodeDel(self, node):
1612
+ async def runNodeDel(self, node, useriden):
1610
1613
 
1611
1614
  if self.core.migration or self.core.safemode:
1612
1615
  return
1613
1616
 
1614
- await self.triggers.runNodeDel(node)
1617
+ await self.triggers.runNodeDel(node, useriden)
1615
1618
 
1616
- async def runPropSet(self, node, prop, oldv):
1619
+ async def runPropSet(self, node, prop, oldv, useriden):
1617
1620
  '''
1618
1621
  Handle when a prop set trigger event fired
1619
1622
  '''
1620
1623
  if self.core.migration or self.core.safemode:
1621
1624
  return
1622
1625
 
1623
- await self.triggers.runPropSet(node, prop, oldv)
1626
+ await self.triggers.runPropSet(node, prop, oldv, useriden)
1624
1627
 
1625
- async def runEdgeAdd(self, n1, edge, n2):
1628
+ async def runEdgeAdd(self, n1, edge, n2, useriden):
1626
1629
 
1627
1630
  if self.core.migration or self.core.safemode:
1628
1631
  return
1629
1632
 
1630
- await self.triggers.runEdgeAdd(n1, edge, n2)
1633
+ await self.triggers.runEdgeAdd(n1, edge, n2, useriden)
1631
1634
 
1632
- async def runEdgeDel(self, n1, edge, n2):
1635
+ async def runEdgeDel(self, n1, edge, n2, useriden):
1633
1636
 
1634
1637
  if self.core.migration or self.core.safemode:
1635
1638
  return
1636
1639
 
1637
- await self.triggers.runEdgeDel(n1, edge, n2)
1640
+ await self.triggers.runEdgeDel(n1, edge, n2, useriden)
1638
1641
 
1639
1642
  async def addTrigger(self, tdef):
1640
1643
  '''
synapse/models/files.py CHANGED
@@ -390,7 +390,7 @@ class FileModule(s_module.CoreModule):
390
390
  'doc': 'The typecode for the resource.',
391
391
  }),
392
392
 
393
- ('pe:langid', ('int', {'enums': s_l_pe.getLangCodes()}), {
393
+ ('pe:langid', ('int', {'min': 0, 'max': 0xffff, 'enums': s_l_pe.getLangCodes(), 'enums:strict': False}), {
394
394
  'doc': 'The PE language id.',
395
395
  }),
396
396
 
synapse/models/orgs.py CHANGED
@@ -729,6 +729,9 @@ class OuModule(s_module.CoreModule):
729
729
 
730
730
  ('ext:id', ('str', {'strip': True}), {
731
731
  'doc': 'An external identifier for the technique.'}),
732
+
733
+ ('parent', ('ou:technique', {}), {
734
+ 'doc': 'The parent technique for the technique.'}),
732
735
  )),
733
736
  ('ou:technique:taxonomy', {}, ()),
734
737
  ('ou:orgtype', {}, ()),
@@ -426,7 +426,7 @@ class CortexTest(s_t_utils.SynTest):
426
426
  self.len(1, await core00.nodes('test:str=foo', opts=view00opts))
427
427
 
428
428
  layr = core01.getLayer(layr01iden)
429
- await layr.storNodeEdits((), {})
429
+ await layr.storNodeEdits((), {'user': s_common.guid()})
430
430
 
431
431
  async def test_cortex_must_upgrade(self):
432
432