eryph-compute 0.1.1

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 (222) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +47 -0
  3. data/LICENSE +21 -0
  4. data/README.md +144 -0
  5. data/eryph-clientruntime.gemspec +42 -0
  6. data/eryph-compute.gemspec +44 -0
  7. data/lib/eryph/clientruntime/client_credentials_lookup.rb +295 -0
  8. data/lib/eryph/clientruntime/config_store.rb +206 -0
  9. data/lib/eryph/clientruntime/config_stores_reader.rb +211 -0
  10. data/lib/eryph/clientruntime/endpoint_lookup.rb +226 -0
  11. data/lib/eryph/clientruntime/environment.rb +440 -0
  12. data/lib/eryph/clientruntime/local_identity_provider_info.rb +153 -0
  13. data/lib/eryph/clientruntime/token_provider.rb +275 -0
  14. data/lib/eryph/clientruntime/version.rb +6 -0
  15. data/lib/eryph/clientruntime.rb +79 -0
  16. data/lib/eryph/compute/client.rb +442 -0
  17. data/lib/eryph/compute/generated/Gemfile +9 -0
  18. data/lib/eryph/compute/generated/README.md +206 -0
  19. data/lib/eryph/compute/generated/Rakefile +10 -0
  20. data/lib/eryph/compute/generated/compute_client.gemspec +41 -0
  21. data/lib/eryph/compute/generated/docs/ApiVersion.md +20 -0
  22. data/lib/eryph/compute/generated/docs/ApiVersionResponse.md +18 -0
  23. data/lib/eryph/compute/generated/docs/Catlet.md +32 -0
  24. data/lib/eryph/compute/generated/docs/CatletConfigOperationResult.md +18 -0
  25. data/lib/eryph/compute/generated/docs/CatletConfigValidationResult.md +20 -0
  26. data/lib/eryph/compute/generated/docs/CatletConfiguration.md +18 -0
  27. data/lib/eryph/compute/generated/docs/CatletDrive.md +20 -0
  28. data/lib/eryph/compute/generated/docs/CatletDriveType.md +15 -0
  29. data/lib/eryph/compute/generated/docs/CatletList.md +18 -0
  30. data/lib/eryph/compute/generated/docs/CatletNetwork.md +30 -0
  31. data/lib/eryph/compute/generated/docs/CatletNetworkAdapter.md +20 -0
  32. data/lib/eryph/compute/generated/docs/CatletStatus.md +15 -0
  33. data/lib/eryph/compute/generated/docs/CatletStopMode.md +15 -0
  34. data/lib/eryph/compute/generated/docs/CatletsApi.md +863 -0
  35. data/lib/eryph/compute/generated/docs/DiskStatus.md +15 -0
  36. data/lib/eryph/compute/generated/docs/ExpandCatletConfigRequestBody.md +22 -0
  37. data/lib/eryph/compute/generated/docs/ExpandNewCatletConfigRequest.md +22 -0
  38. data/lib/eryph/compute/generated/docs/FloatingNetworkPort.md +26 -0
  39. data/lib/eryph/compute/generated/docs/Gene.md +30 -0
  40. data/lib/eryph/compute/generated/docs/GeneList.md +18 -0
  41. data/lib/eryph/compute/generated/docs/GeneType.md +15 -0
  42. data/lib/eryph/compute/generated/docs/GeneWithUsage.md +34 -0
  43. data/lib/eryph/compute/generated/docs/GenesApi.md +281 -0
  44. data/lib/eryph/compute/generated/docs/NewCatletRequest.md +20 -0
  45. data/lib/eryph/compute/generated/docs/NewProjectMemberBody.md +22 -0
  46. data/lib/eryph/compute/generated/docs/NewProjectRequest.md +20 -0
  47. data/lib/eryph/compute/generated/docs/NewVirtualDiskRequest.md +30 -0
  48. data/lib/eryph/compute/generated/docs/Operation.md +32 -0
  49. data/lib/eryph/compute/generated/docs/OperationList.md +18 -0
  50. data/lib/eryph/compute/generated/docs/OperationLogEntry.md +24 -0
  51. data/lib/eryph/compute/generated/docs/OperationResource.md +22 -0
  52. data/lib/eryph/compute/generated/docs/OperationResult.md +18 -0
  53. data/lib/eryph/compute/generated/docs/OperationStatus.md +15 -0
  54. data/lib/eryph/compute/generated/docs/OperationTask.md +30 -0
  55. data/lib/eryph/compute/generated/docs/OperationTaskReference.md +22 -0
  56. data/lib/eryph/compute/generated/docs/OperationTaskStatus.md +15 -0
  57. data/lib/eryph/compute/generated/docs/OperationsApi.md +157 -0
  58. data/lib/eryph/compute/generated/docs/PopulateCatletConfigVariablesRequest.md +20 -0
  59. data/lib/eryph/compute/generated/docs/ProblemDetails.md +26 -0
  60. data/lib/eryph/compute/generated/docs/Project.md +22 -0
  61. data/lib/eryph/compute/generated/docs/ProjectList.md +18 -0
  62. data/lib/eryph/compute/generated/docs/ProjectMemberRole.md +26 -0
  63. data/lib/eryph/compute/generated/docs/ProjectMemberRoleList.md +18 -0
  64. data/lib/eryph/compute/generated/docs/ProjectMembersApi.md +293 -0
  65. data/lib/eryph/compute/generated/docs/ProjectsApi.md +286 -0
  66. data/lib/eryph/compute/generated/docs/ResourceType.md +15 -0
  67. data/lib/eryph/compute/generated/docs/StopCatletRequestBody.md +18 -0
  68. data/lib/eryph/compute/generated/docs/TaskReferenceType.md +15 -0
  69. data/lib/eryph/compute/generated/docs/UpdateCatletRequestBody.md +20 -0
  70. data/lib/eryph/compute/generated/docs/UpdateProjectNetworksRequestBody.md +20 -0
  71. data/lib/eryph/compute/generated/docs/ValidateConfigRequest.md +18 -0
  72. data/lib/eryph/compute/generated/docs/ValidationIssue.md +20 -0
  73. data/lib/eryph/compute/generated/docs/VersionApi.md +69 -0
  74. data/lib/eryph/compute/generated/docs/VirtualDisk.md +42 -0
  75. data/lib/eryph/compute/generated/docs/VirtualDiskAttachedCatlet.md +20 -0
  76. data/lib/eryph/compute/generated/docs/VirtualDiskGeneInfo.md +22 -0
  77. data/lib/eryph/compute/generated/docs/VirtualDiskList.md +18 -0
  78. data/lib/eryph/compute/generated/docs/VirtualDisksApi.md +291 -0
  79. data/lib/eryph/compute/generated/docs/VirtualNetwork.md +28 -0
  80. data/lib/eryph/compute/generated/docs/VirtualNetworkConfiguration.md +18 -0
  81. data/lib/eryph/compute/generated/docs/VirtualNetworkList.md +18 -0
  82. data/lib/eryph/compute/generated/docs/VirtualNetworksApi.md +291 -0
  83. data/lib/eryph/compute/generated/git_push.sh +57 -0
  84. data/lib/eryph/compute/generated/lib/compute_client/api/catlets_api.rb +812 -0
  85. data/lib/eryph/compute/generated/lib/compute_client/api/genes_api.rb +262 -0
  86. data/lib/eryph/compute/generated/lib/compute_client/api/operations_api.rb +154 -0
  87. data/lib/eryph/compute/generated/lib/compute_client/api/project_members_api.rb +297 -0
  88. data/lib/eryph/compute/generated/lib/compute_client/api/projects_api.rb +269 -0
  89. data/lib/eryph/compute/generated/lib/compute_client/api/version_api.rb +79 -0
  90. data/lib/eryph/compute/generated/lib/compute_client/api/virtual_disks_api.rb +272 -0
  91. data/lib/eryph/compute/generated/lib/compute_client/api/virtual_networks_api.rb +282 -0
  92. data/lib/eryph/compute/generated/lib/compute_client/api_client.rb +437 -0
  93. data/lib/eryph/compute/generated/lib/compute_client/api_error.rb +58 -0
  94. data/lib/eryph/compute/generated/lib/compute_client/configuration.rb +392 -0
  95. data/lib/eryph/compute/generated/lib/compute_client/models/api_version.rb +263 -0
  96. data/lib/eryph/compute/generated/lib/compute_client/models/api_version_response.rb +237 -0
  97. data/lib/eryph/compute/generated/lib/compute_client/models/catlet.rb +400 -0
  98. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_config_operation_result.rb +234 -0
  99. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_config_validation_result.rb +251 -0
  100. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_configuration.rb +223 -0
  101. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_drive.rb +270 -0
  102. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_drive_type.rb +43 -0
  103. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_list.rb +239 -0
  104. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_network.rb +318 -0
  105. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_network_adapter.rb +247 -0
  106. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_status.rb +43 -0
  107. data/lib/eryph/compute/generated/lib/compute_client/models/catlet_stop_mode.rb +41 -0
  108. data/lib/eryph/compute/generated/lib/compute_client/models/disk_status.rb +40 -0
  109. data/lib/eryph/compute/generated/lib/compute_client/models/expand_catlet_config_request_body.rb +243 -0
  110. data/lib/eryph/compute/generated/lib/compute_client/models/expand_new_catlet_config_request.rb +243 -0
  111. data/lib/eryph/compute/generated/lib/compute_client/models/floating_network_port.rb +313 -0
  112. data/lib/eryph/compute/generated/lib/compute_client/models/gene.rb +415 -0
  113. data/lib/eryph/compute/generated/lib/compute_client/models/gene_list.rb +239 -0
  114. data/lib/eryph/compute/generated/lib/compute_client/models/gene_type.rb +41 -0
  115. data/lib/eryph/compute/generated/lib/compute_client/models/gene_with_usage.rb +439 -0
  116. data/lib/eryph/compute/generated/lib/compute_client/models/new_catlet_request.rb +233 -0
  117. data/lib/eryph/compute/generated/lib/compute_client/models/new_project_member_body.rb +273 -0
  118. data/lib/eryph/compute/generated/lib/compute_client/models/new_project_request.rb +247 -0
  119. data/lib/eryph/compute/generated/lib/compute_client/models/new_virtual_disk_request.rb +345 -0
  120. data/lib/eryph/compute/generated/lib/compute_client/models/operation.rb +352 -0
  121. data/lib/eryph/compute/generated/lib/compute_client/models/operation_list.rb +239 -0
  122. data/lib/eryph/compute/generated/lib/compute_client/models/operation_log_entry.rb +299 -0
  123. data/lib/eryph/compute/generated/lib/compute_client/models/operation_resource.rb +311 -0
  124. data/lib/eryph/compute/generated/lib/compute_client/models/operation_result.rb +242 -0
  125. data/lib/eryph/compute/generated/lib/compute_client/models/operation_status.rb +42 -0
  126. data/lib/eryph/compute/generated/lib/compute_client/models/operation_task.rb +366 -0
  127. data/lib/eryph/compute/generated/lib/compute_client/models/operation_task_reference.rb +311 -0
  128. data/lib/eryph/compute/generated/lib/compute_client/models/operation_task_status.rb +42 -0
  129. data/lib/eryph/compute/generated/lib/compute_client/models/populate_catlet_config_variables_request.rb +233 -0
  130. data/lib/eryph/compute/generated/lib/compute_client/models/problem_details.rb +261 -0
  131. data/lib/eryph/compute/generated/lib/compute_client/models/project.rb +289 -0
  132. data/lib/eryph/compute/generated/lib/compute_client/models/project_list.rb +239 -0
  133. data/lib/eryph/compute/generated/lib/compute_client/models/project_member_role.rb +341 -0
  134. data/lib/eryph/compute/generated/lib/compute_client/models/project_member_role_list.rb +239 -0
  135. data/lib/eryph/compute/generated/lib/compute_client/models/resource_type.rb +42 -0
  136. data/lib/eryph/compute/generated/lib/compute_client/models/stop_catlet_request_body.rb +259 -0
  137. data/lib/eryph/compute/generated/lib/compute_client/models/task_reference_type.rb +40 -0
  138. data/lib/eryph/compute/generated/lib/compute_client/models/update_catlet_request_body.rb +233 -0
  139. data/lib/eryph/compute/generated/lib/compute_client/models/update_project_networks_request_body.rb +233 -0
  140. data/lib/eryph/compute/generated/lib/compute_client/models/validate_config_request.rb +223 -0
  141. data/lib/eryph/compute/generated/lib/compute_client/models/validation_issue.rb +249 -0
  142. data/lib/eryph/compute/generated/lib/compute_client/models/virtual_disk.rb +479 -0
  143. data/lib/eryph/compute/generated/lib/compute_client/models/virtual_disk_attached_catlet.rb +285 -0
  144. data/lib/eryph/compute/generated/lib/compute_client/models/virtual_disk_gene_info.rb +289 -0
  145. data/lib/eryph/compute/generated/lib/compute_client/models/virtual_disk_list.rb +239 -0
  146. data/lib/eryph/compute/generated/lib/compute_client/models/virtual_network.rb +351 -0
  147. data/lib/eryph/compute/generated/lib/compute_client/models/virtual_network_configuration.rb +223 -0
  148. data/lib/eryph/compute/generated/lib/compute_client/models/virtual_network_list.rb +239 -0
  149. data/lib/eryph/compute/generated/lib/compute_client/version.rb +15 -0
  150. data/lib/eryph/compute/generated/lib/compute_client.rb +101 -0
  151. data/lib/eryph/compute/generated/spec/api/catlets_api_spec.rb +182 -0
  152. data/lib/eryph/compute/generated/spec/api/genes_api_spec.rb +81 -0
  153. data/lib/eryph/compute/generated/spec/api/operations_api_spec.rb +62 -0
  154. data/lib/eryph/compute/generated/spec/api/project_members_api_spec.rb +86 -0
  155. data/lib/eryph/compute/generated/spec/api/projects_api_spec.rb +82 -0
  156. data/lib/eryph/compute/generated/spec/api/version_api_spec.rb +46 -0
  157. data/lib/eryph/compute/generated/spec/api/virtual_disks_api_spec.rb +83 -0
  158. data/lib/eryph/compute/generated/spec/api/virtual_networks_api_spec.rb +84 -0
  159. data/lib/eryph/compute/generated/spec/models/api_version_response_spec.rb +36 -0
  160. data/lib/eryph/compute/generated/spec/models/api_version_spec.rb +42 -0
  161. data/lib/eryph/compute/generated/spec/models/catlet_config_operation_result_spec.rb +36 -0
  162. data/lib/eryph/compute/generated/spec/models/catlet_config_validation_result_spec.rb +42 -0
  163. data/lib/eryph/compute/generated/spec/models/catlet_configuration_spec.rb +36 -0
  164. data/lib/eryph/compute/generated/spec/models/catlet_drive_spec.rb +42 -0
  165. data/lib/eryph/compute/generated/spec/models/catlet_drive_type_spec.rb +30 -0
  166. data/lib/eryph/compute/generated/spec/models/catlet_list_spec.rb +36 -0
  167. data/lib/eryph/compute/generated/spec/models/catlet_network_adapter_spec.rb +42 -0
  168. data/lib/eryph/compute/generated/spec/models/catlet_network_spec.rb +72 -0
  169. data/lib/eryph/compute/generated/spec/models/catlet_spec.rb +78 -0
  170. data/lib/eryph/compute/generated/spec/models/catlet_status_spec.rb +30 -0
  171. data/lib/eryph/compute/generated/spec/models/catlet_stop_mode_spec.rb +30 -0
  172. data/lib/eryph/compute/generated/spec/models/disk_status_spec.rb +30 -0
  173. data/lib/eryph/compute/generated/spec/models/expand_catlet_config_request_body_spec.rb +48 -0
  174. data/lib/eryph/compute/generated/spec/models/expand_new_catlet_config_request_spec.rb +48 -0
  175. data/lib/eryph/compute/generated/spec/models/floating_network_port_spec.rb +60 -0
  176. data/lib/eryph/compute/generated/spec/models/gene_list_spec.rb +36 -0
  177. data/lib/eryph/compute/generated/spec/models/gene_spec.rb +72 -0
  178. data/lib/eryph/compute/generated/spec/models/gene_type_spec.rb +30 -0
  179. data/lib/eryph/compute/generated/spec/models/gene_with_usage_spec.rb +84 -0
  180. data/lib/eryph/compute/generated/spec/models/new_catlet_request_spec.rb +42 -0
  181. data/lib/eryph/compute/generated/spec/models/new_project_member_body_spec.rb +48 -0
  182. data/lib/eryph/compute/generated/spec/models/new_project_request_spec.rb +42 -0
  183. data/lib/eryph/compute/generated/spec/models/new_virtual_disk_request_spec.rb +72 -0
  184. data/lib/eryph/compute/generated/spec/models/operation_list_spec.rb +36 -0
  185. data/lib/eryph/compute/generated/spec/models/operation_log_entry_spec.rb +54 -0
  186. data/lib/eryph/compute/generated/spec/models/operation_resource_spec.rb +48 -0
  187. data/lib/eryph/compute/generated/spec/models/operation_result_spec.rb +36 -0
  188. data/lib/eryph/compute/generated/spec/models/operation_spec.rb +78 -0
  189. data/lib/eryph/compute/generated/spec/models/operation_status_spec.rb +30 -0
  190. data/lib/eryph/compute/generated/spec/models/operation_task_reference_spec.rb +48 -0
  191. data/lib/eryph/compute/generated/spec/models/operation_task_spec.rb +72 -0
  192. data/lib/eryph/compute/generated/spec/models/operation_task_status_spec.rb +30 -0
  193. data/lib/eryph/compute/generated/spec/models/populate_catlet_config_variables_request_spec.rb +42 -0
  194. data/lib/eryph/compute/generated/spec/models/problem_details_spec.rb +60 -0
  195. data/lib/eryph/compute/generated/spec/models/project_list_spec.rb +36 -0
  196. data/lib/eryph/compute/generated/spec/models/project_member_role_list_spec.rb +36 -0
  197. data/lib/eryph/compute/generated/spec/models/project_member_role_spec.rb +60 -0
  198. data/lib/eryph/compute/generated/spec/models/project_spec.rb +48 -0
  199. data/lib/eryph/compute/generated/spec/models/resource_type_spec.rb +30 -0
  200. data/lib/eryph/compute/generated/spec/models/stop_catlet_request_body_spec.rb +36 -0
  201. data/lib/eryph/compute/generated/spec/models/task_reference_type_spec.rb +30 -0
  202. data/lib/eryph/compute/generated/spec/models/update_catlet_request_body_spec.rb +42 -0
  203. data/lib/eryph/compute/generated/spec/models/update_project_networks_request_body_spec.rb +42 -0
  204. data/lib/eryph/compute/generated/spec/models/validate_config_request_spec.rb +36 -0
  205. data/lib/eryph/compute/generated/spec/models/validation_issue_spec.rb +42 -0
  206. data/lib/eryph/compute/generated/spec/models/virtual_disk_attached_catlet_spec.rb +42 -0
  207. data/lib/eryph/compute/generated/spec/models/virtual_disk_gene_info_spec.rb +48 -0
  208. data/lib/eryph/compute/generated/spec/models/virtual_disk_list_spec.rb +36 -0
  209. data/lib/eryph/compute/generated/spec/models/virtual_disk_spec.rb +108 -0
  210. data/lib/eryph/compute/generated/spec/models/virtual_network_configuration_spec.rb +36 -0
  211. data/lib/eryph/compute/generated/spec/models/virtual_network_list_spec.rb +36 -0
  212. data/lib/eryph/compute/generated/spec/models/virtual_network_spec.rb +66 -0
  213. data/lib/eryph/compute/generated/spec/spec_helper.rb +111 -0
  214. data/lib/eryph/compute/generated.rb +137 -0
  215. data/lib/eryph/compute/operation_result.rb +255 -0
  216. data/lib/eryph/compute/operation_tracker.rb +247 -0
  217. data/lib/eryph/compute/problem_details_error.rb +139 -0
  218. data/lib/eryph/compute/version.rb +6 -0
  219. data/lib/eryph/compute.rb +40 -0
  220. data/lib/eryph/version.rb +4 -0
  221. data/lib/eryph.rb +68 -0
  222. metadata +329 -0
@@ -0,0 +1,275 @@
1
+ require 'jwt'
2
+ require 'securerandom'
3
+ require 'time'
4
+ require 'uri'
5
+ require 'json'
6
+ require 'faraday'
7
+
8
+ module Eryph
9
+ module ClientRuntime
10
+ # Token response structure
11
+ class TokenResponse
12
+ # @return [String] the access token
13
+ attr_reader :access_token
14
+
15
+ # @return [String] the token type (usually "Bearer")
16
+ attr_reader :token_type
17
+
18
+ # @return [Integer] expires in seconds
19
+ attr_reader :expires_in
20
+
21
+ # @return [Array<String>] granted scopes
22
+ attr_reader :scopes
23
+
24
+ # @return [Time] when this token was issued
25
+ attr_reader :issued_at
26
+
27
+ # Initialize token response
28
+ # @param access_token [String] access token
29
+ # @param token_type [String] token type
30
+ # @param expires_in [Integer] expires in seconds
31
+ # @param scopes [Array<String>] granted scopes
32
+ def initialize(access_token:, expires_in:, token_type: 'Bearer', scopes: [])
33
+ @access_token = access_token
34
+ @token_type = token_type
35
+ @expires_in = expires_in.to_i
36
+ @scopes = scopes
37
+ @issued_at = Time.now
38
+ end
39
+
40
+ # Check if the token is expired or will expire soon
41
+ # @param buffer_seconds [Integer] buffer time in seconds before considering expired
42
+ # @return [Boolean] true if token is expired or will expire soon
43
+ def expired?(buffer_seconds = 300)
44
+ return true if @expires_in <= 0
45
+
46
+ expires_at = @issued_at + @expires_in
47
+ Time.now >= (expires_at - buffer_seconds)
48
+ end
49
+
50
+ # Get the authorization header value
51
+ # @return [String] authorization header value
52
+ def authorization_header
53
+ "#{@token_type} #{@access_token}"
54
+ end
55
+ end
56
+
57
+ # Provides OAuth2 access tokens using client credentials flow with JWT authentication
58
+ # Follows the same pattern as the .NET TokenProvider
59
+ class TokenProvider
60
+ # @return [ClientCredentials] the client credentials
61
+ attr_reader :credentials
62
+
63
+ # @return [Array<String>] OAuth2 scopes
64
+ attr_reader :scopes
65
+
66
+ # @return [Hash] HTTP client configuration
67
+ attr_reader :http_config
68
+
69
+ # Initialize the token provider
70
+ # @param credentials [ClientCredentials] client credentials
71
+ # @param scopes [Array<String>] OAuth2 scopes
72
+ # @param http_config [Hash] HTTP client configuration
73
+ def initialize(credentials, scopes: ['compute:read', 'compute:write'], http_config: {})
74
+ @credentials = credentials
75
+ @scopes = scopes
76
+ @http_config = default_http_config.merge(http_config)
77
+ @current_token = nil
78
+ @token_mutex = Mutex.new
79
+ end
80
+
81
+ # Get token response object, refreshing if necessary
82
+ # @return [TokenResponse] full token response
83
+ # @raise [TokenRequestError] if token request fails
84
+ def token
85
+ @token_mutex.synchronize do
86
+ @current_token = request_new_token if @current_token.nil? || @current_token.expired?
87
+
88
+ @current_token
89
+ end
90
+ end
91
+
92
+ # Get current access token, ensuring one exists
93
+ # @return [String] access token
94
+ # @raise [TokenRequestError] if token request fails
95
+ def access_token
96
+ @token_mutex.synchronize do
97
+ @current_token = request_new_token if @current_token.nil? || @current_token.expired?
98
+
99
+ @current_token.access_token
100
+ end
101
+ end
102
+
103
+ # Alias for backward compatibility
104
+ # @return [String] access token
105
+ # @raise [TokenRequestError] if token request fails
106
+ alias ensure_access_token access_token
107
+
108
+ # Get the current token response (if any)
109
+ # @return [TokenResponse, nil] current token response
110
+ def current_token
111
+ @token_mutex.synchronize { @current_token }
112
+ end
113
+
114
+ # Get the authorization header for HTTP requests
115
+ # @return [String] authorization header value
116
+ def authorization_header
117
+ token = @token_mutex.synchronize { @current_token }
118
+ return nil unless token && !token.expired?
119
+
120
+ token.authorization_header
121
+ end
122
+
123
+ # Force a token refresh
124
+ # @return [String] new access token
125
+ # @raise [TokenRequestError] if token request fails
126
+ def refresh_token
127
+ @token_mutex.synchronize do
128
+ @current_token = request_new_token
129
+ @current_token.access_token
130
+ end
131
+ end
132
+
133
+ private
134
+
135
+ # Get cached access token without forcing refresh
136
+ # @return [String, nil] current access token or nil if no token cached
137
+ def cached_access_token
138
+ @token_mutex.synchronize do
139
+ return nil if @current_token.nil?
140
+
141
+ if @current_token.expired?
142
+ # Auto-refresh expired token
143
+ @current_token = request_new_token
144
+ end
145
+
146
+ @current_token.access_token
147
+ end
148
+ end
149
+
150
+ private
151
+
152
+ def default_http_config
153
+ {
154
+ timeout: 60,
155
+ verify_ssl: true,
156
+ ca_file: nil,
157
+ ca_cert: nil,
158
+ verify_hostname: true,
159
+ user_agent: "eryph-ruby-clientruntime/#{ClientRuntime::VERSION}",
160
+ logger: nil,
161
+ }
162
+ end
163
+
164
+ def request_new_token
165
+ client_assertion = create_client_assertion
166
+
167
+ request_body = {
168
+ 'grant_type' => 'client_credentials',
169
+ 'client_id' => @credentials.client_id,
170
+ 'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
171
+ 'client_assertion' => client_assertion,
172
+ 'scope' => @scopes.join(' '),
173
+ }
174
+
175
+ response = make_token_request(request_body)
176
+ parse_token_response(response)
177
+ end
178
+
179
+ def create_client_assertion
180
+ now = Time.now.to_i
181
+
182
+ claims = {
183
+ 'iss' => @credentials.client_id, # issuer
184
+ 'sub' => @credentials.client_id, # subject
185
+ 'aud' => @credentials.token_endpoint, # audience
186
+ 'jti' => SecureRandom.uuid, # JWT ID
187
+ 'iat' => now, # issued at
188
+ 'exp' => now + 300, # expires in 5 minutes
189
+ }
190
+
191
+ JWT.encode(claims, @credentials.private_key, 'RS256')
192
+ end
193
+
194
+ def make_token_request(request_body)
195
+ log_debug "Requesting token from #{@credentials.token_endpoint} for client #{@credentials.client_id}"
196
+
197
+ connection = create_faraday_connection
198
+
199
+ response = connection.post do |req|
200
+ req.url @credentials.token_endpoint
201
+ req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
202
+ req.headers['User-Agent'] = @http_config[:user_agent]
203
+ req.body = URI.encode_www_form(request_body)
204
+ end
205
+
206
+ unless response.success?
207
+ error_details = parse_error_response(response)
208
+ raise TokenRequestError, "Token request failed (#{response.status}): #{error_details}"
209
+ end
210
+
211
+ log_debug 'Token request successful'
212
+ response
213
+ end
214
+
215
+ def parse_token_response(response)
216
+ data = JSON.parse(response.body)
217
+
218
+ TokenResponse.new(
219
+ access_token: data['access_token'],
220
+ token_type: data['token_type'] || 'Bearer',
221
+ expires_in: data['expires_in'] || 3600,
222
+ scopes: (data['scope'] || '').split
223
+ )
224
+ rescue JSON::ParserError => e
225
+ raise TokenRequestError, "Invalid JSON response from token endpoint: #{e.message}"
226
+ rescue KeyError => e
227
+ raise TokenRequestError, "Missing required field in token response: #{e.message}"
228
+ end
229
+
230
+ def parse_error_response(response)
231
+ return response.reason_phrase if response.body.nil? || response.body.empty?
232
+
233
+ data = JSON.parse(response.body)
234
+ error = data['error'] || 'unknown_error'
235
+ description = data['error_description'] || response.reason_phrase
236
+ "#{error}: #{description}"
237
+ rescue JSON::ParserError
238
+ response.body
239
+ end
240
+
241
+ def create_faraday_connection
242
+ ssl_options = build_ssl_options
243
+
244
+ options = { ssl: ssl_options }
245
+ options[:request] = { timeout: @http_config[:timeout] } if @http_config[:timeout]
246
+
247
+ Faraday.new(options) do |conn|
248
+ conn.adapter Faraday.default_adapter
249
+ end
250
+ end
251
+
252
+ def build_ssl_options
253
+ ssl_options = {}
254
+
255
+ if @http_config[:verify_ssl] == false
256
+ ssl_options[:verify] = false
257
+ else
258
+ ssl_options[:verify] = true
259
+ ssl_options[:verify_hostname] = @http_config[:verify_hostname] != false
260
+ end
261
+
262
+ ssl_options[:ca_file] = @http_config[:ca_file] if @http_config[:ca_file]
263
+ ssl_options[:ca_cert] = @http_config[:ca_cert] if @http_config[:ca_cert]
264
+
265
+ ssl_options
266
+ end
267
+
268
+ def log_debug(message)
269
+ return unless @http_config[:logger]
270
+
271
+ @http_config[:logger].debug("[TokenProvider] #{message}")
272
+ end
273
+ end
274
+ end
275
+ end
@@ -0,0 +1,6 @@
1
+ module Eryph
2
+ module ClientRuntime
3
+ # Current version of the Eryph Client Runtime
4
+ VERSION = '0.1.1'.freeze
5
+ end
6
+ end
@@ -0,0 +1,79 @@
1
+ # Eryph Client Runtime
2
+ # Provides authentication, configuration, and credential lookup for eryph client libraries
3
+
4
+ require_relative 'clientruntime/version'
5
+ require_relative 'clientruntime/environment'
6
+ require_relative 'clientruntime/config_store'
7
+ require_relative 'clientruntime/config_stores_reader'
8
+ require_relative 'clientruntime/local_identity_provider_info'
9
+ require_relative 'clientruntime/client_credentials_lookup'
10
+ require_relative 'clientruntime/token_provider'
11
+ require_relative 'clientruntime/endpoint_lookup'
12
+
13
+ module Eryph
14
+ # Client runtime module providing authentication and configuration services
15
+ # This module can be used by multiple eryph client libraries (compute, identity, etc.)
16
+ module ClientRuntime
17
+ # Error raised when authentication fails
18
+ class AuthenticationError < StandardError; end
19
+
20
+ # Error raised when credentials cannot be found
21
+ class CredentialsNotFoundError < AuthenticationError; end
22
+
23
+ # Error raised when no user credentials found (for automatic discovery)
24
+ class NoUserCredentialsError < CredentialsNotFoundError; end
25
+
26
+ # Error raised when token request fails
27
+ class TokenRequestError < AuthenticationError; end
28
+
29
+ # Error raised when configuration is invalid
30
+ class ConfigurationError < StandardError; end
31
+
32
+ class << self
33
+ # Create a client credentials lookup instance
34
+ # @param config_name [String, nil] configuration name for specific lookup, nil for automatic discovery
35
+ # @param environment [Environment] environment instance for dependency injection
36
+ # @return [ClientCredentialsLookup] configured lookup instance
37
+ def create_credentials_lookup(config_name: nil, environment: nil)
38
+ environment ||= Environment.new
39
+ reader = ConfigStoresReader.new(environment)
40
+ ClientCredentialsLookup.new(reader, config_name)
41
+ end
42
+
43
+ # Test if credentials are available for the specified configuration
44
+ # @param config_name [String, nil] configuration name, nil for automatic discovery
45
+ # @return [Boolean] true if credentials are available
46
+ def credentials_available?(config_name: nil)
47
+ lookup = create_credentials_lookup(config_name: config_name)
48
+ lookup.find_credentials
49
+ true
50
+ rescue CredentialsNotFoundError, NoUserCredentialsError
51
+ false
52
+ end
53
+
54
+ # Check if eryph-zero is running locally
55
+ # @param identity_provider_name [String] identity provider name
56
+ # @return [Boolean] true if eryph-zero is running
57
+ def zero_running?(identity_provider_name: 'zero')
58
+ environment = Environment.new
59
+ provider_info = LocalIdentityProviderInfo.new(environment, identity_provider_name)
60
+ provider_info.running?
61
+ end
62
+
63
+ # Get endpoints from running eryph-zero instance
64
+ # @param identity_provider_name [String] identity provider name
65
+ # @return [Hash] endpoint name -> URL mapping
66
+ def zero_endpoints(identity_provider_name: 'zero')
67
+ environment = Environment.new
68
+ provider_info = LocalIdentityProviderInfo.new(environment, identity_provider_name)
69
+
70
+ if provider_info.running?
71
+ # Convert URI objects to strings
72
+ provider_info.endpoints.transform_values(&:to_s)
73
+ else
74
+ {}
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end