Fixed #267 Email verification feature (#268)

* <Implemented basic welcome email upon signup>

* <Added config option to enable>

* <Added verification link routing>

* <Added views for verify/resend and reconfigured routes>

* <Finished Verification System minus Rspec changes>

* <Fixed code style>

* <Modified dome rspec tests>

* <Switched sample env back to default>

* <Added test cases to increase code coverage>

* <Added test_helper>

* <Minor code changes>

* <Added requested changes>

* <Added requested changes>

* <see if this fixes migration>

* <see if this fixes migration>

* <fixed migrations>

* Delete .rake_tasks~

* Update _confirm_button.html.erb

* Update _resend_button.html.erb

* Update verify_email.html.erb

* Update verify_email.text.erb

* <Fixed bugs>

* <Added validation of verification link and fixed some code style>

* <Disabled updating email for omniauth>
v2
John Ma 4 years ago committed by Jesus Federico
parent daaf305c30
commit 967c805836
  1. 7
      Rakefile
  2. 7
      app/controllers/rooms_controller.rb
  3. 44
      app/controllers/users_controller.rb
  4. 13
      app/helpers/sessions_helper.rb
  5. 27
      app/mailers/user_mailer.rb
  6. 1
      app/models/user.rb
  7. 3
      app/views/layouts/mailer.html.erb
  8. 3
      app/views/layouts/mailer.text.erb
  9. 18
      app/views/shared/components/_confirm_button.html.erb
  10. 18
      app/views/shared/components/_resend_button.html.erb
  11. 2
      app/views/shared/settings/_account.html.erb
  12. 35
      app/views/user_mailer/verify_email.html.erb
  13. 28
      app/views/user_mailer/verify_email.text.erb
  14. 33
      app/views/users/verify.html.erb
  15. 3
      config/application.rb
  16. 13
      config/environments/development.rb
  17. 4
      config/locales/en.yml
  18. 6
      config/routes.rb
  19. 23
      db/migrate/20180920193451_add_email_verified_to_user.rb
  20. 3
      db/schema.rb
  21. 19
      sample.env
  22. 57
      spec/controllers/users_controller_spec.rb
  23. 1
      spec/factories.rb
  24. 4
      test/mailers/previews/user_mailer_preview.rb
  25. 6
      test/mailers/user_mailer_test.rb
  26. 0
      test/test_helper.rb

@ -4,5 +4,12 @@
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require_relative 'config/application'
require 'rake/testtask'
Rake::TestTask.new do |t|
t.libs << "test"
t.test_files = FileList['test/test*.rb']
t.verbose = true
end
Rails.application.load_tasks

@ -18,6 +18,7 @@
class RoomsController < ApplicationController
before_action :validate_accepted_terms, unless: -> { !Rails.configuration.terms }
before_action :validate_verified_email, unless: -> { !Rails.configuration.enable_email_verification }
before_action :find_room, except: :create
before_action :verify_room_ownership, except: [:create, :show, :join, :logout]
@ -184,4 +185,10 @@ class RoomsController < ApplicationController
redirect_to terms_path unless current_user.accepted_terms
end
end
def validate_verified_email
if current_user
redirect_to resend_path unless current_user.email_verified
end
end
end

@ -28,7 +28,10 @@ class UsersController < ApplicationController
@user = User.new(user_params)
@user.provider = "greenlight"
if @user.save
if Rails.configuration.enable_email_verification && @user.save
UserMailer.verify_email(@user, verification_link(@user)).deliver
login(@user)
elsif @user.save
login(@user)
else
# Handle error on user creation.
@ -81,6 +84,9 @@ class UsersController < ApplicationController
errors.each { |k, v| @user.errors.add(k, v) }
render :edit, params: { settings: params[:settings] }
end
elsif user_params[:email] != @user.email && @user.update_attributes(user_params)
@user.update_attributes(email_verified: false)
redirect_to edit_user_path(@user), notice: I18n.t("info_update_success")
elsif @user.update_attributes(user_params)
redirect_to edit_user_path(@user), notice: I18n.t("info_update_success")
else
@ -97,18 +103,50 @@ class UsersController < ApplicationController
redirect_to root_path
end
# GET /terms
# GET | POST /terms
def terms
redirect_to '/404' unless Rails.configuration.terms
if params[:accept] == "true"
current_user.update_attributes(accepted_terms: true)
redirect_to current_user.main_room if current_user
login(current_user)
end
end
# GET | POST /u/verify/confirm
def confirm
if !current_user || current_user.uid != params[:user_uid]
redirect_to '/404'
elsif current_user.email_verified
login(current_user)
elsif params[:email_verified] == "true"
current_user.update_attributes(email_verified: true)
login(current_user)
else
render 'verify'
end
end
# GET /u/verify/resend
def resend
if !current_user
redirect_to '/404'
elsif current_user.email_verified
login(current_user)
elsif params[:email_verified] == "false"
UserMailer.verify_email(current_user, verification_link(current_user)).deliver
render 'verify'
else
render 'verify'
end
end
private
def verification_link(user)
request.base_url + confirm_path(user.uid)
end
def find_user
@user = User.find_by!(uid: params[:user_uid])
end

@ -21,14 +21,23 @@ module SessionsHelper
def login(user)
session[:user_id] = user.id
# If there are not terms, or the user has accepted them, go to their room.
# If there are not terms, or the user has accepted them, check for email verification
if !Rails.configuration.terms || user.accepted_terms
redirect_to user.main_room
check_email_verified(user)
else
redirect_to terms_path
end
end
# If email verification is disabled, or the user has verified, go to their room
def check_email_verified(user)
if !Rails.configuration.enable_email_verification || user.email_verified
redirect_to user.main_room
else
redirect_to resend_path
end
end
# Logs current user out of GreenLight.
def logout
session.delete(:user_id) if current_user

@ -0,0 +1,27 @@
# frozen_string_literal: true
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
class UserMailer < ApplicationMailer
default from: 'notifications@example.com'
def verify_email(user, url)
@user = user
@url = url
mail(to: @user.email, subject: 'Welcome to BigBlueButton!')
end
end

@ -51,6 +51,7 @@ class User < ApplicationRecord
u.username = auth_username(auth) unless u.username
u.email = auth_email(auth)
u.image = auth_image(auth)
u.email_verified = true
u.save!
end
end

@ -1,6 +1,8 @@
<%
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
@ -9,6 +11,7 @@
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>

@ -1,6 +1,8 @@
<%
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
@ -9,6 +11,7 @@
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>

@ -0,0 +1,18 @@
<%
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<div class="btn-list text-right pt-8">
<%= button_to t("verify.accept"), confirm_path, params: { user_uid: params[:user_uid], email_verified: true }, class: "btn btn-primary btn-space" %>
</div>

@ -0,0 +1,18 @@
<%
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<div class="btn-list text-right pt-8">
<%= button_to t("verify.resend"), resend_path, params: { email_verified: false }, class: "btn btn-primary btn-space" %>
</div>

@ -27,7 +27,7 @@
<div class="col-6">
<%= f.label t("email"), class: "form-label" %>
<div class="input-icon">
<%= f.text_field :email, class: "form-control #{form_is_invalid?(@user, :email)}", placeholder: t("email") %>
<%= f.text_field :email, class: "form-control #{form_is_invalid?(@user, :email)}", placeholder: t("email"), readonly: !current_user.greenlight_account? %>
</div>
</div>
</div>

@ -0,0 +1,35 @@
<%
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
</head>
<body>
<h1>Welcome to Greenlight!, <%= @user.name %></h1>
<p>
You have successfully signed up for Greenlight,
your username is: <%= @user.email %>.<br>
</p>
<p>
To verify your account, just follow this link: <%= link_to 'verify your email', @url %>.
</p>
<p>Thanks for joining and have a great day!</p>
</body>
</html>

@ -0,0 +1,28 @@
<%
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
Welcome to Greenlight, <%= @user.name %>
===============================================
You have successfully signed up for Greenlight,
your username is: <%= @user.email %>.
To verify your account, just follow this link: <%= link_to 'verify your email', @url %>.
Thanks for joining and have a great day!

@ -0,0 +1,33 @@
<%
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<div class="container pt-5">
<div class="col-md-8 offset-2">
<div class="card">
<div class="card-header">
<h3 class="card-title"><%= t("verify.title") %></h3>
</div>
<div class="card-body">
<p> Your account has not been verified yet. </p>
<% if Rails.configuration.enable_email_verification && params[:user_uid] == current_user.uid %>
<%= render "/shared/components/confirm_button" %>
<% else %>
<%= render "/shared/components/resend_button" %>
<% end %>
</form>
</div>
</div>
</div>
</div>

@ -68,6 +68,9 @@ module Greenlight
config.bigbluebutton_endpoint += "api/" unless config.bigbluebutton_endpoint.ends_with?('api/')
end
# Determine if GreenLight should enable email verification
config.enable_email_verification = (ENV['GREENLIGHT_MAIL_NOTIFICATIONS'] == "true")
# Determine if GreenLight should allow non-omniauth signup/login.
config.allow_user_signup = (ENV['ALLOW_GREENLIGHT_ACCOUNTS'] == "true")

@ -28,6 +28,19 @@ Rails.application.configure do
config.cache_store = :null_store
end
# Tell Action Mailer to use smtp server
config.action_mailer.delivery_method = :smtp
ActionMailer::Base.smtp_settings = {
address: ENV['SMTP_SERVER'],
port: ENV["SMTP_PORT"],
domain: ENV['SMTP_DOMAIN'],
user_name: ENV['SMTP_USERNAME'],
password: ENV['SMTP_PASSWORD'],
authentication: ENV['SMTP_AUTH'],
enable_starttls_auto: ENV['SMTP_STARTTLS_AUTO'],
}
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false

@ -164,3 +164,7 @@ en:
This deployment is using a pre-configured testing server, you should replace this with your own.
For details, see the %{href}.
update: Update
verify:
title: Verify your email
resend: Resend verification email
accept: Verify

@ -31,6 +31,12 @@ Rails.application.routes.draw do
# User resources.
scope '/u' do
# Verification Routes
scope '/verify' do
match '/resend', to: 'users#resend', via: [:get, :post], as: :resend
match '/confirm/:user_uid', to: 'users#confirm', via: [:get, :post], as: :confirm
end
# Handles login of greenlight provider accounts.
post '/login', to: 'sessions#create', as: :create_session

@ -0,0 +1,23 @@
# frozen_string_literal: true
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
class AddEmailVerifiedToUser < ActiveRecord::Migration[5.0]
def change
add_column :users, :email_verified, :boolean, default: false
end
end

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20180504131705) do
ActiveRecord::Schema.define(version: 20180920193451) do
create_table "rooms", force: :cascade do |t|
t.integer "user_id"
@ -42,6 +42,7 @@ ActiveRecord::Schema.define(version: 20180504131705) do
t.boolean "accepted_terms", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "email_verified", default: false
t.index ["password_digest"], name: "index_users_on_password_digest", unique: true
t.index ["room_id"], name: "index_users_on_room_id"
end

@ -61,6 +61,25 @@ LDAP_PASSWORD=
#
ALLOW_GREENLIGHT_ACCOUNTS=true
# Set this to true if you want GreenLight to send verification emails upon
# the creation of a new account
#
# SMTP variables can be taken from the list in the following table:
#
# (SMTP_SERVER= SMTP SETTINGS)
# (SMTP_DOMAIN= URL)
#
# https://serversmtp.com/smtp-server-address/
#
GREENLIGHT_MAIL_NOTIFICATIONS=true
SMTP_SERVER=smtp.gmail.com
SMTP_PORT=587
SMTP_DOMAIN=gmail.com
SMTP_USERNAME=youremail@gmail.com
SMTP_PASSWORD=yourpassword
SMTP_AUTH=plain
SMTP_STARTTLS_AUTO=true
# Prefix for the applications root URL.
# Useful for deploying the application to a subdirectory, which is highly recommended
# if deploying on a BigBlueButton server. Keep in mind that if you change this, you'll

@ -27,6 +27,7 @@ def random_valid_user_params
password: pass,
password_confirmation: pass,
accepted_terms: true,
email_verified: true,
},
}
end
@ -40,6 +41,7 @@ describe UsersController, type: :controller do
password: "pass",
password_confirmation: "invalid",
accepted_terms: false,
email_verified: false,
},
}
end
@ -56,6 +58,7 @@ describe UsersController, type: :controller do
describe "POST #create" do
context "allow greenlight accounts" do
before { allow(Rails.configuration).to receive(:allow_user_signup).and_return(true) }
before { allow(Rails.configuration).to receive(:enable_email_verification).and_return(false) }
it "redirects to user room on successful create" do
params = random_valid_user_params
@ -65,6 +68,7 @@ describe UsersController, type: :controller do
expect(u).to_not be_nil
expect(u.name).to eql(params[:user][:name])
expect(response).to redirect_to(room_path(u.main_room))
end
@ -125,4 +129,57 @@ describe UsersController, type: :controller do
expect(response).to render_template(:edit)
end
end
describe "GET | POST #resend" do
before { allow(Rails.configuration).to receive(:allow_user_signup).and_return(true) }
before { allow(Rails.configuration).to receive(:enable_email_verification).and_return(true) }
it "redirects to main room if verified" do
params = random_valid_user_params
post :create, params: params
u = User.find_by(name: params[:user][:name], email: params[:user][:email])
u.email_verified = false
get :resend
expect(response).to render_template(:verify)
end
it "resend email upon click if unverified" do
params = random_valid_user_params
post :create, params: params
u = User.find_by(name: params[:user][:name], email: params[:user][:email])
u.email_verified = false
expect { post :resend, params: { email_verified: false } }.to change { ActionMailer::Base.deliveries.count }.by(1)
expect(response).to render_template(:verify)
end
end
describe "GET | POST #confirm" do
before { allow(Rails.configuration).to receive(:allow_user_signup).and_return(true) }
before { allow(Rails.configuration).to receive(:enable_email_verification).and_return(true) }
it "redirects to main room if already verified" do
params = random_valid_user_params
post :create, params: params
u = User.find_by(name: params[:user][:name], email: params[:user][:email])
post :confirm, params: { user_uid: u.uid, email_verified: true }
expect(response).to redirect_to(room_path(u.main_room))
end
it "renders confirmation pane if unverified" do
params = random_valid_user_params
post :create, params: params
u = User.find_by(name: params[:user][:name], email: params[:user][:email])
u.email_verified = false
get :confirm, params: { user_uid: u.uid }
expect(response).to render_template(:verify)
end
end
end

@ -28,6 +28,7 @@ FactoryBot.define do
password { password }
password_confirmation { password }
accepted_terms { true }
email_verified { true }
end
factory :room do

@ -0,0 +1,4 @@
# frozen_string_literal: true
class UserMailerPreview < ActionMailer::Preview
end

@ -0,0 +1,6 @@
# frozen_string_literal: true
require 'test_helper'
class UserMailerTest < ActionMailer::TestCase
end