gds-sso 17.1.1 → 18.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +0 -6
- data/lib/gds-sso/railtie.rb +1 -0
- data/lib/gds-sso/version.rb +1 -1
- data/spec/internal/app/controllers/example_controller.rb +5 -4
- data/spec/internal/config/initializers/gds-sso.rb +1 -1
- data/spec/internal/config/routes.rb +2 -3
- data/spec/spec_helper.rb +13 -7
- data/spec/system/authentication_and_authorisation_spec.rb +115 -0
- metadata +38 -56
- data/spec/fixtures/integration/authorize_api_users.sql +0 -7
- data/spec/fixtures/integration/signon.sql +0 -26
- data/spec/internal/config/storage.yml +0 -3
- data/spec/internal/public/favicon.ico +0 -0
- data/spec/requests/end_to_end_spec.rb +0 -215
- data/spec/support/backport_controller_test_params.rb +0 -21
- data/spec/support/signon_integration_helpers.rb +0 -56
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6450ab2114242674c0c5f9ddeba9d15cca06e538f2f211a1c621469f9fd2d9af
|
4
|
+
data.tar.gz: eedc4aaa3abd89833c43af03c841f63dc6d68f9fbbd965a15aa917bc20ad933a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cfe330d97944bac31202779575770107056ca493ac5af511eb5fb316bbd4a83b459367dd683d69c01656c3e446177dabd008e47cf6d83138f29f139b7e765374
|
7
|
+
data.tar.gz: 01b11032563802c91717c6418150dafadd4518f95845c938bb6ad855895971d541f1a95d59cddae92060e4896ec6fdea6620af24a72515cd9fb79074d705c3c3
|
data/README.md
CHANGED
@@ -177,12 +177,6 @@ Run the tests with:
|
|
177
177
|
bundle exec rake
|
178
178
|
```
|
179
179
|
|
180
|
-
By default, the tests use the master of [Signon](https://github.com/alphagov/signon) for running integration tests. If you want to use a branch (or commit, or tag), you can run it like this:
|
181
|
-
|
182
|
-
```
|
183
|
-
SIGNON_COMMITISH=my_branch_name bundle exec rake
|
184
|
-
```
|
185
|
-
|
186
180
|
## Licence
|
187
181
|
|
188
182
|
[MIT License](LICENCE)
|
data/lib/gds-sso/railtie.rb
CHANGED
data/lib/gds-sso/version.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
class ExampleController < ApplicationController
|
2
|
-
before_action :authenticate_user!,
|
2
|
+
before_action :authenticate_user!, except: :not_restricted
|
3
|
+
before_action -> { authorise_user!("execute") }, only: :this_requires_execute_permission
|
3
4
|
|
4
|
-
def
|
5
|
+
def not_restricted
|
5
6
|
render body: "jabberwocky"
|
6
7
|
end
|
7
8
|
|
@@ -9,7 +10,7 @@ class ExampleController < ApplicationController
|
|
9
10
|
render body: "restricted kablooie"
|
10
11
|
end
|
11
12
|
|
12
|
-
def
|
13
|
-
render body: "you have
|
13
|
+
def this_requires_execute_permission
|
14
|
+
render body: "you have execute permission"
|
14
15
|
end
|
15
16
|
end
|
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
Rails.application.routes.draw do
|
4
|
-
|
5
|
-
root to: "example#index"
|
4
|
+
get "/not-restricted" => "example#not_restricted"
|
6
5
|
get "/restricted" => "example#restricted"
|
7
|
-
get "/
|
6
|
+
get "/this-requires-execute-permission" => "example#this_requires_execute_permission"
|
8
7
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -2,22 +2,28 @@
|
|
2
2
|
# Bad things happen if we don't ;-)
|
3
3
|
ENV["GDS_SSO_STRATEGY"] = "real"
|
4
4
|
|
5
|
-
require "bundler/setup"
|
6
|
-
require "combustion"
|
7
5
|
require "capybara/rspec"
|
6
|
+
require "webmock/rspec"
|
7
|
+
require "combustion"
|
8
8
|
|
9
|
-
Combustion.initialize! :all
|
9
|
+
Combustion.initialize! :all do
|
10
|
+
config.cache_store = :null_store
|
11
|
+
end
|
10
12
|
|
11
13
|
require "rspec/rails"
|
12
14
|
require "capybara/rails"
|
13
|
-
|
14
|
-
require "capybara/mechanize"
|
15
|
+
WebMock.disable_net_connect!
|
15
16
|
|
16
17
|
Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")].sort.each { |f| require f }
|
17
18
|
|
19
|
+
Capybara.register_driver :rack_test do |app|
|
20
|
+
Capybara::RackTest::Driver.new(app, follow_redirects: false)
|
21
|
+
end
|
22
|
+
|
18
23
|
RSpec.configure do |config|
|
19
24
|
config.run_all_when_everything_filtered = true
|
20
25
|
config.filter_run :focus
|
26
|
+
config.use_transactional_fixtures = true
|
21
27
|
|
22
28
|
# Run specs in random order to surface order dependencies. If you find an
|
23
29
|
# order dependency and want to debug it, you can fix the order by providing
|
@@ -25,6 +31,6 @@ RSpec.configure do |config|
|
|
25
31
|
# --seed 1234
|
26
32
|
config.order = "random"
|
27
33
|
|
28
|
-
config.include
|
29
|
-
config.include
|
34
|
+
config.include Warden::Test::Helpers
|
35
|
+
config.include Capybara::DSL
|
30
36
|
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe "Authenication and authorisation" do
|
4
|
+
context "when accessing a route that doesn't require permissions or authentication" do
|
5
|
+
it "allows access" do
|
6
|
+
visit "/not-restricted"
|
7
|
+
expect(page).to have_content("jabberwocky")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context "when accessing a route that requires authentication" do
|
12
|
+
it "redirects an unauthenticated request to signon" do
|
13
|
+
# We manually follow the redirects because we have configured capybara
|
14
|
+
# to not follow redirects (and thus allow testing an external redirect)
|
15
|
+
visit "/restricted"
|
16
|
+
expect(page.response_headers["Location"]).to match("/auth/gds")
|
17
|
+
visit page.response_headers["Location"]
|
18
|
+
expect(page.response_headers["Location"]).to match("http://signon/oauth/authorize")
|
19
|
+
end
|
20
|
+
|
21
|
+
it "allows access for an authenticated user" do
|
22
|
+
stub_signon_authenticated
|
23
|
+
|
24
|
+
visit "/restricted"
|
25
|
+
expect(page).to have_content("restricted kablooie")
|
26
|
+
end
|
27
|
+
|
28
|
+
it "restricts access if a user is authenticated but remotely signed out" do
|
29
|
+
stub_signon_authenticated
|
30
|
+
User.last.set_remotely_signed_out!
|
31
|
+
|
32
|
+
visit "/restricted"
|
33
|
+
expect(page.status_code).to eql(302)
|
34
|
+
expect(page.response_headers["Location"]).to match("/auth/gds")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "restricts access if a user is authenticated but session has expired" do
|
38
|
+
stub_signon_authenticated
|
39
|
+
|
40
|
+
Timecop.travel(Time.now.utc + GDS::SSO::Config.auth_valid_for + 5.minutes) do
|
41
|
+
visit "/restricted"
|
42
|
+
expect(page.status_code).to eql(302)
|
43
|
+
expect(page.response_headers["Location"]).to match("/auth/gds")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "allows access when given a valid bearer token" do
|
48
|
+
stub_signon_user_request
|
49
|
+
page.driver.header("Authorization", "Bearer 123")
|
50
|
+
|
51
|
+
visit "/restricted"
|
52
|
+
expect(page).to have_content("restricted kablooie")
|
53
|
+
end
|
54
|
+
|
55
|
+
it "restricts access when given an invalid bearer token" do
|
56
|
+
stub_request(:get, "http://signon/user.json?client_id=gds-sso-test")
|
57
|
+
.to_return(status: 401)
|
58
|
+
page.driver.header("Authorization", "Bearer 123")
|
59
|
+
|
60
|
+
visit "/restricted"
|
61
|
+
expect(page.status_code).to eq(401)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "returns a 401 when a bearer token is missing and the app is api_only" do
|
65
|
+
allow(GDS::SSO::Config).to receive(:api_only).and_return(true)
|
66
|
+
|
67
|
+
visit "/restricted"
|
68
|
+
expect(page.status_code).to eq(401)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "when accessing a route that requires a permission" do
|
73
|
+
it "allows access when an authenticated user has the permission" do
|
74
|
+
stub_signon_authenticated(permissions: %w[execute])
|
75
|
+
visit "/this-requires-execute-permission"
|
76
|
+
expect(page).to have_content("you have execute permission")
|
77
|
+
end
|
78
|
+
|
79
|
+
it "restricts access when an authenticated user lacks the permission" do
|
80
|
+
stub_signon_authenticated
|
81
|
+
visit "/this-requires-execute-permission"
|
82
|
+
expect(page.status_code).to eq(403)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def stub_signon_authenticated(permissions: [])
|
87
|
+
# visit restricted page to trigger redirect URL to record state attribute
|
88
|
+
visit "/auth/gds"
|
89
|
+
state = CGI.parse(URI.parse(page.response_headers["Location"]).query)
|
90
|
+
.then { |query| query["state"].first }
|
91
|
+
|
92
|
+
stub_request(:post, "http://signon/oauth/access_token")
|
93
|
+
.to_return(body: { access_token: "token" }.to_json,
|
94
|
+
headers: { content_type: "application/json" })
|
95
|
+
|
96
|
+
stub_signon_user_request(permissions: permissions)
|
97
|
+
|
98
|
+
visit "/auth/gds/callback?code=code&state=#{state}"
|
99
|
+
end
|
100
|
+
|
101
|
+
def stub_signon_user_request(permissions: [])
|
102
|
+
stub_request(:get, "http://signon/user.json?client_id=gds-sso-test")
|
103
|
+
.to_return(
|
104
|
+
body: {
|
105
|
+
user: {
|
106
|
+
uid: "123",
|
107
|
+
email: "test-user@example.com",
|
108
|
+
name: "Test User",
|
109
|
+
permissions: permissions,
|
110
|
+
},
|
111
|
+
}.to_json,
|
112
|
+
headers: { content_type: "application/json" },
|
113
|
+
)
|
114
|
+
end
|
115
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gds-sso
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 18.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GOV.UK Dev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oauth2
|
@@ -128,26 +128,6 @@ dependencies:
|
|
128
128
|
- - "~>"
|
129
129
|
- !ruby/object:Gem::Version
|
130
130
|
version: '3'
|
131
|
-
- !ruby/object:Gem::Dependency
|
132
|
-
name: capybara-mechanize
|
133
|
-
requirement: !ruby/object:Gem::Requirement
|
134
|
-
requirements:
|
135
|
-
- - "~>"
|
136
|
-
- !ruby/object:Gem::Version
|
137
|
-
version: '1'
|
138
|
-
- - ">="
|
139
|
-
- !ruby/object:Gem::Version
|
140
|
-
version: 1.12.1
|
141
|
-
type: :development
|
142
|
-
prerelease: false
|
143
|
-
version_requirements: !ruby/object:Gem::Requirement
|
144
|
-
requirements:
|
145
|
-
- - "~>"
|
146
|
-
- !ruby/object:Gem::Version
|
147
|
-
version: '1'
|
148
|
-
- - ">="
|
149
|
-
- !ruby/object:Gem::Version
|
150
|
-
version: 1.12.1
|
151
131
|
- !ruby/object:Gem::Dependency
|
152
132
|
name: combustion
|
153
133
|
requirement: !ruby/object:Gem::Requirement
|
@@ -194,16 +174,16 @@ dependencies:
|
|
194
174
|
name: rubocop-govuk
|
195
175
|
requirement: !ruby/object:Gem::Requirement
|
196
176
|
requirements:
|
197
|
-
- -
|
177
|
+
- - "~>"
|
198
178
|
- !ruby/object:Gem::Version
|
199
|
-
version: 4
|
179
|
+
version: '4'
|
200
180
|
type: :development
|
201
181
|
prerelease: false
|
202
182
|
version_requirements: !ruby/object:Gem::Requirement
|
203
183
|
requirements:
|
204
|
-
- -
|
184
|
+
- - "~>"
|
205
185
|
- !ruby/object:Gem::Version
|
206
|
-
version: 4
|
186
|
+
version: '4'
|
207
187
|
- !ruby/object:Gem::Dependency
|
208
188
|
name: sqlite3
|
209
189
|
requirement: !ruby/object:Gem::Requirement
|
@@ -232,6 +212,20 @@ dependencies:
|
|
232
212
|
- - "~>"
|
233
213
|
- !ruby/object:Gem::Version
|
234
214
|
version: '0.9'
|
215
|
+
- !ruby/object:Gem::Dependency
|
216
|
+
name: webmock
|
217
|
+
requirement: !ruby/object:Gem::Requirement
|
218
|
+
requirements:
|
219
|
+
- - ">="
|
220
|
+
- !ruby/object:Gem::Version
|
221
|
+
version: '0'
|
222
|
+
type: :development
|
223
|
+
prerelease: false
|
224
|
+
version_requirements: !ruby/object:Gem::Requirement
|
225
|
+
requirements:
|
226
|
+
- - ">="
|
227
|
+
- !ruby/object:Gem::Version
|
228
|
+
version: '0'
|
235
229
|
description: Client for GDS' OAuth 2-based SSO
|
236
230
|
email:
|
237
231
|
- govuk-dev@digital.cabinet-office.gov.uk
|
@@ -264,8 +258,6 @@ files:
|
|
264
258
|
- lib/omniauth/strategies/gds.rb
|
265
259
|
- spec/controller/api_user_controller_spec.rb
|
266
260
|
- spec/controller/controller_methods_spec.rb
|
267
|
-
- spec/fixtures/integration/authorize_api_users.sql
|
268
|
-
- spec/fixtures/integration/signon.sql
|
269
261
|
- spec/internal/app/assets/config/manifest.js
|
270
262
|
- spec/internal/app/controllers/application_controller.rb
|
271
263
|
- spec/internal/app/controllers/example_controller.rb
|
@@ -273,17 +265,13 @@ files:
|
|
273
265
|
- spec/internal/config/database.yml
|
274
266
|
- spec/internal/config/initializers/gds-sso.rb
|
275
267
|
- spec/internal/config/routes.rb
|
276
|
-
- spec/internal/config/storage.yml
|
277
268
|
- spec/internal/db/schema.rb
|
278
|
-
- spec/internal/public/favicon.ico
|
279
|
-
- spec/requests/end_to_end_spec.rb
|
280
269
|
- spec/spec_helper.rb
|
281
|
-
- spec/support/backport_controller_test_params.rb
|
282
270
|
- spec/support/controller_spy.rb
|
283
271
|
- spec/support/serializable_user.rb
|
284
|
-
- spec/support/signon_integration_helpers.rb
|
285
272
|
- spec/support/test_user.rb
|
286
273
|
- spec/support/timecop.rb
|
274
|
+
- spec/system/authentication_and_authorisation_spec.rb
|
287
275
|
- spec/unit/api_access_spec.rb
|
288
276
|
- spec/unit/bearer_token_spec.rb
|
289
277
|
- spec/unit/config_spec.rb
|
@@ -303,44 +291,38 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
303
291
|
requirements:
|
304
292
|
- - ">="
|
305
293
|
- !ruby/object:Gem::Version
|
306
|
-
version: '
|
294
|
+
version: '3.0'
|
307
295
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
308
296
|
requirements:
|
309
297
|
- - ">="
|
310
298
|
- !ruby/object:Gem::Version
|
311
299
|
version: '0'
|
312
300
|
requirements: []
|
313
|
-
rubygems_version: 3.
|
301
|
+
rubygems_version: 3.4.18
|
314
302
|
signing_key:
|
315
303
|
specification_version: 4
|
316
304
|
summary: Client for GDS' OAuth 2-based SSO
|
317
305
|
test_files:
|
318
|
-
- spec/spec_helper.rb
|
319
|
-
- spec/fixtures/integration/signon.sql
|
320
|
-
- spec/fixtures/integration/authorize_api_users.sql
|
321
|
-
- spec/requests/end_to_end_spec.rb
|
322
|
-
- spec/support/controller_spy.rb
|
323
|
-
- spec/support/timecop.rb
|
324
|
-
- spec/support/serializable_user.rb
|
325
|
-
- spec/support/test_user.rb
|
326
|
-
- spec/support/signon_integration_helpers.rb
|
327
|
-
- spec/support/backport_controller_test_params.rb
|
328
306
|
- spec/controller/api_user_controller_spec.rb
|
329
307
|
- spec/controller/controller_methods_spec.rb
|
330
|
-
- spec/internal/config/
|
308
|
+
- spec/internal/app/assets/config/manifest.js
|
309
|
+
- spec/internal/app/controllers/application_controller.rb
|
310
|
+
- spec/internal/app/controllers/example_controller.rb
|
311
|
+
- spec/internal/app/models/user.rb
|
331
312
|
- spec/internal/config/database.yml
|
332
313
|
- spec/internal/config/initializers/gds-sso.rb
|
333
|
-
- spec/internal/config/
|
334
|
-
- spec/internal/app/models/user.rb
|
335
|
-
- spec/internal/app/controllers/example_controller.rb
|
336
|
-
- spec/internal/app/controllers/application_controller.rb
|
337
|
-
- spec/internal/app/assets/config/manifest.js
|
314
|
+
- spec/internal/config/routes.rb
|
338
315
|
- spec/internal/db/schema.rb
|
339
|
-
- spec/
|
340
|
-
- spec/
|
341
|
-
- spec/
|
316
|
+
- spec/spec_helper.rb
|
317
|
+
- spec/support/controller_spy.rb
|
318
|
+
- spec/support/serializable_user.rb
|
319
|
+
- spec/support/test_user.rb
|
320
|
+
- spec/support/timecop.rb
|
321
|
+
- spec/system/authentication_and_authorisation_spec.rb
|
342
322
|
- spec/unit/api_access_spec.rb
|
343
|
-
- spec/unit/
|
344
|
-
- spec/unit/session_serialisation_spec.rb
|
323
|
+
- spec/unit/bearer_token_spec.rb
|
345
324
|
- spec/unit/config_spec.rb
|
325
|
+
- spec/unit/mock_bearer_token_spec.rb
|
346
326
|
- spec/unit/railtie_spec.rb
|
327
|
+
- spec/unit/session_serialisation_spec.rb
|
328
|
+
- spec/unit/user_spec.rb
|
@@ -1,7 +0,0 @@
|
|
1
|
-
DELETE FROM `oauth_access_tokens`;
|
2
|
-
|
3
|
-
INSERT INTO oauth_access_tokens (resource_owner_id, application_id, token, refresh_token, expires_in, created_at)
|
4
|
-
VALUES (1, 1, 'caaeb53be5c7277fb0ef158181bfd1537b57f9e3b83eb795be3cd0af6e118b28', '1bc343797483954d7306d67e96687feccdfdaa8b23ed662ae23e2b03e6661d16', POW(2, 31)-1, '2012-06-27 13:57:47');
|
5
|
-
|
6
|
-
INSERT INTO oauth_access_tokens (resource_owner_id, application_id, token, refresh_token, expires_in, created_at)
|
7
|
-
VALUES (1, 2, '98c72f4da02fdc43398e029d05567542944d2a9b0df3c20b0accd8bd6c5dc728', 'e2da0489a58219fd4f542139909737627874ceacd2af23f5c268ccecb36e85af', POW(2, 31)-1, '2014-07-14 09:06:14');
|
@@ -1,26 +0,0 @@
|
|
1
|
-
-- Clean data from database
|
2
|
-
DELETE FROM `oauth_access_grants`;
|
3
|
-
DELETE FROM `oauth_access_tokens`;
|
4
|
-
DELETE FROM `oauth_applications`;
|
5
|
-
DELETE FROM `supported_permissions`;
|
6
|
-
DELETE FROM `users`;
|
7
|
-
DELETE FROM `user_application_permissions`;
|
8
|
-
|
9
|
-
-- Setup fixture data
|
10
|
-
INSERT INTO `oauth_applications` (id, name, uid, secret, redirect_uri, created_at, updated_at, home_uri, description)
|
11
|
-
VALUES (1,'GDS_SSO integration test','gds-sso-test','secret','http://www.example-client.com/auth/gds/callback','2012-04-19 13:26:54','2012-04-19 13:26:54', 'http://home.com', 'GDS_SSO integration test');
|
12
|
-
INSERT INTO `oauth_applications` (id, name, uid, secret, redirect_uri, created_at, updated_at, home_uri, description)
|
13
|
-
VALUES (2,'A different appilcation','application-2','different secret','http://www.example-client2.com/auth/gds/callback','2014-07-14-09:07:32','2014-07-14-09:07:32', 'http://www.example-client2.com', '');
|
14
|
-
|
15
|
-
INSERT INTO `supported_permissions` (id, application_id, name, created_at, updated_at)
|
16
|
-
VALUES (1,1,'signin','2012-04-19 13:26:54','2012-04-19 13:26:54');
|
17
|
-
INSERT INTO `supported_permissions` (id, application_id, name, created_at, updated_at)
|
18
|
-
VALUES (2,2,'signin','2012-04-19 13:26:54','2012-04-19 13:26:54');
|
19
|
-
|
20
|
-
INSERT INTO `users` (id, email, encrypted_password, password_salt, created_at, updated_at, confirmed_at, name, uid, role, password_changed_at)
|
21
|
-
VALUES (1,'test@example-client.com','3a890fe5e95b328b83f2ba57ea893cae595f4937291ff5550acb68f4a8dafeac22e5f8120c1e66be8f2b769df142dd3d111b404c5c1741595c9ecc9e7e6ad827','QCsZsJs7m5ojdgvysHSy','2012-04-19 13:26:54','2012-04-19 13:26:54','2012-04-19 13:26:54','Test User','integration-uid', "normal", NOW());
|
22
|
-
|
23
|
-
INSERT INTO `user_application_permissions` (user_id, application_id, supported_permission_id, created_at, updated_at)
|
24
|
-
VALUES (1,1,1,'2012-04-19 13:26:54','2012-04-19 13:26:54');
|
25
|
-
INSERT INTO `user_application_permissions` (user_id, application_id, supported_permission_id, created_at, updated_at)
|
26
|
-
VALUES (1,2,2,'2012-04-19 13:26:54','2012-04-19 13:26:54');
|
File without changes
|
@@ -1,215 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
require "timecop"
|
3
|
-
|
4
|
-
describe "Integration of client using GDS-SSO with signon" do
|
5
|
-
include SignonIntegrationHelpers
|
6
|
-
|
7
|
-
before :all do
|
8
|
-
wait_for_signon_to_start
|
9
|
-
end
|
10
|
-
|
11
|
-
before :each do
|
12
|
-
# points to an internal app, using combustion gem
|
13
|
-
# see spec/internal
|
14
|
-
@client_host = "www.example-client.com"
|
15
|
-
Capybara.current_driver = :mechanize
|
16
|
-
Capybara::Mechanize.local_hosts << @client_host
|
17
|
-
|
18
|
-
load_signon_setup_fixture
|
19
|
-
end
|
20
|
-
|
21
|
-
describe "Web client accesses" do
|
22
|
-
before :each do
|
23
|
-
page.driver.header "accept", "text/html"
|
24
|
-
end
|
25
|
-
|
26
|
-
specify "a non-restricted page can be accessed without authentication" do
|
27
|
-
visit "http://#{@client_host}/"
|
28
|
-
expect(page).to have_content("jabberwocky")
|
29
|
-
end
|
30
|
-
|
31
|
-
specify "first access to a restricted page requires authentication and application approval" do
|
32
|
-
visit "http://#{@client_host}/restricted"
|
33
|
-
expect(page).to have_content("Sign in")
|
34
|
-
fill_in "Email", with: "test@example-client.com"
|
35
|
-
fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
|
36
|
-
click_on "Sign in"
|
37
|
-
|
38
|
-
expect(page).to have_content("restricted kablooie")
|
39
|
-
end
|
40
|
-
|
41
|
-
specify "access to a restricted page for an approved application requires only authentication" do
|
42
|
-
# First we login to authorise the app
|
43
|
-
visit "http://#{@client_host}/restricted"
|
44
|
-
fill_in "Email", with: "test@example-client.com"
|
45
|
-
fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
|
46
|
-
click_on "Sign in"
|
47
|
-
|
48
|
-
# At this point the app should be authorised, we reset the session to simulate a new browser visit.
|
49
|
-
reset_session!
|
50
|
-
page.driver.header "accept", "text/html"
|
51
|
-
|
52
|
-
visit "http://#{@client_host}/restricted"
|
53
|
-
expect(page).to have_content("Sign in")
|
54
|
-
|
55
|
-
fill_in "Email", with: "test@example-client.com"
|
56
|
-
fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
|
57
|
-
click_on "Sign in"
|
58
|
-
|
59
|
-
expect(page).to have_content("restricted kablooie")
|
60
|
-
end
|
61
|
-
|
62
|
-
specify "access to a page that requires signin permission granted" do
|
63
|
-
# First we login to authorise the app
|
64
|
-
visit "http://#{@client_host}/this_requires_signin_permission"
|
65
|
-
fill_in "Email", with: "test@example-client.com"
|
66
|
-
fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
|
67
|
-
click_on "Sign in"
|
68
|
-
|
69
|
-
# At this point the app should be authorised, we reset the session to simulate a new browser visit.
|
70
|
-
reset_session!
|
71
|
-
page.driver.header "accept", "text/html"
|
72
|
-
|
73
|
-
visit "http://#{@client_host}/this_requires_signin_permission"
|
74
|
-
expect(page).to have_content("Sign in")
|
75
|
-
|
76
|
-
fill_in "Email", with: "test@example-client.com"
|
77
|
-
fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
|
78
|
-
click_on "Sign in"
|
79
|
-
|
80
|
-
expect(page).to have_content("you have signin permission")
|
81
|
-
end
|
82
|
-
|
83
|
-
describe "remotely signed out" do
|
84
|
-
specify "should prevent all access to the application until successful signin" do
|
85
|
-
# First we login and authorise the app
|
86
|
-
visit "http://#{@client_host}/restricted"
|
87
|
-
fill_in "Email", with: "test@example-client.com"
|
88
|
-
fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
|
89
|
-
click_on "Sign in"
|
90
|
-
|
91
|
-
page.driver.header "accept", "text/html"
|
92
|
-
expect(page).to have_content("restricted kablooie")
|
93
|
-
|
94
|
-
# logout from signon
|
95
|
-
visit "http://localhost:4567/users/sign_out"
|
96
|
-
|
97
|
-
# Simulate a POST to /auth/gds/api/users/:uid/reauth by signon
|
98
|
-
# This is already tested in api_user_controller_spec.rb
|
99
|
-
user = User.where(email: "test@example-client.com").first
|
100
|
-
user.set_remotely_signed_out!
|
101
|
-
|
102
|
-
# attempt to visit a restricted page
|
103
|
-
visit "http://#{@client_host}/restricted"
|
104
|
-
|
105
|
-
# be redirected to signon
|
106
|
-
expect(page).to have_content("GOV.UK Signon")
|
107
|
-
fill_in "Email", with: "test@example-client.com"
|
108
|
-
fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
|
109
|
-
click_on "Sign in"
|
110
|
-
|
111
|
-
# then back again to the restricted page
|
112
|
-
expect(page).to have_content("restricted kablooie")
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
describe "session expiry" do
|
117
|
-
it "should force you to re-authenticate with signon N hours after login" do
|
118
|
-
visit "http://#{@client_host}/restricted"
|
119
|
-
expect(page).to have_content("Sign in")
|
120
|
-
fill_in "Email", with: "test@example-client.com"
|
121
|
-
fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
|
122
|
-
click_on "Sign in"
|
123
|
-
|
124
|
-
expect(page).to have_content("restricted kablooie")
|
125
|
-
|
126
|
-
visit "http://localhost:4567/users/sign_out"
|
127
|
-
|
128
|
-
Timecop.travel(Time.now.utc + GDS::SSO::Config.auth_valid_for + 5.minutes) do
|
129
|
-
visit "http://#{@client_host}/restricted"
|
130
|
-
end
|
131
|
-
|
132
|
-
expect(page).to have_content("Sign in")
|
133
|
-
end
|
134
|
-
|
135
|
-
it "should accept signon's remembered authentication N hours after login" do
|
136
|
-
visit "http://#{@client_host}/restricted"
|
137
|
-
expect(page).to have_content("Sign in")
|
138
|
-
fill_in "Email", with: "test@example-client.com"
|
139
|
-
fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
|
140
|
-
click_on "Sign in"
|
141
|
-
|
142
|
-
expect(page).to have_content("restricted kablooie")
|
143
|
-
|
144
|
-
Timecop.travel(Time.now.utc + GDS::SSO::Config.auth_valid_for + 5.minutes) do
|
145
|
-
visit "http://#{@client_host}/restricted"
|
146
|
-
end
|
147
|
-
|
148
|
-
expect(page).to have_content("restricted kablooie")
|
149
|
-
end
|
150
|
-
|
151
|
-
it "should not require re-authentication with signon fewer than N hours after login" do
|
152
|
-
visit "http://#{@client_host}/restricted"
|
153
|
-
expect(page).to have_content("Sign in")
|
154
|
-
fill_in "Email", with: "test@example-client.com"
|
155
|
-
fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
|
156
|
-
click_on "Sign in"
|
157
|
-
|
158
|
-
expect(page).to have_content("restricted kablooie")
|
159
|
-
|
160
|
-
Timecop.travel(Time.now.utc + GDS::SSO::Config.auth_valid_for - 5.minutes) do
|
161
|
-
visit "http://#{@client_host}/restricted"
|
162
|
-
end
|
163
|
-
|
164
|
-
expect(page).to have_content("restricted kablooie")
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
describe "OAuth based API client accesses" do
|
170
|
-
before :each do
|
171
|
-
page.driver.header "accept", "application/json"
|
172
|
-
authorize_signon_api_user
|
173
|
-
|
174
|
-
token = "caaeb53be5c7277fb0ef158181bfd1537b57f9e3b83eb795be3cd0af6e118b28"
|
175
|
-
page.driver.header "authorization", "Bearer #{token}"
|
176
|
-
end
|
177
|
-
|
178
|
-
specify "access to a restricted page for an api client requires auth" do
|
179
|
-
page.driver.header "authorization", "Bearer Bad Token"
|
180
|
-
visit "http://#{@client_host}/restricted"
|
181
|
-
expect(page.driver.response.status).to eq(401)
|
182
|
-
end
|
183
|
-
|
184
|
-
specify "setting a correct bearer token allows sign in" do
|
185
|
-
visit "http://#{@client_host}/restricted"
|
186
|
-
expect(page).to have_content("restricted kablooie")
|
187
|
-
end
|
188
|
-
|
189
|
-
specify "setting a correct bearer token picks up permissions" do
|
190
|
-
visit "http://#{@client_host}/this_requires_signin_permission"
|
191
|
-
expect(page).to have_content("you have signin permission")
|
192
|
-
end
|
193
|
-
|
194
|
-
specify "a token for one app cannot be used to access a different app" do
|
195
|
-
page.driver.header "authorization", "Bearer 98c72f4da02fdc43398e029d05567542944d2a9b0df3c20b0accd8bd6c5dc728"
|
196
|
-
visit "http://#{@client_host}/restricted"
|
197
|
-
expect(page.driver.response.status).to eq(401)
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
context "when in api_only mode" do
|
202
|
-
around do |examples|
|
203
|
-
GDS::SSO::Config.api_only = true
|
204
|
-
Combustion::Application.reload_routes!
|
205
|
-
examples.run
|
206
|
-
GDS::SSO::Config.api_only = false
|
207
|
-
Combustion::Application.reload_routes!
|
208
|
-
end
|
209
|
-
|
210
|
-
specify "accessing without a bearer token is not authorized" do
|
211
|
-
visit "http://#{@client_host}/restricted"
|
212
|
-
expect(page.driver.response.status).to eq(401)
|
213
|
-
end
|
214
|
-
end
|
215
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
module BackportControllerTestParams
|
2
|
-
def delete(*args)
|
3
|
-
action, rest = *args
|
4
|
-
super(action, rest[:params])
|
5
|
-
end
|
6
|
-
|
7
|
-
def get(*args)
|
8
|
-
action, rest = *args
|
9
|
-
super(action, rest[:params])
|
10
|
-
end
|
11
|
-
|
12
|
-
def post(*args)
|
13
|
-
action, rest = *args
|
14
|
-
super(action, rest[:params])
|
15
|
-
end
|
16
|
-
|
17
|
-
def put(*args)
|
18
|
-
action, rest = *args
|
19
|
-
super(action, rest[:params])
|
20
|
-
end
|
21
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
require "net/http"
|
2
|
-
|
3
|
-
module SignonIntegrationHelpers
|
4
|
-
def wait_for_signon_to_start
|
5
|
-
retries = 0
|
6
|
-
url = GDS::SSO::Config.oauth_root_url
|
7
|
-
puts "Waiting for signon to start at #{url}"
|
8
|
-
until signon_started?(url)
|
9
|
-
print "."
|
10
|
-
if retries > 20
|
11
|
-
raise "Signon is not running at #{url}. Please start with `./start_signon.sh`. Under jenkins this should happen automatically."
|
12
|
-
end
|
13
|
-
|
14
|
-
retries += 1
|
15
|
-
sleep 1
|
16
|
-
end
|
17
|
-
puts "Signon is now running at #{url}"
|
18
|
-
end
|
19
|
-
|
20
|
-
def signon_started?(url)
|
21
|
-
uri = URI.parse(url)
|
22
|
-
conn = Net::HTTP.start(uri.host, uri.port)
|
23
|
-
true
|
24
|
-
rescue Errno::ECONNREFUSED
|
25
|
-
false
|
26
|
-
ensure
|
27
|
-
conn.try(:finish)
|
28
|
-
end
|
29
|
-
|
30
|
-
def load_signon_setup_fixture
|
31
|
-
load_signon_fixture("signon.sql")
|
32
|
-
end
|
33
|
-
|
34
|
-
def authorize_signon_api_user
|
35
|
-
load_signon_fixture("authorize_api_users.sql")
|
36
|
-
end
|
37
|
-
|
38
|
-
def load_signon_fixture(filename)
|
39
|
-
require "erb"
|
40
|
-
parsed = ERB.new(File.read("#{signon_path}/config/database.yml")).result
|
41
|
-
db = YAML.safe_load(parsed, aliases: true)["test"]
|
42
|
-
|
43
|
-
cmd = "mysql #{db['database']} -u#{db['username']} -p#{db['password']} < #{fixture_file(filename)}"
|
44
|
-
system cmd or raise "Error loading signon fixture"
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
def fixture_file(filename)
|
50
|
-
File.join(File.dirname(__FILE__), "../fixtures/integration", filename)
|
51
|
-
end
|
52
|
-
|
53
|
-
def signon_path
|
54
|
-
Rails.root.join("..", "..", "tmp", "signon").to_s
|
55
|
-
end
|
56
|
-
end
|