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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +47 -0
- data/LICENSE +21 -0
- data/README.md +144 -0
- data/eryph-clientruntime.gemspec +42 -0
- data/eryph-compute.gemspec +44 -0
- data/lib/eryph/clientruntime/client_credentials_lookup.rb +295 -0
- data/lib/eryph/clientruntime/config_store.rb +206 -0
- data/lib/eryph/clientruntime/config_stores_reader.rb +211 -0
- data/lib/eryph/clientruntime/endpoint_lookup.rb +226 -0
- data/lib/eryph/clientruntime/environment.rb +440 -0
- data/lib/eryph/clientruntime/local_identity_provider_info.rb +153 -0
- data/lib/eryph/clientruntime/token_provider.rb +275 -0
- data/lib/eryph/clientruntime/version.rb +6 -0
- data/lib/eryph/clientruntime.rb +79 -0
- data/lib/eryph/compute/client.rb +442 -0
- data/lib/eryph/compute/generated/Gemfile +9 -0
- data/lib/eryph/compute/generated/README.md +206 -0
- data/lib/eryph/compute/generated/Rakefile +10 -0
- data/lib/eryph/compute/generated/compute_client.gemspec +41 -0
- data/lib/eryph/compute/generated/docs/ApiVersion.md +20 -0
- data/lib/eryph/compute/generated/docs/ApiVersionResponse.md +18 -0
- data/lib/eryph/compute/generated/docs/Catlet.md +32 -0
- data/lib/eryph/compute/generated/docs/CatletConfigOperationResult.md +18 -0
- data/lib/eryph/compute/generated/docs/CatletConfigValidationResult.md +20 -0
- data/lib/eryph/compute/generated/docs/CatletConfiguration.md +18 -0
- data/lib/eryph/compute/generated/docs/CatletDrive.md +20 -0
- data/lib/eryph/compute/generated/docs/CatletDriveType.md +15 -0
- data/lib/eryph/compute/generated/docs/CatletList.md +18 -0
- data/lib/eryph/compute/generated/docs/CatletNetwork.md +30 -0
- data/lib/eryph/compute/generated/docs/CatletNetworkAdapter.md +20 -0
- data/lib/eryph/compute/generated/docs/CatletStatus.md +15 -0
- data/lib/eryph/compute/generated/docs/CatletStopMode.md +15 -0
- data/lib/eryph/compute/generated/docs/CatletsApi.md +863 -0
- data/lib/eryph/compute/generated/docs/DiskStatus.md +15 -0
- data/lib/eryph/compute/generated/docs/ExpandCatletConfigRequestBody.md +22 -0
- data/lib/eryph/compute/generated/docs/ExpandNewCatletConfigRequest.md +22 -0
- data/lib/eryph/compute/generated/docs/FloatingNetworkPort.md +26 -0
- data/lib/eryph/compute/generated/docs/Gene.md +30 -0
- data/lib/eryph/compute/generated/docs/GeneList.md +18 -0
- data/lib/eryph/compute/generated/docs/GeneType.md +15 -0
- data/lib/eryph/compute/generated/docs/GeneWithUsage.md +34 -0
- data/lib/eryph/compute/generated/docs/GenesApi.md +281 -0
- data/lib/eryph/compute/generated/docs/NewCatletRequest.md +20 -0
- data/lib/eryph/compute/generated/docs/NewProjectMemberBody.md +22 -0
- data/lib/eryph/compute/generated/docs/NewProjectRequest.md +20 -0
- data/lib/eryph/compute/generated/docs/NewVirtualDiskRequest.md +30 -0
- data/lib/eryph/compute/generated/docs/Operation.md +32 -0
- data/lib/eryph/compute/generated/docs/OperationList.md +18 -0
- data/lib/eryph/compute/generated/docs/OperationLogEntry.md +24 -0
- data/lib/eryph/compute/generated/docs/OperationResource.md +22 -0
- data/lib/eryph/compute/generated/docs/OperationResult.md +18 -0
- data/lib/eryph/compute/generated/docs/OperationStatus.md +15 -0
- data/lib/eryph/compute/generated/docs/OperationTask.md +30 -0
- data/lib/eryph/compute/generated/docs/OperationTaskReference.md +22 -0
- data/lib/eryph/compute/generated/docs/OperationTaskStatus.md +15 -0
- data/lib/eryph/compute/generated/docs/OperationsApi.md +157 -0
- data/lib/eryph/compute/generated/docs/PopulateCatletConfigVariablesRequest.md +20 -0
- data/lib/eryph/compute/generated/docs/ProblemDetails.md +26 -0
- data/lib/eryph/compute/generated/docs/Project.md +22 -0
- data/lib/eryph/compute/generated/docs/ProjectList.md +18 -0
- data/lib/eryph/compute/generated/docs/ProjectMemberRole.md +26 -0
- data/lib/eryph/compute/generated/docs/ProjectMemberRoleList.md +18 -0
- data/lib/eryph/compute/generated/docs/ProjectMembersApi.md +293 -0
- data/lib/eryph/compute/generated/docs/ProjectsApi.md +286 -0
- data/lib/eryph/compute/generated/docs/ResourceType.md +15 -0
- data/lib/eryph/compute/generated/docs/StopCatletRequestBody.md +18 -0
- data/lib/eryph/compute/generated/docs/TaskReferenceType.md +15 -0
- data/lib/eryph/compute/generated/docs/UpdateCatletRequestBody.md +20 -0
- data/lib/eryph/compute/generated/docs/UpdateProjectNetworksRequestBody.md +20 -0
- data/lib/eryph/compute/generated/docs/ValidateConfigRequest.md +18 -0
- data/lib/eryph/compute/generated/docs/ValidationIssue.md +20 -0
- data/lib/eryph/compute/generated/docs/VersionApi.md +69 -0
- data/lib/eryph/compute/generated/docs/VirtualDisk.md +42 -0
- data/lib/eryph/compute/generated/docs/VirtualDiskAttachedCatlet.md +20 -0
- data/lib/eryph/compute/generated/docs/VirtualDiskGeneInfo.md +22 -0
- data/lib/eryph/compute/generated/docs/VirtualDiskList.md +18 -0
- data/lib/eryph/compute/generated/docs/VirtualDisksApi.md +291 -0
- data/lib/eryph/compute/generated/docs/VirtualNetwork.md +28 -0
- data/lib/eryph/compute/generated/docs/VirtualNetworkConfiguration.md +18 -0
- data/lib/eryph/compute/generated/docs/VirtualNetworkList.md +18 -0
- data/lib/eryph/compute/generated/docs/VirtualNetworksApi.md +291 -0
- data/lib/eryph/compute/generated/git_push.sh +57 -0
- data/lib/eryph/compute/generated/lib/compute_client/api/catlets_api.rb +812 -0
- data/lib/eryph/compute/generated/lib/compute_client/api/genes_api.rb +262 -0
- data/lib/eryph/compute/generated/lib/compute_client/api/operations_api.rb +154 -0
- data/lib/eryph/compute/generated/lib/compute_client/api/project_members_api.rb +297 -0
- data/lib/eryph/compute/generated/lib/compute_client/api/projects_api.rb +269 -0
- data/lib/eryph/compute/generated/lib/compute_client/api/version_api.rb +79 -0
- data/lib/eryph/compute/generated/lib/compute_client/api/virtual_disks_api.rb +272 -0
- data/lib/eryph/compute/generated/lib/compute_client/api/virtual_networks_api.rb +282 -0
- data/lib/eryph/compute/generated/lib/compute_client/api_client.rb +437 -0
- data/lib/eryph/compute/generated/lib/compute_client/api_error.rb +58 -0
- data/lib/eryph/compute/generated/lib/compute_client/configuration.rb +392 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/api_version.rb +263 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/api_version_response.rb +237 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/catlet.rb +400 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/catlet_config_operation_result.rb +234 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/catlet_config_validation_result.rb +251 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/catlet_configuration.rb +223 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/catlet_drive.rb +270 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/catlet_drive_type.rb +43 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/catlet_list.rb +239 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/catlet_network.rb +318 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/catlet_network_adapter.rb +247 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/catlet_status.rb +43 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/catlet_stop_mode.rb +41 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/disk_status.rb +40 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/expand_catlet_config_request_body.rb +243 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/expand_new_catlet_config_request.rb +243 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/floating_network_port.rb +313 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/gene.rb +415 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/gene_list.rb +239 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/gene_type.rb +41 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/gene_with_usage.rb +439 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/new_catlet_request.rb +233 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/new_project_member_body.rb +273 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/new_project_request.rb +247 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/new_virtual_disk_request.rb +345 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/operation.rb +352 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/operation_list.rb +239 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/operation_log_entry.rb +299 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/operation_resource.rb +311 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/operation_result.rb +242 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/operation_status.rb +42 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/operation_task.rb +366 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/operation_task_reference.rb +311 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/operation_task_status.rb +42 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/populate_catlet_config_variables_request.rb +233 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/problem_details.rb +261 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/project.rb +289 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/project_list.rb +239 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/project_member_role.rb +341 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/project_member_role_list.rb +239 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/resource_type.rb +42 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/stop_catlet_request_body.rb +259 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/task_reference_type.rb +40 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/update_catlet_request_body.rb +233 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/update_project_networks_request_body.rb +233 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/validate_config_request.rb +223 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/validation_issue.rb +249 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/virtual_disk.rb +479 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/virtual_disk_attached_catlet.rb +285 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/virtual_disk_gene_info.rb +289 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/virtual_disk_list.rb +239 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/virtual_network.rb +351 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/virtual_network_configuration.rb +223 -0
- data/lib/eryph/compute/generated/lib/compute_client/models/virtual_network_list.rb +239 -0
- data/lib/eryph/compute/generated/lib/compute_client/version.rb +15 -0
- data/lib/eryph/compute/generated/lib/compute_client.rb +101 -0
- data/lib/eryph/compute/generated/spec/api/catlets_api_spec.rb +182 -0
- data/lib/eryph/compute/generated/spec/api/genes_api_spec.rb +81 -0
- data/lib/eryph/compute/generated/spec/api/operations_api_spec.rb +62 -0
- data/lib/eryph/compute/generated/spec/api/project_members_api_spec.rb +86 -0
- data/lib/eryph/compute/generated/spec/api/projects_api_spec.rb +82 -0
- data/lib/eryph/compute/generated/spec/api/version_api_spec.rb +46 -0
- data/lib/eryph/compute/generated/spec/api/virtual_disks_api_spec.rb +83 -0
- data/lib/eryph/compute/generated/spec/api/virtual_networks_api_spec.rb +84 -0
- data/lib/eryph/compute/generated/spec/models/api_version_response_spec.rb +36 -0
- data/lib/eryph/compute/generated/spec/models/api_version_spec.rb +42 -0
- data/lib/eryph/compute/generated/spec/models/catlet_config_operation_result_spec.rb +36 -0
- data/lib/eryph/compute/generated/spec/models/catlet_config_validation_result_spec.rb +42 -0
- data/lib/eryph/compute/generated/spec/models/catlet_configuration_spec.rb +36 -0
- data/lib/eryph/compute/generated/spec/models/catlet_drive_spec.rb +42 -0
- data/lib/eryph/compute/generated/spec/models/catlet_drive_type_spec.rb +30 -0
- data/lib/eryph/compute/generated/spec/models/catlet_list_spec.rb +36 -0
- data/lib/eryph/compute/generated/spec/models/catlet_network_adapter_spec.rb +42 -0
- data/lib/eryph/compute/generated/spec/models/catlet_network_spec.rb +72 -0
- data/lib/eryph/compute/generated/spec/models/catlet_spec.rb +78 -0
- data/lib/eryph/compute/generated/spec/models/catlet_status_spec.rb +30 -0
- data/lib/eryph/compute/generated/spec/models/catlet_stop_mode_spec.rb +30 -0
- data/lib/eryph/compute/generated/spec/models/disk_status_spec.rb +30 -0
- data/lib/eryph/compute/generated/spec/models/expand_catlet_config_request_body_spec.rb +48 -0
- data/lib/eryph/compute/generated/spec/models/expand_new_catlet_config_request_spec.rb +48 -0
- data/lib/eryph/compute/generated/spec/models/floating_network_port_spec.rb +60 -0
- data/lib/eryph/compute/generated/spec/models/gene_list_spec.rb +36 -0
- data/lib/eryph/compute/generated/spec/models/gene_spec.rb +72 -0
- data/lib/eryph/compute/generated/spec/models/gene_type_spec.rb +30 -0
- data/lib/eryph/compute/generated/spec/models/gene_with_usage_spec.rb +84 -0
- data/lib/eryph/compute/generated/spec/models/new_catlet_request_spec.rb +42 -0
- data/lib/eryph/compute/generated/spec/models/new_project_member_body_spec.rb +48 -0
- data/lib/eryph/compute/generated/spec/models/new_project_request_spec.rb +42 -0
- data/lib/eryph/compute/generated/spec/models/new_virtual_disk_request_spec.rb +72 -0
- data/lib/eryph/compute/generated/spec/models/operation_list_spec.rb +36 -0
- data/lib/eryph/compute/generated/spec/models/operation_log_entry_spec.rb +54 -0
- data/lib/eryph/compute/generated/spec/models/operation_resource_spec.rb +48 -0
- data/lib/eryph/compute/generated/spec/models/operation_result_spec.rb +36 -0
- data/lib/eryph/compute/generated/spec/models/operation_spec.rb +78 -0
- data/lib/eryph/compute/generated/spec/models/operation_status_spec.rb +30 -0
- data/lib/eryph/compute/generated/spec/models/operation_task_reference_spec.rb +48 -0
- data/lib/eryph/compute/generated/spec/models/operation_task_spec.rb +72 -0
- data/lib/eryph/compute/generated/spec/models/operation_task_status_spec.rb +30 -0
- data/lib/eryph/compute/generated/spec/models/populate_catlet_config_variables_request_spec.rb +42 -0
- data/lib/eryph/compute/generated/spec/models/problem_details_spec.rb +60 -0
- data/lib/eryph/compute/generated/spec/models/project_list_spec.rb +36 -0
- data/lib/eryph/compute/generated/spec/models/project_member_role_list_spec.rb +36 -0
- data/lib/eryph/compute/generated/spec/models/project_member_role_spec.rb +60 -0
- data/lib/eryph/compute/generated/spec/models/project_spec.rb +48 -0
- data/lib/eryph/compute/generated/spec/models/resource_type_spec.rb +30 -0
- data/lib/eryph/compute/generated/spec/models/stop_catlet_request_body_spec.rb +36 -0
- data/lib/eryph/compute/generated/spec/models/task_reference_type_spec.rb +30 -0
- data/lib/eryph/compute/generated/spec/models/update_catlet_request_body_spec.rb +42 -0
- data/lib/eryph/compute/generated/spec/models/update_project_networks_request_body_spec.rb +42 -0
- data/lib/eryph/compute/generated/spec/models/validate_config_request_spec.rb +36 -0
- data/lib/eryph/compute/generated/spec/models/validation_issue_spec.rb +42 -0
- data/lib/eryph/compute/generated/spec/models/virtual_disk_attached_catlet_spec.rb +42 -0
- data/lib/eryph/compute/generated/spec/models/virtual_disk_gene_info_spec.rb +48 -0
- data/lib/eryph/compute/generated/spec/models/virtual_disk_list_spec.rb +36 -0
- data/lib/eryph/compute/generated/spec/models/virtual_disk_spec.rb +108 -0
- data/lib/eryph/compute/generated/spec/models/virtual_network_configuration_spec.rb +36 -0
- data/lib/eryph/compute/generated/spec/models/virtual_network_list_spec.rb +36 -0
- data/lib/eryph/compute/generated/spec/models/virtual_network_spec.rb +66 -0
- data/lib/eryph/compute/generated/spec/spec_helper.rb +111 -0
- data/lib/eryph/compute/generated.rb +137 -0
- data/lib/eryph/compute/operation_result.rb +255 -0
- data/lib/eryph/compute/operation_tracker.rb +247 -0
- data/lib/eryph/compute/problem_details_error.rb +139 -0
- data/lib/eryph/compute/version.rb +6 -0
- data/lib/eryph/compute.rb +40 -0
- data/lib/eryph/version.rb +4 -0
- data/lib/eryph.rb +68 -0
- 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,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
|