Skip to content

Commit b36c528

Browse files
committed
Prevent new profile record being created every time a user is updated.
Fix solr indexing weirdness with trainers
1 parent 95fd174 commit b36c528

6 files changed

Lines changed: 38 additions & 49 deletions

File tree

app/controllers/users_controller.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class UsersController < ApplicationController
66
prepend_before_action :set_user, only: %i[show edit update destroy change_token]
77
prepend_before_action :init_user, only: [:create]
88
before_action :set_breadcrumbs
9+
before_action :check_profile_id, only: [:update]
910

1011
include ActionView::Helpers::TextHelper
1112

@@ -127,7 +128,7 @@ def init_user
127128

128129
def user_params
129130
allowed_parameters = [:email, :username, :password, :image, :image_url, {
130-
profile_attributes: [:firstname, :surname, :email, :website, :public,
131+
profile_attributes: [:id, :firstname, :surname, :email, :website, :public,
131132
:description, :location, :orcid, :experience,
132133
{ expertise_academic: [] }, { expertise_technical: [] },
133134
{ interest: [] }, { activity: [] }, { language: [] },
@@ -141,4 +142,13 @@ def user_params
141142
def invitees_enabled?
142143
feature_enabled?('invitation')
143144
end
145+
146+
# Prevent assigning other profiles
147+
def check_profile_id
148+
profile_id = @user.profile.id
149+
incoming_id = user_params.dig(:profile_attributes, :id)
150+
if profile_id && incoming_id && profile_id.to_i != incoming_id.to_i
151+
handle_error(:forbidden, 'Invalid profile ID.')
152+
end
153+
end
144154
end

app/models/concerns/searchable.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,12 @@ def search_and_filter(user, search_params = '', selected_facets = {}, page: 1, s
8686
end
8787
end
8888

89-
if attribute_method?(:public) && !user&.is_admin? # Find a better way of checking this
89+
if name == 'Trainer' || name == 'Profile'
90+
# `public` means "Show in trainer registry" for Trainers/Profiles
91+
any_of do
92+
with(:public, true)
93+
end
94+
elsif attribute_method?(:public) && !user&.is_admin? # Find a better way of checking this
9095
any_of do
9196
with(:public, true)
9297
with(:user_id, user.id) if user

app/models/profile.rb

Lines changed: 7 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,48 +9,14 @@ class Profile < ApplicationRecord
99
validates :website, url: true, http_url: true, allow_blank: true
1010
validates :orcid, orcid: true, allow_blank: true
1111
after_validation :check_public
12+
after_commit :reindex_trainer
1213
clean_array_fields(:expertise_academic, :expertise_technical, :fields,
1314
:interest, :activity, :language, :social_media)
1415
update_suggestions(:expertise_technical, :interest)
1516

1617
extend FriendlyId
1718
friendly_id :full_name, use: :slugged
1819

19-
after_update_commit :reindex
20-
after_destroy_commit :reindex
21-
22-
if TeSS::Config.solr_enabled
23-
# :nocov:
24-
searchable do
25-
# full text search fields
26-
text :firstname
27-
text :surname
28-
text :description
29-
# sort title
30-
string :sort_title do
31-
full_name.downcase
32-
end
33-
# other fields
34-
integer :user_id
35-
string :full_name
36-
string :location
37-
string :orcid
38-
string :experience do
39-
TrainerExperienceDictionary.instance.lookup_value(self.experience, 'title')
40-
end
41-
string :expertise_academic, multiple: true
42-
string :expertise_technical, multiple: true
43-
string :fields, :multiple => true
44-
string :interest, multiple: true
45-
string :activity, multiple: true
46-
string :language, multiple: true
47-
string :social_media, multiple: true
48-
time :updated_at
49-
boolean :public
50-
end
51-
# :nocov:
52-
end
53-
5420
def self.facet_fields
5521
field_list = %w( full_name )
5622
end
@@ -93,14 +59,14 @@ def check_public
9359
public ? self.type = 'Trainer' : self.type = 'Profile'
9460
end
9561

96-
def reindex
97-
if TeSS::Config.solr_enabled
98-
Trainer.reindex
99-
end
100-
end
101-
10262
def should_generate_new_friendly_id?
10363
firstname_changed? or surname_changed?
10464
end
10565

66+
# This is needed if the `public` status of the profile changes when it is already instantiated - it won't be cast
67+
# to a Trainer object and indexed by solr (only Trainer class is `searchable`).
68+
def reindex_trainer
69+
return unless TeSS::Config.solr_enabled
70+
becomes(Trainer).solr_index
71+
end
10672
end

app/models/trainer.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22

33
# define model for Trainer as subset of Profile
44
class Trainer < Profile
5-
6-
after_update_commit :reindex
7-
after_destroy_commit :reindex
8-
95
extend FriendlyId
106
friendly_id :full_name, use: :slugged
117

test/controllers/users_controller_test.rb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,19 @@ class UsersControllerTest < ActionController::TestCase
118118
end
119119

120120
test "should update profile" do
121-
sign_in users(:regular_user)
122-
patch :update, params: { id: @user, user: { profile_attributes: { email: 'hot@mail.com' } } }
121+
sign_in @user
122+
profile_id = @user.profile.id
123+
patch :update, params: { id: @user, user: { profile_attributes: { id: profile_id, email: 'hot@mail.com' } } }
123124
assert_redirected_to user_path(assigns(:user))
125+
assert_equal profile_id, assigns(:user).profile.id
126+
end
127+
128+
test "should not update profile ID" do
129+
sign_in @user
130+
another_profile_id = users(:another_regular_user).profile.id
131+
patch :update, params: { id: @user, user: { profile_attributes: { id: another_profile_id, email: 'hot@mail.com' } } }
132+
assert_response :forbidden
133+
assert_not_equal another_profile_id, @user.reload.profile.id
124134
end
125135

126136
test "should reset token" do

test/test_helper.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,8 @@ def mock_images
220220
WebMock.stub_request(:get, 'https://bio.tools/api/tool?q=Material%20with%20suggestions')
221221
.with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent' => 'Ruby' })
222222
.to_return(status: 200, body: '', headers: {})
223+
224+
WebMock.stub_request(:any, 'http://example.com/').to_return(status: 200)
223225
end
224226

225227
def mock_orcids

0 commit comments

Comments
 (0)