pact_broker 1.17.2 → 1.18.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ddeb15f7e5f35bcccea163c2bdb0f96f0f76904e
4
- data.tar.gz: 3eec1a584c7dc3bf0669a08e983a620dd94950c6
3
+ metadata.gz: c780083d99fd7a3c73e53f16b703c3bfe5b86a78
4
+ data.tar.gz: aa8ef0eb97c2e18461179607d395c7b493e9f50e
5
5
  SHA512:
6
- metadata.gz: f47149c6ca610fcc5e76222f25b69cf78b53928d536459ed5b854bd63b8127bf2df8fe940285169c430c064fc850072430774a0408c93d88022520c3c6f0aef1
7
- data.tar.gz: 66e8590f37514286d4dcc04efdbeef38c1ad54e9b20a801307e1e9e908dfc5ff53ebea473e5c197172a46ed56bf16b3ffe419a7e22171656c9a508cfea946c9b
6
+ metadata.gz: 63db04af90da66ce5833f9f2de88f734529f94c7ebb759ffe4a8eb89f5a7cf5b439c221bf5464bf4d499f5e326038668dd58e5ebbff6f9db31ab7a9b92669861
7
+ data.tar.gz: 261ccb52f5819dc9d9eee9c1088bff11c0d6fb3269492d68e1de25f5687e66adc7a814a43ad7c69c8e3b883ec10ca5e0bf865a0e46bf5f515011384057e402fd
data/CHANGELOG.md CHANGED
@@ -2,6 +2,9 @@ Do this to generate your change history
2
2
 
3
3
  $ git log --pretty=format:' * %h - %s (%an, %ad)' vX.Y.Z..HEAD
4
4
 
5
+ #### 1.18.0.beta.1 (2017-05-05)
6
+ * 99e825b - Add in-built configuration for basic auth. (Beth Skurrie, Fri May 5 10:22:53 2017 +1000)
7
+
5
8
  #### 1.17.2 (2017-05-04)
6
9
  * b8f45e1 - fix issue with pact document link not displaying #94 (Matt Fellows, Wed May 3 11:23:09 2017 +1000)
7
10
 
data/example/config.ru CHANGED
@@ -27,6 +27,8 @@ app = PactBroker::App.new do | config |
27
27
  # config.use_hal_browser = true
28
28
  config.database_connection = Sequel.connect(DATABASE_CREDENTIALS.merge(:logger => config.logger))
29
29
  config.database_connection.timezone = :utc
30
+ # See configuration section of wiki for more basic auth configuration options
31
+ # config.protect_with_basic_auth :app_read, {username: 'username', password: 'password'}
30
32
  end
31
33
 
32
34
  run app
File without changes
File without changes
@@ -27,7 +27,7 @@ $ git push heroku master
27
27
  Your Pact Broker instance is now available!
28
28
 
29
29
  ## Publish consumer pacts - consumer side
30
- You will need to set these environment variables with your basic auth credentials
30
+ You will need to set these environment variables with your basic auth credentials
31
31
  ```
32
32
  export PACT_BROKER_USERNAME=admin
33
33
  export PACT_BROKER_PASSWORD=changeme
@@ -0,0 +1,12 @@
1
+ require 'fileutils'
2
+ require 'logger'
3
+ require 'sequel'
4
+ require 'pact_broker'
5
+ require 'pg'
6
+
7
+ app = PactBroker::App.new do | config |
8
+ config.database_connection = Sequel.connect(ENV['DATABASE_URL'], adapter: "postgres", encoding: 'utf8')
9
+ config.protect_with_basic_auth :all, {username: ENV['PACT_BROKER_USERNAME'], password: ENV['PACT_BROKER_PASSWORD']}
10
+ end
11
+
12
+ run app
@@ -3,6 +3,7 @@ require 'pact_broker/db'
3
3
  require 'pact_broker/project_root'
4
4
  require 'rack/hal_browser'
5
5
  require 'rack/pact_broker/convert_file_extension_to_accept_header'
6
+ require 'pact_broker/configuration/configure_basic_auth'
6
7
 
7
8
  module PactBroker
8
9
 
@@ -70,12 +71,12 @@ module PactBroker
70
71
  apps << PactBroker::UI::App.new
71
72
  apps << PactBroker::API
72
73
 
74
+ cascade = Rack::Cascade.new(apps)
75
+ app_with_basic_auth = PactBroker::Configuration::ConfigureBasicAuth.call(cascade, configuration)
76
+
73
77
  @app.map "/" do
74
- run Rack::Cascade.new(apps)
78
+ run app_with_basic_auth
75
79
  end
76
-
77
80
  end
78
-
79
81
  end
80
-
81
- end
82
+ end
@@ -4,13 +4,33 @@ module PactBroker
4
4
  @@configuration ||= Configuration.default_configuration
5
5
  end
6
6
 
7
+ def self.reset_configuration
8
+ @@configuration = Configuration.default_configuration
9
+ end
10
+
7
11
  class Configuration
8
12
 
13
+ REQUEST_METHOD = 'REQUEST_METHOD'.freeze
14
+ GET = 'GET'.freeze
15
+ PATH_INFO = 'PATH_INFO'.freeze
16
+ DIAGNOSTIC = '/diagnostic/'.freeze
17
+
9
18
  attr_accessor :log_dir, :database_connection, :auto_migrate_db, :use_hal_browser, :html_pact_renderer
10
19
  attr_accessor :validate_database_connection_config, :enable_diagnostic_endpoints, :version_parser
11
- attr_accessor :use_case_sensitive_resource_names
20
+ attr_accessor :use_case_sensitive_resource_names, :basic_auth_predicates
12
21
  attr_writer :logger
13
22
 
23
+ def initialize
24
+ @basic_auth_config = {}
25
+ @basic_auth_predicates = [
26
+ [:diagnostic, ->(env) { env[PATH_INFO].start_with? DIAGNOSTIC }],
27
+ [:app, ->(env) { !env[PATH_INFO].start_with? DIAGNOSTIC } ],
28
+ [:app_read, ->(env) { env[REQUEST_METHOD] == GET }],
29
+ [:app_write, ->(env) { env[REQUEST_METHOD] != GET }],
30
+ [:all, ->(env) { true }]
31
+ ]
32
+ end
33
+
14
34
  def logger
15
35
  @logger ||= create_logger log_path
16
36
  end
@@ -29,6 +49,7 @@ module PactBroker
29
49
  config
30
50
  end
31
51
 
52
+ # public
32
53
  def self.default_html_pact_render
33
54
  lambda { |pact|
34
55
  require 'pact_broker/api/renderers/html_pact_renderer'
@@ -36,8 +57,28 @@ module PactBroker
36
57
  }
37
58
  end
38
59
 
60
+ # public
61
+ def protect_with_basic_auth scopes, credentials
62
+ [*scopes].each do | scope |
63
+ basic_auth_config[scope] ||= []
64
+ basic_auth_config[scope] << credentials
65
+ end
66
+ end
67
+
68
+ # private
69
+ def protect_with_basic_auth? scope
70
+ !!basic_auth_credentials_list_for(scope)
71
+ end
72
+
73
+ # private
74
+ def basic_auth_credentials_list_for scope
75
+ basic_auth_config[scope]
76
+ end
77
+
39
78
  private
40
79
 
80
+ attr_reader :basic_auth_config
81
+
41
82
  def create_logger path
42
83
  FileUtils::mkdir_p File.dirname(path)
43
84
  logger = Logger.new(path)
@@ -0,0 +1,83 @@
1
+ require 'pact_broker/configuration'
2
+
3
+ module PactBroker
4
+ class Configuration
5
+
6
+ class ConfigurableBasicAuth
7
+
8
+ def initialize(app)
9
+ @app = app
10
+ @predicates = []
11
+ end
12
+
13
+ def protect credentials_list, &predicate
14
+ basic_auth_proxy = ::Rack::Auth::Basic.new(app) do | username, password |
15
+ credentials_list.any? do | credentials |
16
+ username == credentials[:username] && password == credentials[:password]
17
+ end
18
+ end
19
+ predicates << [predicate, basic_auth_proxy]
20
+ end
21
+
22
+ def call(env)
23
+ predicates = matching_predicates(env)
24
+ if predicates.any?
25
+ cascade(predicates, env)
26
+ else
27
+ app.call(env)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ attr_accessor :app, :predicates
34
+
35
+ def matching_predicates env
36
+ predicates.select do | predicate, basic_auth_proxy |
37
+ predicate.call(env)
38
+ end
39
+ end
40
+
41
+ def cascade predicates, env
42
+ response = nil
43
+ predicates.each do | predicate, basic_auth_proxy |
44
+ response = basic_auth_proxy.call(env)
45
+ return response if response.first != 401
46
+ end
47
+ response
48
+ end
49
+
50
+ end
51
+
52
+ class ConfigureBasicAuth
53
+
54
+ def self.call app, configuration
55
+ new(app, configuration).call
56
+ end
57
+
58
+ def initialize app, configuration
59
+ @configuration = configuration
60
+ @basic_auth_proxy = ConfigurableBasicAuth.new(app)
61
+ end
62
+
63
+ def call
64
+ configuration.basic_auth_predicates.each do | scope, predicate |
65
+ configure_basic_auth_for_scope scope, &predicate
66
+ end
67
+
68
+ basic_auth_proxy
69
+ end
70
+
71
+ private
72
+
73
+ attr_accessor :basic_auth_proxy, :configuration
74
+
75
+ def configure_basic_auth_for_scope scope, &predicate
76
+ if configuration.protect_with_basic_auth?(scope)
77
+ credentials = configuration.basic_auth_credentials_list_for(scope)
78
+ basic_auth_proxy.protect(credentials, &predicate)
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = '1.17.2'
2
+ VERSION = '1.18.0.beta.1'
3
3
  end
@@ -0,0 +1,267 @@
1
+ require 'pact_broker/configuration/configure_basic_auth'
2
+ require 'pact_broker/app'
3
+
4
+ module PactBroker
5
+ class Configuration
6
+ describe ConfigurableBasicAuth do
7
+
8
+ let(:target_app) { ->(env){ [200, {}, ["hello"]] } }
9
+ let(:app) do
10
+ auth_app = ConfigurableBasicAuth.new(target_app)
11
+ auth_app.protect([{username: 'username', password: 'password'}]) do | env |
12
+ env['PATH_INFO'] == '/foo'
13
+ end
14
+ auth_app
15
+ end
16
+
17
+ context "when not authorized" do
18
+ it "does not allow requests" do
19
+ get "/foo"
20
+ expect(last_response.status).to eq 401
21
+ end
22
+ end
23
+
24
+ context "when authorized" do
25
+
26
+ before { basic_authorize 'username', 'password' }
27
+
28
+ it "allows requests" do
29
+ get "/foo"
30
+ expect(last_response.status).to eq 200
31
+ end
32
+ end
33
+ end
34
+
35
+ describe ConfigureBasicAuth do
36
+ def read_request_to_ui
37
+ get "/"
38
+ last_response
39
+ end
40
+
41
+ def read_request_to_api
42
+ get "/pacts/latest"
43
+ last_response
44
+ end
45
+
46
+ def write_request_to_api
47
+ put "/pacts/provider/foo/consumer/bar/version/1.2.3", '{}', {'Content-Type' => 'application/json'}
48
+ last_response
49
+ end
50
+
51
+ def read_request_to_diagnostic
52
+ get "/diagnostic/status/heartbeat"
53
+ last_response
54
+ end
55
+
56
+ def authorize_request
57
+ basic_authorize 'username', 'password'
58
+ end
59
+
60
+
61
+ before do
62
+ PactBroker.reset_configuration
63
+ end
64
+
65
+ describe "with no basic_auth" do
66
+ let(:app) do
67
+ app = PactBroker::App.new do | config |
68
+ config.database_connection = PactBroker::DB.connection
69
+ end
70
+ end
71
+
72
+ context "when not authorized" do
73
+ it "allows GET requests to the UI" do
74
+ expect(read_request_to_ui.status).to_not eq 401
75
+ end
76
+
77
+ it "allows GET requests to the API" do
78
+ expect(read_request_to_api.status).to_not eq 401
79
+ end
80
+
81
+ it "allows non GET requests to the API" do
82
+ expect(write_request_to_api.status).to_not eq 401
83
+ end
84
+
85
+ it "allows GET requests to the diagnostics app" do
86
+ expect(read_request_to_diagnostic.status).to_not eq 401
87
+ end
88
+ end
89
+ end
90
+
91
+ describe "with basic_auth for :all" do
92
+ let(:app) do
93
+ app = PactBroker::App.new do | config |
94
+ config.database_connection = PactBroker::DB.connection
95
+ config.protect_with_basic_auth :all, {username: 'username', password: 'password'}
96
+ end
97
+ end
98
+
99
+ context "when authorized" do
100
+ before do
101
+ authorize_request
102
+ end
103
+
104
+ it "allows GET requests to the UI" do
105
+ expect(read_request_to_ui.status).to_not eq 401
106
+ end
107
+
108
+ it "allows GET requests to the API" do
109
+ expect(read_request_to_api.status).to_not eq 401
110
+ end
111
+
112
+ it "allows non GET requests to the API" do
113
+ expect(write_request_to_api.status).to_not eq 401
114
+ end
115
+
116
+ it "allows GET requests to the diagnostics app" do
117
+ expect(read_request_to_diagnostic.status).to_not eq 401
118
+ end
119
+ end
120
+
121
+ context "when not authorized" do
122
+ it "does not allow GET requests to the UI" do
123
+ expect(read_request_to_ui.status).to eq 401
124
+ end
125
+
126
+ it "does not allow GET requests the API" do
127
+ expect(read_request_to_api.status).to eq 401
128
+ end
129
+
130
+ it "does not allow non GET requests the API" do
131
+ expect(write_request_to_api.status).to eq 401
132
+ end
133
+
134
+ it "does not allow GET requests to the diagnostics app" do
135
+ expect(read_request_to_diagnostic.status).to eq 401
136
+ end
137
+ end
138
+ end
139
+
140
+ describe "with basic_auth for :app_write" do
141
+
142
+ let(:app) do
143
+ app = PactBroker::App.new do | config |
144
+ config.database_connection = PactBroker::DB.connection
145
+ config.protect_with_basic_auth :app_write, {username: 'username', password: 'password'}
146
+ end
147
+ end
148
+
149
+ context "when authorized" do
150
+ before do
151
+ authorize_request
152
+ end
153
+
154
+ it "allows GET requests to the diagnostics app" do
155
+ expect(read_request_to_diagnostic.status).to_not eq 401
156
+ end
157
+
158
+ it "allows GET requests to the app_write app" do
159
+ expect(read_request_to_diagnostic.status).to_not eq 401
160
+ end
161
+ end
162
+
163
+ context "when not authorized" do
164
+
165
+ it "allows GET requests to the UI" do
166
+ expect(read_request_to_ui.status).to_not eq 401
167
+ end
168
+
169
+ it "allows GET requests the API" do
170
+ expect(read_request_to_api.status).to_not eq 401
171
+ end
172
+
173
+ it "does not allow non GET requests the API" do
174
+ expect(write_request_to_api.status).to eq 401
175
+ end
176
+
177
+ it "allows GET requests to the diagnostics app" do
178
+ expect(read_request_to_diagnostic.status).to_not eq 401
179
+ end
180
+ end
181
+ end
182
+
183
+ describe "with multiple users for for :app_write" do
184
+ let(:app) do
185
+ app = PactBroker::App.new do | config |
186
+ config.database_connection = PactBroker::DB.connection
187
+ config.protect_with_basic_auth :app_write, {username: 'read_username', password: 'password'}
188
+ config.protect_with_basic_auth :app_write, {username: 'another_read_username', password: 'password'}
189
+ end
190
+ end
191
+
192
+ context "when the first credentials are used" do
193
+ before { basic_authorize 'read_username', 'password' }
194
+
195
+ it "allows a request" do
196
+ write_request_to_api
197
+ expect(last_response.status).to_not eq 401
198
+ end
199
+ end
200
+
201
+ context "when the second credentials are used" do
202
+ before { basic_authorize 'another_read_username', 'password' }
203
+
204
+ it "allows a request" do
205
+ write_request_to_api
206
+ expect(last_response.status).to_not eq 401
207
+ end
208
+ end
209
+
210
+ context "when the wrong credentials are used" do
211
+ before { basic_authorize 'wrong_username', 'password' }
212
+
213
+ it "does not allow the request" do
214
+ write_request_to_api
215
+ expect(last_response.status).to eq 401
216
+ end
217
+ end
218
+
219
+ context "when no credentials are used" do
220
+ it "does not allow the request" do
221
+ write_request_to_api
222
+ expect(last_response.status).to eq 401
223
+ end
224
+ end
225
+
226
+ end
227
+
228
+ describe "with an app_read user and an app user" do
229
+ let(:app) do
230
+ app = PactBroker::App.new do | config |
231
+ config.database_connection = PactBroker::DB.connection
232
+ config.protect_with_basic_auth :app_read, {username: 'read_username', password: 'password'}
233
+ config.protect_with_basic_auth :app, {username: 'read_and_write_username', password: 'password'}
234
+ end
235
+ end
236
+
237
+ context "when the app credentials are used" do
238
+ before { basic_authorize 'read_and_write_username', 'password' }
239
+
240
+ it "allows a read request" do
241
+ read_request_to_api
242
+ expect(last_response.status).to_not eq 401
243
+ end
244
+
245
+ it "allows a write request" do
246
+ write_request_to_api
247
+ expect(last_response.status).to_not eq 401
248
+ end
249
+ end
250
+
251
+ context "when the read_username credentials are used" do
252
+ before { basic_authorize 'read_username', 'password' }
253
+
254
+ it "allows a read request" do
255
+ read_request_to_api
256
+ expect(last_response.status).to_not eq 401
257
+ end
258
+
259
+ it "does not allow a write request" do
260
+ write_request_to_api
261
+ expect(last_response.status).to eq 401
262
+ end
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end
@@ -7,15 +7,35 @@ module PactBroker
7
7
 
8
8
  context "default configuration" do
9
9
  describe ".html_pact_renderer" do
10
-
11
10
  let(:pact) { double('pact') }
12
11
 
13
12
  it "calls the inbuilt HtmlPactRenderer" do
14
13
  expect(PactBroker::Api::Renderers::HtmlPactRenderer).to receive(:call).with(pact)
15
14
  PactBroker.configuration.html_pact_renderer.call pact
16
15
  end
16
+ end
17
+
18
+ describe "protect_with_basic_auth" do
19
+ let(:config) do
20
+ config = Configuration.new
21
+ config.protect_with_basic_auth [:foo, :bar], {some: 'credentials'}
22
+ config.protect_with_basic_auth :foo, {some: 'othercredentials'}
23
+ config
24
+ end
17
25
 
26
+ it "groups credentials by scope" do
27
+ expect(config.basic_auth_credentials_list_for(:foo)).to eq([{some: 'credentials'},{some: 'othercredentials'}])
28
+ expect(config.basic_auth_credentials_list_for(:bar)).to eq([{some: 'credentials'}])
29
+ end
30
+
31
+ describe "protect_with_basic_auth?" do
32
+ it "indicates whether a scope is protected" do
33
+ expect(config.protect_with_basic_auth?(:foo)).to be true
34
+ expect(config.protect_with_basic_auth?(:bar)).to be true
35
+ expect(config.protect_with_basic_auth?(:wiffle)).to be false
36
+ end
37
+ end
18
38
  end
19
39
  end
20
40
  end
21
- end
41
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pact_broker
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.17.2
4
+ version: 1.18.0.beta.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bethany Skurrie
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2017-05-04 00:00:00.000000000 Z
13
+ date: 2017-05-05 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: httparty
@@ -395,11 +395,11 @@ files:
395
395
  - db/migrations/migration_helper.rb
396
396
  - db/pact_broker_database.sqlite3
397
397
  - example/Gemfile
398
- - example/basic_auth/Gemfile
399
- - example/basic_auth/Procfile
400
- - example/basic_auth/README.md
401
- - example/basic_auth/config.ru
402
398
  - example/config.ru
399
+ - example/heroku/Gemfile
400
+ - example/heroku/Procfile
401
+ - example/heroku/README.md
402
+ - example/heroku/config.ru
403
403
  - example/pact_broker_database.sqlite3
404
404
  - lib/db.rb
405
405
  - lib/pact_broker.rb
@@ -462,6 +462,7 @@ files:
462
462
  - lib/pact_broker/api/resources/webhooks.rb
463
463
  - lib/pact_broker/app.rb
464
464
  - lib/pact_broker/configuration.rb
465
+ - lib/pact_broker/configuration/configure_basic_auth.rb
465
466
  - lib/pact_broker/constants.rb
466
467
  - lib/pact_broker/date_helper.rb
467
468
  - lib/pact_broker/db.rb
@@ -633,6 +634,7 @@ files:
633
634
  - spec/lib/pact_broker/api/resources/webhook_execution_spec.rb
634
635
  - spec/lib/pact_broker/api/resources/webhook_spec.rb
635
636
  - spec/lib/pact_broker/api/resources/webhooks_spec.rb
637
+ - spec/lib/pact_broker/configuration/configure_basic_auth_spec.rb
636
638
  - spec/lib/pact_broker/configuration_spec.rb
637
639
  - spec/lib/pact_broker/db/validate_encoding_spec.rb
638
640
  - spec/lib/pact_broker/diagnostic/resources/dependencies_spec.rb
@@ -730,9 +732,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
730
732
  version: 2.2.0
731
733
  required_rubygems_version: !ruby/object:Gem::Requirement
732
734
  requirements:
733
- - - ">="
735
+ - - ">"
734
736
  - !ruby/object:Gem::Version
735
- version: '0'
737
+ version: 1.3.1
736
738
  requirements: []
737
739
  rubyforge_project:
738
740
  rubygems_version: 2.6.11
@@ -795,6 +797,7 @@ test_files:
795
797
  - spec/lib/pact_broker/api/resources/webhook_execution_spec.rb
796
798
  - spec/lib/pact_broker/api/resources/webhook_spec.rb
797
799
  - spec/lib/pact_broker/api/resources/webhooks_spec.rb
800
+ - spec/lib/pact_broker/configuration/configure_basic_auth_spec.rb
798
801
  - spec/lib/pact_broker/configuration_spec.rb
799
802
  - spec/lib/pact_broker/db/validate_encoding_spec.rb
800
803
  - spec/lib/pact_broker/diagnostic/resources/dependencies_spec.rb
@@ -1,19 +0,0 @@
1
- require 'fileutils'
2
- require 'logger'
3
- require 'sequel'
4
- require 'pact_broker'
5
- require 'pg'
6
-
7
- use Rack::Auth::Basic, "Restricted Area" do |username, password|
8
- username == ENV['PACT_BROKER_USERNAME'] and password == ENV['PACT_BROKER_PASSWORD']
9
- end
10
-
11
- app = PactBroker::App.new do | config |
12
- # change these from their default values if desired
13
- # config.log_dir = "./log"
14
- # config.auto_migrate_db = true
15
- # config.use_hal_browser = true
16
- config.database_connection = Sequel.connect(ENV['DATABASE_URL'], adapter: "postgres", encoding: 'utf8')
17
- end
18
-
19
- run app