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.
- api/__init__.py +1 -0
- api/manager/__init__.py +1 -0
- api/manager/v1/__init__.py +78 -0
- api/manager/v1/authentication.py +278 -0
- api/manager/v1/client.py +111 -0
- api/manager/v1/domains.py +64 -0
- api/manager/v1/entities.py +97 -0
- api/manager/v1/exceptions.py +98 -0
- api/manager/v1/identity.py +44 -0
- api/manager/v1/models.py +342 -0
- api/manager/v1/passports.py +131 -0
- api/manager/v1/sessions.py +83 -0
- api/manager/v1/transport.py +253 -0
- api/manager/v1/tunnels.py +120 -0
- cryptography/__init__.py +0 -0
- cryptography/certificate.py +390 -0
- cryptography/encoding.py +0 -0
- cryptography/identity.py +124 -0
- cryptography/keys.py +463 -0
- cryptography/signatures.py +63 -0
- cryptography/utils.py +0 -0
- domain/__init__.py +0 -0
- domain/domain.py +80 -0
- domain/membership.py +21 -0
- domain/permissions.py +14 -0
- domain/policies.py +2 -0
- identity/__init__.py +0 -0
- identity/identification.py +64 -0
- identity/store.py +150 -0
- modules/__init__.py +0 -0
- modules/shell/__init__.py +0 -0
- modules/shell/client.py +361 -0
- modules/shell/models.py +61 -0
- modules/shell/protocol.py +249 -0
- modules/shell/server.py +511 -0
- modules/shell/session.py +339 -0
- modules/utils.py +212 -0
- openshell_shared-0.1.2.dist-info/METADATA +59 -0
- openshell_shared-0.1.2.dist-info/RECORD +62 -0
- openshell_shared-0.1.2.dist-info/WHEEL +5 -0
- openshell_shared-0.1.2.dist-info/top_level.txt +7 -0
- protocols/__init__.py +0 -0
- protocols/negotiation/challenge.py +127 -0
- protocols/negotiation/models.py +28 -0
- standards/__init__.py +0 -0
- standards/certificates/__init__.py +0 -0
- standards/certificates/status.py +12 -0
- standards/certificates/types.py +11 -0
- standards/entities/__init__.py +0 -0
- standards/entities/types.py +14 -0
- standards/events/__init__.py +0 -0
- standards/events/schemas/__init__.py +0 -0
- standards/events/schemas/entity_registered.py +13 -0
- standards/events/types.py +18 -0
- standards/passports/__init__.py +0 -0
- standards/passports/types.py +5 -0
- standards/permissions/__init__.py +0 -0
- standards/permissions/types.py +14 -0
- standards/roles/__init__.py +0 -0
- standards/roles/types.py +8 -0
- standards/transports/__init__.py +0 -0
- standards/transports/types.py +24 -0
modules/shell/session.py
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
# shell/session.py
|
|
2
|
+
|
|
3
|
+
# =====================================================
|
|
4
|
+
# LIBRARY IMPORTS
|
|
5
|
+
# =====================================================
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import pty
|
|
9
|
+
import fcntl
|
|
10
|
+
import signal
|
|
11
|
+
import struct
|
|
12
|
+
import termios
|
|
13
|
+
import asyncio
|
|
14
|
+
import subprocess
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# =====================================================
|
|
18
|
+
# SHELL SESSION
|
|
19
|
+
# =====================================================
|
|
20
|
+
|
|
21
|
+
class ShellSession:
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
session_token: str
|
|
26
|
+
):
|
|
27
|
+
|
|
28
|
+
self.session_token = (
|
|
29
|
+
session_token
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
self.process = None
|
|
33
|
+
|
|
34
|
+
self.master_fd = None
|
|
35
|
+
self.slave_fd = None
|
|
36
|
+
|
|
37
|
+
self._lock = asyncio.Lock()
|
|
38
|
+
|
|
39
|
+
# =================================================
|
|
40
|
+
# START
|
|
41
|
+
# =================================================
|
|
42
|
+
|
|
43
|
+
async def start(self):
|
|
44
|
+
|
|
45
|
+
if self.process is not None:
|
|
46
|
+
return
|
|
47
|
+
|
|
48
|
+
self.master_fd, self.slave_fd = (
|
|
49
|
+
pty.openpty()
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
self.process = subprocess.Popen(
|
|
53
|
+
[
|
|
54
|
+
"/bin/bash",
|
|
55
|
+
"-i"
|
|
56
|
+
],
|
|
57
|
+
stdin=self.slave_fd,
|
|
58
|
+
stdout=self.slave_fd,
|
|
59
|
+
stderr=self.slave_fd,
|
|
60
|
+
start_new_session=True,
|
|
61
|
+
close_fds=True
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
os.set_blocking(
|
|
65
|
+
self.master_fd,
|
|
66
|
+
False
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# =================================================
|
|
70
|
+
# WRITE
|
|
71
|
+
# =================================================
|
|
72
|
+
|
|
73
|
+
async def write(
|
|
74
|
+
self,
|
|
75
|
+
data: str
|
|
76
|
+
):
|
|
77
|
+
|
|
78
|
+
async with self._lock:
|
|
79
|
+
|
|
80
|
+
if self.process is None:
|
|
81
|
+
|
|
82
|
+
await self.start()
|
|
83
|
+
|
|
84
|
+
if isinstance(
|
|
85
|
+
data,
|
|
86
|
+
str
|
|
87
|
+
):
|
|
88
|
+
|
|
89
|
+
data = data.encode()
|
|
90
|
+
|
|
91
|
+
os.write(
|
|
92
|
+
self.master_fd,
|
|
93
|
+
data
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# =================================================
|
|
97
|
+
# READ
|
|
98
|
+
# =================================================
|
|
99
|
+
|
|
100
|
+
async def read(
|
|
101
|
+
self,
|
|
102
|
+
max_bytes: int = 65536
|
|
103
|
+
) -> str:
|
|
104
|
+
|
|
105
|
+
if self.process is None:
|
|
106
|
+
return ""
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
|
|
110
|
+
data = os.read(
|
|
111
|
+
self.master_fd,
|
|
112
|
+
max_bytes
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
return data.decode(
|
|
116
|
+
errors="ignore"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
except BlockingIOError:
|
|
120
|
+
|
|
121
|
+
return ""
|
|
122
|
+
|
|
123
|
+
except OSError:
|
|
124
|
+
|
|
125
|
+
return ""
|
|
126
|
+
|
|
127
|
+
# =================================================
|
|
128
|
+
# READ AVAILABLE
|
|
129
|
+
# =================================================
|
|
130
|
+
|
|
131
|
+
async def read_available(
|
|
132
|
+
self,
|
|
133
|
+
chunk_size: int = 65536
|
|
134
|
+
) -> str:
|
|
135
|
+
|
|
136
|
+
output = []
|
|
137
|
+
|
|
138
|
+
while True:
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
|
|
142
|
+
chunk = os.read(
|
|
143
|
+
self.master_fd,
|
|
144
|
+
chunk_size
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
if not chunk:
|
|
148
|
+
break
|
|
149
|
+
|
|
150
|
+
output.append(
|
|
151
|
+
chunk.decode(
|
|
152
|
+
errors="ignore"
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
except BlockingIOError:
|
|
157
|
+
|
|
158
|
+
break
|
|
159
|
+
|
|
160
|
+
except OSError:
|
|
161
|
+
|
|
162
|
+
break
|
|
163
|
+
|
|
164
|
+
return "".join(
|
|
165
|
+
output
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# =================================================
|
|
169
|
+
# RESIZE
|
|
170
|
+
# =================================================
|
|
171
|
+
|
|
172
|
+
async def resize(
|
|
173
|
+
self,
|
|
174
|
+
rows: int,
|
|
175
|
+
cols: int
|
|
176
|
+
):
|
|
177
|
+
|
|
178
|
+
if self.master_fd is None:
|
|
179
|
+
return
|
|
180
|
+
|
|
181
|
+
winsize = struct.pack(
|
|
182
|
+
"HHHH",
|
|
183
|
+
rows,
|
|
184
|
+
cols,
|
|
185
|
+
0,
|
|
186
|
+
0
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
fcntl.ioctl(
|
|
190
|
+
self.master_fd,
|
|
191
|
+
termios.TIOCSWINSZ,
|
|
192
|
+
winsize
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# =================================================
|
|
196
|
+
# SIGNAL
|
|
197
|
+
# =================================================
|
|
198
|
+
|
|
199
|
+
async def signal(
|
|
200
|
+
self,
|
|
201
|
+
signal_name: str
|
|
202
|
+
):
|
|
203
|
+
|
|
204
|
+
if self.process is None:
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
sig = getattr(
|
|
208
|
+
signal,
|
|
209
|
+
signal_name,
|
|
210
|
+
None
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
if sig is None:
|
|
214
|
+
return
|
|
215
|
+
|
|
216
|
+
try:
|
|
217
|
+
|
|
218
|
+
os.killpg(
|
|
219
|
+
self.process.pid,
|
|
220
|
+
sig
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
except Exception:
|
|
224
|
+
|
|
225
|
+
pass
|
|
226
|
+
|
|
227
|
+
# =================================================
|
|
228
|
+
# PID
|
|
229
|
+
# =================================================
|
|
230
|
+
|
|
231
|
+
@property
|
|
232
|
+
def pid(
|
|
233
|
+
self
|
|
234
|
+
):
|
|
235
|
+
|
|
236
|
+
if self.process is None:
|
|
237
|
+
return None
|
|
238
|
+
|
|
239
|
+
return (
|
|
240
|
+
self.process.pid
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# =================================================
|
|
244
|
+
# IS ALIVE
|
|
245
|
+
# =================================================
|
|
246
|
+
|
|
247
|
+
def is_alive(
|
|
248
|
+
self
|
|
249
|
+
) -> bool:
|
|
250
|
+
|
|
251
|
+
if self.process is None:
|
|
252
|
+
return False
|
|
253
|
+
|
|
254
|
+
return (
|
|
255
|
+
self.process.poll()
|
|
256
|
+
is None
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# =================================================
|
|
260
|
+
# RETURN CODE
|
|
261
|
+
# =================================================
|
|
262
|
+
|
|
263
|
+
def return_code(
|
|
264
|
+
self
|
|
265
|
+
):
|
|
266
|
+
|
|
267
|
+
if self.process is None:
|
|
268
|
+
return None
|
|
269
|
+
|
|
270
|
+
return (
|
|
271
|
+
self.process.poll()
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# =================================================
|
|
275
|
+
# CLOSE
|
|
276
|
+
# =================================================
|
|
277
|
+
|
|
278
|
+
async def close(self):
|
|
279
|
+
|
|
280
|
+
if self.process is None:
|
|
281
|
+
return
|
|
282
|
+
|
|
283
|
+
try:
|
|
284
|
+
|
|
285
|
+
os.killpg(
|
|
286
|
+
self.process.pid,
|
|
287
|
+
signal.SIGTERM
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
except Exception:
|
|
291
|
+
|
|
292
|
+
pass
|
|
293
|
+
|
|
294
|
+
try:
|
|
295
|
+
|
|
296
|
+
await asyncio.wait_for(
|
|
297
|
+
asyncio.to_thread(
|
|
298
|
+
self.process.wait
|
|
299
|
+
),
|
|
300
|
+
timeout=3
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
except asyncio.TimeoutError:
|
|
304
|
+
|
|
305
|
+
try:
|
|
306
|
+
|
|
307
|
+
os.killpg(
|
|
308
|
+
self.process.pid,
|
|
309
|
+
signal.SIGKILL
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
except Exception:
|
|
313
|
+
|
|
314
|
+
pass
|
|
315
|
+
|
|
316
|
+
try:
|
|
317
|
+
|
|
318
|
+
os.close(
|
|
319
|
+
self.master_fd
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
except Exception:
|
|
323
|
+
|
|
324
|
+
pass
|
|
325
|
+
|
|
326
|
+
try:
|
|
327
|
+
|
|
328
|
+
os.close(
|
|
329
|
+
self.slave_fd
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
except Exception:
|
|
333
|
+
|
|
334
|
+
pass
|
|
335
|
+
|
|
336
|
+
self.process = None
|
|
337
|
+
|
|
338
|
+
self.master_fd = None
|
|
339
|
+
self.slave_fd = None
|
modules/utils.py
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import websockets
|
|
3
|
+
|
|
4
|
+
from asyncdatapackage import AsyncDataPackage
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class WSStreamClient:
|
|
8
|
+
"""
|
|
9
|
+
Convierte WebSocket en stream tipo TCP para AsyncDataPackage.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, host: str, port: int):
|
|
13
|
+
|
|
14
|
+
self._host = host
|
|
15
|
+
self._port = port
|
|
16
|
+
|
|
17
|
+
self._ws = None
|
|
18
|
+
|
|
19
|
+
self._buffer = bytearray()
|
|
20
|
+
self._recv_queue = asyncio.Queue()
|
|
21
|
+
|
|
22
|
+
self._reader_task = None
|
|
23
|
+
self._running = False
|
|
24
|
+
|
|
25
|
+
# =====================================================
|
|
26
|
+
# CONNECT
|
|
27
|
+
# =====================================================
|
|
28
|
+
|
|
29
|
+
async def connect(self):
|
|
30
|
+
|
|
31
|
+
uri = f"ws://{self._host}:{self._port}"
|
|
32
|
+
|
|
33
|
+
self._ws = await websockets.connect(
|
|
34
|
+
uri,
|
|
35
|
+
max_size=None,
|
|
36
|
+
ping_interval=None
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
self._running = True
|
|
40
|
+
|
|
41
|
+
self._reader_task = asyncio.create_task(
|
|
42
|
+
self._reader_loop()
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# =====================================================
|
|
46
|
+
# READER LOOP (WS → STREAM BUFFER)
|
|
47
|
+
# =====================================================
|
|
48
|
+
|
|
49
|
+
async def _reader_loop(self):
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
async for message in self._ws:
|
|
53
|
+
|
|
54
|
+
if isinstance(message, str):
|
|
55
|
+
message = message.encode("utf-8")
|
|
56
|
+
|
|
57
|
+
self._buffer.extend(message)
|
|
58
|
+
|
|
59
|
+
except Exception:
|
|
60
|
+
self._running = False
|
|
61
|
+
|
|
62
|
+
# =====================================================
|
|
63
|
+
# WRITE (STREAM → WS)
|
|
64
|
+
# =====================================================
|
|
65
|
+
|
|
66
|
+
async def write(self, data: bytes) -> bool:
|
|
67
|
+
|
|
68
|
+
if not self._ws:
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
await self._ws.send(data)
|
|
73
|
+
return True
|
|
74
|
+
except Exception:
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
# =====================================================
|
|
78
|
+
# READ (TCP-LIKE SEMANTICS)
|
|
79
|
+
# =====================================================
|
|
80
|
+
|
|
81
|
+
# =====================================================
|
|
82
|
+
# READ (TCP-LIKE SEMANTICS FIXED)
|
|
83
|
+
# =====================================================
|
|
84
|
+
|
|
85
|
+
async def read(self, amount: int) -> bytes:
|
|
86
|
+
|
|
87
|
+
# Bloquear solo si no hay absolutamente nada que leer
|
|
88
|
+
while len(self._buffer) == 0:
|
|
89
|
+
if not self._running:
|
|
90
|
+
return b""
|
|
91
|
+
await asyncio.sleep(0.001)
|
|
92
|
+
|
|
93
|
+
# Extraer hasta 'amount' bytes, o la longitud actual (lo que sea menor)
|
|
94
|
+
chunk_size = min(amount, len(self._buffer))
|
|
95
|
+
|
|
96
|
+
result = bytes(self._buffer[:chunk_size])
|
|
97
|
+
del self._buffer[:chunk_size]
|
|
98
|
+
|
|
99
|
+
return result
|
|
100
|
+
|
|
101
|
+
# =====================================================
|
|
102
|
+
# CLOSE
|
|
103
|
+
# =====================================================
|
|
104
|
+
|
|
105
|
+
async def close(self):
|
|
106
|
+
|
|
107
|
+
self._running = False
|
|
108
|
+
|
|
109
|
+
if self._ws:
|
|
110
|
+
await self._ws.close()
|
|
111
|
+
|
|
112
|
+
if self._reader_task:
|
|
113
|
+
self._reader_task.cancel()
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
await self._reader_task
|
|
117
|
+
except:
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# =========================================================
|
|
122
|
+
# COMMUNICATION HANDLER (DROP-IN COMPATIBLE)
|
|
123
|
+
# =========================================================
|
|
124
|
+
|
|
125
|
+
class CommunicationHandler:
|
|
126
|
+
|
|
127
|
+
def __init__(
|
|
128
|
+
self,
|
|
129
|
+
auth_token: str,
|
|
130
|
+
tunnel_token: str,
|
|
131
|
+
tunnel_port: int,
|
|
132
|
+
tunnel_host: str = "127.0.0.1"
|
|
133
|
+
):
|
|
134
|
+
self.auth_token = auth_token
|
|
135
|
+
self.tunnel_token = tunnel_token
|
|
136
|
+
|
|
137
|
+
self.tunnel_host = tunnel_host
|
|
138
|
+
self.tunnel_port = tunnel_port
|
|
139
|
+
|
|
140
|
+
self._stream = None
|
|
141
|
+
self.datapackage = None
|
|
142
|
+
|
|
143
|
+
# =====================================================
|
|
144
|
+
# CONNECTION
|
|
145
|
+
# =====================================================
|
|
146
|
+
|
|
147
|
+
async def connect(self):
|
|
148
|
+
|
|
149
|
+
self._stream = WSStreamClient(
|
|
150
|
+
self.tunnel_host,
|
|
151
|
+
self.tunnel_port
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
await self._stream.connect()
|
|
155
|
+
|
|
156
|
+
async def write_fn(data: bytes):
|
|
157
|
+
return await self._stream.write(data)
|
|
158
|
+
|
|
159
|
+
async def read_fn():
|
|
160
|
+
# IMPORTANT: AsyncDataPackage expects chunk reads
|
|
161
|
+
return await self._stream.read(65536)
|
|
162
|
+
|
|
163
|
+
self.datapackage = AsyncDataPackage(
|
|
164
|
+
write_function=write_fn,
|
|
165
|
+
read_function=read_fn,
|
|
166
|
+
backpressure_mode="drop_oldest"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
await self.datapackage.start()
|
|
170
|
+
|
|
171
|
+
await self._bind()
|
|
172
|
+
|
|
173
|
+
# =====================================================
|
|
174
|
+
# BIND
|
|
175
|
+
# =====================================================
|
|
176
|
+
|
|
177
|
+
async def _bind(self):
|
|
178
|
+
|
|
179
|
+
await self.datapackage.send_datapackage({
|
|
180
|
+
"auth_token": self.auth_token,
|
|
181
|
+
"tunnel_token": self.tunnel_token,
|
|
182
|
+
"packet_type": "CONTROL",
|
|
183
|
+
"packet_control": "BIND"
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
# =====================================================
|
|
187
|
+
# SEND
|
|
188
|
+
# =====================================================
|
|
189
|
+
|
|
190
|
+
async def send_datapackage(self, packet: dict):
|
|
191
|
+
|
|
192
|
+
await self.datapackage.send_datapackage(packet)
|
|
193
|
+
|
|
194
|
+
# =====================================================
|
|
195
|
+
# RECEIVE
|
|
196
|
+
# =====================================================
|
|
197
|
+
|
|
198
|
+
async def receive_datapackage(self):
|
|
199
|
+
|
|
200
|
+
return await self.datapackage.receive_datapackage()
|
|
201
|
+
|
|
202
|
+
# =====================================================
|
|
203
|
+
# CLOSE
|
|
204
|
+
# =====================================================
|
|
205
|
+
|
|
206
|
+
async def close(self):
|
|
207
|
+
|
|
208
|
+
if self.datapackage:
|
|
209
|
+
await self.datapackage.stop()
|
|
210
|
+
|
|
211
|
+
if self._stream:
|
|
212
|
+
await self._stream.close()
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: openshell-shared
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Shared library for OpenShell (OSAM/OSA/OSAC)
|
|
5
|
+
Author: Daniel Pérez
|
|
6
|
+
License: Proprietary
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: httpx
|
|
10
|
+
Requires-Dist: cryptography
|
|
11
|
+
Requires-Dist: websockets
|
|
12
|
+
Requires-Dist: uuid6
|
|
13
|
+
|
|
14
|
+
# OpenShell Shared
|
|
15
|
+
|
|
16
|
+
Librería compartida usada por los componentes de OpenShell (OSAM, OSA, OSAC).
|
|
17
|
+
|
|
18
|
+
Contiene:
|
|
19
|
+
|
|
20
|
+
- **identity/** — Identidad lógica de las entidades (`EntityIdentity`, `Identification`) y su
|
|
21
|
+
persistencia (`store.py`).
|
|
22
|
+
- **cryptography/** — Primitivas Ed25519 (`keys.py`, `signatures.py`), identidad criptográfica
|
|
23
|
+
(`identity.py`) y certificados (`certificate.py`). `encoding.py` y `utils.py` son módulos
|
|
24
|
+
reservados para trabajo futuro (actualmente sin implementación).
|
|
25
|
+
- **protocols/negotiation/** — Protocolo de challenge-response (`challenge.py`) y sus modelos.
|
|
26
|
+
- **domain/** — Modelo de dominio (`Domain`, `Membership`, `Permission`) y políticas de
|
|
27
|
+
autorización. `policies.py` es un stub pendiente de implementación real.
|
|
28
|
+
- **standards/** — Enumeraciones y contratos compartidos entre componentes: tipos de entidad,
|
|
29
|
+
certificados, eventos, roles, permisos, passports y transportes.
|
|
30
|
+
- **modules/shell/** — Implementación del subsistema de shell remoto (cliente, servidor, sesión
|
|
31
|
+
y protocolo de framing) usado por OSA y OSAC.
|
|
32
|
+
- **api/manager/v1/** — SDK oficial async para consumir la API HTTP de OSAM (`OSAMClient`).
|
|
33
|
+
Todo el sistema (OSAC, OSA, herramientas internas) debe consumir OSAM exclusivamente a través
|
|
34
|
+
de este paquete; ningún otro componente debe importar `httpx`/`requests` directamente para
|
|
35
|
+
hablar con OSAM.
|
|
36
|
+
|
|
37
|
+
## Notas de la fusión (v2.1.0)
|
|
38
|
+
|
|
39
|
+
Esta versión unifica dos ramas que habían divergido:
|
|
40
|
+
|
|
41
|
+
- Se conserva la estructura de empaquetado (`pyproject.toml`, layout `src`) y los módulos
|
|
42
|
+
`domain/` y `standards/`, introducidos en la rama de refactor v2.
|
|
43
|
+
- Se restaura `modules/shell/` y se sustituye el cliente HTTP monolítico
|
|
44
|
+
(`api/manager/1/osam_client.py`) por el SDK modular (`api/manager/v1/`), que es la versión
|
|
45
|
+
vigente y más completa (excepciones tipadas, dataclasses, suite de tests con mock transport).
|
|
46
|
+
|
|
47
|
+
### Deuda técnica pendiente identificada durante la fusión
|
|
48
|
+
|
|
49
|
+
1. `domain/permissions.py` y `standards/permissions/types.py` definen dos enums `Permission`
|
|
50
|
+
distintos y no compatibles entre sí (`AGENT_READ/AGENT_EXECUTE/DOMAIN_ADMIN/PROXY_USE` vs.
|
|
51
|
+
`DOMAIN_READ/DOMAIN_WRITE/ENTITY_REGISTER/ENTITY_REVOKE/PROXY_USE`). No se han unificado
|
|
52
|
+
automáticamente porque implica una decisión de diseño (cuál es la fuente de verdad de
|
|
53
|
+
permisos). Requiere consolidación manual.
|
|
54
|
+
2. `cryptography/encoding.py`, `cryptography/utils.py` y `domain/policies.py` son placeholders
|
|
55
|
+
vacíos o triviales (`Policy.evaluate` siempre retorna `True`). No implementan lógica real.
|
|
56
|
+
3. El SDK (`api/manager/v1/`) no reexpone los helpers de alto nivel `full_connect` /
|
|
57
|
+
`open_and_link` que existían en el cliente monolítico anterior (composición de
|
|
58
|
+
autenticación + túnel + sesión en una sola llamada). Si se siguen usando, conviene
|
|
59
|
+
reimplementarlos como métodos de conveniencia sobre `OSAMClient`.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
api/__init__.py,sha256=gxFxYhnSvtNu2Bvd3C1vrlOFkVKAJY5Vzk1qTB9IOVk,21
|
|
2
|
+
api/manager/__init__.py,sha256=66EsLcAnZvOfbhmRKT3uY_LPJu3o_rHicCdkAVqpdEs,16
|
|
3
|
+
api/manager/v1/__init__.py,sha256=JPUvoGHq3y47eYmUVNypTZtPixdE9P-lrMnYoK5aljU,1739
|
|
4
|
+
api/manager/v1/authentication.py,sha256=KswoqDUhjiSLeZmnlprg0pfJNZfMK-kHYbAcE40LRdI,7034
|
|
5
|
+
api/manager/v1/client.py,sha256=oIlq0FjUDj42eCzGp1_SHZ-mtK94KQ2Oi3r5g2aLdOk,3885
|
|
6
|
+
api/manager/v1/domains.py,sha256=5v_mSFXRqbdRBshAlO0IN8oBfJsF2W2EVmoejuMYjfg,2048
|
|
7
|
+
api/manager/v1/entities.py,sha256=W2_51oGBHejQO0_tDRmC3_0bfJTjQon72_FjBYtpFY8,3663
|
|
8
|
+
api/manager/v1/exceptions.py,sha256=YqLAFrFHSgkedmFwwT9y-ay4QBOnHyYKAjORI2RGHIE,3627
|
|
9
|
+
api/manager/v1/identity.py,sha256=P_H6FtvS6TiqBNOPG3slAJmmia7frAOcZo11PEdpodw,1669
|
|
10
|
+
api/manager/v1/models.py,sha256=OZCHN9ozVGs46ZR71QDw-iQgdOMGPl7LqNpZG6KoRIs,12223
|
|
11
|
+
api/manager/v1/passports.py,sha256=BOSFhP11d9fBsu9PAKnpOZkfhwUjDiS2f6XUp7SGnPY,4957
|
|
12
|
+
api/manager/v1/sessions.py,sha256=hWi_cb1tejKAkZ0_9ecE2sCmZj7GJgUt9P-2xYziWKk,2435
|
|
13
|
+
api/manager/v1/transport.py,sha256=fiRKiYMjT6dnViL-dB63HQFG2sIGgphxG86YHRvwTiQ,8452
|
|
14
|
+
api/manager/v1/tunnels.py,sha256=Yn6YPwA6_GM0G5HbrJVounGLK7_oOtqHELRJBJDtsoA,4042
|
|
15
|
+
cryptography/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
cryptography/certificate.py,sha256=GlacatBv26w2ltJ32vNzaJ68dffKntM6Y7o-NLSsn6U,9736
|
|
17
|
+
cryptography/encoding.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
cryptography/identity.py,sha256=8ngNnn76CoRdgdctKSl6b2AGCp1P04O8v_MZ7KveyiU,3557
|
|
19
|
+
cryptography/keys.py,sha256=QLdiIMLCvqmnw2zlQXnE5Me_8StJ_mSjo5KXe9JTWHA,8333
|
|
20
|
+
cryptography/signatures.py,sha256=7jrpZjCYKzeGLckPg3wq8BoR9Sj2EKMPdKYzZijRLvg,1143
|
|
21
|
+
cryptography/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
|
+
domain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
+
domain/domain.py,sha256=p4qh0Pee06IY9pmA_QyVovBlvhFJX1R8QjK_xebo2zY,1734
|
|
24
|
+
domain/membership.py,sha256=X5BAiv2CKoIYZ095ZDGw5YJgBrhQsuIS40glTv8C30I,425
|
|
25
|
+
domain/permissions.py,sha256=ppZZU_-vVuQQ9SujYAKbR4u0brP7OGeRz7_Oq2IO5Ks,222
|
|
26
|
+
domain/policies.py,sha256=jkBzdWKjcwIa1UlrpzCZGhxDXOivX2QHLaj8y68TzqI,66
|
|
27
|
+
identity/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
+
identity/identification.py,sha256=KQ5XpxDj9TrG-U116j_FC5cMn-0sB_FtSZVwA_QcFgQ,1495
|
|
29
|
+
identity/store.py,sha256=s40UTbBenQYkPZnS7x8jkVzyg4OPQTLeEB3Hd32udVE,4317
|
|
30
|
+
modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
|
+
modules/utils.py,sha256=c2R9RkuU1WQyFnXT8mNvD0qBtsBeeHJKrvNplcyvSXs,5515
|
|
32
|
+
modules/shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
|
+
modules/shell/client.py,sha256=D2yAXwj6yr7CRa5PUKtrV8Rk3_6n8Q8AbP7_aJWDeGs,8094
|
|
34
|
+
modules/shell/models.py,sha256=_a1c-xzai_B5UBhVa8zjfPMrM8hotCTNpje5x62Pgi4,1200
|
|
35
|
+
modules/shell/protocol.py,sha256=JlhboJIOM9FQAyqZWKeS4Xbk8lcbUEdq7HOGowjq-ac,4619
|
|
36
|
+
modules/shell/server.py,sha256=Pfm2JINcMZTyxy5uTE425o8ZM6Dtc_p8B67lc_-ktqM,11902
|
|
37
|
+
modules/shell/session.py,sha256=g1VjO0UOii2zITkXHc222C3a_gF2XYFqiV9t30wpfSk,6226
|
|
38
|
+
protocols/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
|
+
protocols/negotiation/challenge.py,sha256=OJN_S1aHuvAQkIWVp8QLtGBTD8CuSSgd4AfyFbVQOL8,4134
|
|
40
|
+
protocols/negotiation/models.py,sha256=lNbRds5zyhWV6eEHmm6aFkSunmGPWTMIUKoFJYb04ro,363
|
|
41
|
+
standards/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
|
+
standards/certificates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
43
|
+
standards/certificates/status.py,sha256=wI_0WKo34l-jYyHfkI6EVVJ54iB0VZkcY-v6r7p6hL4,277
|
|
44
|
+
standards/certificates/types.py,sha256=0EJ8-OrP1I_-YIxGl1rgpMDSIlsoGxT_80XxUy5-FYs,286
|
|
45
|
+
standards/entities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
46
|
+
standards/entities/types.py,sha256=rcCV5XaUskf8ZKeIJyZAj0rTfhrH5QDo-g-AQrB8Ax8,183
|
|
47
|
+
standards/events/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
|
+
standards/events/types.py,sha256=OcZ8ibQv1dPOISM01wUSB7ckwYuxBDdCf0PwNm26WZM,348
|
|
49
|
+
standards/events/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
|
+
standards/events/schemas/entity_registered.py,sha256=BMCvnv2WHJKff5Zd7vx9sUyrXbBOuiPjcfqkQS6dG2I,155
|
|
51
|
+
standards/passports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
52
|
+
standards/passports/types.py,sha256=NRTHSV6TdavW5mBklLvBg587LR2-3dmdrdnq_sY8uMk,88
|
|
53
|
+
standards/permissions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
|
+
standards/permissions/types.py,sha256=ktyRE1CDRMs9ISgsMMWer2-KDP4pcpvIvFuSckoT_3o,227
|
|
55
|
+
standards/roles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
|
+
standards/roles/types.py,sha256=ZDCUTGz7s3WslocydW9ZhSEA-LeVwr9mD-5iIp-dScM,172
|
|
57
|
+
standards/transports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
58
|
+
standards/transports/types.py,sha256=rkJhZlunaGCkND-bhYixg8wBFtrrd4nwOZUiKuw3tmk,397
|
|
59
|
+
openshell_shared-0.1.2.dist-info/METADATA,sha256=IikTmxI49ECBLiFR6za92qc2ZbVLdXEYnTtA8yOHmB4,3210
|
|
60
|
+
openshell_shared-0.1.2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
61
|
+
openshell_shared-0.1.2.dist-info/top_level.txt,sha256=t4a8dGEQBmWWTub6GnION4QEEYGWGBChA8jUHIpeCw0,61
|
|
62
|
+
openshell_shared-0.1.2.dist-info/RECORD,,
|
protocols/__init__.py
ADDED
|
File without changes
|