moat-kv 0.71.0__py3-none-any.whl → 0.71.7__py3-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.
Files changed (178) hide show
  1. moat/kv/__init__.py +6 -7
  2. moat/kv/_cfg.yaml +3 -2
  3. moat/kv/actor/__init__.py +2 -1
  4. moat/kv/actor/deletor.py +4 -1
  5. moat/kv/auth/__init__.py +12 -13
  6. moat/kv/auth/_test.py +4 -1
  7. moat/kv/auth/password.py +11 -7
  8. moat/kv/backend/mqtt.py +4 -5
  9. moat/kv/client.py +20 -39
  10. moat/kv/code.py +3 -3
  11. moat/kv/command/data.py +4 -3
  12. moat/kv/command/dump/__init__.py +36 -34
  13. moat/kv/command/internal.py +2 -3
  14. moat/kv/command/job.py +1 -2
  15. moat/kv/command/type.py +3 -6
  16. moat/kv/data.py +9 -8
  17. moat/kv/errors.py +16 -8
  18. moat/kv/mock/__init__.py +2 -12
  19. moat/kv/model.py +29 -33
  20. moat/kv/obj/__init__.py +3 -3
  21. moat/kv/obj/command.py +3 -3
  22. moat/kv/runner.py +4 -5
  23. moat/kv/server.py +106 -126
  24. moat/kv/types.py +10 -12
  25. {moat_kv-0.71.0.dist-info → moat_kv-0.71.7.dist-info}/METADATA +6 -2
  26. moat_kv-0.71.7.dist-info/RECORD +47 -0
  27. {moat_kv-0.71.0.dist-info → moat_kv-0.71.7.dist-info}/WHEEL +1 -1
  28. moat_kv-0.71.7.dist-info/licenses/LICENSE +3 -0
  29. moat_kv-0.71.7.dist-info/licenses/LICENSE.APACHE2 +202 -0
  30. moat_kv-0.71.7.dist-info/licenses/LICENSE.MIT +20 -0
  31. moat_kv-0.71.7.dist-info/top_level.txt +1 -0
  32. build/lib/docs/source/conf.py +0 -201
  33. build/lib/examples/pathify.py +0 -45
  34. build/lib/moat/kv/__init__.py +0 -19
  35. build/lib/moat/kv/_cfg.yaml +0 -93
  36. build/lib/moat/kv/_main.py +0 -91
  37. build/lib/moat/kv/actor/__init__.py +0 -98
  38. build/lib/moat/kv/actor/deletor.py +0 -139
  39. build/lib/moat/kv/auth/__init__.py +0 -444
  40. build/lib/moat/kv/auth/_test.py +0 -166
  41. build/lib/moat/kv/auth/password.py +0 -234
  42. build/lib/moat/kv/auth/root.py +0 -58
  43. build/lib/moat/kv/backend/__init__.py +0 -67
  44. build/lib/moat/kv/backend/mqtt.py +0 -71
  45. build/lib/moat/kv/client.py +0 -1025
  46. build/lib/moat/kv/code.py +0 -236
  47. build/lib/moat/kv/codec.py +0 -11
  48. build/lib/moat/kv/command/__init__.py +0 -1
  49. build/lib/moat/kv/command/acl.py +0 -180
  50. build/lib/moat/kv/command/auth.py +0 -261
  51. build/lib/moat/kv/command/code.py +0 -293
  52. build/lib/moat/kv/command/codec.py +0 -186
  53. build/lib/moat/kv/command/data.py +0 -265
  54. build/lib/moat/kv/command/dump/__init__.py +0 -143
  55. build/lib/moat/kv/command/error.py +0 -149
  56. build/lib/moat/kv/command/internal.py +0 -248
  57. build/lib/moat/kv/command/job.py +0 -433
  58. build/lib/moat/kv/command/log.py +0 -53
  59. build/lib/moat/kv/command/server.py +0 -114
  60. build/lib/moat/kv/command/type.py +0 -201
  61. build/lib/moat/kv/config.py +0 -46
  62. build/lib/moat/kv/data.py +0 -216
  63. build/lib/moat/kv/errors.py +0 -561
  64. build/lib/moat/kv/exceptions.py +0 -126
  65. build/lib/moat/kv/mock/__init__.py +0 -101
  66. build/lib/moat/kv/mock/mqtt.py +0 -159
  67. build/lib/moat/kv/mock/tracer.py +0 -63
  68. build/lib/moat/kv/model.py +0 -1069
  69. build/lib/moat/kv/obj/__init__.py +0 -646
  70. build/lib/moat/kv/obj/command.py +0 -241
  71. build/lib/moat/kv/runner.py +0 -1347
  72. build/lib/moat/kv/server.py +0 -2809
  73. build/lib/moat/kv/types.py +0 -513
  74. ci/rtd-requirements.txt +0 -4
  75. ci/test-requirements.txt +0 -7
  76. ci/travis.sh +0 -96
  77. debian/.gitignore +0 -7
  78. debian/changelog +0 -1435
  79. debian/control +0 -43
  80. debian/moat-kv/usr/lib/python3/dist-packages/docs/source/conf.py +0 -201
  81. debian/moat-kv/usr/lib/python3/dist-packages/examples/pathify.py +0 -45
  82. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/__init__.py +0 -19
  83. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/_cfg.yaml +0 -93
  84. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/_main.py +0 -91
  85. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/actor/__init__.py +0 -98
  86. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/actor/deletor.py +0 -139
  87. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/__init__.py +0 -444
  88. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/_test.py +0 -166
  89. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/password.py +0 -234
  90. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/root.py +0 -58
  91. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/__init__.py +0 -67
  92. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/mqtt.py +0 -71
  93. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/client.py +0 -1025
  94. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/code.py +0 -236
  95. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/codec.py +0 -11
  96. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/__init__.py +0 -1
  97. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/acl.py +0 -180
  98. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/auth.py +0 -261
  99. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/code.py +0 -293
  100. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/codec.py +0 -186
  101. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/data.py +0 -265
  102. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/dump/__init__.py +0 -143
  103. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/error.py +0 -149
  104. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/internal.py +0 -248
  105. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/job.py +0 -433
  106. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/log.py +0 -53
  107. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/server.py +0 -114
  108. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/type.py +0 -201
  109. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/config.py +0 -46
  110. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/data.py +0 -216
  111. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/errors.py +0 -561
  112. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/exceptions.py +0 -126
  113. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/__init__.py +0 -101
  114. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/mqtt.py +0 -159
  115. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/tracer.py +0 -63
  116. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/model.py +0 -1069
  117. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/obj/__init__.py +0 -646
  118. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/obj/command.py +0 -241
  119. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/runner.py +0 -1347
  120. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/server.py +0 -2809
  121. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/types.py +0 -513
  122. debian/moat-kv.postinst +0 -3
  123. debian/rules +0 -20
  124. debian/source/format +0 -1
  125. debian/watch +0 -4
  126. docs/Makefile +0 -20
  127. docs/make.bat +0 -36
  128. docs/source/TODO.rst +0 -61
  129. docs/source/_static/.gitkeep +0 -0
  130. docs/source/acls.rst +0 -80
  131. docs/source/auth.rst +0 -84
  132. docs/source/client_protocol.rst +0 -456
  133. docs/source/code.rst +0 -341
  134. docs/source/command_line.rst +0 -1187
  135. docs/source/common_protocol.rst +0 -47
  136. docs/source/conf.py +0 -201
  137. docs/source/debugging.rst +0 -70
  138. docs/source/extend.rst +0 -37
  139. docs/source/history.rst +0 -36
  140. docs/source/index.rst +0 -75
  141. docs/source/model.rst +0 -54
  142. docs/source/overview.rst +0 -83
  143. docs/source/related.rst +0 -89
  144. docs/source/server_protocol.rst +0 -450
  145. docs/source/startup.rst +0 -31
  146. docs/source/translator.rst +0 -244
  147. docs/source/tutorial.rst +0 -711
  148. docs/source/v3.rst +0 -168
  149. examples/code/transform.scale.yml +0 -21
  150. examples/code/transform.switch.yml +0 -82
  151. examples/code/transform.timeslot.yml +0 -63
  152. examples/pathify.py +0 -45
  153. moat/kv/codec.py +0 -11
  154. moat_kv-0.71.0.dist-info/RECORD +0 -188
  155. moat_kv-0.71.0.dist-info/top_level.txt +0 -9
  156. scripts/current +0 -15
  157. scripts/env +0 -8
  158. scripts/init +0 -39
  159. scripts/recover +0 -17
  160. scripts/rotate +0 -33
  161. scripts/run +0 -29
  162. scripts/run-all +0 -10
  163. scripts/run-any +0 -10
  164. scripts/run-single +0 -15
  165. scripts/success +0 -4
  166. systemd/moat-kv-recover.service +0 -21
  167. systemd/moat-kv-rotate.service +0 -20
  168. systemd/moat-kv-rotate.timer +0 -10
  169. systemd/moat-kv-run-all.service +0 -26
  170. systemd/moat-kv-run-all@.service +0 -25
  171. systemd/moat-kv-run-any.service +0 -26
  172. systemd/moat-kv-run-any@.service +0 -25
  173. systemd/moat-kv-run-single.service +0 -26
  174. systemd/moat-kv-run-single@.service +0 -25
  175. systemd/moat-kv.service +0 -27
  176. systemd/postinst +0 -7
  177. systemd/sysusers +0 -3
  178. {moat_kv-0.71.0.dist-info → moat_kv-0.71.7.dist-info}/licenses/LICENSE.txt +0 -0
@@ -1,236 +0,0 @@
1
- """
2
- This module locally stores code that's been uploaded to MoaT-KV
3
- so that it can be called easily.
4
-
5
- "Code" consists of either Python modules or single procedures.
6
-
7
- """
8
- from __future__ import annotations
9
-
10
- import logging
11
- import sys
12
- from functools import partial
13
-
14
- import anyio
15
- from moat.util import NotGiven, P, make_module, make_proc
16
-
17
- from .obj import ClientEntry, ClientRoot
18
-
19
- logger = logging.getLogger(__name__)
20
-
21
-
22
- class EmptyCode(Exception):
23
- """
24
- There is no code here.
25
- """
26
-
27
- pass
28
-
29
-
30
- class ModuleRoot(ClientRoot):
31
- """
32
- Modules are stored at ``(*PREFIX,*name)``.
33
- The prefix defaults to ``("code","module")``.
34
-
35
- The module code is stored textually. Content that is not UTF-8 is TODO.
36
- """
37
-
38
- CFG = "modules"
39
-
40
- err: ErrorRoot = None # noqa: F821
41
-
42
- @classmethod
43
- def child_type(cls, name):
44
- return ModuleEntry
45
-
46
- async def run_starting(self):
47
- from .errors import ErrorRoot
48
-
49
- self.err = await ErrorRoot.as_handler(self.client)
50
- await super().run_starting()
51
-
52
- async def add(self, path, code=None):
53
- """
54
- Add or replace this code at this location.
55
- """
56
- # test-compile the code for validity
57
- if code is None:
58
- return await self.remove(path)
59
-
60
- make_module(code, path)
61
-
62
- r = await self.client.set(self._path + path, value=dict(code=code), nchain=2)
63
- await self.wait_chain(r.chain)
64
-
65
- async def remove(self, path):
66
- """
67
- Remove code at this location.
68
- """
69
- entry = self.follow(self._path + path, create=False)
70
- return await entry.delete()
71
-
72
-
73
- class ModuleEntry(ClientEntry):
74
- _module = None
75
-
76
- @property
77
- def name(self):
78
- return ".".join(self.subpath)
79
-
80
- async def set_value(self, value):
81
- await super().set_value(value)
82
- if value is NotGiven:
83
- self._module = None
84
- try:
85
- del sys.modules[self.name]
86
- except KeyError:
87
- pass
88
- return
89
-
90
- try:
91
- c = self.value.code
92
- if not isinstance(c, str):
93
- raise RuntimeError("Not a string, cannot compile")
94
- m = make_module(c, self.subpath)
95
- except Exception as exc:
96
- self._module = None
97
- logger.warning("Could not compile @%r", self.subpath)
98
- await self.root.err.record_error(
99
- "compile",
100
- self.subpath,
101
- exc=exc,
102
- message="compiler error",
103
- )
104
- else:
105
- await self.root.err.record_working("compile", self.subpath)
106
- self._module = m
107
-
108
-
109
- class CodeRoot(ClientRoot):
110
- """
111
- This class represents the root of a code storage hierarchy. Ideally
112
- there should only be one, but you can configure more.
113
-
114
- You typically don't create this class directly; instead, call
115
- :meth:`CodeRoot.as_handler`::
116
-
117
- code = await CodeRoot.as_handler(client, your_config.get("code-storage",{})
118
-
119
- The prefix defaults to ``("code","proc")``.
120
-
121
- Configuration:
122
-
123
- Arguments:
124
- prefix (list): Where to store the code in MoaT-KV.
125
- The default is ``('.moat','kv','code','proc')``.
126
-
127
- The code is stored as a dict.
128
-
129
- Arguments:
130
- code: the actual code to run. It is parsed as a function body; be
131
- aware that multi-line strings will be indented more than you'd like.
132
- async: flag whether the code should run asynchronously.
133
- True: yes, None (default): no, False: run in a separate thread.
134
- vars: array of names to be used as named arguments.
135
-
136
- All arguments are mandatory and should be named.
137
- Extra arguments will be available in the "kw" dict.
138
-
139
- """
140
-
141
- CFG = "codes"
142
-
143
- err = None
144
-
145
- @classmethod
146
- def child_type(cls, name):
147
- return CodeEntry
148
-
149
- async def run_starting(self):
150
- from .errors import ErrorRoot
151
-
152
- self.err = await ErrorRoot.as_handler(self.client)
153
- await super().run_starting()
154
-
155
- async def add(self, path, code=None, *, is_async=None, variables=()):
156
- """
157
- Add or replace this code at this location.
158
- """
159
- if code is NotGiven:
160
- return await self.remove(path)
161
-
162
- # test-compile the code for validity
163
- make_proc(code, variables, path, use_async=is_async)
164
-
165
- r = await self.client.set(
166
- self._path + path,
167
- value=dict(code=code, is_async=is_async, vars=variables),
168
- nchain=2,
169
- )
170
- await self.wait_chain(r.chain)
171
-
172
- async def remove(self, path):
173
- """Drop this code"""
174
- r = await self.client.set(*self._path, *path, value=None, nchain=2)
175
- await self.wait_chain(r.chain)
176
-
177
- def __call__(self, name, *a, **kw):
178
- if isinstance(name, str):
179
- name = name.split(".")
180
- c = self
181
- for k in name:
182
- c = c[k]
183
- if c is self:
184
- raise RuntimeError("Empty code names don't make sense")
185
- return c(*a, **kw)
186
-
187
-
188
- class CodeEntry(ClientEntry):
189
- _code = None
190
- is_async = None
191
-
192
- def __init__(self, *a, **kv):
193
- self.reload_event = anyio.Event()
194
- super().__init__(*a, *kv)
195
-
196
- @property
197
- def name(self):
198
- return P(self.subpath)
199
-
200
- async def set_value(self, value):
201
- await super().set_value(value)
202
- if value is NotGiven:
203
- self._code = None
204
- return
205
-
206
- try:
207
- v = self.value
208
- c = v["code"]
209
- a = v.get("is_async", None)
210
- p = make_proc(c, v.get("vars", ()), self.subpath, use_async=a)
211
- except Exception as exc:
212
- logger.warning("Could not compile @%s", self.subpath)
213
- await self.root.err.record_error(
214
- "compile",
215
- self.subpath,
216
- exc=exc,
217
- message="compiler error",
218
- )
219
- self._code = None
220
- else:
221
- await self.root.err.record_working("compile", self.subpath)
222
- self._code = p
223
- self.is_async = a
224
- self.reload_event.set()
225
- self.reload_event = anyio.Event()
226
-
227
- def __call__(self, *a, **kw):
228
- if self._code is None:
229
- raise EmptyCode(self._path)
230
- if self.is_async is False:
231
- proc = self._code
232
- if kw:
233
- proc = partial(proc, **kw)
234
- return anyio.to_thread.run_sync(proc, *a)
235
-
236
- return self._code(*a, **kw)
@@ -1,11 +0,0 @@
1
- """
2
- This module contains helper functions for packing+unpacking of single messages,
3
- plus an unpacker factory for streams.
4
- """
5
-
6
- # compatibility
7
- from __future__ import annotations
8
-
9
- from moat.util import * # noqa: F403
10
-
11
- __all__ = ["packer", "unpacker", "stream_unpacker"] # noqa: F405
@@ -1,180 +0,0 @@
1
- # command line interface
2
- from __future__ import annotations
3
-
4
- import sys
5
-
6
- import asyncclick as click
7
- from moat.util import P, Path, yprint
8
-
9
- from moat.kv.data import data_get
10
-
11
- ACL = set("rwdcxena")
12
- # read, write, delete, create, access, enumerate
13
-
14
-
15
- @click.group() # pylint: disable=undefined-variable
16
- async def cli():
17
- """Manage ACLs. Usage: … acl …"""
18
- pass
19
-
20
-
21
- @cli.command("list")
22
- @click.pass_obj
23
- async def list_(obj):
24
- """List ACLs."""
25
- res = await obj.client._request(
26
- action="enum_internal",
27
- path=("acl",),
28
- iter=False,
29
- nchain=obj.meta,
30
- empty=True,
31
- )
32
- yprint(res if obj.meta else res.result, stream=obj.stdout)
33
-
34
-
35
- @cli.command()
36
- @click.option(
37
- "-d",
38
- "--as-dict",
39
- default=None,
40
- help="Structure as dictionary. The argument is the key to use "
41
- "for values. Default: return as list",
42
- )
43
- @click.argument("name", nargs=1)
44
- @click.argument("path", nargs=1)
45
- @click.pass_obj
46
- async def dump(obj, name, path, as_dict):
47
- """Dump a complete (or partial) ACL."""
48
- path = P(path)
49
- await data_get(obj, Path("acl", name, path), internal=True, as_dict=as_dict)
50
-
51
-
52
- @cli.command()
53
- @click.argument("name", nargs=1)
54
- @click.argument("path", nargs=1)
55
- @click.pass_obj
56
- async def get(obj, name, path):
57
- """Read an ACL.
58
-
59
- This command does not test a path. Use "… acl test …" for that.
60
- """
61
- path = P(path)
62
- if not len(path):
63
- raise click.UsageError("You need a non-empty path.")
64
- res = await obj.client._request(
65
- action="get_internal",
66
- path=("acl", name) + path,
67
- iter=False,
68
- nchain=obj.meta,
69
- )
70
-
71
- if not obj.meta:
72
- try:
73
- res = res.value
74
- except KeyError:
75
- if obj.debug:
76
- print("No value.", file=sys.stderr)
77
- return
78
- yprint(res, stream=obj.stdout)
79
-
80
-
81
- @cli.command(name="set")
82
- @click.option(
83
- "-a",
84
- "--acl",
85
- default="+x",
86
- help="The value to set. Start with '+' to add, '-' to remove rights.",
87
- )
88
- @click.argument("name", nargs=1)
89
- @click.argument("path", nargs=1)
90
- @click.pass_obj
91
- async def set_(obj, acl, name, path):
92
- """Set or change an ACL."""
93
-
94
- path = P(path)
95
- if not len(path):
96
- raise click.UsageError("You need a non-empty path.")
97
- if len(acl) > 1 and acl[0] in "+-":
98
- mode = acl[0]
99
- acl = acl[1:]
100
- else:
101
- mode = "x"
102
- acl = set(acl)
103
-
104
- if acl - ACL:
105
- raise click.UsageError(f"You're trying to set an unknown ACL flag: {acl - ACL!r}")
106
-
107
- res = await obj.client._request(
108
- action="get_internal",
109
- path=("acl", name) + path,
110
- iter=False,
111
- nchain=3 if obj.meta else 1,
112
- )
113
- ov = set(res.get("value", ""))
114
- if ov - ACL:
115
- print(f"Warning: original ACL contains unknown: {ov - acl!r}", file=sys.stderr)
116
-
117
- if mode == "-" and not acl:
118
- res = await obj.client._request(
119
- action="delete_internal",
120
- path=("acl", name) + path,
121
- iter=False,
122
- chain=res.chain,
123
- )
124
- v = "-"
125
-
126
- else:
127
- if mode == "+":
128
- v = ov + acl
129
- elif mode == "-":
130
- v = ov - acl
131
- else:
132
- v = acl
133
- res = await obj.client._request(
134
- action="set_internal",
135
- path=("acl", name) + path,
136
- value="".join(v),
137
- iter=False,
138
- chain=res.get("chain", None),
139
- )
140
-
141
- if obj.meta:
142
- res = {
143
- "old": "".join(ov),
144
- "new": "".join(v),
145
- "chain": res.chain,
146
- "tock": res.tock,
147
- }
148
- yprint(res, stream=obj.stdout)
149
- else:
150
- res = {"old": "".join(ov), "new": "".join(v)}
151
- yprint(res, stream=obj.stdout)
152
-
153
-
154
- @cli.command()
155
- @click.option("-m", "--mode", default=None, help="Mode letter to test.")
156
- @click.option("-a", "--acl", default=None, help="ACL to test. Default: current")
157
- @click.argument("path", nargs=1)
158
- @click.pass_obj
159
- async def test(obj, path, acl, mode):
160
- """Test which ACL entry matches a path"""
161
- path = P(path)
162
- if not len(path):
163
- raise click.UsageError("You need a non-empty path.")
164
-
165
- if mode is not None and len(mode) != 1:
166
- raise click.UsageError("Mode must be one letter.")
167
- res = await obj.client._request(
168
- action="test_acl",
169
- path=path,
170
- iter=False,
171
- nchain=obj.meta,
172
- **({} if mode is None else {"mode": mode}),
173
- **({} if acl is None else {"acl": acl}),
174
- )
175
- if obj.meta:
176
- yprint(res, stream=obj.stdout)
177
- elif isinstance(res.access, bool):
178
- print("+" if res.access else "-", file=obj.stdout)
179
- else:
180
- print(res.access, file=obj.stdout)
@@ -1,261 +0,0 @@
1
- # command line interface
2
- from __future__ import annotations
3
-
4
- import asyncclick as click
5
- from moat.util import NotGiven, Path, split_arg, yprint
6
-
7
- from moat.kv.auth import gen_auth, loader
8
-
9
-
10
- @click.group(short_help="Manage authorization") # pylint: disable=undefined-variable
11
- @click.option("-m", "--method", default=None, help="Affect/use this auth method")
12
- @click.pass_obj
13
- async def cli(obj, method):
14
- """Manage authorization. Usage: … auth METHOD command…. Use '.' for 'all methods'."""
15
- a = await obj.client._request(action="auth_info")
16
- a = a.get("value", None)
17
- if a is not None:
18
- a = a["current"]
19
- obj.auth_current = a
20
- if method is None:
21
- obj.auth = a or (await one_auth(obj))
22
- else:
23
- if method == "-":
24
- method = None
25
- obj.auth = method
26
-
27
-
28
- async def enum_auth(obj):
29
- """List all configured auth types."""
30
- if obj.get("auth", None) is not None:
31
- yield obj.auth
32
- return
33
- # TODO create a method for this
34
- res = await obj.client._request(
35
- action="enum_internal",
36
- path=Path("auth"),
37
- iter=False,
38
- with_data=False,
39
- empty=True,
40
- nchain=0,
41
- )
42
- for r in res.result:
43
- print(r)
44
- yield r
45
-
46
-
47
- async def one_auth(obj):
48
- """Return the current auth method (from the command line or as used by the server)."""
49
- if obj.get("auth", None) is not None:
50
- return obj.auth
51
- auth = None
52
- async for a in enum_auth(obj):
53
- if auth is not None:
54
- raise click.UsageError("You need to set the auth method")
55
- auth = a
56
- if auth is None:
57
- raise click.UsageError("You need to set the auth method")
58
- return auth
59
-
60
-
61
- async def enum_typ(obj, kind="user", ident=None, nchain=0):
62
- """List all known auth entries of a kind."""
63
- async for auth in enum_auth(obj):
64
- if ident is not None:
65
- res = await obj.client._request(
66
- action="auth_list",
67
- typ=auth,
68
- kind=kind,
69
- ident=ident,
70
- iter=False,
71
- nchain=nchain,
72
- )
73
- yield res
74
- else:
75
- async with obj.client._stream(
76
- action="auth_list",
77
- typ=auth,
78
- kind=kind,
79
- nchain=nchain,
80
- ) as res:
81
- async for r in res:
82
- yield r
83
-
84
-
85
- @cli.command("list")
86
- @click.pass_obj
87
- async def list_(obj):
88
- """List known auth methods"""
89
- async for auth in enum_auth(obj):
90
- print(auth, file=obj.stdout)
91
-
92
-
93
- @cli.command()
94
- @click.option("-s", "--switch", is_flag=True, help="Switch to a different auth method")
95
- @click.pass_obj
96
- async def init(obj, switch):
97
- """Setup authorization"""
98
- if obj.auth_current is not None and not switch:
99
- raise click.UsageError("Authentication is already set up")
100
-
101
- await obj.client._request(action="set_auth_typ", typ=obj.auth)
102
- if obj.debug >= 0:
103
- if obj.auth:
104
- print("Authorization switched to", obj.auth, file=obj.stdout)
105
- else:
106
- print("Authorization turned off.", file=obj.stdout)
107
-
108
-
109
- @cli.group()
110
- async def user():
111
- """Manage users."""
112
- pass
113
-
114
-
115
- @user.command("list")
116
- @click.option(
117
- "-v",
118
- "--verbose",
119
- is_flag=True,
120
- help="Print complete results. Default: just the names",
121
- )
122
- @click.pass_obj # pylint: disable=function-redefined
123
- async def list_user(obj, verbose):
124
- """List all users (raw data)."""
125
- async for r in enum_typ(obj, nchain=obj.meta):
126
- if obj.meta or verbose:
127
- if obj.debug < 2:
128
- del r["seq"]
129
- del r["tock"]
130
- yprint(r, stream=obj.stdout)
131
- else:
132
- print(r.ident, file=obj.stdout)
133
-
134
-
135
- @user.command()
136
- @click.argument("ident", nargs=1)
137
- @click.pass_obj
138
- async def get(obj, ident):
139
- """Retrieve a user (processed)."""
140
- lv = loader(await one_auth(obj), "user", make=True, server=False)
141
- if obj.DEBUG:
142
- lv._length = 16
143
-
144
- u = await lv.recv(obj.client, ident, _initial=False)
145
- yprint(u.export(), stream=obj.stdout)
146
-
147
-
148
- @user.command()
149
- @click.argument("args", nargs=-1)
150
- @click.pass_obj
151
- async def add(obj, args):
152
- """Add a user."""
153
- await add_mod_user(obj, args, None)
154
-
155
-
156
- @user.command()
157
- @click.option("-n", "--new", is_flag=True, help="New: ignore previous content")
158
- @click.argument("ident", nargs=1)
159
- @click.argument("type", nargs=1)
160
- @click.argument("key", nargs=1)
161
- @click.argument("args", nargs=-1)
162
- @click.pass_obj
163
- async def param(obj, new, ident, type, key, args): # pylint: disable=redefined-builtin
164
- """Set user parameters for auth, conversion, etc."""
165
- auth = await one_auth(obj)
166
- u = loader(auth, "user", make=True, server=False)
167
- if obj.DEBUG:
168
- u._length = 16
169
- # ou = await u.recv(obj.client, ident, _initial=False) # unused
170
- res = await obj.client._request(
171
- action="get_internal",
172
- path=("auth", auth, "user", ident, type),
173
- iter=False,
174
- nchain=3,
175
- )
176
-
177
- kw = res.get("value", NotGiven)
178
- if new or kw is NotGiven:
179
- kw = {}
180
- res.chain = None
181
- if key == "-":
182
- if args:
183
- raise click.UsageError("You can't set params when deleting")
184
- res = await obj.client._request(
185
- action="delete_internal",
186
- path=("auth", auth, "user", ident, type),
187
- iter=False,
188
- chain=res.chain,
189
- )
190
-
191
- else:
192
- kw["key"] = key
193
-
194
- for a in args:
195
- split_arg(a, kw)
196
-
197
- res = await obj.client._request(
198
- action="set_internal",
199
- path=("auth", auth, "user", ident, type),
200
- iter=False,
201
- chain=res.chain,
202
- value=kw,
203
- )
204
- if obj.meta:
205
- # res.ident = ident
206
- # res.type = type
207
- yprint(res, stream=obj.stdout)
208
- else:
209
- print(ident, type, key, file=obj.stdout)
210
-
211
-
212
- @user.command()
213
- @click.argument("ident", nargs=1)
214
- @click.argument("args", nargs=-1)
215
- @click.pass_obj
216
- async def mod(obj, ident, args):
217
- """Change a user."""
218
- await add_mod_user(obj, args, ident)
219
-
220
-
221
- async def add_mod_user(obj, args, modify):
222
- auth = await one_auth(obj)
223
- u = loader(auth, "user", make=True, server=False)
224
- if obj.DEBUG:
225
- u._length = 16
226
- if modify:
227
- ou = await u.recv(obj.client, modify, _initial=False)
228
- kw = ou.export()
229
- else:
230
- kw = {}
231
- for a in args:
232
- split_arg(a, kw)
233
-
234
- u = u.build(kw, _initial=False)
235
- if modify is None or u.ident != modify:
236
- u._chain = None # new user
237
- else:
238
- u._chain = ou._chain
239
- res = await u.send(obj.client)
240
- if obj.meta:
241
- res.ident = u.ident
242
- yprint(res, stream=obj.stdout)
243
- else:
244
- print(u.ident, file=obj.stdout)
245
-
246
-
247
- @user.command(name="auth")
248
- @click.option(
249
- "-a",
250
- "--auth",
251
- type=str,
252
- default="root",
253
- help="Auth params. =file or 'type param=value…' Default: root",
254
- )
255
- @click.pass_obj
256
- async def auth_(obj, auth):
257
- """Test user authorization."""
258
- user = gen_auth(auth) # pylint: disable=redefined-outer-name
259
- await user.auth(obj.client)
260
- if obj.debug > 0:
261
- print("OK.", file=obj.stdout)