moat-kv 0.70.24__py3-none-any.whl → 0.71.6__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.
- moat/kv/__init__.py +6 -7
- moat/kv/_cfg.yaml +5 -8
- moat/kv/actor/__init__.py +2 -1
- moat/kv/actor/deletor.py +4 -1
- moat/kv/auth/__init__.py +12 -13
- moat/kv/auth/_test.py +4 -1
- moat/kv/auth/password.py +11 -7
- moat/kv/backend/mqtt.py +4 -8
- moat/kv/client.py +20 -39
- moat/kv/code.py +3 -3
- moat/kv/command/data.py +4 -3
- moat/kv/command/dump/__init__.py +29 -29
- moat/kv/command/internal.py +2 -3
- moat/kv/command/job.py +1 -2
- moat/kv/command/type.py +3 -6
- moat/kv/data.py +9 -8
- moat/kv/errors.py +16 -8
- moat/kv/mock/__init__.py +2 -12
- moat/kv/model.py +28 -32
- moat/kv/obj/__init__.py +3 -3
- moat/kv/obj/command.py +3 -3
- moat/kv/runner.py +4 -5
- moat/kv/server.py +106 -126
- moat/kv/types.py +8 -6
- {moat_kv-0.70.24.dist-info → moat_kv-0.71.6.dist-info}/METADATA +7 -6
- moat_kv-0.71.6.dist-info/RECORD +47 -0
- {moat_kv-0.70.24.dist-info → moat_kv-0.71.6.dist-info}/WHEEL +1 -1
- moat_kv-0.71.6.dist-info/licenses/LICENSE +3 -0
- moat_kv-0.71.6.dist-info/licenses/LICENSE.APACHE2 +202 -0
- moat_kv-0.71.6.dist-info/licenses/LICENSE.MIT +20 -0
- moat_kv-0.71.6.dist-info/top_level.txt +1 -0
- build/lib/docs/source/conf.py +0 -201
- build/lib/examples/pathify.py +0 -45
- build/lib/moat/kv/__init__.py +0 -19
- build/lib/moat/kv/_cfg.yaml +0 -97
- build/lib/moat/kv/_main.py +0 -91
- build/lib/moat/kv/actor/__init__.py +0 -98
- build/lib/moat/kv/actor/deletor.py +0 -139
- build/lib/moat/kv/auth/__init__.py +0 -444
- build/lib/moat/kv/auth/_test.py +0 -166
- build/lib/moat/kv/auth/password.py +0 -234
- build/lib/moat/kv/auth/root.py +0 -58
- build/lib/moat/kv/backend/__init__.py +0 -67
- build/lib/moat/kv/backend/mqtt.py +0 -74
- build/lib/moat/kv/backend/serf.py +0 -45
- build/lib/moat/kv/client.py +0 -1025
- build/lib/moat/kv/code.py +0 -236
- build/lib/moat/kv/codec.py +0 -11
- build/lib/moat/kv/command/__init__.py +0 -1
- build/lib/moat/kv/command/acl.py +0 -180
- build/lib/moat/kv/command/auth.py +0 -261
- build/lib/moat/kv/command/code.py +0 -293
- build/lib/moat/kv/command/codec.py +0 -186
- build/lib/moat/kv/command/data.py +0 -265
- build/lib/moat/kv/command/dump/__init__.py +0 -143
- build/lib/moat/kv/command/error.py +0 -149
- build/lib/moat/kv/command/internal.py +0 -248
- build/lib/moat/kv/command/job.py +0 -433
- build/lib/moat/kv/command/log.py +0 -53
- build/lib/moat/kv/command/server.py +0 -114
- build/lib/moat/kv/command/type.py +0 -201
- build/lib/moat/kv/config.py +0 -46
- build/lib/moat/kv/data.py +0 -216
- build/lib/moat/kv/errors.py +0 -561
- build/lib/moat/kv/exceptions.py +0 -126
- build/lib/moat/kv/mock/__init__.py +0 -101
- build/lib/moat/kv/mock/mqtt.py +0 -159
- build/lib/moat/kv/mock/serf.py +0 -250
- build/lib/moat/kv/mock/tracer.py +0 -63
- build/lib/moat/kv/model.py +0 -1069
- build/lib/moat/kv/obj/__init__.py +0 -646
- build/lib/moat/kv/obj/command.py +0 -241
- build/lib/moat/kv/runner.py +0 -1347
- build/lib/moat/kv/server.py +0 -2809
- build/lib/moat/kv/types.py +0 -513
- debian/moat-kv/usr/lib/python3/dist-packages/docs/source/conf.py +0 -201
- debian/moat-kv/usr/lib/python3/dist-packages/examples/pathify.py +0 -45
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/__init__.py +0 -19
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/_cfg.yaml +0 -97
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/_main.py +0 -91
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/actor/__init__.py +0 -98
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/actor/deletor.py +0 -139
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/__init__.py +0 -444
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/_test.py +0 -166
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/password.py +0 -234
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/root.py +0 -58
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/__init__.py +0 -67
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/mqtt.py +0 -74
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/serf.py +0 -45
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/client.py +0 -1025
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/code.py +0 -236
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/codec.py +0 -11
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/__init__.py +0 -1
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/acl.py +0 -180
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/auth.py +0 -261
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/code.py +0 -293
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/codec.py +0 -186
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/data.py +0 -265
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/dump/__init__.py +0 -143
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/error.py +0 -149
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/internal.py +0 -248
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/job.py +0 -433
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/log.py +0 -53
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/server.py +0 -114
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/type.py +0 -201
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/config.py +0 -46
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/data.py +0 -216
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/errors.py +0 -561
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/exceptions.py +0 -126
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/__init__.py +0 -101
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/mqtt.py +0 -159
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/serf.py +0 -250
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/tracer.py +0 -63
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/model.py +0 -1069
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/obj/__init__.py +0 -646
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/obj/command.py +0 -241
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/runner.py +0 -1347
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/server.py +0 -2809
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/types.py +0 -513
- docs/source/conf.py +0 -201
- examples/pathify.py +0 -45
- moat/kv/backend/serf.py +0 -45
- moat/kv/codec.py +0 -11
- moat/kv/mock/serf.py +0 -250
- moat_kv-0.70.24.dist-info/RECORD +0 -137
- moat_kv-0.70.24.dist-info/top_level.txt +0 -9
- {moat_kv-0.70.24.dist-info → moat_kv-0.71.6.dist-info}/licenses/LICENSE.txt +0 -0
@@ -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
|
@@ -1,166 +0,0 @@
|
|
1
|
-
#
|
2
|
-
"""
|
3
|
-
Test auth method.
|
4
|
-
|
5
|
-
Does not limit anything, allows everything.
|
6
|
-
"""
|
7
|
-
|
8
|
-
from __future__ import annotations
|
9
|
-
|
10
|
-
import logging
|
11
|
-
|
12
|
-
log = logging.getLogger(__name__)
|
13
|
-
|
14
|
-
from ..client import Client
|
15
|
-
from . import (
|
16
|
-
BaseClientAuth,
|
17
|
-
BaseClientAuthMaker,
|
18
|
-
BaseServerAuthMaker,
|
19
|
-
RootServerUser,
|
20
|
-
null_client_login,
|
21
|
-
null_server_login,
|
22
|
-
)
|
23
|
-
|
24
|
-
|
25
|
-
def load(typ: str, *, make: bool = False, server: bool):
|
26
|
-
if typ == "client":
|
27
|
-
if server:
|
28
|
-
return null_server_login
|
29
|
-
else:
|
30
|
-
return null_client_login
|
31
|
-
if typ != "user":
|
32
|
-
raise NotImplementedError("This module only handles users")
|
33
|
-
if server:
|
34
|
-
if make:
|
35
|
-
return ServerUserMaker
|
36
|
-
else:
|
37
|
-
return ServerUser
|
38
|
-
else:
|
39
|
-
if make:
|
40
|
-
return ClientUserMaker
|
41
|
-
else:
|
42
|
-
return ClientUser
|
43
|
-
|
44
|
-
|
45
|
-
class ServerUserMaker(BaseServerAuthMaker):
|
46
|
-
name = None
|
47
|
-
|
48
|
-
@property
|
49
|
-
def ident(self):
|
50
|
-
return self.name
|
51
|
-
|
52
|
-
# Overly-complicated methods of exchanging the user name
|
53
|
-
|
54
|
-
@classmethod
|
55
|
-
async def recv(cls, cmd, data):
|
56
|
-
await cmd.send(step="GiveName")
|
57
|
-
msg = await cmd.recv()
|
58
|
-
assert msg.step == "HasName"
|
59
|
-
self = cls()
|
60
|
-
self.name = msg.name
|
61
|
-
return self
|
62
|
-
|
63
|
-
async def send(self, cmd):
|
64
|
-
await cmd.send(step="SendWant")
|
65
|
-
msg = await cmd.recv()
|
66
|
-
assert msg.step == "WantName"
|
67
|
-
await cmd.send(step="SendName", name=self.name, chain=self._chain.serialize(nchain=3))
|
68
|
-
msg = await cmd.recv()
|
69
|
-
|
70
|
-
# Annoying methods to read+save the user name from/to KV
|
71
|
-
|
72
|
-
@classmethod
|
73
|
-
def load(cls, data):
|
74
|
-
self = super().load(data)
|
75
|
-
self.name = data.name
|
76
|
-
return self
|
77
|
-
|
78
|
-
|
79
|
-
class ServerUser(RootServerUser):
|
80
|
-
pass
|
81
|
-
|
82
|
-
|
83
|
-
class ClientUserMaker(BaseClientAuthMaker):
|
84
|
-
gen_schema = dict(
|
85
|
-
type="object",
|
86
|
-
additionalProperties=False,
|
87
|
-
properties=dict(name=dict(type="string", minLength=1, pattern="^[a-zA-Z][a-zA-Z0-9_]*$")),
|
88
|
-
required=["name"],
|
89
|
-
)
|
90
|
-
mod_schema = dict(
|
91
|
-
type="object",
|
92
|
-
additionalProperties=False,
|
93
|
-
properties=dict(name=dict(type="string", minLength=1, pattern="^[a-zA-Z][a-zA-Z0-9_]*$")),
|
94
|
-
# required=[],
|
95
|
-
)
|
96
|
-
name = None
|
97
|
-
|
98
|
-
@property
|
99
|
-
def ident(self):
|
100
|
-
return self.name
|
101
|
-
|
102
|
-
# Overly-complicated methods of exchanging the user name
|
103
|
-
|
104
|
-
@classmethod
|
105
|
-
async def recv(cls, client: Client, ident: str, _kind: str = "user", _initial=True):
|
106
|
-
"""Read a record representing a user from the server."""
|
107
|
-
async with client._stream(
|
108
|
-
action="auth_get",
|
109
|
-
typ=cls._auth_method,
|
110
|
-
kind=_kind,
|
111
|
-
ident=ident,
|
112
|
-
stream=True,
|
113
|
-
nchain=0 if _initial else 2,
|
114
|
-
) as s:
|
115
|
-
m = await s.recv()
|
116
|
-
assert m.step == "SendWant", m
|
117
|
-
await s.send(step="WantName")
|
118
|
-
m = await s.recv()
|
119
|
-
assert m.step == "SendName", m
|
120
|
-
assert m.name == ident
|
121
|
-
|
122
|
-
self = cls(name=m.name, _initial=_initial)
|
123
|
-
self._chain = m.chain
|
124
|
-
return self
|
125
|
-
|
126
|
-
async def send(self, client: Client, _kind="user"):
|
127
|
-
"""Send a record representing this user to the server."""
|
128
|
-
async with client._stream(
|
129
|
-
action="auth_set",
|
130
|
-
typ=type(self)._auth_method,
|
131
|
-
kind=_kind,
|
132
|
-
ident=self.ident,
|
133
|
-
stream=True,
|
134
|
-
) as s:
|
135
|
-
# we could initially send the ident but don't here, for testing
|
136
|
-
m = await s.recv()
|
137
|
-
assert m.step == "GiveName", m
|
138
|
-
await s.send(step="HasName", name=self.name, chain=self._chain)
|
139
|
-
m = await s.recv()
|
140
|
-
assert m.chain.prev is None
|
141
|
-
|
142
|
-
def export(self):
|
143
|
-
"""Return the data required to re-create the user via :meth:`build`."""
|
144
|
-
return {"name": self.name}
|
145
|
-
|
146
|
-
|
147
|
-
class ClientUser(BaseClientAuth):
|
148
|
-
name = None
|
149
|
-
|
150
|
-
schema = dict(
|
151
|
-
type="object",
|
152
|
-
additionalProperties=False,
|
153
|
-
properties=dict(name=dict(type="string", minLength=1, pattern="^[a-zA-Z][a-zA-Z0-9_]*$")),
|
154
|
-
required=["name"],
|
155
|
-
)
|
156
|
-
_name = None
|
157
|
-
|
158
|
-
@property
|
159
|
-
def ident(self):
|
160
|
-
return self.name
|
161
|
-
|
162
|
-
@classmethod
|
163
|
-
def build(cls, user):
|
164
|
-
self = super().build(user)
|
165
|
-
self.name = user["name"]
|
166
|
-
return self
|