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
identity/store.py ADDED
@@ -0,0 +1,150 @@
1
+ # shared/identity/store.py
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Any, Dict, Optional
6
+ import os
7
+
8
+
9
+ class IdentityStoreError(Exception):
10
+ pass
11
+
12
+
13
+ class IdentityStore:
14
+ """
15
+ Generic filesystem-based identity vault.
16
+
17
+ Responsibilities:
18
+ - persist identity blobs (public/private)
19
+ - load identity blobs
20
+ - export safe/public view
21
+ - ensure structural consistency
22
+
23
+ It does NOT:
24
+ - generate identities
25
+ - perform cryptographic operations
26
+ - enforce business rules
27
+ """
28
+
29
+ def __init__(self, base_path: Path, namespace: str):
30
+ """
31
+ Args:
32
+ base_path: root storage path (e.g. storage/)
33
+ namespace: entity namespace (ra, osam, agent, proxy, etc.)
34
+ """
35
+
36
+ self._base = Path(base_path)
37
+ self._namespace = namespace
38
+
39
+ self._root = self._base / namespace / "identity"
40
+
41
+ self._public_path = self._root / "profile.public.json"
42
+ self._private_path = self._root / "profile.private.json"
43
+ self._meta_path = self._root / "metadata.json"
44
+
45
+ self._ensure_structure()
46
+
47
+ # ---------------------------------------------------------
48
+ # INTERNAL
49
+ # ---------------------------------------------------------
50
+ def _ensure_structure(self):
51
+ self._root.mkdir(parents=True, exist_ok=True)
52
+
53
+ def _write_json(self, path: Path, data: Dict[str, Any]):
54
+ tmp_path = path.with_suffix(".tmp")
55
+
56
+ with open(tmp_path, "w") as f:
57
+ json.dump(data, f, indent=4)
58
+
59
+ tmp_path.replace(path)
60
+
61
+ def _read_json(self, path: Path) -> Dict[str, Any]:
62
+ if not path.exists():
63
+ raise IdentityStoreError(f"Missing identity file: {path}")
64
+
65
+ with open(path, "r") as f:
66
+ return json.load(f)
67
+
68
+ # ---------------------------------------------------------
69
+ # LIFECYCLE
70
+ # ---------------------------------------------------------
71
+ def exists(self) -> bool:
72
+ print("CWD:", os.getcwd())
73
+ print("PUBLIC:", self._public_path)
74
+ print("PRIVATE:", self._private_path)
75
+
76
+ print("PUBLIC EXISTS:", self._public_path.exists())
77
+ print("PRIVATE EXISTS:", self._private_path.exists())
78
+
79
+ return self._public_path.exists() and self._private_path.exists()
80
+
81
+ def save(
82
+ self,
83
+ public_profile: Dict[str, Any],
84
+ private_profile: Dict[str, Any],
85
+ metadata: Optional[Dict[str, Any]] = None
86
+ ) -> None:
87
+ """
88
+ Persist identity bundle atomically.
89
+ """
90
+
91
+ self._write_json(self._public_path, public_profile)
92
+ self._write_json(self._private_path, private_profile)
93
+
94
+ if metadata is not None:
95
+ self._write_json(self._meta_path, metadata)
96
+
97
+ def load(self) -> Dict[str, Any]:
98
+ """
99
+ Load full identity bundle.
100
+ """
101
+
102
+ return {
103
+ "public": self._read_json(self._public_path),
104
+ "private": self._read_json(self._private_path),
105
+ "metadata": (
106
+ self._read_json(self._meta_path)
107
+ if self._meta_path.exists()
108
+ else None
109
+ )
110
+ }
111
+
112
+ def load_public(self) -> Dict[str, Any]:
113
+ return self._read_json(self._public_path)
114
+
115
+ def load_private(self) -> Dict[str, Any]:
116
+ return self._read_json(self._private_path)
117
+
118
+ # ---------------------------------------------------------
119
+ # EXPORT (SAFE)
120
+ # ---------------------------------------------------------
121
+ def export_public(self, export_path: Path) -> Path:
122
+ """
123
+ Export only public identity (safe for sharing).
124
+ """
125
+
126
+ data = self.load_public()
127
+
128
+ export_path = Path(export_path)
129
+ export_path.mkdir(parents=True, exist_ok=True)
130
+
131
+ file_path = export_path / f"{self._namespace}.identity.public.json"
132
+
133
+ with open(file_path, "w") as f:
134
+ json.dump(data, f, indent=4)
135
+
136
+ return file_path
137
+
138
+ # ---------------------------------------------------------
139
+ # DELETE (optional, destructive)
140
+ # ---------------------------------------------------------
141
+ def delete(self) -> None:
142
+ """
143
+ Remove identity completely.
144
+ """
145
+
146
+ if self._root.exists():
147
+ for file in self._root.glob("*"):
148
+ file.unlink()
149
+
150
+ self._root.rmdir()
modules/__init__.py ADDED
File without changes
File without changes
@@ -0,0 +1,361 @@
1
+ # shell/client.py
2
+
3
+ # =====================================================
4
+ # LIBRARY IMPORTS
5
+ # =====================================================
6
+
7
+ import os
8
+ import tty
9
+ import termios
10
+ import asyncio
11
+ import traceback
12
+
13
+ from .protocol import ShellProtocol
14
+ from .models import (
15
+ ProtocolType,
16
+ ProtocolStandard,
17
+ ShellSignal
18
+ )
19
+
20
+
21
+ # =====================================================
22
+ # SHELL CLIENT
23
+ # =====================================================
24
+
25
+ class ShellClient:
26
+
27
+ def __init__(
28
+ self,
29
+ communication_handler,
30
+ auth_token: str,
31
+ tunnel_token: str,
32
+ session_token: str
33
+ ):
34
+
35
+ self._communication_handler = (
36
+ communication_handler
37
+ )
38
+
39
+ self._auth_token = auth_token
40
+ self._tunnel_token = tunnel_token
41
+ self._session_token = session_token
42
+
43
+ self._is_running = False
44
+
45
+ self._shell_protocol = ShellProtocol(
46
+ auth_token=self._auth_token,
47
+ tunnel_token=self._tunnel_token,
48
+ session_token=self._session_token
49
+ )
50
+
51
+ self._stdin_fd = (
52
+ os.sys.stdin.fileno()
53
+ )
54
+
55
+ self._old_term = None
56
+
57
+ # =================================================
58
+ # START
59
+ # =================================================
60
+
61
+ async def start(self):
62
+
63
+ self._is_running = True
64
+
65
+ print(
66
+ "[SHELL] Opening PTY..."
67
+ )
68
+
69
+ await (
70
+ self._communication_handler
71
+ .send_datapackage(
72
+ self._shell_protocol.open()
73
+ )
74
+ )
75
+
76
+ self._enable_raw_mode()
77
+
78
+ try:
79
+
80
+ receiver_task = (
81
+ asyncio.create_task(
82
+ self._receiver_loop()
83
+ )
84
+ )
85
+
86
+ sender_task = (
87
+ asyncio.create_task(
88
+ self._sender_loop()
89
+ )
90
+ )
91
+
92
+ done, pending = (
93
+ await asyncio.wait(
94
+ [
95
+ receiver_task,
96
+ sender_task
97
+ ],
98
+ return_when=
99
+ asyncio.FIRST_COMPLETED
100
+ )
101
+ )
102
+
103
+ for task in pending:
104
+
105
+ task.cancel()
106
+
107
+ finally:
108
+
109
+ self._disable_raw_mode()
110
+
111
+ try:
112
+
113
+ await (
114
+ self._communication_handler
115
+ .send_datapackage(
116
+ self._shell_protocol.close()
117
+ )
118
+ )
119
+
120
+ except Exception:
121
+
122
+ pass
123
+
124
+ self._is_running = False
125
+
126
+ print(
127
+ "\n[SHELL] Closed"
128
+ )
129
+
130
+ # =================================================
131
+ # RAW MODE
132
+ # =================================================
133
+
134
+ def _enable_raw_mode(self):
135
+
136
+ self._old_term = (
137
+ termios.tcgetattr(
138
+ self._stdin_fd
139
+ )
140
+ )
141
+
142
+ tty.setraw(
143
+ self._stdin_fd
144
+ )
145
+
146
+ def _disable_raw_mode(self):
147
+
148
+ if self._old_term:
149
+
150
+ termios.tcsetattr(
151
+ self._stdin_fd,
152
+ termios.TCSADRAIN,
153
+ self._old_term
154
+ )
155
+
156
+ # =================================================
157
+ # RECEIVER
158
+ # =================================================
159
+
160
+ async def _receiver_loop(self):
161
+
162
+ while self._is_running:
163
+
164
+ try:
165
+
166
+ packet = await (
167
+ self._communication_handler
168
+ .receive_datapackage()
169
+ )
170
+
171
+ if not isinstance(
172
+ packet,
173
+ dict
174
+ ):
175
+ continue
176
+
177
+ payload = packet.get(
178
+ "payload",
179
+ {}
180
+ )
181
+
182
+ if (
183
+ payload.get(
184
+ "protocol"
185
+ )
186
+ !=
187
+ ProtocolStandard
188
+ .SHELL
189
+ .value
190
+ ):
191
+ continue
192
+
193
+ if (
194
+ payload.get(
195
+ "protocol_type"
196
+ )
197
+ !=
198
+ ProtocolType
199
+ .SERVER
200
+ .value
201
+ ):
202
+ continue
203
+
204
+ event = payload.get(
205
+ "event"
206
+ )
207
+
208
+ # -------------------------
209
+ # OUTPUT
210
+ # -------------------------
211
+
212
+ if event == "OUTPUT":
213
+
214
+ data = payload.get(
215
+ "data",
216
+ ""
217
+ )
218
+
219
+ if data:
220
+
221
+ print(
222
+ data,
223
+ end="",
224
+ flush=True
225
+ )
226
+
227
+ continue
228
+
229
+ # -------------------------
230
+ # EXIT
231
+ # -------------------------
232
+
233
+ if event == "EXIT":
234
+
235
+ code = payload.get(
236
+ "return_code",
237
+ 0
238
+ )
239
+
240
+ print(
241
+ f"\n\r"
242
+ f"[REMOTE EXIT {code}]"
243
+ )
244
+
245
+ self.stop()
246
+
247
+ break
248
+
249
+ except asyncio.CancelledError:
250
+
251
+ break
252
+
253
+ except Exception:
254
+
255
+ print(
256
+ "\n[SHELL-CLIENT] "
257
+ "Receiver exception:"
258
+ )
259
+
260
+ traceback.print_exc()
261
+
262
+ self.stop()
263
+
264
+ break
265
+
266
+ # =================================================
267
+ # SENDER
268
+ # =================================================
269
+
270
+ async def _sender_loop(self):
271
+
272
+ loop = (
273
+ asyncio.get_running_loop()
274
+ )
275
+
276
+ while self._is_running:
277
+
278
+ try:
279
+
280
+ data = await (
281
+ loop.run_in_executor(
282
+ None,
283
+ os.read,
284
+ self._stdin_fd,
285
+ 1024
286
+ )
287
+ )
288
+
289
+ if not data:
290
+ continue
291
+
292
+ # -------------------------
293
+ # CTRL+C
294
+ # -------------------------
295
+
296
+ if data == b"\x03":
297
+
298
+ await (
299
+ self._communication_handler
300
+ .send_datapackage(
301
+ self._shell_protocol.signal(
302
+ ShellSignal
303
+ .SIGINT
304
+ .value
305
+ )
306
+ )
307
+ )
308
+
309
+ continue
310
+
311
+ # -------------------------
312
+ # CTRL+D
313
+ # -------------------------
314
+
315
+ if data == b"\x04":
316
+
317
+ self.stop()
318
+
319
+ break
320
+
321
+ # -------------------------
322
+ # INPUT
323
+ # -------------------------
324
+
325
+ await (
326
+ self._communication_handler
327
+ .send_datapackage(
328
+ self._shell_protocol.input(
329
+ data.decode(
330
+ errors="ignore"
331
+ )
332
+ )
333
+ )
334
+ )
335
+
336
+ except asyncio.CancelledError:
337
+
338
+ break
339
+
340
+ except Exception:
341
+
342
+ print(
343
+ "\n[SHELL-CLIENT] "
344
+ "Sender exception:"
345
+ )
346
+
347
+ traceback.print_exc()
348
+
349
+ self.stop()
350
+
351
+ break
352
+
353
+ # =================================================
354
+ # STOP
355
+ # =================================================
356
+
357
+ def stop(self):
358
+
359
+ self._is_running = False
360
+
361
+ return True
@@ -0,0 +1,61 @@
1
+ # shell/models.py
2
+
3
+ # =====================================================
4
+ # LIBRARY IMPORTS
5
+ # =====================================================
6
+
7
+ from enum import Enum
8
+
9
+
10
+ # =====================================================
11
+ # PROTOCOL TYPE
12
+ # =====================================================
13
+
14
+ class ProtocolType(str, Enum):
15
+
16
+ CLIENT = "CLIENT"
17
+ SERVER = "SERVER"
18
+
19
+
20
+ # =====================================================
21
+ # PROTOCOL STANDARD
22
+ # =====================================================
23
+
24
+ class ProtocolStandard(str, Enum):
25
+
26
+ SHELL = "SHELL"
27
+ FTP = "FTP"
28
+ SPECIAL = "SPECIAL"
29
+
30
+
31
+ # =====================================================
32
+ # SHELL EVENTS
33
+ # =====================================================
34
+
35
+ class ShellEvent(str, Enum):
36
+
37
+ OPEN = "OPEN"
38
+
39
+ INPUT = "INPUT"
40
+
41
+ OUTPUT = "OUTPUT"
42
+
43
+ RESIZE = "RESIZE"
44
+
45
+ SIGNAL = "SIGNAL"
46
+
47
+ CLOSE = "CLOSE"
48
+
49
+
50
+ # =====================================================
51
+ # SHELL SIGNALS
52
+ # =====================================================
53
+
54
+ class ShellSignal(str, Enum):
55
+
56
+ SIGINT = "SIGINT"
57
+ SIGTERM = "SIGTERM"
58
+ SIGKILL = "SIGKILL"
59
+ SIGHUP = "SIGHUP"
60
+ SIGQUIT = "SIGQUIT"
61
+ SIGTSTP = "SIGTSTP"