* <Added password reset system> * <Added rspec tests> * <Fixed code style> * <Added rescue for invalid smtp configuration>v2
parent
e3389c84d1
commit
8fe66d81f1
@ -0,0 +1,90 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/. |
||||
# |
||||
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below). |
||||
# |
||||
# This program is free software; you can redistribute it and/or modify it under the |
||||
# terms of the GNU Lesser General Public License as published by the Free Software |
||||
# Foundation; either version 3.0 of the License, or (at your option) any later |
||||
# version. |
||||
# |
||||
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY |
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
||||
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU Lesser General Public License along |
||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. |
||||
|
||||
class PasswordResetsController < ApplicationController |
||||
before_action :disable_password_reset, unless: -> { Rails.configuration.enable_email_verification } |
||||
before_action :find_user, only: [:edit, :update] |
||||
before_action :valid_user, only: [:edit, :update] |
||||
before_action :check_expiration, only: [:edit, :update] |
||||
|
||||
def index |
||||
end |
||||
|
||||
def create |
||||
@user = User.find_by(email: params[:password_reset][:email].downcase) |
||||
if @user |
||||
@user.create_reset_digest |
||||
@user.send_password_reset_email(request.base_url) |
||||
redirect_to root_url, notice: I18n.t("email_sent") |
||||
else |
||||
redirect_to new_password_reset_path, notice: I18n.t("no_user_email_exists") |
||||
end |
||||
rescue => e |
||||
logger.error "Error in email delivery: #{e}" |
||||
redirect_to root_path, notice: I18n.t(params[:message], default: I18n.t("delivery_error")) |
||||
end |
||||
|
||||
def edit |
||||
end |
||||
|
||||
def update |
||||
if params[:user][:password].empty? |
||||
flash.now[:notice] = I18n.t("password_empty_notice") |
||||
render 'edit' |
||||
elsif params[:user][:password] != params[:user][:password_confirmation] |
||||
flash.now[:notice] = I18n.t("password_different_notice") |
||||
render 'edit' |
||||
elsif current_user.update_attributes(user_params) |
||||
redirect_to root_path, notice: I18n.t("password_reset_success") |
||||
else |
||||
render 'edit' |
||||
end |
||||
end |
||||
|
||||
private |
||||
|
||||
def find_user |
||||
@user = User.find_by(email: params[:email]) |
||||
end |
||||
|
||||
def current_user |
||||
@user |
||||
end |
||||
|
||||
def user_params |
||||
params.require(:user).permit(:password, :password_confirmation) |
||||
end |
||||
|
||||
# Checks expiration of reset token. |
||||
def check_expiration |
||||
if current_user.password_reset_expired? |
||||
redirect_to new_password_reset_url, notice: I18n.t("expired_reset_token") |
||||
end |
||||
end |
||||
|
||||
# Confirms a valid user. |
||||
def valid_user |
||||
unless current_user&.email_verified && current_user.authenticated?(:reset, params[:id]) |
||||
redirect_to root_url |
||||
end |
||||
end |
||||
|
||||
def disable_password_reset |
||||
redirect_to '/404' |
||||
end |
||||
end |
@ -0,0 +1,20 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/. |
||||
# |
||||
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below). |
||||
# |
||||
# This program is free software; you can redistribute it and/or modify it under the |
||||
# terms of the GNU Lesser General Public License as published by the Free Software |
||||
# Foundation; either version 3.0 of the License, or (at your option) any later |
||||
# version. |
||||
# |
||||
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY |
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
||||
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU Lesser General Public License along |
||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. |
||||
|
||||
module PasswordResetsHelper |
||||
end |
@ -0,0 +1,50 @@ |
||||
<% |
||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/. |
||||
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below). |
||||
# This program is free software; you can redistribute it and/or modify it under the |
||||
# terms of the GNU Lesser General Public License as published by the Free Software |
||||
# Foundation; either version 3.0 of the License, or (at your option) any later |
||||
# version. |
||||
# |
||||
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY |
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
||||
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. |
||||
# You should have received a copy of the GNU Lesser General Public License along |
||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. |
||||
%> |
||||
|
||||
<% unless flash.empty? %> |
||||
<%= render "shared/error_banner" do %> |
||||
<% flash.each do |key, value| %> |
||||
<%= content_tag :div, value, class: "flash #{key} d-inline" %> |
||||
<% end %> |
||||
<% end %> |
||||
<% end %> |
||||
|
||||
<div class="container"> |
||||
<div class="row pt-7"> |
||||
<div class="col col-4 offset-4"> |
||||
<div class="card"> |
||||
<div class="card-header background"> |
||||
<h4 class="mt-2"><%= t("reset_password.subtitle") %></h4> |
||||
</div> |
||||
<div class="card-body background"> |
||||
<%= form_for(@user, url: password_reset_path(params[:id])) do |f| %> |
||||
|
||||
<%= hidden_field_tag :email, @user.email %> |
||||
|
||||
<%= f.label t('reset_password.password'), class: "form-label" %> |
||||
<%= f.password_field :password, class: 'form-control' %> |
||||
<br> |
||||
|
||||
<%= f.label t('reset_password.confirm'), class: "form-label" %> |
||||
<%= f.password_field :password_confirmation, class: 'form-control' %> |
||||
<br> |
||||
|
||||
<%= f.submit t('reset_password.update'), class: "btn btn-primary" %> |
||||
<% end %> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
@ -0,0 +1,43 @@ |
||||
<% |
||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/. |
||||
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below). |
||||
# This program is free software; you can redistribute it and/or modify it under the |
||||
# terms of the GNU Lesser General Public License as published by the Free Software |
||||
# Foundation; either version 3.0 of the License, or (at your option) any later |
||||
# version. |
||||
# |
||||
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY |
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
||||
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. |
||||
# You should have received a copy of the GNU Lesser General Public License along |
||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. |
||||
%> |
||||
|
||||
<% unless flash.empty? %> |
||||
<%= render "shared/error_banner" do %> |
||||
<% flash.each do |key, value| %> |
||||
<%= content_tag :div, value, class: "flash #{key} d-inline" %> |
||||
<% end %> |
||||
<% end %> |
||||
<% end %> |
||||
|
||||
<div class="container"> |
||||
<div class="row pt-7"> |
||||
<div class="col col-4 offset-4"> |
||||
<div class="card"> |
||||
<div class="card-header background"> |
||||
<h4 class="mt-2"><%= t("forgot_password.subtitle") %></h4> |
||||
</div> |
||||
<div class="card-body background"> |
||||
<%= form_for(:password_reset, url: password_resets_path) do |f| %> |
||||
<%= f.label t("forgot_password.email"), class: "form-label" %> |
||||
<%= f.email_field :email, class: "form-control" %> |
||||
<br> |
||||
|
||||
<%= f.submit t("forgot_password.submit"), class: "btn btn-primary" %> |
||||
<% end %> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
@ -0,0 +1,32 @@ |
||||
<% |
||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/. |
||||
# |
||||
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below). |
||||
# |
||||
# This program is free software; you can redistribute it and/or modify it under the |
||||
# terms of the GNU Lesser General Public License as published by the Free Software |
||||
# Foundation; either version 3.0 of the License, or (at your option) any later |
||||
# version. |
||||
# |
||||
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY |
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
||||
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU Lesser General Public License along |
||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. |
||||
%> |
||||
|
||||
<h1>Password reset</h1> |
||||
|
||||
<p>Please click the link below to reset your password:</p> |
||||
|
||||
<%= link_to "Reset password", edit_password_reset_url(@user.reset_token, |
||||
email: @user.email, |
||||
host: @url) %> |
||||
|
||||
<p>This link will expire in two hours.</p> |
||||
|
||||
<p> |
||||
If you did not request your password to be reset, please ignore this email and |
||||
your password will not be changed. |
||||
</p> |
@ -0,0 +1,26 @@ |
||||
<% |
||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/. |
||||
# |
||||
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below). |
||||
# |
||||
# This program is free software; you can redistribute it and/or modify it under the |
||||
# terms of the GNU Lesser General Public License as published by the Free Software |
||||
# Foundation; either version 3.0 of the License, or (at your option) any later |
||||
# version. |
||||
# |
||||
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY |
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
||||
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU Lesser General Public License along |
||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. |
||||
%> |
||||
|
||||
Please click the link below to reset your password: |
||||
|
||||
<%= edit_password_reset_url(@user.reset_token, email: @user.email, host: @url) %> |
||||
|
||||
This link will expire in two hours. |
||||
|
||||
If you did not request your password to be reset, please ignore this email and |
||||
your password will not be changed. |
@ -0,0 +1,24 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/. |
||||
# |
||||
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below). |
||||
# |
||||
# This program is free software; you can redistribute it and/or modify it under the |
||||
# terms of the GNU Lesser General Public License as published by the Free Software |
||||
# Foundation; either version 3.0 of the License, or (at your option) any later |
||||
# version. |
||||
# |
||||
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY |
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
||||
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU Lesser General Public License along |
||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. |
||||
|
||||
class AddResetToUsers < ActiveRecord::Migration[5.0] |
||||
def change |
||||
add_column :users, :reset_digest, :string |
||||
add_column :users, :reset_sent_at, :datetime |
||||
end |
||||
end |
@ -0,0 +1,138 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/. |
||||
# |
||||
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below). |
||||
# |
||||
# This program is free software; you can redistribute it and/or modify it under the |
||||
# terms of the GNU Lesser General Public License as published by the Free Software |
||||
# Foundation; either version 3.0 of the License, or (at your option) any later |
||||
# version. |
||||
# |
||||
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY |
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
||||
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU Lesser General Public License along |
||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. |
||||
|
||||
require "rails_helper" |
||||
|
||||
def random_valid_user_params |
||||
pass = Faker::Internet.password(8) |
||||
{ |
||||
user: { |
||||
name: Faker::Name.first_name, |
||||
email: Faker::Internet.email, |
||||
password: pass, |
||||
password_confirmation: pass, |
||||
accepted_terms: true, |
||||
email_verified: true, |
||||
}, |
||||
} |
||||
end |
||||
|
||||
describe PasswordResetsController, type: :controller do |
||||
describe "POST #create" do |
||||
context "allow mail notifications" do |
||||
before { allow(Rails.configuration).to receive(:enable_email_verification).and_return(true) } |
||||
|
||||
it "redirects to root url if email is sent" do |
||||
user = create(:user) |
||||
|
||||
params = { |
||||
password_reset: { |
||||
email: user.email, |
||||
}, |
||||
} |
||||
|
||||
post :create, params: params |
||||
expect(response).to redirect_to(root_path) |
||||
end |
||||
|
||||
it "reloads the page if no email exists in the database" do |
||||
params = { |
||||
password_reset: { |
||||
email: nil, |
||||
}, |
||||
} |
||||
|
||||
post :create, params: params |
||||
expect(response).to redirect_to(new_password_reset_path) |
||||
end |
||||
end |
||||
|
||||
context "does not allow mail notifications" do |
||||
before { allow(Rails.configuration).to receive(:enable_email_verification).and_return(false) } |
||||
|
||||
it "renders a 404 page upon if email notifications are disabled" do |
||||
get :create |
||||
expect(response).to redirect_to("/404") |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe "PATCH #update" do |
||||
before { allow(Rails.configuration).to receive(:enable_email_verification).and_return(true) } |
||||
|
||||
context "valid user" do |
||||
it "reloads page with notice if password is empty" do |
||||
token = "reset_token" |
||||
|
||||
controller.stub(:valid_user).and_return(nil) |
||||
controller.stub(:check_expiration).and_return(nil) |
||||
|
||||
params = { |
||||
id: token, |
||||
user: { |
||||
password: nil, |
||||
}, |
||||
} |
||||
|
||||
patch :update, params: params |
||||
expect(response).to render_template(:edit) |
||||
end |
||||
|
||||
it "reloads page with notice if password is confirmation doesn't match" do |
||||
token = "reset_token" |
||||
|
||||
controller.stub(:valid_user).and_return(nil) |
||||
controller.stub(:check_expiration).and_return(nil) |
||||
|
||||
params = { |
||||
id: token, |
||||
user: { |
||||
password: :password, |
||||
password_confirmation: nil, |
||||
}, |
||||
} |
||||
|
||||
patch :update, params: params |
||||
expect(response).to render_template(:edit) |
||||
end |
||||
|
||||
it "updates attributes if the password update is a success" do |
||||
user = create(:user) |
||||
token = "reset_token" |
||||
|
||||
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost |
||||
user.reset_digest = BCrypt::Password.create(token, cost: cost) |
||||
|
||||
controller.stub(:valid_user).and_return(nil) |
||||
controller.stub(:check_expiration).and_return(nil) |
||||
controller.stub(:current_user).and_return(user) |
||||
|
||||
params = { |
||||
id: token, |
||||
user: { |
||||
password: :password, |
||||
password_confirmation: :password, |
||||
}, |
||||
} |
||||
|
||||
patch :update, params: params |
||||
expect(response).to redirect_to(root_path) |
||||
end |
||||
end |
||||
end |
||||
end |
Reference in new issue