diff --git a/app/controllers/users/passwords_controller.rb b/app/controllers/users/passwords_controller.rb new file mode 100644 index 0000000000..b18c97ebd4 --- /dev/null +++ b/app/controllers/users/passwords_controller.rb @@ -0,0 +1,71 @@ +class Users::PasswordsController < Devise::PasswordsController + include ApplicationHelper + include PhoneNumberHelper + include SmsBodyHelper + + def create + email, phone_number = [params[resource_name][:email], params[resource_name][:phone_number]] + @resource = email.blank? ? User.find_by(phone_number: phone_number) : User.find_by(email: email) + + # re-render and display any errors + params_is_valid, error_resource = password_params_is_valid(resource, email, phone_number) + if !params_is_valid + respond_with(error_resource) + return + end + + # generate a reset token and + # call devise mailer + reset_token = send_email_reset(email) + # for case where user enters ONLY a phone number, generate a new reset token to use; + # otherwise, use the same reset token as sent by devise mailer + send_sms_reset(@resource, phone_number, reset_token) + redirect_to after_sending_reset_password_instructions_path_for(resource_name), notice: "You will receive an email or SMS with instructions on how to reset your password in a few minutes." + end + + private + + def send_email_reset(email) + reset_token = nil + if !email.blank? + reset_token = @resource.send_reset_password_instructions + end + reset_token + end + + def send_sms_reset(resource, phone_number, reset_token) + if !phone_number.blank? + reset_token ||= resource.generate_password_reset_token + short_io_service = ShortUrlService.new + short_io_service.create_short_url(request.base_url + "/users/password/edit?reset_password_token=#{reset_token}") + twilio_service = TwilioService.new(resource.casa_org.twilio_api_key_sid, resource.casa_org.twilio_api_key_secret, resource.casa_org.twilio_account_sid) + sms_params = { + From: resource.casa_org.twilio_phone_number, + Body: password_reset_msg(resource.display_name, short_io_service.short_url), + To: phone_number + } + twilio_service.send_sms(sms_params) + end + end + + def password_params_is_valid(resource, email, phone_number) + if email.blank? && phone_number.blank? + resource.errors.add(:base, "Please enter at least one field.") + return [false, resource] + end + + phone_number_is_valid, error_message = valid_phone_number(phone_number) + if !phone_number_is_valid + resource.errors.add(:phone_number, error_message) + return [false, resource] + end + + if resource.email != email || resource.phone_number != phone_number + # A new, empty resource is returned (see application helper) + # so to check for nil, we need to check its email/phone fields + resource.errors.add(:base, "User does not exist.") + return [false, resource] + end + [true, nil] + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 21f8ebf75f..306f0af64a 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -44,4 +44,16 @@ def flash_class(level) def og_tag(type, options = {}) tag.meta(property: "og:#{type}", **options) end + + def resource_name + :user + end + + def resource + @resource ||= User.new + end + + def devise_mapping + @devise_mapping ||= Devise.mappings[:user] + end end diff --git a/app/helpers/sms_body_helper.rb b/app/helpers/sms_body_helper.rb index 42cd991670..3cfc66b53e 100644 --- a/app/helpers/sms_body_helper.rb +++ b/app/helpers/sms_body_helper.rb @@ -29,4 +29,8 @@ def no_contact_made_msg(contact_type, short_link) def case_contact_flagged_msg(display_name, short_link) "#{display_name} has flagged a Case Contact that needs follow up. Click to see more: #{short_link}" end + + def password_reset_msg(display_name, short_link) + "Hi #{display_name}, click here to reset your password: #{short_link}" + end end diff --git a/app/models/casa_case_contact_type.rb b/app/models/casa_case_contact_type.rb index a41eda5dfc..570ddbf4bd 100644 --- a/app/models/casa_case_contact_type.rb +++ b/app/models/casa_case_contact_type.rb @@ -12,8 +12,8 @@ class CasaCaseContactType < ApplicationRecord # id :bigint not null, primary key # created_at :datetime not null # updated_at :datetime not null -# casa_case_id :bigint not null -# contact_type_id :bigint not null +# casa_case_id :integer not null +# contact_type_id :integer not null # # Indexes # diff --git a/app/models/case_assignment.rb b/app/models/case_assignment.rb index a9a7a918bf..93b601b6fe 100644 --- a/app/models/case_assignment.rb +++ b/app/models/case_assignment.rb @@ -36,8 +36,8 @@ def casa_case_and_volunteer_must_belong_to_same_casa_org # hide_old_contacts :boolean default(FALSE) # created_at :datetime not null # updated_at :datetime not null -# casa_case_id :bigint not null -# volunteer_id :bigint not null +# casa_case_id :integer not null +# volunteer_id :integer not null # # Indexes # diff --git a/app/models/case_contact.rb b/app/models/case_contact.rb index 5b1e238001..d798791afa 100644 --- a/app/models/case_contact.rb +++ b/app/models/case_contact.rb @@ -215,8 +215,8 @@ def self.options_for_sorted_by # want_driving_reimbursement :boolean default(FALSE) # created_at :datetime not null # updated_at :datetime not null -# casa_case_id :bigint not null -# creator_id :bigint not null +# casa_case_id :integer not null +# creator_id :integer not null # # Indexes # diff --git a/app/models/case_contact_contact_type.rb b/app/models/case_contact_contact_type.rb index 9376453cba..a15836a2e6 100644 --- a/app/models/case_contact_contact_type.rb +++ b/app/models/case_contact_contact_type.rb @@ -12,8 +12,8 @@ class CaseContactContactType < ApplicationRecord # id :bigint not null, primary key # created_at :datetime not null # updated_at :datetime not null -# case_contact_id :bigint not null -# contact_type_id :bigint not null +# case_contact_id :integer not null +# contact_type_id :integer not null # # Indexes # diff --git a/app/models/contact_type.rb b/app/models/contact_type.rb index 91f400e08b..0552ffc790 100644 --- a/app/models/contact_type.rb +++ b/app/models/contact_type.rb @@ -21,7 +21,7 @@ class ContactType < ApplicationRecord # name :string not null # created_at :datetime not null # updated_at :datetime not null -# contact_type_group_id :bigint not null +# contact_type_group_id :integer not null # # Indexes # diff --git a/app/models/contact_type_group.rb b/app/models/contact_type_group.rb index 5d4e97c577..752d5ddb87 100644 --- a/app/models/contact_type_group.rb +++ b/app/models/contact_type_group.rb @@ -49,7 +49,7 @@ def generate_for_org!(casa_org) # name :string not null # created_at :datetime not null # updated_at :datetime not null -# casa_org_id :bigint not null +# casa_org_id :integer not null # # Indexes # diff --git a/app/models/court_date.rb b/app/models/court_date.rb index 045b9bef0b..73ddd69fef 100644 --- a/app/models/court_date.rb +++ b/app/models/court_date.rb @@ -73,7 +73,7 @@ def case_court_orders_context_hash # date :datetime not null # created_at :datetime not null # updated_at :datetime not null -# casa_case_id :bigint not null +# casa_case_id :integer not null # hearing_type_id :bigint # judge_id :bigint # diff --git a/app/models/emancipation_option.rb b/app/models/emancipation_option.rb index 17ad42f96d..6f1293ff26 100644 --- a/app/models/emancipation_option.rb +++ b/app/models/emancipation_option.rb @@ -24,7 +24,7 @@ class EmancipationOption < ApplicationRecord # name :string not null # created_at :datetime not null # updated_at :datetime not null -# emancipation_category_id :bigint not null +# emancipation_category_id :integer not null # # Indexes # diff --git a/app/models/hearing_type.rb b/app/models/hearing_type.rb index e35d0f0d7f..2c1ff0969c 100644 --- a/app/models/hearing_type.rb +++ b/app/models/hearing_type.rb @@ -38,7 +38,7 @@ def generate_for_org!(casa_org) # active :boolean default(TRUE), not null # checklist_updated_date :string default("None"), not null # name :string not null -# casa_org_id :bigint not null +# casa_org_id :integer not null # # Indexes # diff --git a/app/models/judge.rb b/app/models/judge.rb index 0892f10b80..2d34129209 100644 --- a/app/models/judge.rb +++ b/app/models/judge.rb @@ -16,7 +16,7 @@ class Judge < ApplicationRecord # name :string # created_at :datetime not null # updated_at :datetime not null -# casa_org_id :bigint not null +# casa_org_id :integer not null # # Indexes # diff --git a/app/models/supervisor.rb b/app/models/supervisor.rb index 55a445d483..3f9262abaa 100644 --- a/app/models/supervisor.rb +++ b/app/models/supervisor.rb @@ -73,7 +73,7 @@ def recently_unassigned_volunteers # created_at :datetime not null # updated_at :datetime not null # casa_org_id :bigint not null -# invited_by_id :bigint +# invited_by_id :integer # # Indexes # diff --git a/app/models/supervisor_volunteer.rb b/app/models/supervisor_volunteer.rb index a4dfbd9e1b..e8df9d30a7 100644 --- a/app/models/supervisor_volunteer.rb +++ b/app/models/supervisor_volunteer.rb @@ -24,8 +24,8 @@ def ensure_supervisor_and_volunteer_belong_to_same_casa_org # is_active :boolean default(TRUE) # created_at :datetime not null # updated_at :datetime not null -# supervisor_id :bigint not null -# volunteer_id :bigint not null +# supervisor_id :integer not null +# volunteer_id :integer not null # # Indexes # diff --git a/app/models/user.rb b/app/models/user.rb index f212295595..6ea60b36d2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -165,7 +165,7 @@ def serving_transition_aged_youth? # created_at :datetime not null # updated_at :datetime not null # casa_org_id :bigint not null -# invited_by_id :bigint +# invited_by_id :integer # # Indexes # diff --git a/app/models/volunteer.rb b/app/models/volunteer.rb index 6554ceaa27..89e2694f5b 100644 --- a/app/models/volunteer.rb +++ b/app/models/volunteer.rb @@ -164,7 +164,7 @@ def cases_where_contact_made_in_days(num_days = CONTACT_MADE_IN_DAYS_NUM) # created_at :datetime not null # updated_at :datetime not null # casa_org_id :bigint not null -# invited_by_id :bigint +# invited_by_id :integer # # Indexes # diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb index 03d4549d98..c0008a01fc 100644 --- a/app/views/devise/passwords/new.html.erb +++ b/app/views/devise/passwords/new.html.erb @@ -10,9 +10,16 @@ <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: {method: :post}) do |f| %> <%= render "/shared/error_messages", resource: resource %> +