synapse 2.175.0__py311-none-any.whl → 2.177.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/axon.py +24 -9
  2. synapse/cortex.py +330 -168
  3. synapse/cryotank.py +46 -37
  4. synapse/datamodel.py +17 -4
  5. synapse/exc.py +19 -0
  6. synapse/lib/agenda.py +7 -13
  7. synapse/lib/ast.py +6 -5
  8. synapse/lib/auth.py +1520 -0
  9. synapse/lib/cell.py +255 -53
  10. synapse/lib/grammar.py +5 -0
  11. synapse/lib/hive.py +24 -3
  12. synapse/lib/hiveauth.py +6 -32
  13. synapse/lib/layer.py +7 -4
  14. synapse/lib/link.py +21 -17
  15. synapse/lib/lmdbslab.py +149 -0
  16. synapse/lib/modelrev.py +1 -1
  17. synapse/lib/schemas.py +136 -0
  18. synapse/lib/storm.py +70 -33
  19. synapse/lib/stormlib/aha.py +1 -1
  20. synapse/lib/stormlib/auth.py +185 -10
  21. synapse/lib/stormlib/cortex.py +16 -5
  22. synapse/lib/stormlib/gen.py +80 -0
  23. synapse/lib/stormlib/model.py +55 -0
  24. synapse/lib/stormlib/modelext.py +60 -0
  25. synapse/lib/stormlib/storm.py +117 -5
  26. synapse/lib/stormlib/tabular.py +212 -0
  27. synapse/lib/stormtypes.py +14 -1
  28. synapse/lib/trigger.py +1 -1
  29. synapse/lib/version.py +2 -2
  30. synapse/lib/view.py +55 -28
  31. synapse/models/base.py +7 -0
  32. synapse/models/biz.py +4 -0
  33. synapse/models/files.py +8 -1
  34. synapse/models/inet.py +31 -0
  35. synapse/tests/files/changelog/model_2.176.0_16ee721a6b7221344eaf946c3ab4602dda546b1a.yaml.gz +0 -0
  36. synapse/tests/files/changelog/model_2.176.0_2a25c58bbd344716cd7cbc3f4304d8925b0f4ef2.yaml.gz +0 -0
  37. synapse/tests/test_axon.py +7 -4
  38. synapse/tests/test_cortex.py +127 -82
  39. synapse/tests/test_cryotank.py +4 -4
  40. synapse/tests/test_datamodel.py +7 -0
  41. synapse/tests/test_lib_agenda.py +7 -0
  42. synapse/tests/{test_lib_hiveauth.py → test_lib_auth.py} +314 -11
  43. synapse/tests/test_lib_cell.py +161 -8
  44. synapse/tests/test_lib_httpapi.py +18 -14
  45. synapse/tests/test_lib_layer.py +33 -33
  46. synapse/tests/test_lib_link.py +42 -1
  47. synapse/tests/test_lib_lmdbslab.py +68 -0
  48. synapse/tests/test_lib_nexus.py +4 -4
  49. synapse/tests/test_lib_node.py +0 -7
  50. synapse/tests/test_lib_storm.py +45 -0
  51. synapse/tests/test_lib_stormlib_aha.py +1 -2
  52. synapse/tests/test_lib_stormlib_auth.py +21 -0
  53. synapse/tests/test_lib_stormlib_cortex.py +12 -12
  54. synapse/tests/test_lib_stormlib_gen.py +99 -0
  55. synapse/tests/test_lib_stormlib_model.py +108 -0
  56. synapse/tests/test_lib_stormlib_modelext.py +64 -0
  57. synapse/tests/test_lib_stormlib_storm.py +82 -1
  58. synapse/tests/test_lib_stormlib_tabular.py +226 -0
  59. synapse/tests/test_lib_stormsvc.py +4 -1
  60. synapse/tests/test_lib_stormtypes.py +10 -0
  61. synapse/tests/test_model_base.py +3 -0
  62. synapse/tests/test_model_biz.py +3 -0
  63. synapse/tests/test_model_files.py +12 -2
  64. synapse/tests/test_model_inet.py +55 -0
  65. synapse/tests/test_tools_changelog.py +196 -0
  66. synapse/tests/test_tools_healthcheck.py +4 -3
  67. synapse/tests/utils.py +1 -1
  68. synapse/tools/changelog.py +774 -15
  69. {synapse-2.175.0.dist-info → synapse-2.177.0.dist-info}/METADATA +3 -3
  70. {synapse-2.175.0.dist-info → synapse-2.177.0.dist-info}/RECORD +73 -67
  71. {synapse-2.175.0.dist-info → synapse-2.177.0.dist-info}/WHEEL +1 -1
  72. {synapse-2.175.0.dist-info → synapse-2.177.0.dist-info}/LICENSE +0 -0
  73. {synapse-2.175.0.dist-info → synapse-2.177.0.dist-info}/top_level.txt +0 -0
synapse/lib/storm.py CHANGED
@@ -12,6 +12,7 @@ import synapse.telepath as s_telepath
12
12
  import synapse.datamodel as s_datamodel
13
13
 
14
14
  import synapse.lib.ast as s_ast
15
+ import synapse.lib.auth as s_auth
15
16
  import synapse.lib.base as s_base
16
17
  import synapse.lib.chop as s_chop
17
18
  import synapse.lib.coro as s_coro
@@ -28,7 +29,6 @@ import synapse.lib.msgpack as s_msgpack
28
29
  import synapse.lib.spooled as s_spooled
29
30
  import synapse.lib.version as s_version
30
31
  import synapse.lib.hashitem as s_hashitem
31
- import synapse.lib.hiveauth as s_hiveauth
32
32
  import synapse.lib.stormctrl as s_stormctrl
33
33
  import synapse.lib.stormtypes as s_stormtypes
34
34
 
@@ -1066,7 +1066,7 @@ stormcmds = (
1066
1066
  $lib.view.del($view.iden)
1067
1067
  $lib.layer.del($layriden)
1068
1068
  } else {
1069
- $view.wipeLayer()
1069
+ $view.swapLayer()
1070
1070
  }
1071
1071
  $lib.print("View merged: {iden}", iden=$cmdopts.iden)
1072
1072
  ''',
@@ -1831,7 +1831,7 @@ class Runtime(s_base.Base):
1831
1831
 
1832
1832
  '''
1833
1833
 
1834
- _admin_reason = s_hiveauth._allowedReason(True, isadmin=True)
1834
+ _admin_reason = s_auth._allowedReason(True, isadmin=True)
1835
1835
  async def __anit__(self, query, snap, opts=None, user=None, root=None):
1836
1836
 
1837
1837
  await s_base.Base.__anit__(self)
@@ -1874,6 +1874,14 @@ class Runtime(s_base.Base):
1874
1874
  valu.incref()
1875
1875
  self.vars.update(varz)
1876
1876
 
1877
+ self._initRuntVars(query)
1878
+
1879
+ self.proxies = {}
1880
+
1881
+ self.onfini(self._onRuntFini)
1882
+
1883
+ def _initRuntVars(self, query):
1884
+
1877
1885
  # declare path builtins as non-runtsafe
1878
1886
  self.runtvars = {
1879
1887
  'node': False,
@@ -1882,17 +1890,14 @@ class Runtime(s_base.Base):
1882
1890
 
1883
1891
  # inherit runtsafe vars from our root
1884
1892
  if self.root is not None:
1885
- self.runtvars.update(root.runtvars)
1893
+ self.runtvars.update(self.root.runtvars)
1886
1894
  self.runtvars.update({k: True for k in self.root.getScopeVars().keys()})
1887
1895
 
1888
1896
  # all vars/ctors are de-facto runtsafe
1889
1897
  self.runtvars.update({k: True for k in self.vars.keys()})
1890
1898
  self.runtvars.update({k: True for k in self.ctors.keys()})
1891
1899
 
1892
- self.proxies = {}
1893
-
1894
1900
  self._loadRuntVars(query)
1895
- self.onfini(self._onRuntFini)
1896
1901
 
1897
1902
  def getScopeVars(self):
1898
1903
  '''
@@ -3723,6 +3728,8 @@ class MergeCmd(Cmd):
3723
3728
  pars = Cmd.getArgParser(self)
3724
3729
  pars.add_argument('--apply', default=False, action='store_true',
3725
3730
  help='Execute the merge changes.')
3731
+ pars.add_argument('--wipe', default=False, action='store_true',
3732
+ help='Replace the top layer in the view with a fresh layer.')
3726
3733
  pars.add_argument('--no-tags', default=False, action='store_true',
3727
3734
  help='Do not merge tags/tagprops or syn:tag nodes.')
3728
3735
  pars.add_argument('--only-tags', default=False, action='store_true',
@@ -3796,13 +3803,15 @@ class MergeCmd(Cmd):
3796
3803
  layr1 = runt.snap.view.layers[1].iden
3797
3804
 
3798
3805
  if not allows['forms'] and sode.get('valu') is not None:
3799
- runt.confirm(('node', 'del', node.form.name), gateiden=layr0)
3806
+ if not self.opts.wipe:
3807
+ runt.confirm(('node', 'del', node.form.name), gateiden=layr0)
3800
3808
  runt.confirm(('node', 'add', node.form.name), gateiden=layr1)
3801
3809
 
3802
3810
  if not allows['props']:
3803
3811
  for name in sode.get('props', {}).keys():
3804
3812
  prop = node.form.prop(name)
3805
- runt.confirmPropDel(prop, layriden=layr0)
3813
+ if not self.opts.wipe:
3814
+ runt.confirmPropDel(prop, layriden=layr0)
3806
3815
  runt.confirmPropSet(prop, layriden=layr1)
3807
3816
 
3808
3817
  if not allows['tags']:
@@ -3813,7 +3822,8 @@ class MergeCmd(Cmd):
3813
3822
  if valu != (None, None):
3814
3823
  tagadds.append(tag)
3815
3824
  tagperm = tuple(tag.split('.'))
3816
- runt.confirm(('node', 'tag', 'del') + tagperm, gateiden=layr0)
3825
+ if not self.opts.wipe:
3826
+ runt.confirm(('node', 'tag', 'del') + tagperm, gateiden=layr0)
3817
3827
  runt.confirm(('node', 'tag', 'add') + tagperm, gateiden=layr1)
3818
3828
  else:
3819
3829
  tags.append((len(tag), tag))
@@ -3825,22 +3835,26 @@ class MergeCmd(Cmd):
3825
3835
 
3826
3836
  tagadds.append(tag)
3827
3837
  tagperm = tuple(tag.split('.'))
3828
- runt.confirm(('node', 'tag', 'del') + tagperm, gateiden=layr0)
3838
+ if not self.opts.wipe:
3839
+ runt.confirm(('node', 'tag', 'del') + tagperm, gateiden=layr0)
3829
3840
  runt.confirm(('node', 'tag', 'add') + tagperm, gateiden=layr1)
3830
3841
 
3831
3842
  for tag in sode.get('tagprops', {}).keys():
3832
3843
  tagperm = tuple(tag.split('.'))
3833
- runt.confirm(('node', 'tag', 'del') + tagperm, gateiden=layr0)
3844
+ if not self.opts.wipe:
3845
+ runt.confirm(('node', 'tag', 'del') + tagperm, gateiden=layr0)
3834
3846
  runt.confirm(('node', 'tag', 'add') + tagperm, gateiden=layr1)
3835
3847
 
3836
3848
  if not allows['ndata']:
3837
3849
  async for name in runt.snap.view.layers[0].iterNodeDataKeys(node.buid):
3838
- runt.confirm(('node', 'data', 'pop', name), gateiden=layr0)
3850
+ if not self.opts.wipe:
3851
+ runt.confirm(('node', 'data', 'pop', name), gateiden=layr0)
3839
3852
  runt.confirm(('node', 'data', 'set', name), gateiden=layr1)
3840
3853
 
3841
3854
  if not allows['edges']:
3842
3855
  async for verb in runt.snap.view.layers[0].iterNodeEdgeVerbsN1(node.buid):
3843
- runt.confirm(('node', 'edge', 'del', verb), gateiden=layr0)
3856
+ if not self.opts.wipe:
3857
+ runt.confirm(('node', 'edge', 'del', verb), gateiden=layr0)
3844
3858
  runt.confirm(('node', 'edge', 'add', verb), gateiden=layr1)
3845
3859
 
3846
3860
  async def execStormCmd(self, runt, genr):
@@ -3849,6 +3863,11 @@ class MergeCmd(Cmd):
3849
3863
  mesg = 'You may only merge nodes in forked views'
3850
3864
  raise s_exc.CantMergeView(mesg=mesg)
3851
3865
 
3866
+ if self.opts.wipe:
3867
+ mesg = 'merge --wipe requires view admin'
3868
+ runt.reqAdmin(gateiden=runt.snap.view.iden, mesg=mesg)
3869
+ runt.confirm(('layer', 'del'), gateiden=runt.snap.view.layers[0].iden)
3870
+
3852
3871
  notags = self.opts.no_tags
3853
3872
  onlytags = self.opts.only_tags
3854
3873
  doapply = self.opts.apply
@@ -3862,18 +3881,27 @@ class MergeCmd(Cmd):
3862
3881
  doperms = doapply and not (runt.isAdmin(gateiden=layr0.iden) and runt.isAdmin(gateiden=layr1.iden))
3863
3882
 
3864
3883
  if doperms:
3865
- allows = {
3866
- 'forms': runt.user.allowed(('node', 'del'), gateiden=layr0.iden, deepdeny=True) and
3867
- runt.user.allowed(('node', 'add'), gateiden=layr1.iden, deepdeny=True),
3868
- 'props': runt.user.allowed(('node', 'prop', 'del'), gateiden=layr0.iden, deepdeny=True) and
3869
- runt.user.allowed(('node', 'prop', 'set'), gateiden=layr1.iden, deepdeny=True),
3870
- 'tags': runt.user.allowed(('node', 'tag', 'del'), gateiden=layr0.iden, deepdeny=True) and
3871
- runt.user.allowed(('node', 'tag', 'add'), gateiden=layr1.iden, deepdeny=True),
3872
- 'ndata': runt.user.allowed(('node', 'data', 'pop'), gateiden=layr0.iden, deepdeny=True) and
3873
- runt.user.allowed(('node', 'data', 'set'), gateiden=layr1.iden, deepdeny=True),
3874
- 'edges': runt.user.allowed(('node', 'edge', 'del'), gateiden=layr0.iden, deepdeny=True) and
3875
- runt.user.allowed(('node', 'edge', 'add'), gateiden=layr1.iden, deepdeny=True),
3876
- }
3884
+ if not self.opts.wipe:
3885
+ allows = {
3886
+ 'forms': runt.user.allowed(('node', 'del'), gateiden=layr0.iden, deepdeny=True) and
3887
+ runt.user.allowed(('node', 'add'), gateiden=layr1.iden, deepdeny=True),
3888
+ 'props': runt.user.allowed(('node', 'prop', 'del'), gateiden=layr0.iden, deepdeny=True) and
3889
+ runt.user.allowed(('node', 'prop', 'set'), gateiden=layr1.iden, deepdeny=True),
3890
+ 'tags': runt.user.allowed(('node', 'tag', 'del'), gateiden=layr0.iden, deepdeny=True) and
3891
+ runt.user.allowed(('node', 'tag', 'add'), gateiden=layr1.iden, deepdeny=True),
3892
+ 'ndata': runt.user.allowed(('node', 'data', 'pop'), gateiden=layr0.iden, deepdeny=True) and
3893
+ runt.user.allowed(('node', 'data', 'set'), gateiden=layr1.iden, deepdeny=True),
3894
+ 'edges': runt.user.allowed(('node', 'edge', 'del'), gateiden=layr0.iden, deepdeny=True) and
3895
+ runt.user.allowed(('node', 'edge', 'add'), gateiden=layr1.iden, deepdeny=True),
3896
+ }
3897
+ else:
3898
+ allows = {
3899
+ 'forms': runt.user.allowed(('node', 'add'), gateiden=layr1.iden, deepdeny=True),
3900
+ 'props': runt.user.allowed(('node', 'prop', 'set'), gateiden=layr1.iden, deepdeny=True),
3901
+ 'tags': runt.user.allowed(('node', 'tag', 'add'), gateiden=layr1.iden, deepdeny=True),
3902
+ 'ndata': runt.user.allowed(('node', 'data', 'set'), gateiden=layr1.iden, deepdeny=True),
3903
+ 'edges': runt.user.allowed(('node', 'edge', 'add'), gateiden=layr1.iden, deepdeny=True),
3904
+ }
3877
3905
 
3878
3906
  doperms = not all(allows.values())
3879
3907
 
@@ -3961,7 +3989,8 @@ class MergeCmd(Cmd):
3961
3989
  if name == '.created':
3962
3990
  if doapply:
3963
3991
  protonode.props['.created'] = valu
3964
- subs.append((s_layer.EDIT_PROP_DEL, (name, valu, stortype), ()))
3992
+ if not self.opts.wipe:
3993
+ subs.append((s_layer.EDIT_PROP_DEL, (name, valu, stortype), ()))
3965
3994
  continue
3966
3995
 
3967
3996
  isset = False
@@ -3985,7 +4014,8 @@ class MergeCmd(Cmd):
3985
4014
  await runt.printf(f'{nodeiden} {form}:{name} = {valurepr}')
3986
4015
  else:
3987
4016
  await protonode.set(name, valu)
3988
- subs.append((s_layer.EDIT_PROP_DEL, (name, valu, stortype), ()))
4017
+ if not self.opts.wipe:
4018
+ subs.append((s_layer.EDIT_PROP_DEL, (name, valu, stortype), ()))
3989
4019
 
3990
4020
  if doapply and protonode is None:
3991
4021
  protonode = await editor.addNode(form, node.ndef[1], norminfo={})
@@ -4004,7 +4034,8 @@ class MergeCmd(Cmd):
4004
4034
  await runt.printf(f'{nodeiden} {form}#{tag}{valurepr}')
4005
4035
  else:
4006
4036
  await protonode.addTag(tag, valu)
4007
- subs.append((s_layer.EDIT_TAG_DEL, (tag, valu), ()))
4037
+ if not self.opts.wipe:
4038
+ subs.append((s_layer.EDIT_TAG_DEL, (tag, valu), ()))
4008
4039
 
4009
4040
  for tag, tagdict in sode.get('tagprops', {}).items():
4010
4041
 
@@ -4017,7 +4048,8 @@ class MergeCmd(Cmd):
4017
4048
  await runt.printf(f'{nodeiden} {form}#{tag}:{prop} = {valurepr}')
4018
4049
  else:
4019
4050
  await protonode.setTagProp(tag, prop, valu)
4020
- subs.append((s_layer.EDIT_TAGPROP_DEL, (tag, prop, valu, stortype), ()))
4051
+ if not self.opts.wipe:
4052
+ subs.append((s_layer.EDIT_TAGPROP_DEL, (tag, prop, valu, stortype), ()))
4021
4053
 
4022
4054
  if not onlytags or form == 'syn:tag':
4023
4055
 
@@ -4027,7 +4059,8 @@ class MergeCmd(Cmd):
4027
4059
  await runt.printf(f'{nodeiden} {form} DATA {name} = {valurepr}')
4028
4060
  else:
4029
4061
  await protonode.setData(name, valu)
4030
- subs.append((s_layer.EDIT_NODEDATA_DEL, (name, valu), ()))
4062
+ if not self.opts.wipe:
4063
+ subs.append((s_layer.EDIT_NODEDATA_DEL, (name, valu), ()))
4031
4064
 
4032
4065
  async for edge in s_coro.pause(layr0.iterNodeEdgesN1(node.buid)):
4033
4066
  name, dest = edge
@@ -4035,9 +4068,10 @@ class MergeCmd(Cmd):
4035
4068
  await runt.printf(f'{nodeiden} {form} +({name})> {dest}')
4036
4069
  else:
4037
4070
  await protonode.addEdge(name, dest)
4038
- subs.append((s_layer.EDIT_EDGE_DEL, edge, ()))
4071
+ if not self.opts.wipe:
4072
+ subs.append((s_layer.EDIT_EDGE_DEL, edge, ()))
4039
4073
 
4040
- if delnode:
4074
+ if delnode and not self.opts.wipe:
4041
4075
  subs.append((s_layer.EDIT_NODE_DEL, valu, ()))
4042
4076
 
4043
4077
  if doapply:
@@ -4052,6 +4086,9 @@ class MergeCmd(Cmd):
4052
4086
  runt.snap.clearCachedNode(node.buid)
4053
4087
  yield await runt.snap.getNodeByBuid(node.buid), path
4054
4088
 
4089
+ if doapply and self.opts.wipe:
4090
+ await runt.snap.view.swapLayer()
4091
+
4055
4092
  class MoveNodesCmd(Cmd):
4056
4093
  '''
4057
4094
  Move storage nodes between layers.
@@ -486,7 +486,7 @@ The ready column indicates that a service has entered into the realtime change w
486
486
  if $svcinfo.online {
487
487
  $nexusOffset = $_getNexus($name)
488
488
  } else {
489
- $nexusOffset = 'Service is not online. Will not attempt to retrieve its nexus offset.'
489
+ $nexusOffset = '<offline>'
490
490
  }
491
491
  }
492
492
  $name=$name.ljust(45)
@@ -633,7 +633,7 @@ class UserProfile(s_stormtypes.Prim):
633
633
  async def deref(self, name):
634
634
  name = await s_stormtypes.tostr(name)
635
635
  self.runt.confirm(('auth', 'user', 'get', 'profile', name))
636
- return copy.deepcopy(await self.runt.snap.core.getUserProfInfo(self.valu, name))
636
+ return await self.runt.snap.core.getUserProfInfo(self.valu, name)
637
637
 
638
638
  async def setitem(self, name, valu):
639
639
  name = await s_stormtypes.tostr(name)
@@ -654,7 +654,7 @@ class UserProfile(s_stormtypes.Prim):
654
654
 
655
655
  async def value(self):
656
656
  self.runt.confirm(('auth', 'user', 'get', 'profile'))
657
- return copy.deepcopy(await self.runt.snap.core.getUserProfile(self.valu))
657
+ return await self.runt.snap.core.getUserProfile(self.valu)
658
658
 
659
659
  @s_stormtypes.registry.registerType
660
660
  class UserJson(s_stormtypes.Prim):
@@ -807,7 +807,7 @@ class UserVars(s_stormtypes.Prim):
807
807
 
808
808
  async def deref(self, name):
809
809
  name = await s_stormtypes.tostr(name)
810
- return copy.deepcopy(await self.runt.snap.core.getUserVarValu(self.valu, name))
810
+ return await self.runt.snap.core.getUserVarValu(self.valu, name)
811
811
 
812
812
  async def setitem(self, name, valu):
813
813
  name = await s_stormtypes.tostr(name)
@@ -821,7 +821,7 @@ class UserVars(s_stormtypes.Prim):
821
821
 
822
822
  async def iter(self):
823
823
  async for name, valu in self.runt.snap.core.iterUserVars(self.valu):
824
- yield name, copy.deepcopy(valu)
824
+ yield name, valu
825
825
  await asyncio.sleep(0)
826
826
 
827
827
  @s_stormtypes.registry.registerType
@@ -1596,6 +1596,181 @@ class LibAuth(s_stormtypes.Lib):
1596
1596
  perm = await s_stormtypes.toprim(perm)
1597
1597
  return self.runt.snap.core.getPermDef(perm)
1598
1598
 
1599
+ @s_stormtypes.registry.registerType
1600
+ class StormUserVarsDict(s_stormtypes.Prim):
1601
+ '''
1602
+ A Storm Primitive that maps the HiveDict interface to a user vars dictionary.
1603
+ '''
1604
+ _storm_locals = (
1605
+ {'name': 'get', 'desc': 'Get the value for a user var.',
1606
+ 'type': {'type': 'function', '_funcname': '_get',
1607
+ 'args': (
1608
+ {'name': 'name', 'type': 'str', 'desc': 'The name of the var.', },
1609
+ {'name': 'default', 'type': 'prim', 'default': None,
1610
+ 'desc': 'The default value to return if not set.', },
1611
+ ),
1612
+ 'returns': {'type': 'prim', 'desc': 'The requested value.', }}},
1613
+ {'name': 'pop', 'desc': 'Remove a user var value.',
1614
+ 'type': {'type': 'function', '_funcname': '_pop',
1615
+ 'args': (
1616
+ {'name': 'name', 'type': 'str', 'desc': 'The name of the var.', },
1617
+ {'name': 'default', 'type': 'prim', 'default': None,
1618
+ 'desc': 'The default value to return if not set.', },
1619
+ ),
1620
+ 'returns': {'type': 'prim', 'desc': 'The requested value.', }}},
1621
+ {'name': 'set', 'desc': 'Set a user var value.',
1622
+ 'type': {'type': 'function', '_funcname': '_set',
1623
+ 'args': (
1624
+ {'name': 'name', 'type': 'str', 'desc': 'The name of the var to set.', },
1625
+ {'name': 'valu', 'type': 'prim', 'desc': 'The value to store.', },
1626
+ ),
1627
+ 'returns': {'type': ['null', 'prim'],
1628
+ 'desc': 'Old value of the var if it was previously set, or none.', }}},
1629
+ {'name': 'list', 'desc': 'List the vars and their values.',
1630
+ 'type': {'type': 'function', '_funcname': '_list',
1631
+ 'returns': {'type': 'list', 'desc': 'A list of tuples containing var, value pairs.', }}},
1632
+ )
1633
+ _storm_typename = 'user:vars:dict'
1634
+ _ismutable = True
1635
+
1636
+ def __init__(self, runt, valu, path=None):
1637
+ s_stormtypes.Prim.__init__(self, valu, path=path)
1638
+ self.runt = runt
1639
+ self.locls.update(self.getObjLocals())
1640
+
1641
+ def getObjLocals(self):
1642
+ return {
1643
+ 'get': self._get,
1644
+ 'pop': self._pop,
1645
+ 'set': self._set,
1646
+ 'list': self._list,
1647
+ }
1648
+
1649
+ @s_stormtypes.stormfunc(readonly=True)
1650
+ async def _get(self, name, default=None):
1651
+ name = await s_stormtypes.tostr(name)
1652
+ return await self.runt.snap.core.getUserVarValu(self.valu, name, default=default)
1653
+
1654
+ async def _pop(self, name, default=None):
1655
+ name = await s_stormtypes.tostr(name)
1656
+ return await self.runt.snap.core.popUserVarValu(self.valu, name, default=default)
1657
+
1658
+ async def _set(self, name, valu):
1659
+ if not isinstance(name, str):
1660
+ mesg = 'The name of a variable must be a string.'
1661
+ raise s_exc.StormRuntimeError(mesg=mesg, name=name)
1662
+
1663
+ name = await s_stormtypes.tostr(name)
1664
+ oldv = await self.runt.snap.core.getUserVarValu(self.valu, name)
1665
+
1666
+ valu = await s_stormtypes.toprim(valu)
1667
+
1668
+ await self.runt.snap.core.setUserVarValu(self.valu, name, valu)
1669
+ return oldv
1670
+
1671
+ @s_stormtypes.stormfunc(readonly=True)
1672
+ async def _list(self):
1673
+ valu = await self.value()
1674
+ return list(valu.items())
1675
+
1676
+ async def iter(self):
1677
+ async for name, valu in self.runt.snap.core.iterUserVars(self.valu):
1678
+ yield name, valu
1679
+ await asyncio.sleep(0)
1680
+
1681
+ async def value(self):
1682
+ varz = {}
1683
+ async for key, valu in self.runt.snap.core.iterUserVars(self.valu):
1684
+ varz[key] = valu
1685
+ await asyncio.sleep(0)
1686
+
1687
+ return varz
1688
+
1689
+ @s_stormtypes.registry.registerType
1690
+ class StormUserProfileDict(s_stormtypes.Prim):
1691
+ '''
1692
+ A Storm Primitive that maps the HiveDict interface to a user profile dictionary.
1693
+ '''
1694
+ _storm_locals = (
1695
+ {'name': 'get', 'desc': 'Get a user profile value.',
1696
+ 'type': {'type': 'function', '_funcname': '_get',
1697
+ 'args': (
1698
+ {'name': 'name', 'type': 'str', 'desc': 'The name of the user profile value.', },
1699
+ {'name': 'default', 'type': 'prim', 'default': None,
1700
+ 'desc': 'The default value to return if not set.', },
1701
+ ),
1702
+ 'returns': {'type': 'prim', 'desc': 'The requested value.', }}},
1703
+ {'name': 'pop', 'desc': 'Remove a user profile value.',
1704
+ 'type': {'type': 'function', '_funcname': '_pop',
1705
+ 'args': (
1706
+ {'name': 'name', 'type': 'str', 'desc': 'The name of the user profile value.', },
1707
+ {'name': 'default', 'type': 'prim', 'default': None,
1708
+ 'desc': 'The default value to return if not set.', },
1709
+ ),
1710
+ 'returns': {'type': 'prim', 'desc': 'The requested value.', }}},
1711
+ {'name': 'set', 'desc': 'Set a user profile value.',
1712
+ 'type': {'type': 'function', '_funcname': '_set',
1713
+ 'args': (
1714
+ {'name': 'name', 'type': 'str', 'desc': 'The name of the user profile value to set.', },
1715
+ {'name': 'valu', 'type': 'prim', 'desc': 'The value to store.', },
1716
+ ),
1717
+ 'returns': {'type': ['null', 'prim'],
1718
+ 'desc': 'Old value if it was previously set, or none.', }}},
1719
+ {'name': 'list', 'desc': 'List the user profile vars and their values.',
1720
+ 'type': {'type': 'function', '_funcname': '_list',
1721
+ 'returns': {'type': 'list', 'desc': 'A list of tuples containing var, value pairs.', }}},
1722
+ )
1723
+ _storm_typename = 'user:profile:dict'
1724
+ _ismutable = True
1725
+
1726
+ def __init__(self, runt, valu, path=None):
1727
+ s_stormtypes.Prim.__init__(self, valu, path=path)
1728
+ self.runt = runt
1729
+ self.locls.update(self.getObjLocals())
1730
+
1731
+ def getObjLocals(self):
1732
+ return {
1733
+ 'get': self._get,
1734
+ 'pop': self._pop,
1735
+ 'set': self._set,
1736
+ 'list': self._list,
1737
+ }
1738
+
1739
+ @s_stormtypes.stormfunc(readonly=True)
1740
+ async def _get(self, name, default=None):
1741
+ name = await s_stormtypes.tostr(name)
1742
+ return await self.runt.snap.core.getUserProfInfo(self.valu, name, default=default)
1743
+
1744
+ async def _pop(self, name, default=None):
1745
+ name = await s_stormtypes.tostr(name)
1746
+ return await self.runt.snap.core.popUserProfInfo(self.valu, name, default=default)
1747
+
1748
+ async def _set(self, name, valu):
1749
+ if not isinstance(name, str):
1750
+ mesg = 'The name of a variable must be a string.'
1751
+ raise s_exc.StormRuntimeError(mesg=mesg, name=name)
1752
+
1753
+ name = await s_stormtypes.tostr(name)
1754
+ oldv = await self.runt.snap.core.getUserProfInfo(self.valu, name)
1755
+
1756
+ valu = await s_stormtypes.toprim(valu)
1757
+
1758
+ await self.runt.snap.core.setUserProfInfo(self.valu, name, valu)
1759
+ return oldv
1760
+
1761
+ @s_stormtypes.stormfunc(readonly=True)
1762
+ async def _list(self):
1763
+ valu = await self.value()
1764
+ return list(valu.items())
1765
+
1766
+ async def iter(self):
1767
+ async for name, valu in self.runt.snap.core.iterUserProfInfo(self.valu):
1768
+ yield name, valu
1769
+ await asyncio.sleep(0)
1770
+
1771
+ async def value(self):
1772
+ return await self.runt.snap.core.getUserProfile(self.valu)
1773
+
1599
1774
  @s_stormtypes.registry.registerLib
1600
1775
  class LibUser(s_stormtypes.Lib):
1601
1776
  '''
@@ -1614,10 +1789,10 @@ class LibUser(s_stormtypes.Lib):
1614
1789
  ),
1615
1790
  'returns': {'type': 'boolean',
1616
1791
  'desc': 'True if the user has the requested permission, false otherwise.', }}},
1617
- {'name': 'vars', 'desc': "Get a Hive dictionary representing the current user's persistent variables.",
1618
- 'type': 'hive:dict', },
1619
- {'name': 'profile', 'desc': "Get a Hive dictionary representing the current user's profile information.",
1620
- 'type': 'hive:dict', },
1792
+ {'name': 'vars', 'desc': "Get a dictionary representing the current user's persistent variables.",
1793
+ 'type': 'auth:user:vars', },
1794
+ {'name': 'profile', 'desc': "Get a dictionary representing the current user's profile information.",
1795
+ 'type': 'auth:user:profile', },
1621
1796
  {'name': 'iden', 'desc': 'The user GUID for the current storm user.', 'type': 'str'},
1622
1797
  )
1623
1798
  _storm_lib_path = ('user', )
@@ -1632,9 +1807,9 @@ class LibUser(s_stormtypes.Lib):
1632
1807
  def addLibFuncs(self):
1633
1808
  super().addLibFuncs()
1634
1809
  self.locls.update({
1635
- 'vars': s_stormtypes.StormHiveDict(self.runt, self.runt.user.vars),
1810
+ 'vars': StormUserVarsDict(self.runt, self.runt.user.iden),
1636
1811
  'json': UserJson(self.runt, self.runt.user.iden),
1637
- 'profile': s_stormtypes.StormHiveDict(self.runt, self.runt.user.profile),
1812
+ 'profile': StormUserProfileDict(self.runt, self.runt.user.iden),
1638
1813
  })
1639
1814
 
1640
1815
  @s_stormtypes.stormfunc(readonly=True)
@@ -20,8 +20,21 @@ stormcmds = [
20
20
  'storm': '''
21
21
  $apis = $lib.cortex.httpapi.list()
22
22
  if $apis {
23
- $header = 'order iden owner auth runas path'
24
- $lib.print($header)
23
+ $config = (({
24
+ "separators": {
25
+ 'data:row': ''
26
+ },
27
+ "columns": [
28
+ {'name': 'order', 'width': 5},
29
+ {'name': 'iden', 'width': 32},
30
+ {'name': 'owner', 'width': 20},
31
+ {'name': 'auth', 'width': 5},
32
+ {'name': 'runas', 'width': 6},
33
+ {'name': 'path'},
34
+ ]
35
+ }))
36
+ $printer = $lib.tabular.printer($config)
37
+ $lib.print($printer.header())
25
38
  for ($n, $api) in $lib.iters.enum($apis) {
26
39
  try {
27
40
  $user = $api.owner.name
@@ -29,9 +42,7 @@ stormcmds = [
29
42
  $user = `No user found ({$err.info.user})`
30
43
  }
31
44
  $auth = `{$api.authenticated}`
32
- $order = `{$n}`
33
- $mesg=`{$order.ljust(5)} {$api.iden} {$user.ljust(20)} {$auth.ljust(5)} {$api.runas.ljust(6)} {$api.path}`
34
- $lib.print($mesg)
45
+ $lib.print($printer.row(($n, $api.iden, $user, $auth, $api.runas, $api.path)))
35
46
  }
36
47
  } else {
37
48
  $lib.print('No Extended HTTP API endpoints are registered.')
@@ -136,6 +136,34 @@ class LibGen(s_stormtypes.Lib):
136
136
  {'name': 'name', 'type': 'str', 'desc': 'The name of the place.'},
137
137
  ),
138
138
  'returns': {'type': 'node', 'desc': 'A geo:place node with the given name.'}}},
139
+ {'name': 'fileBytesBySha256',
140
+ 'desc': 'Returns a file:bytes node by SHA256, adding the node if it does not exist.',
141
+ 'type': {'type': 'function', '_funcname': '_storm_query',
142
+ 'args': (
143
+ {'name': 'sha256', 'type': ['str', 'hash:sha256'], 'desc': 'The SHA256 fingerprint for the file:bytes node.'},
144
+ {'name': 'try', 'type': 'boolean', 'default': False,
145
+ 'desc': 'Type normalization will fail silently instead of raising an exception.'},
146
+ ),
147
+ 'returns': {'type': 'node', 'desc': 'A file:bytes node with the given SHA256.'}}},
148
+ {'name': 'cryptoX509CertBySha256',
149
+ 'desc': 'Returns a crypto:x509:cert node by SHA256, adding the node if it does not exist.',
150
+ 'type': {'type': 'function', '_funcname': '_storm_query',
151
+ 'args': (
152
+ {'name': 'sha256', 'type': ['str', 'hash:sha256'], 'desc': 'The SHA256 fingerprint for the certificate.'},
153
+ {'name': 'try', 'type': 'boolean', 'default': False,
154
+ 'desc': 'Type normalization will fail silently instead of raising an exception.'},
155
+ ),
156
+ 'returns': {'type': 'node', 'desc': 'A crypto:x509:cert node with the given SHA256.'}}},
157
+ {'name': 'inetTlsServerCertByServerAndSha256',
158
+ 'desc': 'Returns an inet:tls:servercert node by server and SHA256, adding the node if it does not exist.',
159
+ 'type': {'type': 'function', '_funcname': '_storm_query',
160
+ 'args': (
161
+ {'name': 'server', 'type': ['str', 'inet:server'], 'desc': 'The server associated with the x509 certificate.'},
162
+ {'name': 'sha256', 'type': ['str', 'hash:sha256'], 'desc': 'The SHA256 fingerprint for the certificate.'},
163
+ {'name': 'try', 'type': 'boolean', 'default': False,
164
+ 'desc': 'Type normalization will fail silently instead of raising an exception.'},
165
+ ),
166
+ 'returns': {'type': 'node', 'desc': 'An inet:tls:servercert node with the given server and SHA256.'}}},
139
167
  )
140
168
  _storm_lib_path = ('gen',)
141
169
 
@@ -425,6 +453,58 @@ class LibGen(s_stormtypes.Lib):
425
453
  [ geo:place=(gen, name, $geoname) :name=$geoname ]
426
454
  return($node)
427
455
  }
456
+
457
+ function fileBytesBySha256(sha256, try=$lib.false) {
458
+ ($ok, $sha256) = $__maybeCast($try, hash:sha256, $sha256)
459
+ if (not $ok) { return() }
460
+
461
+ file:bytes=$sha256
462
+ return($node)
463
+
464
+ file:bytes:sha256=$sha256
465
+ return($node)
466
+
467
+ [ file:bytes=$sha256 ]
468
+ return($node)
469
+ }
470
+
471
+ function cryptoX509CertBySha256(sha256, try=$lib.false) {
472
+ ($ok, $sha256) = $__maybeCast($try, hash:sha256, $sha256)
473
+ if (not $ok) { return() }
474
+
475
+ $guid = $lib.guid(valu=$sha256)
476
+
477
+ // Try to lift crypto:x509:cert by guid
478
+ crypto:x509:cert=$guid
479
+ return($node)
480
+
481
+ // Try to lift crypto:x509:cert by sha256
482
+ crypto:x509:cert:sha256=$sha256
483
+ return($node)
484
+
485
+ // Try to lift crypto:x509:cert by file
486
+ file:bytes:sha256=$sha256 -> crypto:x509:cert:file
487
+ { -:sha256 [ :sha256 = $sha256 ] }
488
+ return($node)
489
+
490
+ // Create a new crypto:x509:cert with file and sha256
491
+ [ crypto:x509:cert=$guid
492
+ :file = $fileBytesBySha256($sha256)
493
+ :sha256 = $sha256
494
+ ]
495
+ return($node)
496
+ }
497
+
498
+ function inetTlsServerCertByServerAndSha256(server, sha256, try=$lib.false) {
499
+ ($ok, $server) = $__maybeCast($try, inet:server, $server)
500
+ if (not $ok) { return() }
501
+
502
+ $crypto = $cryptoX509CertBySha256($sha256, try=$try)
503
+ if (not $crypto) { return() }
504
+
505
+ [ inet:tls:servercert=($server, $crypto) ]
506
+ return($node)
507
+ }
428
508
  '''
429
509
 
430
510
  stormcmds = (