From af1a0315ff0a8ae1897fff65558006260f5a9092 Mon Sep 17 00:00:00 2001 From: Ryan Orlando Date: Sat, 15 Apr 2023 07:52:57 -0400 Subject: [PATCH 1/2] Add shibboleth single sign on --- .rubocop.yml | 9 ++++- Gemfile | 4 +++ Gemfile.lock | 7 ++++ app/controllers/omniauth_controller.rb | 10 ++++++ .../omniauthcallbacks_controller.rb | 34 +++++++++++++++++++ app/models/user.rb | 34 ++++++++++++++++++- app/views/_user_util_links.html.erb | 9 ++--- config/initializers/devise.rb | 32 +++++++++++------ config/routes.rb | 13 ++++++- .../20230415114846_add_colum_to_user.rb | 5 +++ 10 files changed, 140 insertions(+), 17 deletions(-) create mode 100644 app/controllers/omniauth_controller.rb create mode 100644 app/controllers/omniauthcallbacks_controller.rb create mode 100644 db/migrate/20230415114846_add_colum_to_user.rb diff --git a/.rubocop.yml b/.rubocop.yml index 17d97d5ee..af6125994 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -37,6 +37,7 @@ Metrics/ClassLength: - lib/tufts/hostname_fixer.rb Metrics/MethodLength: Exclude: + - app/controllers/omniauthcallbacks_controller.rb - app/controllers/tufts/tdl_resources_controller.rb - app/models/tufts/iiif_manifest.rb - lib/tufts/hostname_fixer.rb @@ -76,4 +77,10 @@ RSpec/ExampleLength: - spec/features/feature_page_customizations_spec.rb - spec/features/sir_trevor_customizations_spec.rb - spec/features/tdl_ingest_spec.rb - - spec/features/view_customizations_spec.rb \ No newline at end of file + - spec/features/view_customizations_spec.rb +Rails/UnknownEnv: + Environments: + - production + - development + - test + - tdldev \ No newline at end of file diff --git a/Gemfile b/Gemfile index 38c278305..945129c31 100644 --- a/Gemfile +++ b/Gemfile @@ -46,6 +46,10 @@ gem 'twitter-typeahead-rails', '0.11.1.pre.corejavascript' gem 'devise_ldap_authenticatable' +# shib login +gem 'omniauth', '1.9.1' +gem 'omniauth-shibboleth' + group :development do # Access an IRB console on exception pages or by using <%= console %> in views gem 'web-console', '>= 3.3.0' diff --git a/Gemfile.lock b/Gemfile.lock index 7904534b7..6b274c9da 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -312,6 +312,11 @@ GEM rack (>= 1.2, < 4) snaky_hash (~> 2.0) version_gem (~> 1.1) + omniauth (1.9.1) + hashie (>= 3.4.6) + rack (>= 1.6.2, < 3) + omniauth-shibboleth (1.3.0) + omniauth (>= 1.0.0) open4 (1.3.4) openseadragon (0.6.0) rails (> 3.2.0) @@ -560,6 +565,8 @@ DEPENDENCIES jquery-rails ladle mysql2 + omniauth (= 1.9.1) + omniauth-shibboleth rails (~> 5.2) riiif rsolr (>= 1.0) diff --git a/app/controllers/omniauth_controller.rb b/app/controllers/omniauth_controller.rb new file mode 100644 index 000000000..1264fe8ab --- /dev/null +++ b/app/controllers/omniauth_controller.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true +class OmniauthController < Devise::SessionsController + def new + if Rails.env.production? || Rails.env.tdldev? + redirect_to user_shibboleth_omniauth_authorize_path + else + super + end + end +end diff --git a/app/controllers/omniauthcallbacks_controller.rb b/app/controllers/omniauthcallbacks_controller.rb new file mode 100644 index 000000000..cbe6810fa --- /dev/null +++ b/app/controllers/omniauthcallbacks_controller.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true +class OmniauthcallbacksController < Devise::OmniauthCallbacksController + # handle omniauth logins from shibboleth + def shibboleth + # auth_headers = %w(HTTP_AFFILIATION HTTP_AUTH_TYPE HTTP_COOKIE HTTP_HOST + # HTTP_PERSISTENT_ID HTTP_EPPN HTTP_REMOTE_USER HTTP_SHIB_APPLICATION_ID + # HTTP_SHIB_AUTHENTICATION_INSTANT HTTP_SHIB_AUTHENTICATION_METHOD + # HTTP_SHIB_AUTHNCONTEXT_CLASS HTTP_SHIB_HANDLER HTTP_SHIB_IDENTITY_PROVIDER + # HTTP_SHIB_SESSION_ID HTTP_SHIB_SESSION_INDEX HTTP_UNSCOPED_AFFILIATION) + auth_headers = { + uid: 'uid', + shib_session_id: 'Shib-Session-ID', + shib_application_id: 'Shib-Application-ID', + provider: 'Shib-Identity-Provider', + mail: 'mail' + } + auth = {} + auth_headers.each do |k, v| + auth[k] = request.env[v] + end + auth.delete_if { |_k, v| v.blank? } + @user = User.from_omniauth(auth) + # capture data about the user from shib + set_flash_message :notice, :success, kind: "Shibboleth" + sign_in_and_redirect @user + end + + ## when shib login fails + def failure + ## redirect them to the devise local login page + # redirect_to new_local_user_session_path, :notice => "Shibboleth isn't available - local login only" + redirect_to root_path, notice: "Shibboleth isn't available - local login only" + end +end diff --git a/app/models/user.rb b/app/models/user.rb index b1a71c1a3..987226325 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -5,6 +5,20 @@ class User < ApplicationRecord attr_accessible :email, :password, :password_confirmation if Blacklight::Utils.needs_attr_accessible? # Connects this user object to Blacklights Bookmarks. include Blacklight::User + + if Rails.env.development? || Rails.env.test? + # Include default devise modules. Others available are: + # :confirmable, :lockable, :timeoutable and :omniauthable + # Removed :recoverable and :registerable to eliminate unwanted links on login page + devise :invitable, :ldap_authenticatable, + :trackable, :validatable + # copied devise,think through these modules and there differeneces. + # devise :ldap_authenticatable, :registerable, + # :recoverable, :rememberable, :trackable, :validatable + else + devise_modules = [:omniauthable, :rememberable, :trackable, omniauth_providers: [:shibboleth], authentication_keys: [:username]] + devise(*devise_modules) + end # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable # Removed :recoverable and :registerable to eliminate unwanted links on login page @@ -23,5 +37,23 @@ def to_s def ldap_before_save self.email = Devise::LDAP::Adapter.get_ldap_param(username, 'mail').first end - ## + + # allow omniauth (including shibboleth) logins + # this will create a local user based on an omniauth/shib login + # if they haven't logged in before + def self.from_omniauth(auth) + logger.warn "Finding omni user auth:: #{auth}" + user = find_by(username: auth[:uid]) + if user.nil? + user = User.create( + email: auth[:mail], + username: auth[:uid] + ) + else + user.username = auth[:uid] + user.email = auth[:mail] + user.save + end + user + end end diff --git a/app/views/_user_util_links.html.erb b/app/views/_user_util_links.html.erb index 2806d17a8..a98039468 100644 --- a/app/views/_user_util_links.html.erb +++ b/app/views/_user_util_links.html.erb @@ -26,10 +26,11 @@
  • <% end %> - -
  • - <%= link_to t('spotlight.header_links.logout'), main_app.destroy_user_session_path %> -
  • + <% if Rails.env.production? || Rails.env.tdldev? %> +
  • <%= link_to t("hyrax.toolbar.profile.logout"), "#{request.base_url}/Shibboleth.sso/Logout?return=#{request.base_url}/sign_out" %>
  • + <% else %> +
  • <%= link_to t("hyrax.toolbar.profile.logout"), main_app.destroy_user_session_path %>
  • + <% end %> <% end %> diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 6e0e2229e..1fc7a0c74 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -1,18 +1,21 @@ # frozen_string_literal: true +require "omniauth-shibboleth" # Use this hook to configure devise mailer, warden hooks and so forth. # Many of these configuration options can be set straight in your model. Devise.setup do |config| - # ==> LDAP Configuration - config.ldap_logger = false - config.ldap_create_user = true - config.ldap_update_password = false - # config.ldap_config = "#{Rails.root}/config/ldap.yml" - # config.ldap_check_group_membership = false - # config.ldap_check_group_membership_without_admin = false - # config.ldap_check_attributes = false - # config.ldap_use_admin_to_bind = false - # config.ldap_ad_group_check = false + if Rails.env.development? || Rails.env.test? + # ==> LDAP Configuration + config.ldap_logger = false + config.ldap_create_user = true + config.ldap_update_password = false + # config.ldap_config = "#{Rails.root}/config/ldap.yml" + # config.ldap_check_group_membership = false + # config.ldap_check_group_membership_without_admin = false + # config.ldap_check_attributes = false + # config.ldap_use_admin_to_bind = false + # config.ldap_ad_group_check = false + end # The secret key used by Devise. Devise uses this key to generate # random tokens. Changing this key will render invalid all existing @@ -335,4 +338,13 @@ # When using OmniAuth, Devise cannot automatically set OmniAuth path, # so you need to do it manually. For the users scope, it would be: # config.omniauth_path_prefix = '/my_engine/users/auth' + + if Rails.env.production? || Rails.env.tdldev? + config.omniauth :shibboleth, { + uid_field: 'uid', + info_fields: { display_name: 'displayName', uid: 'uid', mail: 'mail' }, + callback_url: '/users/auth/shibboleth/callback', + strategy_class: OmniAuth::Strategies::Shibboleth + } + end end diff --git a/config/routes.rb b/config/routes.rb index 1203a1510..8849d4ef0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -14,7 +14,18 @@ concerns :searchable end - devise_for :users + if Rails.env.production? || Rails.env.tdldev? + devise_for :users, controllers: { omniauth_callbacks: "omniauthcallbacks" }, skip: [:sessions] + devise_scope :user do + get 'users/sign_in', to: 'omniauth#new' + post 'sign_in', to: 'omniauth#new', as: :new_user_session + post 'sign_in', to: 'omniauth_callbacks#shibboleth', as: :new_session + get 'sign_out', to: 'devise/sessions#destroy', as: :destroy_user_session + end + else + devise_for :users + end + concern :exportable, Blacklight::Routes::Exportable.new resources :solr_documents, only: [:show], path: '/catalog', controller: 'catalog' do diff --git a/db/migrate/20230415114846_add_colum_to_user.rb b/db/migrate/20230415114846_add_colum_to_user.rb new file mode 100644 index 000000000..2d10f85b1 --- /dev/null +++ b/db/migrate/20230415114846_add_colum_to_user.rb @@ -0,0 +1,5 @@ +class AddColumToUser < ActiveRecord::Migration[5.2] + def change + add_column :users, :provider, :string + end +end From 580d01fb4b0c2db7776b868fae3c7fb486e97d07 Mon Sep 17 00:00:00 2001 From: Mike Korcynski Date: Wed, 31 May 2023 15:14:21 -0400 Subject: [PATCH 2/2] test default authentication key --- app/models/user.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/user.rb b/app/models/user.rb index 987226325..dd181d562 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -16,7 +16,7 @@ class User < ApplicationRecord # devise :ldap_authenticatable, :registerable, # :recoverable, :rememberable, :trackable, :validatable else - devise_modules = [:omniauthable, :rememberable, :trackable, omniauth_providers: [:shibboleth], authentication_keys: [:username]] + devise_modules = [:omniauthable, :rememberable, :trackable, omniauth_providers: [:shibboleth]] devise(*devise_modules) end # Include default devise modules. Others available are: