nexus-debug 1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c5e977b37cb2b84fdff288647f916aaf31d72e330fbb75219732cf40acef41b8
4
+ data.tar.gz: 65c9201977d7d1a661834015c1dd9d24aa125a8d3d5341ea418c5c144ef5dd2c
5
+ SHA512:
6
+ metadata.gz: 5dffd2d02171b34bad106eb0eed21fc4c4990802f91d5d6a67c00176caaf8d0cc418de995a4c65385871d8ec9e72a7d74aa4e24943da0ac775c9ec846cefa3f1
7
+ data.tar.gz: 3a5cfd62ff57b6c97d262b7abe43dab2b5366e71e1c378d6b98a783f06f7fe86e1b516e8eaf0a1c6cc63c6e18eb200a8543bf392230bcf630957411cb4164358
data/MIT-LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2009, Nick Quaranto
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ #-*- mode: ruby -*-
2
+
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+ require 'rubygems/package_task'
6
+
7
+ task :default => [ :test, :package ]
8
+
9
+ Gem::PackageTask.new( Gem::Specification.load( 'nexus.gemspec' ) ) do
10
+ end
11
+
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.libs << "test"
14
+ t.test_files = FileList['test/*_test.rb']
15
+ t.verbose = true
16
+ end
17
+
18
+ # vim: syntax=Ruby
data/bin/nbundle ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'bundler/monkey_patch'
4
+
5
+ load Gem.bin_path('bundler', 'bundle')
@@ -0,0 +1,225 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubygems/local_remote_options'
4
+ require 'net/http'
5
+ require 'base64'
6
+ require 'nexus/config'
7
+
8
+ module Gem
9
+ class AbstractCommand < Gem::Command
10
+ include Gem::LocalRemoteOptions
11
+
12
+ def initialize(name, summary)
13
+ super
14
+
15
+ add_option('-r', '--repo KEY',
16
+ "pick the configuration under that key.\n can be used in conjuction with --clear-repo and the upload itself.") do |value, options|
17
+ options[:nexus_repo] = value
18
+ end
19
+
20
+ add_option('-c', '--clear-repo',
21
+ 'Clears the nexus config for the given repo or the default repo') do |value, options|
22
+ options[:nexus_clear] = value
23
+ end
24
+
25
+ add_option('--url URL',
26
+ 'URL of the rubygems repository on a Nexus server') do |value, options|
27
+ options[:nexus_url] = value
28
+ end
29
+
30
+ add_option('--credential USER:PASS',
31
+ 'Enter your Nexus credentials in "Username:Password" format') do |value, options|
32
+ options[:nexus_credential] = value
33
+ end
34
+
35
+ add_option('--nexus-config FILE',
36
+ "File location of nexus config to use.\n default #{Nexus::Config.default_file}") do |value, options|
37
+ options[:nexus_config] = File.expand_path(value)
38
+ end
39
+
40
+ add_option('--ignore-ssl-errors',
41
+ 'No check certificate.') do |_value, options|
42
+ options[:ssl_verify_mode] = OpenSSL::SSL::VERIFY_NONE
43
+ end
44
+ end
45
+
46
+ def url
47
+ url = config.url
48
+ # no leading slash
49
+ url&.sub!(%r{/$}, '')
50
+ url
51
+ end
52
+
53
+ def configure_url
54
+ url =
55
+ if options[:nexus_url]
56
+ options[:nexus_url]
57
+ else
58
+ say 'Enter the URL of the rubygems repository on a Nexus server'
59
+ ask('URL: ')
60
+ end
61
+
62
+ if !URI.parse(url.to_s).host.nil?
63
+ config.url = url
64
+
65
+ say "The Nexus URL has been stored in #{config}"
66
+ else
67
+ raise 'no URL given'
68
+ end
69
+ end
70
+
71
+ def setup
72
+ prompt_encryption if config.encrypted?
73
+ configure_url if config.url.nil? || options[:nexus_clear]
74
+ use_proxy!(url) if http_proxy(url)
75
+ if authorization.nil? ||
76
+ config.always_prompt? ||
77
+ options[:nexus_clear]
78
+ sign_in
79
+ end
80
+ end
81
+
82
+ def prompt_encryption
83
+ password = ask_for_password('Enter your Nexus encryption credentials (no prompt)')
84
+
85
+ # recreate config with password
86
+ config.password = password
87
+ end
88
+
89
+ def sign_in
90
+ token =
91
+ if options[:nexus_credential]
92
+ options[:nexus_credential]
93
+ else
94
+ say 'Enter your Nexus credentials'
95
+ username = ask('Username: ')
96
+ password = ask_for_password('Password: ')
97
+ "#{username}:#{password}"
98
+ end
99
+
100
+ # mimic strict_encode64 which is not there on ruby1.8
101
+ auth = "Basic #{Base64.encode64(token).gsub(/\s+/, '')}"
102
+ @authorization = token == ':' ? nil : auth
103
+
104
+ return if config.always_prompt?
105
+
106
+ config.authorization = @authorization
107
+ if @authorization
108
+ say "Your Nexus credentials have been stored in #{config}"
109
+ else
110
+ say "Your Nexus credentials have been deleted from #{config}"
111
+ end
112
+ end
113
+
114
+ def this_config(_pass = nil)
115
+ Nexus::Config.new(options[:nexus_config],
116
+ options[:nexus_repo])
117
+ end
118
+
119
+ private :this_config
120
+
121
+ def config(pass = nil)
122
+ @config = this_config(pass) if pass
123
+ @config ||= this_config
124
+ end
125
+
126
+ def authorization
127
+ @authorization || config.authorization
128
+ end
129
+
130
+ def make_request(method, path)
131
+ require 'net/http'
132
+ require 'net/https'
133
+
134
+ url = URI.parse("#{self.url}/#{path}")
135
+
136
+ http = proxy_class.new(url.host, url.port)
137
+
138
+ if url.scheme == 'https'
139
+ http.use_ssl = true
140
+ http.verify_mode =
141
+ options[:ssl_verify_mode] || config.ssl_verify_mode || OpenSSL::SSL::VERIFY_PEER
142
+ end
143
+
144
+ # Because sometimes our gems are huge and our people are on vpns
145
+ http.read_timeout = 300
146
+
147
+ request_method =
148
+ case method
149
+ when :get
150
+ proxy_class::Get
151
+ when :post
152
+ proxy_class::Post
153
+ when :put
154
+ proxy_class::Put
155
+ when :delete
156
+ proxy_class::Delete
157
+ else
158
+ raise ArgumentError
159
+ end
160
+
161
+ request = request_method.new(url.path)
162
+ request.add_field 'User-Agent', 'Ruby' unless RUBY_VERSION =~ /^1.9/
163
+
164
+ yield request if block_given?
165
+
166
+ if Gem.configuration.verbose.to_s.to_i.positive?
167
+ warn "#{request.method} #{url}"
168
+ if config.authorization
169
+ warn 'use authorization'
170
+ else
171
+ warn 'no authorization'
172
+ end
173
+
174
+ warn "use proxy at #{http.proxy_address}:#{http.proxy_port}" if http.proxy_address
175
+ end
176
+
177
+ if Gem.configuration.verbose.to_s.to_i.positive?
178
+ say "http request to #{self.url}/#{path} for #{method}"
179
+ end
180
+ http.request(request)
181
+ end
182
+
183
+ def use_proxy!(url)
184
+ proxy_uri = http_proxy(url)
185
+ @proxy_class = Net::HTTP::Proxy(proxy_uri.host,
186
+ proxy_uri.port,
187
+ proxy_uri.user,
188
+ proxy_uri.password)
189
+ end
190
+
191
+ def proxy_class
192
+ @proxy_class || Net::HTTP
193
+ end
194
+
195
+ # @return [URI, nil] the HTTP-proxy as a URI if set; +nil+ otherwise
196
+ def http_proxy(url)
197
+ uri = begin
198
+ URI.parse(url)
199
+ rescue StandardError
200
+ nil
201
+ end
202
+ return nil if uri.nil?
203
+
204
+ no_proxy = ENV['no_proxy']
205
+ if (no_proxy || ENV['NO_PROXY']) && no_proxy.split(/, */).member?(uri.host)
206
+ # does not look on ip-adress ranges
207
+ return nil
208
+ end
209
+
210
+ key = uri.scheme == 'http' ? 'http_proxy' : 'https_proxy'
211
+ proxy = Gem.configuration[:http_proxy] || ENV[key] || ENV[key.upcase]
212
+ return nil if proxy.nil? || proxy == :no_proxy
213
+
214
+ URI.parse(proxy)
215
+ end
216
+
217
+ def ask_for_password(message)
218
+ system 'stty -echo'
219
+ password = ask(message)
220
+ system 'stty echo'
221
+ ui.say("\n")
222
+ password
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gem
4
+ module Commands
5
+ class NexusCommand < Gem::AbstractCommand
6
+ def description
7
+ 'Upload a gem up to Nexus server'
8
+ end
9
+
10
+ def arguments
11
+ 'GEM built gem to upload. some options do not require it.'
12
+ end
13
+
14
+ def usage
15
+ "#{program_name} GEM [GEM [...]]"
16
+ end
17
+
18
+ def list_repos
19
+ puts
20
+ config.repos.each do |k, v|
21
+ puts "#{k}: #{v}"
22
+ end
23
+ puts
24
+ end
25
+
26
+ private :list_repos
27
+
28
+ def initialize
29
+ super 'nexus', description
30
+ add_proxy_option
31
+
32
+ add_option('--all-repos',
33
+ 'list all configured repos with their respective urls.') do |value, options|
34
+ options[:nexus_all_repos] = value
35
+ end
36
+
37
+ add_option('--clear-all',
38
+ 'clears all credentials') do |value, options|
39
+ options[:nexus_clear_all] = value
40
+ end
41
+
42
+ add_option('--secrets FILE',
43
+ 'move the credentials to the given secrets file.') do |value, options|
44
+ options[:nexus_secrets] = File.expand_path(value)
45
+ end
46
+
47
+ add_option('--no-secrets',
48
+ 'move the credentials to the configuration file and delete the secrets file.') do |_value, options|
49
+ options[:nexus_secrets] = false
50
+ end
51
+
52
+ add_option('--[no-]prompt',
53
+ 'always prompt for the credentials. this is helpful to reset your username/password for a specific host.') do |value, options|
54
+ options[:nexus_prompt_all] = value
55
+ end
56
+
57
+ add_option('--[no-]encrypt',
58
+ 'encrypt/decrypt the credentials with a master password.') do |value, options|
59
+ options[:nexus_encrypt] = value
60
+ end
61
+ end
62
+
63
+ def execute
64
+ names = begin
65
+ get_all_gem_names
66
+ rescue StandardError
67
+ nil
68
+ end
69
+ if names && (!options[:nexus_all_repos].nil? ||
70
+ !options[:nexus_clear_all].nil? ||
71
+ !options[:nexus_prompt_all].nil? ||
72
+ !options[:nexus_encrypt].nil? ||
73
+ !options[:nexus_secrets].nil?)
74
+ warn "given gemfile(s) #{names.join(' ')} get ignored due to the options used"
75
+ end
76
+
77
+ if options[:nexus_all_repos]
78
+ list_repos
79
+ elsif options[:nexus_prompt_all]
80
+ if ask('setup nexus to always prompt username/passwords and delete all current credentials ? (y/N)') == 'y'
81
+ config.always_prompt
82
+ end
83
+ elsif options[:nexus_prompt_all] == false
84
+ say('setup nexus to store username/passwords')
85
+ config.clear_always_prompt
86
+ elsif options[:nexus_clear_all]
87
+ config.clear_credentials if ask('delete all current credentials ? (y/N)') == 'y'
88
+ elsif options[:nexus_encrypt]
89
+ prompt_encryption
90
+ config.encrypt_credentials
91
+ elsif options[:nexus_encrypt] == false
92
+ prompt_encryption
93
+ config.decrypt_credentials
94
+ elsif options[:nexus_secrets] == false
95
+ config.new_secrets(nil)
96
+ elsif options[:nexus_secrets]
97
+ config.new_secrets(options[:nexus_secrets])
98
+ else
99
+ setup
100
+ # if there is no gemname and no options which then fail with send_gem
101
+ send_gem
102
+ end
103
+ end
104
+
105
+ def send_gem
106
+ gems = get_all_gem_names
107
+
108
+ say "Uploading #{gems.size} gem#{'s' if gems.size != 1} to Nexus..."
109
+
110
+ gems.each do |path|
111
+ response = make_request(:put, "gems/#{File.basename(path)}") do |request|
112
+ request.body = Gem.read_binary(path)
113
+ request.add_field('Content-Length', request.body.size)
114
+ request.add_field('Content-Type', 'application/octet-stream')
115
+ request.add_field('Authorization', authorization.strip) if authorization
116
+ end
117
+
118
+ case response.code
119
+ when '400'
120
+ say 'something went wrong - maybe (re)deployment is not allowed'
121
+ when '401'
122
+ say 'Unauthorized'
123
+ when '500'
124
+ say 'something went wrong'
125
+ else
126
+ if Gem.configuration.verbose.to_s.to_i.positive?
127
+ say "body:\n\t`#{response.message}`\n"
128
+ end
129
+ say "#{response.message} #{path.split(%r{/}).last}"
130
+ end
131
+
132
+ exit 1 unless response.is_a? Net::HTTPSuccess
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,53 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ module Nexus
5
+ class Cipher
6
+
7
+ def initialize(pass, token = nil)
8
+ @token = Base64.strict_decode64(token) if token
9
+ @token ||= OpenSSL::Random.random_bytes(32)
10
+
11
+ iter = 20_000
12
+ key_len = 32
13
+ @key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(pass,
14
+ @token,
15
+ iter,
16
+ key_len)
17
+ end
18
+
19
+ def cipher
20
+ @cipher ||= OpenSSL::Cipher.new('aes-256-cbc')
21
+ end
22
+
23
+ private :cipher
24
+
25
+ def token
26
+ Base64.strict_encode64(@token)
27
+ end
28
+
29
+ def iv
30
+ Base64.strict_encode64(@iv) if @iv
31
+ end
32
+
33
+ def iv=(iv)
34
+ @iv = Base64.strict_decode64(iv)
35
+ end
36
+
37
+ def encrypt(data)
38
+ c = cipher
39
+ c.encrypt
40
+ c.key = @key
41
+ @iv = c.random_iv
42
+ Base64.strict_encode64(c.update(data) + c.final)
43
+ end
44
+
45
+ def decrypt(data)
46
+ c = cipher
47
+ c.decrypt
48
+ c.key = @key
49
+ c.iv = @iv
50
+ c.update(Base64.strict_decode64(data)) + c.final
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,195 @@
1
+ # frozen_string_literal: true
2
+ require 'nexus/cipher'
3
+ require 'nexus/config_file'
4
+
5
+ module Nexus
6
+ class Config
7
+
8
+ def self.default_file
9
+ File.join(Gem.user_home, '.gem', 'nexus')
10
+ end
11
+
12
+ def initialize(file = nil, repo = nil)
13
+ @repo = repo
14
+ @conf = ConfigFile.new(file || self.class.default_file)
15
+ @secr = ConfigFile.new(@conf[:secrets]) if @conf.key? :secrets
16
+ end
17
+
18
+ private
19
+
20
+ def encrypt_or_decrypt_credentials
21
+ map = config.all
22
+ yield map if map[:authorization]
23
+ map.each do |k, v|
24
+ yield v if v.is_a?(Hash) && v[:authorization]
25
+ end
26
+ end
27
+
28
+ def move_credentials(from, to)
29
+ keys = %i[secrets token iv authorization]
30
+ ([nil] + from.repos).each do |repo|
31
+ keys.each do |k|
32
+ to[k, repo] = from[k, repo]
33
+ from[k, repo] = nil
34
+ end
35
+ end
36
+ end
37
+
38
+ def config
39
+ @config ||= @secr || @conf
40
+ end
41
+
42
+ def key?(key)
43
+ config.key?(key, @repo)
44
+ end
45
+
46
+ def [](key)
47
+ config[key, @repo]
48
+ end
49
+
50
+ def []=(key, value)
51
+ config[key, @repo] = value
52
+ end
53
+
54
+ public
55
+
56
+ def encrypted?
57
+ config.key?(:token)
58
+ end
59
+
60
+ def password=(pass)
61
+ @cipher = Cipher.new(pass, config[:token])
62
+ end
63
+
64
+ def always_prompt?
65
+ @conf[:always_prompt]
66
+ end
67
+
68
+ def clear_always_prompt
69
+ @conf.delete(:always_prompt)
70
+ @conf.store
71
+ end
72
+
73
+ def clear_credentials(store = true)
74
+ secrets = @conf[:secrets]
75
+ if secrets && !config.key?(:token)
76
+ FileUtils.rm_f(secrets)
77
+ @map = @conf
78
+ else
79
+ config.delete :iv, :authorization
80
+ end
81
+ @conf.delete(:secrets)
82
+ @conf.store if store
83
+ end
84
+
85
+ def always_prompt
86
+ @conf[:always_prompt, nil] = true
87
+
88
+ config.delete(:token)
89
+
90
+ clear_credentials(false)
91
+
92
+ @conf.store
93
+ end
94
+
95
+ def decrypt_credentials
96
+ unless encrypted?
97
+ warn 'not encrypted - nothing to do'
98
+ return
99
+ end
100
+ encrypt_or_decrypt_credentials do |c|
101
+ @cipher.iv = c[:iv]
102
+ c[:authorization] = @cipher.decrypt(c[:authorization])
103
+ c.delete(:iv)
104
+ end
105
+ config.all.delete(:token)
106
+ config.store
107
+ @cipher = nil
108
+ end
109
+
110
+ def encrypt_credentials
111
+ if encrypted?
112
+ warn 'already encrypted - nothing to do'
113
+ return
114
+ end
115
+ encrypt_or_decrypt_credentials do |c|
116
+ c[:authorization] = @cipher.encrypt(c[:authorization])
117
+ c[:iv] = @cipher.iv
118
+ end
119
+ config.all[:token] = @cipher.token
120
+ config.store
121
+ end
122
+
123
+ def new_secrets(new)
124
+ old = @conf.all[:secrets]
125
+ FileUtils.mv(old, new) if old && new
126
+ @secr = ConfigFile.new(new) if new
127
+
128
+ if new.nil? && old
129
+ @conf.merge!(@secr)
130
+ FileUtils.rm_f(old)
131
+ @secr = nil
132
+ end
133
+
134
+ if old.nil? && new
135
+ move_credentials(@conf, @secr)
136
+
137
+ @secr.store
138
+ end
139
+
140
+ # store the new location
141
+ @conf[:secrets, nil] = new
142
+ @conf.store
143
+ end
144
+
145
+ def repos
146
+ result = @conf.section(:url)
147
+ url = result.delete(:url)
148
+ result['DEFAULT'] = url if url
149
+ result.each_key do |key|
150
+ result[key] = result[key][:url] if key != 'DEFAULT'
151
+ end
152
+ result
153
+ end
154
+
155
+ def authorization
156
+ auth = self[:authorization]
157
+ if @cipher && auth && self[:iv]
158
+ @cipher.iv = self[:iv]
159
+ @cipher.decrypt(auth)
160
+ elsif @cipher && auth
161
+ _authorization = auth
162
+ else
163
+ auth
164
+ end
165
+ end
166
+
167
+ def authorization=(auth)
168
+ if @cipher && auth
169
+ self[:authorization] = @cipher.encrypt(auth)
170
+ self[:iv] = @cipher.iv
171
+ else
172
+ self[:authorization] = auth
173
+ end
174
+ config.store
175
+ auth
176
+ end
177
+
178
+ def url
179
+ @conf[:url, @repo]
180
+ end
181
+
182
+ def url=(repo_url)
183
+ @conf[:url, @repo] = repo_url
184
+ @conf.store
185
+ end
186
+
187
+ def ssl_verify_mode
188
+ @conf[:ssl_verify_mode, @repo]
189
+ end
190
+
191
+ def to_s
192
+ config.file
193
+ end
194
+ end
195
+ end