Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

user login #983

Merged
merged 3 commits into from
Apr 6, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,32 @@ class ApplicationController < ActionController::Base

helper_method :blackcat_config

def login
session[:redirect_url] = home_or_original_path
session[:groups] = request.env.fetch(Settings.groups_header, '').split(',')
if current_user
redirect_to session[:redirect_url] || '/'
else
redirect_to login_path
end
end

before_action do
authorize_profiler
end

private

# @note The Matomo ID is used to capture statistics from our production instance. This is very unlikely to change.
# We can use this method to limit profiling to only dev and preview instances until we can implement a way to limit
# it to particular users and UMGs.
def home_or_original_path
banukutlu marked this conversation as resolved.
Show resolved Hide resolved
original_fullpath = request.env.fetch('ORIGINAL_FULLPATH', '/')
# prevent redirect loops when user hits /login directly
return '/' if original_fullpath == '/login'

original_fullpath
end

def authorize_profiler
return if Settings.matomo_id.to_i == 7
return unless request.session.fetch(:groups, []).include?(Settings.admin_group)
banukutlu marked this conversation as resolved.
Show resolved Hide resolved

Rack::MiniProfiler.authorize_request
end
Expand Down
6 changes: 5 additions & 1 deletion app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class User < ApplicationRecord
include Blacklight::User
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
devise :http_header_authenticatable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable

# Method added by Blacklight; Blacklight uses #to_s on your
Expand All @@ -14,4 +14,8 @@ class User < ApplicationRecord
def to_s
email
end

def password_required?
false
end
end
16 changes: 12 additions & 4 deletions config/initializers/devise.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# frozen_string_literal: true

require 'devise/redirect_to_login_failure'

Devise.add_module(:http_header_authenticatable,
strategy: true,
controller: :sessions,
model: 'devise/models/http_header_authenticatable')

# 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|
Expand Down Expand Up @@ -262,10 +269,11 @@
# 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.intercept_401 = false
# manager.default_strategies(scope: :user).unshift :some_external_strategy
# end
config.warden do |manager|
manager.failure_app = RedirectToLoginFailure
# manager.intercept_401 = false
# manager.default_strategies(scope: :user).unshift :some_external_strategy
end

# ==> Mountable engine configurations
# When using Devise inside an engine, let's call it `MyEngine`, and this engine
Expand Down
4 changes: 4 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
mount BlacklightAdvancedSearch::Engine => '/'
mount OkComputer::Engine, at: '/health'

authenticate :user do
get '/login', to: 'application#login', as: :login
end

# resource and resources
resource :catalog, only: [:index], as: 'catalog', path: '/catalog', controller: 'catalog' do
concerns [:searchable, :range_searchable]
Expand Down
3 changes: 3 additions & 0 deletions config/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ datadog:
enabled: false
# defaults to Rails.env. override here
# env: feature-foo
user_header: HTTP_X_AUTH_REQUEST_EMAIL
groups_header: HTTP_X_AUTH_REQUEST_GROUPS
admin_group: umg-up.ul-dev-team
redis:
sessions:
uri: redis://127.0.0.1:6379/3
Expand Down
11 changes: 11 additions & 0 deletions lib/devise/models/http_header_authenticatable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

require 'devise/strategies/http_header_authenticatable'

module Devise
module Models
module HttpHeaderAuthenticatable
extend ActiveSupport::Concern
end
end
end
7 changes: 7 additions & 0 deletions lib/devise/redirect_to_login_failure.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

class RedirectToLoginFailure < Devise::FailureApp
def redirect_url
'/login'
end
end
26 changes: 26 additions & 0 deletions lib/devise/strategies/http_header_authenticatable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

require 'devise/strategies/authenticatable'
module Devise
module Strategies
class HttpHeaderAuthenticatable < Authenticatable
def authenticate!
http_user = remote_user(request.headers)
return fail! if http_user.blank?

user = User.find_or_create_by(email: http_user)
success!(user)
end

def valid?
remote_user(request.headers).present?
end

def remote_user(headers)
headers.fetch(Settings.user_header, nil)
end
end
end
end

Warden::Strategies.add(:http_header_authenticatable, Devise::Strategies::HttpHeaderAuthenticatable)
59 changes: 53 additions & 6 deletions spec/controllers/application_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,73 @@
require 'rails_helper'

RSpec.describe ApplicationController do
context 'when given the right headers' do
before do
@request.env['devise.mapping'] = Devise.mappings[:user]
@request.headers[Settings.user_header] = '[email protected]'
@request.headers[Settings.groups_header] = Settings.admin_group
user = User.new(email: '[email protected]')
sign_in user
end

it 'sets the group' do
get :login
expect(session[:groups]).to include(Settings.admin_group)
end

it 'redirects to index' do
get :login
expect(response).to redirect_to('/')
end

it 'redirects to home' do
@request.env['ORIGINAL_FULLPATH'] = '/home'
get :login
expect(response).to redirect_to('/home')
end
end

context 'when given the wrong headers' do
before do
user = User.new(email: '[email protected]')
sign_in user
end

it 'does not set a the group' do
banukutlu marked this conversation as resolved.
Show resolved Hide resolved
get :login
expect(session[:groups]).to eq([])
end

it 'redirects to login' do
get :login
expect(response).to redirect_to('/login')
end
end

describe '#authorize_profiler' do
before do
allow(Rack::MiniProfiler).to receive(:authorize_request)
end

context 'when the matomo ID is not 7' do
context 'when signed in as admin' do
before do
request.session[:groups] = [Settings.admin_group]
end

specify do
expect(Settings.matomo_id).not_to eq(7)
controller.send(:authorize_profiler)
expect(Rack::MiniProfiler).to have_received(:authorize_request)
end
end

context 'when the matomo ID is 7' do
context 'when not signed in' do
before do
request.session[:groups] = []
end

specify do
current = Settings.matomo_id
Settings.matomo_id = 7
controller.send(:authorize_profiler)
expect(Rack::MiniProfiler).not_to have_received(:authorize_request)
Settings.matomo_id = current
end
end
end
Expand Down
1 change: 1 addition & 0 deletions spec/rails_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@

config.include ViewComponent::TestHelpers, type: :component
config.include Capybara::RSpecMatchers, type: :component
config.include Devise::Test::ControllerHelpers, type: :controller
end

Capybara.register_driver :chrome_headless do |app|
Expand Down