Skip to content
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
3 changes: 3 additions & 0 deletions app/assets/stylesheets/modules/shared.css
Original file line number Diff line number Diff line change
Expand Up @@ -152,5 +152,8 @@ a.page__heading {
.push--bottom {
margin-bottom: 60px; }

.push--bottom-s {
margin-bottom: 15px; }

.push--s {
margin-top: 20px; }
11 changes: 11 additions & 0 deletions app/controllers/email_confirmations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ def create
redirect_to root_path, notice: t('.promise_resend')
end

# used to resend confirmation mail for unconfirmed_email validation
def unconfirmed
if current_user.regenerate_confirmation_token && current_user.save
Mailer.delay.email_reset(current_user)
flash[:notice] = t('profiles.update.confirmation_mail_sent')
else
flash[:notice] = t('.try_again')
end
redirect_to edit_profile_path
end

private

def confirmation_params
Expand Down
12 changes: 4 additions & 8 deletions app/controllers/profiles_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,13 @@ def show
def update
@user = current_user.clone
if @user.update_attributes(params_user)
if @user.unconfirmed?
if @user.unconfirmed_email
Mailer.delay.email_reset(current_user)
sign_out
flash[:notice] = "You will receive an email within the next few " \
"minutes. It contains instructions for reconfirming " \
"your account with your new email address."
redirect_to_root
flash[:notice] = t('.confirmation_mail_sent')
else
flash[:notice] = "Your profile was updated."
redirect_to edit_profile_path
flash[:notice] = t('.updated')
end
redirect_to edit_profile_path
else
current_user.reload
render :edit
Expand Down
2 changes: 1 addition & 1 deletion app/mailers/mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class Mailer < ActionMailer::Base
def email_reset(user)
@user = User.find(user['id'])
mail from: Clearance.configuration.mailer_sender,
to: @user.email,
to: @user.unconfirmed_email,
subject: I18n.t('mailer.confirmation_subject',
default: 'Please confirm your email address with RubyGems.org')
end
Expand Down
23 changes: 20 additions & 3 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class User < ActiveRecord::Base
has_many :subscriptions
has_many :web_hooks

before_validation :unconfirm_email, if: :email_changed?, on: :update
after_validation :set_unconfirmed_email, if: :email_changed?, on: :update
before_create :generate_api_key, :regenerate_confirmation_token

validates :handle, uniqueness: true, allow_nil: true
Expand All @@ -40,6 +40,7 @@ class User < ActiveRecord::Base

validates :twitter_username, length: { within: 0..20 }, allow_nil: true
validates :password, length: { within: 10..200 }, allow_nil: true, unless: :skip_password_validation?
validate :unconfirmed_email_uniqueness

def self.authenticate(who, password)
user = find_by(email: who.downcase) || find_by(handle: who)
Expand Down Expand Up @@ -101,8 +102,8 @@ def encode_with(coder)
coder.map = payload
end

def unconfirm_email
self.email_confirmed = false
def set_unconfirmed_email
self.attributes = { unconfirmed_email: email, email: email_was }
regenerate_confirmation_token
end

Expand All @@ -123,6 +124,7 @@ def total_rubygems_count
end

def confirm_email!
update_email! if unconfirmed_email
update!(email_confirmed: true, confirmation_token: nil)
end

Expand All @@ -139,4 +141,19 @@ def regenerate_confirmation_token
def unconfirmed?
!email_confirmed
end

private

def update_email!
self.attributes = { email: unconfirmed_email, unconfirmed_email: nil }
save!(validate: false)
end

def unconfirmed_email_uniqueness
errors.add(:email, I18n.t('errors.messages.taken')) if unconfirmed_email_exists?
end

def unconfirmed_email_exists?
User.where(unconfirmed_email: email).exists?
end
end
9 changes: 9 additions & 0 deletions app/views/profiles/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@
</div>
</div>

<% if current_user.unconfirmed_email %>
<div class="push--bottom-s">
<p class='form__field__instructions'>
<%= t('.email_awaiting_confirmation', unconfirmed_email: current_user.unconfirmed_email) %>
</p>
<%= link_to "Resend confirmation", unconfirmed_email_confirmations_path, method: :patch, class: 'form__field__instructions t-link' %>
</div>
<% end %>

<div class="text_field">
<%= form.label :email, :class => 'form__label' %>
<%= form.email_field :email, :class => 'form__input' %>
Expand Down
5 changes: 5 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ en:
profiles:
edit:
title: Edit profile
email_awaiting_confirmation: Please confirm your new email address %{unconfirmed_email}
hide_email: Hide email in public profile
optional_twitter_username: Optional Twitter username. Will be displayed publicly
enter_password: Please enter your account's password
Expand All @@ -109,6 +110,8 @@ en:
reset: Reset my API key
confirm_reset: Are you sure? This cannot be undone.
update:
confirmation_mail_sent: You will receive an email within the next few minutes. It contains instructions for confirming your new email address.
updated: Your profile was updated.
request_denied: This request was denied. We could not verify your password.

rubygems:
Expand Down Expand Up @@ -254,3 +257,5 @@ en:
submit: Resend Confirmation
create:
promise_resend: We will email you confirmation link to activate your account if one exists.
unconfirmed_email:
try_again: "Something went wrong. Please try again."
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@

resource :email_confirmations, only: [:new, :create] do
get 'confirm/:token', to: 'email_confirmations#update', as: :update
patch 'unconfirmed'
end

# login path is "/session" => "session#create"
Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20161231080902_add_unconfirmed_email_to_users.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddUnconfirmedEmailToUsers < ActiveRecord::Migration
def change
add_column :users, :unconfirmed_email, :string
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20160929104437) do
ActiveRecord::Schema.define(version: 20161231080902) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -143,6 +143,7 @@
t.string "handle"
t.boolean "hide_email"
t.string "twitter_username"
t.string "unconfirmed_email"
end

add_index "users", ["email"], name: "index_users_on_email", using: :btree
Expand Down
24 changes: 24 additions & 0 deletions test/functional/profiles_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,30 @@ class ProfilesControllerTest < ActionController::TestCase
assert_equal @handle, @user.handle
end
end

context "updating email with existing email" do
setup do
create(:user, email: "cannotchange@tothis.com")
put :update, user: { email: "cannotchange@tothis.com", password: @user.password }
end

should "not set unconfirmed_email" do
assert page.has_content? "Email address has already been taken"
refute_equal "cannotchange@tothis.com", @user.unconfirmed_email
end
end

context "updating email with existing unconfirmed_email" do
setup do
create(:user, unconfirmed_email: "cannotchange@tothis.com")
put :update, user: { email: "cannotchange@tothis.com", password: @user.password }
end

should "not set unconfirmed_email" do
assert page.has_content? "Email address has already been taken"
refute_equal "cannotchange@tothis.com", @user.unconfirmed_email
end
end
end
end

Expand Down
9 changes: 5 additions & 4 deletions test/integration/profile_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def sign_in
assert page.has_link?("nick1", href: "/profiles/nick1")
end

test "changing email signs out user and asks to confirm email" do
test "changing email does not change email and asks to confirm email" do
sign_in
visit profile_path("nick1")
click_link "Edit Profile"
Expand All @@ -62,17 +62,18 @@ def sign_in
fill_in "Password", with: "password12345"
click_button "Update"

assert page.has_content? "Sign in"
assert page.has_selector? "input[value='nick@example.com']"
assert page.has_selector? '#flash_notice', text: "You will receive "\
"an email within the next few minutes. It contains instructions "\
"for reconfirming your account with your new email address."
"for confirming your new email address."

link = last_email_link
assert_not_nil link
visit link

assert page.has_content? "Sign out"
assert page.has_selector? "#flash_notice", text: "Your email address has been verified"
visit edit_profile_path
assert page.has_selector? "input[value='nick2@example.com']"
end

test "disabling email on profile" do
Expand Down
4 changes: 2 additions & 2 deletions test/unit/user_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@ def assert_resetting_email_changes(attr_name)
assert_resetting_email_changes :confirmation_token
end

should "unconfirm email" do
assert_resetting_email_changes :unconfirmed?
should "store unconfirm email" do
assert_resetting_email_changes :unconfirmed_email
end

should "reset token_expires_at" do
Expand Down