agile-proxy 0.1.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 +7 -0
- data/.bowerrc +3 -0
- data/.gitignore +8 -0
- data/.rspec +2 -0
- data/.rubocop.yml +36 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +267 -0
- data/Guardfile +20 -0
- data/LICENSE +22 -0
- data/README.md +93 -0
- data/Rakefile +13 -0
- data/agile-proxy.gemspec +50 -0
- data/assets/index.html +39 -0
- data/assets/ui/app/HttpFlexibleProxyApi.js +31 -0
- data/assets/ui/app/app.js +1 -0
- data/assets/ui/app/controller/Stubs.js +64 -0
- data/assets/ui/app/controller/main.js +12 -0
- data/assets/ui/app/directive/AppEnhancedFormElement.js +21 -0
- data/assets/ui/app/directive/AppFor.js +16 -0
- data/assets/ui/app/directive/AppResponseEditor.js +54 -0
- data/assets/ui/app/model/RequestSpec.js +6 -0
- data/assets/ui/app/routes.js +10 -0
- data/assets/ui/app/service/Dialog.js +49 -0
- data/assets/ui/app/service/DomId.js +10 -0
- data/assets/ui/app/service/Error.js +7 -0
- data/assets/ui/app/service/Stub.js +36 -0
- data/assets/ui/app/view/404.html +2 -0
- data/assets/ui/app/view/dialog/error.html +10 -0
- data/assets/ui/app/view/dialog/yesNo.html +8 -0
- data/assets/ui/app/view/responses/editForm.html +78 -0
- data/assets/ui/app/view/status.html +1 -0
- data/assets/ui/app/view/stubs.html +19 -0
- data/assets/ui/app/view/stubs/edit.html +58 -0
- data/assets/ui/css/main.css +3 -0
- data/bin/agile_proxy +113 -0
- data/bower.json +27 -0
- data/config.yml +6 -0
- data/db.yml +10 -0
- data/db/migrations/20140818110800_create_users.rb +9 -0
- data/db/migrations/20140818134700_create_applications.rb +10 -0
- data/db/migrations/20140818135200_create_request_specs.rb +13 -0
- data/db/migrations/20140821115300_create_responses.rb +14 -0
- data/db/migrations/20140823082900_add_method_to_request_specs.rb +7 -0
- data/db/migrations/20140823083900_rename_request_spec_columns.rb +8 -0
- data/db/migrations/20141031072100_add_url_type_to_request_specs.rb +8 -0
- data/db/migrations/20141105125600_add_conditions_to_request_specs.rb +7 -0
- data/db/migrations/20141106083100_add_username_and_password_to_applications.rb +8 -0
- data/db/migrations/20141119143800_add_record_to_applications.rb +7 -0
- data/db/migrations/20141119174300_create_recordings.rb +18 -0
- data/db/schema.rb +78 -0
- data/examples/README.md +1 -0
- data/examples/facebook_api.html +59 -0
- data/examples/tumblr_api.html +22 -0
- data/lib/agile_proxy.rb +8 -0
- data/lib/agile_proxy/api/applications.rb +77 -0
- data/lib/agile_proxy/api/recordings.rb +52 -0
- data/lib/agile_proxy/api/request_specs.rb +85 -0
- data/lib/agile_proxy/api/root.rb +41 -0
- data/lib/agile_proxy/config.rb +63 -0
- data/lib/agile_proxy/handlers/handler.rb +43 -0
- data/lib/agile_proxy/handlers/proxy_handler.rb +110 -0
- data/lib/agile_proxy/handlers/request_handler.rb +57 -0
- data/lib/agile_proxy/handlers/stub_handler.rb +113 -0
- data/lib/agile_proxy/mitm.crt +22 -0
- data/lib/agile_proxy/mitm.key +27 -0
- data/lib/agile_proxy/model/application.rb +20 -0
- data/lib/agile_proxy/model/recording.rb +16 -0
- data/lib/agile_proxy/model/request_spec.rb +47 -0
- data/lib/agile_proxy/model/response.rb +56 -0
- data/lib/agile_proxy/model/user.rb +17 -0
- data/lib/agile_proxy/proxy_connection.rb +113 -0
- data/lib/agile_proxy/route.rb +106 -0
- data/lib/agile_proxy/router.rb +99 -0
- data/lib/agile_proxy/server.rb +85 -0
- data/lib/agile_proxy/servers/api.rb +41 -0
- data/lib/agile_proxy/servers/request_spec.rb +30 -0
- data/lib/agile_proxy/version.rb +6 -0
- data/load_proxy.js +39 -0
- data/log/.gitkeep +0 -0
- data/spec/common_helper.rb +32 -0
- data/spec/fixtures/test-server.crt +15 -0
- data/spec/fixtures/test-server.key +15 -0
- data/spec/integration/helpers/request_spec_helper.rb +60 -0
- data/spec/integration/specs/lib/server_spec.rb +407 -0
- data/spec/integration_spec_helper.rb +18 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/test_server.rb +75 -0
- data/spec/unit/agile_proxy/api/applications_spec.rb +102 -0
- data/spec/unit/agile_proxy/api/common_helper.rb +31 -0
- data/spec/unit/agile_proxy/api/recordings_spec.rb +115 -0
- data/spec/unit/agile_proxy/api/request_specs_spec.rb +159 -0
- data/spec/unit/agile_proxy/handlers/handler_spec.rb +8 -0
- data/spec/unit/agile_proxy/handlers/proxy_handler_spec.rb +138 -0
- data/spec/unit/agile_proxy/handlers/request_handler_spec.rb +55 -0
- data/spec/unit/agile_proxy/handlers/stub_handler_spec.rb +154 -0
- data/spec/unit/agile_proxy/model/recording_spec.rb +0 -0
- data/spec/unit/agile_proxy/model/request_spec_spec.rb +45 -0
- data/spec/unit/agile_proxy/model/response_spec.rb +38 -0
- data/spec/unit/agile_proxy/server_spec.rb +88 -0
- data/spec/unit/agile_proxy/servers/api_spec.rb +31 -0
- data/spec/unit/agile_proxy/servers/request_spec_spec.rb +32 -0
- metadata +618 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
require 'tmpdir'
|
|
3
|
+
# Agile Proxy
|
|
4
|
+
module AgileProxy
|
|
5
|
+
#
|
|
6
|
+
# Configuration for the agile proxy
|
|
7
|
+
class Config
|
|
8
|
+
DEFAULT_WHITELIST = ['127.0.0.1', 'localhost']
|
|
9
|
+
RANDOM_AVAILABLE_PORT = 0 # https://github.com/eventmachine/eventmachine/wiki/FAQ#wiki-can-i-start-a-server-on-a-random-available-port
|
|
10
|
+
|
|
11
|
+
attr_accessor :logger, :cache, :cache_request_headers, :whitelist, :path_blacklist, :ignore_params,
|
|
12
|
+
:persist_cache, :ignore_cache_port, :non_successful_cache_disabled, :non_successful_error_level,
|
|
13
|
+
:non_whitelisted_requests_disabled, :cache_path, :proxy_port, :proxied_request_inactivity_timeout,
|
|
14
|
+
:proxied_request_connect_timeout, :dynamic_jsonp, :dynamic_jsonp_keys,
|
|
15
|
+
:webserver_host, :webserver_port, :database_config_file, :environment
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
@logger = defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
|
|
19
|
+
reset
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Resets the configuration with the defaults
|
|
23
|
+
def reset
|
|
24
|
+
@cache = true
|
|
25
|
+
@cache_request_headers = false
|
|
26
|
+
@whitelist = DEFAULT_WHITELIST
|
|
27
|
+
@path_blacklist = []
|
|
28
|
+
@ignore_params = []
|
|
29
|
+
@persist_cache = false
|
|
30
|
+
@dynamic_jsonp = false
|
|
31
|
+
@dynamic_jsonp_keys = ['callback']
|
|
32
|
+
@ignore_cache_port = true
|
|
33
|
+
@non_successful_cache_disabled = false
|
|
34
|
+
@non_successful_error_level = :warn
|
|
35
|
+
@non_whitelisted_requests_disabled = false
|
|
36
|
+
@cache_path = File.join(Dir.tmpdir, 'agile-proxy')
|
|
37
|
+
@proxy_port = RANDOM_AVAILABLE_PORT
|
|
38
|
+
@proxied_request_inactivity_timeout = 10 # defaults from https://github.com/igrigorik/em-http-request/wiki/Redirects-and-Timeouts
|
|
39
|
+
@proxied_request_connect_timeout = 5
|
|
40
|
+
@webserver_port = 3020
|
|
41
|
+
@webserver_host = 'localhost'
|
|
42
|
+
@database_config_file = File.join(File.dirname(__FILE__), '..', '..', 'config.yml')
|
|
43
|
+
@environment = ENV['AGILE_PROXY_ENV']
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Configures the system using a block which has the global instance of this config yielded
|
|
48
|
+
def self.configure
|
|
49
|
+
yield config if block_given?
|
|
50
|
+
config
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Common log method - sends the log to the appropriate place
|
|
54
|
+
def self.log(*args)
|
|
55
|
+
config.logger.send(*args) unless config.logger.nil?
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def self.config
|
|
61
|
+
@config ||= Config.new
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'rack'
|
|
2
|
+
module AgileProxy
|
|
3
|
+
# A mixin that all handlers must include
|
|
4
|
+
module Handler
|
|
5
|
+
##
|
|
6
|
+
#
|
|
7
|
+
# Handles an incoming rack request and returns a rack response.
|
|
8
|
+
#
|
|
9
|
+
# This method accepts rack request parameters and must return
|
|
10
|
+
# a rack response array containing [status, headers, content]
|
|
11
|
+
# , or [404, {}, ''] if the request cannot be fulfilled.
|
|
12
|
+
#
|
|
13
|
+
# @param _env [Hash] The rack environment
|
|
14
|
+
# @return [Array] An array of [status, headers, content]
|
|
15
|
+
# Returns status of 404 if the request cannot be fulfilled.
|
|
16
|
+
def call(_env)
|
|
17
|
+
[500, {}, 'The handler has not overridden the handle_request method!']
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def username_password(env)
|
|
23
|
+
Base64.decode64(env['HTTP_PROXY_AUTHORIZATION'].sub(/^Basic /, '')).split(':') if proxy_auth? env
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def proxy_auth?(env)
|
|
27
|
+
env.key?('HTTP_PROXY_AUTHORIZATION') && env['HTTP_PROXY_AUTHORIZATION'] =~ /^Basic /
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def downcase_header_name(name)
|
|
31
|
+
name.split(/_/).drop(1).map { |word| word.downcase.capitalize }.join('-')
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def downcased_headers(env)
|
|
35
|
+
headers = {}
|
|
36
|
+
env.each do |name, value|
|
|
37
|
+
next unless name =~ /^HTTP_/
|
|
38
|
+
headers[downcase_header_name(name)] = value
|
|
39
|
+
end
|
|
40
|
+
headers
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
require 'agile_proxy/handlers/handler'
|
|
2
|
+
require 'eventmachine'
|
|
3
|
+
require 'em-synchrony/em-http'
|
|
4
|
+
|
|
5
|
+
module AgileProxy
|
|
6
|
+
# = The handler used for proxying the request on to the original server
|
|
7
|
+
#
|
|
8
|
+
# This handler is responsible for proxying the request through to the original server and passing back its response
|
|
9
|
+
#
|
|
10
|
+
# It works as a rack end point as usual :-)
|
|
11
|
+
#
|
|
12
|
+
class ProxyHandler
|
|
13
|
+
include Handler
|
|
14
|
+
|
|
15
|
+
# The endpoint called by 'rack'
|
|
16
|
+
#
|
|
17
|
+
# Requests the response back from the destination server and passes it back to the client
|
|
18
|
+
#
|
|
19
|
+
# @param env [Hash] The rack environment hash
|
|
20
|
+
# @return [Array] The rack response array (status, headers, body)
|
|
21
|
+
def call(env)
|
|
22
|
+
request = ActionDispatch::Request.new(env)
|
|
23
|
+
method = request.request_method.downcase
|
|
24
|
+
body = request.body.read
|
|
25
|
+
request.body.rewind
|
|
26
|
+
url = request.url
|
|
27
|
+
if handles_request?(request)
|
|
28
|
+
req = EventMachine::HttpRequest.new(request.url,
|
|
29
|
+
inactivity_timeout: AgileProxy.config.proxied_request_inactivity_timeout,
|
|
30
|
+
connect_timeout: AgileProxy.config.proxied_request_connect_timeout
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
req = req.send(method.downcase, build_request_options(request.headers, body))
|
|
34
|
+
|
|
35
|
+
if req.error
|
|
36
|
+
return [500, {}, "Request to #{request.url} failed with error: #{req.error}"]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
if req.response
|
|
40
|
+
response = process_response(req)
|
|
41
|
+
|
|
42
|
+
unless allowed_response_code?(response[:status])
|
|
43
|
+
AgileProxy.log(:warn, "agile-proxy: Received response status code #{response[:status]} for '#{url}'")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
AgileProxy.log(:info, "agile-proxy: PROXY #{request.request_method} succeeded for '#{request.url}'")
|
|
47
|
+
return [response[:status], response[:headers], response[:content]]
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
[404, {}, 'Not proxied']
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def handles_request?(request)
|
|
56
|
+
!disabled_request?(request.url)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def build_request_options(headers, body)
|
|
60
|
+
headers = Hash[headers.map { |k, v| [k.downcase, v] }]
|
|
61
|
+
headers.delete('accept-encoding')
|
|
62
|
+
|
|
63
|
+
req_opts = {
|
|
64
|
+
redirects: 0,
|
|
65
|
+
keepalive: false,
|
|
66
|
+
head: headers,
|
|
67
|
+
ssl: { verify: false }
|
|
68
|
+
}
|
|
69
|
+
req_opts[:body] = body if body
|
|
70
|
+
req_opts
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def process_response(req)
|
|
74
|
+
response = {
|
|
75
|
+
status: req.response_header.status,
|
|
76
|
+
headers: req.response_header.raw,
|
|
77
|
+
content: req.response.force_encoding('BINARY') }
|
|
78
|
+
response[:headers].merge!('Connection' => 'close')
|
|
79
|
+
response[:headers].delete('Transfer-Encoding')
|
|
80
|
+
response
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def disabled_request?(url)
|
|
84
|
+
return false unless AgileProxy.config.non_whitelisted_requests_disabled
|
|
85
|
+
|
|
86
|
+
uri = URI(url)
|
|
87
|
+
# In isolated environments, you may want to stop the request from happening
|
|
88
|
+
# or else you get "getaddrinfo: Name or service not known" errors
|
|
89
|
+
blacklisted_path?(uri.path) || !whitelisted_url?(uri)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def allowed_response_code?(status)
|
|
93
|
+
successful_status?(status)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def whitelisted_url?(url)
|
|
97
|
+
!AgileProxy.config.whitelist.index do |v|
|
|
98
|
+
v =~ /^#{url.host}(?::#{url.port})?$/
|
|
99
|
+
end.nil?
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def blacklisted_path?(path)
|
|
103
|
+
!AgileProxy.config.path_blacklist.index { |bl| path.include?(bl) }.nil?
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def successful_status?(status)
|
|
107
|
+
(200..299).include?(status) || status == 304
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'agile_proxy/model/application'
|
|
2
|
+
require 'agile_proxy/model/recording'
|
|
3
|
+
require 'rack'
|
|
4
|
+
module AgileProxy
|
|
5
|
+
#
|
|
6
|
+
# =The Central Request Handler
|
|
7
|
+
#
|
|
8
|
+
# As a request is made from the client to the server via the proxy server, it comes through an instance of this class.
|
|
9
|
+
#
|
|
10
|
+
# This class will then pass the request on to the StubHandler, then finally the ProxyHandler
|
|
11
|
+
class RequestHandler
|
|
12
|
+
extend Forwardable
|
|
13
|
+
include Handler
|
|
14
|
+
|
|
15
|
+
def_delegators :stub_handler, :stub
|
|
16
|
+
|
|
17
|
+
# A rack endpoint
|
|
18
|
+
#
|
|
19
|
+
# This method is called as a rack endpoint and returns a rack response.
|
|
20
|
+
#
|
|
21
|
+
# @param env [Hash] The 'rack' environment
|
|
22
|
+
# @return [Array] The rack response (status, headers, content)
|
|
23
|
+
def call(env)
|
|
24
|
+
request = ActionDispatch::Request.new(env)
|
|
25
|
+
username, password = username_password env
|
|
26
|
+
application = Application.where(username: username, password: password).first
|
|
27
|
+
body = request.body.read
|
|
28
|
+
request.body.rewind
|
|
29
|
+
rack_response = rack_app.call(env)
|
|
30
|
+
if rack_response[0] == 404
|
|
31
|
+
rack_response = [
|
|
32
|
+
500,
|
|
33
|
+
{},
|
|
34
|
+
"Connection to #{request.url}#{body} not stubbed and new http connections are disabled"
|
|
35
|
+
]
|
|
36
|
+
end
|
|
37
|
+
if application.record_requests
|
|
38
|
+
application.recordings.create request_headers: request.headers,
|
|
39
|
+
request_body: body,
|
|
40
|
+
request_url: request.url,
|
|
41
|
+
request_method: request.request_method,
|
|
42
|
+
response_headers: rack_response[1],
|
|
43
|
+
response_body: rack_response[2],
|
|
44
|
+
response_status: rack_response[0]
|
|
45
|
+
end
|
|
46
|
+
rack_response
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def rack_app
|
|
52
|
+
@__app ||= Rack::Builder.new do
|
|
53
|
+
run Rack::Cascade.new([StubHandler.new, ProxyHandler.new])
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
require 'agile_proxy/handlers/handler'
|
|
2
|
+
require 'agile_proxy/model/request_spec'
|
|
3
|
+
require 'agile_proxy/router'
|
|
4
|
+
require 'agile_proxy/model/application'
|
|
5
|
+
require 'rack/parser'
|
|
6
|
+
require 'action_dispatch'
|
|
7
|
+
require 'base64'
|
|
8
|
+
module AgileProxy
|
|
9
|
+
# = The stub handler
|
|
10
|
+
#
|
|
11
|
+
# This class is resonsible for matching incoming requests
|
|
12
|
+
# with a stub (request spec) and if one exists, respond with it
|
|
13
|
+
# rather than the real response from the destination server.
|
|
14
|
+
class StubHandler
|
|
15
|
+
include Handler
|
|
16
|
+
|
|
17
|
+
# Called by 'rack' as an endpoint.
|
|
18
|
+
#
|
|
19
|
+
# Fetches a 'short list' of potential matching request specs and builds a routing table to allow
|
|
20
|
+
# a router to do the hard work of parsing parameters, matching them, matching URL's etc...
|
|
21
|
+
#
|
|
22
|
+
# @param env [Hash] The rack environment
|
|
23
|
+
# @return [Array] The rack response [status, headers, body]
|
|
24
|
+
def call(env)
|
|
25
|
+
request = ActionDispatch::Request.new(env)
|
|
26
|
+
username, password = username_password env
|
|
27
|
+
@route_set = ActionDispatch::Routing::RouteSet.new
|
|
28
|
+
|
|
29
|
+
my_short_list = short_list(username, password, request.request_method, request.url).all.to_a
|
|
30
|
+
headers = downcased_headers(env)
|
|
31
|
+
body = request.body.read
|
|
32
|
+
request.body.rewind
|
|
33
|
+
setup_router my_short_list, request, headers, body
|
|
34
|
+
rack_app.call(env)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def setup_router(my_short_list, request, headers, body)
|
|
40
|
+
me = self
|
|
41
|
+
@route_set.draw do
|
|
42
|
+
route_set_instance = self
|
|
43
|
+
me.instance_eval do
|
|
44
|
+
my_short_list.each(&add_to_router(request, headers, body, route_set_instance))
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def add_to_router(request, headers, body, route_set)
|
|
50
|
+
proc do |spec|
|
|
51
|
+
path = URI.parse(spec.url).path
|
|
52
|
+
path = '/' if path == ''
|
|
53
|
+
method = spec.http_method.downcase.to_sym
|
|
54
|
+
method = :get if method == :head
|
|
55
|
+
route_spec = {
|
|
56
|
+
path => proc do |router_env|
|
|
57
|
+
AgileProxy.log(:info, "agile-proxy: STUB #{method} for '#{request.url}'")
|
|
58
|
+
spec.call(router_env['action_dispatch.request.parameters'], headers, body)
|
|
59
|
+
end
|
|
60
|
+
}
|
|
61
|
+
route_spec[:constraints] = ActiveSupport::JSON.decode(spec.conditions).symbolize_keys unless spec.conditions.empty?
|
|
62
|
+
route_set.send method, route_spec
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def collection(username, password)
|
|
67
|
+
Application.where(username: username, password: password).first.request_specs
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def short_list(username, password, _method, url)
|
|
71
|
+
parsed_url = URI.parse(url)
|
|
72
|
+
parsed_url.path = ''
|
|
73
|
+
parsed_url.query = nil
|
|
74
|
+
parsed_url.fragment = nil
|
|
75
|
+
# @TODO This is being lazy - the ruby code will do all the finding work.
|
|
76
|
+
# This has to change as we go towards mutli user and larger data sets
|
|
77
|
+
collection(username, password).where('url LIKE ?', "#{parsed_url}%")
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def rack_app
|
|
81
|
+
route_set = @route_set
|
|
82
|
+
Rack::Builder.new do
|
|
83
|
+
use ActionDispatch::ParamsParser
|
|
84
|
+
use MergePostAndGetParams
|
|
85
|
+
run route_set
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
#
|
|
90
|
+
# @private
|
|
91
|
+
# A rack middleware to merge post and get parameters ready for routing
|
|
92
|
+
class MergePostAndGetParams
|
|
93
|
+
def initialize(app)
|
|
94
|
+
@app = app
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def call(env)
|
|
98
|
+
request = ActionDispatch::Request.new(env)
|
|
99
|
+
env['action_dispatch.request.parameters'].merge!(
|
|
100
|
+
env['action_dispatch.request.request_parameters']
|
|
101
|
+
) unless request.content_length.zero?
|
|
102
|
+
@app.call(env)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
module ActionDispatch
|
|
107
|
+
module Routing
|
|
108
|
+
# Extension to action dispatch to ensure all request parameters are collected not just GET
|
|
109
|
+
class RouteSet
|
|
110
|
+
PARAMETERS_KEY = 'action_dispatch.request.parameters'
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIIDojCCAooCCQCYeVsjl+UFxzANBgkqhkiG9w0BAQUFADCBkjELMAkGA1UEBhMC
|
|
3
|
+
VVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MSEwHwYDVQQK
|
|
4
|
+
ExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxFjAUBgNVBAMTDVB1ZmZpbmcgQmls
|
|
5
|
+
bHkxIzAhBgkqhkiG9w0BCQEWFG9sbHkuc21pdGhAZ21haWwuY29tMB4XDTEyMTAw
|
|
6
|
+
MjA2NDIxMVoXDTEzMTAwMjA2NDIxMVowgZIxCzAJBgNVBAYTAlVTMQswCQYDVQQI
|
|
7
|
+
EwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEhMB8GA1UEChMYSW50ZXJuZXQg
|
|
8
|
+
V2lkZ2l0cyBQdHkgTHRkMRYwFAYDVQQDEw1QdWZmaW5nIEJpbGx5MSMwIQYJKoZI
|
|
9
|
+
hvcNAQkBFhRvbGx5LnNtaXRoQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
|
|
10
|
+
ggEPADCCAQoCggEBALW7WQIIGCgUk28x1eIV0+VDG6SZV8dosJPU3WM4zLT0KLsv
|
|
11
|
+
2+5KNO373KyXhIDQqkv+4+1myVYj5wkvDSFMLYvBTNmypS7Xh6+wyKjQQ5wupXoB
|
|
12
|
+
BNrJw5CbfUX9Kwq31H6aO1ItWZ6QHjW4dpE3AOu7ohwwyyL2Q0QkzTeP5b6SofbV
|
|
13
|
+
9y1DpeH5L2an84LIjm1XrNbswY0p0CDT7TTsmYoXnw0MD5I73rT4arOOKWdVrD/G
|
|
14
|
+
lbvfcZN6c+nN713tGI9XfM5cQEXUsbSY4NKuF+jn7B2tsx+QRVwUyMwS8ZVpDt5q
|
|
15
|
+
DMBwROskWTMLKxeuxxpmjFWHlLuBlJ0M/rKII98CAwEAATANBgkqhkiG9w0BAQUF
|
|
16
|
+
AAOCAQEAMkhR7JDQKcYDy5Yb80cPIjw5kaEfTb+/Vh6DLLEtwEVIc2PcIvXCBZRo
|
|
17
|
+
79Esa42BaNEaWPxRVAs0Doubh4IdGk9qp9a/71gqhi3iNe//depX5VLf1+5LnQtV
|
|
18
|
+
BbFruXUkzOF4kykm5Fcf9yQ9W951d0qaT6K9LefSsaoMrQjgKCeFAcIOXjqHyWns
|
|
19
|
+
KAO34C1kzAFemMY20RcgZf9/+09Zs/ZiDBeoWTc+juaa9zCgvg/1FKHqsA/iLg6/
|
|
20
|
+
VcdOquI9fJ5hRTZFkPoFLpxf2O1H3IpyNXmpcJDFvKfh7wIcHkLWIg5BFXc2S4+v
|
|
21
|
+
bNFEKqipZH+D9++jXGyumxA8VoZOOA==
|
|
22
|
+
-----END CERTIFICATE-----
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
|
2
|
+
MIIEowIBAAKCAQEAtbtZAggYKBSTbzHV4hXT5UMbpJlXx2iwk9TdYzjMtPQouy/b
|
|
3
|
+
7ko07fvcrJeEgNCqS/7j7WbJViPnCS8NIUwti8FM2bKlLteHr7DIqNBDnC6legEE
|
|
4
|
+
2snDkJt9Rf0rCrfUfpo7Ui1ZnpAeNbh2kTcA67uiHDDLIvZDRCTNN4/lvpKh9tX3
|
|
5
|
+
LUOl4fkvZqfzgsiObVes1uzBjSnQINPtNOyZihefDQwPkjvetPhqs44pZ1WsP8aV
|
|
6
|
+
u99xk3pz6c3vXe0Yj1d8zlxARdSxtJjg0q4X6OfsHa2zH5BFXBTIzBLxlWkO3moM
|
|
7
|
+
wHBE6yRZMwsrF67HGmaMVYeUu4GUnQz+sogj3wIDAQABAoIBAEM8cF7vDbjue+m8
|
|
8
|
+
32wJNV9yJ60LSs2tLv9S1yHZpusgFl3DBDSyYcjW0TtNx6k9CnSZdkykJcNn/xeH
|
|
9
|
+
v+zc2VEGkF9O2Axvk3TuDB9hBlKnc3OjIt+rnF5JGN0nIKCTiNvaRi5ONwUSPwsT
|
|
10
|
+
F1L8rauJvR1+8/kYcaSplP+EjrSlvaKtUPNKfB8V6IktVLHiBUFs7nWoOGSaEB4/
|
|
11
|
+
8jlji4s8xzVammTRg175mhOkgqdI+cWfXnn8PhOJZ2XjZz5eFOWxdvNldBbsm9Yc
|
|
12
|
+
g3URB9FpOPMY8DvgZNrAYp6/7C9ACecAIZAnDK0/aQOa95UeeCEbmAv59F2OqOc2
|
|
13
|
+
35HUSrkCgYEA55e+o7pxUY7YPVopBz27JWANk/zwMN1ZgDlR87Q0ENmrjOB8kPmz
|
|
14
|
+
02SHuVoXMPj/SD/S4J1NY2o7u5ofSpxUsdofCETsCeCbqhshRLqCu7yCjGagHknY
|
|
15
|
+
M4dJdRoFPN03gy+qHB7EKT0U89lvwRsjShc8h56wXsRHP6qM2uVgGyUCgYEAyOJh
|
|
16
|
+
ZPKBa0WVG9NxFS4Ej7CDpmWA415PyCrTK9cA2LCA0sWXHAhJ9guUx4zlqwLkfYBr
|
|
17
|
+
mmobAKjllFOUiECFLs49Oy/vD9Kw/ga9KO6eU9E/j9XHLVYupkPEVWeM7jzaQBul
|
|
18
|
+
CvMcPGgXX/84Xgtjjim8RuAPIgmRHSDfqRFUtbMCgYEAmQwxEiZuKMXLpY/luUFU
|
|
19
|
+
Yfi+QGRRnxlIwnIe9HzMQ651rl3UNEKwUi0HfLhKxzRmECsNgx6xO9fCrdHGiBoT
|
|
20
|
+
5o0NIPvbORPUC3BuZesT5llHtN1FR37pf/QR2W9essBGpU1kj7zNSatyI0w4jFcQ
|
|
21
|
+
1S/R8pYuXBI+O5bMCwS2pHkCgYBqFKG53RXav/PtrcqZlKNz/ZKH3DIj3zniSjsZ
|
|
22
|
+
e4BG7W4Z353cf8QO2i7G8fCWTgC7BYXNFRsNTiNuIHTfPrMV9HMBPl7PzEMK4iQh
|
|
23
|
+
6WBSgr0+B3YWytv3kPGs5/HUHO5jzDVrgtX2UEGHwA7UGs+H0yJJiyhyoPqwlxuE
|
|
24
|
+
/FHvYQKBgEJaI/DaSpxJotq56lBHzTte1icbD8IFXMlbmWyIFxONWxlz98tQ2ypp
|
|
25
|
+
WhMGIzMsFE1X+P8vRbc865t36UMpucjRuAJ3MTeeCa60EghmdfajzA2ZWh/MnEyA
|
|
26
|
+
yvTr72g5gj5Yi/Zbiy1xeTo3UeaBxiCi8xyWAGodYg8891UVKqJu
|
|
27
|
+
-----END RSA PRIVATE KEY-----
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require_relative 'user'
|
|
2
|
+
require_relative 'recording'
|
|
3
|
+
module AgileProxy
|
|
4
|
+
#
|
|
5
|
+
# = The Application Model
|
|
6
|
+
#
|
|
7
|
+
# The application model is responsible for storing and retrieving information about the application
|
|
8
|
+
# that the application under test / development is using.
|
|
9
|
+
#
|
|
10
|
+
# This is found using the username and password, therefore, a username and password must be globally unique.
|
|
11
|
+
#
|
|
12
|
+
# The reason for doing it this way is because we can only pass username and password in the URL for the proxy server
|
|
13
|
+
#
|
|
14
|
+
class Application < ActiveRecord::Base
|
|
15
|
+
belongs_to :user
|
|
16
|
+
has_many :request_specs
|
|
17
|
+
has_many :recordings
|
|
18
|
+
validates_uniqueness_of :password, scope: :username
|
|
19
|
+
end
|
|
20
|
+
end
|