Commit 9c553715 by Quang Vinh Nguyen

Add account activation

parent 48d60581
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/
// Place all the styles related to the AccountActivations controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
class AccountActivationsController < ApplicationController
def edit
user = User.find_by(email: params[:email])
if user && !user.activated? && user.authenticated?(:activation, params[:id])
user.activate
user.update_attribute(:activated, true)
user.update_attribute(:activated_at, Time.zone.now)
log_in user
flash[:success] = 'Account activated!'
redirect_to user
else
flash[:danger] = 'Invalid activation link'
redirect_to root_url
end
end
end
......@@ -4,36 +4,28 @@ class SessionsController < ApplicationController
# debugger
end
# def create
# user = User.find_by(email: params[:session][:email].downcase)
# if user && user.authenticate(params[:session][:password])
# # Log the user in and redirect to the user's show page.
# log_in user
# # remember user # update: remember_digest attribute
# params[:session][:remember_me] == '1' ? remember(user) : forget(user)
# redirect_to user
# else
# # Create an error message.
# flash.now[:danger] = 'Invalid email/password combination'
# render 'new'
# end
# end
def create
@user = User.find_by(email: params[:session][:email].downcase)
if @user && @user.authenticate(params[:session][:password])
# Log the user in and redirect to the user's show page.
if @user.activated?
log_in @user
# remember user # update: remember_digest attribute
params[:session][:remember_me] == '1' ? remember(@user) : forget(@user)
# redirect_to @user
redirect_back_or @user
else
message = 'Account not activated.'
message += 'Check your email for the activation link.'
flash[:waring] = message
redirect_to root_url
end
else
# Create an error message.
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
log_out if logged_in?
redirect_to root_url
......
......@@ -3,9 +3,6 @@ class UsersController < ApplicationController
before_action :logged_in_user, only: %i[edit update index destroy]
before_action :correct_user, only: %i[edit update]
before_action :admin_user, only: :destroy
def show
@user = User.find(params[:id])
end
def new
@user = User.new
......@@ -14,9 +11,13 @@ class UsersController < ApplicationController
def create
@user = User.new(user_params)
if @user.save
log_in @user
flash[:success] = 'Welcome to the Sample App!'
redirect_to @user # redirect_to user_url(@user)
# log_in @user
# flash[:success] = 'Welcome to the Sample App!'
# redirect_to @user # redirect_to user_url(@user)
UserMailer.account_activation(@user).deliver_now
flash[:into] = 'Please check your email to activate your account.'
redirect_to root_url
else
render 'new'
end
......@@ -38,7 +39,13 @@ class UsersController < ApplicationController
def index
# @users = User.all
@users = User.paginate(page: params[:page])
# @users = User.paginate(page: params[:page])
@users = User.where(activated: true).paginate(page: params[:page])
end
def show
@user = User.find(params[:id])
redirect_to root_url and return unless @user.activated?
end
def destroy
......@@ -51,7 +58,7 @@ class UsersController < ApplicationController
# comment
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
params.require(:user).permit(:name, :email, :password, :password_confirmation )
end
# Before filters
......
module AccountActivationsHelper
end
......@@ -24,7 +24,8 @@ module SessionsHelper
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(cookies[:remember_token])
# if user && user.authenticated?(cookies[:remember_token])
if user && user.authenticated?(:remember, cookies[:remember_token])
log_in user
@current_user = user
end
......
class ApplicationMailer < ActionMailer::Base
default from: 'from@example.com'
# default from: 'from@example.com'
default from: 'noreply@example.com'
layout 'mailer'
end
class UserMailer < ApplicationMailer
# Subject can be set in your I18n file at config/locales/en.yml
# with the following lookup:
#
# en.user_mailer.account_activation.subject
#
# def account_activation
# @greeting = 'Hi'
# mail to: 'to@example.org'
# end
def account_activation(user)
@user = user
mail to: user.email, subject: 'Account activation'
end
# Subject can be set in your I18n file at config/locales/en.yml
# with the following lookup:
#
# en.user_mailer.password_reset.subject
#
def password_reset
@greeting = 'Hi'
mail to: 'to@example.org'
end
end
# comment
class User < ApplicationRecord
attr_accessor :remember_token # we don't wan't to save this attribute to database
before_save { email.downcase! }
attr_accessor :remember_token, :activation_token # we don't wan't to save this attribute to database
before_save :downcase_email # before_save { email.downcase! }
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
......@@ -30,13 +31,44 @@ class User < ApplicationRecord
end
# Returns true if the given token matches the digest.
def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
# def authenticated?(remember_token)
# return false if remember_digest.nil?
# BCrypt::Password.new(remember_digest).is_password?(remember_token)
# end
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
# Forgets a user.
def forget
update_attribute(:remember_digest, nil)
end
# Activates an account.
def activate
# update_attribute(:activated, true)
# update_attribute(:activated_at, Time.zone.now)
update_columns(activated: true, activated_at: Time.zone.now)
end
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
private
# converts email to all lower-case.
def downcase_email
# self.email = email.downcase
email.downcase!
end
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
<h1>Sample App</h1>
<p>Hi <%= @user.name %>,</p>
<p>
Welcome to the Sample App! Click on the link below to activate your account:
</p>
<%= link_to "Activate", edit_account_activation_url(@user.activation_token,
email: @user.email) %>
\ No newline at end of file
Hi <%= @user.name %>,
Welcome to the Sample App! Click on the link below to activate your account:
<%= edit_account_activation_url(@user.activation_token, email: @user.email) %>
\ No newline at end of file
<h1>User#password_reset</h1>
<p>
<%= @greeting %>, find me in app/views/user_mailer/password_reset.html.erb
</p>
User#password_reset
<%= @greeting %>, find me in app/views/user_mailer/password_reset.text.erb
......@@ -27,8 +27,12 @@ Rails.application.configure do
end
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
# config.action_mailer.raise_delivery_errors = false
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :test
host = 'localhost:3000' # 'example.com'
config.action_mailer.default_url_options = { host: host, protocol: 'https'}
# config.action_mailer.default_url_options = { host: host, protocol: ''}
config.action_mailer.perform_caching = false
# Print deprecation notices to the Rails logger.
......
......@@ -65,6 +65,27 @@ Rails.application.configure do
# Ignore bad email addresses and do not raise email delivery errors.
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
# config.action_mailer.raise_delivery_errors = false
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
host = 'https://frozen-plateau-78948.herokuapp.com/'
config.action_mailer.default_url_options = { host: host }
ActionMailer::Base.smtp_settings = {
:address => 'smtp.sendgrid.net',
:port => '587',
:authentication => :plain,
# :user_name => ENV['SENDGRID_USERNAME'],
# :password => ENV['SENDGRID_PASSWORD'],
:user_name => ENV['app97879387@heroku.com'],
:password => ENV['0h6rbntu4629'],
:domain => 'heroku.com',
:enable_starttls_auto => true
}
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation cannot be found).
......
......@@ -33,6 +33,7 @@ Rails.application.configure do
# 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: 'example.com'}
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
......
......@@ -10,4 +10,5 @@ Rails.application.routes.draw do
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
resources :users
resources :account_activations, only: [:edit]
end
class AddActivationToUsers < ActiveRecord::Migration[5.1]
def change
add_column :users, :activation_digest, :string
add_column :users, :activated, :boolean, default: false
add_column :users, :activated_at, :datetime
end
end
......@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20180601060938) do
ActiveRecord::Schema.define(version: 20180601074419) do
create_table "users", force: :cascade do |t|
t.string "name"
......@@ -20,6 +20,9 @@ ActiveRecord::Schema.define(version: 20180601060938) do
t.string "password_digest"
t.string "remember_digest"
t.boolean "admin", default: false
t.string "activation_digest"
t.boolean "activated", default: false
t.datetime "activated_at"
t.index ["email"], name: "index_users_on_email", unique: true
end
......
......@@ -2,13 +2,17 @@ User.create!(name: 'foo',
email: 'foo@bar.com',
password: 'foobar',
password_confirmation: 'foobar',
admin: true)
admin: true,
activated: true,
activated_at: Time.zone.now)
User.create!(name: 'Example User',
email: 'example@railstutorial.org',
password: 'foobar',
password_confirmation: 'foobar',
admin: true)
admin: true,
activated: true,
activated_at: Time.zone.now)
99.times do |n|
name = Faker::Name.name
......
require 'test_helper'
class AccountActivationsControllerTest < ActionDispatch::IntegrationTest
# test "the truth" do
# assert true
# end
end
......@@ -4,28 +4,40 @@ foo:
email: foo@bar.com
password_digest: <%= User.digest('password') %>
admin: true
activated: true
activated_at: <%= Time.zone.now %>
michael:
name: Michael Example
email: michael@example.com
password_digest: <%= User.digest('password') %>
admin: true
activated: true
activated_at: <%= Time.zone.now %>
archer:
name: Sterling Archer
email: duchess@example.gov
password_digest: <%= User.digest('password') %>
activated: true
activated_at: <%= Time.zone.now %>
lana:
name: Lana Kane
email: hands@example.gov
password_digest: <%= User.digest('password') %>
activated: true
activated_at: <%= Time.zone.now %>
malory:
name: Malory Archer
email: boss@example.gov
password_digest: <%= User.digest('password') %>
activated: true
activated_at: <%= Time.zone.now %>
<% 30.times do |n| %>
user_<%= n %>:
name: <%= "User #{n}" %>
email: <%= "user-#{n}@example.com" %>
password_digest: <%= User.digest('password') %>
activated: true
activated_at: <%= Time.zone.now %>
<% end %>
\ No newline at end of file
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
def setup
ActionMailer::Base.deliveries.clear
end
test 'invalid signup information' do
get signup_path
# assert_select 'form[action="/signup"]'
......@@ -16,18 +21,36 @@ class UsersSignupTest < ActionDispatch::IntegrationTest
assert_select 'div.alert'
end
test 'valid signup information' do
test 'valid signup information with account activation' do
get signup_path
assert_difference 'User.count', 1 do
post signup_path, params: { user: { name: 'Example User',
# post signup_path, params: { user: { name: 'Example User',
post users_path, params: { user: { name: 'Example User',
email: 'user@example.com',
password: 'foobar',
password_confirmation: 'foobar' } }
end
assert_equal 1, ActionMailer::Base.deliveries.size
user = assigns(:user)
assert_not user.activated?
# Try to log in before activation.
log_in_as(user)
assert_not is_logged_in?
# Invalid activation token
get edit_account_activation_path('invalid token', email: user.email)
assert_not is_logged_in?
# Valid token, wrong email
get edit_account_activation_path(user.activation_token, email: 'wrong')
assert_not is_logged_in?
# Valid activation token
get edit_account_activation_path(user.activation_token, email: user.email)
assert user.reload.activated?
follow_redirect!
assert_template 'users/show'
assert_not flash.empty?
assert_select 'div.alert'
# assert_not flash.empty?
# assert_select 'div.alert'
assert is_logged_in?
end
end
# Preview all emails at http://localhost:3000/rails/mailers/user_mailer
class UserMailerPreview < ActionMailer::Preview
# Preview this email at http://localhost:3000/rails/mailers/user_mailer/account_activation
def account_activation
# UserMailer.account_activation
user = User.first
user.activation_token = User.new_token
UserMailer.account_activation(user)
end
# Preview this email at http://localhost:3000/rails/mailers/user_mailer/password_reset
def password_reset
UserMailer.password_reset
end
end
require 'test_helper'
class UserMailerTest < ActionMailer::TestCase
# test "account_activation" do
# mail = UserMailer.account_activation
# assert_equal "Account activation", mail.subject
# assert_equal ["to@example.org"], mail.to
# assert_equal ["from@example.com"], mail.from
# assert_match "Hi", mail.body.encoded
# end
test 'account_activation' do
user = users(:foo)
user.activation_token = User.new_token
mail = UserMailer.account_activation(user)
assert_equal 'Account activation', mail.subject
# assert_equal ['to@example.org'], mail.to
assert_equal [user.email], mail.to
# assert_equal ['from@example.com'], mail.from
assert_equal ['noreply@example.com'], mail.from
# assert_match 'Hi', mail.body.encoded
assert_match user.name, mail.body.encoded
assert_match user.activation_token, mail.body.encoded
assert_match CGI.escape(user.email), mail.body.encoded
end
# test "password_reset" do
# mail = UserMailer.password_reset
# assert_equal "Password reset", mail.subject
# assert_equal ["to@example.org"], mail.to
# assert_equal ["from@example.com"], mail.from
# assert_match "Hi", mail.body.encoded
# end
end
......@@ -64,6 +64,7 @@ class UserTest < ActiveSupport::TestCase
end
test 'authenticated? should return false for a user with nil digest' do
assert_not @user.authenticated?('')
# assert_not @user.authenticated?('')
assert_not @user.authenticated?(:remember, '')
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment