mrok 0.6.0__py3-none-any.whl → 0.8.0__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 (43) hide show
  1. mrok/agent/devtools/inspector/__main__.py +3 -23
  2. mrok/agent/devtools/inspector/app.py +407 -112
  3. mrok/agent/devtools/inspector/utils.py +149 -0
  4. mrok/cli/commands/admin/bootstrap.py +2 -2
  5. mrok/cli/commands/admin/register/extensions.py +7 -9
  6. mrok/cli/commands/admin/register/instances.py +13 -16
  7. mrok/cli/commands/admin/unregister/extensions.py +7 -11
  8. mrok/cli/commands/admin/unregister/instances.py +12 -12
  9. mrok/cli/commands/agent/run/asgi.py +1 -1
  10. mrok/cli/commands/frontend/run.py +1 -1
  11. mrok/cli/main.py +17 -1
  12. mrok/cli/utils.py +26 -0
  13. mrok/conf.py +15 -7
  14. mrok/constants.py +21 -0
  15. mrok/controller/app.py +12 -10
  16. mrok/controller/auth/__init__.py +11 -0
  17. mrok/controller/auth/backends.py +60 -0
  18. mrok/controller/auth/base.py +38 -0
  19. mrok/controller/auth/manager.py +31 -0
  20. mrok/controller/auth/registry.py +17 -0
  21. mrok/frontend/app.py +94 -26
  22. mrok/frontend/main.py +8 -5
  23. mrok/frontend/middleware.py +35 -0
  24. mrok/frontend/utils.py +83 -0
  25. mrok/logging.py +24 -22
  26. mrok/proxy/app.py +13 -5
  27. mrok/proxy/middleware.py +7 -8
  28. mrok/proxy/models.py +36 -10
  29. mrok/proxy/ziticorn.py +8 -17
  30. mrok/ziti/api.py +4 -4
  31. mrok/ziti/bootstrap.py +0 -5
  32. mrok/ziti/identities.py +11 -10
  33. mrok/ziti/services.py +6 -6
  34. {mrok-0.6.0.dist-info → mrok-0.8.0.dist-info}/METADATA +9 -3
  35. {mrok-0.6.0.dist-info → mrok-0.8.0.dist-info}/RECORD +38 -35
  36. mrok/agent/devtools/__main__.py +0 -34
  37. mrok/cli/commands/agent/utils.py +0 -5
  38. mrok/controller/auth.py +0 -87
  39. mrok/proxy/constants.py +0 -22
  40. mrok/proxy/utils.py +0 -90
  41. {mrok-0.6.0.dist-info → mrok-0.8.0.dist-info}/WHEEL +0 -0
  42. {mrok-0.6.0.dist-info → mrok-0.8.0.dist-info}/entry_points.txt +0 -0
  43. {mrok-0.6.0.dist-info → mrok-0.8.0.dist-info}/licenses/LICENSE.txt +0 -0
mrok/ziti/api.py CHANGED
@@ -38,7 +38,7 @@ class ZitiBadRequestError(ZitiAPIError):
38
38
  class BaseZitiAPI(ABC):
39
39
  def __init__(self, settings: Settings):
40
40
  self.settings = settings
41
- self.limit = self.settings.pagination.limit
41
+ self.limit = self.settings.controller.pagination.limit
42
42
  self.token = None
43
43
 
44
44
  @property
@@ -62,7 +62,7 @@ class BaseZitiAPI(ABC):
62
62
  auth=self.auth,
63
63
  verify=self.settings.ziti.ssl_verify,
64
64
  timeout=httpx.Timeout(
65
- connect=0.25,
65
+ connect=self.settings.ziti.connect_timeout,
66
66
  read=self.settings.ziti.read_timeout,
67
67
  write=2.0,
68
68
  pool=5.0,
@@ -263,7 +263,7 @@ class ZitiIdentityAuth(BaseZitiAuth):
263
263
  class ZitiManagementAPI(BaseZitiAPI):
264
264
  @property
265
265
  def base_url(self):
266
- return f"{self.settings.ziti.api.management}/edge/management/v1"
266
+ return f"{self.settings.ziti.base_urls.management}/edge/management/v1"
267
267
 
268
268
  def services(
269
269
  self,
@@ -465,7 +465,7 @@ class ZitiManagementAPI(BaseZitiAPI):
465
465
  class ZitiClientAPI(BaseZitiAPI):
466
466
  @property
467
467
  def base_url(self):
468
- return f"{self.settings.ziti.api.client}/edge/client/v1"
468
+ return f"{self.settings.ziti.base_urls.client}/edge/client/v1"
469
469
 
470
470
  async def enroll_identity(self, jti: str, csr_pem: str) -> dict[str, Any]:
471
471
  response = await self.httpx_client.post(
mrok/ziti/bootstrap.py CHANGED
@@ -36,11 +36,6 @@ async def bootstrap_identity(
36
36
  logger.info("Deleted existing identity")
37
37
  existing_identity = None
38
38
 
39
- if forced and config_type:
40
- await mgmt_api.delete_config_type(config_type["id"])
41
- logger.info(f"Deleted existing config type '{config_type_name}' ({config_type['id']})")
42
- config_type = None
43
-
44
39
  if existing_identity:
45
40
  frontend_id = existing_identity["id"]
46
41
  logger.info(f"Identity '{identity_name}' ({frontend_id}) is already enrolled")
mrok/ziti/identities.py CHANGED
@@ -41,7 +41,8 @@ async def register_identity(
41
41
  raise ServiceNotFoundError(f"A service with name `{service_external_id}` does not exists.")
42
42
 
43
43
  identity_name = identity_external_id.lower()
44
- service_policy_name = f"{identity_name}:bind"
44
+ router_policy_name = f"{identity_external_id.lower()}.{service_name}"
45
+ service_policy_name = f"{identity_external_id.lower()}.{service_name}:bind"
45
46
  self_service_policy_name = f"self.{service_policy_name}"
46
47
 
47
48
  identity = await mgmt_api.search_identity(identity_name)
@@ -52,7 +53,7 @@ async def register_identity(
52
53
  service_policy = await mgmt_api.search_service_policy(self_service_policy_name)
53
54
  if service_policy:
54
55
  await mgmt_api.delete_service_policy(service_policy["id"])
55
- router_policy = await mgmt_api.search_router_policy(identity_name)
56
+ router_policy = await mgmt_api.search_router_policy(router_policy_name)
56
57
  if router_policy:
57
58
  await mgmt_api.delete_router_policy(router_policy["id"])
58
59
  await mgmt_api.delete_identity(identity["id"])
@@ -66,10 +67,9 @@ async def register_identity(
66
67
  identity_id,
67
68
  identity,
68
69
  mrok={
69
- "identity": identity_name,
70
- "extension": service_external_id,
71
- "instance": identity_external_id,
72
- "domain": settings.proxy.domain,
70
+ "extension": service_name,
71
+ "instance": identity_name,
72
+ "domain": settings.frontend.domain,
73
73
  "tags": identity_tags,
74
74
  },
75
75
  )
@@ -84,7 +84,7 @@ async def register_identity(
84
84
  self_service["id"],
85
85
  identity_id,
86
86
  )
87
- await mgmt_api.create_router_policy(identity_name, identity_id)
87
+ await mgmt_api.create_router_policy(router_policy_name, identity_id)
88
88
 
89
89
  return identity, identity_json
90
90
 
@@ -100,8 +100,9 @@ async def unregister_identity(
100
100
  if not service:
101
101
  raise ServiceNotFoundError(f"A service with name `{service_external_id}` does not exists.")
102
102
 
103
- identity_name = f"{identity_external_id.lower()}.{service_name}"
104
- service_policy_name = f"{identity_name}:bind"
103
+ identity_name = identity_external_id.lower()
104
+ router_policy_name = f"{identity_external_id.lower()}.{service_name}"
105
+ service_policy_name = f"{identity_external_id.lower()}.{service_name}:bind"
105
106
 
106
107
  identity = await mgmt_api.search_identity(identity_name)
107
108
  if not identity:
@@ -120,7 +121,7 @@ async def unregister_identity(
120
121
  service_policy = await mgmt_api.search_service_policy(service_policy_name)
121
122
  if service_policy:
122
123
  await mgmt_api.delete_service_policy(service_policy["id"])
123
- router_policy = await mgmt_api.search_router_policy(identity_name)
124
+ router_policy = await mgmt_api.search_router_policy(router_policy_name)
124
125
  if router_policy:
125
126
  await mgmt_api.delete_router_policy(router_policy["id"])
126
127
  await mgmt_api.delete_identity(identity["id"])
mrok/ziti/services.py CHANGED
@@ -19,15 +19,15 @@ async def register_service(
19
19
  ) -> dict[str, Any]:
20
20
  service_name = external_id.lower()
21
21
  registered = False
22
- proxy_identity = await mgmt_api.search_identity(settings.proxy.identity)
22
+ proxy_identity = await mgmt_api.search_identity(settings.frontend.identity)
23
23
  if not proxy_identity:
24
24
  raise ProxyIdentityNotFoundError(
25
- f"Identity for proxy `{settings.proxy.identity}` not found.",
25
+ f"Identity for proxy `{settings.frontend.identity}` not found.",
26
26
  )
27
27
 
28
- config_type = await mgmt_api.search_config_type(f"{settings.proxy.mode}.proxy.v1")
28
+ config_type = await mgmt_api.search_config_type(f"{settings.frontend.mode}.proxy.v1")
29
29
  if not config_type:
30
- raise ConfigTypeNotFoundError(f"Config type `{settings.proxy.mode}.proxy.v1` not found.")
30
+ raise ConfigTypeNotFoundError(f"Config type `{settings.frontend.mode}.proxy.v1` not found.")
31
31
 
32
32
  config = await mgmt_api.search_config(service_name)
33
33
  if not config:
@@ -43,7 +43,7 @@ async def register_service(
43
43
  else:
44
44
  service_id = service["id"]
45
45
  proxy_identity_id = proxy_identity["id"]
46
- service_policy_name = f"{service_name}:{settings.proxy.identity}:dial"
46
+ service_policy_name = f"{service_name}:{settings.frontend.identity}:dial"
47
47
  dial_service_policy = await mgmt_api.search_service_policy(service_policy_name)
48
48
  if not dial_service_policy:
49
49
  await mgmt_api.create_dial_service_policy(
@@ -75,7 +75,7 @@ async def unregister_service(
75
75
  if router_policy:
76
76
  await mgmt_api.delete_service_router_policy(router_policy["id"])
77
77
 
78
- service_policy_name = f"{service_name}:{settings.proxy.identity}:dial"
78
+ service_policy_name = f"{service_name}:{settings.frontend.identity}:dial"
79
79
 
80
80
  dial_service_policy = await mgmt_api.search_service_policy(service_policy_name)
81
81
  if dial_service_policy:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mrok
3
- Version: 0.6.0
3
+ Version: 0.8.0
4
4
  Summary: MPT Extensions OpenZiti Orchestrator
5
5
  Author: SoftwareOne AG
6
6
  License: Apache License
@@ -214,6 +214,7 @@ Requires-Dist: fastapi[standard]<0.120.0,>=0.119.0
214
214
  Requires-Dist: gunicorn<24.0.0,>=23.0.0
215
215
  Requires-Dist: hdrhistogram<0.11.0,>=0.10.3
216
216
  Requires-Dist: httpcore<2.0.0,>=1.0.9
217
+ Requires-Dist: multipart<2.0.0,>=1.3.0
217
218
  Requires-Dist: openziti<2.0.0,>=1.3.1
218
219
  Requires-Dist: psutil<8.0.0,>=7.1.3
219
220
  Requires-Dist: pydantic<3.0.0,>=2.11.7
@@ -223,8 +224,8 @@ Requires-Dist: pyyaml<7.0.0,>=6.0.2
223
224
  Requires-Dist: pyzmq<28.0.0,>=27.1.0
224
225
  Requires-Dist: rich<15.0.0,>=14.1.0
225
226
  Requires-Dist: textual-serve<2.0.0,>=1.1.3
226
- Requires-Dist: textual<7.0.0,>=6.5.0
227
- Requires-Dist: typer<0.20.0,>=0.19.2
227
+ Requires-Dist: textual[syntax]<8.0.0,>=7.2.0
228
+ Requires-Dist: typer<1.0.0,>=0.21.1
228
229
  Requires-Dist: uvicorn-worker<0.5.0,>=0.4.0
229
230
  Description-Content-Type: text/markdown
230
231
 
@@ -240,6 +241,7 @@ It uses the [OpenZiti](https://openziti.io) zero-trust network overlay to create
240
241
  - **Agent** – Runs alongside an extension in two modes:
241
242
  - *Sidecar mode*: proxies traffic between the Ziti network and a local TCP or Unix socket.
242
243
  - *Embeddable mode*: integrates with ASGI servers (e.g. Uvicorn) to serve a Python application directly.
244
+ - **Frontend** - Proxies internet request to a specific extension through the OpenZiti network.
243
245
  - **CLI** – A command-line tool for administrative tasks and for running the agent in either mode.
244
246
 
245
247
  ## Key Features
@@ -247,5 +249,9 @@ It uses the [OpenZiti](https://openziti.io) zero-trust network overlay to create
247
249
  - Zero-trust networking with automatic balancing across Extension instances.
248
250
  - Simple API and CLI for managing services and identities.
249
251
 
252
+ ## Development
253
+ The included docker compose starts a local Ziti Network (controller + router) and mrok (controller and frontend).
254
+
255
+
250
256
  ## License
251
257
  [Apache 2.0](LICENSE)
@@ -1,54 +1,57 @@
1
1
  mrok/__init__.py,sha256=D1PUs3KtMCqG4bFLceVNG62L3RN53NS95uSCNXpgvzs,181
2
- mrok/conf.py,sha256=_5Z-A5LyojQeY8J7W8C0QidsmrPl99r9qKYEoMf4kcI,840
3
- mrok/constants.py,sha256=UTGYqs3DgEd_SN-k0JK10ekmHVWQaaARtdh1Y-0JG_s,122
2
+ mrok/conf.py,sha256=5AgRgwE_Yq0Dv7xDv0SWakhPSr3nnUNjIIgnn0Zgf9c,1057
3
+ mrok/constants.py,sha256=QXaMw4LuHijj_TUTCsM5uUjpgT04HBvd0wRbjvn1z9A,449
4
4
  mrok/errors.py,sha256=ruNMDFr2_0ezCGXuCG1OswCEv-bHOIzMMd02J_0ABcs,37
5
- mrok/logging.py,sha256=ZMWn0w4fJ-F_g-L37H_GM14BSXAIF2mFF_ougX5S7mg,2856
5
+ mrok/logging.py,sha256=PS_x0uAQDsIPF_tbF11fIyijwLS03DQwJzzUHMdVdnE,3000
6
6
  mrok/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  mrok/agent/ziticorn.py,sha256=eHUYs9QaSp35rBzYHRV-SrYxF5ySyECaQg7U-XbdINE,1025
8
8
  mrok/agent/devtools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- mrok/agent/devtools/__main__.py,sha256=R8ezbW7hCik5r45U3w2TgiTubg9SlbVsWA-bapILJXU,781
10
9
  mrok/agent/devtools/inspector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- mrok/agent/devtools/inspector/__main__.py,sha256=HeYcRf1bjXPji2LKMPCcTU61afrRH2P1RqnFmHClRTc,524
12
- mrok/agent/devtools/inspector/app.py,sha256=rmfm0GVz_iZ_tqne6E966D2He5QVprO2g7PHFrnjG0U,16484
10
+ mrok/agent/devtools/inspector/__main__.py,sha256=vof04S9fwiU8lGjt7YiM6O9YOXEW_lT1AxGoXyF4bg8,97
11
+ mrok/agent/devtools/inspector/app.py,sha256=TxBBIy8lXHj6h8KmQHCeLiIM2QqMzMsCpSWFjH8cGu4,25636
13
12
  mrok/agent/devtools/inspector/server.py,sha256=C4uD6_1psSHMjJLUDCMPGvKdQYKaEwYTw27NAbwuuA0,636
13
+ mrok/agent/devtools/inspector/utils.py,sha256=K-_4rTyB54Y0faEIGgutMEHGyP1W7eOhbcuUKxNnsYA,4261
14
14
  mrok/agent/sidecar/__init__.py,sha256=DrjJGhqFyxsVODW06KI20Wpr6HsD2lD6qFCKUXc7GIE,59
15
15
  mrok/agent/sidecar/app.py,sha256=sPQqjnwETRQk0cj8hAxSVUDCkYqsVZCS6ixEqkcGY5A,2534
16
16
  mrok/agent/sidecar/main.py,sha256=jeJzrCbltfXOYsKSCjcw8h5lxh4_bGT87kCC5dV4kYU,2190
17
17
  mrok/cli/__init__.py,sha256=mtFEa8IeS1x6Gm4dUYoSnAxyEzNqbUVSmWxtuZUMR84,61
18
- mrok/cli/main.py,sha256=DFcYPwDskXi8SKAgEsuP4GMFzaniIf_6bZaSDWvYKDk,2724
18
+ mrok/cli/main.py,sha256=T029FYxK_jDrwiA14oX-Onoqp_X14XHRAA_4bbaOgV8,3123
19
19
  mrok/cli/rich.py,sha256=P3Dyu8EArUR9_0j7DPK7LRx85TWdYdZ1SaJzD_S1ZCE,511
20
- mrok/cli/utils.py,sha256=m_olScdIUGks5IoC6p2F9D6CQIucWZ7LHyrvwm2bkJw,106
20
+ mrok/cli/utils.py,sha256=FXqyNef0cRFy_d-63ZY-kT3uqmDOK3USSNgBSZyKXIE,831
21
21
  mrok/cli/commands/__init__.py,sha256=-UOGzh38oWX7fPeI2nc5I9z8LylRdQAt868q4G6rNGk,140
22
22
  mrok/cli/commands/admin/__init__.py,sha256=WU49jpMF9p18UONjYywWEFzjF57zLpLKJ0qAZvrzcR4,414
23
- mrok/cli/commands/admin/bootstrap.py,sha256=9ADSeiVbFAZXh6GxHEf9h2g_XHGOIlMmg1rgsxMfdow,1699
23
+ mrok/cli/commands/admin/bootstrap.py,sha256=McIGngjpRQSsI2CqW_LBTF1lBeGXvTbbt31jYS35xIY,1705
24
24
  mrok/cli/commands/admin/utils.py,sha256=Z7YTAFZKOi6nkw2oX4rJoGoUD41RYL3AOqEDhlV3jR0,1357
25
25
  mrok/cli/commands/admin/list/__init__.py,sha256=kjCMcpn1gopcrQaaHxfFh8Kyngldepnle8R2br5dJ_0,195
26
26
  mrok/cli/commands/admin/list/extensions.py,sha256=16fhDB5ucL8su2WQnSaQ1E6MhgC4vkP9-nuHAcPpzyE,4405
27
27
  mrok/cli/commands/admin/list/instances.py,sha256=kaqeyidwUxgYqfaHXqp2m76rm5h2ErBsYyZcNeaBRwY,5912
28
28
  mrok/cli/commands/admin/register/__init__.py,sha256=5Jb_bc2L47MEpQIrOcquzduTFWQ01Jd1U1MpqaR-Ekw,209
29
- mrok/cli/commands/admin/register/extensions.py,sha256=dxciVA_S31rZSm0A7lkecn2mI9TMlWDhcJTgwgNXbM4,1460
30
- mrok/cli/commands/admin/register/instances.py,sha256=raF57jPUTryWdvNqGCosth1C-8jjv9IbA0UuNbDel3A,2220
29
+ mrok/cli/commands/admin/register/extensions.py,sha256=3ooHR-zfFImtqAZ-06kS0555v9gQLQ1G5-ARe_mJ9e4,1353
30
+ mrok/cli/commands/admin/register/instances.py,sha256=KjyLJX1mSXK-6ZmkW9I4PJFYNfgsOyAmJWh92XxSkYg,1982
31
31
  mrok/cli/commands/admin/unregister/__init__.py,sha256=-GjjCPX1pISbWmJK6GpKO3ijGsDQb21URjU1hNu99O4,215
32
- mrok/cli/commands/admin/unregister/extensions.py,sha256=GR3Iwzeksk_R0GkgmCSG7iHRcUrI7ABqDi25Gbes64Y,1016
33
- mrok/cli/commands/admin/unregister/instances.py,sha256=-28wL8pTXTWHVHtw93y8-dqi-Dlf0OZOnlBCKOyGo80,1138
32
+ mrok/cli/commands/admin/unregister/extensions.py,sha256=xL2yX0kn8dhitQL7NcLTn83bbPZfgPJNzHjZAdiP8yM,891
33
+ mrok/cli/commands/admin/unregister/instances.py,sha256=sLBfBhHDgR7Qw5Zc-EVOSuQUgfLMgh7cFnOP-73iM70,1167
34
34
  mrok/cli/commands/agent/__init__.py,sha256=ZAi7eTkKQtfwwV1c1mv3uvEEsyMMrhCQ_-id_0wksAQ,218
35
- mrok/cli/commands/agent/utils.py,sha256=m_olScdIUGks5IoC6p2F9D6CQIucWZ7LHyrvwm2bkJw,106
36
35
  mrok/cli/commands/agent/dev/__init__.py,sha256=ZfreyRuaLqO0AwPS8Ll1DIpsKacsu7_dTmbxV5QecOM,172
37
36
  mrok/cli/commands/agent/dev/console.py,sha256=rrKAGoKXVQQBOC75H0JSuX1sYyvc2QSrV-dfMPK49p4,673
38
37
  mrok/cli/commands/agent/dev/web.py,sha256=O9dYk-o1FV2E_sKLOezdEmLsnexwbJNDdsYL5pATZRQ,1028
39
38
  mrok/cli/commands/agent/run/__init__.py,sha256=E_IJCl3BfMffqFASe8gzJwhhQgt5bQfjhuyekVwdEBA,164
40
- mrok/cli/commands/agent/run/asgi.py,sha256=dCgzwJtTLv2eyEIP7v1tDfe_PrFBS02SfN5dSDw1Jzg,2054
39
+ mrok/cli/commands/agent/run/asgi.py,sha256=FzM3suWJPRqQ08SoDrF9mLfjiBJ6huSbfP3wkTbh3Uo,2054
41
40
  mrok/cli/commands/agent/run/sidecar.py,sha256=UOewegTLFwAZ70VFJb6_9kV0LmsvnXuq-yqgrMlTeZo,4182
42
41
  mrok/cli/commands/controller/__init__.py,sha256=2xw-YVN0akiLiuGUU3XbYyZZ0ugOjQ6XhtTkzEKSmMA,161
43
42
  mrok/cli/commands/controller/openapi.py,sha256=QLjVao9UkB2vBaGkFi_q_jrlg4Np4ldMRwDIJsrJ7A8,1175
44
43
  mrok/cli/commands/controller/run.py,sha256=yl1p7oRHhQINWWjUKlRHtMIWUCV0KsxYdyVyazhX834,2406
45
44
  mrok/cli/commands/frontend/__init__.py,sha256=0kK37yG6qs7yAa8TYlKZUA-nHrWsO4y5CjbVkXafnuk,123
46
- mrok/cli/commands/frontend/run.py,sha256=_X1ylMe4-YCTghsu0XY-PB4nk3PL-PQq9YIgbkgJok8,2796
45
+ mrok/cli/commands/frontend/run.py,sha256=E6vJC9LprGyPetGLfyfJm8GDemEIRVnqetao4V3W9Kk,2796
47
46
  mrok/controller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
- mrok/controller/app.py,sha256=XxCIB7N1YE52vSYfvGW2UPgEEOZ9jxDMe2l9D2SfXi8,1866
49
- mrok/controller/auth.py,sha256=hYa0OPJ5X0beGxRP6qbxwJOVXj5TmzHjmam2OjTBKn4,2704
47
+ mrok/controller/app.py,sha256=JgyfEFbQeGLpHCrjFKQjdP8TRfV4YVec7Re8i0P4FE8,2040
50
48
  mrok/controller/pagination.py,sha256=raYpYa34q8Ckl4BXBOEdpWlKkFj6z7e6QLWr2HT7dzI,2187
51
49
  mrok/controller/schemas.py,sha256=PZPEsSJNrGSuplfjCPF_E-VJ721AzgR1Jj8P-Shw1cg,1699
50
+ mrok/controller/auth/__init__.py,sha256=st0q-NHQEQwYlvLEnQdonMVsDmczeL5cS4hkVK7NT6s,412
51
+ mrok/controller/auth/backends.py,sha256=xwiF7qFHh5okhqbTld4P2jSnFkSPZlGR4gfQu598Xrg,2288
52
+ mrok/controller/auth/base.py,sha256=NWEVtc9Y8I56NnyYrBiNzxHZxFSzVEbmkMY2u6RCANs,1131
53
+ mrok/controller/auth/manager.py,sha256=VQwov4UiAOXHBl_a9oPG90_QKRMNz2tWONF3JFR5RmM,1118
54
+ mrok/controller/auth/registry.py,sha256=VmwPPI6E2-oyB2MZDkK_G39EaAeU3Uq-b14GGKz1E-A,485
52
55
  mrok/controller/dependencies/__init__.py,sha256=voewk6gjkA0OarL6HFmfT_RLqBns0Fpl-VIqK5xVAEI,202
53
56
  mrok/controller/dependencies/conf.py,sha256=2Pa8fxJHkZ29q6UL-w6hUP_wr7WnNELfw5LlzWg1Tec,162
54
57
  mrok/controller/dependencies/ziti.py,sha256=fYoxeJb4s6p2_3gxbExbFSRabjpvp_gZMBb3ocXZV3Y,702
@@ -59,36 +62,36 @@ mrok/controller/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
59
62
  mrok/controller/routes/extensions.py,sha256=zoY4sNz_BIZcbly6WXM7Rbpn2jmB89njS_0xdJkoKfs,9192
60
63
  mrok/controller/routes/instances.py,sha256=v-fn_F6JHbDZ4YUNCIZzClgHp6aC1Eu5HB7k7qBG5pk,2202
61
64
  mrok/frontend/__init__.py,sha256=SN3LoFwAye18lfJ8OKNNS-7kLc2A9OxPGIEIEYYtAOA,54
62
- mrok/frontend/app.py,sha256=I2cvEI2ZGhbeazFhF6LavxBYywsv-4QkuNxCDo6-dkA,2627
63
- mrok/frontend/main.py,sha256=0KtchIGLn70A_Oxekmhr_qSYUg5QqIMfrdYcyFdpj9s,1717
65
+ mrok/frontend/app.py,sha256=_FLz5fqZdlFc1tdBBMwhmvjGa0UnNeaNj6EnYEnuPAQ,5280
66
+ mrok/frontend/main.py,sha256=zvfCh7NDx-mkpN-ppM2AqWmgKn1cBrNOCky8UgjLou4,1833
67
+ mrok/frontend/middleware.py,sha256=xTXt5gYikr9RCXaI4uVKgxFhlH49RNY3MD3eZPcX9cc,1161
68
+ mrok/frontend/utils.py,sha256=Oh987pCpg7ZIIIpRWcpX7Nrh8FGbJ4cH4ciiG1xRoQI,2120
64
69
  mrok/proxy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
65
- mrok/proxy/app.py,sha256=flnVPoUO3pSF3b0nYFBhKjZ9jp5ljijo2a-5e37gACs,6016
70
+ mrok/proxy/app.py,sha256=HU-YRKA3ijY2AW9v-NwfUnbSgdI-XqsnkQ90TocZ1Ts,6257
66
71
  mrok/proxy/asgi.py,sha256=2uw5bLquyUsiYlNwq8RhJd8OqVvSJDvYjzOVGLLB3Cs,3528
67
72
  mrok/proxy/backend.py,sha256=dRmIUJin2DM3PUxrVX0j4t1oB6DOX7N9JV2lIcopE38,1649
68
- mrok/proxy/constants.py,sha256=ao5gI2HFBWmrdd2Yc6XFK_RGaHk-omxI4AqvfIiGes8,409
69
73
  mrok/proxy/event_publisher.py,sha256=TAuwEqIhRYxgazJFgC3DekwUAXlJ2UFjbdx_A9vwA1g,2511
70
74
  mrok/proxy/exceptions.py,sha256=61OhdihQNdnBUqAI9mbBkXD1yWg-6Eftk7EhWCU1VF0,642
71
75
  mrok/proxy/master.py,sha256=HB2q_nPLim23z0mGDGKs_RshhGVAO8VgOYP4hA__zC4,6891
72
76
  mrok/proxy/metrics.py,sha256=Sg2aIiaj9fzkyu190YCsJvNn5P-XLun3BcvuVBsdWbA,3640
73
- mrok/proxy/middleware.py,sha256=St8r2hY5vfn4q5FtuqeI85tVmSZmC6Vkbecpx_iX6SM,4455
74
- mrok/proxy/models.py,sha256=CA520bqexPwYUHDrkg58OXAyzBIKQSU9i-SlymU_vt0,5188
77
+ mrok/proxy/middleware.py,sha256=So024RvtNgMliClKz4pzau2Wb6tlfZGspmHBzzEeF4U,4208
78
+ mrok/proxy/models.py,sha256=VDou3LGDi9zCpzpOwihP4Og1oRvXl7gb2mgczf6_Z14,6029
75
79
  mrok/proxy/stream.py,sha256=7V-bSAF9uNV1yVHKaEhHo95WxafSGWkyHPVABZX0djY,2134
76
- mrok/proxy/utils.py,sha256=OxX6pJv_Wh_KgWx95YeJ3YeuSgwm2tsd00897P3fxys,2126
77
80
  mrok/proxy/worker.py,sha256=uEUC2Hbx0YQiDMFNZfwkHMDnijN96b6iRoIErfI21Tg,1921
78
- mrok/proxy/ziticorn.py,sha256=YwbyNUK-TL3dANntM6gtlP9Su39QvDCC0dSbuZisXIo,2873
81
+ mrok/proxy/ziticorn.py,sha256=qiHKIB7ZRR9S9f-zXTxkzBWW3qlP9CqPOc0QQr-lgvc,2437
79
82
  mrok/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
80
83
  mrok/types/proxy.py,sha256=40Yds4tUykMpzsoQbMtHG85r8xtm5Q3fQZ17bp7cDiM,818
81
84
  mrok/types/ziti.py,sha256=EeQnTbDEJ-Y-KMS6zu1Xjxb58Up2VxUwqzUwy3H28JY,36
82
85
  mrok/ziti/__init__.py,sha256=20OWMiexRhOovZOX19zlX87-V78QyWnEnSZfyAftUdE,263
83
- mrok/ziti/api.py,sha256=ikl3l446Gu-YZuJJFRHFSBpKhh5KJxmk-Jr4YbSrqbk,16072
84
- mrok/ziti/bootstrap.py,sha256=PRZlYDtcGbix-1bQDuJ4wX4dy845VAx9SFPotc4BCeg,2715
86
+ mrok/ziti/api.py,sha256=Z6Hs17-UaKtcRc6uMowhvQW5fbyW6wFr-7lAvI3aZm0,16125
87
+ mrok/ziti/bootstrap.py,sha256=RSL8nZfI-MZ_z6h0F-rNevQoE6g9oGKLr6HlZF696_c,2499
85
88
  mrok/ziti/constants.py,sha256=Urq1X3bCBQZfw8NbnEa1pqmY4oq1wmzkwPfzam3kbTw,339
86
89
  mrok/ziti/errors.py,sha256=yYCbVDwktnR0AYduqtynIjo73K3HOhIrwA_vQimvEd4,368
87
- mrok/ziti/identities.py,sha256=9BIBQOirvcdAkRFNqZPTOkC8kvDQbq6EgaQMq22NQsQ,6730
90
+ mrok/ziti/identities.py,sha256=cOMv-Jv8MEjtzyjRcWOF8Ziz4HJY2Z-uHXWpZRqgPxs,6883
88
91
  mrok/ziti/pki.py,sha256=o2tySqHC8-7bvFuI2Tqxg9vX6H6ZSxWxfP_9x29e19M,1954
89
- mrok/ziti/services.py,sha256=TukG0vAZxgjbS8OLiyg7u1GwuVeGTco-rb9ne6a4PUA,3213
90
- mrok-0.6.0.dist-info/METADATA,sha256=0z3llP3xsGv5I-CZANA3tA9rhqNQ9sgH2j2sdLX5z7A,15705
91
- mrok-0.6.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
92
- mrok-0.6.0.dist-info/entry_points.txt,sha256=tloXwvU1uJicBJR2h-8HoVclPgwJWDwuREMHN8Zq-nU,38
93
- mrok-0.6.0.dist-info/licenses/LICENSE.txt,sha256=6PaICaoA3yNsZKLv5G6OKqSfLSoX7MakYqTDgJoTCBs,11346
94
- mrok-0.6.0.dist-info/RECORD,,
92
+ mrok/ziti/services.py,sha256=P2c9qRUyUFu1pSKPdT8L6s3yTKYVpTabRiHPWqbBIiU,3231
93
+ mrok-0.8.0.dist-info/METADATA,sha256=SmmFB0ZpkWORFIvUKENkpOszVnFjgqVZS6p7rbPyIwc,15978
94
+ mrok-0.8.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
95
+ mrok-0.8.0.dist-info/entry_points.txt,sha256=tloXwvU1uJicBJR2h-8HoVclPgwJWDwuREMHN8Zq-nU,38
96
+ mrok-0.8.0.dist-info/licenses/LICENSE.txt,sha256=6PaICaoA3yNsZKLv5G6OKqSfLSoX7MakYqTDgJoTCBs,11346
97
+ mrok-0.8.0.dist-info/RECORD,,
@@ -1,34 +0,0 @@
1
- #!/usr/bin/env python3
2
- """mrok agent devtools CLI entrypoint.
3
-
4
- Provides a small CLI that accepts an optional subscriber port and
5
- invokes the `run` function with that port.
6
- """
7
-
8
- import argparse
9
- from typing import Any
10
-
11
-
12
- def run(port: int) -> None:
13
- """Run the devtools agent using the given subscriber port.
14
-
15
- This is a stub for the runtime function. Implementation goes here.
16
- """
17
- pass
18
-
19
-
20
- def main(argv: Any = None) -> None:
21
- parser = argparse.ArgumentParser(description="mrok devtools agent")
22
- parser.add_argument(
23
- "-p",
24
- "--subscriber-port",
25
- type=int,
26
- default=50001,
27
- help="Port for subscriber (default: 50001)",
28
- )
29
- args = parser.parse_args(argv)
30
- run(args.subscriber_port)
31
-
32
-
33
- if __name__ == "__main__":
34
- main()
@@ -1,5 +0,0 @@
1
- import multiprocessing
2
-
3
-
4
- def number_of_workers() -> int:
5
- return (multiprocessing.cpu_count() * 2) + 1
mrok/controller/auth.py DELETED
@@ -1,87 +0,0 @@
1
- import logging
2
- from typing import Annotated
3
-
4
- import httpx
5
- import jwt
6
- from fastapi import Depends, HTTPException, Request, status
7
- from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
8
-
9
- from mrok.controller.dependencies.conf import AppSettings
10
-
11
- logger = logging.getLogger("mrok.controller")
12
-
13
- UNAUTHORIZED_EXCEPTION = HTTPException(
14
- status_code=status.HTTP_401_UNAUTHORIZED, detail="Unauthorized."
15
- )
16
-
17
-
18
- class JWTCredentials(HTTPAuthorizationCredentials):
19
- pass
20
-
21
-
22
- class JWTBearer(HTTPBearer):
23
- def __init__(self):
24
- super().__init__(auto_error=False)
25
-
26
- async def __call__(self, request: Request) -> JWTCredentials:
27
- credentials = await super().__call__(request)
28
- if not credentials:
29
- raise UNAUTHORIZED_EXCEPTION
30
- try:
31
- return JWTCredentials(
32
- scheme=credentials.scheme,
33
- credentials=credentials.credentials,
34
- )
35
- except jwt.InvalidTokenError:
36
- raise UNAUTHORIZED_EXCEPTION
37
-
38
-
39
- async def authenticate(
40
- settings: AppSettings,
41
- credentials: Annotated[JWTCredentials, Depends(JWTBearer())],
42
- ):
43
- async with httpx.AsyncClient(
44
- timeout=httpx.Timeout(
45
- connect=0.25,
46
- read=settings.auth.read_timeout,
47
- write=2.0,
48
- pool=5.0,
49
- ),
50
- ) as client:
51
- try:
52
- config_resp = await client.get(settings.auth.openid_config_url)
53
- config_resp.raise_for_status()
54
- config = config_resp.json()
55
- issuer = config["issuer"]
56
- jwks_uri = config["jwks_uri"]
57
-
58
- jwks_resp = await client.get(jwks_uri)
59
- jwks_resp.raise_for_status()
60
- jwks = jwks_resp.json()
61
-
62
- header = jwt.get_unverified_header(credentials.credentials)
63
- kid = header["kid"]
64
-
65
- key_data = next((k for k in jwks["keys"] if k["kid"] == kid), None)
66
- except Exception:
67
- logger.exception("Error fetching openid-config/jwks")
68
- raise UNAUTHORIZED_EXCEPTION
69
- if key_data is None:
70
- logger.error("Key ID not found in JWKS")
71
- raise UNAUTHORIZED_EXCEPTION
72
-
73
- try:
74
- payload = jwt.decode(
75
- credentials.credentials,
76
- jwt.PyJWK(key_data),
77
- algorithms=[header["alg"]],
78
- issuer=issuer,
79
- audience=settings.auth.audience,
80
- )
81
- return payload
82
- except jwt.InvalidKeyError as e:
83
- logger.error(f"Invalid jwt token: {e} ({credentials.credentials})")
84
- raise UNAUTHORIZED_EXCEPTION
85
- except jwt.InvalidTokenError as e:
86
- logger.error(f"Invalid jwt token: {e} ({credentials.credentials})")
87
- raise UNAUTHORIZED_EXCEPTION
mrok/proxy/constants.py DELETED
@@ -1,22 +0,0 @@
1
- MAX_REQUEST_BODY_BYTES = 2 * 1024 * 1024
2
- MAX_RESPONSE_BODY_BYTES = 5 * 1024 * 1024
3
-
4
- BINARY_CONTENT_TYPES = {
5
- "application/octet-stream",
6
- "application/pdf",
7
- }
8
-
9
- BINARY_PREFIXES = (
10
- "image/",
11
- "video/",
12
- "audio/",
13
- )
14
-
15
- TEXTUAL_CONTENT_TYPES = {
16
- "application/json",
17
- "application/xml",
18
- "application/javascript",
19
- "application/x-www-form-urlencoded",
20
- }
21
-
22
- TEXTUAL_PREFIXES = ("text/",)
mrok/proxy/utils.py DELETED
@@ -1,90 +0,0 @@
1
- from collections.abc import Mapping
2
-
3
- from mrok.proxy.constants import (
4
- BINARY_CONTENT_TYPES,
5
- BINARY_PREFIXES,
6
- MAX_REQUEST_BODY_BYTES,
7
- MAX_RESPONSE_BODY_BYTES,
8
- TEXTUAL_CONTENT_TYPES,
9
- TEXTUAL_PREFIXES,
10
- )
11
-
12
-
13
- def is_binary(content_type: str) -> bool:
14
- ct = content_type.lower()
15
- if ct in BINARY_CONTENT_TYPES:
16
- return True
17
- if any(ct.startswith(p) for p in BINARY_PREFIXES):
18
- return True
19
- return False
20
-
21
-
22
- def is_textual(content_type: str) -> bool:
23
- ct = content_type.lower()
24
- if ct in TEXTUAL_CONTENT_TYPES:
25
- return True
26
- if any(ct.startswith(p) for p in TEXTUAL_PREFIXES):
27
- return True
28
- return False
29
-
30
-
31
- def must_capture_request(
32
- method: str,
33
- headers: Mapping,
34
- ) -> bool:
35
- method = method.upper()
36
-
37
- # No body expected
38
- if method in ("GET", "HEAD", "OPTIONS", "TRACE"):
39
- return False
40
-
41
- content_type = headers.get("content-type", "").lower()
42
-
43
- content_length = None
44
- if "content-length" in headers:
45
- content_length = int(headers["content-length"])
46
-
47
- if is_binary(content_type):
48
- return False
49
-
50
- if content_type.startswith("multipart/form-data"):
51
- return False
52
-
53
- if content_length is not None and content_length > MAX_REQUEST_BODY_BYTES:
54
- return False
55
-
56
- if is_textual(content_type):
57
- return True
58
-
59
- if content_length is None:
60
- return True
61
-
62
- return content_length <= MAX_REQUEST_BODY_BYTES
63
-
64
-
65
- def must_capture_response(
66
- headers: Mapping,
67
- ) -> bool:
68
- content_type = headers.get("content-type", "").lower()
69
- disposition = headers.get("content-disposition", "").lower()
70
-
71
- content_length = None
72
- if "content-length" in headers:
73
- content_length = int(headers["content-length"])
74
-
75
- if "attachment" in disposition:
76
- return False
77
-
78
- if is_binary(content_type):
79
- return False
80
-
81
- if content_length is not None and content_length > MAX_RESPONSE_BODY_BYTES:
82
- return False
83
-
84
- if is_textual(content_type):
85
- return True
86
-
87
- if content_length is None:
88
- return True
89
-
90
- return content_length <= MAX_RESPONSE_BODY_BYTES
File without changes