Commit 060cf8f0 authored by manojmj's avatar manojmj

Send email notification on disabling 2FA

This change sends email notification on
disabling 2FA
parent f05e1aec
......@@ -111,10 +111,14 @@ class Admin::UsersController < Admin::ApplicationController
end
def disable_two_factor
update_user { |user| user.disable_two_factor! }
result = TwoFactor::DestroyService.new(current_user, user: user).execute
if result[:status] == :success
redirect_to admin_user_path(user),
notice: _('Two-factor Authentication has been disabled for this user')
else
redirect_to admin_user_path(user), alert: result[:message]
end
end
def create
......
......@@ -73,9 +73,13 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
end
def destroy
current_user.disable_two_factor!
result = TwoFactor::DestroyService.new(current_user, user: current_user).execute
redirect_to profile_account_path, status: :found
if result[:status] == :success
redirect_to profile_account_path, status: :found, notice: s_('Two-factor authentication has been disabled successfully!')
else
redirect_to profile_account_path, status: :found, alert: result[:message]
end
end
def skip
......
......@@ -72,6 +72,16 @@ module Emails
end
end
end
def disabled_two_factor_email(user)
return unless user
@user = user
Gitlab::I18n.with_locale(@user.preferred_language) do
mail(to: @user.notification_email, subject: subject(_("Two-factor authentication disabled")))
end
end
end
end
......
......@@ -25,6 +25,7 @@ class UserPolicy < BasePolicy
rule { default }.enable :read_user_profile
rule { (private_profile | blocked_user) & ~(user_is_self | admin) }.prevent :read_user_profile
rule { user_is_self | admin }.enable :disable_two_factor
end
UserPolicy.prepend_if_ee('EE::UserPolicy')
......@@ -35,6 +35,12 @@ class NotificationService
@async ||= Async.new(self)
end
def disabled_two_factor(user)
return unless user.can?(:receive_notifications)
mailer.disabled_two_factor_email(user).deliver_later
end
# Always notify user about ssh key added
# only if ssh key is not deploy key
#
......
# frozen_string_literal: true
module TwoFactor
class BaseService
include BaseServiceUtility
attr_reader :current_user, :params, :user
def initialize(current_user, params = {})
@current_user, @params = current_user, params
@user = params.delete(:user)
end
end
end
# frozen_string_literal: true
module TwoFactor
class DestroyService < ::TwoFactor::BaseService
def execute
return error(_('You are not authorized to perform this action')) unless can?(current_user, :disable_two_factor, user)
return error(_('Two-factor authentication is not enabled for this user')) unless user.two_factor_enabled?
result = disable_two_factor
notification_service.disabled_two_factor(user) if result[:status] == :success
result
end
private
def disable_two_factor
::Users::UpdateService.new(current_user, user: user).execute do |user|
user.disable_two_factor!
end
end
end
end
%p
= _('Hi %{username}!') % { username: sanitize_name(@user.name) }
%p
= _('Two-factor authentication has been disabled for your GitLab account.')
%p
- two_factor_page_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: profile_two_factor_auth_url }
= html_escape(_('If you want to re-enable Two-factor authentication, visit the %{two_factor_page_start}Two-factor authentication settings%{two_factor_page_end} page.')) % { two_factor_page_start: two_factor_page_start, two_factor_page_end: '</a>'.html_safe }
<%= _('Hi %{username}!') % { username: sanitize_name(@user.name) } %>
<%= _('Two-factor authentication has been disabled for your GitLab account.') %>
<%= _('If you want to re-enable Two-factor authentication, visit %{two_factor_link}') % { two_factor_link: profile_two_factor_auth_url } %>
---
title: Send email notification on disabling 2FA
merge_request: 39572
author:
type: added
......@@ -144,6 +144,7 @@ Users will be notified of the following events:
| New email added | User | Security email, always sent. |
| Email changed | User | Security email, always sent. |
| Password changed | User | Security email, always sent. |
| Two-factor Authentication disabled | User | Security email, always sent. |
| New user created | User | Sent on user creation, except for OmniAuth (LDAP)|
| User added to project | User | Sent when user is added to project |
| Project access level changed | User | Sent when user project access level is changed |
......
......@@ -12698,6 +12698,12 @@ msgstr ""
msgid "If you remove this license, GitLab will fall back on the previous license, if any."
msgstr ""
msgid "If you want to re-enable Two-factor authentication, visit %{two_factor_link}"
msgstr ""
msgid "If you want to re-enable Two-factor authentication, visit the %{two_factor_page_start}Two-factor authentication settings%{two_factor_page_end} page."
msgstr ""
msgid "If your HTTP repository is not publicly accessible, add your credentials."
msgstr ""
......@@ -26004,6 +26010,18 @@ msgstr ""
msgid "Two-factor authentication"
msgstr ""
msgid "Two-factor authentication disabled"
msgstr ""
msgid "Two-factor authentication has been disabled for your GitLab account."
msgstr ""
msgid "Two-factor authentication has been disabled successfully!"
msgstr ""
msgid "Two-factor authentication is not enabled for this user"
msgstr ""
msgid "Type"
msgstr ""
......
......@@ -218,28 +218,44 @@ RSpec.describe Admin::UsersController do
end
describe 'PATCH disable_two_factor' do
subject { patch :disable_two_factor, params: { id: user.to_param } }
context 'for a user that has 2FA enabled' do
let(:user) { create(:user, :two_factor) }
it 'disables 2FA for the user' do
expect(user).to receive(:disable_two_factor!)
allow(subject).to receive(:user).and_return(user)
subject
go
expect(user.reload.two_factor_enabled?).to eq(false)
end
it 'redirects back' do
go
subject
expect(response).to redirect_to(admin_user_path(user))
end
it 'displays an alert' do
go
it 'displays a notice on success' do
subject
expect(flash[:notice])
.to eq _('Two-factor Authentication has been disabled for this user')
end
end
context 'for a user that does not have 2FA enabled' do
it 'redirects back' do
subject
expect(response).to redirect_to(admin_user_path(user))
end
def go
patch :disable_two_factor, params: { id: user.to_param }
it 'displays an alert on failure' do
subject
expect(flash[:alert])
.to eq _('Two-factor authentication is not enabled for this user')
end
end
end
......
......@@ -107,18 +107,46 @@ RSpec.describe Profiles::TwoFactorAuthsController do
end
describe 'DELETE destroy' do
subject { delete :destroy }
context 'for a user that has 2FA enabled' do
let(:user) { create(:user, :two_factor) }
it 'disables two factor' do
expect(user).to receive(:disable_two_factor!)
subject
expect(user.reload.two_factor_enabled?).to eq(false)
end
it 'redirects to profile_account_path' do
subject
expect(response).to redirect_to(profile_account_path)
end
delete :destroy
it 'displays a notice on success' do
subject
expect(flash[:notice])
.to eq _('Two-factor authentication has been disabled successfully!')
end
end
context 'for a user that does not have 2FA enabled' do
let(:user) { create(:user) }
it 'redirects to profile_account_path' do
delete :destroy
subject
expect(response).to redirect_to(profile_account_path)
end
it 'displays an alert on failure' do
subject
expect(flash[:alert])
.to eq _('Two-factor authentication is not enabled for this user')
end
end
end
end
......@@ -256,4 +256,26 @@ RSpec.describe Emails::Profile do
end
end
end
describe 'disabled two-factor authentication email' do
let_it_be(:user) { create(:user) }
subject { Notify.disabled_two_factor_email(user) }
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like 'a user cannot unsubscribe through footer link'
it 'is sent to the user' do
is_expected.to deliver_to user.email
end
it 'has the correct subject' do
is_expected.to have_subject /^Two-factor authentication disabled$/i
end
it 'includes a link to two-factor authentication settings page' do
is_expected.to have_body_text /#{profile_two_factor_auth_path}/
end
end
end
......@@ -82,4 +82,24 @@ RSpec.describe UserPolicy do
describe "updating a user" do
it_behaves_like 'changing a user', :update_user
end
describe 'disabling two-factor authentication' do
context 'disabling their own two-factor authentication' do
let(:user) { current_user }
it { is_expected.to be_allowed(:disable_two_factor) }
end
context 'disabling the two-factor authentication of another user' do
context 'when the executor is an admin', :enable_admin_mode do
let(:current_user) { create(:user, :admin) }
it { is_expected.to be_allowed(:disable_two_factor) }
end
context 'when the executor is not an admin' do
it { is_expected.not_to be_allowed(:disable_two_factor) }
end
end
end
end
......@@ -272,6 +272,16 @@ RSpec.describe NotificationService, :mailer do
end
end
describe '#disabled_two_factor' do
let_it_be(:user) { create(:user) }
subject { notification.disabled_two_factor(user) }
it 'sends email to the user' do
expect { subject }.to have_enqueued_email(user, mail: 'disabled_two_factor_email')
end
end
describe 'Notes' do
context 'issue note' do
let(:project) { create(:project, :private) }
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe TwoFactor::DestroyService do
let_it_be(:current_user) { create(:user) }
subject { described_class.new(current_user, user: user).execute }
context 'disabling two-factor authentication' do
shared_examples_for 'does not send notification email' do
context 'notification', :mailer do
it 'does not send a notification' do
perform_enqueued_jobs do
subject
end
should_not_email(user)
end
end
end
context 'when the user does not have two-factor authentication enabled' do
let(:user) { current_user }
it 'returns error' do
expect(subject).to eq(
{
status: :error,
message: 'Two-factor authentication is not enabled for this user'
}
)
end
it_behaves_like 'does not send notification email'
end
context 'when the user has two-factor authentication enabled' do
context 'when the executor is not authorized to disable two-factor authentication' do
context 'disabling the two-factor authentication of another user' do
let(:user) { create(:user, :two_factor) }
it 'returns error' do
expect(subject).to eq(
{
status: :error,
message: 'You are not authorized to perform this action'
}
)
end
it 'does not disable two-factor authentication' do
expect { subject }.not_to change { user.reload.two_factor_enabled? }.from(true)
end
it_behaves_like 'does not send notification email'
end
end
context 'when the executor is authorized to disable two-factor authentication' do
shared_examples_for 'disables two-factor authentication' do
it 'returns success' do
expect(subject).to eq({ status: :success })
end
it 'disables the two-factor authentication of the user' do
expect { subject }.to change { user.reload.two_factor_enabled? }.from(true).to(false)
end
context 'notification', :mailer do
it 'sends a notification' do
perform_enqueued_jobs do
subject
end
should_email(user)
end
end
end
context 'disabling their own two-factor authentication' do
let(:current_user) { create(:user, :two_factor) }
let(:user) { current_user }
it_behaves_like 'disables two-factor authentication'
end
context 'admin disables the two-factor authentication of another user' do
let(:current_user) { create(:admin) }
let(:user) { create(:user, :two_factor) }
it_behaves_like 'disables two-factor authentication'
end
end
end
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