openshell-shared 0.1.2__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 (62) hide show
  1. api/__init__.py +1 -0
  2. api/manager/__init__.py +1 -0
  3. api/manager/v1/__init__.py +78 -0
  4. api/manager/v1/authentication.py +278 -0
  5. api/manager/v1/client.py +111 -0
  6. api/manager/v1/domains.py +64 -0
  7. api/manager/v1/entities.py +97 -0
  8. api/manager/v1/exceptions.py +98 -0
  9. api/manager/v1/identity.py +44 -0
  10. api/manager/v1/models.py +342 -0
  11. api/manager/v1/passports.py +131 -0
  12. api/manager/v1/sessions.py +83 -0
  13. api/manager/v1/transport.py +253 -0
  14. api/manager/v1/tunnels.py +120 -0
  15. cryptography/__init__.py +0 -0
  16. cryptography/certificate.py +390 -0
  17. cryptography/encoding.py +0 -0
  18. cryptography/identity.py +124 -0
  19. cryptography/keys.py +463 -0
  20. cryptography/signatures.py +63 -0
  21. cryptography/utils.py +0 -0
  22. domain/__init__.py +0 -0
  23. domain/domain.py +80 -0
  24. domain/membership.py +21 -0
  25. domain/permissions.py +14 -0
  26. domain/policies.py +2 -0
  27. identity/__init__.py +0 -0
  28. identity/identification.py +64 -0
  29. identity/store.py +150 -0
  30. modules/__init__.py +0 -0
  31. modules/shell/__init__.py +0 -0
  32. modules/shell/client.py +361 -0
  33. modules/shell/models.py +61 -0
  34. modules/shell/protocol.py +249 -0
  35. modules/shell/server.py +511 -0
  36. modules/shell/session.py +339 -0
  37. modules/utils.py +212 -0
  38. openshell_shared-0.1.2.dist-info/METADATA +59 -0
  39. openshell_shared-0.1.2.dist-info/RECORD +62 -0
  40. openshell_shared-0.1.2.dist-info/WHEEL +5 -0
  41. openshell_shared-0.1.2.dist-info/top_level.txt +7 -0
  42. protocols/__init__.py +0 -0
  43. protocols/negotiation/challenge.py +127 -0
  44. protocols/negotiation/models.py +28 -0
  45. standards/__init__.py +0 -0
  46. standards/certificates/__init__.py +0 -0
  47. standards/certificates/status.py +12 -0
  48. standards/certificates/types.py +11 -0
  49. standards/entities/__init__.py +0 -0
  50. standards/entities/types.py +14 -0
  51. standards/events/__init__.py +0 -0
  52. standards/events/schemas/__init__.py +0 -0
  53. standards/events/schemas/entity_registered.py +13 -0
  54. standards/events/types.py +18 -0
  55. standards/passports/__init__.py +0 -0
  56. standards/passports/types.py +5 -0
  57. standards/permissions/__init__.py +0 -0
  58. standards/permissions/types.py +14 -0
  59. standards/roles/__init__.py +0 -0
  60. standards/roles/types.py +8 -0
  61. standards/transports/__init__.py +0 -0
  62. standards/transports/types.py +24 -0
@@ -0,0 +1,511 @@
1
+ # shell/server.py
2
+
3
+ # =====================================================
4
+ # LIBRARY IMPORTS
5
+ # =====================================================
6
+
7
+ import asyncio
8
+ import traceback
9
+
10
+ from .protocol import ShellProtocol
11
+ from .models import (
12
+ ProtocolType,
13
+ ProtocolStandard
14
+ )
15
+ from .session import ShellSession
16
+
17
+
18
+ # =====================================================
19
+ # SHELL SERVER
20
+ # =====================================================
21
+
22
+ class ShellServer:
23
+
24
+ def __init__(
25
+ self,
26
+ communication_handler,
27
+ auth_token: str,
28
+ tunnel_token: str
29
+ ):
30
+
31
+ self._communication_handler = (
32
+ communication_handler
33
+ )
34
+
35
+ self._auth_token = auth_token
36
+ self._tunnel_token = tunnel_token
37
+
38
+ self._is_running = False
39
+
40
+ # -------------------------------------------------
41
+ # SESSION REGISTRY
42
+ # -------------------------------------------------
43
+
44
+ self._sessions = {}
45
+
46
+ # {
47
+ # session_token: {
48
+ # "session": ShellSession,
49
+ # "reader": asyncio.Task
50
+ # }
51
+ # }
52
+
53
+ # -------------------------------------------------
54
+ # PROTOCOL
55
+ # -------------------------------------------------
56
+
57
+ self._shell_protocol = ShellProtocol(
58
+ auth_token=self._auth_token,
59
+ tunnel_token=self._tunnel_token,
60
+ session_token=""
61
+ )
62
+
63
+ # =================================================
64
+ # SESSION READER
65
+ # =================================================
66
+
67
+ async def _session_reader(
68
+ self,
69
+ session_token: str,
70
+ session: ShellSession
71
+ ):
72
+
73
+ print(
74
+ f"[SHELL-SERVER] "
75
+ f"Reader started: "
76
+ f"{session_token}"
77
+ )
78
+
79
+ try:
80
+
81
+ while (
82
+ self._is_running
83
+ and
84
+ session.is_alive()
85
+ ):
86
+
87
+ output = await (
88
+ session.read_available()
89
+ )
90
+
91
+ if output:
92
+
93
+ packet = (
94
+ self._shell_protocol.output(
95
+ session_token=session_token,
96
+ data=output
97
+ )
98
+ )
99
+
100
+ await (
101
+ self._communication_handler
102
+ .send_datapackage(
103
+ packet
104
+ )
105
+ )
106
+
107
+ await asyncio.sleep(
108
+ 0.01
109
+ )
110
+
111
+ # -----------------------------------------
112
+ # PROCESS EXITED
113
+ # -----------------------------------------
114
+
115
+ exit_code = (
116
+ session.return_code()
117
+ )
118
+
119
+ packet = (
120
+ self._shell_protocol.exit(
121
+ session_token=session_token,
122
+ return_code=(
123
+ exit_code
124
+ if exit_code is not None
125
+ else -1
126
+ )
127
+ )
128
+ )
129
+
130
+ await (
131
+ self._communication_handler
132
+ .send_datapackage(
133
+ packet
134
+ )
135
+ )
136
+
137
+ except asyncio.CancelledError:
138
+
139
+ pass
140
+
141
+ except Exception:
142
+
143
+ print(
144
+ "[SHELL-SERVER] "
145
+ "Reader exception:"
146
+ )
147
+
148
+ traceback.print_exc()
149
+
150
+ finally:
151
+
152
+ print(
153
+ f"[SHELL-SERVER] "
154
+ f"Reader stopped: "
155
+ f"{session_token}"
156
+ )
157
+
158
+ # =================================================
159
+ # SESSION RESOLUTION
160
+ # =================================================
161
+
162
+ async def _get_session(
163
+ self,
164
+ session_token: str
165
+ ) -> ShellSession:
166
+
167
+ entry = self._sessions.get(
168
+ session_token
169
+ )
170
+
171
+ # ---------------------------------------------
172
+ # CREATE
173
+ # ---------------------------------------------
174
+
175
+ if entry is None:
176
+
177
+ print(
178
+ f"[SHELL-SERVER] "
179
+ f"Creating session: "
180
+ f"{session_token}"
181
+ )
182
+
183
+ session = ShellSession(
184
+ session_token=session_token
185
+ )
186
+
187
+ await session.start()
188
+
189
+ reader_task = (
190
+ asyncio.create_task(
191
+ self._session_reader(
192
+ session_token,
193
+ session
194
+ )
195
+ )
196
+ )
197
+
198
+ self._sessions[
199
+ session_token
200
+ ] = {
201
+ "session": session,
202
+ "reader": reader_task
203
+ }
204
+
205
+ return session
206
+
207
+ session = entry["session"]
208
+
209
+ # ---------------------------------------------
210
+ # RECREATE DEAD
211
+ # ---------------------------------------------
212
+
213
+ if not session.is_alive():
214
+
215
+ print(
216
+ f"[SHELL-SERVER] "
217
+ f"Recreating session: "
218
+ f"{session_token}"
219
+ )
220
+
221
+ try:
222
+
223
+ entry[
224
+ "reader"
225
+ ].cancel()
226
+
227
+ except Exception:
228
+
229
+ pass
230
+
231
+ try:
232
+
233
+ await session.close()
234
+
235
+ except Exception:
236
+
237
+ pass
238
+
239
+ session = ShellSession(
240
+ session_token=session_token
241
+ )
242
+
243
+ await session.start()
244
+
245
+ reader_task = (
246
+ asyncio.create_task(
247
+ self._session_reader(
248
+ session_token,
249
+ session
250
+ )
251
+ )
252
+ )
253
+
254
+ self._sessions[
255
+ session_token
256
+ ] = {
257
+ "session": session,
258
+ "reader": reader_task
259
+ }
260
+
261
+ return session
262
+
263
+ # =================================================
264
+ # CLOSE SESSION
265
+ # =================================================
266
+
267
+ async def _close_session(
268
+ self,
269
+ session_token: str
270
+ ):
271
+
272
+ entry = self._sessions.pop(
273
+ session_token,
274
+ None
275
+ )
276
+
277
+ if entry is None:
278
+ return
279
+
280
+ try:
281
+
282
+ entry[
283
+ "reader"
284
+ ].cancel()
285
+
286
+ except Exception:
287
+
288
+ pass
289
+
290
+ try:
291
+
292
+ await (
293
+ entry[
294
+ "session"
295
+ ].close()
296
+ )
297
+
298
+ except Exception:
299
+
300
+ traceback.print_exc()
301
+
302
+ # =================================================
303
+ # START
304
+ # =================================================
305
+
306
+ async def start(self):
307
+
308
+ self._is_running = True
309
+
310
+ print(
311
+ "[SHELL-SERVER] Started"
312
+ )
313
+
314
+ while self._is_running:
315
+
316
+ try:
317
+
318
+ packet = await (
319
+ self._communication_handler
320
+ .receive_datapackage()
321
+ )
322
+
323
+ if not isinstance(
324
+ packet,
325
+ dict
326
+ ):
327
+ continue
328
+
329
+ payload = packet.get(
330
+ "payload",
331
+ {}
332
+ )
333
+
334
+ session_token = packet.get(
335
+ "session_token"
336
+ )
337
+
338
+ if not session_token:
339
+ continue
340
+
341
+ # -------------------------------------
342
+ # PROTOCOL VALIDATION
343
+ # -------------------------------------
344
+
345
+ if (
346
+ payload.get(
347
+ "protocol"
348
+ )
349
+ !=
350
+ ProtocolStandard
351
+ .SHELL
352
+ .value
353
+ ):
354
+ continue
355
+
356
+ if (
357
+ payload.get(
358
+ "protocol_type"
359
+ )
360
+ !=
361
+ ProtocolType
362
+ .CLIENT
363
+ .value
364
+ ):
365
+ continue
366
+
367
+ event = payload.get(
368
+ "event"
369
+ )
370
+
371
+ # -------------------------------------
372
+ # OPEN
373
+ # -------------------------------------
374
+
375
+ if event == "OPEN":
376
+
377
+ await self._get_session(
378
+ session_token
379
+ )
380
+
381
+ continue
382
+
383
+ # -------------------------------------
384
+ # INPUT
385
+ # -------------------------------------
386
+
387
+ if event == "INPUT":
388
+
389
+ session = await (
390
+ self._get_session(
391
+ session_token
392
+ )
393
+ )
394
+
395
+ await (
396
+ session.write(
397
+ payload.get(
398
+ "data",
399
+ ""
400
+ )
401
+ )
402
+ )
403
+
404
+ continue
405
+
406
+ # -------------------------------------
407
+ # RESIZE
408
+ # -------------------------------------
409
+
410
+ if event == "RESIZE":
411
+
412
+ session = await (
413
+ self._get_session(
414
+ session_token
415
+ )
416
+ )
417
+
418
+ await (
419
+ session.resize(
420
+ payload.get(
421
+ "rows",
422
+ 24
423
+ ),
424
+ payload.get(
425
+ "cols",
426
+ 80
427
+ )
428
+ )
429
+ )
430
+
431
+ continue
432
+
433
+ # -------------------------------------
434
+ # SIGNAL
435
+ # -------------------------------------
436
+
437
+ if event == "SIGNAL":
438
+
439
+ session = await (
440
+ self._get_session(
441
+ session_token
442
+ )
443
+ )
444
+
445
+ await (
446
+ session.signal(
447
+ payload.get(
448
+ "signal"
449
+ )
450
+ )
451
+ )
452
+
453
+ continue
454
+
455
+ # -------------------------------------
456
+ # CLOSE
457
+ # -------------------------------------
458
+
459
+ if event == "CLOSE":
460
+
461
+ await (
462
+ self._close_session(
463
+ session_token
464
+ )
465
+ )
466
+
467
+ continue
468
+
469
+ except asyncio.CancelledError:
470
+
471
+ break
472
+
473
+ except Exception:
474
+
475
+ print(
476
+ "[SHELL-SERVER] "
477
+ "Captured exception:"
478
+ )
479
+
480
+ traceback.print_exc()
481
+
482
+ # =================================================
483
+ # CLEANUP
484
+ # =================================================
485
+
486
+ print(
487
+ "[SHELL-SERVER] "
488
+ "Closing sessions..."
489
+ )
490
+
491
+ for session_token in list(
492
+ self._sessions.keys()
493
+ ):
494
+
495
+ try:
496
+
497
+ await (
498
+ self._close_session(
499
+ session_token
500
+ )
501
+ )
502
+
503
+ except Exception:
504
+
505
+ traceback.print_exc()
506
+
507
+ self._sessions.clear()
508
+
509
+ print(
510
+ "[SHELL-SERVER] Stopped"
511
+ )