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
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# shared/api/manager/v1/sessions.py
|
|
2
|
+
"""
|
|
3
|
+
Dominio: Sessions.
|
|
4
|
+
|
|
5
|
+
Encapsula la creación, listado y cierre de sesiones de shell sobre un
|
|
6
|
+
túnel ya establecido.
|
|
7
|
+
|
|
8
|
+
Endpoints cubiertos (todos bajo ``/api/v/1/sessions``):
|
|
9
|
+
POST /request
|
|
10
|
+
POST /query
|
|
11
|
+
POST /delete
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import List
|
|
17
|
+
|
|
18
|
+
from .models import SessionDeletionResult, SessionInfo
|
|
19
|
+
from .transport import HttpTransport
|
|
20
|
+
|
|
21
|
+
_PREFIX = "/api/v/1/sessions"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SessionsAPI:
|
|
25
|
+
"""API especializada para la gestión de sesiones OSAM."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, transport: HttpTransport) -> None:
|
|
28
|
+
self._transport = transport
|
|
29
|
+
|
|
30
|
+
async def create(
|
|
31
|
+
self, auth_token: str, tunnel_token: str, destination_uid: str
|
|
32
|
+
) -> dict:
|
|
33
|
+
"""
|
|
34
|
+
POST /api/v/1/sessions/request
|
|
35
|
+
|
|
36
|
+
Crea una nueva sesión sobre el túnel ``tunnel_token`` hacia la
|
|
37
|
+
entidad destino ``destination_uid``.
|
|
38
|
+
"""
|
|
39
|
+
body = await self._transport.post(
|
|
40
|
+
f"{_PREFIX}/request",
|
|
41
|
+
json={
|
|
42
|
+
"auth_token": auth_token,
|
|
43
|
+
"tunnel_token": tunnel_token,
|
|
44
|
+
"destination_uid": destination_uid,
|
|
45
|
+
},
|
|
46
|
+
)
|
|
47
|
+
return body
|
|
48
|
+
|
|
49
|
+
async def list(self, auth_token: str, tunnel_token: str) -> list:
|
|
50
|
+
"""
|
|
51
|
+
POST /api/v/1/sessions/query
|
|
52
|
+
|
|
53
|
+
Lista las sesiones activas. Nota: el servidor exige ``tunnel_token``
|
|
54
|
+
en el payload (forma parte del modelo de request), aunque la
|
|
55
|
+
implementación actual del handler no lo use para filtrar — se envía
|
|
56
|
+
igualmente para cumplir el contrato HTTP exacto del servidor.
|
|
57
|
+
"""
|
|
58
|
+
body = await self._transport.post(
|
|
59
|
+
f"{_PREFIX}/query",
|
|
60
|
+
json={"auth_token": auth_token, "tunnel_token": tunnel_token},
|
|
61
|
+
)
|
|
62
|
+
sessions = body.get("sessions", [])
|
|
63
|
+
if not isinstance(sessions, list):
|
|
64
|
+
sessions = []
|
|
65
|
+
return sessions
|
|
66
|
+
|
|
67
|
+
async def close(
|
|
68
|
+
self, auth_token: str, tunnel_token: str, session_token: str
|
|
69
|
+
) -> dict:
|
|
70
|
+
"""
|
|
71
|
+
POST /api/v/1/sessions/delete
|
|
72
|
+
|
|
73
|
+
Revoca la sesión identificada por ``session_token``.
|
|
74
|
+
"""
|
|
75
|
+
body = await self._transport.post(
|
|
76
|
+
f"{_PREFIX}/delete",
|
|
77
|
+
json={
|
|
78
|
+
"auth_token": auth_token,
|
|
79
|
+
"tunnel_token": tunnel_token,
|
|
80
|
+
"session_token": session_token,
|
|
81
|
+
},
|
|
82
|
+
)
|
|
83
|
+
return body
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# shared/api/manager/v1/transport.py
|
|
2
|
+
"""
|
|
3
|
+
Capa de transporte HTTP centralizada de la SDK de OSAM.
|
|
4
|
+
|
|
5
|
+
``HttpTransport`` es el único lugar del paquete que sabe construir URLs,
|
|
6
|
+
serializar/deserializar JSON, aplicar timeouts y traducir respuestas HTTP
|
|
7
|
+
de error en excepciones propias de la SDK (ver ``exceptions.py``).
|
|
8
|
+
|
|
9
|
+
Ningún otro módulo de ``shared/api/manager/v1`` debe importar ``httpx`` directamente
|
|
10
|
+
ni construir URLs a mano: todos los módulos de dominio (``identity.py``,
|
|
11
|
+
``authentication.py``, ``domains.py``, etc.) reciben una instancia de
|
|
12
|
+
``HttpTransport`` y delegan en ella toda la comunicación de red.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
from typing import Any, Dict, Mapping, Optional, Union
|
|
19
|
+
|
|
20
|
+
import httpx
|
|
21
|
+
|
|
22
|
+
from .exceptions import (
|
|
23
|
+
APIError,
|
|
24
|
+
AuthenticationError,
|
|
25
|
+
AuthorizationError,
|
|
26
|
+
EntityNotFoundError,
|
|
27
|
+
InvalidResponseError,
|
|
28
|
+
NetworkError,
|
|
29
|
+
ServerError,
|
|
30
|
+
ValidationError,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger("osam.transport")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _mask_secret(value: Optional[str], keep: int = 4) -> str:
|
|
37
|
+
"""
|
|
38
|
+
Devuelve una versión enmascarada de un secreto (token, código, etc.)
|
|
39
|
+
apta para logging. Nunca se debe registrar un token completo.
|
|
40
|
+
"""
|
|
41
|
+
if not value:
|
|
42
|
+
return "<empty>"
|
|
43
|
+
if len(value) <= keep * 2:
|
|
44
|
+
return "*" * len(value)
|
|
45
|
+
return f"{value[:keep]}...{value[-keep:]}"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _extract_detail(body: Any) -> str:
|
|
49
|
+
"""
|
|
50
|
+
Extrae un mensaje legible del cuerpo de error de FastAPI.
|
|
51
|
+
|
|
52
|
+
FastAPI usa de forma consistente el formato ``{"detail": ...}``.
|
|
53
|
+
``detail`` normalmente es un string (HTTPException explícita) pero en
|
|
54
|
+
errores de validación de pydantic (422) puede ser una lista de objetos
|
|
55
|
+
``{"loc": ..., "msg": ..., "type": ...}``.
|
|
56
|
+
"""
|
|
57
|
+
if isinstance(body, Mapping):
|
|
58
|
+
detail = body.get("detail")
|
|
59
|
+
if isinstance(detail, str):
|
|
60
|
+
return detail
|
|
61
|
+
if isinstance(detail, list):
|
|
62
|
+
parts = []
|
|
63
|
+
for item in detail:
|
|
64
|
+
if isinstance(item, Mapping) and "msg" in item:
|
|
65
|
+
loc = ".".join(str(p) for p in item.get("loc", []))
|
|
66
|
+
parts.append(f"{loc}: {item['msg']}" if loc else str(item["msg"]))
|
|
67
|
+
else:
|
|
68
|
+
parts.append(str(item))
|
|
69
|
+
if parts:
|
|
70
|
+
return "; ".join(parts)
|
|
71
|
+
if detail is not None:
|
|
72
|
+
return str(detail)
|
|
73
|
+
return "Error desconocido devuelto por el servidor OSAM"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class HttpTransport:
|
|
77
|
+
"""
|
|
78
|
+
Cliente HTTP interno y de bajo nivel usado por toda la SDK.
|
|
79
|
+
|
|
80
|
+
Responsabilidades:
|
|
81
|
+
* Mantener la URL base (protocolo + host + puerto).
|
|
82
|
+
* Aplicar timeouts y verificación TLS de forma consistente.
|
|
83
|
+
* Serializar el cuerpo de la petición y deserializar la respuesta JSON.
|
|
84
|
+
* Traducir errores de red y códigos HTTP de error en la jerarquía de
|
|
85
|
+
excepciones de ``exceptions.py``.
|
|
86
|
+
|
|
87
|
+
No conoce nada sobre los dominios funcionales de OSAM (identidad,
|
|
88
|
+
pasaportes, túneles...); eso vive en los módulos de dominio que
|
|
89
|
+
consumen esta clase.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
def __init__(
|
|
93
|
+
self,
|
|
94
|
+
base_url: str,
|
|
95
|
+
*,
|
|
96
|
+
timeout: float = 10.0,
|
|
97
|
+
verify_ssl: Union[bool, str] = True,
|
|
98
|
+
default_headers: Optional[Dict[str, str]] = None,
|
|
99
|
+
) -> None:
|
|
100
|
+
self._base_url = base_url.rstrip("/")
|
|
101
|
+
self._client = httpx.AsyncClient(
|
|
102
|
+
base_url=self._base_url,
|
|
103
|
+
timeout=timeout,
|
|
104
|
+
verify=verify_ssl,
|
|
105
|
+
headers=default_headers or {},
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# -- ciclo de vida -----------------------------------------------------
|
|
109
|
+
|
|
110
|
+
async def aclose(self) -> None:
|
|
111
|
+
await self._client.aclose()
|
|
112
|
+
|
|
113
|
+
async def __aenter__(self) -> "HttpTransport":
|
|
114
|
+
return self
|
|
115
|
+
|
|
116
|
+
async def __aexit__(self, *exc_info: Any) -> None:
|
|
117
|
+
await self.aclose()
|
|
118
|
+
|
|
119
|
+
# -- API pública usada por los módulos de dominio -----------------------
|
|
120
|
+
|
|
121
|
+
async def get(
|
|
122
|
+
self,
|
|
123
|
+
path: str,
|
|
124
|
+
*,
|
|
125
|
+
params: Optional[Mapping[str, Any]] = None,
|
|
126
|
+
bearer_token: Optional[str] = None,
|
|
127
|
+
) -> Dict[str, Any]:
|
|
128
|
+
return await self._request(
|
|
129
|
+
"GET", path, params=params, bearer_token=bearer_token
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
async def post(
|
|
133
|
+
self,
|
|
134
|
+
path: str,
|
|
135
|
+
*,
|
|
136
|
+
json: Optional[Mapping[str, Any]] = None,
|
|
137
|
+
bearer_token: Optional[str] = None,
|
|
138
|
+
) -> Dict[str, Any]:
|
|
139
|
+
return await self._request(
|
|
140
|
+
"POST", path, json=json, bearer_token=bearer_token
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# -- internals ----------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
async def _request(
|
|
146
|
+
self,
|
|
147
|
+
method: str,
|
|
148
|
+
path: str,
|
|
149
|
+
*,
|
|
150
|
+
json: Optional[Mapping[str, Any]] = None,
|
|
151
|
+
params: Optional[Mapping[str, Any]] = None,
|
|
152
|
+
bearer_token: Optional[str] = None,
|
|
153
|
+
) -> Dict[str, Any]:
|
|
154
|
+
headers: Dict[str, str] = {}
|
|
155
|
+
if bearer_token:
|
|
156
|
+
headers["Authorization"] = f"Bearer {bearer_token}"
|
|
157
|
+
|
|
158
|
+
logger.debug(
|
|
159
|
+
"OSAM %s %s%s (bearer=%s)",
|
|
160
|
+
method,
|
|
161
|
+
self._base_url,
|
|
162
|
+
path,
|
|
163
|
+
_mask_secret(bearer_token),
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
response = await self._client.request(
|
|
168
|
+
method,
|
|
169
|
+
path,
|
|
170
|
+
json=dict(json) if json is not None else None,
|
|
171
|
+
params=dict(params) if params is not None else None,
|
|
172
|
+
headers=headers,
|
|
173
|
+
)
|
|
174
|
+
except httpx.TimeoutException as exc:
|
|
175
|
+
raise NetworkError(
|
|
176
|
+
f"Tiempo de espera agotado al llamar a {method} {path}"
|
|
177
|
+
) from exc
|
|
178
|
+
except httpx.TransportError as exc:
|
|
179
|
+
raise NetworkError(
|
|
180
|
+
f"No se pudo establecer comunicación con el servidor OSAM "
|
|
181
|
+
f"({method} {path}): {exc}"
|
|
182
|
+
) from exc
|
|
183
|
+
|
|
184
|
+
body = self._parse_body(response, method, path)
|
|
185
|
+
|
|
186
|
+
if response.is_success:
|
|
187
|
+
return body
|
|
188
|
+
|
|
189
|
+
# _raise_for_status siempre lanza una subclase de APIError.
|
|
190
|
+
self._raise_for_status(response.status_code, body, method, path)
|
|
191
|
+
raise AssertionError("unreachable") # pragma: no cover
|
|
192
|
+
|
|
193
|
+
@staticmethod
|
|
194
|
+
def _parse_body(
|
|
195
|
+
response: httpx.Response, method: str, path: str
|
|
196
|
+
) -> Dict[str, Any]:
|
|
197
|
+
if not response.content:
|
|
198
|
+
return {}
|
|
199
|
+
try:
|
|
200
|
+
data = response.json()
|
|
201
|
+
except ValueError as exc:
|
|
202
|
+
raise InvalidResponseError(
|
|
203
|
+
f"Respuesta no es JSON válido para {method} {path} "
|
|
204
|
+
f"(status={response.status_code})"
|
|
205
|
+
) from exc
|
|
206
|
+
|
|
207
|
+
if isinstance(data, dict):
|
|
208
|
+
return data
|
|
209
|
+
if isinstance(data, list):
|
|
210
|
+
# Algunos endpoints (ej. /domains/query) devuelven una lista
|
|
211
|
+
# cruda en lugar de un objeto. Se envuelve para mantener una
|
|
212
|
+
# interfaz homogénea hacia el resto de la SDK.
|
|
213
|
+
return {"items": data}
|
|
214
|
+
|
|
215
|
+
raise InvalidResponseError(
|
|
216
|
+
f"Respuesta JSON con forma inesperada para {method} {path}: "
|
|
217
|
+
f"se esperaba un objeto o una lista, se obtuvo {type(data).__name__}"
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
@staticmethod
|
|
221
|
+
def _raise_for_status(
|
|
222
|
+
status_code: int, body: Dict[str, Any], method: str, path: str
|
|
223
|
+
) -> None:
|
|
224
|
+
detail = _extract_detail(body)
|
|
225
|
+
message = f"{method} {path} -> HTTP {status_code}: {detail}"
|
|
226
|
+
|
|
227
|
+
if status_code in (400, 422):
|
|
228
|
+
raise ValidationError(
|
|
229
|
+
message, status_code=status_code, detail=detail, response_body=body
|
|
230
|
+
)
|
|
231
|
+
if status_code == 401:
|
|
232
|
+
raise AuthenticationError(
|
|
233
|
+
message, status_code=status_code, detail=detail, response_body=body
|
|
234
|
+
)
|
|
235
|
+
if status_code == 403:
|
|
236
|
+
raise AuthorizationError(
|
|
237
|
+
message, status_code=status_code, detail=detail, response_body=body
|
|
238
|
+
)
|
|
239
|
+
if status_code == 404:
|
|
240
|
+
raise EntityNotFoundError(
|
|
241
|
+
message, status_code=status_code, detail=detail, response_body=body
|
|
242
|
+
)
|
|
243
|
+
if status_code >= 500:
|
|
244
|
+
raise ServerError(
|
|
245
|
+
message, status_code=status_code, detail=detail, response_body=body
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
# Cualquier otro código de error no contemplado explícitamente por
|
|
249
|
+
# la API de OSAM cae en el caso genérico, sin perder información.
|
|
250
|
+
raise APIError(
|
|
251
|
+
message, status_code=status_code, detail=detail, response_body=body
|
|
252
|
+
)
|
|
253
|
+
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# shared/api/manager/v1/tunnels.py
|
|
2
|
+
"""
|
|
3
|
+
Dominio: Tunnels.
|
|
4
|
+
|
|
5
|
+
Encapsula la solicitud y gestión de túneles. La API actual del servidor
|
|
6
|
+
expone:
|
|
7
|
+
|
|
8
|
+
* ``request`` (vigente): solicita un nuevo túnel asociado a la entidad
|
|
9
|
+
autenticada.
|
|
10
|
+
* ``open`` y ``link`` (marcados ``DEPRECATED`` explícitamente en el código
|
|
11
|
+
de las rutas del servidor): se mantienen en la SDK por compatibilidad,
|
|
12
|
+
pero cada llamada emite un ``DeprecationWarning`` para que el código
|
|
13
|
+
consumidor migre al flujo vigente (``request`` + ``sessions``).
|
|
14
|
+
|
|
15
|
+
No existen (todavía) endpoints de consulta de estado o renovación de un
|
|
16
|
+
túnel ya creado; se dejan ``get_status`` y ``renew`` como puntos de
|
|
17
|
+
extensión explícitos.
|
|
18
|
+
|
|
19
|
+
Endpoints cubiertos:
|
|
20
|
+
POST /api/v/1/services/tunnels/request
|
|
21
|
+
POST /api/v/1/services/tunnels/open (deprecated)
|
|
22
|
+
POST /api/v/1/services/tunnels/link (deprecated)
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import warnings
|
|
28
|
+
|
|
29
|
+
from .models import TunnelInfo, TunnelOperationResult
|
|
30
|
+
from .transport import HttpTransport
|
|
31
|
+
|
|
32
|
+
_PREFIX = "/api/v/1/services/tunnels"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class TunnelsAPI:
|
|
36
|
+
"""API especializada para la gestión de túneles OSAM."""
|
|
37
|
+
|
|
38
|
+
def __init__(self, transport: HttpTransport) -> None:
|
|
39
|
+
self._transport = transport
|
|
40
|
+
|
|
41
|
+
async def request(self, auth_token: str) -> dict:
|
|
42
|
+
"""
|
|
43
|
+
POST /api/v/1/services/tunnels/request
|
|
44
|
+
|
|
45
|
+
Solicita un nuevo túnel para la entidad identificada por
|
|
46
|
+
``auth_token``. Requiere que la entidad ya pertenezca a, al menos,
|
|
47
|
+
un dominio.
|
|
48
|
+
"""
|
|
49
|
+
body = await self._transport.post(
|
|
50
|
+
f"{_PREFIX}/request",
|
|
51
|
+
json={"auth_token": auth_token},
|
|
52
|
+
)
|
|
53
|
+
return body
|
|
54
|
+
|
|
55
|
+
async def open(self, session_token: str, tunnel_uid: str) -> dict:
|
|
56
|
+
"""
|
|
57
|
+
POST /api/v/1/services/tunnels/open [DEPRECATED]
|
|
58
|
+
|
|
59
|
+
Mantenido por compatibilidad con integraciones existentes. Use
|
|
60
|
+
:meth:`request` seguido del dominio de ``sessions`` en código nuevo.
|
|
61
|
+
"""
|
|
62
|
+
warnings.warn(
|
|
63
|
+
"TunnelsAPI.open() llama a un endpoint marcado como DEPRECATED "
|
|
64
|
+
"en el servidor OSAM. Use TunnelsAPI.request() junto con "
|
|
65
|
+
"SessionsAPI.create() en código nuevo.",
|
|
66
|
+
DeprecationWarning,
|
|
67
|
+
stacklevel=2,
|
|
68
|
+
)
|
|
69
|
+
body = await self._transport.post(
|
|
70
|
+
f"{_PREFIX}/open",
|
|
71
|
+
json={"session_token": session_token, "tunnel_uid": tunnel_uid},
|
|
72
|
+
)
|
|
73
|
+
return body
|
|
74
|
+
|
|
75
|
+
async def link(
|
|
76
|
+
self,
|
|
77
|
+
session_token: str,
|
|
78
|
+
source_tunnel_uid: str,
|
|
79
|
+
destination_tunnel_uid: str,
|
|
80
|
+
) -> dict:
|
|
81
|
+
"""
|
|
82
|
+
POST /api/v/1/services/tunnels/link [DEPRECATED]
|
|
83
|
+
|
|
84
|
+
Mantenido por compatibilidad con integraciones existentes.
|
|
85
|
+
"""
|
|
86
|
+
warnings.warn(
|
|
87
|
+
"TunnelsAPI.link() llama a un endpoint marcado como DEPRECATED "
|
|
88
|
+
"en el servidor OSAM.",
|
|
89
|
+
DeprecationWarning,
|
|
90
|
+
stacklevel=2,
|
|
91
|
+
)
|
|
92
|
+
body = await self._transport.post(
|
|
93
|
+
f"{_PREFIX}/link",
|
|
94
|
+
json={
|
|
95
|
+
"session_token": session_token,
|
|
96
|
+
"source_tunnel_uid": source_tunnel_uid,
|
|
97
|
+
"destination_tunnel_uid": destination_tunnel_uid,
|
|
98
|
+
},
|
|
99
|
+
)
|
|
100
|
+
return body
|
|
101
|
+
|
|
102
|
+
async def get_status(self, auth_token: str, tunnel_uid: str) -> TunnelInfo:
|
|
103
|
+
"""
|
|
104
|
+
Punto de extensión: el servidor de OSAM no expone todavía un
|
|
105
|
+
endpoint dedicado de consulta de estado de un túnel existente.
|
|
106
|
+
"""
|
|
107
|
+
raise NotImplementedError(
|
|
108
|
+
"El servidor de OSAM no expone todavía un endpoint de consulta "
|
|
109
|
+
"de estado de túneles."
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
async def renew(self, auth_token: str, tunnel_uid: str) -> TunnelInfo:
|
|
113
|
+
"""
|
|
114
|
+
Punto de extensión: el servidor de OSAM no expone todavía un
|
|
115
|
+
endpoint de renovación de un túnel existente.
|
|
116
|
+
"""
|
|
117
|
+
raise NotImplementedError(
|
|
118
|
+
"El servidor de OSAM no expone todavía un endpoint de "
|
|
119
|
+
"renovación de túneles."
|
|
120
|
+
)
|
cryptography/__init__.py
ADDED
|
File without changes
|