-
Notifications
You must be signed in to change notification settings - Fork 479
/
registrations_controller.rb
326 lines (294 loc) · 10.7 KB
/
registrations_controller.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
class RegistrationsController < Devise::RegistrationsController
respond_to :json
prepend_before_action :authenticate_scope!, only: [
:edit, :update, :destroy, :upgrade, :set_email, :set_user_type,
:migrate_to_multi_auth, :demigrate_from_multi_auth
]
skip_before_action :verify_authenticity_token, only: [:set_age]
def new
session[:user_return_to] ||= params[:user_return_to]
@already_hoc_registered = params[:already_hoc_registered]
super
end
#
# PUT /users
#
def update
return head(:bad_request) if params[:user].nil?
# Use set_user_type instead
return head(:bad_request) if params[:user][:user_type].present?
# Use set_email instead
return head(:bad_request) if params[:user][:email].present?
return head(:bad_request) if params[:user][:hashed_email].present?
successfully_updated =
if forbidden_change?(current_user, params)
false
elsif needs_password?(current_user, params)
current_user.update_with_password(update_params(params))
else
# remove the virtual current_password attribute update_without_password
# doesn't know how to ignore it
params[:user].delete(:current_password)
current_user.update_without_password(update_params(params))
end
respond_to_account_update(successfully_updated)
end
def create
Retryable.retryable on: [Mysql2::Error, ActiveRecord::RecordNotUnique], matching: /Duplicate entry/ do
super
end
should_send_new_teacher_email = current_user && current_user.teacher?
TeacherMailer.new_teacher_email(current_user).deliver_now if should_send_new_teacher_email
if current_user
storage_id = take_storage_id_ownership_from_cookie(current_user.id)
current_user.generate_progress_from_storage_id(storage_id) if storage_id
end
end
def sign_up_params
super.tap do |params|
if params[:user_type] == "teacher"
params[:email_preference_opt_in_required] = true
params[:email_preference_request_ip] = request.ip
params[:email_preference_source] = EmailPreference::ACCOUNT_SIGN_UP
params[:email_preference_form_kind] = "0"
end
params[:data_transfer_agreement_accepted] = params[:data_transfer_agreement_accepted] == "1"
if params[:data_transfer_agreement_required] && params[:data_transfer_agreement_accepted]
params[:data_transfer_agreement_accepted] = true
params[:data_transfer_agreement_request_ip] = request.ip
params[:data_transfer_agreement_source] = User::ACCOUNT_SIGN_UP
params[:data_transfer_agreement_kind] = "0"
params[:data_transfer_agreement_at] = DateTime.now
end
end
end
# Set age for the current user if empty - skips CSRF verification because this can be called
# from cached pages which will not populate the CSRF token
def set_age
current_user.update(age: params[:user][:age]) unless current_user.age.present?
end
def upgrade
return head(:bad_request) if params[:user].nil?
params_to_pass = params.deep_dup
# Set provider to nil to mark the account as self-managed
user_params = params_to_pass[:user].merge!({provider: nil})
current_user.reload # Needed to make tests pass for reasons noted in registrations_controller_test.rb
can_update =
if current_user.teacher_managed_account?
if current_user.secret_word_account?
secret_words_match = user_params[:secret_words] == current_user.secret_words
unless secret_words_match
error_string = user_params[:secret_words].blank? ? :blank_plural : :invalid_plural
current_user.errors.add(:secret_words, error_string)
end
secret_words_match
else
true
end
else
false
end
successfully_updated = can_update && current_user.update(update_params(params_to_pass))
has_email = current_user.parent_email.blank? && current_user.hashed_email.present?
success_message_kind = has_email ? :personal_login_created_email : :personal_login_created_username
if successfully_updated && current_user.parent_email.present?
ParentMailer.student_associated_with_parent_email(current_user.parent_email, current_user).deliver_now
end
current_user.reload unless successfully_updated # if update fails, roll back user model so error page renders correctly
respond_to_account_update(successfully_updated, success_message_kind)
end
#
# PATCH /users/email
#
# Route allowing user to update their primary email address.
#
def set_email
return head(:bad_request) if params[:user].nil?
successfully_updated =
if current_user.migrated?
if forbidden_change?(current_user, params)
false
elsif needs_password?(current_user, params)
if current_user.valid_password?(params[:user][:current_password])
current_user.update_primary_contact_info(user: set_email_params)
else
false
end
else
current_user.update_primary_contact_info(user: set_email_params)
end
else
if forbidden_change?(current_user, params)
false
elsif needs_password?(current_user, params)
current_user.update_with_password(set_email_params)
else
params[:user].delete(:current_password)
current_user.update_without_password(set_email_params)
end
end
if successfully_updated
head :no_content
else
render status: :unprocessable_entity,
json: current_user.errors.as_json(full_messages: true),
content_type: 'application/json'
end
end
#
# PATCH /users/user_type
#
# Route allowing user to change from a student to a teacher, or from a
# teacher to a student.
#
def set_user_type
return head(:bad_request) if params[:user].nil?
return head(:bad_request) if params[:user][:user_type].nil?
successfully_updated =
if forbidden_change?(current_user, params)
false
elsif needs_password?(current_user, params)
# Guaranteed to fail, but sets appropriate user errors for response
current_user.update_with_password(set_user_type_params)
else
current_user.update_without_password(set_user_type_params)
end
if successfully_updated
head :no_content
else
render status: :unprocessable_entity,
json: current_user.errors.as_json(full_messages: true),
content_type: 'application/json'
end
end
#
# GET /users/migrate_to_multi_auth
#
def migrate_to_multi_auth
was_migrated = current_user.migrated?
current_user.migrate_to_multi_auth
redirect_to after_update_path_for(current_user),
notice: "Multi-auth is #{was_migrated ? 'still' : 'now'} enabled on your account."
end
#
# GET /users/demigrate_from_multi_auth
#
def demigrate_from_multi_auth
was_migrated = current_user.migrated?
current_user.demigrate_from_multi_auth
redirect_to after_update_path_for(current_user),
notice: "Multi-auth is #{was_migrated ? 'now' : 'still'} disabled on your account."
end
private
def respond_to_account_update(successfully_updated, flash_message_kind = :updated)
user = current_user
respond_to do |format|
if successfully_updated
set_locale_cookie(user.locale)
# Sign in the user bypassing validation in case his password changed
bypass_sign_in user
format.html do
set_flash_message :notice, flash_message_kind, {username: user.username}
begin
redirect_back fallback_location: after_update_path_for(user)
rescue ActionController::RedirectBackError
redirect_to after_update_path_for(user)
end
end
format.any {head :no_content}
else
format.html {render "edit", formats: [:html]}
format.any do
render status: :unprocessable_entity,
json: user.errors.as_json(full_messages: true),
content_type: 'application/json'
end
end
end
end
# Reject certain changes for certain users outright
def forbidden_change?(user, params)
return true if params[:user][:password].present? && !user.can_edit_password?
return true if params[:user][:email].present? && !user.can_edit_email?
return true if params[:user][:hashed_email].present? && !user.can_edit_email?
false
end
# check if we need password to update user data
# ie if password or email was changed
# extend this as needed
def needs_password?(user, params)
return false if user.migrated? && user.encrypted_password.blank? && params[:user][:password].blank?
email_is_changing = params[:user][:email].present? &&
user.email != params[:user][:email]
hashed_email_is_changing = params[:user][:hashed_email].present? &&
user.hashed_email != params[:user][:hashed_email]
new_email_matches_hashed_email = email_is_changing &&
User.hash_email(params[:user][:email]) == user.hashed_email
(email_is_changing && !new_email_matches_hashed_email) ||
hashed_email_is_changing ||
params[:user][:password].present?
end
# Accept only whitelisted params for update.
def update_params(params)
params.require(:user).permit(
:parent_email,
:username,
:password,
:encrypted_password,
:current_password,
:password_confirmation,
:gender,
:name,
:locale,
:age,
:birthday,
:school,
:full_address,
:terms_of_service_version,
:provider,
school_info_attributes: [
:country,
:school_type,
:state, :school_state,
:zip, :school_zip,
:school_district_id,
:school_district_other,
:school_district_name,
:school_id,
:school_other,
:school_name,
:full_address
],
races: []
)
end
def set_email_params
params.
require(:user).
permit(:email, :hashed_email, :current_password).
merge(email_preference_params(EmailPreference::ACCOUNT_EMAIL_CHANGE, "0"))
end
def set_user_type_params
params.
require(:user).
permit(:user_type, :email, :hashed_email).
merge(email_preference_params(EmailPreference::ACCOUNT_TYPE_CHANGE, "0"))
end
def email_preference_params(source, form_kind)
params.
require(:user).
tap do |user|
if user[:email_preference_opt_in].present?
user[:email_preference_request_ip] = request.ip
user[:email_preference_source] = source
user[:email_preference_form_kind] = form_kind
end
end.
permit(
:email_preference_opt_in,
:email_preference_request_ip,
:email_preference_source,
:email_preference_form_kind,
)
end
end