Skip to content
This repository has been archived by the owner on Apr 17, 2023. It is now read-only.

Added the "Password forgotten" option #325

Merged
merged 1 commit into from
Sep 11, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Upcoming Version

- Added a mechanism of password recovery in case users forget about their
password. See PR [#325](https://github.com/SUSE/Portus/pull/325).
- Set admin user from a rake task and disable first-user is admin. See PR [#314]
(https://github.com/SUSE/Portus/pull/314)
- Review requirements and provides in the RPM
Expand Down
1 change: 1 addition & 0 deletions app/controllers/auth/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class Auth::SessionsController < Devise::SessionsController
# the signup page.
def new
if User.not_portus.any? || Portus::LDAP.enabled?
@errors_occurred = flash[:alert] && !flash[:alert].empty?
super
else
# For some reason if we get here from the root path, we'll get a flashy
Expand Down
43 changes: 43 additions & 0 deletions app/controllers/passwords_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# PasswordsController is a Devise controller that takes care of the "password
# forgotten" mechanism.
class PasswordsController < Devise::PasswordsController
layout "authentication"

# Re-implemented from Devise to respond with a proper message on error.
def create
self.resource = resource_class.send_reset_password_instructions(resource_params)
yield resource if block_given?

if successfully_sent?(resource)
respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name))
else
redirect_to new_user_password_path, alert: resource.errors.full_messages
end
end

# Re-implemented from Devise to respond with a proper message on error.
def update
self.resource = resource_class.reset_password_by_token(resource_params)
yield resource if block_given?

if resource.errors.empty?
update_success
else
token = params[:user][:reset_password_token]
redirect_to "/users/password/edit?reset_password_token=#{token}",
alert: resource.errors.full_messages
end
end

protected

def update_success
resource.unlock_access! if unlockable?(resource)

flash_message = resource.active_for_authentication? ? :updated : :updated_not_active
set_flash_message(:notice, flash_message) if is_flashing_format?
sign_in(resource_name, resource)

respond_with resource, location: after_resetting_password_path_for(resource)
end
end
4 changes: 4 additions & 0 deletions app/mailers/devise_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class DeviseMailer < Devise::Mailer
default from: "#{APP_CONFIG["email"]["name"]} <#{APP_CONFIG["email"]["from"]}>"
default reply_to: APP_CONFIG["email"]["reply_to"]
end
15 changes: 15 additions & 0 deletions app/views/devise/passwords/edit.html.slim
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
section.row-0
.center-panel
.col-md-4.col-sm-2.col-xs-1
.col-md-4.col-sm-8.col-xs-10.text-center
= render 'shared/notifications'
= image_tag 'layout/portus-logo-login-page.png', class: 'login-picture'
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f|
= f.hidden_field :reset_password_token
= f.password_field :password, class: 'input form-control input-lg', placeholder: 'Password', autofocus: true, autocomplete: "off", required: true
= f.password_field :password_confirmation, class: 'input form-control input-lg', placeholder: 'Password confirmation', autocomplete: "off", required: true

= f.button class: 'classbutton btn btn-primary btn-block btn-lg' do
i.fa.fa-check Change my password

.text-center = link_to 'Go back to the login page', new_user_session_url
12 changes: 12 additions & 0 deletions app/views/devise/passwords/new.html.slim
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
section.row-0
.center-panel
.col-md-4.col-sm-2.col-xs-1
.col-md-4.col-sm-8.col-xs-10.text-center
= render 'shared/notifications'
= image_tag 'layout/portus-logo-login-page.png', class: 'login-picture'
= form_for(resource, as: resource_name, url: password_path(resource_name)) do |f|
= f.email_field :email, class: 'input form-control input-lg', placeholder: 'Email', autofocus: true, required: true
= f.button class: 'classbutton btn btn-primary btn-block btn-lg' do
i.fa.fa-check Reset password

.text-center = link_to 'Go back to the login page', new_user_session_url
3 changes: 3 additions & 0 deletions app/views/devise/sessions/new.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ section.row-0
| <b>NOTE</b>: The first user to be created will have admin permissions !
- else
.text-center = link_to 'or, Create a new account', new_user_registration_url

- if @errors_occurred
.text-center = link_to "Did you forget your password?", new_user_password_path
11 changes: 11 additions & 0 deletions config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@
# application. In order to change them, write your own config-local.yml file
# (it will be ignored by git).

# Settings for the Portus mailer.
email:
from: "[email protected]"
name: "Portus"
reply_to: "[email protected]"

# If set to true, then SMTP will be used with the configuration values as
# given in config/initializers/smtp.rb. Otherwise 'sendmail' will be used
# (defaults to: /usr/sbin/sendmail -i -t).
smtp: false

# If enabled, then the profile picture will be picked from the Gravatar
# associated with each user. See: https://en.gravatar.com/
gravatar:
Expand Down
8 changes: 8 additions & 0 deletions config/environments/production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@
# Send deprecation notices to registered listeners.
config.active_support.deprecation = :notify

# If SMTP is enabled, then pick it up, and the config/initializers/smtp.rb
# will be loaded. Otherwise, we fallback to sendmail.
if APP_CONFIG["email"]["smtp"].enabled?
config.action_mailer.delivery_method = :smtp
else
config.action_mailer.delivery_method = :sendmail
end

# Use default logging formatter so that PID and timestamp are not suppressed.
config.log_formatter = ::Logger::Formatter.new

Expand Down
1 change: 1 addition & 0 deletions config/environments/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }

# Randomize the order test cases are executed.
config.active_support.test_order = :random
Expand Down
6 changes: 3 additions & 3 deletions config/initializers/devise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
# Configure the e-mail address which will be shown in Devise::Mailer,
# note that it will be overwritten if you use your own mailer class
# with default "from" parameter.
config.mailer_sender = "[email protected]"
# config.mailer_sender = "[email protected]"

# Configure the class responsible to send e-mails.
# config.mailer = 'Devise::Mailer'
config.mailer = "DeviseMailer"

# ==> ORM configuration
# Load and configure the ORM. Supports :active_record (default) and
Expand Down Expand Up @@ -185,7 +185,7 @@
# ==> Configuration for :recoverable
#
# Defines which key will be used when recovering the password for an account
# config.reset_password_keys = [:email]
config.reset_password_keys = [:email]

# Time interval you can reset your password with a reset password key.
# Don't put a too small interval or your users won't have the time to
Expand Down
21 changes: 21 additions & 0 deletions config/initializers/smtp.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Fetch the value of the given "PORTUS_SMTP_*" environment variable. If it's
# not set, then it will raise an exception containing a descriptive message.
def safe_env(name)
name = "PORTUS_SMTP_#{name}".upcase
unless ENV[name]
raise StandardError, "SMTP is enabled but the environment variable '#{name}' has not been set!"
end
ENV[name]
end

if Rails.env.production? && APP_CONFIG["email"]["smtp"]
ActionMailer::Base.smtp_settings = {
address: safe_env("address"),
port: safe_env("port"),
user_name: safe_env("username"),
password: safe_env("password"),
domain: safe_env("domain"),
authentication: :login,
enable_starttls_auto: true
}
end
5 changes: 3 additions & 2 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
post :toggle_star, on: :member
end

devise_for :users, controllers: { registrations: "auth/registrations", sessions: "auth/sessions" }
devise_for :users, controllers: { registrations: "auth/registrations",
sessions: "auth/sessions",
passwords: "passwords" }
resource :dashboard, only: [:index]
resources :search, only: [:index]

Expand Down Expand Up @@ -42,5 +44,4 @@
end
end
match "(errors)/:status", to: "errors#show", constraints: { status: /\d{3}/ }, via: :all

end
33 changes: 33 additions & 0 deletions spec/controllers/passwords_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require "rails_helper"

describe PasswordsController do
before :each do
request.env["devise.mapping"] = Devise.mappings[:user]
@user = create(:admin)
@raw = @user.send_reset_password_instructions
end

it "updates the user's password on success" do
put :update, "user" => {
"reset_password_token" => @raw,
"password" => "12341234",
"password_confirmation" => "12341234"
}

expect(response.status).to eq 302
@user.reload
expect(@user.valid_password?("12341234")).to be true
end

it "does nothing if the user's password does not match confirm" do
put :update, "user" => {
"reset_password_token" => @raw,
"password" => "12341234",
"password_confirmation" => "12341234asdasda"
}

expect(response.status).to eq 302
@user.reload
expect(@user.valid_password?("12341234")).to be false
end
end
47 changes: 47 additions & 0 deletions spec/features/forgotten_password_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
require "rails_helper"

feature "Forgotten password support" do
let!(:user) { create(:admin) }

before :each do
APP_CONFIG["email"] = {
"from" => "[email protected]",
"name" => "Portus",
"reply_to" => "[email protected]"
}
end

scenario "gives the user a link to reset their password", js: true do
visit new_user_session_path
expect(page).to_not have_content("Did you forget your password?")

fill_in "Username", with: "random"
fill_in "Password", with: "12341234"
click_button "Login"

expect(current_path).to eq new_user_session_path
expect(page).to have_content("Did you forget your password?")
click_link("Did you forget your password?")
expect(current_path).to eq new_user_password_path
end

scenario "sends the reset email when appropiate", js: true do
visit new_user_password_path

fill_in "Email", with: "[email protected]"
click_button "Reset password"
expect(current_path).to eq new_user_password_path
expect(page).to have_content("Email not found")

fill_in "Email", with: user.email
click_button "Reset password"
expect(current_path).to eq new_user_session_path
expect(page).to have_content("You will receive an email with instructions on " \
"how to reset your password in a few minutes.")

# The email has been sent.
mail = ActionMailer::Base.deliveries.first
ActionMailer::Base.deliveries.clear
expect(mail.to).to match_array [user.email]
end
end