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,139 +0,0 @@
1
- """
2
- This module implements additional code for the server-side DeleteActor,
3
- which is used to clean up the list of deleted nodes.
4
- """
5
-
6
- from __future__ import annotations
7
-
8
- import weakref
9
- from collections import deque
10
-
11
- import anyio
12
- from asyncactor import Actor, PingEvent, TagEvent
13
- from asyncactor.backend import get_transport
14
-
15
- TAGS = 4
16
-
17
-
18
- class DeleteActor:
19
- _enabled = None
20
-
21
- def __init__(self, server):
22
- self._server = weakref.ref(server)
23
- self.deleted = deque()
24
- self.tags = []
25
- self.actor = None
26
-
27
- self.max_seen = 0
28
- self.n_tags = 0
29
- self.n_pings = 0
30
- self.n_nodes = 0
31
-
32
- @property
33
- def server(self):
34
- return self._server()
35
-
36
- async def tock_me(self):
37
- """
38
- Add the current tock to our buffer.
39
-
40
- This is updated whenever a new leader is selected.
41
- """
42
- self.tags.append(self.server.tock)
43
- self.tags = self.tags[-TAGS:]
44
- await self.actor.set_value((self.tags[0], self.tags[-1]))
45
-
46
- def add_deleted(self, nodes: NodeSet): # noqa: F821
47
- """
48
- These nodes are deleted. Remember them for some time.
49
- """
50
- if self.n_nodes == 0:
51
- return
52
- self.deleted.append((self.server.tock, nodes))
53
-
54
- def purge_to(self, tock):
55
- """
56
- Sufficient time has passed since this tock was seen, while all
57
- Delete actor nodes were active. Finally flush the entries that have
58
- been deleted before it.
59
- """
60
- while self.deleted and self.deleted[0][0] < tock:
61
- d = self.deleted.popleft()
62
- self.server.purge_deleted(d[1])
63
-
64
- async def enable(self, n):
65
- """
66
- Enable this actor, as a group of N.
67
- """
68
- if self.actor is None:
69
- self._enabled = True
70
- else:
71
- await self.actor.enable(n)
72
- self.n_tags = 0
73
- self.n_pings = 0
74
- self.n_nodes = n
75
-
76
- async def disable(self, n: int = 0):
77
- """
78
- Disable this actor. It will still listen, and require N Delete
79
- actor members in order to flush its deletion entries.
80
-
81
- Completely disable deletion flushing by passing n=0.
82
- """
83
- if self.actor is None:
84
- self._enabled = False
85
- else:
86
- await self.actor.disable()
87
- self.n_tags = 0
88
- self.n_pings = 0
89
- self.n_nodes = n
90
-
91
- async def run(self, evt: anyio.abc.Event = None):
92
- """
93
- The task that monitors the Delete actor.
94
- """
95
- try:
96
- T = get_transport("moat_kv")
97
- async with Actor(
98
- T(self.server.backend, *self.server.cfg.server.root, "del"),
99
- name=self.server.node.name,
100
- cfg=self.server.cfg.server.delete,
101
- enabled=False,
102
- ) as actor:
103
- self.actor = actor
104
- if self._enabled is not None:
105
- if self._enabled:
106
- await actor.enable()
107
- else:
108
- await actor.disable()
109
- if evt is not None:
110
- evt.set()
111
- async for evt in actor:
112
- if isinstance(evt, PingEvent):
113
- val = evt.value
114
- if val is None:
115
- self.n_pings = self.n_tags = 0
116
- continue
117
- if len(evt.msg.history) < self.n_nodes:
118
- self.n_pings = self.n_tags = 0
119
- continue
120
- self.n_pings += 1
121
- if self.n_pings > self.n_nodes:
122
- mx, self.max_seen = (
123
- self.max_seen,
124
- max(self.max_seen, val[1]),
125
- )
126
- if val[0] > mx > 0:
127
- await self.server.resync_deleted(evt.msg.history)
128
- continue
129
- self.purge_to(val[0])
130
- self.max_seen = max(self.max_seen, val[1])
131
-
132
- elif isinstance(evt, TagEvent):
133
- if actor.history_size == self.n_nodes:
134
- self.n_tags += 1
135
- if self.n_tags > 2:
136
- self.purge_to(self.tags[0])
137
- await self.tock_me()
138
- finally:
139
- self.actor = None
@@ -1,444 +0,0 @@
1
- # moat.kv.auth
2
- # template for authorization
3
-
4
- """
5
- This set of modules authenticates users.
6
-
7
- A submodule is expected to export a "load(type:str, make:bool, server:bool)"
8
- method that returns a class.
9
-
10
- It must recognize, at minimum:
11
-
12
- * load("stream", server:bool):
13
-
14
- A filter used for logging in a user.
15
-
16
- * load("user", server:bool, make:bool)
17
-
18
- A class used to represent the user, or a way to create/manipulate a user record.
19
-
20
- The client process is:
21
-
22
- * create a user:
23
-
24
- * Create a :class:`BaseUserMaker` by calling :meth:`BaseUserMaker.build`
25
- with a record conforming to its schema.
26
-
27
- * Export that and save it to the server, at (None,"auth","user",NAME).
28
-
29
- * modify a user:
30
-
31
- * Call :class:`BaseUserMaker.import` with the value from the server.
32
-
33
- * Log in:
34
-
35
- * Create a :class:`BaseUser` by calling :meth:`BaseUserMaker.build`
36
- with a record conforming to its schema.
37
-
38
- * Call :meth:`BaseUser.auth`.
39
-
40
- The server process is:
41
-
42
- * create a user:
43
-
44
- * The server intercepts the write call and uses the data to call
45
- :meth:`BaseServerAuthMaker.build`.
46
-
47
- * It calls :meth:`BaseServerMaker.save` and stores the actual result.
48
-
49
- * modify a user:
50
-
51
- * the server calls :meth:`BaseServerAuth.read` with the stored data,
52
- then sends a record created with :meth:`BaseServerAuth.save` to the client.
53
-
54
- * verify a user:
55
-
56
- * The server calls :meth:`BaseServerAuth.read` with the stored data.
57
-
58
- * The server calls :meth:`BaseServerAuth.auth` with the record from the client.
59
-
60
- """
61
-
62
- from __future__ import annotations
63
-
64
- import io
65
- from importlib import import_module
66
-
67
- import jsonschema
68
- from moat.util import NotGiven, Path, attrdict, split_arg, yload
69
-
70
- from ..client import Client, NoData
71
- from ..exceptions import NoAuthModuleError
72
- from ..model import Entry
73
- from ..server import ServerClient, StreamCommand
74
- from ..types import ACLFinder, NullACL
75
-
76
- # Empty schema
77
- null_schema = {"type": "object", "additionalProperties": False}
78
-
79
- # Additional schema data for specific types
80
- add_schema = {
81
- "user": {
82
- "acl": {
83
- "type": "object",
84
- "additionalProperties": False,
85
- "properties": {"key": {type: "string", "minLength": 1}},
86
- },
87
- "conv": {
88
- "type": "object",
89
- "additionalProperties": False,
90
- "properties": {"key": {type: "string", "minLength": 1}},
91
- },
92
- },
93
- }
94
-
95
-
96
- def loader(method: str, typ: str, *a, **k):
97
- m = method
98
- if "." not in m:
99
- m = "moat.kv.auth." + m
100
- cls = import_module(m).load(typ, *a, **k)
101
- cls._auth_method = method
102
- cls._auth_typ = typ
103
- cls.aux_schemas = add_schema.get(typ, null_schema)
104
- return cls
105
-
106
-
107
- def gen_auth(s: str):
108
- """
109
- Generate auth data from parameters or YAML file (if first char is '=').
110
- """
111
- if not isinstance(s, str):
112
- return s # called twice. Oh well.
113
-
114
- m, *p = s.split()
115
- if len(p) == 0 and m[0] == "=":
116
- with open(m[1:], encoding="utf-8") as f:
117
- kw = yload(f)
118
- m = kw.pop("type")
119
- else:
120
- kw = {}
121
- for pp in p:
122
- split_arg(pp, kw)
123
- try:
124
- m = loader(m, "user", server=False)
125
- except ModuleNotFoundError:
126
- raise NoAuthModuleError(m) from None
127
- return m.build(kw)
128
-
129
-
130
- def load(typ: str, *, make: bool = False, server: bool):
131
- """
132
- This procedure is used to load and return a user management class.
133
-
134
- Arguments:
135
- typ: the type of module to load.
136
- make: flag that the caller wants a record-generating, not a
137
- record-using class.
138
- server: flag that the class is to be used on the server, not on the
139
- client.
140
-
141
- Types:
142
- stream: the filter used for authorizing the user.
143
- user: represents a user record.
144
- """
145
- raise NotImplementedError("You need to implement me") # pragma: no cover
146
-
147
-
148
- async def null_server_login(stream):
149
- return stream
150
-
151
-
152
- async def null_client_login(stream, user: BaseClientAuth): # pylint: disable=unused-argument
153
- return stream
154
-
155
-
156
- def _load_example(typ: str, make: bool, server: bool): # pragma: no cover
157
- """example code for :proc:`load`"""
158
- if typ == "client":
159
- if server:
160
- return null_server_login
161
- else:
162
- return null_client_login
163
- if typ != "user":
164
- raise NotImplementedError("This module only handles users")
165
- if server:
166
- if make:
167
- return BaseServerAuthMaker
168
- else:
169
- return BaseServerAuth
170
- else:
171
- if make:
172
- return BaseClientAuthMaker
173
- else:
174
- return BaseClientAuth
175
-
176
-
177
- class _AuthLoaded:
178
- # This class is mainly there to appease pylint
179
- _auth_method = None
180
-
181
-
182
- class BaseClientAuth(_AuthLoaded):
183
- """
184
- This class is used for creating a data record which authenticates a user.
185
-
186
- The schema verifies the input to :meth:`build`.
187
- """
188
-
189
- schema = null_schema
190
-
191
- def __init__(self, **data):
192
- jsonschema.validate(instance=data, schema=type(self).schema)
193
- for k, v in data.items():
194
- setattr(self, k, v)
195
-
196
- @classmethod
197
- def build(cls, user):
198
- """
199
- Create a user record from the data conforming to this schema.
200
- """
201
- return cls(**user)
202
-
203
- @property
204
- def ident(self):
205
- """Some user identifier.
206
- Required so that the server can actually find the record.
207
- """
208
- return "*"
209
-
210
- async def auth(self, client: Client, chroot=()):
211
- """
212
- Authorizes this record with the server.
213
- """
214
- try:
215
- await client._request(
216
- action="auth",
217
- typ=self._auth_method,
218
- iter=False,
219
- chroot=chroot,
220
- ident=self.ident,
221
- data=self.auth_data(),
222
- )
223
- except NoData:
224
- pass
225
-
226
- def auth_data(self):
227
- """
228
- Additional data for the initial auth message.
229
-
230
- Does NOT include 'ident', that gets added explicitly by :meth:`auth`.
231
- """
232
- return {}
233
-
234
-
235
- class BaseClientAuthMaker(_AuthLoaded):
236
- """
237
- This class is used for creating a data record which describes a user record.
238
-
239
- While :class:`BaseClientAuth` is used solely for authentication,
240
- this class is used to represent the server's user data.
241
-
242
- The schema verifies the input to :meth:`build`.
243
- """
244
-
245
- gen_schema = null_schema
246
- mod_schema = null_schema
247
- _chain = None
248
-
249
- def __init__(self, _initial=True, **data):
250
- if _initial:
251
- jsonschema.validate(instance=data, schema=type(self).gen_schema)
252
- else:
253
- jsonschema.validate(instance=data, schema=type(self).mod_schema)
254
- for k, v in data.items():
255
- setattr(self, k, v)
256
-
257
- @classmethod
258
- def build(cls, user, _initial=True):
259
- """
260
- Create a user record from the data conforming to this schema.
261
- """
262
- return cls(**user, _initial=_initial)
263
-
264
- def export(self):
265
- """Return the data required to re-create the user via :meth:`build`."""
266
- return {} # pragma: no cover
267
-
268
- @property
269
- def ident(self):
270
- """The identifier for this user.
271
-
272
- Required so that the server can actually find the record.
273
- """
274
- return "*" # pragma: no cover
275
-
276
- @classmethod
277
- async def recv(cls, client: Client, ident: str, _kind="user", _initial=True):
278
- """Read this user from the server.
279
-
280
- Sample code …
281
- """
282
- # pragma: no cover
283
- res = await client._request(
284
- "auth_get",
285
- typ=cls._auth_method,
286
- kind=_kind,
287
- ident=ident,
288
- nchain=0 if _initial else 2,
289
- )
290
- self = cls(_initial=_initial)
291
- self._chain = res.chain
292
- return self
293
-
294
- async def send(self, client: Client, _kind="user"):
295
- """Send this user to the server."""
296
- try:
297
- await client._request(
298
- "auth_set",
299
- iter=False,
300
- typ=type(self)._auth_method,
301
- kind=_kind,
302
- ident=self.ident,
303
- chain=self._chain,
304
- data=self.send_data(),
305
- )
306
- except NoData:
307
- pass
308
-
309
- def send_data(self):
310
- return {}
311
-
312
-
313
- class BaseServerAuth(_AuthLoaded):
314
- """
315
- This class is used on the server to represent / verify a user.
316
-
317
- The schema verifies whatever data the associated ``ClientAuth`` initially sends.
318
- """
319
-
320
- schema = null_schema.copy()
321
- schema["additionalProperties"] = True
322
-
323
- is_super_root = False
324
- can_create_subtree = False
325
- can_auth_read = False
326
- can_auth_write = False
327
-
328
- def __init__(self, data: dict = {}): # pylint: disable=dangerous-default-value
329
- if data:
330
- for k, v in data.items():
331
- setattr(self, k, v)
332
-
333
- @classmethod
334
- def load(cls, data: Entry):
335
- """Create a ServerAuth object from existing stored data"""
336
- return cls(data.data)
337
-
338
- async def auth(self, cmd: StreamCommand, data): # pylint: disable=unused-argument
339
- """Verify that @data authenticates this user."""
340
- jsonschema.validate(instance=data.get("data", {}), schema=type(self).schema)
341
-
342
- def aux_conv(self, data: Entry, root: Entry):
343
- from ..types import ConvNull
344
-
345
- try:
346
- data = data["conv"].data["key"]
347
- res, _ = root.follow_acl(Path(None, "conv", data), create=False, nulls_ok=True)
348
- return res
349
- except (KeyError, AttributeError):
350
- return ConvNull
351
-
352
- def aux_acl(self, data: Entry, root: Entry):
353
- try:
354
- data = data["acl"].data["key"]
355
- if data == "*":
356
- return NullACL
357
- acl, _ = root.follow_acl(Path(None, "acl", data), create=False, nulls_ok=True)
358
- return ACLFinder(acl)
359
- except (KeyError, AttributeError):
360
- return NullACL
361
-
362
- def info(self):
363
- """
364
- Return whatever public data the user might want to have displayed.
365
-
366
- This includes information to identify the user, but not anything
367
- that'd be suitable for verifying or even faking authorization.
368
- """
369
- return {}
370
-
371
- async def check_read(self, *path, client: ServerClient, data=None): # pylint: disable=unused-argument
372
- """Check that this user may read the element at this location.
373
- This method may modify the data.
374
- """
375
- return data
376
-
377
- async def check_write(self, *path, client: ServerClient, data=None): # pylint: disable=unused-argument
378
- """Check that this user may write the element at this location.
379
- This method may modify the data.
380
- """
381
- return data
382
-
383
-
384
- class RootServerUser(BaseServerAuth):
385
- """The default user when no auth is required
386
-
387
- Interim record. TODO: create a separate ACL thing.
388
- """
389
-
390
- is_super_root = True
391
- can_create_subtree = True
392
- can_auth_read = True
393
- can_auth_write = True
394
-
395
-
396
- class BaseServerAuthMaker(_AuthLoaded):
397
- """
398
- This class is used on the server to verify the transmitted user record
399
- and to store it in MoaT-KV.
400
-
401
- The schema verifies the data from the client.
402
- """
403
-
404
- schema = null_schema
405
- aux_schemas = None # set by the loader
406
-
407
- def __init__(self, chain=None, data=None):
408
- if data is not None and data is not NotGiven:
409
- for k, v in data.items():
410
- setattr(self, k, v)
411
- self._chain = chain
412
-
413
- @classmethod
414
- def load(cls, data: Entry):
415
- """Read the user data from MoaT-KV"""
416
- return cls(chain=data.chain, data=data.data)
417
-
418
- @classmethod
419
- async def recv(
420
- cls,
421
- cmd: StreamCommand,
422
- data: attrdict, # pylint: disable=unused-argument
423
- ) -> BaseServerAuthMaker:
424
- """Create/update a new user by reading the record from the client"""
425
- dt = data.get("data", None) or {}
426
- jsonschema.validate(instance=dt, schema=cls.schema)
427
- self = cls(chain=data["chain"], data=dt)
428
- return self
429
-
430
- @property
431
- def ident(self):
432
- """The record to store this user under."""
433
- return "*"
434
-
435
- def save(self):
436
- """Return a record to represent this user, suitable for saving to MoaT-KV"""
437
- # does NOT contain "ident" or "chain"!
438
- return {}
439
-
440
- async def send(self, cmd: StreamCommand): # pylint: disable=unused-argument
441
- """Send a record to the client, possibly multi-step / secured / whatever"""
442
- res = {}
443
- res["chain"] = self._chain.serialize() if self._chain else None
444
- return res