gds-sso 14.3.0 → 16.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) 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/app/views/layouts/unauthorised.html.erb +1 -1
  7. data/config/routes.rb +7 -6
  8. data/lib/gds-sso.rb +27 -24
  9. data/lib/gds-sso/api_access.rb +1 -1
  10. data/lib/gds-sso/bearer_token.rb +24 -24
  11. data/lib/gds-sso/config.rb +13 -12
  12. data/lib/gds-sso/controller_methods.rb +7 -8
  13. data/lib/gds-sso/failure_app.rb +8 -8
  14. data/lib/gds-sso/lint/user_spec.rb +27 -28
  15. data/lib/gds-sso/lint/user_test.rb +28 -28
  16. data/lib/gds-sso/railtie.rb +12 -0
  17. data/lib/gds-sso/user.rb +13 -13
  18. data/lib/gds-sso/version.rb +1 -1
  19. data/lib/gds-sso/warden_config.rb +21 -31
  20. data/spec/controller/api_user_controller_spec.rb +40 -37
  21. data/spec/controller/controller_methods_spec.rb +28 -42
  22. data/spec/internal/app/assets/config/manifest.js +0 -0
  23. data/spec/internal/app/controllers/application_controller.rb +1 -1
  24. data/spec/internal/app/controllers/example_controller.rb +1 -2
  25. data/spec/internal/config/initializers/gds-sso.rb +2 -2
  26. data/spec/internal/config/routes.rb +5 -2
  27. data/spec/internal/config/storage.yml +3 -0
  28. data/spec/internal/db/combustion_test.sqlite +0 -0
  29. data/spec/internal/db/schema.rb +9 -5
  30. data/spec/internal/log/test.log +1142 -1200
  31. data/spec/requests/end_to_end_spec.rb +45 -46
  32. data/spec/spec_helper.rb +12 -13
  33. data/spec/support/controller_spy.rb +14 -0
  34. data/spec/support/serializable_user.rb +3 -0
  35. data/spec/support/signon_integration_helpers.rb +10 -8
  36. data/spec/support/test_user.rb +29 -0
  37. data/spec/support/timecop.rb +1 -1
  38. data/spec/unit/api_access_spec.rb +7 -7
  39. data/spec/unit/bearer_token_spec.rb +14 -15
  40. data/spec/unit/config_spec.rb +5 -5
  41. data/spec/unit/mock_bearer_token_spec.rb +4 -4
  42. data/spec/unit/railtie_spec.rb +14 -0
  43. data/spec/unit/session_serialisation_spec.rb +5 -9
  44. data/spec/unit/user_spec.rb +20 -51
  45. metadata +94 -54
@@ -6,20 +6,19 @@ module GDS
6
6
 
7
7
  def self.included(base)
8
8
  base.rescue_from PermissionDeniedException do |e|
9
- if GDS::SSO::Config.api_only?
9
+ if GDS::SSO::Config.api_only
10
10
  render json: { message: e.message }, status: :forbidden
11
11
  else
12
12
  render "authorisations/unauthorised", layout: "unauthorised", status: :forbidden, locals: { message: e.message }
13
13
  end
14
14
  end
15
15
 
16
- unless GDS::SSO::Config.api_only?
16
+ unless GDS::SSO::Config.api_only
17
17
  base.helper_method :user_signed_in?
18
18
  base.helper_method :current_user
19
19
  end
20
20
  end
21
21
 
22
-
23
22
  def authorise_user!(permissions)
24
23
  # Ensure that we're authenticated (and by extension that current_user is set).
25
24
  # Otherwise current_user might be nil, and we'd error out
@@ -52,7 +51,7 @@ module GDS
52
51
  end
53
52
 
54
53
  def user_signed_in?
55
- warden && warden.authenticated? && ! warden.user.remotely_signed_out?
54
+ warden && warden.authenticated? && !warden.user.remotely_signed_out?
56
55
  end
57
56
 
58
57
  def current_user
@@ -64,22 +63,22 @@ module GDS
64
63
  end
65
64
 
66
65
  def warden
67
- request.env['warden']
66
+ request.env["warden"]
68
67
  end
69
68
 
70
- private
69
+ private
71
70
 
72
71
  def authorise_user_with_at_least_one_of_permissions!(permissions)
73
72
  if permissions.none? { |permission| current_user.has_permission?(permission) }
74
73
  raise PermissionDeniedException,
75
- "Sorry, you don't seem to have any of the permissions: #{permissions.to_sentence} for this app."
74
+ "Sorry, you don't seem to have any of the permissions: #{permissions.to_sentence} for this app."
76
75
  end
77
76
  end
78
77
 
79
78
  def authorise_user_with_all_permissions!(permissions)
80
79
  unless permissions.all? { |permission| current_user.has_permission?(permission) }
81
80
  raise PermissionDeniedException,
82
- "Sorry, you don't seem to have all of the permissions: #{permissions.to_sentence} for this app."
81
+ "Sorry, you don't seem to have all of the permissions: #{permissions.to_sentence} for this app."
83
82
  end
84
83
  end
85
84
  end
@@ -1,5 +1,5 @@
1
1
  require "action_controller/metal"
2
- require 'rails'
2
+ require "rails"
3
3
 
4
4
  # Failure application that will be called every time :warden is thrown from
5
5
  # any strategy or hook.
@@ -18,7 +18,7 @@ module GDS
18
18
  def self.call(env)
19
19
  if GDS::SSO::ApiAccess.api_call?(env)
20
20
  action(:api_invalid_token).call(env)
21
- elsif GDS::SSO::Config.api_only?
21
+ elsif GDS::SSO::Config.api_only
22
22
  action(:api_missing_token).call(env)
23
23
  else
24
24
  action(:redirect).call(env)
@@ -27,15 +27,15 @@ module GDS
27
27
 
28
28
  def redirect
29
29
  store_location!
30
- redirect_to '/auth/gds'
30
+ redirect_to "/auth/gds"
31
31
  end
32
32
 
33
33
  def api_invalid_token
34
- api_unauthorized('Bearer token does not appear to be valid', 'invalid_token')
34
+ api_unauthorized("Bearer token does not appear to be valid", "invalid_token")
35
35
  end
36
36
 
37
37
  def api_missing_token
38
- api_unauthorized('No bearer token was provided', 'invalid_request')
38
+ api_unauthorized("No bearer token was provided", "invalid_request")
39
39
  end
40
40
 
41
41
  # Stores requested uri to redirect the user after signing in. We cannot use
@@ -45,13 +45,13 @@ module GDS
45
45
 
46
46
  # TOTALLY NOT DOING THE SCOPE THING. PROBABLY SHOULD.
47
47
  def store_location!
48
- session["return_to"] = request.env['warden.options'][:attempted_path] if request.get?
48
+ session["return_to"] = request.env["warden.options"][:attempted_path] if request.get?
49
49
  end
50
50
 
51
- private
51
+ private
52
52
 
53
53
  def api_unauthorized(message, bearer_error)
54
- headers['WWW-Authenticate'] = %(Bearer error="#{bearer_error}")
54
+ headers["WWW-Authenticate"] = %(Bearer error="#{bearer_error}")
55
55
  render json: { message: message }, status: :unauthorized
56
56
  end
57
57
  end
@@ -1,10 +1,10 @@
1
1
  RSpec.shared_examples "a gds-sso user class" do
2
- subject { described_class.new(:uid => '12345') }
2
+ subject { described_class.new(uid: "12345") }
3
3
 
4
4
  it "implements #where" do
5
5
  expect(described_class).to respond_to(:where)
6
6
 
7
- result = described_class.where(uid: '123')
7
+ result = described_class.where(uid: "123")
8
8
  expect(result).to respond_to(:first)
9
9
  end
10
10
 
@@ -15,8 +15,8 @@ RSpec.shared_examples "a gds-sso user class" do
15
15
  expect(subject).to be_remotely_signed_out
16
16
  end
17
17
 
18
- it "implements #update_attributes" do
19
- subject.update_attributes(email: "ab@c.com")
18
+ it "implements #update!" do
19
+ subject.update!(email: "ab@c.com")
20
20
  expect(subject.email).to eq("ab@c.com")
21
21
  end
22
22
 
@@ -30,50 +30,49 @@ RSpec.shared_examples "a gds-sso user class" do
30
30
 
31
31
  describe "#has_all_permissions?" do
32
32
  it "is false when there are no permissions" do
33
- subject.update_attributes(permissions: nil)
34
- required_permissions = ["signin"]
33
+ subject.update!(permissions: nil)
34
+ required_permissions = %w[signin]
35
35
  expect(subject.has_all_permissions?(required_permissions)).to be_falsy
36
36
  end
37
37
 
38
38
  it "is false when it does not have all required permissions" do
39
- subject.update_attributes(permissions: ["signin"])
40
- required_permissions = ["signin", "not_granted_permission_one", "not_granted_permission_two"]
39
+ subject.update!(permissions: %w[signin])
40
+ required_permissions = %w[signin not_granted_permission_one not_granted_permission_two]
41
41
  expect(subject.has_all_permissions?(required_permissions)).to be false
42
42
  end
43
43
 
44
44
  it "is true when it has all required permissions" do
45
- subject.update_attributes(permissions: ["signin", "internal_app"])
46
- required_permissions = ["signin", "internal_app"]
45
+ subject.update!(permissions: %w[signin internal_app])
46
+ required_permissions = %w[signin internal_app]
47
47
  expect(subject.has_all_permissions?(required_permissions)).to be true
48
48
  end
49
-
50
49
  end
51
50
 
52
51
  specify "the User class and GDS::SSO::User mixin work together" do
53
52
  auth_hash = {
54
- 'uid' => '12345',
55
- 'info' => {
56
- 'name' => 'Joe Smith',
57
- 'email' => 'joe.smith@example.com',
53
+ "uid" => "12345",
54
+ "info" => {
55
+ "name" => "Joe Smith",
56
+ "email" => "joe.smith@example.com",
57
+ },
58
+ "extra" => {
59
+ "user" => {
60
+ "disabled" => false,
61
+ "permissions" => %w[signin],
62
+ "organisation_slug" => "cabinet-office",
63
+ "organisation_content_id" => "91e57ad9-29a3-4f94-9ab4-5e9ae6d13588",
64
+ },
58
65
  },
59
- 'extra' => {
60
- 'user' => {
61
- 'disabled' => false,
62
- 'permissions' => ['signin'],
63
- 'organisation_slug' => 'cabinet-office',
64
- 'organisation_content_id' => '91e57ad9-29a3-4f94-9ab4-5e9ae6d13588'
65
- }
66
- }
67
66
  }
68
67
 
69
68
  user = described_class.find_for_gds_oauth(auth_hash)
70
69
  expect(user).to be_an_instance_of(described_class)
71
- expect(user.uid).to eq('12345')
70
+ expect(user.uid).to eq("12345")
72
71
  expect(user.name).to eq("Joe Smith")
73
- expect(user.email).to eq('joe.smith@example.com')
72
+ expect(user.email).to eq("joe.smith@example.com")
74
73
  expect(user).not_to be_disabled
75
- expect(user.permissions).to eq(['signin'])
76
- expect(user.organisation_slug).to eq('cabinet-office')
77
- expect(user.organisation_content_id).to eq('91e57ad9-29a3-4f94-9ab4-5e9ae6d13588')
74
+ expect(user.permissions).to eq(%w[signin])
75
+ expect(user.organisation_slug).to eq("cabinet-office")
76
+ expect(user.organisation_content_id).to eq("91e57ad9-29a3-4f94-9ab4-5e9ae6d13588")
78
77
  end
79
78
  end
@@ -17,58 +17,58 @@ module GDS
17
17
  #
18
18
  class UserTest < ActiveSupport::TestCase
19
19
  def user_class
20
- raise 'Reopen `GDS::SSO::Lint::UserTest` and add `#user_class` to return the class including `GDS::SSO::User`'
20
+ raise "Reopen `GDS::SSO::Lint::UserTest` and add `#user_class` to return the class including `GDS::SSO::User`"
21
21
  end
22
22
 
23
23
  setup do
24
- @lint_user = user_class.new(uid: '12345')
24
+ @lint_user = user_class.new(uid: "12345")
25
25
  end
26
26
 
27
- test 'implement #where' do
28
- result = user_class.where(uid: '123')
27
+ test "implement #where" do
28
+ result = user_class.where(uid: "123")
29
29
  assert result.respond_to?(:first)
30
30
  end
31
31
 
32
- test 'implement #update_attribute' do
32
+ test "implement #update_attribute" do
33
33
  @lint_user.update_attribute(:remotely_signed_out, true)
34
34
  assert @lint_user.remotely_signed_out?
35
35
  end
36
36
 
37
- test 'implement #update_attributes' do
38
- @lint_user.update_attributes(email: 'test@example.com')
39
- assert_equal @lint_user.email, 'test@example.com'
37
+ test "implement #update!" do
38
+ @lint_user.update!(email: "test@example.com")
39
+ assert_equal @lint_user.email, "test@example.com"
40
40
  end
41
41
 
42
- test 'implement #create!' do
42
+ test "implement #create!" do
43
43
  assert user_class.respond_to?(:create!)
44
44
  end
45
45
 
46
- test 'verify the User class and GDS::SSO::User work together' do
46
+ test "verify the User class and GDS::SSO::User work together" do
47
47
  auth_hash = {
48
- 'uid' => '12345',
49
- 'info' => {
50
- 'name' => 'Joe Smith',
51
- 'email' => 'joe.smith@example.com',
48
+ "uid" => "12345",
49
+ "info" => {
50
+ "name" => "Joe Smith",
51
+ "email" => "joe.smith@example.com",
52
+ },
53
+ "extra" => {
54
+ "user" => {
55
+ "disabled" => false,
56
+ "permissions" => %w[signin],
57
+ "organisation_slug" => "cabinet-office",
58
+ "organisation_content_id" => "91e57ad9-29a3-4f94-9ab4-5e9ae6d13588",
59
+ },
52
60
  },
53
- 'extra' => {
54
- 'user' => {
55
- 'disabled' => false,
56
- 'permissions' => ['signin'],
57
- 'organisation_slug' => 'cabinet-office',
58
- 'organisation_content_id' => '91e57ad9-29a3-4f94-9ab4-5e9ae6d13588',
59
- }
60
- }
61
61
  }
62
62
 
63
63
  user = user_class.find_for_gds_oauth(auth_hash)
64
64
  assert_equal user_class, user.class
65
- assert_equal '12345', user.uid
66
- assert_equal 'Joe Smith', user.name
67
- assert_equal 'joe.smith@example.com', user.email
65
+ assert_equal "12345", user.uid
66
+ assert_equal "Joe Smith", user.name
67
+ assert_equal "joe.smith@example.com", user.email
68
68
  assert_equal false, user.disabled
69
- assert_equal ['signin'], user.permissions
70
- assert_equal 'cabinet-office', user.organisation_slug
71
- assert_equal '91e57ad9-29a3-4f94-9ab4-5e9ae6d13588', user.organisation_content_id
69
+ assert_equal %w[signin], user.permissions
70
+ assert_equal "cabinet-office", user.organisation_slug
71
+ assert_equal "91e57ad9-29a3-4f94-9ab4-5e9ae6d13588", user.organisation_content_id
72
72
  end
73
73
  end
74
74
  end
@@ -0,0 +1,12 @@
1
+ module GDS
2
+ module SSO
3
+ class Railtie < Rails::Railtie
4
+ initializer "gds-sso.initializer" do
5
+ GDS::SSO.config do |config|
6
+ config.cache = Rails.cache
7
+ config.api_only = Rails.configuration.api_only
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
data/lib/gds-sso/user.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'active_support/concern'
1
+ require "active_support/concern"
2
2
 
3
3
  module GDS
4
4
  module SSO
@@ -21,32 +21,32 @@ module GDS
21
21
 
22
22
  def self.user_params_from_auth_hash(auth_hash)
23
23
  {
24
- 'uid' => auth_hash['uid'],
25
- 'email' => auth_hash['info']['email'],
26
- 'name' => auth_hash['info']['name'],
27
- 'permissions' => auth_hash['extra']['user']['permissions'],
28
- 'organisation_slug' => auth_hash['extra']['user']['organisation_slug'],
29
- 'organisation_content_id' => auth_hash['extra']['user']['organisation_content_id'],
30
- 'disabled' => auth_hash['extra']['user']['disabled'],
24
+ "uid" => auth_hash["uid"],
25
+ "email" => auth_hash["info"]["email"],
26
+ "name" => auth_hash["info"]["name"],
27
+ "permissions" => auth_hash["extra"]["user"]["permissions"],
28
+ "organisation_slug" => auth_hash["extra"]["user"]["organisation_slug"],
29
+ "organisation_content_id" => auth_hash["extra"]["user"]["organisation_content_id"],
30
+ "disabled" => auth_hash["extra"]["user"]["disabled"],
31
31
  }
32
32
  end
33
33
 
34
34
  def clear_remotely_signed_out!
35
- self.update_attribute(:remotely_signed_out, false)
35
+ update_attribute(:remotely_signed_out, false)
36
36
  end
37
37
 
38
38
  def set_remotely_signed_out!
39
- self.update_attribute(:remotely_signed_out, true)
39
+ update_attribute(:remotely_signed_out, true)
40
40
  end
41
41
 
42
42
  module ClassMethods
43
43
  def find_for_gds_oauth(auth_hash)
44
44
  user_params = GDS::SSO::User.user_params_from_auth_hash(auth_hash.to_hash)
45
- user = self.where(:uid => user_params['uid']).first ||
46
- self.where(:email => user_params['email']).first
45
+ user = where(uid: user_params["uid"]).first ||
46
+ where(email: user_params["email"]).first
47
47
 
48
48
  if user
49
- user.update_attributes(user_params)
49
+ user.update!(user_params)
50
50
  user
51
51
  else # Create a new user.
52
52
  create!(user_params)
@@ -1,5 +1,5 @@
1
1
  module GDS
2
2
  module SSO
3
- VERSION = "14.3.0"
3
+ VERSION = "16.0.1".freeze
4
4
  end
5
5
  end
@@ -1,63 +1,55 @@
1
- require 'warden'
2
- require 'warden-oauth2'
3
- require 'gds-sso/bearer_token'
1
+ require "warden"
2
+ require "warden-oauth2"
3
+ require "gds-sso/bearer_token"
4
4
 
5
5
  def logger
6
- if Rails.logger # if we are actually running in a rails app
7
- Rails.logger
8
- else
9
- env['rack.logger']
10
- end
6
+ Rails.logger || env["rack.logger"]
11
7
  end
12
8
 
13
- Warden::Manager.after_authentication do |user, auth, opts|
9
+ Warden::Manager.after_authentication do |user, _auth, _opts|
14
10
  # We've successfully signed in.
15
11
  # If they were remotely signed out, clear the flag as they're no longer suspended
16
12
  user.clear_remotely_signed_out!
17
13
  end
18
14
 
19
15
  Warden::Manager.serialize_into_session do |user|
20
- if user.respond_to?(:uid) and user.uid
16
+ if user.respond_to?(:uid) && user.uid
21
17
  [user.uid, Time.now.utc.iso8601]
22
- else
23
- nil
24
18
  end
25
19
  end
26
20
 
27
21
  Warden::Manager.serialize_from_session do |(uid, auth_timestamp)|
28
22
  # This will reject old sessions that don't have a previous login timestamp
29
23
  if auth_timestamp.is_a?(String)
30
- auth_timestamp = begin
31
- Time.parse(auth_timestamp)
24
+ begin
25
+ auth_timestamp = Time.parse(auth_timestamp)
32
26
  rescue ArgumentError
33
- nil
27
+ auth_timestamp = nil
34
28
  end
35
29
  end
36
30
 
37
- if auth_timestamp and (auth_timestamp + GDS::SSO::Config.auth_valid_for) > Time.now.utc
38
- GDS::SSO::Config.user_klass.where(:uid => uid, :remotely_signed_out => false).first
39
- else
40
- nil
31
+ if auth_timestamp && ((auth_timestamp + GDS::SSO::Config.auth_valid_for) > Time.now.utc)
32
+ GDS::SSO::Config.user_klass.where(uid: uid, remotely_signed_out: false).first
41
33
  end
42
34
  end
43
35
 
44
36
  Warden::Strategies.add(:gds_sso) do
45
37
  def valid?
46
- ! ::GDS::SSO::ApiAccess.api_call?(env)
38
+ !::GDS::SSO::ApiAccess.api_call?(env)
47
39
  end
48
40
 
49
41
  def authenticate!
50
42
  logger.debug("Authenticating with gds_sso strategy")
51
43
 
52
- if request.env['omniauth.auth'].nil?
44
+ if request.env["omniauth.auth"].nil?
53
45
  fail!("No credentials, bub")
54
46
  else
55
- user = prep_user(request.env['omniauth.auth'])
47
+ user = prep_user(request.env["omniauth.auth"])
56
48
  success!(user)
57
49
  end
58
50
  end
59
51
 
60
- private
52
+ private
61
53
 
62
54
  def prep_user(auth_hash)
63
55
  user = GDS::SSO::Config.user_klass.find_for_gds_oauth(auth_hash)
@@ -73,27 +65,25 @@ Warden::Strategies.add(:gds_bearer_token, Warden::OAuth2::Strategies::Bearer)
73
65
 
74
66
  Warden::Strategies.add(:mock_gds_sso) do
75
67
  def valid?
76
- ! ::GDS::SSO::ApiAccess.api_call?(env)
68
+ !::GDS::SSO::ApiAccess.api_call?(env)
77
69
  end
78
70
 
79
71
  def authenticate!
80
72
  logger.warn("Authenticating with mock_gds_sso strategy")
81
73
 
82
74
  test_user = GDS::SSO.test_user
83
- test_user ||= ENV['GDS_SSO_MOCK_INVALID'].present? ? nil : GDS::SSO::Config.user_klass.first
75
+ test_user ||= ENV["GDS_SSO_MOCK_INVALID"].present? ? nil : GDS::SSO::Config.user_klass.first
84
76
  if test_user
85
77
  # Brute force ensure test user has correct perms to signin
86
- if ! test_user.has_permission?("signin")
78
+ unless test_user.has_permission?("signin")
87
79
  permissions = test_user.permissions || []
88
80
  test_user.update_attribute(:permissions, permissions << "signin")
89
81
  end
90
82
  success!(test_user)
83
+ elsif Rails.env.test? && ENV["GDS_SSO_MOCK_INVALID"].present?
84
+ fail!(:invalid)
91
85
  else
92
- if Rails.env.test? && ENV['GDS_SSO_MOCK_INVALID'].present?
93
- fail!(:invalid)
94
- else
95
- raise "GDS-SSO running in mock mode and no test user found. Normally we'd load the first user in the database. Create a user in the database."
96
- end
86
+ raise "GDS-SSO running in mock mode and no test user found. Normally we'd load the first user in the database. Create a user in the database."
97
87
  end
98
88
  end
99
89
  end