diff --git a/Gemfile.lock b/Gemfile.lock index 7f815c7..1aa2a8e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -98,7 +98,7 @@ GEM base64 (0.1.1) bcrypt (3.1.19) bindata (2.4.15) - blacklight (7.33.1) + blacklight (7.34.0) deprecation globalid hashdiff @@ -107,7 +107,7 @@ GEM kaminari (>= 0.15) ostruct (>= 0.3.2) rails (>= 5.1, < 7.1) - view_component (~> 2.66) + view_component (>= 2.66, < 4) brakeman (6.0.1) builder (3.2.4) bundler-audit (0.9.1) @@ -134,7 +134,7 @@ GEM reline (>= 0.3.1) deprecation (1.1.0) activesupport - devise (4.9.2) + devise (4.9.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0) @@ -171,7 +171,7 @@ GEM i18n (1.14.1) concurrent-ruby (~> 1.0) io-console (0.6.0) - irb (1.8.1) + irb (1.8.3) rdoc reline (>= 0.3.8) jbuilder (2.11.5) @@ -199,7 +199,7 @@ GEM kaminari-core (1.2.2) language_server-protocol (3.17.0.3) lint_roller (1.1.0) - loofah (2.21.3) + loofah (2.21.4) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.8.1) @@ -215,14 +215,14 @@ GEM multi_json (1.15.0) multi_xml (0.6.0) mysql2 (0.5.5) - net-imap (0.3.7) + net-imap (0.4.1) date net-protocol net-pop (0.1.2) net-protocol net-protocol (0.2.1) timeout - net-smtp (0.3.3) + net-smtp (0.4.0) net-protocol nio4r (2.5.9) nokogiri (1.15.4-arm64-darwin) @@ -254,13 +254,13 @@ GEM orm_adapter (0.5.0) ostruct (0.5.5) parallel (1.23.0) - parser (3.2.2.3) + parser (3.2.2.4) ast (~> 2.4.1) racc - psych (5.1.0) + psych (5.1.1) stringio public_suffix (5.0.3) - puma (6.3.1) + puma (6.4.0) nio4r (~> 2.0) racc (1.7.1) rack (2.2.8) @@ -306,10 +306,10 @@ GEM rake (13.0.6) rdoc (6.5.0) psych (>= 4.0.0) - regexp_parser (2.8.1) - reline (0.3.8) + regexp_parser (2.8.2) + reline (0.3.9) io-console (~> 0.5) - responders (3.1.0) + responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) rexml (3.2.6) @@ -333,7 +333,7 @@ GEM rspec-mocks (~> 3.12) rspec-support (~> 3.12) rspec-support (3.12.1) - rubocop (1.56.3) + rubocop (1.56.4) base64 (~> 0.1.1) json (~> 2.3) language_server-protocol (>= 3.17.0) @@ -354,18 +354,18 @@ GEM rubocop-performance (1.19.1) rubocop (>= 1.7.0, < 2.0) rubocop-ast (>= 0.4.0) - rubocop-rails (2.21.1) + rubocop-rails (2.21.2) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 1.33.0, < 2.0) - rubocop-rspec (2.24.0) + rubocop-rspec (2.24.1) rubocop (~> 1.33) rubocop-capybara (~> 2.17) rubocop-factory_bot (~> 2.22) ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) rubyzip (2.3.2) - selenium-webdriver (4.12.0) + selenium-webdriver (4.14.0) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) @@ -390,28 +390,28 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) - standard (1.31.1) + standard (1.31.2) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.0) - rubocop (~> 1.56.2) + rubocop (~> 1.56.4) standard-custom (~> 1.0.0) standard-performance (~> 1.2) standard-custom (1.0.2) lint_roller (~> 1.0) rubocop (~> 1.50) - standard-performance (1.2.0) + standard-performance (1.2.1) lint_roller (~> 1.1) - rubocop-performance (~> 1.19.0) + rubocop-performance (~> 1.19.1) stringio (3.0.8) - strong_migrations (1.6.2) + strong_migrations (1.6.3) activerecord (>= 5.2) thor (1.2.2) timeout (0.4.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (2.4.2) + unicode-display_width (2.5.0) version_gem (1.1.3) - view_component (2.82.0) + view_component (3.6.0) activesupport (>= 5.2.0, < 8.0) concurrent-ruby (~> 1.0) method_source (~> 1.0) @@ -427,7 +427,7 @@ GEM websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.11) + zeitwerk (2.6.12) zk (1.10.0) zookeeper (~> 1.5.0) zookeeper (1.5.5) diff --git a/app/controllers/concerns/auth_session_concern.rb b/app/controllers/concerns/auth_session_concern.rb new file mode 100644 index 0000000..8836075 --- /dev/null +++ b/app/controllers/concerns/auth_session_concern.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module AuthSessionConcern + extend ActiveSupport::Concern + + def new_session_path(_scope) + new_user_session_path + end +end diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb index 6ab37e2..ed93dfb 100644 --- a/app/controllers/users/omniauth_callbacks_controller.rb +++ b/app/controllers/users/omniauth_callbacks_controller.rb @@ -22,6 +22,13 @@ def catalogue_shared sign_in_and_redirect @user, event: :authentication end + def catalogue_patron + Rails.logger.debug(request.env["omniauth.auth"]) + @user = User.from_keycloak_patron(request.env["omniauth.auth"]) + store_keycloak_data(request.env["omniauth.auth"]) + sign_in_and_redirect @user, event: :authentication + end + # Keycloak will display its own error page when there is a failure to login. # :nocov: def failure diff --git a/app/models/user.rb b/app/models/user.rb index 74c2492..3da843d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -31,8 +31,12 @@ class User < PatronsRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable - devise :user_reg_authenticatable, :timeoutable, - :omniauthable, omniauth_providers: %i[catalogue_sol catalogue_spl catalogue_shared] + if ENV["KC_PATRON_REALM"] + devise :timeoutable, :omniauthable, omniauth_providers: %i[catalogue_patron catalogue_sol catalogue_spl catalogue_shared] + else + devise :user_reg_authenticatable, :timeoutable, + :omniauthable, omniauth_providers: %i[catalogue_patron catalogue_sol catalogue_spl catalogue_shared] + end attr_accessor :username, :password, :session_token @@ -68,6 +72,31 @@ def self.from_keycloak(auth) end end + def self.from_keycloak_patron(auth) + ActiveRecord::Base.transaction do + user = find_or_create_by!(folio_id: auth.extra.raw_info.preferred_username) do |user| + # We don't really care about the password since auth is via Keycloak, so we're just + # putting a dummy value here. + user.encrypted_password = SecureRandom.hex(14) + end + # set/update values from Keycloak in case they've changed + user.email = auth.info.email.present? ? auth.info.email : "" + user.provider = auth.provider + user.uid = auth.uid + user.name_given = auth.info.first_name + user.name_family = auth.info.last_name + + # TODO: send request to catalogue services to determine active state of user account in FOLIO + + # this is required for backchannel logout + user.session_token = auth.extra.raw_info.sid + + # reload user with updated values from database + user.save! + user.reload + end + end + # Method added by Blacklight; Blacklight uses #to_s on your # user class to get a user-displayable login/identifier for # the account. @@ -80,6 +109,8 @@ def to_s "#{name} (SPL)" elsif provider == "catalogue_shared" "#{name} (TOL)" + else + name end end name diff --git a/app/views/users/sessions/new.html.erb b/app/views/users/sessions/new.html.erb index 4c455c3..daa910b 100644 --- a/app/views/users/sessions/new.html.erb +++ b/app/views/users/sessions/new.html.erb @@ -1,21 +1,25 @@

Login

-<%= form_for(resource, as: resource_name, html: {'data-turbo' => "false"}, url: session_path(resource_name)) do |f| %> -
- <%= f.label :username, "User ID" %> - <%= f.text_field :username, autofocus: true, autocomplete: "username",class: "form-control col-md-4" %> -
+<% if ENV["KC_PATRON_REALM"] %> + <%= button_to t("auth.patron_login"), user_catalogue_patron_omniauth_authorize_path, class: "btn btn-primary", data: { turbo: false } %> +<% else %> + <%= form_for(resource, as: resource_name, html: {'data-turbo' => "false"}, url: session_path(resource_name)) do |f| %> +
+ <%= f.label :username, "User ID" %> + <%= f.text_field :username, autofocus: true, autocomplete: "username",class: "form-control col-md-4" %> +
-
- <%= f.label :password, "Family Name" %> - <%= f.text_field :password, autocomplete: "off",class: "form-control col-md-4" %> -
+
+ <%= f.label :password, "Family Name" %> + <%= f.text_field :password, autocomplete: "off",class: "form-control col-md-4" %> +
-

<%= t("devise.registrations.register", url: ENV["NATIONAL_LIBRARY_CARD_URL"]).html_safe %>

+

<%= t("devise.registrations.register", url: ENV["NATIONAL_LIBRARY_CARD_URL"]).html_safe %>

-
- <%= f.submit "Login", class: "btn btn-primary" %> -
+
+ <%= f.submit "Login", class: "btn btn-primary" %> +
+ <% end %> <% end %> <%= render "users/shared/links" %> diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index bb5eaf5..5c81b89 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -90,7 +90,9 @@ def recall # session. If you need permissions, you should implement that in a before filter. # You can also supply a hash where the value is a boolean determining whether # or not authentication should be aborted when the value is not present. - config.authentication_keys = {username: true, password: true} + unless ENV["KC_PATRON_REALM"] + config.authentication_keys = {username: true, password: true} + end # Configure parameters from the request object used for authentication. Each entry # given should be a request method and it will automatically be passed to the @@ -107,7 +109,9 @@ def recall # Configure which authentication keys should have whitespace stripped. # These keys will have whitespace before and after removed upon creating or # modifying a user and when used to authenticate or find a user. Default is :email. - config.strip_whitespace_keys = [:username, :password] + unless ENV["KC_PATRON_REALM"] + config.strip_whitespace_keys = [:username, :password] + end # Tell if authentication through request.params is enabled. True by default. # It can be set to an array that will enable params authentication only for the @@ -321,9 +325,11 @@ def recall # If you want to use other strategies, that are not supported by Devise, or # change the failure app, you can configure them inside the config.warden block. # - config.warden do |manager| - manager.failure_app = CatalogueFailureApp - manager.default_strategies(scope: :user).unshift :user_reg_authenticatable + unless ENV["KC_PATRON_REALM"] + config.warden do |manager| + manager.failure_app = CatalogueFailureApp + manager.default_strategies(scope: :user).unshift :user_reg_authenticatable + end end # ==> Mountable engine configurations diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index b47638d..876bb45 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -22,4 +22,12 @@ strategy_class: OmniAuth::Strategies::KeycloakOpenId, authorize_params: {scope: "openid"}, name: "catalogue_shared" + + provider :keycloak_openid, + ENV.fetch("KC_PATRON_CLIENT", "patron"), + ENV.fetch("KC_PATRON_SECRET", "default secret"), + client_options: {site: ENV.fetch("KEYCLOAK_URL", "http://localhost:9090"), realm: ENV.fetch("KC_PATRON_REALM", "nla-patron")}, + strategy_class: OmniAuth::Strategies::KeycloakOpenId, + authorize_params: {scope: "openid"}, + name: "catalogue_patron" end diff --git a/config/locales/auth.en.yml b/config/locales/auth.en.yml index ff75b21..74ea6e4 100644 --- a/config/locales/auth.en.yml +++ b/config/locales/auth.en.yml @@ -6,3 +6,4 @@ en: sol_login: "Staff Official Loan" spl_login: "Staff Personal Loan" shared_login: "Team Official Loan" + patron_login: "Patron Login" diff --git a/config/routes.rb b/config/routes.rb index 8d14baf..2551fce 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -51,6 +51,10 @@ } devise_scope(:user) do + if ENV["KC_PATRON_REALM"] + get "sign_in", to: "users/sessions#new", as: :new_user_session + delete "sign_out", to: "users/sessions#destroy", as: :destroy_user_session + end post "/backchannel_logout", to: "users/sessions#backchannel_logout", as: :backchannel_logout end end diff --git a/spec/dummy/app/controllers/application_controller.rb b/spec/dummy/app/controllers/application_controller.rb index 1652e22..d45ad21 100644 --- a/spec/dummy/app/controllers/application_controller.rb +++ b/spec/dummy/app/controllers/application_controller.rb @@ -1,3 +1,7 @@ class ApplicationController < ActionController::Base # Attempt to find the mapped route for devise based on request path + + if ENV["KC_PATRON_REALM"] + include AuthSessionConcern + end end diff --git a/spec/dummy/app/views/pages/about.html.erb b/spec/dummy/app/views/pages/about.html.erb index 3453cf2..56bfe1f 100644 --- a/spec/dummy/app/views/pages/about.html.erb +++ b/spec/dummy/app/views/pages/about.html.erb @@ -1,2 +1,2 @@ -

Pages#home

-

Find me in app/views/pages/home.html.erb

+

Pages#about

+

Find me in app/views/pages/about.html.erb

diff --git a/spec/dummy/app/views/pages/home.html.erb b/spec/dummy/app/views/pages/home.html.erb index 3453cf2..b528a86 100644 --- a/spec/dummy/app/views/pages/home.html.erb +++ b/spec/dummy/app/views/pages/home.html.erb @@ -1,2 +1,6 @@

Pages#home

Find me in app/views/pages/home.html.erb

+ +<% if current_user.present? %> + Logged in as <%= current_user %> +<% end %> diff --git a/spec/dummy/app/views/shared/_user_links.html.erb b/spec/dummy/app/views/shared/_user_links.html.erb index b9a7ca2..ff6ca8e 100644 --- a/spec/dummy/app/views/shared/_user_links.html.erb +++ b/spec/dummy/app/views/shared/_user_links.html.erb @@ -3,7 +3,7 @@
  • <%= link_to "About", about_path, class: "#{active_link_class_controller(%w[pages])}" %>
  • <% if current_user %>
  • - <%= link_to t('blacklight.header_links.logout'), destroy_user_session_path, method: :delete, data: { turbo_method: :delete } %> + <%= link_to t('blacklight.header_links.logout'), destroy_user_session_url, method: :delete, data: { turbo_method: :delete } %>
  • <% unless current_user.to_s.blank? %>
  • diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 9701389..95ea94d 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -32,12 +32,18 @@ name_given { "Test" } name_family { "User" } + if ENV["KC_PATRON_REALM"] + uid { SecureRandom.uuid } + provider { "catalogue_patron" } + session_token { SecureRandom.hex } + end + trait :staff do provider { "catalogue_sol" } uid { "603e26dd-b2d4-4a88-ad9d-406eaec31463" } name_given { "Staff" } name_family { "User" } - email { "staff@nla.gov.au" } + sequence(:email) { |n| "staff-#{n.to_s.rjust(3, "0")}@nla.gov.au" } session_token { SecureRandom.hex } end diff --git a/spec/features/active_link_spec.rb b/spec/features/active_link_spec.rb index 2322ad4..6b09478 100644 --- a/spec/features/active_link_spec.rb +++ b/spec/features/active_link_spec.rb @@ -33,5 +33,24 @@ expect(page).to have_css("a.active", text: "blacklight test") end + + context "when Keycloak patron authentication is enabled" do + before do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with("KC_PATRON_REALM").and_return("patron_realm") + end + + it "returns active given the controller and action" do + visit root_path + click_link "Login" + expect(page).to have_content("Login") + + click_button "Patron Login" + + visit account_path + + expect(page).to have_css("a.active", text: "Blacklight Test") + end + end end end diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb index 5dffe14..80f893e 100644 --- a/spec/features/login_spec.rb +++ b/spec/features/login_spec.rb @@ -9,67 +9,88 @@ # rubocop:enable RSpec/AnyInstance end - it "creates a new session for the user" do - visit root_path - click_link "Login" - expect(page).to have_content("Login") - - fill_in "user_username", with: "bltest" - fill_in "user_password", with: "test" - click_button "Login" - - expect(page).to have_content(I18n.t("devise.sessions.signed_in")) - expect(page).to have_content("blacklight test") - end - - it "displays a registration link" do - visit new_user_session_path + context "when Keycloak patron authentication is enabled" do + before do + allow(ENV).to receive(:[]).and_call_original + allow(ENV).to receive(:[]).with("KC_PATRON_REALM").and_return("patron_realm") + end - expect(page).to have_link("here", href: ENV["NATIONAL_LIBRARY_CARD_URL"]) - end + it "displays a login link" do + visit root_path - it "disables Turbo" do - visit new_user_session_path + expect(page).to have_link("Login", href: new_user_session_path) + end - expect(page).to have_css("form[data-turbo]") + it "displays the patron login button" do + visit root_path + click_link "Login" + expect(page).to have_content(I18n.t("auth.patron_login")) + end end - context "when user is inactive" do - it "displays an error message" do + context "when Keycloak patron authentication is disabled" do + it "creates a new session for the user" do visit root_path click_link "Login" expect(page).to have_content("Login") fill_in "user_username", with: "bltest" - fill_in "user_password", with: "blacklight" + fill_in "user_password", with: "test" click_button "Login" - expect(page).to have_content(I18n.t("devise.failure.expired")) - expect(page).to have_content("Login") + expect(page).to have_content(I18n.t("devise.sessions.signed_in")) + expect(page).to have_content("blacklight test") end - end - - context "when user has the wrong credentials" do - it "displays an error message" do - visit root_path - click_link "Login" - expect(page).to have_content("Login") - fill_in "user_username", with: "0000" - fill_in "user_password", with: "failure" - click_button "Login" + it "displays a registration link" do + visit new_user_session_path - expect(page).to have_link("Ask a Librarian", href: ENV["ASK_LIBRARIAN_URL"]) - expect(page).to have_content("Login") + expect(page).to have_link("here", href: ENV["NATIONAL_LIBRARY_CARD_URL"]) end - end - context "when user does not enter a username or password" do - it "displays an error message" do + it "disables Turbo" do visit new_user_session_path - click_button "Login" - expect(page).to have_link("Ask a Librarian", href: ENV["ASK_LIBRARIAN_URL"]) + expect(page).to have_css("form[data-turbo]") + end + + context "when user is inactive" do + it "displays an error message" do + visit root_path + click_link "Login" + expect(page).to have_content("Login") + + fill_in "user_username", with: "bltest" + fill_in "user_password", with: "blacklight" + click_button "Login" + + expect(page).to have_content(I18n.t("devise.failure.expired")) + expect(page).to have_content("Login") + end + end + + context "when user has the wrong credentials" do + it "displays an error message" do + visit root_path + click_link "Login" + expect(page).to have_content("Login") + + fill_in "user_username", with: "0000" + fill_in "user_password", with: "failure" + click_button "Login" + + expect(page).to have_link("Ask a Librarian", href: ENV["ASK_LIBRARIAN_URL"]) + expect(page).to have_content("Login") + end + end + + context "when user does not enter a username or password" do + it "displays an error message" do + visit new_user_session_path + click_button "Login" + + expect(page).to have_link("Ask a Librarian", href: ENV["ASK_LIBRARIAN_URL"]) + end end end end diff --git a/spec/features/logout_spec.rb b/spec/features/logout_spec.rb deleted file mode 100644 index f54bb1d..0000000 --- a/spec/features/logout_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_state_literal: true - -require "rails_helper" - -RSpec.describe "Logout" do - before do - # rubocop:disable RSpec/AnyInstance - allow_any_instance_of(PatronHelper).to receive(:user_location).and_return(:offsite) - # rubocop:enable RSpec/AnyInstance - end - - it "destroys the user session" do - visit new_user_session_path - fill_in "user_username", with: "bltest" - fill_in "user_password", with: "test" - click_button "Login" - - visit root_path - click_link "Log Out" - - expect(page).to have_content(I18n.t("devise.sessions.signed_out")) - expect(page).not_to have_content("blacklight test") - end -end diff --git a/spec/features/staff_login_spec.rb b/spec/features/staff_login_spec.rb deleted file mode 100644 index 51a6618..0000000 --- a/spec/features/staff_login_spec.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -require "rails_helper" - -RSpec.describe "Staff login" do - context "when in public network" do - # rubocop:disable RSpec/AnyInstance - before do - allow_any_instance_of(PatronHelper).to receive(:user_location).and_return(:offsite) - end - # rubocop:enable RSpec/AnyInstance - - it "display login links" do - visit root_path - click_link "Login" - expect(page).to have_content("Login") - - expect(page).not_to have_content(I18n.t("auth.staff.sol_login")) - expect(page).not_to have_content(I18n.t("auth.staff.spl_login")) - expect(page).not_to have_content(I18n.t("auth.staff.shared_login")) - end - end - - context "when inside local network" do - # rubocop:disable RSpec/AnyInstance - before do - allow_any_instance_of(PatronHelper).to receive(:user_location).and_return(:onsite) - end - # rubocop:enable RSpec/AnyInstance - - it "display login links" do - visit root_path - click_link "Login" - expect(page).to have_content("Login") - - expect(page).not_to have_content(I18n.t("auth.staff.sol_login")) - expect(page).not_to have_content(I18n.t("auth.staff.spl_login")) - expect(page).not_to have_content(I18n.t("auth.staff.shared_login")) - end - end - - context "when inside staff network" do - # rubocop:disable RSpec/AnyInstance - before do - allow_any_instance_of(PatronHelper).to receive(:user_location).and_return(:staff) - end - # rubocop:enable RSpec/AnyInstance - - it "display login links" do - visit root_path - click_link "Login" - expect(page).to have_content("Login") - - expect(page).to have_content(I18n.t("auth.staff.sol_login")) - expect(page).to have_content(I18n.t("auth.staff.spl_login")) - expect(page).to have_content(I18n.t("auth.staff.shared_login")) - end - end -end diff --git a/spec/files/auth/auth_hash.json b/spec/files/auth/auth_hash.json index 1babc18..81198d4 100644 --- a/spec/files/auth/auth_hash.json +++ b/spec/files/auth/auth_hash.json @@ -2,28 +2,28 @@ "provider": "keycloakopenid", "uid": "603e26dd-b2d4-4a88-ad9d-406eaec31463", "info": { - "name": "Staff User", - "email": "staff@nla.gov.au", - "first_name": "Staff", - "last_name": "User" + "name": "Blacklight Test", + "email": "test@example.com", + "first_name": "Blacklight", + "last_name": "Test" }, "credentials": { - "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImYyZjVmYjc3OWY4YjhhNjM2NDVlYjcxMTEzOWJhN2VhIn0.eyJleHAiOjE2NzM4NDAyOTYsImlhdCI6MTY3MzgzNjY5NiwiYXV0aF90aW1lIjoxNjczODM2Njk2LCJqdGkiOiI4ZGJmMDg4NS1kNzI3LTQ1YzQtODYwYS05ODE2YWUxOWZmNWYiLCJpc3MiOiJodHRwczovL2xvZ2luLmV4YW1wbGUuY29tL2F1dGgvcmVhbG1zL2V4YW1wbGUiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiZjVhZmFkODEtMDZjNC00OWE2LTgyN2UtMmVmMmRkM2I4ZmRhIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiY2F0YWxvZ3VlIiwic2Vzc2lvbl9zdGF0ZSI6IjgwMjQyNDYwLWVlMjEtNGUyYy1iZmY2LTNjZmM1M2JjOGVmYiIsImFsbG93ZWQtb3JpZ2lucyI6WyJsb2NhbGhvc3QiLCIwLjAuMC4wIiwiY2F0YWxvZ3VlLm5sYS5nb3YuYXUiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6InByb2ZpbGUgZW1haWwgcm9sZXMiLCJzaWQiOiI4MDI0MjQ2MC1lZTIxLTRlMmMtYmZmNi0zY2ZjNTNiYzhlZmIiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXSwibmFtZSI6IlN0YWZmIFVzZXIiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJzdGFmZiIsImdpdmVuX25hbWUiOiJTdGFmZiIsImZhbWlseV9uYW1lIjoiVXNlciIsImVtYWlsIjoic3RhZmZAbmxhLmdvdi5hdSJ9.cjKwwSfeFvujsEduYtzDfoZSBnPHVb3vvc7mYwOoa2Ji_LYd7UNRz7iXzCqQG149_UlyrBfk8f9-JU6AVp_izTYt58H9p8FjgkE6p1U-C-SXKk8TdbM0Lf4GMbZs2spQNK4LTZYTjduGS8nU3ao30O8RQvtdd1XvucCTmEBFLlKG8U6j1IfxVEtNzHBtqJFyWvKQdfruVfgWfi2QpgkH-X-Hjr6PS5OKNsZhx6N6d9iTS8MMzZGZtIuHd9Ho-A3iL8dj6ReUAxXLAF_qFXCnp5xZZ0RtUsmr38XDl6A-iqhfD19vh3L1JZUboRgDaX85ViOkTkU5s-JJi7oC3e39OrAk2o_brDT5FM8AvkJjOB_d9xIZaMScgVgPHI3cueP0yiXgmYNxaJ8NtF0LlV4qrec3DB2y4FvF7kYGnXjof5_P3urC8MrT4c1xigfM6alXmkNUuKTTooDL9l9SpivV7c7WUrJ1nNmH3511ObmKjJWZIggJphfOpGBq9CqJ8nCr", + "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImYyZjVmYjc3OWY4YjhhNjM2NDVlYjcxMTEzOWJhN2VhIn0.eyJleHAiOjE2NzM4NDAyOTYsImlhdCI6MTY3MzgzNjY5NiwiYXV0aF90aW1lIjoxNjczODM2Njk2LCJqdGkiOiI4ZGJmMDg4NS1kNzI3LTQ1YzQtODYwYS05ODE2YWUxOWZmNWYiLCJpc3MiOiJodHRwczovL2xvZ2luLmV4YW1wbGUuY29tL2F1dGgvcmVhbG1zL2V4YW1wbGUiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiZjVhZmFkODEtMDZjNC00OWE2LTgyN2UtMmVmMmRkM2I4ZmRhIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiY2F0YWxvZ3VlIiwic2Vzc2lvbl9zdGF0ZSI6IjgwMjQyNDYwLWVlMjEtNGUyYy1iZmY2LTNjZmM1M2JjOGVmYiIsImFsbG93ZWQtb3JpZ2lucyI6WyJsb2NhbGhvc3QiLCIwLjAuMC4wIiwiY2F0YWxvZ3VlLm5sYS5nb3YuYXUiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6InByb2ZpbGUgZW1haWwgcm9sZXMiLCJzaWQiOiI4MDI0MjQ2MC1lZTIxLTRlMmMtYmZmNi0zY2ZjNTNiYzhlZmIiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXSwibmFtZSI6IkJsYWNrbGlnaHQgVGVzdCIsInByZWZlcnJlZF91c2VybmFtZSI6ImMyMDJhN2Q1LTNkMzAtNDExYS1iMGE3LTUzNzUzODAxYmY5OCIsImdpdmVuX25hbWUiOiJCbGFja2xpZ2h0IiwiZmFtaWx5X25hbWUiOiJUZXN0IiwiZW1haWwiOiJ0ZXN0QGV4YW1wbGUuY29tIn0.lMGOYGiMroCMtmDF9LORZoPMzkj40mKB5Ee1c_cCdCYdzZCXygeED4eSAewtCexID-Dfc8-F-CaKr4I-mXjjtn2G5FKjfhvQGLmDvQ9pnpSsfHrK7V4c3AQgpgeEHw_aiW6uXicbp9yNLhuAOWJ0nKwGjFPQRMP9MOK1uQ_KWHBmdniz5XJ-E6C_KmFib-_Tzo2QVlGZsjIcankJ-cUEkyvrVmJ0y6JfaC6Nguf2YbwBELrpJgSATrWNQLicQ-yzA_pkKjCLQwRIOqkQU7s25Ty3a2CtVAJnjxMTOgpMbKoiy79Pzw8QVC_HJyL0vXEP9fDNNA9uHJIGxcHIjdcpBw", "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJjNjRhZTM5OS1hYmFmLTQ0MDQtYTUwNS04MDgwZmFkM2UzZTAifQ.eyJleHAiOjE2NzQwMDk0OTYsImlhdCI6MTY3MzgzNjY5NiwianRpIjoiZDE4ODViNzctMGUxNi00MzBjLWEyOTctNGE3NTA1ZWY0ZDFmIiwiaXNzIjoiaHR0cHM6Ly9sb2dpbi1kZXZlbC5ubGEuZ292LmF1L2F1dGgvcmVhbG1zL3NoaXJlIiwiYXVkIjoiaHR0cHM6Ly9sb2dpbi1kZXZlbC5ubGEuZ292LmF1L2F1dGgvcmVhbG1zL3NoaXJlIiwic3ViIjoiZjVhZmFkODEtMDZjNC00OWE2LTgyN2UtMmVmMmRkM2I4ZmRhIiwidHlwIjoiUmVmcmVzaCIsImF6cCI6ImNhdGFsb2d1ZSIsInNlc3Npb25fc3RhdGUiOiI4MDI0MjQ2MC1lZTIxLTRlMmMtYmZmNi0zY2ZjNTNiYzhlZmIiLCJzY29wZSI6InByb2ZpbGUgZW1haWwgcm9sZXMiLCJzaWQiOiI4MDI0MjQ2MC1lZTIxLTRlMmMtYmZmNi0zY2ZjNTNiYzhlZmIifQ.q_P4zHKyeEHnhlu0FFENX9c6n_YimkaokX7AV9MC-Ag", - "expires_at": 1673840296, + "expires_at": 1728943716, "expires": true }, "extra": { "raw_info": { - "exp": 1673840296, - "iat": 1673836696, + "exp": 1728943716, + "iat": 1697407716, "auth_time": 1673836696, "jti": "8dbf0885-d727-45c4-860a-9816ae19ff5f", - "iss": "https://login.example.com/auth/realms/staff", + "iss": "https://login.example.com/auth/realms/example", "aud": "account", "sub": "603e26dd-b2d4-4a88-ad9d-406eaec31463", "typ": "Bearer", - "azp": "catalogue", + "azp": "join-us", "session_state": "80242460-ee21-4e2c-bff6-3cfc53bc8efb", "allowed-origins": [ "localhost", @@ -52,11 +52,11 @@ "offline_access", "uma_authorization" ], - "name": "Staff User", - "preferred_username": "staff", - "given_name": "Staff", - "family_name": "User", - "email": "staff@nla.gov.au" + "name": "Blacklight Test", + "preferred_username": "c202a7d5-3d30-411a-b0a7-53753801bf98", + "given_name": "Blacklight", + "family_name": "Test", + "email": "test@example.com" }, "id_token": null } diff --git a/spec/requests/staff_logout_spec.rb b/spec/requests/logout_spec.rb similarity index 88% rename from spec/requests/staff_logout_spec.rb rename to spec/requests/logout_spec.rb index 6335a32..012ad8e 100644 --- a/spec/requests/staff_logout_spec.rb +++ b/spec/requests/logout_spec.rb @@ -2,15 +2,15 @@ require "rails_helper" -RSpec.describe "Staff logout" do +RSpec.describe "Logout" do include Devise::Test::IntegrationHelpers let(:file) { IO.read("spec/files/auth/staff_auth_hash.json") } let(:auth_hash) { OmniAuth::AuthHash.new(JSON.parse(file)) } before do - staff = create(:user, :staff) - sign_in staff + patron = create(:user, :staff) + sign_in patron end it "redirects to Keycloak's logout URL" do @@ -34,19 +34,19 @@ jwt = JWT.decode(logout_token, nil, false) jwt[0]["sid"] end - let(:staff) do + let(:patron) do user = create(:user, :staff) user.update_column(:session_token, session_id) user.reload end before do - sign_in staff + sign_in patron end it "updates the session_token for the logged out user" do post "/backchannel_logout", params: {logout_token: logout_token} - expect(staff.session_token).not_to eq session_id + expect(patron.session_token).not_to eq session_id end end end diff --git a/spec/simplecov_helper.rb b/spec/simplecov_helper.rb index 19e769d..dfeb33b 100644 --- a/spec/simplecov_helper.rb +++ b/spec/simplecov_helper.rb @@ -23,4 +23,6 @@ # these will be removed soon add_filter "lib/devise/models/getalibrarycard_authenticatable.rb" add_filter "lib/devise/strategies/getalibrarycard_authenticatable.rb" + add_filter "lib/devise/models/user_reg_authenticatable.rb" + add_filter "lib/devise/strategies/user_reg_authenticatable.rb" end diff --git a/spec/support/omniauth.rb b/spec/support/omniauth.rb new file mode 100644 index 0000000..14ec3fd --- /dev/null +++ b/spec/support/omniauth.rb @@ -0,0 +1,9 @@ +require "omniauth" + +OmniAuth.config.test_mode = true + +RSpec.configure do |config| + config.before do + OmniAuth.config.add_mock(:catalogue_patron, JSON.parse(IO.read("spec/files/auth/auth_hash.json"))) + end +end