zrok2-build-debugging 22.0.2000008__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 (213) hide show
  1. test/__init__.py +0 -0
  2. test/test_access201_response.py +52 -0
  3. test/test_access_request.py +54 -0
  4. test/test_access_summary.py +60 -0
  5. test/test_accesses_list.py +63 -0
  6. test/test_account_api.py +79 -0
  7. test/test_add_frontend_grant_request.py +52 -0
  8. test/test_add_namespace_frontend_mapping_request.py +53 -0
  9. test/test_add_namespace_grant_request.py +52 -0
  10. test/test_add_organization_member_request.py +53 -0
  11. test/test_admin_api.py +199 -0
  12. test/test_agent_api.py +85 -0
  13. test/test_auth_user.py +52 -0
  14. test/test_change_password_request.py +53 -0
  15. test/test_client_version_check_request.py +51 -0
  16. test/test_configuration.py +56 -0
  17. test/test_create_frontend201_response.py +51 -0
  18. test/test_create_frontend_request.py +55 -0
  19. test/test_create_identity201_response.py +52 -0
  20. test/test_create_identity_request.py +51 -0
  21. test/test_create_namespace201_response.py +51 -0
  22. test/test_create_namespace_request.py +54 -0
  23. test/test_create_organization201_response.py +51 -0
  24. test/test_create_organization_request.py +51 -0
  25. test/test_create_share_name_request.py +52 -0
  26. test/test_delete_identity_request.py +51 -0
  27. test/test_disable_request.py +51 -0
  28. test/test_enable_request.py +52 -0
  29. test/test_enroll200_response.py +51 -0
  30. test/test_enroll_request.py +51 -0
  31. test/test_environment.py +63 -0
  32. test/test_environment_and_resources.py +96 -0
  33. test/test_environment_api.py +43 -0
  34. test/test_environment_summary.py +60 -0
  35. test/test_environments_list.py +63 -0
  36. test/test_frontend.py +59 -0
  37. test/test_get_sparklines200_response.py +62 -0
  38. test/test_get_sparklines_request.py +57 -0
  39. test/test_invite_request.py +52 -0
  40. test/test_invite_token_generate_request.py +53 -0
  41. test/test_list_frontend_namespace_mappings200_response_inner.py +54 -0
  42. test/test_list_frontends200_response_inner.py +58 -0
  43. test/test_list_memberships200_response.py +56 -0
  44. test/test_list_memberships200_response_memberships_inner.py +53 -0
  45. test/test_list_namespaces200_response_inner.py +56 -0
  46. test/test_list_organization_members200_response.py +55 -0
  47. test/test_list_organization_members200_response_members_inner.py +52 -0
  48. test/test_list_organizations200_response.py +55 -0
  49. test/test_list_organizations200_response_organizations_inner.py +52 -0
  50. test/test_list_share_namespaces200_response_inner.py +53 -0
  51. test/test_login_request.py +52 -0
  52. test/test_metadata_api.py +145 -0
  53. test/test_metrics.py +59 -0
  54. test/test_metrics_sample.py +53 -0
  55. test/test_name.py +56 -0
  56. test/test_name_selection.py +52 -0
  57. test/test_overview.py +110 -0
  58. test/test_overview_names_inner.py +56 -0
  59. test/test_ping200_response.py +51 -0
  60. test/test_principal.py +55 -0
  61. test/test_regenerate_account_token200_response.py +51 -0
  62. test/test_regenerate_account_token_request.py +51 -0
  63. test/test_register_request.py +52 -0
  64. test/test_remote_access_request.py +60 -0
  65. test/test_remote_share200_response.py +54 -0
  66. test/test_remote_share_request.py +74 -0
  67. test/test_remote_status200_response.py +82 -0
  68. test/test_remote_status200_response_accesses_inner.py +62 -0
  69. test/test_remote_status200_response_shares_inner.py +64 -0
  70. test/test_remote_status200_response_shares_inner_failure.py +54 -0
  71. test/test_remote_unaccess_request.py +52 -0
  72. test/test_remote_unshare_request.py +52 -0
  73. test/test_remove_namespace_frontend_mapping_request.py +52 -0
  74. test/test_remove_organization_member_request.py +52 -0
  75. test/test_reset_password_request.py +52 -0
  76. test/test_share.py +67 -0
  77. test/test_share_api.py +103 -0
  78. test/test_share_http_healthcheck200_response.py +52 -0
  79. test/test_share_http_healthcheck_request.py +56 -0
  80. test/test_share_request.py +75 -0
  81. test/test_share_response.py +54 -0
  82. test/test_share_summary.py +62 -0
  83. test/test_shares_list.py +65 -0
  84. test/test_spark_data_sample.py +52 -0
  85. test/test_unaccess_request.py +53 -0
  86. test/test_unshare_request.py +52 -0
  87. test/test_update_access_request.py +53 -0
  88. test/test_update_frontend_request.py +55 -0
  89. test/test_update_namespace_request.py +55 -0
  90. test/test_update_share_name_request.py +53 -0
  91. test/test_update_share_request.py +57 -0
  92. test/test_verify200_response.py +51 -0
  93. test/test_verify_request.py +51 -0
  94. test/test_version_inventory200_response.py +51 -0
  95. zrok/__init__.py +4 -0
  96. zrok/_version.py +21 -0
  97. zrok/access.py +58 -0
  98. zrok/decor.py +33 -0
  99. zrok/dialer.py +10 -0
  100. zrok/environment/__init__.py +1 -0
  101. zrok/environment/dirs.py +32 -0
  102. zrok/environment/root.py +182 -0
  103. zrok/listener.py +29 -0
  104. zrok/model.py +75 -0
  105. zrok/overview.py +77 -0
  106. zrok/proxy.py +203 -0
  107. zrok/share.py +199 -0
  108. zrok2_build_debugging-22.0.2000008.dist-info/METADATA +24 -0
  109. zrok2_build_debugging-22.0.2000008.dist-info/RECORD +213 -0
  110. zrok2_build_debugging-22.0.2000008.dist-info/WHEEL +5 -0
  111. zrok2_build_debugging-22.0.2000008.dist-info/top_level.txt +3 -0
  112. zrok_api/__init__.py +231 -0
  113. zrok_api/api/__init__.py +10 -0
  114. zrok_api/api/account_api.py +2261 -0
  115. zrok_api/api/admin_api.py +7673 -0
  116. zrok_api/api/agent_api.py +2547 -0
  117. zrok_api/api/environment_api.py +589 -0
  118. zrok_api/api/metadata_api.py +5614 -0
  119. zrok_api/api/share_api.py +3321 -0
  120. zrok_api/api_client.py +801 -0
  121. zrok_api/api_response.py +21 -0
  122. zrok_api/configuration.py +602 -0
  123. zrok_api/exceptions.py +216 -0
  124. zrok_api/models/__init__.py +103 -0
  125. zrok_api/models/access201_response.py +89 -0
  126. zrok_api/models/access_request.py +93 -0
  127. zrok_api/models/access_summary.py +105 -0
  128. zrok_api/models/accesses_list.py +95 -0
  129. zrok_api/models/add_frontend_grant_request.py +89 -0
  130. zrok_api/models/add_namespace_frontend_mapping_request.py +91 -0
  131. zrok_api/models/add_namespace_grant_request.py +89 -0
  132. zrok_api/models/add_organization_member_request.py +91 -0
  133. zrok_api/models/auth_user.py +89 -0
  134. zrok_api/models/change_password_request.py +91 -0
  135. zrok_api/models/client_version_check_request.py +87 -0
  136. zrok_api/models/configuration.py +97 -0
  137. zrok_api/models/create_frontend201_response.py +87 -0
  138. zrok_api/models/create_frontend_request.py +105 -0
  139. zrok_api/models/create_identity201_response.py +89 -0
  140. zrok_api/models/create_identity_request.py +87 -0
  141. zrok_api/models/create_namespace201_response.py +87 -0
  142. zrok_api/models/create_namespace_request.py +93 -0
  143. zrok_api/models/create_organization201_response.py +87 -0
  144. zrok_api/models/create_organization_request.py +87 -0
  145. zrok_api/models/create_share_name_request.py +89 -0
  146. zrok_api/models/delete_identity_request.py +87 -0
  147. zrok_api/models/disable_request.py +87 -0
  148. zrok_api/models/enable_request.py +89 -0
  149. zrok_api/models/enroll200_response.py +87 -0
  150. zrok_api/models/enroll_request.py +87 -0
  151. zrok_api/models/environment.py +111 -0
  152. zrok_api/models/environment_and_resources.py +111 -0
  153. zrok_api/models/environment_summary.py +105 -0
  154. zrok_api/models/environments_list.py +95 -0
  155. zrok_api/models/frontend.py +103 -0
  156. zrok_api/models/get_sparklines200_response.py +95 -0
  157. zrok_api/models/get_sparklines_request.py +91 -0
  158. zrok_api/models/invite_request.py +89 -0
  159. zrok_api/models/invite_token_generate_request.py +87 -0
  160. zrok_api/models/list_frontend_namespace_mappings200_response_inner.py +93 -0
  161. zrok_api/models/list_frontends200_response_inner.py +101 -0
  162. zrok_api/models/list_memberships200_response.py +95 -0
  163. zrok_api/models/list_memberships200_response_memberships_inner.py +91 -0
  164. zrok_api/models/list_namespaces200_response_inner.py +97 -0
  165. zrok_api/models/list_organization_members200_response.py +95 -0
  166. zrok_api/models/list_organization_members200_response_members_inner.py +89 -0
  167. zrok_api/models/list_organizations200_response.py +95 -0
  168. zrok_api/models/list_organizations200_response_organizations_inner.py +89 -0
  169. zrok_api/models/list_share_namespaces200_response_inner.py +91 -0
  170. zrok_api/models/login_request.py +89 -0
  171. zrok_api/models/metrics.py +101 -0
  172. zrok_api/models/metrics_sample.py +91 -0
  173. zrok_api/models/name.py +97 -0
  174. zrok_api/models/name_selection.py +89 -0
  175. zrok_api/models/overview.py +117 -0
  176. zrok_api/models/overview_names_inner.py +97 -0
  177. zrok_api/models/ping200_response.py +87 -0
  178. zrok_api/models/principal.py +95 -0
  179. zrok_api/models/regenerate_account_token200_response.py +87 -0
  180. zrok_api/models/regenerate_account_token_request.py +87 -0
  181. zrok_api/models/register_request.py +89 -0
  182. zrok_api/models/remote_access_request.py +102 -0
  183. zrok_api/models/remote_share200_response.py +89 -0
  184. zrok_api/models/remote_share_request.py +141 -0
  185. zrok_api/models/remote_status200_response.py +105 -0
  186. zrok_api/models/remote_status200_response_accesses_inner.py +101 -0
  187. zrok_api/models/remote_status200_response_shares_inner.py +105 -0
  188. zrok_api/models/remote_status200_response_shares_inner_failure.py +93 -0
  189. zrok_api/models/remote_unaccess_request.py +89 -0
  190. zrok_api/models/remote_unshare_request.py +89 -0
  191. zrok_api/models/remove_namespace_frontend_mapping_request.py +89 -0
  192. zrok_api/models/remove_organization_member_request.py +89 -0
  193. zrok_api/models/reset_password_request.py +89 -0
  194. zrok_api/models/share.py +115 -0
  195. zrok_api/models/share_http_healthcheck200_response.py +89 -0
  196. zrok_api/models/share_http_healthcheck_request.py +97 -0
  197. zrok_api/models/share_request.py +157 -0
  198. zrok_api/models/share_response.py +89 -0
  199. zrok_api/models/share_summary.py +105 -0
  200. zrok_api/models/shares_list.py +95 -0
  201. zrok_api/models/spark_data_sample.py +89 -0
  202. zrok_api/models/unaccess_request.py +91 -0
  203. zrok_api/models/unshare_request.py +89 -0
  204. zrok_api/models/update_access_request.py +91 -0
  205. zrok_api/models/update_frontend_request.py +95 -0
  206. zrok_api/models/update_namespace_request.py +95 -0
  207. zrok_api/models/update_share_name_request.py +91 -0
  208. zrok_api/models/update_share_request.py +91 -0
  209. zrok_api/models/verify200_response.py +87 -0
  210. zrok_api/models/verify_request.py +87 -0
  211. zrok_api/models/version_inventory200_response.py +87 -0
  212. zrok_api/py.typed +0 -0
  213. zrok_api/rest.py +258 -0
zrok/model.py ADDED
@@ -0,0 +1,75 @@
1
+ from dataclasses import dataclass, field
2
+
3
+ BackendMode = str
4
+
5
+ PROXY_BACKEND_MODE: BackendMode = "proxy"
6
+ WEB_BACKEND_MODE: BackendMode = "web"
7
+ TCP_TUNNEL_BACKEND_MODE: BackendMode = "tcpTunnel"
8
+ UDP_TUNNEL_BACKEND_MODE: BackendMode = "udpTunnel"
9
+ CADDY_BACKEND_MODE: BackendMode = "caddy"
10
+ DRIVE_BACKEND_MODE: BackendMode = "drive"
11
+ SOCKS_BACKEND_MODE: BackendMode = "socks"
12
+
13
+ ShareMode = str
14
+
15
+ PRIVATE_SHARE_MODE: ShareMode = "private"
16
+ PUBLIC_SHARE_MODE: ShareMode = "public"
17
+
18
+ PermissionMode = str
19
+
20
+ OPEN_PERMISSION_MODE: PermissionMode = "open"
21
+ CLOSED_PERMISSION_MODE: PermissionMode = "closed"
22
+
23
+
24
+ @dataclass
25
+ class ShareRequest:
26
+ BackendMode: BackendMode
27
+ ShareMode: ShareMode
28
+ Target: str
29
+ Frontends: list[str] = field(default_factory=list[str])
30
+ BasicAuth: list[str] = field(default_factory=list[str])
31
+ OauthProvider: str = ""
32
+ OauthEmailAddressPatterns: list[str] = field(default_factory=list[str])
33
+ OauthAuthorizationCheckInterval: str = ""
34
+ Reserved: bool = False
35
+ UniqueName: str = ""
36
+ PermissionMode: PermissionMode = OPEN_PERMISSION_MODE
37
+ AccessGrants: list[str] = field(default_factory=list[str])
38
+
39
+
40
+ @dataclass
41
+ class Share:
42
+ Token: str
43
+ FrontendEndpoints: list[str]
44
+
45
+
46
+ @dataclass
47
+ class AccessRequest:
48
+ ShareToken: str
49
+
50
+
51
+ @dataclass
52
+ class Access:
53
+ Token: str
54
+ ShareToken: str
55
+ BackendMode: BackendMode
56
+
57
+
58
+ @dataclass
59
+ class SessionMetrics:
60
+ BytesRead: int
61
+ BytesWritten: int
62
+ LastUpdate: int
63
+
64
+
65
+ @dataclass
66
+ class Metrics:
67
+ Namespace: str
68
+ Sessions: dict[str, SessionMetrics]
69
+
70
+
71
+ AuthScheme = str
72
+
73
+ AUTH_SCHEME_NONE: AuthScheme = "none"
74
+ AUTH_SCHEME_BASIC: AuthScheme = "basic"
75
+ AUTH_SCHEME_OAUTH: AuthScheme = "oauth"
zrok/overview.py ADDED
@@ -0,0 +1,77 @@
1
+ import json
2
+ from dataclasses import dataclass, field
3
+ from typing import List
4
+
5
+ import urllib3
6
+ from zrok.environment.root import Root
7
+ from zrok_api.models.environment import Environment
8
+ from zrok_api.models.environment_and_resources import EnvironmentAndResources
9
+ from zrok_api.models.share import Share
10
+
11
+
12
+ @dataclass
13
+ class Overview:
14
+ environments: List[EnvironmentAndResources] = field(default_factory=list)
15
+
16
+ @classmethod
17
+ def create(cls, root: Root) -> 'Overview':
18
+ if not root.IsEnabled():
19
+ raise Exception("environment is not enabled; enable with 'zrok enable' first!")
20
+
21
+ http = urllib3.PoolManager()
22
+ apiEndpoint = root.ApiEndpoint().endpoint
23
+ try:
24
+ response = http.request(
25
+ 'GET',
26
+ apiEndpoint + "/api/v2/overview",
27
+ headers={
28
+ "X-TOKEN": root.env.Token
29
+ })
30
+ except Exception as e:
31
+ raise Exception("unable to get account overview", e)
32
+
33
+ json_data = json.loads(response.data.decode('utf-8'))
34
+ overview = cls()
35
+
36
+ for env_data in json_data.get('environments', []):
37
+ env_dict = env_data['environment']
38
+ # Map the JSON keys to the Environment class parameters
39
+ environment = Environment(
40
+ description=env_dict.get('description'),
41
+ host=env_dict.get('host'),
42
+ address=env_dict.get('address'),
43
+ z_id=env_dict.get('zId'),
44
+ activity=env_dict.get('activity'),
45
+ limited=env_dict.get('limited'),
46
+ created_at=env_dict.get('createdAt'),
47
+ updated_at=env_dict.get('updatedAt')
48
+ )
49
+
50
+ # Create Shares object from share data
51
+ share_list = []
52
+ for share_data in env_data.get('shares', []):
53
+ share = Share(
54
+ share_token=share_data.get('shareToken'),
55
+ z_id=share_data.get('zId'),
56
+ share_mode=share_data.get('shareMode'),
57
+ backend_mode=share_data.get('backendMode'),
58
+ frontend_selection=share_data.get('frontendSelection'),
59
+ frontend_endpoint=share_data.get('frontendEndpoint'),
60
+ backend_proxy_endpoint=share_data.get('backendProxyEndpoint'),
61
+ reserved=share_data.get('reserved'),
62
+ activity=share_data.get('activity'),
63
+ limited=share_data.get('limited'),
64
+ created_at=share_data.get('createdAt'),
65
+ updated_at=share_data.get('updatedAt')
66
+ )
67
+ share_list.append(share)
68
+
69
+ # Create EnvironmentAndResources object
70
+ env_resources = EnvironmentAndResources(
71
+ environment=environment,
72
+ shares=share_list,
73
+ frontends=[]
74
+ )
75
+ overview.environments.append(env_resources)
76
+
77
+ return overview
zrok/proxy.py ADDED
@@ -0,0 +1,203 @@
1
+ """
2
+ Proxy share management functionality for the zrok SDK.
3
+ """
4
+
5
+ import atexit
6
+ import logging
7
+ import urllib.parse
8
+ from dataclasses import dataclass
9
+ from typing import Any, Dict, List, Optional
10
+
11
+ import requests
12
+ from flask import Flask, Response, request
13
+ from waitress import serve
14
+ from zrok.environment.root import Root
15
+ from zrok.model import (PROXY_BACKEND_MODE, PUBLIC_SHARE_MODE, PRIVATE_SHARE_MODE, Share,
16
+ ShareRequest)
17
+ from zrok.overview import Overview
18
+ from zrok.share import CreateShare, ReleaseReservedShare
19
+
20
+ import zrok
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ # List of hop-by-hop headers that should not be returned to the viewer
25
+ HOP_BY_HOP_HEADERS = {
26
+ 'connection',
27
+ 'keep-alive',
28
+ 'proxy-authenticate',
29
+ 'proxy-authorization',
30
+ 'te',
31
+ 'trailers',
32
+ 'transfer-encoding',
33
+ 'upgrade'
34
+ }
35
+
36
+ # The proxy only listens on the zrok socket, the port used to initialize the Waitress server is not actually bound or
37
+ # listening
38
+ DUMMY_PORT = 18081
39
+
40
+
41
+ @dataclass
42
+ class ProxyShare:
43
+ """Represents a proxy share with its configuration and state."""
44
+ root: Root
45
+ share: Share
46
+ target: str
47
+ unique_name: Optional[str] = None
48
+ _cleanup_registered: bool = False
49
+ _app: Optional[Flask] = None
50
+ verify_ssl: bool = True
51
+
52
+ @classmethod
53
+ def create(cls, root: Root, target: str, share_mode: str = PUBLIC_SHARE_MODE, unique_name: Optional[str] = None,
54
+ frontends: Optional[List[str]] = None, verify_ssl: bool = True) -> 'ProxyShare':
55
+ """
56
+ Create a new proxy share, handling reservation and cleanup logic based on unique_name.
57
+
58
+ Args:
59
+ root: The zrok root environment
60
+ target: Target URL or service to proxy to
61
+ unique_name: Optional unique name for a reserved share
62
+ frontends: Optional list of frontends to use, takes precedence over root's default_frontend
63
+ verify_ssl: Whether to verify SSL certificates when forwarding requests.
64
+
65
+ Returns:
66
+ ProxyShare instance configured with the created share
67
+ """
68
+ # First check if we have an existing reserved share with this name
69
+ if unique_name:
70
+ existing_share = cls._find_existing_share(root, unique_name)
71
+ if existing_share:
72
+ logger.debug(f"Found existing share with token: {existing_share.Token}")
73
+ return cls(
74
+ root=root,
75
+ share=existing_share,
76
+ target=target,
77
+ unique_name=unique_name,
78
+ verify_ssl=verify_ssl
79
+ )
80
+
81
+ # Compose the share request
82
+ share_frontends = []
83
+ if share_mode == PUBLIC_SHARE_MODE:
84
+ if frontends:
85
+ share_frontends = frontends
86
+ elif root.cfg and root.cfg.DefaultFrontend:
87
+ share_frontends = [root.cfg.DefaultFrontend]
88
+ else:
89
+ share_frontends = ['public']
90
+
91
+ share_request = ShareRequest(
92
+ BackendMode=PROXY_BACKEND_MODE,
93
+ ShareMode=share_mode,
94
+ Target=target,
95
+ Frontends=share_frontends,
96
+ Reserved=True
97
+ )
98
+ if unique_name:
99
+ share_request.UniqueName = unique_name
100
+
101
+ # Create the share
102
+ share = CreateShare(root=root, request=share_request)
103
+ if share_mode == PUBLIC_SHARE_MODE:
104
+ logger.debug(f"Created new proxy share with endpoints: {', '.join(share.FrontendEndpoints)}")
105
+ elif share_mode == PRIVATE_SHARE_MODE:
106
+ logger.debug(f"Created new private share with token: {share.Token}")
107
+
108
+ # Create class instance and setup cleanup-at-exit if we reserved a random share token
109
+ instance = cls(
110
+ root=root,
111
+ share=share,
112
+ target=target,
113
+ unique_name=unique_name,
114
+ verify_ssl=verify_ssl
115
+ )
116
+ if not unique_name:
117
+ instance.register_cleanup()
118
+ return instance
119
+
120
+ @staticmethod
121
+ def _find_existing_share(root: Root, unique_name: str) -> Optional[Share]:
122
+ """Find an existing share with the given unique name."""
123
+ overview = Overview.create(root=root)
124
+ for env in overview.environments:
125
+ if env.environment.z_id == root.env.ZitiIdentity:
126
+ for share in env.shares:
127
+ if share.share_token == unique_name:
128
+ return Share(Token=share.share_token, FrontendEndpoints=[share.frontend_endpoint])
129
+ return None
130
+
131
+ def register_cleanup(self):
132
+ """Register cleanup handler to release randomly generated shares on exit."""
133
+ if not self._cleanup_registered:
134
+ def cleanup():
135
+ try:
136
+ ReleaseReservedShare(root=self.root, shr=self.share)
137
+ logger.info(f"Share {self.share.Token} released")
138
+ except Exception as e:
139
+ logger.error(f"Error during cleanup: {e}")
140
+
141
+ # Register for normal exit only
142
+ atexit.register(cleanup)
143
+ self._cleanup_registered = True
144
+ return cleanup # Return the cleanup function for reuse
145
+
146
+ def _create_app(self) -> Flask:
147
+ """Create and configure the Flask app for proxying."""
148
+ app = Flask(__name__)
149
+
150
+ @app.route('/', defaults={'path': ''}, methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'])
151
+ @app.route('/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'])
152
+ def proxy(path):
153
+ # Construct the target URL
154
+ url = urllib.parse.urljoin(self.target, path)
155
+
156
+ # Forward the request
157
+ resp = requests.request(
158
+ method=request.method,
159
+ url=url,
160
+ headers={key: value for (key, value) in request.headers
161
+ if key.lower() not in HOP_BY_HOP_HEADERS},
162
+ data=request.get_data(),
163
+ cookies=request.cookies,
164
+ allow_redirects=False,
165
+ stream=True,
166
+ verify=self.verify_ssl
167
+ )
168
+
169
+ # Create the response
170
+ excluded_headers = HOP_BY_HOP_HEADERS.union({'host'})
171
+ headers = [(name, value) for (name, value) in resp.raw.headers.items()
172
+ if name.lower() not in excluded_headers]
173
+
174
+ return Response(
175
+ resp.iter_content(chunk_size=10*1024),
176
+ status=resp.status_code,
177
+ headers=headers
178
+ )
179
+ return app
180
+
181
+ def run(self):
182
+ """Run the proxy server."""
183
+ if not self._app:
184
+ self._app = self._create_app()
185
+
186
+ zrok_opts: Dict[str, Any] = {}
187
+ zrok_opts['cfg'] = zrok.decor.Opts(root=self.root, shrToken=self.token, bindPort=DUMMY_PORT)
188
+
189
+ @zrok.decor.zrok(opts=zrok_opts)
190
+ def run_server():
191
+ serve(self._app, port=DUMMY_PORT, _quiet=True)
192
+
193
+ run_server()
194
+
195
+ @property
196
+ def endpoints(self) -> List[str]:
197
+ """Get the frontend endpoints for this share."""
198
+ return self.share.FrontendEndpoints
199
+
200
+ @property
201
+ def token(self) -> str:
202
+ """Get the share token."""
203
+ return self.share.Token
zrok/share.py ADDED
@@ -0,0 +1,199 @@
1
+ from zrok_api.api import ShareApi
2
+ from zrok.environment.root import Root
3
+ from zrok_api.models.auth_user import AuthUser
4
+ from zrok_api.models.share_request import ShareRequest
5
+ from zrok_api.models.unshare_request import UnshareRequest
6
+ import json
7
+ from zrok_api.exceptions import ApiException
8
+ import zrok.model as model
9
+
10
+
11
+ class Share():
12
+ root: Root
13
+ request: model.ShareRequest
14
+ share: model.Share
15
+
16
+ def __init__(self, root: Root, request: model.ShareRequest):
17
+ self.root = root
18
+ self.request = request
19
+
20
+ def __enter__(self) -> model.Share:
21
+ self.share = CreateShare(root=self.root, request=self.request)
22
+ return self.share
23
+
24
+ def __exit__(self, exception_type, exception_value, exception_traceback):
25
+ if not self.request.Reserved:
26
+ DeleteShare(root=self.root, shr=self.share)
27
+
28
+
29
+ def CreateShare(root: Root, request: model.ShareRequest) -> model.Share:
30
+ if not root.IsEnabled():
31
+ raise Exception("environment is not enabled; enable with 'zrok enable' first!")
32
+
33
+ match request.ShareMode:
34
+ case model.PRIVATE_SHARE_MODE:
35
+ out = __newPrivateShare(root, request)
36
+ case model.PUBLIC_SHARE_MODE:
37
+ out = __newPublicShare(root, request)
38
+ case _:
39
+ raise Exception("unknown share mode " + request.ShareMode)
40
+ out.reserved = request.Reserved
41
+ if request.Reserved:
42
+ out.unique_name = request.UniqueName
43
+
44
+ if len(request.BasicAuth) > 0:
45
+ out.auth_scheme = model.AUTH_SCHEME_BASIC
46
+ for pair in request.BasicAuth:
47
+ tokens = pair.split(":")
48
+ if len(tokens) == 2:
49
+ out.auth_users.append(AuthUser(username=tokens[0].strip(), password=tokens[1].strip()))
50
+ else:
51
+ raise Exception("invalid username:password pair: " + pair)
52
+
53
+ if request.OauthProvider != "":
54
+ out.auth_scheme = model.AUTH_SCHEME_OAUTH
55
+
56
+ try:
57
+ zrok = root.Client()
58
+ except Exception as e:
59
+ raise Exception("error getting zrok client", e)
60
+
61
+ try:
62
+ # Use share_with_http_info to get access to the HTTP info and handle custom response format
63
+ share_api = ShareApi(zrok)
64
+ # Add Accept header to handle the custom content type
65
+ custom_headers = {
66
+ 'Accept': 'application/json, application/zrok.v1+json'
67
+ }
68
+
69
+ response_data = share_api.share_with_http_info(
70
+ body=out,
71
+ _headers=custom_headers
72
+ )
73
+
74
+ # Parse response
75
+ if hasattr(response_data, 'data') and response_data.data is not None:
76
+ res = response_data.data
77
+ else:
78
+ raise Exception("invalid response from server")
79
+
80
+ except ApiException as e:
81
+ # If it's a content type error, try to parse the raw JSON
82
+ if "Unsupported content type: application/zrok.v1+json" in str(e) and hasattr(e, 'body'):
83
+ try:
84
+ # Parse the response body directly
85
+ res_dict = json.loads(e.body)
86
+ # Create a response object with the expected fields
87
+
88
+ class ShareResponse:
89
+ def __init__(self, share_token, frontend_proxy_endpoints):
90
+ self.share_token = share_token
91
+ self.frontend_proxy_endpoints = frontend_proxy_endpoints
92
+
93
+ res = ShareResponse(
94
+ share_token=res_dict.get('shareToken', ''),
95
+ frontend_proxy_endpoints=res_dict.get('frontendProxyEndpoints', [])
96
+ )
97
+ except (json.JSONDecodeError, ValueError, AttributeError) as json_err:
98
+ raise Exception(f"unable to parse API response: {str(json_err)}") from e
99
+ else:
100
+ raise Exception("unable to create share", e)
101
+ except Exception as e:
102
+ raise Exception("unable to create share", e)
103
+
104
+ return model.Share(Token=res.share_token,
105
+ FrontendEndpoints=res.frontend_proxy_endpoints)
106
+
107
+
108
+ def __newPrivateShare(root: Root, request: model.ShareRequest) -> ShareRequest:
109
+ return ShareRequest(env_zid=root.env.ZitiIdentity,
110
+ share_mode=request.ShareMode,
111
+ backend_mode=request.BackendMode,
112
+ backend_proxy_endpoint=request.Target,
113
+ auth_scheme=model.AUTH_SCHEME_NONE,
114
+ permission_mode=request.PermissionMode,
115
+ access_grants=request.AccessGrants
116
+ )
117
+
118
+
119
+ def __newPublicShare(root: Root, request: model.ShareRequest) -> ShareRequest:
120
+ ret = ShareRequest(env_zid=root.env.ZitiIdentity,
121
+ share_mode=request.ShareMode,
122
+ frontend_selection=request.Frontends,
123
+ backend_mode=request.BackendMode,
124
+ backend_proxy_endpoint=request.Target,
125
+ auth_scheme=model.AUTH_SCHEME_NONE,
126
+ oauth_email_domains=request.OauthEmailAddressPatterns,
127
+ oauth_authorization_check_interval=request.OauthAuthorizationCheckInterval,
128
+ permission_mode=request.PermissionMode,
129
+ access_grants=request.AccessGrants
130
+ )
131
+ if request.OauthProvider != "":
132
+ ret.oauth_provider = request.OauthProvider
133
+
134
+ return ret
135
+
136
+
137
+ def DeleteShare(root: Root, shr: model.Share):
138
+ req = UnshareRequest(env_zid=root.env.ZitiIdentity,
139
+ share_token=shr.Token)
140
+
141
+ try:
142
+ zrok = root.Client()
143
+ except Exception as e:
144
+ raise Exception("error getting zrok client", e)
145
+
146
+ try:
147
+ # Add Accept header to handle the custom content type
148
+ share_api = ShareApi(zrok)
149
+ custom_headers = {
150
+ 'Accept': 'application/json, application/zrok.v1+json'
151
+ }
152
+
153
+ # Use unshare_with_http_info to get access to the HTTP info
154
+ share_api.unshare_with_http_info(
155
+ body=req,
156
+ _headers=custom_headers
157
+ )
158
+ except ApiException as e:
159
+ # If it's a content type error but the operation was likely successful, don't propagate the error
160
+ if "Unsupported content type: application/zrok.v1+json" in str(e) and (200 <= e.status <= 299):
161
+ # The operation was likely successful despite the content type error
162
+ pass
163
+ else:
164
+ raise Exception("error deleting share", e)
165
+ except Exception as e:
166
+ raise Exception("error deleting share", e)
167
+
168
+
169
+ def ReleaseReservedShare(root: Root, shr: model.Share):
170
+ req = UnshareRequest(env_zid=root.env.ZitiIdentity,
171
+ share_token=shr.Token,
172
+ reserved=True)
173
+
174
+ try:
175
+ zrok = root.Client()
176
+ except Exception as e:
177
+ raise Exception("error getting zrok client", e)
178
+
179
+ try:
180
+ # Add Accept header to handle the custom content type
181
+ share_api = ShareApi(zrok)
182
+ custom_headers = {
183
+ 'Accept': 'application/json, application/zrok.v1+json'
184
+ }
185
+
186
+ # Use unshare_with_http_info to get access to the HTTP info
187
+ share_api.unshare_with_http_info(
188
+ body=req,
189
+ _headers=custom_headers
190
+ )
191
+ except ApiException as e:
192
+ # If it's a content type error but the operation was likely successful, don't propagate the error
193
+ if "Unsupported content type: application/zrok.v1+json" in str(e) and (200 <= e.status <= 299):
194
+ # The operation was likely successful despite the content type error
195
+ pass
196
+ else:
197
+ raise Exception("error releasing share", e)
198
+ except Exception as e:
199
+ raise Exception("error releasing share", e)
@@ -0,0 +1,24 @@
1
+ Metadata-Version: 2.4
2
+ Name: zrok2-build-debugging
3
+ Version: 22.0.2000008
4
+ Summary: zrok2
5
+ Home-page: https://github.com/openziti/zrok
6
+ Author: NetFoundry
7
+ Author-email: developers@openziti.org
8
+ License: Apache 2.0
9
+ Project-URL: Source, https://github.com/openziti/zrok
10
+ Project-URL: Tracker, https://github.com/openziti/zrok/issues
11
+ Project-URL: Discussion, https://openziti.discourse.group/
12
+ Keywords: zrok,zrok2
13
+ Requires-Python: >3.10.0
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: openziti>=1.0.0
16
+ Requires-Dist: urllib3>=2.1.0
17
+ Dynamic: description
18
+ Dynamic: keywords
19
+ Dynamic: requires-dist
20
+ Dynamic: requires-python
21
+ Dynamic: summary
22
+
23
+ Geo-scale, next-generation peer-to-peer sharing platform built on top of OpenZiti.
24
+