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

Commit

Permalink
Merge pull request #325 from mssola/forgotten
Browse files Browse the repository at this point in the history
Added the "Password forgotten" option
  • Loading branch information
flavio committed Sep 11, 2015
2 parents 079bcf4 + e8a084c commit 77f2138
Show file tree
Hide file tree
Showing 15 changed files with 207 additions and 5 deletions.
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

0 comments on commit 77f2138

Please sign in to comment.