Add account activation

parent c44a8299
Pipeline #1293 canceled with stages
in 0 seconds
// Place all the styles related to the AccountActivations controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: https://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
log_in user
flash[:success] = "Account activated!"
redirect_to user
else
flash[:danger] = "Invalid activation link"
redirect_to root_url
end
end
end
end
......@@ -7,9 +7,16 @@ class SessionsController < ApplicationController
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
redirect_back_or user
if user.activated?
log_in user
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
redirect_back_or user
else
message = "Account not activated. "
message += "Check your email for the activation link."
flash[:warning] = message
redirect_to root_url
end
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
......
......@@ -10,7 +10,7 @@ class UsersController < ApplicationController
end
def index
@users = User.paginate(page: params[:page])
@users = User.where(activated: FILL_IN).paginate(page: params[:page])
end
def new
......@@ -20,10 +20,9 @@ class UsersController < ApplicationController
def create
@user = User.new(user_params)
if @user.save
helpers.log_in @user
flash[:success] = "Welcome to the Sample App!"
redirect_to @user
# Handle a successful save.
@user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
......
module AccountActivationsHelper
end
......@@ -16,7 +16,7 @@ module SessionsHelper
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.encrypted[:user_id])
user = User.find_by(id: user_id)
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: "noreply@example.com"
layout 'mailer'
end
class UserMailer < ApplicationMailer
def account_activation(user)
@user = user
mail to: user.email, subject: "Account activation"
end
def password_reset
@greeting = "Hi"
mail to: "to@example.org"
end
end
class User < ApplicationRecord
attr_accessor :remember_token
before_save { email.downcase! }
attr_accessor :remember_token, :activation_token
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
......@@ -35,4 +36,33 @@ class User < ApplicationRecord
def forget
update_attribute(:remember_digest, nil)
end
def activate
update_attribute(:activated,true)
update_attribute(: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
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
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(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) %>
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
<% provide(:title, "Edit user") %>
<% provide(:button_text, 'Save changes') %>
<h1>Update your profile</h1>
<div class="row">
<div class="col-md-6 offset-md-3">
......
<% provide(:title, 'Sign up') %>
<% provide(:button_text, 'Create my account') %>
<h1>Sign up</h1>
<div class="row">
<div class="col-md-6 offset-md-3">
......
37N0sS6jy8/JjO3ebTAoHVt6QZUs7RhpdvR7NZf6nYo5fvQO2y2NUdmEkIWChqrHB5/03BEiiynzJ4ZclZylJ7nTqS5u7DtE0G/WgJ0K6Trg7Gp8p3z3ZsGMRU/EOsiF22mp/bfxy8y0VocGOL206SRq8BaUF51mLE2UPOYqjCDv9Asrk6CuWh1Iw7APNREsfFSVcYklby7o0AeKoUJJqNL3dmPMWL8JuOoUOa/2qQnUfXv1ZED1Ol3EXwUzNc32D3C5PEcmYfK++XyyVqN0n70Lo3sT3IodrmKXNFzaEAfq9zOxOXYFG3hyfDoyDZxDKMPVpUle3mQlK7R7WDsbUcSN9pgYRoXYUfk7db5BlIrV8KLVqGHLUwfxeQhprDXSZrgelrc2dtJ/bJqBUBlH2r0JigehSDqbSTtY--8FQDH687Y+BsXypY--xr053EAVr+fU95Ndl6AIJg==
\ No newline at end of file
dlp3T/Sj+8HZXcyoctIhKETPLdmkQVwQ75rBdh/fQjURBS2FnaZ6prCTRywcCpa5ZZpjbsK5qCglfQ8Ez91MdKbqVgQZ3NEUNQN7Cw9l1175a7HQj9a+fArClLYWp3aFasb4u1Hb9mxjhkA053S57KiPLxx0l1zaZh1Lc+Q+hKqynWarI0S4K0EzUdCovxme1V3KMZsaa3mSrO3uYZVtbT9SVsp7ChzEHwtqxBdrMXzwZkFJqiDk6aFsU5vKh01OlZc52zpldizDsFSRjXhYnCHSRghbVqxJ999/8S2yQl/6dmRpkVOXzP7EFp8MhJByy4sJSWFGKemBZN0fg0Jftf4TkI1bdWF4PT5KQQ99KOqkxsq5MjSANQYCVZ0lQBK9CX4mpmvzDPQcMzHUMPkhhI5x1XmReG9dnlRxOatLvjPjs1N8div+2TJL8yrPbq45QwqabkbE8pSaVwlwGgBoet2fJswEJLu4t8PnRG3N3UNbkR0jCrnXrTW4LPLhaoX4Owvh2jk0EAsmwqms--7eYd2XpBQbpIFqSL--AwlbbYMZ0OxMywB6gbZdDw==
\ No newline at end of file
......@@ -34,7 +34,16 @@ Rails.application.configure do
config.active_storage.service = :local
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
config.action_mailer.raise_delivery_errors = true
# Action Mailer
config.action_mailer.default_url_options = { host: 'localhost:3000' } # for absolute urls in email
config.action_mailer.asset_host = "http://localhost:3000" # for image URLs in HTML email
# Allow generating absolute urls with routing url helpers.
Rails.application.routes.default_url_options[:host] = 'localhost:3000'
# Use this if developing on localhost.
# config.action_mailer.default_url_options = { host: host, protocol: 'http' }
config.action_mailer.perform_caching = false
......
......@@ -6,6 +6,20 @@ Rails.application.configure do
# Code is not reloaded between requests.
config.cache_classes = true
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
config.action_mailer.default_url_options = { host: "fathomless-garden-95089.herokuapp.com" , protocol: "https"}
config.action_mailer.smtp_settings = {
address: 'smtp.gmail.com',
port: 587,
domain: 'fathomless-garden-95089.herokuapp.com',
user_name: Rails.application.credentials.dig(:google_smtp, :email),
password: Rails.application.credentials.dig(:google_smtp, :password),
authentication: 'plain',
enable_starttls_auto: true }
# Eager load code on boot. This eager loads most of Rails and
# your application in memory, allowing both threaded web servers
# and those relying on copy on write to perform better.
......
......@@ -43,6 +43,13 @@ Rails.application.configure do
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
# Action Mailer
config.action_mailer.default_url_options = { host: 'localhost:3000' } # for absolute urls in email
config.action_mailer.asset_host = "http://localhost:3000" # for image URLs in HTML email
# Allow generating absolute urls with routing url helpers.
Rails.application.routes.default_url_options[:host] = 'localhost:3000'
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
......
......@@ -10,6 +10,7 @@ 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[6.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: 2021_06_22_035322) do
ActiveRecord::Schema.define(version: 2021_06_23_062616) do
create_table "users", force: :cascade do |t|
t.string "name"
......@@ -20,6 +20,9 @@ ActiveRecord::Schema.define(version: 2021_06_22_035322) 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
......
......@@ -3,7 +3,9 @@ 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)
# Generate a bunch of additional users.
99.times do |n|
name = Faker::Name.name
......@@ -12,5 +14,7 @@ User.create!(name: "Example User",
User.create!(name: name,
email: email,
password: password,
password_confirmation: password)
password_confirmation: password,
activated: true,
activated_at: Time.zone.now)
end
\ No newline at end of file
require "test_helper"
class AccountActivationsControllerTest < ActionDispatch::IntegrationTest
end
......@@ -3,8 +3,12 @@ michael:
name: Michael Example
email: michael@example.com
password_digest: <%= User.digest('password') %>
activated: true
activated_at: <%= Time.zone.now %>
archer:
name: Sterling Archer
email: duchess@example.gov
password_digest: <%= User.digest('password') %>
\ No newline at end of file
password_digest: <%= User.digest('password') %>
activated: true
activated_at: <%= Time.zone.now %>
\ 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_no_difference 'User.count' do
post users_path, params: { user: { name: "",
email: "user@invalid", p
assword: "foo",
password_confirmation: "bar" } }
end
assert_template 'users/new'
assert_select 'div#error_explanation'
assert_select 'div.field_with_errors'
end
test "valid signup information with account activation" do
get signup_path
assert_difference 'User.count', 1 do
ppost users_path, params: { user: { name: "Example User",
email: "user@example.com",
password:"password",
password_confirmation: "password" } }
end
follow_redirect!
assert_template 'users/show'
assert is_logged_in?
# assert_template 'users/show'
# assert is_logged_in?
end
end
\ No newline at end of file
# 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
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
user = users(:michael)
user.activation_token = User.new_token
mail = UserMailer.account_activation(user)
assert_equal "Account activation", mail.subject
assert_equal [user.email], mail.to
assert_equal ["noreply@example.com"], mail.from
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
end
......@@ -18,6 +18,6 @@ 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?(:remember, '')
end
end
\ No newline at end of file
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