mrok 0.1.6__py3-none-any.whl → 0.1.8__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 (66) hide show
  1. mrok/__init__.py +6 -0
  2. mrok/agent/__init__.py +0 -0
  3. mrok/agent/sidecar/__init__.py +3 -0
  4. mrok/agent/sidecar/app.py +30 -0
  5. mrok/agent/sidecar/main.py +27 -0
  6. mrok/agent/ziticorn.py +29 -0
  7. mrok/cli/__init__.py +3 -0
  8. mrok/cli/commands/__init__.py +7 -0
  9. mrok/cli/commands/admin/__init__.py +12 -0
  10. mrok/cli/commands/admin/bootstrap.py +58 -0
  11. mrok/cli/commands/admin/list/__init__.py +8 -0
  12. mrok/cli/commands/admin/list/extensions.py +144 -0
  13. mrok/cli/commands/admin/list/instances.py +167 -0
  14. mrok/cli/commands/admin/register/__init__.py +8 -0
  15. mrok/cli/commands/admin/register/extensions.py +46 -0
  16. mrok/cli/commands/admin/register/instances.py +60 -0
  17. mrok/cli/commands/admin/unregister/__init__.py +8 -0
  18. mrok/cli/commands/admin/unregister/extensions.py +33 -0
  19. mrok/cli/commands/admin/unregister/instances.py +34 -0
  20. mrok/cli/commands/admin/utils.py +49 -0
  21. mrok/cli/commands/agent/__init__.py +6 -0
  22. mrok/cli/commands/agent/run/__init__.py +7 -0
  23. mrok/cli/commands/agent/run/asgi.py +49 -0
  24. mrok/cli/commands/agent/run/sidecar.py +54 -0
  25. mrok/cli/commands/controller/__init__.py +7 -0
  26. mrok/cli/commands/controller/openapi.py +47 -0
  27. mrok/cli/commands/controller/run.py +87 -0
  28. mrok/cli/main.py +97 -0
  29. mrok/cli/rich.py +18 -0
  30. mrok/conf.py +32 -0
  31. mrok/controller/__init__.py +0 -0
  32. mrok/controller/app.py +62 -0
  33. mrok/controller/auth.py +87 -0
  34. mrok/controller/dependencies/__init__.py +4 -0
  35. mrok/controller/dependencies/conf.py +7 -0
  36. mrok/controller/dependencies/ziti.py +27 -0
  37. mrok/controller/openapi/__init__.py +3 -0
  38. mrok/controller/openapi/examples.py +44 -0
  39. mrok/controller/openapi/utils.py +35 -0
  40. mrok/controller/pagination.py +79 -0
  41. mrok/controller/routes.py +294 -0
  42. mrok/controller/schemas.py +67 -0
  43. mrok/errors.py +2 -0
  44. mrok/http/__init__.py +0 -0
  45. mrok/http/config.py +65 -0
  46. mrok/http/forwarder.py +299 -0
  47. mrok/http/lifespan.py +10 -0
  48. mrok/http/master.py +90 -0
  49. mrok/http/protocol.py +11 -0
  50. mrok/http/server.py +14 -0
  51. mrok/logging.py +76 -0
  52. mrok/ziti/__init__.py +15 -0
  53. mrok/ziti/api.py +481 -0
  54. mrok/ziti/bootstrap.py +71 -0
  55. mrok/ziti/constants.py +9 -0
  56. mrok/ziti/errors.py +25 -0
  57. mrok/ziti/identities.py +169 -0
  58. mrok/ziti/pki.py +52 -0
  59. mrok/ziti/services.py +87 -0
  60. {mrok-0.1.6.dist-info → mrok-0.1.8.dist-info}/METADATA +7 -9
  61. mrok-0.1.8.dist-info/RECORD +64 -0
  62. {mrok-0.1.6.dist-info → mrok-0.1.8.dist-info}/WHEEL +1 -2
  63. mrok-0.1.6.dist-info/RECORD +0 -6
  64. mrok-0.1.6.dist-info/top_level.txt +0 -1
  65. {mrok-0.1.6.dist-info → mrok-0.1.8.dist-info}/entry_points.txt +0 -0
  66. {mrok-0.1.6.dist-info → mrok-0.1.8.dist-info}/licenses/LICENSE.txt +0 -0
mrok/ziti/api.py ADDED
@@ -0,0 +1,481 @@
1
+ import json
2
+ import logging
3
+ import ssl
4
+ import tempfile
5
+ from abc import ABC, abstractmethod
6
+ from collections.abc import AsyncGenerator
7
+ from functools import cached_property
8
+ from types import TracebackType
9
+ from typing import Any, Literal
10
+
11
+ import httpx
12
+
13
+ from mrok.conf import Settings
14
+ from mrok.ziti.constants import MROK_VERSION_TAG, MROK_VERSION_TAG_NAME
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ TagsType = dict[str, str | bool | None]
19
+
20
+
21
+ class ZitiAPIError(Exception):
22
+ pass
23
+
24
+
25
+ class ZitiAuthError(ZitiAPIError):
26
+ pass
27
+
28
+
29
+ class ZitiBadRequestError(ZitiAPIError):
30
+ def __init__(self, response: dict[str, Any]):
31
+ self.response = response
32
+
33
+ def __str__(self) -> str:
34
+ err = self.response["error"]
35
+ cause = err["cause"]
36
+ return f"{err['code']} - {err['message']} ({cause['field']}: {cause['reason']})"
37
+
38
+
39
+ class BaseZitiAPI(ABC):
40
+ def __init__(self, settings: Settings):
41
+ self.settings = settings
42
+ self.limit = self.settings.pagination.limit
43
+ self.token = None
44
+
45
+ @property
46
+ @abstractmethod
47
+ def base_url(self):
48
+ raise NotImplementedError("base_url property must be implemented in subclasses")
49
+
50
+ @property
51
+ def auth(self):
52
+ if self.settings.ziti.auth.get("username") and self.settings.ziti.auth.get("password"):
53
+ return ZitiPasswordAuth(self)
54
+ elif self.settings.ziti.auth.get("identity"):
55
+ return ZitiIdentityAuth(self)
56
+ else:
57
+ raise ZitiAuthError("Unsupported authentication method for OpenZiti.")
58
+
59
+ @cached_property
60
+ def httpx_client(self) -> httpx.AsyncClient:
61
+ return httpx.AsyncClient(
62
+ base_url=self.base_url,
63
+ auth=self.auth,
64
+ verify=self.settings.ziti.ssl_verify,
65
+ timeout=httpx.Timeout(
66
+ connect=0.25,
67
+ read=self.settings.ziti.read_timeout,
68
+ write=2.0,
69
+ pool=5.0,
70
+ ),
71
+ )
72
+
73
+ async def create(self, endpoint: str, payload: dict[str, Any], tags: TagsType | None) -> str:
74
+ payload["tags"] = self._merge_tags(tags)
75
+ response: httpx.Response = await self.httpx_client.post(
76
+ endpoint,
77
+ json=payload,
78
+ )
79
+ if response.status_code == 400:
80
+ raise ZitiBadRequestError(response.json())
81
+ response.raise_for_status()
82
+ return response.json()["data"]["id"]
83
+
84
+ async def get(
85
+ self,
86
+ endpoint: str,
87
+ id: str,
88
+ additional_path: str | None = None,
89
+ ) -> dict[str, Any]:
90
+ url = f"{endpoint}/{id}"
91
+ if additional_path:
92
+ url = f"{url}/{additional_path}"
93
+ response = await self.httpx_client.get(url)
94
+ response.raise_for_status()
95
+ return response.json()["data"]
96
+
97
+ async def delete(self, endpoint: str, id: str) -> None:
98
+ response = await self.httpx_client.delete(f"{endpoint}/{id}")
99
+ response.raise_for_status()
100
+ return response.json()
101
+
102
+ async def search_by_id_or_name(self, endpoint: str, id_or_name: str) -> dict[str, Any] | None:
103
+ query = (
104
+ f'(id="{id_or_name}" or name="{id_or_name.lower()}") '
105
+ f"and tags.{MROK_VERSION_TAG_NAME} != null"
106
+ )
107
+ response = await self.httpx_client.get(
108
+ endpoint,
109
+ params={"filter": query},
110
+ )
111
+ if response.status_code == 400:
112
+ raise ZitiBadRequestError(response.json())
113
+ response.raise_for_status()
114
+ response_data = response.json()
115
+ if response_data["meta"]["pagination"]["totalCount"] == 1:
116
+ return response_data["data"][0]
117
+
118
+ async def get_page(
119
+ self, endpoint: str, limit: int, offset: int, params: dict[str, Any] | None = None
120
+ ) -> dict[str, Any]:
121
+ params = params or {}
122
+ params["limit"] = limit
123
+ params["offset"] = offset
124
+ page_response = await self.httpx_client.get(endpoint, params=params)
125
+ page_response.raise_for_status()
126
+ page = page_response.json()
127
+ return page
128
+
129
+ async def collection_iterator(
130
+ self, endpoint: str, params: dict[str, Any] | None = None
131
+ ) -> AsyncGenerator[dict, None]:
132
+ offset = 0
133
+ while True:
134
+ page = await self.get_page(endpoint, self.limit, offset, params=params)
135
+ items = page["data"]
136
+
137
+ for item in items:
138
+ yield item
139
+
140
+ pagination_meta = page["meta"]["pagination"]
141
+ total = pagination_meta["totalCount"]
142
+ if total <= self.limit + offset:
143
+ break
144
+
145
+ offset = offset + self.limit
146
+
147
+ async def __aenter__(self):
148
+ await self.httpx_client.__aenter__()
149
+ return self
150
+
151
+ async def __aexit__(
152
+ self,
153
+ exc_type: type[BaseException] | None = None,
154
+ exc_val: BaseException | None = None,
155
+ exc_tb: TracebackType | None = None,
156
+ ) -> None:
157
+ return await self.httpx_client.__aexit__(exc_type, exc_val, exc_tb)
158
+
159
+ def _merge_tags(self, tags: TagsType | None) -> TagsType:
160
+ prepared_tags: TagsType = tags or {}
161
+ prepared_tags.update(MROK_VERSION_TAG)
162
+ return prepared_tags
163
+
164
+
165
+ class BaseZitiAuth(httpx.Auth):
166
+ def __init__(self, api: BaseZitiAPI):
167
+ self.api = api
168
+
169
+ def update_token(self, response: httpx.Response) -> None:
170
+ response.raise_for_status()
171
+ data = response.json()
172
+ self.api.token = data["data"]["token"]
173
+
174
+
175
+ class ZitiIdentityAuthContext:
176
+ def __init__(self, identity_path: str):
177
+ identity = json.load(open(identity_path))
178
+ cert_data = identity["id"]["cert"][4:]
179
+ key_data = identity["id"]["key"][4:]
180
+ ca_data = identity["id"]["ca"][4:]
181
+ with (
182
+ tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".pem") as key_file,
183
+ tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".pem") as cert_file,
184
+ tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".pem") as ca_file,
185
+ ):
186
+ key_file.write(key_data)
187
+ key_file.flush()
188
+ cert_file.write(cert_data)
189
+ cert_file.flush()
190
+ ca_file.write(ca_data)
191
+ ca_file.flush()
192
+ self.ssl_context = ssl.create_default_context(
193
+ cafile=ca_file.name,
194
+ )
195
+ self.ssl_context.load_cert_chain(
196
+ keyfile=key_file.name,
197
+ certfile=cert_file.name,
198
+ )
199
+
200
+
201
+ class ZitiPasswordAuth(BaseZitiAuth):
202
+ requires_response_body = True
203
+
204
+ async def async_auth_flow(
205
+ self, request: httpx.Request
206
+ ) -> AsyncGenerator[httpx.Request, httpx.Response]:
207
+ request.headers["zt-session"] = self.api.token or ""
208
+ response = yield request
209
+
210
+ if response.status_code == 401: # pragma: no branch
211
+ refresh_request = self.build_refresh_request()
212
+ refresh_response = yield refresh_request
213
+ await refresh_response.aread()
214
+ self.update_token(refresh_response)
215
+ request.headers["zt-session"] = self.api.token or ""
216
+ yield request
217
+
218
+ def build_refresh_request(self) -> httpx.Request:
219
+ """Builds the token refresh request."""
220
+ return httpx.Request(
221
+ "POST",
222
+ f"{self.api.base_url}/authenticate",
223
+ params={"method": "password"},
224
+ json={
225
+ "username": self.api.settings.ziti.auth.username,
226
+ "password": self.api.settings.ziti.auth.password,
227
+ },
228
+ )
229
+
230
+
231
+ class ZitiIdentityAuth(BaseZitiAuth):
232
+ requires_response_body = True
233
+
234
+ def __init__(self, client: BaseZitiAPI):
235
+ super().__init__(client)
236
+ self.identity_context = ZitiIdentityAuthContext(self.api.settings.ziti.auth.identity)
237
+
238
+ async def async_auth_flow(
239
+ self, request: httpx.Request
240
+ ) -> AsyncGenerator[httpx.Request, httpx.Response]:
241
+ request.headers["zt-session"] = self.api.token or ""
242
+ response = yield request
243
+
244
+ if response.status_code == 401: # pragma: no cover
245
+ # Use the new client certificate authentication method
246
+ refresh_response = await self.get_auth_token()
247
+ # await refresh_response.aread()
248
+ self.update_token(refresh_response)
249
+ request.headers["zt-session"] = self.api.token or ""
250
+ yield request
251
+
252
+ async def get_auth_token(self):
253
+ async with httpx.AsyncClient(
254
+ base_url=self.api.base_url,
255
+ verify=self.identity_context.ssl_context,
256
+ ) as client:
257
+ response = await client.post(
258
+ "/authenticate",
259
+ params={"method": "cert"},
260
+ )
261
+ return response
262
+
263
+
264
+ class ZitiManagementAPI(BaseZitiAPI):
265
+ @property
266
+ def base_url(self):
267
+ return f"{self.settings.ziti.api.management}/edge/management/v1"
268
+
269
+ def services(
270
+ self,
271
+ params: dict[str, Any] | None = None,
272
+ ) -> AsyncGenerator[dict[str, Any], None]:
273
+ return self.collection_iterator("/services", params=params)
274
+
275
+ def identities(
276
+ self,
277
+ params: dict[str, Any] | None = None,
278
+ ) -> AsyncGenerator[dict[str, Any], None]:
279
+ return self.collection_iterator("/identities", params=params)
280
+
281
+ async def search_config(self, id_or_name) -> dict[str, Any] | None:
282
+ return await self.search_by_id_or_name("/configs", id_or_name)
283
+
284
+ async def create_config(
285
+ self, name: str, config_type_id: str, tags: TagsType | None = None
286
+ ) -> str:
287
+ return await self.create(
288
+ "/configs",
289
+ {
290
+ "configTypeId": config_type_id,
291
+ "name": name,
292
+ "data": {
293
+ "auth_scheme": "none",
294
+ "basic_auth": None,
295
+ "interstitial": True,
296
+ "oauth": None,
297
+ },
298
+ },
299
+ tags,
300
+ )
301
+
302
+ async def delete_config(self, config_id: str) -> None:
303
+ return await self.delete("/configs", config_id)
304
+
305
+ async def create_config_type(self, name: str, tags: TagsType | None = None) -> str:
306
+ return await self.create(
307
+ "/config-types",
308
+ {
309
+ "name": name,
310
+ "schema": {},
311
+ },
312
+ tags,
313
+ )
314
+
315
+ async def create_service(
316
+ self,
317
+ name: str,
318
+ config_id: str,
319
+ tags: TagsType | None = None,
320
+ ) -> str:
321
+ return await self.create(
322
+ "/services",
323
+ {
324
+ "name": name,
325
+ "configs": [config_id],
326
+ "encryptionRequired": True,
327
+ },
328
+ tags,
329
+ )
330
+
331
+ async def create_service_router_policy(
332
+ self,
333
+ name: str,
334
+ service_id: str,
335
+ tags: TagsType | None = None,
336
+ ) -> str:
337
+ return await self.create(
338
+ "/service-edge-router-policies",
339
+ {
340
+ "name": name,
341
+ "edgeRouterRoles": ["#all"],
342
+ "serviceRoles": [
343
+ f"@{service_id}",
344
+ ],
345
+ "semantic": "AllOf",
346
+ },
347
+ tags,
348
+ )
349
+
350
+ async def create_router_policy(
351
+ self,
352
+ name: str,
353
+ identity_id: str,
354
+ tags: TagsType | None = None,
355
+ ) -> str:
356
+ return await self.create(
357
+ "/edge-router-policies",
358
+ {
359
+ "name": name,
360
+ "edgeRouterRoles": ["#all"],
361
+ "identityRoles": [f"@{identity_id}"],
362
+ "semantic": "AllOf",
363
+ },
364
+ tags,
365
+ )
366
+
367
+ async def search_service_router_policy(self, id_or_name: str) -> dict[str, Any] | None:
368
+ return await self.search_by_id_or_name("/service-edge-router-policies", id_or_name)
369
+
370
+ async def search_router_policy(self, id_or_name: str) -> dict[str, Any] | None:
371
+ return await self.search_by_id_or_name("/edge-router-policies", id_or_name)
372
+
373
+ async def delete_service_router_policy(self, policy_id: str) -> None:
374
+ return await self.delete("/service-edge-router-policies", policy_id)
375
+
376
+ async def delete_router_policy(self, policy_id: str) -> None:
377
+ return await self.delete("/edge-router-policies", policy_id)
378
+
379
+ async def search_service(self, id_or_name: str) -> dict[str, Any] | None:
380
+ return await self.search_by_id_or_name("/services", id_or_name)
381
+
382
+ async def get_service(self, service_id: str) -> dict[str, Any]:
383
+ return await self.get("/services", service_id)
384
+
385
+ async def delete_service(self, service_id: str) -> None:
386
+ return await self.delete("/services", service_id)
387
+
388
+ async def create_user_identity(self, name: str, tags: TagsType | None = None) -> str:
389
+ return await self._create_identity(name, "User", tags=tags)
390
+
391
+ async def create_device_identity(self, name: str, tags: TagsType | None = None) -> str:
392
+ return await self._create_identity(name, "Device", tags=tags)
393
+
394
+ async def search_identity(self, id_or_name: str) -> dict[str, Any] | None:
395
+ return await self.search_by_id_or_name("/identities", id_or_name)
396
+
397
+ async def search_config_type(self, id_or_name: str) -> dict[str, Any] | None:
398
+ return await self.search_by_id_or_name("/config-types", id_or_name)
399
+
400
+ async def get_identity(self, identity_id: str) -> dict[str, Any]:
401
+ return await self.get("/identities", identity_id)
402
+
403
+ async def delete_identity(self, identity_id: str) -> None:
404
+ return await self.delete("/identities", identity_id)
405
+
406
+ async def fetch_ca_certificates(self) -> str:
407
+ response = await self.httpx_client.get("/.well-known/est/cacerts")
408
+ response.raise_for_status()
409
+ return response.text
410
+
411
+ async def create_dial_service_policy(
412
+ self, name: str, service_id: str, identity_id: str, tags: TagsType | None = None
413
+ ) -> str:
414
+ return await self._create_service_policy("Dial", name, service_id, identity_id, tags)
415
+
416
+ async def create_bind_service_policy(
417
+ self, name: str, service_id: str, identity_id: str, tags: TagsType | None = None
418
+ ) -> str:
419
+ return await self._create_service_policy("Bind", name, service_id, identity_id, tags)
420
+
421
+ async def search_service_policy(self, id_or_name: str) -> dict[str, Any] | None:
422
+ return await self.search_by_id_or_name("/service-policies", id_or_name)
423
+
424
+ async def delete_service_policy(self, policy_id: str) -> None:
425
+ return await self.delete("/service-policies", policy_id)
426
+
427
+ async def _create_service_policy(
428
+ self,
429
+ type: Literal["Dial", "Bind"],
430
+ name: str,
431
+ service_id: str,
432
+ identity_id: str,
433
+ tags: TagsType | None = None,
434
+ ) -> str:
435
+ return await self.create(
436
+ "/service-policies",
437
+ {
438
+ "name": name,
439
+ "type": type,
440
+ "serviceRoles": [f"@{service_id}"],
441
+ "identityRoles": [f"@{identity_id}"],
442
+ "semantic": "AllOf",
443
+ },
444
+ tags,
445
+ )
446
+
447
+ async def _create_identity(
448
+ self,
449
+ name: str,
450
+ type: Literal["User", "Device", "Default"],
451
+ tags: TagsType | None = None,
452
+ ) -> str:
453
+ return await self.create(
454
+ "/identities",
455
+ {
456
+ "name": name,
457
+ "type": type,
458
+ "isAdmin": False,
459
+ "enrollment": {"ott": True},
460
+ },
461
+ tags,
462
+ )
463
+
464
+
465
+ class ZitiClientAPI(BaseZitiAPI):
466
+ @property
467
+ def base_url(self):
468
+ return f"{self.settings.ziti.api.client}/edge/client/v1"
469
+
470
+ async def enroll_identity(self, jti: str, csr_pem: str) -> dict[str, Any]:
471
+ response = await self.httpx_client.post(
472
+ "/enroll",
473
+ params={
474
+ "method": "ott",
475
+ "token": jti,
476
+ },
477
+ headers={"Content-Type": "application/x-pem-file"},
478
+ content=csr_pem,
479
+ )
480
+ response.raise_for_status()
481
+ return response.json()
mrok/ziti/bootstrap.py ADDED
@@ -0,0 +1,71 @@
1
+ import logging
2
+ from typing import Any
3
+
4
+ from mrok.ziti.api import TagsType, ZitiClientAPI, ZitiManagementAPI
5
+ from mrok.ziti.identities import enroll_proxy_identity
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ async def bootstrap_identity(
11
+ mgmt_api: ZitiManagementAPI,
12
+ client_api: ZitiClientAPI,
13
+ identity_name: str,
14
+ mode: str,
15
+ forced: bool,
16
+ tags: TagsType | None,
17
+ ) -> tuple[str, dict[str, Any] | None]:
18
+ logger.info(f"Bootstrapping '{identity_name}' identity...")
19
+
20
+ identity_json = None
21
+ existing_identity = await mgmt_api.search_identity(identity_name)
22
+
23
+ if forced and existing_identity:
24
+ logger.info(f"Deleting existing identity '{identity_name}' ({existing_identity['id']})")
25
+
26
+ policy = await mgmt_api.search_router_policy(identity_name)
27
+ if policy:
28
+ await mgmt_api.delete_router_policy(policy["id"])
29
+ logger.info(f"Deleted existing ERP '{policy['name']}' ({policy['id']})")
30
+
31
+ await mgmt_api.delete_identity(existing_identity["id"])
32
+ logger.info("Deleted existing identity")
33
+ existing_identity = None
34
+
35
+ if existing_identity:
36
+ frontend_id = existing_identity["id"]
37
+ logger.info(f"Identity '{identity_name}' ({frontend_id}) is already enrolled")
38
+ else:
39
+ frontend_id, identity_json = await enroll_proxy_identity(
40
+ mgmt_api,
41
+ client_api,
42
+ identity_name,
43
+ tags=tags,
44
+ )
45
+ logger.info(f"Identity '{identity_name}' ({frontend_id}) successfully enrolled")
46
+
47
+ policy = await mgmt_api.search_router_policy(identity_name)
48
+ if not policy:
49
+ policy_id = await mgmt_api.create_router_policy(
50
+ identity_name,
51
+ frontend_id,
52
+ tags=tags,
53
+ )
54
+ logger.info(f"Created ERP '{identity_name}' ({policy_id})")
55
+ else:
56
+ logger.info(f"Found ERP '{policy['name']}' ({policy['id']})")
57
+
58
+ config_type_name = f"{mode}.proxy.v1"
59
+ config_type = await mgmt_api.search_config_type(config_type_name)
60
+ if config_type is None:
61
+ config_type_id = await mgmt_api.create_config_type(config_type_name, tags=tags)
62
+ logger.info(f"Created '{config_type_name}' ({config_type_id}) config type")
63
+ else:
64
+ logger.info(f"Found '{config_type_name}' ({config_type['id']}) config type")
65
+
66
+ if config_type and existing_identity:
67
+ logger.info(f"Identity '{identity_name}' was already bootstrapped")
68
+ else:
69
+ logger.info("Bootstrap completed")
70
+
71
+ return frontend_id, identity_json
mrok/ziti/constants.py ADDED
@@ -0,0 +1,9 @@
1
+ import mrok
2
+
3
+ MROK_VERSION_TAG_NAME = "mrok"
4
+ MROK_VERSION_TAG_VALUE = mrok.__version__
5
+ MROK_VERSION_TAG = {MROK_VERSION_TAG_NAME: MROK_VERSION_TAG_VALUE}
6
+ MROK_SERVICE_TAG_NAME = "mrok-service"
7
+ MROK_IDENTITY_TYPE_TAG_NAME = "mrok-identity-type"
8
+ MROK_IDENTITY_TYPE_TAG_VALUE_INSTANCE = "instance"
9
+ MROK_IDENTITY_TYPE_TAG_VALUE_PROXY = "proxy"
mrok/ziti/errors.py ADDED
@@ -0,0 +1,25 @@
1
+ from mrok.errors import MrokError
2
+
3
+
4
+ class ProxyIdentityNotFoundError(MrokError):
5
+ pass
6
+
7
+
8
+ class ProxyIdentityAlreadyExistsError(MrokError):
9
+ pass
10
+
11
+
12
+ class ServiceNotFoundError(MrokError):
13
+ pass
14
+
15
+
16
+ class UserIdentityNotFoundError(MrokError):
17
+ pass
18
+
19
+
20
+ class ConfigTypeNotFoundError(MrokError):
21
+ pass
22
+
23
+
24
+ class ServiceAlreadyRegisteredError(MrokError):
25
+ pass