hybrid_platforms_conductor 33.3.0 → 33.4.0
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 +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +31 -2
- data/docs/config_dsl.md +43 -0
- data/lib/hybrid_platforms_conductor/bitbucket.rb +134 -90
- data/lib/hybrid_platforms_conductor/common_config_dsl/bitbucket.rb +12 -44
- data/lib/hybrid_platforms_conductor/common_config_dsl/github.rb +9 -31
- data/lib/hybrid_platforms_conductor/confluence.rb +93 -88
- data/lib/hybrid_platforms_conductor/credentials.rb +112 -95
- data/lib/hybrid_platforms_conductor/deployer.rb +2 -2
- data/lib/hybrid_platforms_conductor/github.rb +39 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +4 -2
- data/lib/hybrid_platforms_conductor/hpc_plugins/report/confluence.rb +3 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/keepass.rb +2 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/thycotic.rb +3 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/bitbucket_conf.rb +4 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/github_ci.rb +4 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_conf.rb +6 -2
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_masters_ok.rb +6 -2
- data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/confluence.rb +3 -1
- data/lib/hybrid_platforms_conductor/logger_helpers.rb +7 -1
- data/lib/hybrid_platforms_conductor/thycotic.rb +80 -75
- data/lib/hybrid_platforms_conductor/version.rb +1 -1
- data/spec/hybrid_platforms_conductor_test.rb +6 -0
- data/spec/hybrid_platforms_conductor_test/api/credentials_spec.rb +247 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/secrets_reader_plugins/keepass_spec.rb +280 -319
- data/spec/hybrid_platforms_conductor_test/api/deployer/secrets_reader_plugins/thycotic_spec.rb +2 -2
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/bitbucket_conf_spec.rb +49 -69
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/github_ci_spec.rb +29 -39
- metadata +18 -2
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
require 'octokit'
|
|
2
|
-
require 'hybrid_platforms_conductor/credentials'
|
|
3
|
-
|
|
4
1
|
module HybridPlatformsConductor
|
|
5
2
|
|
|
6
3
|
module CommonConfigDsl
|
|
@@ -8,12 +5,19 @@ module HybridPlatformsConductor
|
|
|
8
5
|
# Add common Github config DSL to declare known Github repositories
|
|
9
6
|
module Github
|
|
10
7
|
|
|
8
|
+
# List of Github repositories
|
|
9
|
+
# Array< Hash<Symbol, Object> >
|
|
10
|
+
# * *user* (String): User or organization name, storing repositories
|
|
11
|
+
# * *url* (String): URL to the Github API
|
|
12
|
+
# * *repos* (Array<String> or Symbol): List of repository names from this project, or :all for all
|
|
13
|
+
attr_reader :known_github_repos
|
|
14
|
+
|
|
11
15
|
# Initialize the DSL
|
|
12
16
|
def init_github
|
|
13
17
|
# List of Github repositories definitions
|
|
14
18
|
# Array< Hash<Symbol, Object> >
|
|
15
19
|
# Each definition is just mapping the signature of #github_repos
|
|
16
|
-
@
|
|
20
|
+
@known_github_repos = []
|
|
17
21
|
end
|
|
18
22
|
|
|
19
23
|
# Register new Github repositories
|
|
@@ -23,39 +27,13 @@ module HybridPlatformsConductor
|
|
|
23
27
|
# * *url* (String): URL to the Github API [default: 'https://api.github.com']
|
|
24
28
|
# * *repos* (Array<String> or Symbol): List of repository names from this project, or :all for all [default: :all]
|
|
25
29
|
def github_repos(user:, url: 'https://api.github.com', repos: :all)
|
|
26
|
-
@
|
|
30
|
+
@known_github_repos << {
|
|
27
31
|
url: url,
|
|
28
32
|
user: user,
|
|
29
33
|
repos: repos
|
|
30
34
|
}
|
|
31
35
|
end
|
|
32
36
|
|
|
33
|
-
# Iterate over each Github repository
|
|
34
|
-
#
|
|
35
|
-
# Parameters::
|
|
36
|
-
# * Proc: Code called for each Github repository:
|
|
37
|
-
# * Parameters::
|
|
38
|
-
# * *github* (Octokit::Client): The client instance accessing the Github API
|
|
39
|
-
# * *repo_info* (Hash<Symbol, Object>): The repository info:
|
|
40
|
-
# * *name* (String): Repository name.
|
|
41
|
-
# * *slug* (String): Repository slug.
|
|
42
|
-
def for_each_github_repo
|
|
43
|
-
@github_repos.each do |repo_info|
|
|
44
|
-
Octokit.configure do |c|
|
|
45
|
-
c.api_endpoint = repo_info[:url]
|
|
46
|
-
end
|
|
47
|
-
Credentials.with_credentials_for(:github, @logger, @logger_stderr, url: repo_info[:url]) do |_github_user, github_token|
|
|
48
|
-
client = Octokit::Client.new(access_token: github_token)
|
|
49
|
-
(repo_info[:repos] == :all ? client.repositories(repo_info[:user]).map { |repo| repo[:name] } : repo_info[:repos]).each do |name|
|
|
50
|
-
yield client, {
|
|
51
|
-
name: name,
|
|
52
|
-
slug: "#{repo_info[:user]}/#{name}"
|
|
53
|
-
}
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
37
|
end
|
|
60
38
|
|
|
61
39
|
end
|
|
@@ -7,111 +7,116 @@ require 'hybrid_platforms_conductor/credentials'
|
|
|
7
7
|
|
|
8
8
|
module HybridPlatformsConductor
|
|
9
9
|
|
|
10
|
-
#
|
|
11
|
-
|
|
10
|
+
# Mixin used to access Confluence API
|
|
11
|
+
module Confluence
|
|
12
12
|
|
|
13
|
-
include
|
|
13
|
+
include Credentials
|
|
14
14
|
|
|
15
15
|
# Provide a Confluence connector, and make sure the password is being cleaned when exiting.
|
|
16
16
|
#
|
|
17
17
|
# Parameters::
|
|
18
18
|
# * *confluence_url* (String): The Confluence URL
|
|
19
|
-
# * *logger* (Logger): Logger to be used
|
|
20
|
-
# * *logger_stderr* (Logger): Logger to be used for stderr
|
|
21
19
|
# * Proc: Code called with the Confluence instance.
|
|
22
|
-
# * *confluence* (
|
|
23
|
-
def
|
|
24
|
-
|
|
25
|
-
yield
|
|
20
|
+
# * *confluence* (ConfluenceApi): The Confluence instance to use.
|
|
21
|
+
def with_confluence(confluence_url)
|
|
22
|
+
with_credentials_for(:confluence, resource: confluence_url) do |confluence_user, confluence_password|
|
|
23
|
+
yield ConfluenceApi.new(confluence_url, confluence_user, confluence_password, logger: @logger, logger_stderr: @logger_stderr)
|
|
26
24
|
end
|
|
27
25
|
end
|
|
28
26
|
|
|
29
|
-
#
|
|
30
|
-
|
|
31
|
-
# Parameters::
|
|
32
|
-
# * *confluence_url* (String): The Confluence URL
|
|
33
|
-
# * *confluence_user_name* (String): Confluence user name to be used when querying the API
|
|
34
|
-
# * *confluence_password* (String): Confluence password to be used when querying the API
|
|
35
|
-
# * *logger* (Logger): Logger to be used [default = Logger.new(STDOUT)]
|
|
36
|
-
# * *logger_stderr* (Logger): Logger to be used for stderr [default = Logger.new(STDERR)]
|
|
37
|
-
def initialize(confluence_url, confluence_user_name, confluence_password, logger: Logger.new($stdout), logger_stderr: Logger.new($stderr))
|
|
38
|
-
init_loggers(logger, logger_stderr)
|
|
39
|
-
@confluence_url = confluence_url
|
|
40
|
-
@confluence_user_name = confluence_user_name
|
|
41
|
-
@confluence_password = confluence_password
|
|
42
|
-
end
|
|
27
|
+
# Provide an API access on Confluence
|
|
28
|
+
class ConfluenceApi
|
|
43
29
|
|
|
44
|
-
|
|
45
|
-
#
|
|
46
|
-
# Parameters::
|
|
47
|
-
# * *page_id* (String): Confluence page ID
|
|
48
|
-
# Result::
|
|
49
|
-
# * Nokogiri::HTML: Storage format content, as a Nokogiri object
|
|
50
|
-
def page_storage_format(page_id)
|
|
51
|
-
Nokogiri::HTML(call_api("plugins/viewstorage/viewpagestorage.action?pageId=#{page_id}").body)
|
|
52
|
-
end
|
|
30
|
+
include LoggerHelpers
|
|
53
31
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
32
|
+
# Constructor
|
|
33
|
+
#
|
|
34
|
+
# Parameters::
|
|
35
|
+
# * *confluence_url* (String): The Confluence URL
|
|
36
|
+
# * *confluence_user_name* (String): Confluence user name to be used when querying the API
|
|
37
|
+
# * *confluence_password* (String): Confluence password to be used when querying the API
|
|
38
|
+
# * *logger* (Logger): Logger to be used [default = Logger.new(STDOUT)]
|
|
39
|
+
# * *logger_stderr* (Logger): Logger to be used for stderr [default = Logger.new(STDERR)]
|
|
40
|
+
def initialize(confluence_url, confluence_user_name, confluence_password, logger: Logger.new($stdout), logger_stderr: Logger.new($stderr))
|
|
41
|
+
init_loggers(logger, logger_stderr)
|
|
42
|
+
@confluence_url = confluence_url
|
|
43
|
+
@confluence_user_name = confluence_user_name
|
|
44
|
+
@confluence_password = confluence_password
|
|
45
|
+
end
|
|
63
46
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
version = info['version']['number'] + 1 if version.nil?
|
|
73
|
-
log_debug "Update Confluence page #{page_id}..."
|
|
74
|
-
call_api("rest/api/content/#{page_id}", :put) do |request|
|
|
75
|
-
request['Content-Type'] = 'application/json'
|
|
76
|
-
request.body = {
|
|
77
|
-
type: 'page',
|
|
78
|
-
title: info['title'],
|
|
79
|
-
body: {
|
|
80
|
-
storage: {
|
|
81
|
-
value: content,
|
|
82
|
-
representation: 'storage'
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
version: { number: version }
|
|
86
|
-
}.to_json
|
|
47
|
+
# Return a Confluence storage format content from a page ID
|
|
48
|
+
#
|
|
49
|
+
# Parameters::
|
|
50
|
+
# * *page_id* (String): Confluence page ID
|
|
51
|
+
# Result::
|
|
52
|
+
# * Nokogiri::HTML: Storage format content, as a Nokogiri object
|
|
53
|
+
def page_storage_format(page_id)
|
|
54
|
+
Nokogiri::HTML(call_api("plugins/viewstorage/viewpagestorage.action?pageId=#{page_id}").body)
|
|
87
55
|
end
|
|
88
|
-
end
|
|
89
56
|
|
|
90
|
-
|
|
57
|
+
# Return some info of a given page ID
|
|
58
|
+
#
|
|
59
|
+
# Parameters::
|
|
60
|
+
# * *page_id* (String): Confluence page ID
|
|
61
|
+
# Result::
|
|
62
|
+
# * Hash: Page information, as returned by the Confluence API
|
|
63
|
+
def page_info(page_id)
|
|
64
|
+
JSON.parse(call_api("rest/api/content/#{page_id}").body)
|
|
65
|
+
end
|
|
91
66
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
67
|
+
# Update a Confluence page to a new content.
|
|
68
|
+
#
|
|
69
|
+
# Parameters::
|
|
70
|
+
# * *page_id* (String): Confluence page ID
|
|
71
|
+
# * *content* (String): New content
|
|
72
|
+
# * *version* (String or nil): New version, or nil to automatically increase last existing version [default: nil]
|
|
73
|
+
def update_page(page_id, content, version: nil)
|
|
74
|
+
info = page_info(page_id)
|
|
75
|
+
version = info['version']['number'] + 1 if version.nil?
|
|
76
|
+
log_debug "Update Confluence page #{page_id}..."
|
|
77
|
+
call_api("rest/api/content/#{page_id}", :put) do |request|
|
|
78
|
+
request['Content-Type'] = 'application/json'
|
|
79
|
+
request.body = {
|
|
80
|
+
type: 'page',
|
|
81
|
+
title: info['title'],
|
|
82
|
+
body: {
|
|
83
|
+
storage: {
|
|
84
|
+
value: content,
|
|
85
|
+
representation: 'storage'
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
version: { number: version }
|
|
89
|
+
}.to_json
|
|
90
|
+
end
|
|
113
91
|
end
|
|
114
|
-
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
# Call the Confluence API for a given URL and HTTP verb.
|
|
96
|
+
# Provide a simple way to tweak the request with an optional proc.
|
|
97
|
+
# Automatically handles authentication, base URL and error handling.
|
|
98
|
+
#
|
|
99
|
+
# Parameters::
|
|
100
|
+
# * *api_path* (String): The API path to query
|
|
101
|
+
# * *http_method* (Symbol): HTTP method to be used to create the request [default = :get]
|
|
102
|
+
# * Proc: Optional code called to alter the request
|
|
103
|
+
# * Parameters::
|
|
104
|
+
# * *request* (Net::HTTPRequest): The request
|
|
105
|
+
# Result::
|
|
106
|
+
# * Net::HTTPResponse: The corresponding response
|
|
107
|
+
def call_api(api_path, http_method = :get)
|
|
108
|
+
response = nil
|
|
109
|
+
page_url = URI.parse("#{@confluence_url}/#{api_path}")
|
|
110
|
+
Net::HTTP.start(page_url.host, page_url.port, use_ssl: true) do |http|
|
|
111
|
+
request = Net::HTTP.const_get(http_method.to_s.capitalize.to_sym).new(page_url.request_uri)
|
|
112
|
+
request.basic_auth @confluence_user_name, @confluence_password
|
|
113
|
+
yield request if block_given?
|
|
114
|
+
response = http.request(request)
|
|
115
|
+
raise "Confluence page API request on #{page_url} returned an error: #{response.code}\n#{response.body}\n===== Request body =====\n#{request.body}" unless response.is_a?(Net::HTTPSuccess)
|
|
116
|
+
end
|
|
117
|
+
response
|
|
118
|
+
end
|
|
119
|
+
|
|
115
120
|
end
|
|
116
121
|
|
|
117
122
|
end
|
|
@@ -7,122 +7,139 @@ module HybridPlatformsConductor
|
|
|
7
7
|
# Give a secured and harmonized way to access credentials for a given service.
|
|
8
8
|
# It makes sure to remove passwords from memory for hardened security (this way if a vulnerability allows an attacker to dump the memory it won't get passwords).
|
|
9
9
|
# It gets credentials from the following sources:
|
|
10
|
+
# * Configuration
|
|
10
11
|
# * Environment variables
|
|
11
12
|
# * Netrc file
|
|
12
|
-
|
|
13
|
+
module Credentials
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
# Extend the Config DSL
|
|
16
|
+
module ConfigDSLExtension
|
|
17
|
+
|
|
18
|
+
# List of credentials. Each info has the following properties:
|
|
19
|
+
# * *credential_id* (Symbol): Credential ID this rule applies to
|
|
20
|
+
# * *resource* (Regexp): Resource filtering for this rule
|
|
21
|
+
# * *provider* (Proc): The code providing the credentials:
|
|
22
|
+
# * Parameters::
|
|
23
|
+
# * *resource* (String or nil): The resource for which we want credentials, or nil if none
|
|
24
|
+
# * *requester* (Proc): Code to be called to give credentials to:
|
|
25
|
+
# * Parameters::
|
|
26
|
+
# * *user* (String or nil): The user name, or nil if none
|
|
27
|
+
# * *password* (String or nil): The password, or nil if none
|
|
28
|
+
attr_reader :credentials
|
|
29
|
+
|
|
30
|
+
# Mixin initializer
|
|
31
|
+
def init_credentials_config
|
|
32
|
+
@credentials = []
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Define a credentials provider
|
|
36
|
+
#
|
|
37
|
+
# Parameters::
|
|
38
|
+
# * *credential_id* (Symbol): Credential ID this rule applies to
|
|
39
|
+
# * *resource* (String or Regexp): Resource filtering for this rule [default: /.*/]
|
|
40
|
+
# * *provider* (Proc): The code providing the credentials:
|
|
41
|
+
# * Parameters::
|
|
42
|
+
# * *resource* (String or nil): The resource for which we want credentials, or nil if none
|
|
43
|
+
# * *requester* (Proc): Code to be called to give credentials to:
|
|
44
|
+
# * Parameters::
|
|
45
|
+
# * *user* (String or nil): The user name, or nil if none
|
|
46
|
+
# * *password* (String or nil): The password, or nil if none
|
|
47
|
+
def credentials_for(credential_id, resource: /.*/, &provider)
|
|
48
|
+
@credentials << {
|
|
49
|
+
credential_id: credential_id,
|
|
50
|
+
resource: resource.is_a?(String) ? /^#{Regexp.escape(resource)}$/ : resource,
|
|
51
|
+
provider: provider
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
Config.extend_config_dsl_with ConfigDSLExtension, :init_credentials_config
|
|
15
58
|
|
|
16
59
|
# Get access to credentials and make sure they are wiped out from memory when client code ends.
|
|
17
60
|
# To ensure password safety, never store the password in a scope beyond the client code's Proc.
|
|
18
61
|
#
|
|
19
62
|
# Parameters::
|
|
20
63
|
# * *id* (Symbol): Credential ID
|
|
21
|
-
# * *
|
|
22
|
-
# * *logger_stderr* (Logger): Logger to be used for stderr
|
|
23
|
-
# * *url* (String or nil): The URL for which we want the credentials, or nil if not associated to a URL [default: nil]
|
|
64
|
+
# * *resource* (String or nil): The resource for which we want the credentials, or nil if not associated to a resource [default: nil]
|
|
24
65
|
# * Proc: Client code called with credentials provided
|
|
25
66
|
# * Parameters::
|
|
26
67
|
# * *user* (String or nil): User name, or nil if none
|
|
27
68
|
# * *password* (String or nil): Password, or nil if none.
|
|
28
69
|
# !!! Never store this password in a scope broader than the client code itself !!!
|
|
29
|
-
def
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
yield credentials.user, credentials.password
|
|
33
|
-
ensure
|
|
34
|
-
credentials.clear_password
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Constructor
|
|
39
|
-
#
|
|
40
|
-
# Parameters::
|
|
41
|
-
# * *id* (Symbol): Credential ID
|
|
42
|
-
# * *url* (String or nil): The URL for which we want the credentials, or nil if not associated to a URL [default: nil]
|
|
43
|
-
# * *logger* (Logger): Logger to be used [default = Logger.new(STDOUT)]
|
|
44
|
-
# * *logger_stderr* (Logger): Logger to be used for stderr [default = Logger.new(STDERR)]
|
|
45
|
-
def initialize(id, url: nil, logger: Logger.new($stdout), logger_stderr: Logger.new($stderr))
|
|
46
|
-
init_loggers(logger, logger_stderr)
|
|
47
|
-
@id = id
|
|
48
|
-
@url = url
|
|
49
|
-
@user = nil
|
|
50
|
-
@password = nil
|
|
51
|
-
@retrieved = false
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Provide a helper to clear password from memory for security.
|
|
55
|
-
# To be used when the client knows it won't use the password anymore.
|
|
56
|
-
def clear_password
|
|
57
|
-
@password&.replace('gotyou!' * 100)
|
|
58
|
-
GC.start
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Get the associated user
|
|
62
|
-
#
|
|
63
|
-
# Result::
|
|
64
|
-
# * String or nil: The user name, or nil if none
|
|
65
|
-
def user
|
|
66
|
-
retrieve_credentials
|
|
67
|
-
@user
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Get the associated password
|
|
71
|
-
#
|
|
72
|
-
# Result::
|
|
73
|
-
# * String or nil: The password, or nil if none
|
|
74
|
-
def password
|
|
75
|
-
retrieve_credentials
|
|
76
|
-
@password
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
private
|
|
70
|
+
def with_credentials_for(id, resource: nil)
|
|
71
|
+
# Get the credentials provider
|
|
72
|
+
provider = nil
|
|
80
73
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
74
|
+
# Check configuration
|
|
75
|
+
# Take the last matching provider, this way we can define several providers for resources matched in a increasingly refined way.
|
|
76
|
+
@config.credentials.each do |credentials_info|
|
|
77
|
+
provider = credentials_info[:provider] if credentials_info[:credential_id] == id && (
|
|
78
|
+
(resource.nil? && credentials_info[:resource] == /.*/) || credentials_info[:resource] =~ resource
|
|
79
|
+
)
|
|
80
|
+
end
|
|
86
81
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
82
|
+
provider ||= proc do |requested_resource, requester|
|
|
83
|
+
# Check environment variables
|
|
84
|
+
user = ENV["hpc_user_for_#{id}"].dup
|
|
85
|
+
password = ENV["hpc_password_for_#{id}"].dup
|
|
86
|
+
if user.nil? || user.empty? || password.nil? || password.empty?
|
|
87
|
+
log_debug "[ Credentials for #{id} ] - Credentials not found from environment variables."
|
|
88
|
+
if requested_resource.nil?
|
|
89
|
+
log_debug "[ Credentials for #{id} ] - No resource associated to this credentials, so .netrc can't be used."
|
|
90
|
+
else
|
|
91
|
+
# Check Netrc
|
|
92
|
+
netrc = ::Netrc.read
|
|
93
|
+
begin
|
|
94
|
+
netrc_user, netrc_password = netrc[
|
|
95
|
+
begin
|
|
96
|
+
URI.parse(requested_resource).host.downcase
|
|
97
|
+
rescue URI::InvalidURIError
|
|
98
|
+
requested_resource
|
|
99
|
+
end
|
|
100
|
+
]
|
|
101
|
+
if netrc_user.nil?
|
|
102
|
+
log_debug "[ Credentials for #{id} ] - No credentials retrieved from .netrc."
|
|
103
|
+
# TODO: Add more credentials source if needed here
|
|
104
|
+
log_warn "[ Credentials for #{id} ] - Unable to get credentials for #{id} (Resource: #{requested_resource})."
|
|
105
|
+
else
|
|
106
|
+
user = netrc_user.dup
|
|
107
|
+
password = netrc_password.dup
|
|
108
|
+
log_debug "[ Credentials for #{id} ] - Credentials retrieved from .netrc using #{requested_resource}."
|
|
109
|
+
end
|
|
110
|
+
ensure
|
|
111
|
+
# Make sure the password does not stay in Netrc memory
|
|
112
|
+
# Wipe out any memory trace that might contain passwords in clear
|
|
113
|
+
netrc.instance_variable_get(:@data).each do |data_line|
|
|
114
|
+
data_line.each do |data_string|
|
|
115
|
+
data_string.replace('GotYou!!!' * 100)
|
|
116
|
+
end
|
|
114
117
|
end
|
|
118
|
+
# We do this assignment on purpose so that GC can remove sensitive data later
|
|
119
|
+
# rubocop:disable Lint/UselessAssignment
|
|
120
|
+
netrc = nil
|
|
121
|
+
# rubocop:enable Lint/UselessAssignment
|
|
115
122
|
end
|
|
116
|
-
# We don this assignment on purpose so that GC can remove sensitive data later
|
|
117
|
-
# rubocop:disable Lint/UselessAssignment
|
|
118
|
-
netrc = nil
|
|
119
|
-
# rubocop:enable Lint/UselessAssignment
|
|
120
123
|
end
|
|
124
|
+
else
|
|
125
|
+
log_debug "[ Credentials for #{id} ] - Credentials retrieved from environment variables."
|
|
121
126
|
end
|
|
122
|
-
|
|
123
|
-
|
|
127
|
+
GC.start
|
|
128
|
+
requester.call user, password
|
|
129
|
+
password&.replace('gotyou!' * 100)
|
|
130
|
+
GC.start
|
|
124
131
|
end
|
|
125
|
-
|
|
132
|
+
|
|
133
|
+
requester_called = false
|
|
134
|
+
provider.call(
|
|
135
|
+
resource,
|
|
136
|
+
proc do |user, password|
|
|
137
|
+
requester_called = true
|
|
138
|
+
yield user, password
|
|
139
|
+
end
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
raise "Requester not called by the credentials provider for #{id} (resource: #{resource}) - Please check the credentials_for code in your configuration." unless requester_called
|
|
126
143
|
end
|
|
127
144
|
|
|
128
145
|
end
|