Commit 4bd8c71f authored by Robert Speicher's avatar Robert Speicher

Merge branch 'fix-login-policy' into 'master'

Prevent login for users that don't have the required permission

See merge request gitlab-org/gitlab!28458
parents dc59334b 789f19b5
...@@ -58,6 +58,8 @@ class User < ApplicationRecord ...@@ -58,6 +58,8 @@ class User < ApplicationRecord
BLOCKED_MESSAGE = "Your account has been blocked. Please contact your GitLab " \ BLOCKED_MESSAGE = "Your account has been blocked. Please contact your GitLab " \
"administrator if you think this is an error." "administrator if you think this is an error."
LOGIN_FORBIDDEN = "Your account does not have the required permission to login. Please contact your GitLab " \
"administrator if you think this is an error."
MINIMUM_INACTIVE_DAYS = 180 MINIMUM_INACTIVE_DAYS = 180
...@@ -299,14 +301,6 @@ class User < ApplicationRecord ...@@ -299,14 +301,6 @@ class User < ApplicationRecord
def blocked? def blocked?
true true
end end
def active_for_authentication?
false
end
def inactive_message
BLOCKED_MESSAGE
end
end end
before_transition do before_transition do
...@@ -354,6 +348,20 @@ class User < ApplicationRecord ...@@ -354,6 +348,20 @@ class User < ApplicationRecord
.expiring_and_not_notified(at).select(1)) .expiring_and_not_notified(at).select(1))
end end
def active_for_authentication?
super && can?(:log_in)
end
def inactive_message
if blocked?
BLOCKED_MESSAGE
elsif internal?
LOGIN_FORBIDDEN
else
super
end
end
def self.with_visible_profile(user) def self.with_visible_profile(user)
return with_public_profile if user.nil? return with_public_profile if user.nil?
...@@ -1701,6 +1709,10 @@ class User < ApplicationRecord ...@@ -1701,6 +1709,10 @@ class User < ApplicationRecord
members.non_request.maximum(:access_level) members.non_request.maximum(:access_level)
end end
def confirmation_required_on_sign_in?
!confirmed? && !confirmation_period_valid?
end
protected protected
# override, from Devise::Validatable # override, from Devise::Validatable
......
...@@ -25,8 +25,7 @@ class BasePolicy < DeclarativePolicy::Base ...@@ -25,8 +25,7 @@ class BasePolicy < DeclarativePolicy::Base
with_options scope: :user, score: 0 with_options scope: :user, score: 0
condition(:inactive) do condition(:inactive) do
Feature.enabled?(:inactive_policy_condition, default_enabled: true) && Feature.enabled?(:inactive_policy_condition, default_enabled: true) &&
@user && @user&.confirmation_required_on_sign_in? || @user&.access_locked?
!@user&.active_for_authentication?
end end
with_options scope: :user, score: 0 with_options scope: :user, score: 0
......
...@@ -39,6 +39,7 @@ describe Groups::Analytics::ProductivityAnalyticsController do ...@@ -39,6 +39,7 @@ describe Groups::Analytics::ProductivityAnalyticsController do
context 'when user is not authorized to view productivity analytics' do context 'when user is not authorized to view productivity analytics' do
before do before do
expect(Ability).to receive(:allowed?).with(current_user, :log_in, :global).and_call_original
expect(Ability).to receive(:allowed?).with(current_user, :read_group, group).and_return(true) expect(Ability).to receive(:allowed?).with(current_user, :read_group, group).and_return(true)
expect(Ability).to receive(:allowed?).with(current_user, :view_productivity_analytics, group).and_return(false) expect(Ability).to receive(:allowed?).with(current_user, :view_productivity_analytics, group).and_return(false)
end end
......
...@@ -102,6 +102,7 @@ describe Projects::BoardsController do ...@@ -102,6 +102,7 @@ describe Projects::BoardsController do
context 'with unauthorized user' do context 'with unauthorized user' do
before do before do
expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
allow(Ability).to receive(:allowed?).with(user, :admin_board, project).and_return(false) allow(Ability).to receive(:allowed?).with(user, :admin_board, project).and_return(false)
end end
...@@ -178,6 +179,7 @@ describe Projects::BoardsController do ...@@ -178,6 +179,7 @@ describe Projects::BoardsController do
context 'with unauthorized user' do context 'with unauthorized user' do
before do before do
expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
allow(Ability).to receive(:allowed?).with(user, :admin_board, project).and_return(false) allow(Ability).to receive(:allowed?).with(user, :admin_board, project).and_return(false)
end end
...@@ -227,6 +229,7 @@ describe Projects::BoardsController do ...@@ -227,6 +229,7 @@ describe Projects::BoardsController do
context 'with unauthorized user' do context 'with unauthorized user' do
before do before do
expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
allow(Ability).to receive(:allowed?).with(user, :admin_board, project).and_return(false) allow(Ability).to receive(:allowed?).with(user, :admin_board, project).and_return(false)
end end
......
...@@ -1005,4 +1005,28 @@ describe User do ...@@ -1005,4 +1005,28 @@ describe User do
it { is_expected.to eq [free_group_a, free_group_z] } it { is_expected.to eq [free_group_a, free_group_z] }
end end
end end
describe '#active_for_authentication?' do
subject { user.active_for_authentication? }
let(:user) { create(:user) }
context 'based on user type' do
using RSpec::Parameterized::TableSyntax
where(:user_type, :expected_result) do
'service_user' | true
'support_bot' | false
'visual_review_bot' | false
end
with_them do
before do
user.update(user_type: user_type)
end
it { is_expected.to be expected_result }
end
end
end
end end
...@@ -14,6 +14,7 @@ describe ControllerWithCrossProjectAccessCheck do ...@@ -14,6 +14,7 @@ describe ControllerWithCrossProjectAccessCheck do
context 'When reading cross project is not allowed' do context 'When reading cross project is not allowed' do
before do before do
allow(Ability).to receive(:allowed).and_call_original allow(Ability).to receive(:allowed).and_call_original
expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original
allow(Ability).to receive(:allowed?) allow(Ability).to receive(:allowed?)
.with(user, :read_cross_project, :global) .with(user, :read_cross_project, :global)
.and_return(false) .and_return(false)
......
...@@ -46,6 +46,7 @@ describe GraphqlController do ...@@ -46,6 +46,7 @@ describe GraphqlController do
# User cannot access API in a couple of cases # User cannot access API in a couple of cases
# * When user is internal(like ghost users) # * When user is internal(like ghost users)
# * When user is blocked # * When user is blocked
expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original
expect(Ability).to receive(:allowed?).with(user, :access_api, :global).and_return(false) expect(Ability).to receive(:allowed?).with(user, :access_api, :global).and_return(false)
post :execute post :execute
......
...@@ -26,6 +26,7 @@ describe Groups::BoardsController do ...@@ -26,6 +26,7 @@ describe Groups::BoardsController do
context 'with unauthorized user' do context 'with unauthorized user' do
before do before do
expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true)
allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(true)
allow(Ability).to receive(:allowed?).with(user, :read_board, group).and_return(false) allow(Ability).to receive(:allowed?).with(user, :read_board, group).and_return(false)
...@@ -70,6 +71,7 @@ describe Groups::BoardsController do ...@@ -70,6 +71,7 @@ describe Groups::BoardsController do
context 'with unauthorized user' do context 'with unauthorized user' do
before do before do
expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true)
allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(true)
allow(Ability).to receive(:allowed?).with(user, :read_board, group).and_return(false) allow(Ability).to receive(:allowed?).with(user, :read_board, group).and_return(false)
...@@ -106,6 +108,7 @@ describe Groups::BoardsController do ...@@ -106,6 +108,7 @@ describe Groups::BoardsController do
context 'with unauthorized user' do context 'with unauthorized user' do
before do before do
expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true)
allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(true)
allow(Ability).to receive(:allowed?).with(user, :read_board, group).and_return(false) allow(Ability).to receive(:allowed?).with(user, :read_board, group).and_return(false)
...@@ -144,6 +147,7 @@ describe Groups::BoardsController do ...@@ -144,6 +147,7 @@ describe Groups::BoardsController do
context 'with unauthorized user' do context 'with unauthorized user' do
before do before do
expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true)
allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(true)
allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(false) allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(false)
......
...@@ -32,6 +32,7 @@ describe Projects::BoardsController do ...@@ -32,6 +32,7 @@ describe Projects::BoardsController do
context 'with unauthorized user' do context 'with unauthorized user' do
before do before do
expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false) allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false)
end end
...@@ -75,6 +76,7 @@ describe Projects::BoardsController do ...@@ -75,6 +76,7 @@ describe Projects::BoardsController do
context 'with unauthorized user' do context 'with unauthorized user' do
before do before do
expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false) allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false)
end end
...@@ -130,6 +132,7 @@ describe Projects::BoardsController do ...@@ -130,6 +132,7 @@ describe Projects::BoardsController do
context 'with unauthorized user' do context 'with unauthorized user' do
before do before do
expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false) allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false)
end end
...@@ -167,6 +170,7 @@ describe Projects::BoardsController do ...@@ -167,6 +170,7 @@ describe Projects::BoardsController do
context 'with unauthorized user' do context 'with unauthorized user' do
before do before do
expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false) allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false)
end end
......
...@@ -4475,4 +4475,73 @@ describe User, :do_not_mock_admin_mode do ...@@ -4475,4 +4475,73 @@ describe User, :do_not_mock_admin_mode do
end end
end end
end end
describe '#active_for_authentication?' do
subject { user.active_for_authentication? }
let(:user) { create(:user) }
context 'when user is blocked' do
before do
user.block
end
it { is_expected.to be false }
end
context 'when user is a ghost user' do
before do
user.update(ghost: true)
end
it { is_expected.to be false }
end
context 'based on user type' do
using RSpec::Parameterized::TableSyntax
where(:user_type, :expected_result) do
'human' | true
'alert_bot' | false
end
with_them do
before do
user.update(user_type: user_type)
end
it { is_expected.to be expected_result }
end
end
end
describe '#inactive_message' do
subject { user.inactive_message }
let(:user) { create(:user) }
context 'when user is blocked' do
before do
user.block
end
it { is_expected.to eq User::BLOCKED_MESSAGE }
end
context 'when user is an internal user' do
before do
user.update(ghost: true)
end
it { is_expected.to be User::LOGIN_FORBIDDEN }
end
context 'when user is locked' do
before do
user.lock_access!
end
it { is_expected.to be :locked }
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