diff --git a/Rakefile b/Rakefile index 488c551f..92772ad0 100644 --- a/Rakefile +++ b/Rakefile @@ -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 diff --git a/app/controllers/rooms_controller.rb b/app/controllers/rooms_controller.rb index 0220dae6..857819ec 100644 --- a/app/controllers/rooms_controller.rb +++ b/app/controllers/rooms_controller.rb @@ -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 diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 968f6853..f3f29e3f 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -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 diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb index fa64adfd..2b66807e 100644 --- a/app/helpers/sessions_helper.rb +++ b/app/helpers/sessions_helper.rb @@ -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 diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb new file mode 100644 index 00000000..ecab8127 --- /dev/null +++ b/app/mailers/user_mailer.rb @@ -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 . + +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 diff --git a/app/models/user.rb b/app/models/user.rb index 84f824e8..68dcb033 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -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 diff --git a/app/views/layouts/mailer.html.erb b/app/views/layouts/mailer.html.erb index 179b214d..88b46fc9 100644 --- a/app/views/layouts/mailer.html.erb +++ b/app/views/layouts/mailer.html.erb @@ -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 . %> diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb index c6f81bba..14f380cb 100644 --- a/app/views/layouts/mailer.text.erb +++ b/app/views/layouts/mailer.text.erb @@ -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 . %> diff --git a/app/views/shared/components/_confirm_button.html.erb b/app/views/shared/components/_confirm_button.html.erb new file mode 100644 index 00000000..8fdba35b --- /dev/null +++ b/app/views/shared/components/_confirm_button.html.erb @@ -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 . +%> + +
+ <%= button_to t("verify.accept"), confirm_path, params: { user_uid: params[:user_uid], email_verified: true }, class: "btn btn-primary btn-space" %> +
diff --git a/app/views/shared/components/_resend_button.html.erb b/app/views/shared/components/_resend_button.html.erb new file mode 100644 index 00000000..914c85e6 --- /dev/null +++ b/app/views/shared/components/_resend_button.html.erb @@ -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 . +%> + +
+ <%= button_to t("verify.resend"), resend_path, params: { email_verified: false }, class: "btn btn-primary btn-space" %> +
diff --git a/app/views/shared/settings/_account.html.erb b/app/views/shared/settings/_account.html.erb index d6cf39f9..4a8eb00b 100644 --- a/app/views/shared/settings/_account.html.erb +++ b/app/views/shared/settings/_account.html.erb @@ -27,7 +27,7 @@
<%= f.label t("email"), class: "form-label" %>
- <%= 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? %>
diff --git a/app/views/user_mailer/verify_email.html.erb b/app/views/user_mailer/verify_email.html.erb new file mode 100644 index 00000000..7cda6e2e --- /dev/null +++ b/app/views/user_mailer/verify_email.html.erb @@ -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 . +%> + + + + + + + +

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!

+ + diff --git a/app/views/user_mailer/verify_email.text.erb b/app/views/user_mailer/verify_email.text.erb new file mode 100644 index 00000000..9448e553 --- /dev/null +++ b/app/views/user_mailer/verify_email.text.erb @@ -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 . +%> + + +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! diff --git a/app/views/users/verify.html.erb b/app/views/users/verify.html.erb new file mode 100644 index 00000000..49815d14 --- /dev/null +++ b/app/views/users/verify.html.erb @@ -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 . +%> + +
+
+
+
+

<%= t("verify.title") %>

+
+
+

Your account has not been verified yet.

+ <% if Rails.configuration.enable_email_verification && params[:user_uid] == current_user.uid %> + <%= render "/shared/components/confirm_button" %> + <% else %> + <%= render "/shared/components/resend_button" %> + <% end %> + +
+
+
+
diff --git a/config/application.rb b/config/application.rb index 8bade271..6f8c5343 100644 --- a/config/application.rb +++ b/config/application.rb @@ -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") diff --git a/config/environments/development.rb b/config/environments/development.rb index 3027e432..672afa10 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -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 diff --git a/config/locales/en.yml b/config/locales/en.yml index 82fe17c7..67f2989c 100755 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -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 diff --git a/config/routes.rb b/config/routes.rb index 66b7d111..c8f40630 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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 diff --git a/db/migrate/20180920193451_add_email_verified_to_user.rb b/db/migrate/20180920193451_add_email_verified_to_user.rb new file mode 100644 index 00000000..a6611920 --- /dev/null +++ b/db/migrate/20180920193451_add_email_verified_to_user.rb @@ -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 . + +class AddEmailVerifiedToUser < ActiveRecord::Migration[5.0] + def change + add_column :users, :email_verified, :boolean, default: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 53f29e75..c90470ee 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -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 diff --git a/sample.env b/sample.env index 78959d35..7b47ab2c 100644 --- a/sample.env +++ b/sample.env @@ -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 diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index fd1c18b2..ad06b32e 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -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 diff --git a/spec/factories.rb b/spec/factories.rb index 7d4cd00c..e62cceb0 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -28,6 +28,7 @@ FactoryBot.define do password { password } password_confirmation { password } accepted_terms { true } + email_verified { true } end factory :room do diff --git a/test/mailers/previews/user_mailer_preview.rb b/test/mailers/previews/user_mailer_preview.rb new file mode 100644 index 00000000..18cef94f --- /dev/null +++ b/test/mailers/previews/user_mailer_preview.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +class UserMailerPreview < ActionMailer::Preview +end diff --git a/test/mailers/user_mailer_test.rb b/test/mailers/user_mailer_test.rb new file mode 100644 index 00000000..03000865 --- /dev/null +++ b/test/mailers/user_mailer_test.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +require 'test_helper' + +class UserMailerTest < ActionMailer::TestCase +end diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 00000000..e69de29b