Skip to content

Commit 4e80e61

Browse files
committed
Move ORCID authentication into its own controller
Add error handling
1 parent 0b887ec commit 4e80e61

10 files changed

Lines changed: 252 additions & 67 deletions

File tree

app/controllers/application_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def handle_error(status_code = 500, message = nil)
4848
status_code = status_code.to_i
4949
@message = message
5050
respond_to do |format|
51-
format.html { render 'static/error', status: status_code }
51+
format.html { render 'static/error', status: status_code }
5252
format.json { render json: { error: { message: message, code: status_code } }, status: status_code }
5353
format.json_api { render json: { error: { message: message, code: status_code } }, status: status_code }
5454
format.any { head status_code }

app/controllers/concerns/orcid_authentication.rb renamed to app/controllers/orcid_controller.rb

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
module OrcidAuthentication
2-
extend ActiveSupport::Concern
1+
class OrcidController < ApplicationController
2+
before_action :authenticate_user!
3+
before_action :set_oauth_client, only: [:authenticate, :callback]
34

4-
included do
5-
before_action :set_orcid_oauth_client, only: [:authenticate_orcid, :orcid_callback]
5+
# Faraday::ParsingError occurs in rack-oauth2 if the response does not contain JSON
6+
rescue_from(Rack::OAuth2::Client::Error, Faraday::ParsingError) do
7+
handle_error(:unprocessable_entity, t('orcid.error'))
68
end
79

8-
def authenticate_orcid
10+
def authenticate
911
redirect_to @oauth2_client.authorization_uri(scope: '/authenticate'), allow_other_host: true
1012
end
1113

12-
def orcid_callback
14+
def callback
1315
@oauth2_client.authorization_code = params[:code]
1416
token = Rack::OAuth2::AccessToken::Bearer.new(access_token: @oauth2_client.access_token!)
1517
orcid = token.access_token&.raw_attributes['orcid']
@@ -26,7 +28,7 @@ def orcid_callback
2628

2729
private
2830

29-
def set_orcid_oauth_client
31+
def set_oauth_client
3032
config = Rails.application.config.secrets.orcid
3133
@oauth2_client ||= Rack::OAuth2::Client.new(
3234
identifier: config[:client_id],

app/controllers/users_controller.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ class UsersController < ApplicationController
99
before_action :check_profile_id, only: [:update]
1010

1111
include ActionView::Helpers::TextHelper
12-
include OrcidAuthentication
1312

1413
# GET /users
1514
# GET /users.json

app/views/users/show.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
<% else %>
5353
<%= orcid_link(@user.profile) %>
5454
<% if current_user == @user && !@user.profile.orcid_authenticated? %>
55-
<%= button_to t('orcid.authenticate'), orcid_authenticate_path, class: 'btn btn-default' %>
55+
<%= button_to t('orcid.authenticate'), authenticate_orcid_path, class: 'btn btn-default' %>
5656
<% end %>
5757
<% end %>
5858
</p>

config/routes.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,8 @@
177177

178178
get 'up' => 'health_check#show'
179179

180-
post 'orcid/authenticate' => 'users#authenticate_orcid'
181-
get 'orcid/callback' => 'users#orcid_callback'
180+
post 'orcid/authenticate' => 'orcid#authenticate', as: :authenticate_orcid
181+
get 'orcid/callback' => 'orcid#callback', as: :orcid_callback
182182

183183
# The priority is based upon order of creation: first created -> highest priority.
184184
# See how all your routes lay out with "rake routes".

test/controllers/orcid_controller_test.rb

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,103 @@
33
class OrcidControllerTest < ActionController::TestCase
44
include Devise::Test::ControllerHelpers
55

6+
test 'authenticate orcid logged-in user' do
7+
sign_in users(:regular_user)
8+
9+
post :authenticate
10+
11+
assert_redirected_to /https:\/\/sandbox\.orcid\.org\/oauth\/authorize\?.+/
12+
end
13+
14+
test 'do not authenticate orcid if user not logged-in' do
15+
post :authenticate
16+
17+
assert_redirected_to new_user_session_path
18+
end
19+
20+
test 'handle callback and assign orcid if free' do
21+
mock_images
22+
user = users(:regular_user)
23+
sign_in user
24+
25+
VCR.use_cassette('orcid/get_token_free_orcid') do
26+
get :callback, params: { code: '123xyz' }
27+
end
28+
29+
profile = user.profile.reload
30+
assert_equal '0009-0006-0987-5702', profile.orcid
31+
assert profile.orcid_authenticated?
32+
assert_redirected_to user
33+
end
34+
35+
test 'handle callback and assign orcid if unauthenticated' do
36+
mock_images
37+
user = users(:regular_user)
38+
sign_in user
39+
40+
VCR.use_cassette('orcid/get_token_unauth_orcid') do
41+
get :callback, params: { code: '123xyz' }
42+
end
43+
44+
profile = user.profile.reload
45+
assert_equal '0000-0002-0048-3300', profile.orcid
46+
assert profile.orcid_authenticated?
47+
assert_redirected_to user
48+
end
49+
50+
test 'handle callback but do not assign orcid if already used' do
51+
mock_images
52+
user = users(:regular_user)
53+
sign_in user
54+
55+
VCR.use_cassette('orcid/get_token_existing_orcid') do
56+
get :callback, params: { code: '123xyz' }
57+
end
58+
59+
profile = user.profile.reload
60+
assert profile.orcid.blank?
61+
refute profile.orcid_authenticated?
62+
assert_redirected_to user
63+
assert_includes flash[:error], 'ORCID has already been'
64+
end
65+
66+
test 'do not handle callback if not logged-in' do
67+
mock_images
68+
69+
get :callback, params: { code: '123xyz' }
70+
71+
assert_redirected_to new_user_session_path
72+
end
73+
74+
test 'handle unauth error during callback' do
75+
mock_images
76+
user = users(:regular_user)
77+
sign_in user
78+
79+
VCR.use_cassette('orcid/error_unauth') do
80+
get :callback, params: { code: '123xyz' }
81+
end
82+
83+
assert_response :unprocessable_entity
84+
assert_select '#error-message', text: /ORCID/
85+
profile = user.profile.reload
86+
assert profile.orcid.blank?
87+
refute profile.orcid_authenticated?
88+
end
89+
90+
test 'handle unexpected error during callback' do
91+
mock_images
92+
user = users(:regular_user)
93+
sign_in user
94+
95+
VCR.use_cassette('orcid/error_500') do
96+
get :callback, params: { code: '123xyz' }
97+
end
98+
99+
assert_response :unprocessable_entity
100+
assert_select '#error-message', text: /ORCID/
101+
profile = user.profile.reload
102+
assert profile.orcid.blank?
103+
refute profile.orcid_authenticated?
104+
end
6105
end

test/controllers/users_controller_test.rb

Lines changed: 21 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -491,71 +491,38 @@ class UsersControllerTest < ActionController::TestCase
491491
end
492492
end
493493

494-
test 'authenticate orcid logged-in user' do
495-
sign_in users(:regular_user)
496-
497-
post :authenticate_orcid
498-
499-
assert_redirected_to /https:\/\/sandbox\.orcid\.org\/oauth\/authorize\?.+/
500-
end
501-
502-
test 'do not authenticate orcid if user not logged-in' do
503-
post :authenticate_orcid
504-
505-
assert_redirected_to new_user_session_path
506-
end
494+
test 'should show authenticate orcid button if own profile' do
495+
user = users(:private_user)
496+
assert user.profile.orcid.present?
497+
refute user.profile.orcid_authenticated?
507498

508-
test 'handle callback and assign orcid if free' do
509-
mock_images
510-
user = users(:regular_user)
511499
sign_in user
512500

513-
VCR.use_cassette('orcid/get_token_free_orcid') do
514-
get :orcid_callback, params: { code: '123xyz' }
515-
end
516-
517-
profile = user.profile.reload
518-
assert_equal '0009-0006-0987-5702', profile.orcid
519-
assert profile.orcid_authenticated?
520-
assert_redirected_to user
521-
end
522-
523-
test 'handle callback and assign orcid if unauthenticated' do
524-
mock_images
525-
user = users(:regular_user)
526-
sign_in user
527-
528-
VCR.use_cassette('orcid/get_token_unauth_orcid') do
529-
get :orcid_callback, params: { code: '123xyz' }
530-
end
501+
get :show, params: { id: user }
531502

532-
profile = user.profile.reload
533-
assert_equal '0000-0002-0048-3300', profile.orcid
534-
assert profile.orcid_authenticated?
535-
assert_redirected_to user
503+
assert_response :success
504+
assert_select '#sidebar button', text: 'Authenticate your ORCID'
536505
end
537506

538-
test 'handle callback but do not assign orcid if already used' do
539-
mock_images
540-
user = users(:regular_user)
541-
sign_in user
507+
test 'should not show authenticate orcid button if not own profile' do
508+
user = users(:private_user)
509+
assert user.profile.orcid.present?
510+
refute user.profile.orcid_authenticated?
542511

543-
VCR.use_cassette('orcid/get_token_existing_orcid') do
544-
get :orcid_callback, params: { code: '123xyz' }
545-
end
512+
get :show, params: { id: user }
546513

547-
profile = user.profile.reload
548-
assert profile.orcid.blank?
549-
refute profile.orcid_authenticated?
550-
assert_redirected_to user
551-
assert_includes flash[:error], 'ORCID has already been'
514+
assert_response :success
515+
assert_select '#sidebar button', text: 'Authenticate your ORCID', count: 0
552516
end
553517

554-
test 'do not handle callback if not logged-in' do
555-
mock_images
518+
test 'should not show authenticate orcid button if already authenticated' do
519+
user = users(:trainer_user)
520+
assert user.profile.orcid.present?
521+
assert user.profile.orcid_authenticated?
556522

557-
get :orcid_callback, params: { code: '123xyz' }
523+
get :show, params: { id: user }
558524

559-
assert_redirected_to new_user_session_path
525+
assert_response :success
526+
assert_select '#sidebar button', text: 'Authenticate your ORCID', count: 0
560527
end
561528
end

test/helpers/users_helper_test.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,23 @@ class UsersHelperTest < ActionView::TestCase
55

66
test 'displays authenticated orcid link' do
77
profile = profiles(:trainer_one_profile)
8+
assert profile.orcid.present?
9+
assert profile.orcid_authenticated?
10+
811
l = orcid_link(profile)
12+
913
assert_includes l, 'ORCID-iD_icon_vector.svg'
1014
assert_not_includes l, 'Unauthenticated'
1115
end
1216

1317
test 'displays unauthenticated orcid link' do
1418
profile = profiles(:trainer_two_profile)
19+
assert profile.orcid.present?
20+
refute profile.orcid_authenticated?
21+
1522
l = orcid_link(profile)
23+
1624
assert_includes l, 'ORCID-iD_icon_unauth_vector.svg'
1725
assert_includes l, 'Unauthenticated'
1826
end
19-
2027
end

test/vcr_cassettes/orcid/error_500.yml

Lines changed: 55 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)