gds-sso 20.0.0 → 21.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0ce653302bc22f4fd60d83307eff612eac4a491eff439eed0c602c288d798ef9
4
- data.tar.gz: de16391f9abe70acb77dbb6b00ca2b2f3e5da82965777bf281099e03e8420204
3
+ metadata.gz: 1cce044b9dcc019806b1a70222fb30c99abce4df707245ae7c4d795b92bba8d7
4
+ data.tar.gz: 966b8b0c9ef7b95ee5ec5229c8f087f1870447d4281b1b7b6bcc5fc5627155a6
5
5
  SHA512:
6
- metadata.gz: 51ad269ab4ba83b3b21c8fe9cdcc8b713c3b14c98e2068a4f6c06b198f75e7641ad087b15422bffabfcb5313e43ba255dce86f5ed6ea7ab6c0c8b92b151656ec
7
- data.tar.gz: 261cae8502648293bdd629d09379df37efd5ca632ffb0360463807d609174c9f532ebdce31993a48e55f6fd3576a09c792209a907c7647daa549b62623f044c3
6
+ metadata.gz: 3f3fe06419fd98b0018ff60ce33f45fe6a0173ac2c632009f2ece0d821539ae12503ad5f0a85716d2db16811e3e3bff4e234122ebff71a708fbcba13339bee78
7
+ data.tar.gz: eb36c389d730f589805c6e17bbdcdbf30f545cd2874431283082a437b0668312e5be7faa062f58149f9e5ea11f653c6ff52e67acace2213ad28f9383896912dc
data/README.md CHANGED
@@ -90,7 +90,7 @@ Authorization: Bearer your-token-here
90
90
 
91
91
  To avoid making these requests for each incoming request, this gem will [automatically cache a successful response](https://github.com/alphagov/gds-sso/blob/master/lib/gds-sso/bearer_token.rb), using the [Rails cache](https://github.com/alphagov/gds-sso/blob/master/lib/gds-sso/railtie.rb).
92
92
 
93
- If you are using a Rails 5 app in
93
+ If you are using a Rails app in
94
94
  [api_only](http://guides.rubyonrails.org/api_app.html) mode this gem will
95
95
  automatically disable the oauth layers which use session persistence. You can
96
96
  configure this gem to be in api_only mode (or not) with:
@@ -103,6 +103,19 @@ GDS::SSO.config do |config|
103
103
  end
104
104
  ```
105
105
 
106
+ For apps that have both usage of web and API you can configure a lambda to
107
+ match your API endpoints to ensure they don't support session auth and return
108
+ JSON error messages:
109
+
110
+
111
+ ```ruby
112
+ GDS::SSO.config do |config|
113
+ # ...
114
+ #
115
+ config.api_request_matcher = ->(request) { request.path.start_with?("/api/") }
116
+ end
117
+ ```
118
+
106
119
  ### Use in production mode
107
120
 
108
121
  To use gds-sso in production you will need to setup the following environment variables, which we look for in [the config](https://github.com/alphagov/gds-sso/blob/master/lib/gds-sso/config.rb). You will need to have admin access to Signon to get these.
@@ -1,8 +1,29 @@
1
+ require "rack/request"
2
+
1
3
  module GDS
2
4
  module SSO
3
5
  class ApiAccess
4
6
  def self.api_call?(env)
5
- env["HTTP_AUTHORIZATION"].to_s =~ /\ABearer /
7
+ return env["gds_sso.api_call"] unless env["gds_sso.api_call"].nil?
8
+ return true if GDS::SSO::Config.api_only
9
+
10
+ if GDS::SSO::Config.api_request_matcher
11
+ return GDS::SSO::Config.api_request_matcher.call(Rack::Request.new(env))
12
+ end
13
+
14
+ !bearer_token(env).nil?
15
+ end
16
+
17
+ def self.bearer_token(env)
18
+ Rack::Auth::AbstractRequest::AUTHORIZATION_KEYS.each do |key|
19
+ next unless env.key?(key)
20
+
21
+ if (match = env[key].match(/\ABearer (.+)/))
22
+ return match[1]
23
+ end
24
+ end
25
+
26
+ nil
6
27
  end
7
28
  end
8
29
  end
@@ -6,10 +6,9 @@ module GDS
6
6
  end
7
7
 
8
8
  def matches?(request)
9
- warden = request.env["warden"]
10
- warden.authenticate! if !warden.authenticated? || warden.user.remotely_signed_out?
9
+ user = GDS::SSO.authenticate_user!(request.env["warden"])
11
10
 
12
- GDS::SSO::AuthoriseUser.call(warden.user, permissions)
11
+ GDS::SSO::AuthoriseUser.call(user, permissions)
13
12
  true
14
13
  end
15
14
 
@@ -6,6 +6,8 @@ module GDS
6
6
  module SSO
7
7
  module BearerToken
8
8
  def self.locate(token_string)
9
+ return if token_string.nil? || token_string.empty?
10
+
9
11
  user_details = GDS::SSO::Config.cache.fetch(["api-user-cache", token_string], expires_in: 5.minutes) do
10
12
  access_token = OAuth2::AccessToken.new(oauth_client, token_string)
11
13
  response_body = access_token.get("/user.json?client_id=#{CGI.escape(GDS::SSO::Config.oauth_id)}").body
@@ -56,7 +58,9 @@ module GDS
56
58
 
57
59
  module MockBearerToken
58
60
  def self.locate(_token_string)
59
- dummy_api_user = GDS::SSO.test_user || GDS::SSO::Config.user_klass.where(email: "dummyapiuser@domain.com").first
61
+ return GDS::SSO.test_user if GDS::SSO.test_user
62
+
63
+ dummy_api_user = GDS::SSO::Config.user_klass.where(email: "dummyapiuser@domain.com").first
60
64
  if dummy_api_user.nil?
61
65
  dummy_api_user = GDS::SSO::Config.user_klass.new
62
66
  dummy_api_user.email = "dummyapiuser@domain.com"
@@ -29,6 +29,8 @@ module GDS
29
29
 
30
30
  mattr_accessor :api_only
31
31
 
32
+ mattr_accessor :api_request_matcher
33
+
32
34
  mattr_accessor :intercept_401_responses
33
35
  @@intercept_401_responses = true
34
36
 
@@ -4,17 +4,9 @@ module GDS
4
4
  end
5
5
 
6
6
  module ControllerMethods
7
- # TODO: remove this for the next major release
8
- class PermissionDeniedException < PermissionDeniedError
9
- def initialize(...)
10
- warn "GDS::SSO::ControllerMethods::PermissionDeniedException is deprecated, please replace with GDS::SSO::PermissionDeniedError"
11
- super(...)
12
- end
13
- end
14
-
15
7
  def self.included(base)
16
8
  base.rescue_from PermissionDeniedError do |e|
17
- if GDS::SSO::Config.api_only
9
+ if GDS::SSO::ApiAccess.api_call?(request.env)
18
10
  render json: { message: e.message }, status: :forbidden
19
11
  else
20
12
  render "authorisations/unauthorised", layout: "unauthorised", status: :forbidden, locals: { message: e.message }
@@ -6,20 +6,19 @@ require "rails"
6
6
  module GDS
7
7
  module SSO
8
8
  class FailureApp < ActionController::Metal
9
- include ActionController::UrlFor
10
9
  include ActionController::Redirecting
11
10
  include AbstractController::Rendering
12
11
  include ActionController::Rendering
13
12
  include ActionController::Renderers
14
13
  use_renderers :json
15
14
 
16
- include Rails.application.routes.url_helpers
17
-
18
15
  def self.call(env)
19
- if GDS::SSO::ApiAccess.api_call?(env)
20
- action(:api_invalid_token).call(env)
21
- elsif GDS::SSO::Config.api_only
22
- action(:api_missing_token).call(env)
16
+ if env["gds_sso.api_call"]
17
+ if env["gds_sso.api_bearer_token_present"]
18
+ action(:api_invalid_token).call(env)
19
+ else
20
+ action(:api_missing_token).call(env)
21
+ end
23
22
  else
24
23
  action(:redirect).call(env)
25
24
  end
@@ -1,5 +1,5 @@
1
1
  module GDS
2
2
  module SSO
3
- VERSION = "20.0.0".freeze
3
+ VERSION = "21.0.0".freeze
4
4
  end
5
5
  end
@@ -6,6 +6,12 @@ def logger
6
6
  Rails.logger || env["rack.logger"]
7
7
  end
8
8
 
9
+ Warden::Manager.on_request do |proxy|
10
+ proxy.env["gds_sso.api_call"] ||= ::GDS::SSO::ApiAccess.api_call?(proxy.env)
11
+ proxy.env["gds_sso.api_bearer_token_present"] ||=
12
+ proxy.env["gds_sso.api_call"] && !::GDS::SSO::ApiAccess.bearer_token(proxy.env).nil?
13
+ end
14
+
9
15
  Warden::Manager.after_authentication do |user, _auth, _opts|
10
16
  # We've successfully signed in.
11
17
  # If they were remotely signed out, clear the flag as they're no longer suspended
@@ -35,7 +41,7 @@ end
35
41
 
36
42
  Warden::Strategies.add(:gds_sso) do
37
43
  def valid?
38
- !::GDS::SSO::ApiAccess.api_call?(env)
44
+ !env["gds_sso.api_call"]
39
45
  end
40
46
 
41
47
  def authenticate!
@@ -61,11 +67,32 @@ end
61
67
  Warden::OAuth2.configure do |config|
62
68
  config.token_model = GDS::SSO::Config.use_mock_strategies? ? GDS::SSO::MockBearerToken : GDS::SSO::BearerToken
63
69
  end
64
- Warden::Strategies.add(:gds_bearer_token, Warden::OAuth2::Strategies::Bearer)
70
+
71
+ # We're using our own bearer token strategy rather than the one in Warden::OAuth2
72
+ # so that we can have all requests match either a bearer token or a session
73
+ # strategy. It also allows us to avoid multiple DB queries to locate a user.
74
+ Warden::Strategies.add(:gds_bearer_token, Class.new(Warden::OAuth2::Strategies::Token)) do
75
+ def valid?
76
+ env["gds_sso.api_call"]
77
+ end
78
+
79
+ def token_string
80
+ @token_string ||= GDS::SSO::ApiAccess.bearer_token(env)
81
+ end
82
+
83
+ def token
84
+ # Using a defined? based memo approach over @token ||= so that we can set
85
+ # @token to nil and not re-evaluate the assignment.
86
+ return @token if defined? @token
87
+
88
+ @token = Warden::OAuth2.config.token_model.locate(token_string)
89
+ @token
90
+ end
91
+ end
65
92
 
66
93
  Warden::Strategies.add(:mock_gds_sso) do
67
94
  def valid?
68
- !::GDS::SSO::ApiAccess.api_call?(env)
95
+ !env["gds_sso.api_call"]
69
96
  end
70
97
 
71
98
  def authenticate!
data/lib/gds-sso.rb CHANGED
@@ -25,6 +25,12 @@ module GDS
25
25
  yield GDS::SSO::Config
26
26
  end
27
27
 
28
+ def self.authenticate_user!(warden)
29
+ warden.authenticate! if !warden.authenticated? || warden.user.remotely_signed_out?
30
+
31
+ warden.user
32
+ end
33
+
28
34
  class Engine < ::Rails::Engine
29
35
  # Force routes to be loaded if we are doing any eager load.
30
36
  # TODO - check this one - Stolen from Devise because it looked sensible...
@@ -80,6 +80,16 @@ RSpec.describe "Authenication and authorisation" do
80
80
  end
81
81
  end
82
82
 
83
+ it "restricts access when the request doesn't match the api_request_matcher" do
84
+ allow(GDS::SSO::Config)
85
+ .to receive(:api_request_matcher)
86
+ .and_return(->(_request) { false })
87
+
88
+ visit "/restricted"
89
+ expect(page.status_code).to eql(302)
90
+ expect(page.response_headers["Location"]).to match("/auth/gds")
91
+ end
92
+
83
93
  it "allows access when given a valid bearer token" do
84
94
  stub_signon_user_request
85
95
  page.driver.header("Authorization", "Bearer 123")
@@ -95,13 +105,57 @@ RSpec.describe "Authenication and authorisation" do
95
105
 
96
106
  visit "/restricted"
97
107
  expect(page.status_code).to eq(401)
108
+ expect(page.response_headers["WWW-Authenticate"]).to eq('Bearer error="invalid_token"')
109
+ expect_json_response({ "message" => "Bearer token does not appear to be valid" })
98
110
  end
99
111
 
100
- it "returns a 401 when a bearer token is missing and the app is api_only" do
112
+ it "returns a JSON 401 when a bearer token is missing and the app is api_only" do
101
113
  allow(GDS::SSO::Config).to receive(:api_only).and_return(true)
102
114
 
103
115
  visit "/restricted"
104
116
  expect(page.status_code).to eq(401)
117
+ expect(page.response_headers["WWW-Authenticate"]).to eq('Bearer error="invalid_request"')
118
+ expect_json_response({ "message" => "No bearer token was provided" })
119
+ end
120
+
121
+ it "returns a JSON 401 when a bearer token is missing and the request matches the api_request_matcher" do
122
+ allow(GDS::SSO::Config)
123
+ .to receive(:api_request_matcher)
124
+ .and_return(->(request) { request.path == "/restricted" })
125
+
126
+ visit "/restricted"
127
+ expect(page.status_code).to eq(401)
128
+ expect(page.response_headers["WWW-Authenticate"]).to eq('Bearer error="invalid_request"')
129
+ expect_json_response({ "message" => "No bearer token was provided" })
130
+ end
131
+ end
132
+
133
+ context "when accessing a route that requires authentication with the mock strategies" do
134
+ before do
135
+ # Using allow_any_instance_of because it's hard to access the instance
136
+ # of the class used within the Rails middleware
137
+ allow_any_instance_of(Warden::Config).to receive(:[]).and_call_original
138
+ allow_any_instance_of(Warden::Config)
139
+ .to receive(:[])
140
+ .with(:default_strategies)
141
+ .and_return({ _all: %i[mock_gds_sso gds_bearer_token] })
142
+
143
+ allow(Warden::OAuth2.config).to receive(:token_model).and_return(GDS::SSO::MockBearerToken)
144
+ allow(GDS::SSO).to receive(:test_user).and_return(TestUser.new)
145
+ end
146
+
147
+ it "allows access without being logged in" do
148
+ visit "/restricted"
149
+ expect(page.status_code).to eq(200)
150
+ expect(page.body).to have_content("restricted kablooie")
151
+ end
152
+
153
+ it "allows access to an API mock user" do
154
+ allow(GDS::SSO::Config).to receive(:api_only).and_return(true)
155
+
156
+ visit "/restricted"
157
+ expect(page.status_code).to eq(200)
158
+ expect(page.body).to have_content("restricted kablooie")
105
159
  end
106
160
  end
107
161
 
@@ -116,6 +170,20 @@ RSpec.describe "Authenication and authorisation" do
116
170
  stub_signon_authenticated
117
171
  visit "/this-requires-execute-permission"
118
172
  expect(page.status_code).to eq(403)
173
+ expect(page).to have_content("Sorry, you don't seem to have the execute permission for this app.")
174
+ end
175
+
176
+ it "returns a JSON response when it's an API call" do
177
+ allow(GDS::SSO::Config)
178
+ .to receive(:api_request_matcher)
179
+ .and_return(->(request) { request.path == "/this-requires-execute-permission" })
180
+
181
+ stub_signon_user_request
182
+ page.driver.header("Authorization", "Bearer 123")
183
+
184
+ visit "/this-requires-execute-permission"
185
+ expect(page.status_code).to eq(403)
186
+ expect_json_response({ "message" => "Sorry, you don't seem to have the execute permission for this app." })
119
187
  end
120
188
  end
121
189
 
@@ -169,4 +237,9 @@ RSpec.describe "Authenication and authorisation" do
169
237
  headers: { content_type: "application/json" },
170
238
  )
171
239
  end
240
+
241
+ def expect_json_response(json_match)
242
+ expect(page.response_headers["content-type"]).to match(/application\/json/)
243
+ expect(JSON.parse(page.body)).to match(json_match)
244
+ end
172
245
  end
@@ -1,17 +1,71 @@
1
1
  require "spec_helper"
2
2
  require "gds-sso/api_access"
3
+ require "rack/mock_request"
3
4
 
4
5
  describe GDS::SSO::ApiAccess do
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
+ describe ".api_call?" do
7
+ it "returns true if the rack env has already been tagged as an api_call" do
8
+ expect(described_class.api_call?({ "gds_sso.api_call" => true })).to be(true)
9
+ end
10
+
11
+ it "returns false if the rack env has already been tagged as not an api_call" do
12
+ expect(described_class.api_call?({ "gds_sso.api_call" => false })).to be(false)
13
+ end
14
+
15
+ it "returns true if GDS::SSO has been configured as api_only" do
16
+ allow(GDS::SSO::Config).to receive(:api_only).and_return(true)
17
+
18
+ expect(described_class.api_call?({})).to be(true)
19
+ end
20
+
21
+ it "returns true if the request matches the api_request_matcher" do
22
+ allow(GDS::SSO::Config)
23
+ .to receive(:api_request_matcher)
24
+ .and_return(->(request) { request.path == "/api" })
25
+
26
+ env = Rack::MockRequest.env_for("/api")
27
+ expect(described_class.api_call?(env)).to be(true)
28
+ end
29
+
30
+ it "returns false if the request doesn't match the api_request_matcher" do
31
+ allow(GDS::SSO::Config)
32
+ .to receive(:api_request_matcher)
33
+ .and_return(->(request) { request.path == "/api" })
34
+
35
+ env = Rack::MockRequest.env_for("/other")
36
+ expect(described_class.api_call?(env)).to be(false)
37
+ end
38
+
39
+ it "returns true if a bearer token is present" do
40
+ env = { "HTTP_AUTHORIZATION" => "Bearer 1234:5678" }
41
+ expect(described_class.api_call?(env)).to be(true)
42
+ end
43
+
44
+ it "returns false otherwise" do
45
+ expect(described_class.api_call?({})).to be(false)
46
+ end
10
47
  end
11
48
 
12
- context "with a bearer token" do
13
- it "it is considered an api call" do
14
- expect(GDS::SSO::ApiAccess.api_call?("HTTP_AUTHORIZATION" => "Bearer deadbeef12345678")).to be_truthy
49
+ describe ".bearer_token" do
50
+ it "returns a bearer token set in a HTTP_AUTHORIZATION header" do
51
+ env = { "HTTP_AUTHORIZATION" => "Bearer 1234:5678" }
52
+ expect(described_class.bearer_token(env)).to eq("1234:5678")
53
+ end
54
+
55
+ it "returns nil for an empty bearer token in the HTTP_AUTHORIZATION header" do
56
+ env = { "HTTP_AUTHORIZATION" => "Bearer " }
57
+ expect(described_class.bearer_token(env)).to be_nil
58
+ end
59
+
60
+ it "supports all the authorization headers configured in Rack::Auth::AbstractRequest::AUTHORIZATION_KEYS" do
61
+ Rack::Auth::AbstractRequest::AUTHORIZATION_KEYS.each do |header|
62
+ env = { header => "Bearer 1234" }
63
+ expect(described_class.bearer_token(env)).to eq("1234")
64
+ end
65
+ end
66
+
67
+ it "returns nil if a bearer token isn't set" do
68
+ expect(described_class.bearer_token({})).to be_nil
15
69
  end
16
70
  end
17
71
  end
@@ -3,48 +3,25 @@ require "gds-sso/authorised_user_constraint"
3
3
 
4
4
  describe GDS::SSO::AuthorisedUserConstraint do
5
5
  before do
6
+ allow(GDS::SSO).to receive(:authenticate_user!).and_return(user)
6
7
  allow(GDS::SSO::AuthoriseUser).to receive(:call).and_return(true)
7
8
  end
8
9
 
9
10
  describe "#matches?" do
10
- let(:user) { double("user", remotely_signed_out?: remotely_signed_out) }
11
- let(:warden) do
12
- double(
13
- "warden",
14
- authenticated?: user_authenticated,
15
- user:,
16
- authenticate!: nil,
17
- )
18
- end
19
- let(:user_authenticated) { true }
20
- let(:remotely_signed_out) { false }
11
+ let(:user) { TestUser.new }
12
+ let(:warden) { instance_double("Warden::Proxy") }
21
13
  let(:request) { double("request", env: { "warden" => warden }) }
22
14
 
23
- it "authorises the user" do
24
- expect(GDS::SSO::AuthoriseUser).to receive(:call).with(warden.user, %w[signin])
25
- expect(warden).not_to receive(:authenticate!)
15
+ it "authenticates the user" do
16
+ expect(GDS::SSO).to receive(:authenticate_user!).with(warden)
26
17
 
27
18
  described_class.new(%w[signin]).matches?(request)
28
19
  end
29
20
 
30
- context "when the user is not authenticated" do
31
- let(:user_authenticated) { false }
32
-
33
- it "authenticates the user" do
34
- expect(warden).to receive(:authenticate!)
35
-
36
- described_class.new(%w[signin]).matches?(request)
37
- end
38
- end
39
-
40
- context "when the user is remotely signed out" do
41
- let(:remotely_signed_out) { true }
42
-
43
- it "authenticates the user" do
44
- expect(warden).to receive(:authenticate!)
21
+ it "authorises the user" do
22
+ expect(GDS::SSO::AuthoriseUser).to receive(:call).with(user, %w[signin])
45
23
 
46
- described_class.new(%w[signin]).matches?(request)
47
- end
24
+ described_class.new(%w[signin]).matches?(request)
48
25
  end
49
26
  end
50
27
  end
@@ -25,5 +25,13 @@ describe GDS::SSO::BearerToken do
25
25
 
26
26
  expect(same_user_again.id).to eql(created_user.id)
27
27
  end
28
+
29
+ it "returns nil for a nil token string" do
30
+ expect(described_class.locate(nil)).to be_nil
31
+ end
32
+
33
+ it "returns nil for an empty token string" do
34
+ expect(described_class.locate("")).to be_nil
35
+ end
28
36
  end
29
37
  end
@@ -0,0 +1,37 @@
1
+ require "spec_helper"
2
+
3
+ describe GDS::SSO do
4
+ describe "#authenticate_user!" do
5
+ let(:user) { TestUser.new }
6
+ let(:warden) do
7
+ instance_double("Warden::Proxy",
8
+ authenticate!: true,
9
+ authenticated?: false,
10
+ user:)
11
+ end
12
+
13
+ context "when a user is not already authenticated" do
14
+ it "authenticates the user and returns the user object" do
15
+ expect(described_class.authenticate_user!(warden)).to be(user)
16
+ expect(warden).to have_received(:authenticate!)
17
+ end
18
+ end
19
+
20
+ context "when a user is already authenticated and not remotely signed out" do
21
+ it "doesn't reauthenticate the user" do
22
+ allow(warden).to receive(:authenticated?).and_return(true)
23
+ expect(described_class.authenticate_user!(warden)).to be(user)
24
+
25
+ expect(warden).not_to have_received(:authenticate!)
26
+ end
27
+ end
28
+
29
+ context "when a user is already authenticated and remotely signed out" do
30
+ it "authenticates the user again" do
31
+ user.remotely_signed_out = true
32
+ expect(described_class.authenticate_user!(warden)).to be(user)
33
+ expect(warden).to have_received(:authenticate!)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -2,21 +2,54 @@ require "spec_helper"
2
2
  require "gds-sso/bearer_token"
3
3
 
4
4
  describe GDS::SSO::MockBearerToken do
5
- it "updates the permissions of the user" do
6
- # setup - ensure extra mock permissions required are nil and
7
- # call .locate to create the dummy user initially
8
- GDS::SSO::Config.additional_mock_permissions_required = nil
9
- dummy_user = subject.locate("ABC")
10
- expect(dummy_user.permissions).to match_array(%w[signin])
11
-
12
- # add an extra permission
13
- GDS::SSO::Config.additional_mock_permissions_required = "extra_permission"
14
-
15
- # ensure the dummy user is returned
16
- expect(GDS::SSO).to receive(:test_user).and_return(dummy_user)
17
-
18
- # call .locate again...this should update our permissions
19
- dummy_user_two = subject.locate("ABC")
20
- expect(dummy_user_two.permissions).to match_array(%w[signin extra_permission])
5
+ describe ".locate" do
6
+ it "returns a GDS::SSO.test_user if one is set" do
7
+ test_user = TestUser.new
8
+ allow(GDS::SSO).to receive(:test_user).and_return(test_user)
9
+
10
+ expect(described_class.locate("anything")).to be(test_user)
11
+ end
12
+
13
+ it "doesn't modify the permissions of GDS::SSO.test_user" do
14
+ test_user = TestUser.new(permissions: [])
15
+ allow(GDS::SSO).to receive(:test_user).and_return(test_user)
16
+ allow(GDS::SSO::Config).to receive(:permissions_for_dummy_api_user)
17
+ .and_return(%w[signin extra_permission])
18
+
19
+ expect(described_class.locate("anything")).to be(test_user)
20
+ expect(test_user.permissions).not_to include("extra_permission")
21
+ end
22
+
23
+ it "returns a user with dummyapiuser@domain.com if one exists" do
24
+ test_user = TestUser.new
25
+ allow(GDS::SSO::Config).to receive(:user_klass).and_return(TestUser)
26
+ allow(TestUser).to receive(:where).and_return([test_user])
27
+
28
+ expect(described_class.locate("anything")).to be(test_user)
29
+ expect(TestUser).to have_received(:where).with(email: "dummyapiuser@domain.com")
30
+ end
31
+
32
+ it "creates a user with dummyapiuser@domain.com if one does not exist" do
33
+ allow(GDS::SSO::Config).to receive(:user_klass).and_return(TestUser)
34
+ allow(GDS::SSO::Config).to receive(:additional_mock_permissions_required).and_return(nil)
35
+ allow(TestUser).to receive(:where).and_return([])
36
+
37
+ test_user = described_class.locate("anything")
38
+ expect(test_user).to be_an_instance_of(TestUser)
39
+ expect(test_user).to have_attributes(email: "dummyapiuser@domain.com",
40
+ name: "Dummy API user created by gds-sso",
41
+ permissions: %w[signin])
42
+ end
43
+
44
+ it "uses GDS::SSO::Config to overwrite any existing permissions" do
45
+ test_user = TestUser.new(permissions: %w[signin other_permission])
46
+ allow(GDS::SSO::Config).to receive(:user_klass).and_return(TestUser)
47
+ allow(TestUser).to receive(:where).and_return([test_user])
48
+ allow(GDS::SSO::Config).to receive(:permissions_for_dummy_api_user)
49
+ .and_return(%w[signin extra_permission])
50
+
51
+ test_user = described_class.locate("anything")
52
+ expect(test_user.permissions).to match_array(%w[signin extra_permission])
53
+ end
21
54
  end
22
55
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gds-sso
3
3
  version: !ruby/object:Gem::Version
4
- version: 20.0.0
4
+ version: 21.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GOV.UK Dev
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-26 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: oauth2
@@ -65,6 +65,20 @@ dependencies:
65
65
  - - ">="
66
66
  - !ruby/object:Gem::Version
67
67
  version: '5'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rack
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
68
82
  - !ruby/object:Gem::Dependency
69
83
  name: rails
70
84
  requirement: !ruby/object:Gem::Requirement
@@ -271,6 +285,7 @@ files:
271
285
  - spec/unit/authorised_user_constraint_spec.rb
272
286
  - spec/unit/bearer_token_spec.rb
273
287
  - spec/unit/config_spec.rb
288
+ - spec/unit/gds_sso_spec.rb
274
289
  - spec/unit/mock_bearer_token_spec.rb
275
290
  - spec/unit/railtie_spec.rb
276
291
  - spec/unit/session_serialisation_spec.rb
@@ -293,7 +308,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
293
308
  - !ruby/object:Gem::Version
294
309
  version: '0'
295
310
  requirements: []
296
- rubygems_version: 3.6.6
311
+ rubygems_version: 3.7.1
297
312
  specification_version: 4
298
313
  summary: Client for GDS' OAuth 2-based SSO
299
314
  test_files:
@@ -318,6 +333,7 @@ test_files:
318
333
  - spec/unit/authorised_user_constraint_spec.rb
319
334
  - spec/unit/bearer_token_spec.rb
320
335
  - spec/unit/config_spec.rb
336
+ - spec/unit/gds_sso_spec.rb
321
337
  - spec/unit/mock_bearer_token_spec.rb
322
338
  - spec/unit/railtie_spec.rb
323
339
  - spec/unit/session_serialisation_spec.rb