SECURITY: Only allow users to resend activation email with a valid session.

* Improve error when an active user tries to request for an activation email.
This commit is contained in:
Guo Xiang Tan 2017-03-13 19:19:42 +08:00
parent dd60cb82c3
commit 7ebfa3c901
5 changed files with 51 additions and 6 deletions

View File

@ -1,4 +1,5 @@
import { ajax } from 'discourse/lib/ajax'; import { ajax } from 'discourse/lib/ajax';
import { popupAjaxError } from 'discourse/lib/ajax-error';
import ModalFunctionality from 'discourse/mixins/modal-functionality'; import ModalFunctionality from 'discourse/mixins/modal-functionality';
export default Ember.Controller.extend(ModalFunctionality, { export default Ember.Controller.extend(ModalFunctionality, {
@ -9,9 +10,13 @@ export default Ember.Controller.extend(ModalFunctionality, {
}, },
actions: { actions: {
sendActivationEmail: function() { sendActivationEmail() {
ajax('/users/action/send_activation_email', {data: {username: this.get('username')}, type: 'POST'}); ajax('/users/action/send_activation_email', {
data: { username: this.get('username') },
type: 'POST'
}).then(() => {
this.set('emailSent', true); this.set('emailSent', true);
}).catch(popupAjaxError);
} }
} }

View File

@ -6,6 +6,8 @@ class SessionController < ApplicationController
skip_before_filter :redirect_to_login_if_required skip_before_filter :redirect_to_login_if_required
skip_before_filter :preload_json, :check_xhr, only: ['sso', 'sso_login', 'become', 'sso_provider', 'destroy'] skip_before_filter :preload_json, :check_xhr, only: ['sso', 'sso_login', 'become', 'sso_provider', 'destroy']
ACTIVATE_USER_KEY = "activate_user"
def csrf def csrf
render json: {csrf: form_authenticity_token } render json: {csrf: form_authenticity_token }
end end
@ -276,6 +278,7 @@ class SessionController < ApplicationController
end end
def not_activated(user) def not_activated(user)
session[ACTIVATE_USER_KEY] = user.username
render json: { render json: {
error: I18n.t("login.not_activated"), error: I18n.t("login.not_activated"),
reason: 'not_activated', reason: 'not_activated',
@ -303,6 +306,7 @@ class SessionController < ApplicationController
end end
def login(user) def login(user)
session.delete(ACTIVATE_USER_KEY)
log_on_user(user) log_on_user(user)
if payload = session.delete(:sso_payload) if payload = session.delete(:sso_payload)

View File

@ -567,14 +567,26 @@ class UsersController < ApplicationController
RateLimiter.new(nil, "activate-min-#{request.remote_ip}", 6, 1.minute).performed! RateLimiter.new(nil, "activate-min-#{request.remote_ip}", 6, 1.minute).performed!
end end
if (current_user && !current_user.staff?) ||
(params[:username] != session[SessionController::ACTIVATE_USER_KEY])
raise Discourse::InvalidAccess
end
@user = User.find_by_username_or_email(params[:username].to_s) @user = User.find_by_username_or_email(params[:username].to_s)
raise Discourse::NotFound unless @user raise Discourse::NotFound unless @user
session.delete(SessionController::ACTIVATE_USER_KEY)
if @user.active
render_json_error(I18n.t('activation.activated'), status: 409)
elsif @user
@email_token = @user.email_tokens.unconfirmed.active.first @email_token = @user.email_tokens.unconfirmed.active.first
enqueue_activation_email if @user enqueue_activation_email
render nothing: true render nothing: true
end end
end
def enqueue_activation_email def enqueue_activation_email
@email_token ||= @user.email_tokens.create(email: @user.email) @email_token ||= @user.email_tokens.create(email: @user.email)

View File

@ -614,6 +614,7 @@ en:
welcome_to: "Welcome to %{site_name}!" welcome_to: "Welcome to %{site_name}!"
approval_required: "A moderator must manually approve your new account before you can access this forum. You'll get an email when your account is approved!" approval_required: "A moderator must manually approve your new account before you can access this forum. You'll get an email when your account is approved!"
missing_session: "We cannot detect if your account was created, please ensure you have cookies enabled." missing_session: "We cannot detect if your account was created, please ensure you have cookies enabled."
activated: "Sorry, this account has already been activated."
post_action_types: post_action_types:
off_topic: off_topic:
title: 'Off-Topic' title: 'Off-Topic'

View File

@ -1403,10 +1403,29 @@ describe UsersController do
context 'for an existing user' do context 'for an existing user' do
let(:user) { Fabricate(:user, active: false) } let(:user) { Fabricate(:user, active: false) }
context 'for an activated account' do
it 'fails' do
active_user = Fabricate(:user, active: true)
session[SessionController::ACTIVATE_USER_KEY] = active_user.username
xhr :post, :send_activation_email, username: active_user.username
expect(response.status).to eq(409)
expect(JSON.parse(response.body)['errors']).to include(I18n.t(
'activation.activated'
))
expect(session[SessionController::ACTIVATE_USER_KEY]).to eq(nil)
end
end
context 'with a valid email_token' do context 'with a valid email_token' do
it 'should send the activation email' do it 'should send the activation email' do
session["activate_user"] = user.username
Jobs.expects(:enqueue).with(:critical_user_email, has_entries(type: :signup)) Jobs.expects(:enqueue).with(:critical_user_email, has_entries(type: :signup))
xhr :post, :send_activation_email, username: user.username xhr :post, :send_activation_email, username: user.username
expect(session[SessionController::ACTIVATE_USER_KEY]).to eq(nil)
end end
end end
@ -1418,13 +1437,17 @@ describe UsersController do
it 'should generate a new token' do it 'should generate a new token' do
expect { expect {
session["activate_user"] = user.username
xhr :post, :send_activation_email, username: user.username xhr :post, :send_activation_email, username: user.username
}.to change{ user.email_tokens(true).count }.by(1) }.to change{ user.email_tokens(true).count }.by(1)
end end
it 'should send an email' do it 'should send an email' do
session["activate_user"] = user.username
Jobs.expects(:enqueue).with(:critical_user_email, has_entries(type: :signup)) Jobs.expects(:enqueue).with(:critical_user_email, has_entries(type: :signup))
xhr :post, :send_activation_email, username: user.username xhr :post, :send_activation_email, username: user.username
expect(session[SessionController::ACTIVATE_USER_KEY]).to eq(nil)
end end
end end
end end