proxy_rb 0.1.0 → 0.2.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.env +3 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +1 -0
  5. data/.travis.yml +8 -2
  6. data/Gemfile +7 -3
  7. data/README.md +42 -8
  8. data/Rakefile +8 -23
  9. data/bin/bootstrap +10 -0
  10. data/{script → bin}/console +1 -0
  11. data/bin/test +3 -0
  12. data/cucumber.yml +2 -0
  13. data/fixtures/proxy-config/.rspec +2 -0
  14. data/fixtures/proxy-config/README.md +18 -0
  15. data/fixtures/proxy-config/Rakefile +10 -0
  16. data/fixtures/proxy-config/bin/http_server +14 -0
  17. data/fixtures/proxy-config/spec/spec_helper.rb +8 -0
  18. data/fixtures/proxy-config/spec/support/aruba.rb +2 -0
  19. data/fixtures/proxy-config/spec/support/proxy_rb.rb +2 -0
  20. data/lib/proxy_rb/basic_configuration/in_config_wrapper.rb +28 -0
  21. data/lib/proxy_rb/basic_configuration/option.rb +32 -0
  22. data/lib/proxy_rb/basic_configuration.rb +148 -0
  23. data/lib/proxy_rb/configuration.rb +42 -0
  24. data/lib/proxy_rb/credentials.rb +52 -0
  25. data/lib/proxy_rb/errors.rb +5 -0
  26. data/lib/proxy_rb/http_downloader.rb +23 -0
  27. data/lib/proxy_rb/http_proxy.rb +52 -0
  28. data/lib/proxy_rb/main.rb +8 -2
  29. data/lib/proxy_rb/no_proxy.rb +26 -0
  30. data/lib/proxy_rb/password_fetchers/basic_password_fetcher.rb +12 -0
  31. data/lib/proxy_rb/password_fetchers/environment_password_fetcher.rb +34 -0
  32. data/lib/proxy_rb/password_fetchers/vault_password_fetcher.rb +47 -0
  33. data/lib/proxy_rb/password_fetchers.rb +6 -0
  34. data/lib/proxy_rb/proxy_url.rb +73 -0
  35. data/lib/proxy_rb/proxy_url_parser.rb +56 -0
  36. data/lib/proxy_rb/request.rb +36 -0
  37. data/lib/proxy_rb/resource.rb +28 -0
  38. data/lib/proxy_rb/response.rb +20 -0
  39. data/lib/proxy_rb/rspec/helpers/http_proxy.rb +78 -0
  40. data/lib/proxy_rb/rspec/helpers/passwords.rb +29 -0
  41. data/lib/proxy_rb/rspec.rb +9 -6
  42. data/lib/proxy_rb/user_passwords/environment_user_password.rb +30 -0
  43. data/lib/proxy_rb/user_passwords/vault_user_password.rb +27 -0
  44. data/lib/proxy_rb/user_passwords.rb +7 -0
  45. data/lib/proxy_rb/version.rb +2 -1
  46. data/lib/proxy_rb.rb +2 -0
  47. data/proxy_rb.gemspec +5 -3
  48. metadata +40 -9
  49. data/Gemfile.lock +0 -263
  50. data/script/bootstrap +0 -7
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+ require 'shellwords'
3
+
4
+ require 'proxy_rb/credentials'
5
+ require 'proxy_rb/proxy_url_parser'
6
+
7
+ # ProxyRb
8
+ module ProxyRb
9
+ # Represent proxy
10
+ class HttpProxy
11
+ protected
12
+
13
+ attr_reader :url, :credentials
14
+
15
+ public
16
+
17
+ def initialize(parser)
18
+ @url = parser.proxy_url
19
+ @credentials = parser.credentials
20
+ end
21
+
22
+ # Convert to parameters for PhantomJS
23
+ #
24
+ # @return [Array]
25
+ # An array of parameters for PhantomJS
26
+ def to_phantom_js
27
+ result = []
28
+ result << "--proxy=#{url}" unless url.empty?
29
+ result << "--proxy-auth=#{credentials}" unless credentials.empty?
30
+
31
+ result
32
+ end
33
+
34
+ # Convert to symbol to reference the proxy
35
+ #
36
+ # @return [Symbol]
37
+ # <host>_<port>_<credentials>
38
+ def to_sym
39
+ Shellwords.escape(*[host, port, credentials.user_name].compact.join('_')).to_sym
40
+ end
41
+
42
+ private
43
+
44
+ def host
45
+ url.host
46
+ end
47
+
48
+ def port
49
+ url.port
50
+ end
51
+ end
52
+ end
data/lib/proxy_rb/main.rb CHANGED
@@ -1,14 +1,20 @@
1
+ # frozen_string_literal: true
2
+ require 'logger'
3
+
1
4
  # Main
2
5
  module ProxyRb
3
6
  @debug_mode = false
7
+ @logger = Logger.new($stderr)
4
8
 
5
9
  class << self
6
- private
10
+ protected
7
11
 
8
12
  attr_accessor :debug_mode
9
13
 
10
14
  public
11
15
 
16
+ attr_reader :logger
17
+
12
18
  def debug_mode_enabled?
13
19
  debug_mode == true
14
20
  end
@@ -18,7 +24,7 @@ module ProxyRb
18
24
  %w(pry byebug).each { |l| require l }
19
25
  end
20
26
 
21
- def require_file_matching_pattern(pattern)
27
+ def require_files_matching_pattern(pattern)
22
28
  root = File.expand_path('../', __FILE__)
23
29
  path = File.join(root, pattern)
24
30
  Dir.glob(path).each { |f| require_relative f }
@@ -0,0 +1,26 @@
1
+ # No proxy
2
+ class NoProxy
3
+ def self.to_s
4
+ ""
5
+ end
6
+
7
+ def self.nil?
8
+ true
9
+ end
10
+
11
+ def self.empty?
12
+ true
13
+ end
14
+
15
+ def nil?
16
+ self.class.nil?
17
+ end
18
+
19
+ def empty?
20
+ self.class.empty?
21
+ end
22
+
23
+ def to_s
24
+ ""
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ module ProxyRb
3
+ # Fetch password for user...
4
+ module PasswordFetchers
5
+ # ... process environment
6
+ class BasicPasswordFetcher
7
+ def call(_user_name)
8
+ raise NotImplementedError, 'You need to implement `#call` for a valid password fetcher'
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+ require 'proxy_rb/password_fetchers/basic_password_fetcher'
3
+ require 'proxy_rb/user_passwords/environment_user_password'
4
+
5
+ module ProxyRb
6
+ # Fetch password for user...
7
+ module PasswordFetchers
8
+ # ... process environment
9
+ class EnvironmentPasswordFetcher < BasicPasswordFetcher
10
+ include Contracts::Core
11
+ include Contracts::Builtin
12
+
13
+ protected
14
+
15
+ attr_reader :prefix
16
+
17
+ public
18
+
19
+ # Prefix of key in Environment
20
+ def initialize(prefix:)
21
+ @prefix = prefix
22
+ end
23
+
24
+ # @param [String] user_name
25
+ # Look up user name
26
+ Contract String => String
27
+ def call(user_name)
28
+ UserPasswords::EnvironmentUserPassword.new(
29
+ ENV[format('%s_%s', prefix, user_name.upcase)]
30
+ ).to_s
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+ require 'proxy_rb/password_fetchers/basic_password_fetcher'
3
+ require 'proxy_rb/user_passwords/vault_user_password'
4
+
5
+ begin
6
+ require 'vault'
7
+ rescue LoadError
8
+ ProxyRb.logger.error 'Please add `vault` to your `Gemfile` to use this password fetcher'
9
+ end
10
+
11
+ module ProxyRb
12
+ # Fetch password for user...
13
+ module PasswordFetchers
14
+ # ... from HashiCorp Vault
15
+ class VaultPasswordFetcher < BasicPasswordFetcher
16
+ include Contracts::Core
17
+ include Contracts::Builtin
18
+
19
+ protected
20
+
21
+ attr_reader :prefix, :client
22
+
23
+ public
24
+
25
+ # @param [String] prefix
26
+ # Prefix used to look up password for user name
27
+ #
28
+ # @param [Vault::Client] client
29
+ # The client used to connect to central "Vault" server
30
+ def initialize(prefix:, client: ::Vault::Client.new(address: ENV['VAULT_ADDR']))
31
+ @prefix = prefix
32
+ @client = client
33
+ end
34
+
35
+ # @param [String] user_name
36
+ # Look up user name
37
+ Contract String => String
38
+ def call(user_name)
39
+ client.with_retries(::Vault::HTTPConnectionError, ::Vault::HTTPError) do |_attempt, _e|
40
+ UserPasswords::VaultUserPassword.new(
41
+ ::Vault.logical.read(File.join(prefix, user_name))
42
+ ).to_s
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+ module ProxyRb
3
+ # Fetch password for user...
4
+ module PasswordFetcher
5
+ end
6
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+ require 'addressable/uri'
3
+
4
+ # ProxyRb
5
+ module ProxyRb
6
+ # A ProxyURL
7
+ class ProxyUrl
8
+ # Build a URL from hash
9
+ #
10
+ # @param [Hash] hash
11
+ # An Hash representing the url parts
12
+ #
13
+ # @return [ProxyUrl]
14
+ # The built url
15
+ def self.build(hash)
16
+ new(Addressable::URI.new(hash))
17
+ end
18
+
19
+ # Create URL from string
20
+ #
21
+ # @param [String] string
22
+ # The url
23
+ #
24
+ # @return [ProxyUrl]
25
+ # The parsed url
26
+ def self.parse(string)
27
+ new(Addressable::URI.heuristic_parse(string.to_s))
28
+ end
29
+
30
+ protected
31
+
32
+ attr_reader :url
33
+
34
+ public
35
+
36
+ def initialize(url)
37
+ @url = url
38
+ end
39
+
40
+ %i(host user password port).each do |m|
41
+ define_method m do
42
+ return nil if empty?
43
+
44
+ url.public_send m
45
+ end
46
+ end
47
+
48
+ def to_s
49
+ return "" if empty?
50
+
51
+ url.to_s
52
+ end
53
+
54
+ # Check if url is empty
55
+ def empty?
56
+ url.nil? || url.empty?
57
+ end
58
+
59
+ # Return URL without user name and password
60
+ #
61
+ # @return [ProxyUrl]
62
+ # The cleaned url
63
+ def without_user_name_and_password
64
+ return self.class.new(nil) if empty?
65
+
66
+ h = url.to_hash
67
+ h.delete :user
68
+ h.delete :password
69
+
70
+ self.class.build(h)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+ require 'uri'
3
+ require 'addressable/uri'
4
+ require 'proxy_rb/proxy_url'
5
+
6
+ # ProxyRb
7
+ module ProxyRb
8
+ # Parse urls for proxies
9
+ class ProxyUrlParser
10
+ protected
11
+
12
+ attr_reader :raw_url
13
+
14
+ public
15
+
16
+ attr_reader :proxy_url, :credentials
17
+
18
+ # @param [String] url
19
+ # The url for the proxy configuration
20
+ def initialize(url)
21
+ temporary_url = ProxyUrl.parse(url)
22
+
23
+ @proxy_url = temporary_url.without_user_name_and_password
24
+ @credentials = Credentials.new(temporary_url.user, temporary_url.password)
25
+ end
26
+
27
+ private
28
+
29
+ def url_hash_for_hostname_port
30
+ splitted_url = raw_url.to_s.split(/:/)
31
+
32
+ url_hash = {}
33
+ url_hash[:host] = splitted_url.first
34
+ url_hash[:path] = '/'
35
+ url_hash[:port] = splitted_url.last unless splitted_url.first == splitted_url.last
36
+
37
+ url_hash
38
+ end
39
+
40
+ def url_hash_for_url
41
+ Addressable::URI.parse(raw_url).to_hash
42
+ end
43
+
44
+ def url?(u)
45
+ url = Addressable::URI.parse(u)
46
+
47
+ return false if url.nil?
48
+ return false unless /\A[[:alnum:]]+\Z/ === url.scheme
49
+ return true if url && url.host
50
+
51
+ false
52
+ rescue Addressable::URI::InvalidURIError
53
+ false
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+ # ProxyRb
3
+ module ProxyRb
4
+ # An HTTP request
5
+ class Request
6
+ protected
7
+
8
+ attr_reader :page
9
+
10
+ public
11
+
12
+ def initialize(page)
13
+ @page = page
14
+ end
15
+
16
+ # Was the request successful
17
+ #
18
+ # @return [TrueClass, FalseClass]
19
+ # The result
20
+ def successful?
21
+ page.status_code.to_s.start_with?('2', '3')
22
+ end
23
+
24
+ # def forbidden?
25
+ # page.status_code == 403
26
+ # end
27
+
28
+ # def invalid?
29
+ # page.status_code == 401
30
+ # end
31
+
32
+ # def redirected?
33
+ # page.status_code.to_s.start_with? '3'
34
+ # end
35
+ end
36
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ require 'addressable/uri'
3
+
4
+ # ProxyRb
5
+ module ProxyRb
6
+ # A resource
7
+ class Resource
8
+ attr_accessor :content
9
+
10
+ private
11
+
12
+ attr_reader :url
13
+
14
+ public
15
+
16
+ def initialize(url)
17
+ @url = Addressable::URI.parse(url)
18
+ end
19
+
20
+ # Convert resource to url
21
+ #
22
+ # @return [String] url
23
+ # The url
24
+ def to_url
25
+ url.to_s
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ # ProxyRb
3
+ module ProxyRb
4
+ # A response
5
+ class Response
6
+ protected
7
+
8
+ attr_reader :page
9
+
10
+ public
11
+
12
+ def initialize(page)
13
+ @page = page
14
+ end
15
+
16
+ # def forbidden?
17
+ # page.status_code == 403
18
+ # end
19
+ end
20
+ end
@@ -1,11 +1,89 @@
1
+ # frozen_string_literal: true
2
+ require 'capybara/poltergeist'
3
+
4
+ require 'proxy_rb'
5
+ require 'proxy_rb/resource'
6
+ require 'proxy_rb/http_downloader'
7
+ require 'proxy_rb/http_proxy'
8
+ require 'proxy_rb/request'
9
+ require 'proxy_rb/response'
10
+
1
11
  # Main Module
2
12
  module ProxyRb
3
13
  # Main Module
4
14
  module Rspec
5
15
  # Helpers
6
16
  module Helpers
17
+ # Non includable internal helper methods
18
+ #
19
+ # Not for use by normal users
20
+ module NonIncludes
21
+ class << self
22
+ def clear_environment
23
+ %w(
24
+ http_proxy
25
+ https_proxy
26
+ HTTP_PROXY
27
+ HTTPS_PROXY
28
+ ).each { |v| ENV.delete v }
29
+ end
30
+
31
+ def register_capybara_driver_for_proxy(proxy)
32
+ options = {
33
+ phantomjs_options: proxy.to_phantom_js,
34
+ js_errors: false,
35
+ phantomjs_logger: $stderr
36
+ }
37
+
38
+ ::Capybara.register_driver proxy.to_sym do |app|
39
+ ::Capybara::Poltergeist::Driver.new(app, options)
40
+ end
41
+
42
+ ::Capybara.run_server = false
43
+ ::Capybara.current_driver = proxy.to_sym
44
+ end
45
+ end
46
+ end
47
+
7
48
  # For http proxy
8
49
  module HttpProxy
50
+ include ::Capybara::DSL
51
+
52
+ # The proxy based on subject
53
+ def proxy
54
+ ProxyRb::HttpProxy.new(ProxyUrlParser.new(subject))
55
+ end
56
+
57
+ # Visit an URL
58
+ #
59
+ # @param [String] url
60
+ def visit(url)
61
+ resource = Resource.new(url)
62
+
63
+ NonIncludes.clear_environment
64
+ NonIncludes.register_capybara_driver_for_proxy(proxy)
65
+
66
+ begin
67
+ super(resource.to_url)
68
+ rescue ::Capybara::Poltergeist::TimeoutError
69
+ raise ProxyRb::UrlTimeoutError, "Failed to fetch #{resource.to_url}: Timeout occured."
70
+ end
71
+ end
72
+
73
+ # !@method download
74
+ #
75
+ # @see #visit
76
+ alias download visit
77
+
78
+ # Get access to the request made
79
+ def request
80
+ Request.new(page)
81
+ end
82
+
83
+ # Get access to the response get
84
+ def response
85
+ Response.new(page)
86
+ end
9
87
  end
10
88
  end
11
89
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ require 'contracts'
3
+
4
+ # ProxyRb
5
+ module ProxyRb
6
+ # Rspec
7
+ module Rspec
8
+ # Helpers
9
+ module Helpers
10
+ # Helpers to access passwords
11
+ module Passwords
12
+ include Contracts::Core
13
+ include Contracts::Builtin
14
+
15
+ Contract Or[String, Symbol] => Maybe[String]
16
+ # Get password for user
17
+ #
18
+ # @param [String] user_name
19
+ # The user name
20
+ #
21
+ # @return [String]
22
+ # The password
23
+ def password(user_name)
24
+ ProxyRb.config.password_fetcher.call(user_name.to_s)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,13 +1,14 @@
1
+ # frozen_string_literal: true
1
2
  require 'rspec'
2
- require 'capybara/poltergeist'
3
3
  require 'capybara/rspec'
4
4
 
5
- require 'proxy_rb/rspec'
5
+ require 'proxy_rb'
6
+ require 'proxy_rb/no_proxy'
6
7
 
7
- ProxyRb.require_file_matching_pattern('rspec/helpers/*.rb')
8
- ProxyRb.require_file_matching_pattern('rspec/matchers/*.rb')
9
- ProxyRb.require_file_matching_pattern('rspec/shared_examples/*.rb')
10
- ProxyRb.require_file_matching_pattern('rspec/shared_contexts/*.rb')
8
+ ProxyRb.require_files_matching_pattern('rspec/helpers/*.rb')
9
+ ProxyRb.require_files_matching_pattern('rspec/matchers/*.rb')
10
+ ProxyRb.require_files_matching_pattern('rspec/shared_examples/*.rb')
11
+ ProxyRb.require_files_matching_pattern('rspec/shared_contexts/*.rb')
11
12
 
12
13
  # Main Module
13
14
  module ProxyRb
@@ -18,4 +19,6 @@ end
18
19
 
19
20
  RSpec.configure do |config|
20
21
  config.include ProxyRb::Rspec::Helpers::HttpProxy, type: :http_proxy
22
+ config.include ProxyRb::Rspec::Helpers::Passwords, type: :http_proxy
23
+ config.include Capybara::RSpecMatchers, type: :http_proxy
21
24
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ # ProxyRb
3
+ module ProxyRb
4
+ # User passwords
5
+ module UserPasswords
6
+ # Be a password from environment
7
+ class EnvironmentUserPassword
8
+ include Contracts::Core
9
+ include Contracts::Builtin
10
+
11
+ protected
12
+
13
+ attr_reader :response
14
+
15
+ public
16
+
17
+ def initialize(response)
18
+ @response = response
19
+ end
20
+
21
+ # Return password
22
+ #
23
+ # @return [String]
24
+ Contract None => String
25
+ def to_s
26
+ response.to_s
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ # ProxyRb
3
+ module ProxyRb
4
+ # User Passwords
5
+ module UserPasswords
6
+ # Be a password return from vault
7
+ class VaultUserPassword
8
+ include Contracts::Core
9
+ include Contracts::Builtin
10
+
11
+ protected
12
+
13
+ attr_reader :response
14
+
15
+ public
16
+
17
+ def initialize(response)
18
+ @response = response.data
19
+ end
20
+
21
+ Contract None => String
22
+ def to_s
23
+ response[:password].to_s
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+ # ProxyRb
3
+ module ProxyRb
4
+ # User Passwords
5
+ module UserPasswords
6
+ end
7
+ end
@@ -1,4 +1,5 @@
1
+ # frozen_string_literal: true
1
2
  # Main Module
2
3
  module ProxyRb
3
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
4
5
  end
data/lib/proxy_rb.rb CHANGED
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  require 'proxy_rb/version'
3
+ require 'proxy_rb/configuration'
2
4
  require 'proxy_rb/main'
3
5
 
4
6
  # Main module
data/proxy_rb.gemspec CHANGED
@@ -1,4 +1,5 @@
1
1
  # coding: utf-8
2
+ # frozen_string_literal: true
2
3
  lib = File.expand_path('../lib', __FILE__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'proxy_rb/version'
@@ -24,8 +25,9 @@ Gem::Specification.new do |spec|
24
25
  spec.add_runtime_dependency 'poltergeist'
25
26
 
26
27
  spec.add_runtime_dependency 'addressable'
27
- spec.add_runtime_dependency 'activesupport'
28
- spec.add_runtime_dependency 'uglifier'
28
+ spec.add_runtime_dependency 'contracts'
29
+ spec.add_runtime_dependency 'excon'
30
+ # spec.add_runtime_dependency 'phantomjs'
29
31
 
30
- spec.required_ruby_version = '~> 2.0'
32
+ spec.required_ruby_version = '~> 2.3'
31
33
  end