diff --git a/app/controllers/api/v1/users/passwords_controller.rb b/app/controllers/api/v1/users/passwords_controller.rb index 0eacf3a6..da769eeb 100644 --- a/app/controllers/api/v1/users/passwords_controller.rb +++ b/app/controllers/api/v1/users/passwords_controller.rb @@ -7,16 +7,40 @@ class PasswordsController < ApiController # # @see http://www.rubydoc.info/github/plataformatec/devise/master/Devise/Models/Recoverable # - def reset + def forgot user = User.find_by email: params[:email] - raise 'Could not find a user with that email address' unless user.present? + user.generate_password_token! user.send_reset_password_instructions - render json: { status: :ok } rescue StandardError => e render json: { errors: e.message }, status: :unprocessable_entity end + + def reset + token = params[:reset_password_token].to_s + user = User.with_reset_password_token(token) + user.reset_password_token + if user.present? && user.password_token_valid? + if user.reset_password!(params[:password]) + render json: { status: 'ok' }, status: :ok + else + render json: { error: user.errors.full_messages }, status: :unprocessable_entity + end + else + render json: { error: ['Link not valid or expired. Try generating a new link.'] }, status: :not_found + end + end + + def update + render json: { error: 'Password not present' }, status: :unprocessable_entity unless params[:password].present + + if current_user.reset_password(params[:password]) + render json: { status: 'ok' }, status: :ok + else + render json: { errors: current_user.errors.full_messages }, status: :unprocessable_entity + end + end end end end diff --git a/app/models/user.rb b/app/models/user.rb index 408002c7..c577ed9c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -204,6 +204,24 @@ def role Role.find_by id: role_id end + def generate_password_token! + _raw, enc = Devise.token_generator.generate(User, :reset_password_token) + self.reset_password_token = enc + self.reset_password_sent_at = Time.now.utc + self.save(validate: false) + end + + def reset_password!(new_password) + @password = new_password + self.encrypted_password = password_digest(@password) if @password.present? + self.reset_password_token = nil + save! + end + + def password_token_valid? + (self.reset_password_sent_at + 4.hours) > Time.now.utc + end + private def strip_zip_code diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb index 9fb387e8..e36f57c2 100644 --- a/app/views/devise/mailer/reset_password_instructions.html.erb +++ b/app/views/devise/mailer/reset_password_instructions.html.erb @@ -3,6 +3,5 @@

Someone has requested a link to change your password. You can do this through the link below.

<%= link_to 'Change my password', "https://operationcode.org/reset_password?reset_password_token=#{@token}" %>

-

If you didn't request this, please ignore this email.

Your password won't change until you access the link above and create a new one.

diff --git a/config/environments/development.rb b/config/environments/development.rb index 134572cd..5223b144 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -37,7 +37,7 @@ # Raise an error on page load if there are pending migrations. config.active_record.migration_error = :page_load - + config.action_mailer.default_url_options = { host: 'localhost:3000' } # Raises error for missing translations # config.action_view.raise_on_missing_translations = true diff --git a/config/routes.rb b/config/routes.rb index 2a3e8193..67e9b0c3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -56,7 +56,9 @@ end namespace :users do + post '/passwords/forgot', to: 'passwords#forgot' post '/passwords/reset', to: 'passwords#reset' + put '/password/update', to: 'password#update' end namespace :airtable do