gds-sso 15.0.0 → 16.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +23 -56
  3. data/Rakefile +11 -6
  4. data/app/controllers/api/user_controller.rb +30 -28
  5. data/app/controllers/authentications_controller.rb +4 -6
  6. data/config/routes.rb +7 -6
  7. data/lib/gds-sso.rb +29 -24
  8. data/lib/gds-sso/api_access.rb +1 -1
  9. data/lib/gds-sso/bearer_token.rb +24 -24
  10. data/lib/gds-sso/config.rb +13 -12
  11. data/lib/gds-sso/controller_methods.rb +7 -8
  12. data/lib/gds-sso/failure_app.rb +8 -8
  13. data/lib/gds-sso/lint/user_spec.rb +24 -25
  14. data/lib/gds-sso/lint/user_test.rb +28 -28
  15. data/lib/gds-sso/railtie.rb +12 -0
  16. data/lib/gds-sso/user.rb +12 -12
  17. data/lib/gds-sso/version.rb +1 -1
  18. data/lib/gds-sso/warden_config.rb +21 -31
  19. data/spec/controller/api_user_controller_spec.rb +40 -37
  20. data/spec/controller/controller_methods_spec.rb +28 -42
  21. data/spec/internal/app/controllers/application_controller.rb +1 -1
  22. data/spec/internal/app/controllers/example_controller.rb +1 -2
  23. data/spec/internal/config/initializers/gds-sso.rb +2 -2
  24. data/spec/internal/config/routes.rb +2 -2
  25. data/spec/internal/db/combustion_test.sqlite +0 -0
  26. data/spec/internal/db/schema.rb +5 -5
  27. data/spec/internal/log/test.log +1131 -1123
  28. data/spec/requests/end_to_end_spec.rb +44 -45
  29. data/spec/spec_helper.rb +12 -13
  30. data/spec/support/controller_spy.rb +14 -0
  31. data/spec/support/serializable_user.rb +3 -0
  32. data/spec/support/signon_integration_helpers.rb +10 -8
  33. data/spec/support/test_user.rb +29 -0
  34. data/spec/support/timecop.rb +1 -1
  35. data/spec/unit/api_access_spec.rb +7 -7
  36. data/spec/unit/bearer_token_spec.rb +14 -15
  37. data/spec/unit/config_spec.rb +5 -5
  38. data/spec/unit/mock_bearer_token_spec.rb +4 -4
  39. data/spec/unit/railtie_spec.rb +14 -0
  40. data/spec/unit/session_serialisation_spec.rb +5 -9
  41. data/spec/unit/user_spec.rb +20 -51
  42. metadata +104 -61
@@ -1,5 +1,5 @@
1
- require 'spec_helper'
2
- require 'timecop'
1
+ require "spec_helper"
2
+ require "timecop"
3
3
 
4
4
  describe "Integration of client using GDS-SSO with signon" do
5
5
  include SignonIntegrationHelpers
@@ -11,7 +11,7 @@ describe "Integration of client using GDS-SSO with signon" do
11
11
  before :each do
12
12
  # points to an internal app, using combustion gem
13
13
  # see spec/internal
14
- @client_host = 'www.example-client.com'
14
+ @client_host = "www.example-client.com"
15
15
  Capybara.current_driver = :mechanize
16
16
  Capybara::Mechanize.local_hosts << @client_host
17
17
 
@@ -20,96 +20,96 @@ describe "Integration of client using GDS-SSO with signon" do
20
20
 
21
21
  describe "Web client accesses" do
22
22
  before :each do
23
- page.driver.header 'accept', 'text/html'
23
+ page.driver.header "accept", "text/html"
24
24
  end
25
25
 
26
26
  specify "a non-restricted page can be accessed without authentication" do
27
27
  visit "http://#{@client_host}/"
28
- expect(page).to have_content('jabberwocky')
28
+ expect(page).to have_content("jabberwocky")
29
29
  end
30
30
 
31
31
  specify "first access to a restricted page requires authentication and application approval" do
32
32
  visit "http://#{@client_host}/restricted"
33
33
  expect(page).to have_content("Sign in")
34
- fill_in "Email", :with => "test@example-client.com"
35
- fill_in "Password", :with => "q1w2e3r4t5y6u7i8o9p0"
34
+ fill_in "Email", with: "test@example-client.com"
35
+ fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
36
36
  click_on "Sign in"
37
37
 
38
- expect(page).to have_content('restricted kablooie')
38
+ expect(page).to have_content("restricted kablooie")
39
39
  end
40
40
 
41
41
  specify "access to a restricted page for an approved application requires only authentication" do
42
42
  # First we login to authorise the app
43
43
  visit "http://#{@client_host}/restricted"
44
- fill_in "Email", :with => "test@example-client.com"
45
- fill_in "Password", :with => "q1w2e3r4t5y6u7i8o9p0"
44
+ fill_in "Email", with: "test@example-client.com"
45
+ fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
46
46
  click_on "Sign in"
47
47
 
48
48
  # At this point the app should be authorised, we reset the session to simulate a new browser visit.
49
49
  reset_session!
50
- page.driver.header 'accept', 'text/html'
50
+ page.driver.header "accept", "text/html"
51
51
 
52
52
  visit "http://#{@client_host}/restricted"
53
53
  expect(page).to have_content("Sign in")
54
54
 
55
- fill_in "Email", :with => "test@example-client.com"
56
- fill_in "Password", :with => "q1w2e3r4t5y6u7i8o9p0"
55
+ fill_in "Email", with: "test@example-client.com"
56
+ fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
57
57
  click_on "Sign in"
58
58
 
59
- expect(page).to have_content('restricted kablooie')
59
+ expect(page).to have_content("restricted kablooie")
60
60
  end
61
61
 
62
62
  specify "access to a page that requires signin permission granted" do
63
63
  # First we login to authorise the app
64
64
  visit "http://#{@client_host}/this_requires_signin_permission"
65
- fill_in "Email", :with => "test@example-client.com"
66
- fill_in "Password", :with => "q1w2e3r4t5y6u7i8o9p0"
65
+ fill_in "Email", with: "test@example-client.com"
66
+ fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
67
67
  click_on "Sign in"
68
68
 
69
69
  # At this point the app should be authorised, we reset the session to simulate a new browser visit.
70
70
  reset_session!
71
- page.driver.header 'accept', 'text/html'
71
+ page.driver.header "accept", "text/html"
72
72
 
73
73
  visit "http://#{@client_host}/this_requires_signin_permission"
74
74
  expect(page).to have_content("Sign in")
75
75
 
76
- fill_in "Email", :with => "test@example-client.com"
77
- fill_in "Password", :with => "q1w2e3r4t5y6u7i8o9p0"
76
+ fill_in "Email", with: "test@example-client.com"
77
+ fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
78
78
  click_on "Sign in"
79
79
 
80
- expect(page).to have_content('you have signin permission')
80
+ expect(page).to have_content("you have signin permission")
81
81
  end
82
82
 
83
83
  describe "remotely signed out" do
84
84
  specify "should prevent all access to the application until successful signin" do
85
85
  # First we login and authorise the app
86
86
  visit "http://#{@client_host}/restricted"
87
- fill_in "Email", :with => "test@example-client.com"
88
- fill_in "Password", :with => "q1w2e3r4t5y6u7i8o9p0"
87
+ fill_in "Email", with: "test@example-client.com"
88
+ fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
89
89
  click_on "Sign in"
90
90
 
91
- page.driver.header 'accept', 'text/html'
92
- expect(page).to have_content('restricted kablooie')
91
+ page.driver.header "accept", "text/html"
92
+ expect(page).to have_content("restricted kablooie")
93
93
 
94
94
  # logout from signon
95
95
  visit "http://localhost:4567/users/sign_out"
96
96
 
97
97
  # Simulate a POST to /auth/gds/api/users/:uid/reauth by signon
98
98
  # This is already tested in api_user_controller_spec.rb
99
- user = User.where(:email => "test@example-client.com").first
99
+ user = User.where(email: "test@example-client.com").first
100
100
  user.set_remotely_signed_out!
101
101
 
102
102
  # attempt to visit a restricted page
103
103
  visit "http://#{@client_host}/restricted"
104
104
 
105
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"
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
109
  click_on "Sign in"
110
110
 
111
111
  # then back again to the restricted page
112
- expect(page).to have_content('restricted kablooie')
112
+ expect(page).to have_content("restricted kablooie")
113
113
  end
114
114
  end
115
115
 
@@ -117,11 +117,11 @@ describe "Integration of client using GDS-SSO with signon" do
117
117
  it "should force you to re-authenticate with signon N hours after login" do
118
118
  visit "http://#{@client_host}/restricted"
119
119
  expect(page).to have_content("Sign in")
120
- fill_in "Email", :with => "test@example-client.com"
121
- fill_in "Password", :with => "q1w2e3r4t5y6u7i8o9p0"
120
+ fill_in "Email", with: "test@example-client.com"
121
+ fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
122
122
  click_on "Sign in"
123
123
 
124
- expect(page).to have_content('restricted kablooie')
124
+ expect(page).to have_content("restricted kablooie")
125
125
 
126
126
  visit "http://localhost:4567/users/sign_out"
127
127
 
@@ -135,11 +135,11 @@ describe "Integration of client using GDS-SSO with signon" do
135
135
  it "should accept signon's remembered authentication N hours after login" do
136
136
  visit "http://#{@client_host}/restricted"
137
137
  expect(page).to have_content("Sign in")
138
- fill_in "Email", :with => "test@example-client.com"
139
- fill_in "Password", :with => "q1w2e3r4t5y6u7i8o9p0"
138
+ fill_in "Email", with: "test@example-client.com"
139
+ fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
140
140
  click_on "Sign in"
141
141
 
142
- expect(page).to have_content('restricted kablooie')
142
+ expect(page).to have_content("restricted kablooie")
143
143
 
144
144
  Timecop.travel(Time.now.utc + GDS::SSO::Config.auth_valid_for + 5.minutes) do
145
145
  visit "http://#{@client_host}/restricted"
@@ -148,15 +148,14 @@ describe "Integration of client using GDS-SSO with signon" do
148
148
  expect(page).to have_content("restricted kablooie")
149
149
  end
150
150
 
151
-
152
151
  it "should not require re-authentication with signon fewer than N hours after login" do
153
152
  visit "http://#{@client_host}/restricted"
154
153
  expect(page).to have_content("Sign in")
155
- fill_in "Email", :with => "test@example-client.com"
156
- fill_in "Password", :with => "q1w2e3r4t5y6u7i8o9p0"
154
+ fill_in "Email", with: "test@example-client.com"
155
+ fill_in "Password", with: "q1w2e3r4t5y6u7i8o9p0"
157
156
  click_on "Sign in"
158
157
 
159
- expect(page).to have_content('restricted kablooie')
158
+ expect(page).to have_content("restricted kablooie")
160
159
 
161
160
  Timecop.travel(Time.now.utc + GDS::SSO::Config.auth_valid_for - 5.minutes) do
162
161
  visit "http://#{@client_host}/restricted"
@@ -169,31 +168,31 @@ describe "Integration of client using GDS-SSO with signon" do
169
168
 
170
169
  describe "OAuth based API client accesses" do
171
170
  before :each do
172
- page.driver.header 'accept', 'application/json'
171
+ page.driver.header "accept", "application/json"
173
172
  authorize_signon_api_user
174
173
 
175
174
  token = "caaeb53be5c7277fb0ef158181bfd1537b57f9e3b83eb795be3cd0af6e118b28"
176
- page.driver.header 'authorization', "Bearer #{token}"
175
+ page.driver.header "authorization", "Bearer #{token}"
177
176
  end
178
177
 
179
178
  specify "access to a restricted page for an api client requires auth" do
180
- page.driver.header 'authorization', 'Bearer Bad Token'
179
+ page.driver.header "authorization", "Bearer Bad Token"
181
180
  visit "http://#{@client_host}/restricted"
182
181
  expect(page.driver.response.status).to eq(401)
183
182
  end
184
183
 
185
184
  specify "setting a correct bearer token allows sign in" do
186
185
  visit "http://#{@client_host}/restricted"
187
- expect(page).to have_content('restricted kablooie')
186
+ expect(page).to have_content("restricted kablooie")
188
187
  end
189
188
 
190
189
  specify "setting a correct bearer token picks up permissions" do
191
190
  visit "http://#{@client_host}/this_requires_signin_permission"
192
- expect(page).to have_content('you have signin permission')
191
+ expect(page).to have_content("you have signin permission")
193
192
  end
194
193
 
195
194
  specify "a token for one app cannot be used to access a different app" do
196
- page.driver.header 'authorization', "Bearer 98c72f4da02fdc43398e029d05567542944d2a9b0df3c20b0accd8bd6c5dc728"
195
+ page.driver.header "authorization", "Bearer 98c72f4da02fdc43398e029d05567542944d2a9b0df3c20b0accd8bd6c5dc728"
197
196
  visit "http://#{@client_host}/restricted"
198
197
  expect(page.driver.response.status).to eq(401)
199
198
  end
data/spec/spec_helper.rb CHANGED
@@ -1,21 +1,19 @@
1
1
  # Yes, we really do want to turn off the test environment check here.
2
2
  # Bad things happen if we don't ;-)
3
- ENV['GDS_SSO_STRATEGY'] = 'real'
3
+ ENV["GDS_SSO_STRATEGY"] = "real"
4
4
 
5
- require 'bundler/setup'
6
- require 'combustion'
7
- require 'capybara/rspec'
5
+ require "bundler/setup"
6
+ require "combustion"
7
+ require "capybara/rspec"
8
8
 
9
9
  Combustion.initialize! :all
10
10
 
11
- require 'rspec/rails'
12
- require 'capybara/rails'
13
- require 'mechanize'
14
- require 'capybara/mechanize'
11
+ require "rspec/rails"
12
+ require "capybara/rails"
13
+ require "mechanize"
14
+ require "capybara/mechanize"
15
15
 
16
- include Warden::Test::Helpers
17
-
18
- Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")].each {|f| require f}
16
+ Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")].sort.each { |f| require f }
19
17
 
20
18
  RSpec.configure do |config|
21
19
  config.run_all_when_everything_filtered = true
@@ -25,7 +23,8 @@ RSpec.configure do |config|
25
23
  # order dependency and want to debug it, you can fix the order by providing
26
24
  # the seed, which is printed after each run.
27
25
  # --seed 1234
28
- config.order = 'random'
26
+ config.order = "random"
29
27
 
30
- config.include(BackportControllerTestParams) if Rails.version < '5'
28
+ config.include(BackportControllerTestParams) if Rails.version < "5"
29
+ config.include(Warden::Test::Helpers)
31
30
  end
@@ -0,0 +1,14 @@
1
+ class ControllerSpy < ApplicationController
2
+ include GDS::SSO::ControllerMethods
3
+ # rubocop:disable Lint/MissingSuper
4
+ def initialize(current_user)
5
+ @current_user = current_user
6
+ end
7
+ # rubocop:enable Lint/MissingSuper
8
+
9
+ def authenticate_user!
10
+ true
11
+ end
12
+
13
+ attr_reader :current_user
14
+ end
@@ -0,0 +1,3 @@
1
+ class SerializableUser
2
+ include GDS::SSO::User
3
+ end
@@ -1,15 +1,16 @@
1
- require 'net/http'
1
+ require "net/http"
2
2
 
3
3
  module SignonIntegrationHelpers
4
4
  def wait_for_signon_to_start
5
5
  retries = 0
6
6
  url = GDS::SSO::Config.oauth_root_url
7
7
  puts "Waiting for signon to start at #{url}"
8
- while ! signon_started?(url)
9
- print '.'
8
+ until signon_started?(url)
9
+ print "."
10
10
  if retries > 20
11
11
  raise "Signon is not running at #{url}. Please start with `./start_signon.sh`. Under jenkins this should happen automatically."
12
12
  end
13
+
13
14
  retries += 1
14
15
  sleep 1
15
16
  end
@@ -35,20 +36,21 @@ module SignonIntegrationHelpers
35
36
  end
36
37
 
37
38
  def load_signon_fixture(filename)
38
- require 'erb'
39
- parsed = ERB.new(File.read(signon_path + "/config/database.yml")).result
40
- db = YAML.load(parsed)['test']
39
+ require "erb"
40
+ parsed = ERB.new(File.read("#{signon_path}/config/database.yml")).result
41
+ db = YAML.safe_load(parsed, aliases: true)["test"]
41
42
 
42
43
  cmd = "mysql #{db['database']} -u#{db['username']} -p#{db['password']} < #{fixture_file(filename)}"
43
44
  system cmd or raise "Error loading signon fixture"
44
45
  end
45
46
 
46
47
  private
48
+
47
49
  def fixture_file(filename)
48
- File.join(File.dirname(__FILE__), '../fixtures/integration', filename)
50
+ File.join(File.dirname(__FILE__), "../fixtures/integration", filename)
49
51
  end
50
52
 
51
53
  def signon_path
52
- Rails.root.join('..','..','tmp','signon').to_s
54
+ Rails.root.join("..", "..", "tmp", "signon").to_s
53
55
  end
54
56
  end
@@ -0,0 +1,29 @@
1
+ class TestUser < OpenStruct
2
+ include GDS::SSO::User
3
+
4
+ def self.where(_opts)
5
+ []
6
+ end
7
+
8
+ def self.create!(options, _scope = {})
9
+ new(options)
10
+ end
11
+
12
+ def update_attribute(key, value)
13
+ send("#{key}=".to_sym, value)
14
+ end
15
+
16
+ def update!(options)
17
+ options.each do |key, value|
18
+ update_attribute(key, value)
19
+ end
20
+ end
21
+
22
+ def remotely_signed_out?
23
+ remotely_signed_out
24
+ end
25
+
26
+ def disabled?
27
+ disabled
28
+ end
29
+ end
@@ -1,4 +1,4 @@
1
- require 'timecop'
1
+ require "timecop"
2
2
 
3
3
  RSpec.configure do |config|
4
4
  config.after :each do
@@ -1,17 +1,17 @@
1
- require 'spec_helper'
2
- require 'gds-sso/api_access'
1
+ require "spec_helper"
2
+ require "gds-sso/api_access"
3
3
 
4
4
  describe GDS::SSO::ApiAccess do
5
5
  it "should not consider IE7 accept header as an api call" do
6
- ie7_accept_header = 'image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, ' +
7
- 'application/x-shockwave-flash, application/xaml+xml, application/x-ms-xbap, ' +
8
- 'application/x-ms-application, */*'
9
- expect(GDS::SSO::ApiAccess.api_call?('HTTP_ACCEPT' => ie7_accept_header)).to be_falsey
6
+ ie7_accept_header = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, " \
7
+ "application/x-shockwave-flash, application/xaml+xml, application/x-ms-xbap, " \
8
+ "application/x-ms-application, */*"
9
+ expect(GDS::SSO::ApiAccess.api_call?("HTTP_ACCEPT" => ie7_accept_header)).to be_falsey
10
10
  end
11
11
 
12
12
  context "with a bearer token" do
13
13
  it "it is considered an api call" do
14
- expect(GDS::SSO::ApiAccess.api_call?('HTTP_AUTHORIZATION' => 'Bearer deadbeef12345678')).to be_truthy
14
+ expect(GDS::SSO::ApiAccess.api_call?("HTTP_AUTHORIZATION" => "Bearer deadbeef12345678")).to be_truthy
15
15
  end
16
16
  end
17
17
  end
@@ -1,28 +1,27 @@
1
- require 'spec_helper'
2
- require 'gds-sso/bearer_token'
1
+ require "spec_helper"
2
+ require "gds-sso/bearer_token"
3
3
 
4
4
  describe GDS::SSO::BearerToken do
5
- describe '.locate' do
6
- it 'creates a new user for a token' do
5
+ describe ".locate" do
6
+ it "creates a new user for a token" do
7
7
  response = double(body: {
8
8
  user: {
9
- uid: 'asd',
10
- email: 'user@example.com',
11
- name: 'A Name',
12
- permissions: ['signin'],
13
- organisation_slug: 'hmrc',
14
- organisation_content_id: '67a2b78d-eee3-45b3-80e2-792e7f71cecc',
15
- }
9
+ uid: "asd",
10
+ email: "user@example.com",
11
+ name: "A Name",
12
+ permissions: %w[signin],
13
+ organisation_slug: "hmrc",
14
+ organisation_content_id: "67a2b78d-eee3-45b3-80e2-792e7f71cecc",
15
+ },
16
16
  }.to_json)
17
17
 
18
-
19
18
  allow_any_instance_of(OAuth2::AccessToken).to receive(:get).and_return(response)
20
19
 
21
- created_user = GDS::SSO::BearerToken.locate('MY-API-TOKEN')
20
+ created_user = GDS::SSO::BearerToken.locate("MY-API-TOKEN")
22
21
 
23
- expect(created_user.email).to eql('user@example.com')
22
+ expect(created_user.email).to eql("user@example.com")
24
23
 
25
- same_user_again = GDS::SSO::BearerToken.locate('MY-API-TOKEN')
24
+ same_user_again = GDS::SSO::BearerToken.locate("MY-API-TOKEN")
26
25
 
27
26
  expect(same_user_again.id).to eql(created_user.id)
28
27
  end
@@ -1,26 +1,26 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  describe GDS::SSO::Config do
4
4
  describe "#permissions_for_dummy_user" do
5
5
  context "with no additional mock permissions" do
6
6
  it "returns signin" do
7
7
  subject.additional_mock_permissions_required = nil
8
- expect(subject.permissions_for_dummy_api_user).to eq(["signin"])
8
+ expect(subject.permissions_for_dummy_api_user).to eq(%w[signin])
9
9
  end
10
10
  end
11
11
 
12
12
  context "with an additional mock permission as a string" do
13
13
  it "returns an array of permissions" do
14
14
  subject.additional_mock_permissions_required = "internal_app"
15
- expected_permissions = ["signin", "internal_app"]
15
+ expected_permissions = %w[signin internal_app]
16
16
  expect(subject.permissions_for_dummy_api_user).to eq(expected_permissions)
17
17
  end
18
18
  end
19
19
 
20
20
  context "with additional mock permissions as an array" do
21
21
  it "returns an array of permissions" do
22
- subject.additional_mock_permissions_required = ["another_permission", "yet_another_permission"]
23
- expected_permissions = ["signin", "another_permission", "yet_another_permission"]
22
+ subject.additional_mock_permissions_required = %w[another_permission yet_another_permission]
23
+ expected_permissions = %w[signin another_permission yet_another_permission]
24
24
  expect(subject.permissions_for_dummy_api_user).to eq(expected_permissions)
25
25
  end
26
26
  end