Commit ab270f88 authored by Bob Van Landuyt's avatar Bob Van Landuyt

Merge branch 'fix/use-policies-framework-on-full-private-access-user' into 'master'

fix: use policies framework for full_private_access method on user

Closes #33456

See merge request gitlab-org/gitlab!18530
parents 9ead94ef 5148c8c4
...@@ -1454,7 +1454,7 @@ class User < ApplicationRecord ...@@ -1454,7 +1454,7 @@ class User < ApplicationRecord
# Does the user have access to all private groups & projects? # Does the user have access to all private groups & projects?
# Overridden in EE to also check auditor? # Overridden in EE to also check auditor?
def full_private_access? def full_private_access?
admin? can?(:read_all_resources)
end end
def update_two_factor_requirement def update_two_factor_requirement
......
...@@ -21,10 +21,6 @@ class BasePolicy < DeclarativePolicy::Base ...@@ -21,10 +21,6 @@ class BasePolicy < DeclarativePolicy::Base
with_options scope: :user, score: 0 with_options scope: :user, score: 0
condition(:deactivated) { @user&.deactivated? } condition(:deactivated) { @user&.deactivated? }
desc "User has access to all private groups & projects"
with_options scope: :user, score: 0
condition(:full_private_access) { @user&.full_private_access? }
with_options scope: :user, score: 0 with_options scope: :user, score: 0
condition(:external_user) { @user.nil? || @user.external? } condition(:external_user) { @user.nil? || @user.external? }
...@@ -40,10 +36,12 @@ class BasePolicy < DeclarativePolicy::Base ...@@ -40,10 +36,12 @@ class BasePolicy < DeclarativePolicy::Base
::Gitlab::ExternalAuthorization.perform_check? ::Gitlab::ExternalAuthorization.perform_check?
end end
rule { external_authorization_enabled & ~full_private_access }.policy do rule { external_authorization_enabled & ~can?(:read_all_resources) }.policy do
prevent :read_cross_project prevent :read_cross_project
end end
rule { admin }.enable :read_all_resources
rule { default }.enable :read_cross_project rule { default }.enable :read_cross_project
end end
......
...@@ -30,5 +30,5 @@ class PersonalSnippetPolicy < BasePolicy ...@@ -30,5 +30,5 @@ class PersonalSnippetPolicy < BasePolicy
rule { can?(:create_note) }.enable :award_emoji rule { can?(:create_note) }.enable :award_emoji
rule { full_private_access }.enable :read_personal_snippet rule { can?(:read_all_resources) }.enable :read_personal_snippet
end end
...@@ -28,7 +28,7 @@ class ProjectSnippetPolicy < BasePolicy ...@@ -28,7 +28,7 @@ class ProjectSnippetPolicy < BasePolicy
all?(private_snippet | (internal_snippet & external_user), all?(private_snippet | (internal_snippet & external_user),
~project.guest, ~project.guest,
~is_author, ~is_author,
~full_private_access) ~can?(:read_all_resources))
end.prevent :read_project_snippet end.prevent :read_project_snippet
rule { internal_snippet & ~is_author & ~admin }.policy do rule { internal_snippet & ~is_author & ~admin }.policy do
......
---
title: Hide projects without access to admin user when admin mode is disabled
merge_request: 18530
author: Diego Louzán
type: changed
...@@ -245,47 +245,29 @@ end ...@@ -245,47 +245,29 @@ end
#### Use self-descriptive wrapper methods #### Use self-descriptive wrapper methods
When it's not possible/logical to modify the implementation of a When it's not possible/logical to modify the implementation of a method, then
method. Wrap it in a self-descriptive method and use that method. wrap it in a self-descriptive method and use that method.
For example, in CE only an `admin` is allowed to access all private For example, in GitLab-FOSS, the only user created by the system is `User.ghost`
projects/groups, but in EE also an `auditor` has full private but in EE there are several types of bot-users that aren't really users. It would
access. It would be incorrect to override the implementation of be incorrect to override the implementation of `User#ghost?`, so instead we add
`User#admin?`, so instead add a method `full_private_access?` to a method `#internal?` to `app/models/user.rb`. The implementation will be:
`app/models/users.rb`. The implementation in CE will be:
```ruby ```ruby
def full_private_access? def internal?
admin? ghost?
end end
``` ```
In EE, the implementation `ee/app/models/ee/users.rb` would be: In EE, the implementation `ee/app/models/ee/users.rb` would be:
```ruby ```ruby
override :full_private_access? override :internal?
def full_private_access? def internal?
super || auditor? super || bot?
end end
``` ```
In `lib/gitlab/visibility_level.rb` this method is used to return the
allowed visibility levels:
```ruby
def levels_for_user(user = nil)
if user.full_private_access?
[PRIVATE, INTERNAL, PUBLIC]
elsif # ...
end
```
See [CE MR][ce-mr-full-private] and [EE MR][ee-mr-full-private] for
full implementation details.
[ce-mr-full-private]: https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/12373
[ee-mr-full-private]: https://gitlab.com/gitlab-org/gitlab/merge_requests/2199
### Code in `config/routes` ### Code in `config/routes`
When we add `draw :admin` in `config/routes.rb`, the application will try to When we add `draw :admin` in `config/routes.rb`, the application will try to
......
...@@ -183,11 +183,6 @@ module EE ...@@ -183,11 +183,6 @@ module EE
self.auditor = (new_level == 'auditor') self.auditor = (new_level == 'auditor')
end end
# Does the user have access to all private groups & projects?
def full_private_access?
super || auditor?
end
def email_opted_in_source def email_opted_in_source
email_opted_in_source_id == EMAIL_OPT_IN_SOURCE_ID_GITLAB_COM ? 'GitLab.com' : '' email_opted_in_source_id == EMAIL_OPT_IN_SOURCE_ID_GITLAB_COM ? 'GitLab.com' : ''
end end
...@@ -302,6 +297,7 @@ module EE ...@@ -302,6 +297,7 @@ module EE
super super
end end
override :internal?
def internal? def internal?
super || bot? super || bot?
end end
......
...@@ -19,6 +19,8 @@ module EE ...@@ -19,6 +19,8 @@ module EE
with_scope :global with_scope :global
condition(:license_block) { License.block_changes? } condition(:license_block) { License.block_changes? }
rule { auditor }.enable :read_all_resources
end end
end end
end end
...@@ -52,15 +52,30 @@ describe Gitlab::Elastic::SnippetSearchResults, :elastic, :sidekiq_might_not_nee ...@@ -52,15 +52,30 @@ describe Gitlab::Elastic::SnippetSearchResults, :elastic, :sidekiq_might_not_nee
end end
end end
context 'when user has full_private_access' do context 'when user has full_private_access', :do_not_mock_admin_mode do
include_context 'custom session'
let(:user) { create(:admin) } let(:user) { create(:admin) }
let(:results) { described_class.new(user, 'foo', :any) } let(:results) { described_class.new(user, 'foo', :any) }
context 'admin mode disabled' do
it 'returns nothing' do
expect(results.snippet_titles_count).to eq(0)
expect(results.snippet_blobs_count).to eq(0)
end
end
context 'admin mode enabled' do
before do
Gitlab::Auth::CurrentUserMode.new(user).enable_admin_mode!(password: user.password)
end
it 'returns matched snippets' do it 'returns matched snippets' do
expect(results.snippet_titles_count).to eq(1) expect(results.snippet_titles_count).to eq(1)
expect(results.snippet_blobs_count).to eq(1) expect(results.snippet_blobs_count).to eq(1)
end end
end end
end
context 'when content is too long' do context 'when content is too long' do
let(:content) { "abc" + (" " * Elastic::Latest::SnippetInstanceProxy::MAX_INDEX_SIZE) + "xyz" } let(:content) { "abc" + (" " * Elastic::Latest::SnippetInstanceProxy::MAX_INDEX_SIZE) + "xyz" }
......
...@@ -144,8 +144,8 @@ describe Issue do ...@@ -144,8 +144,8 @@ describe Issue do
describe 'when a user cannot read cross project' do describe 'when a user cannot read cross project' do
it 'only returns issues within the same project' do it 'only returns issues within the same project' do
expect(Ability).to receive(:allowed?).with(user, :read_cross_project) expect(Ability).to receive(:allowed?).with(user, :read_all_resources, :global).and_call_original
.and_return(false) expect(Ability).to receive(:allowed?).with(user, :read_cross_project).and_return(false)
expect(authorized_issue_a.related_issues(user)) expect(authorized_issue_a.related_issues(user))
.to contain_exactly(authorized_issue_b) .to contain_exactly(authorized_issue_b)
......
...@@ -2,9 +2,13 @@ ...@@ -2,9 +2,13 @@
require 'spec_helper' require 'spec_helper'
describe BasePolicy do describe BasePolicy, :do_not_mock_admin_mode do
include ExternalAuthorizationServiceHelpers include ExternalAuthorizationServiceHelpers
let(:auditor) { build(:auditor) }
subject { described_class.new(auditor, nil) }
describe 'read cross project' do describe 'read cross project' do
context 'when an external authorization service is enabled' do context 'when an external authorization service is enabled' do
before do before do
...@@ -12,8 +16,14 @@ describe BasePolicy do ...@@ -12,8 +16,14 @@ describe BasePolicy do
end end
it 'allows auditors' do it 'allows auditors' do
expect(described_class.new(build(:auditor), nil)).to be_allowed(:read_cross_project) is_expected.to be_allowed(:read_cross_project)
end
end end
end end
describe 'read all resources' do
it 'allows auditors' do
is_expected.to be_allowed(:read_all_resources)
end
end end
end end
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
require 'spec_helper' require 'spec_helper'
describe ProjectsFinder do describe ProjectsFinder, :do_not_mock_admin_mode do
include AdminModeHelper
describe '#execute' do describe '#execute' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:group) { create(:group, :public) } let(:group) { create(:group, :public) }
...@@ -188,5 +190,21 @@ describe ProjectsFinder do ...@@ -188,5 +190,21 @@ describe ProjectsFinder do
it { is_expected.to eq([internal_project, public_project]) } it { is_expected.to eq([internal_project, public_project]) }
end end
describe 'with admin user' do
let(:user) { create(:admin) }
context 'admin mode enabled' do
before do
enable_admin_mode!(current_user)
end
it { is_expected.to match_array([public_project, internal_project, private_project, shared_project]) }
end
context 'admin mode disabled' do
it { is_expected.to match_array([public_project, internal_project]) }
end
end
end end
end end
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
describe User do describe User, :do_not_mock_admin_mode do
include ProjectForksHelper include ProjectForksHelper
include TermsHelper include TermsHelper
...@@ -2797,12 +2797,28 @@ describe User do ...@@ -2797,12 +2797,28 @@ describe User do
expect(user.full_private_access?).to be_falsy expect(user.full_private_access?).to be_falsy
end end
it 'returns true for admin user' do context 'for admin user' do
user = build(:user, :admin) include_context 'custom session'
let(:user) { build(:user, :admin) }
context 'when admin mode is disabled' do
it 'returns false' do
expect(user.full_private_access?).to be_falsy
end
end
context 'when admin mode is enabled' do
before do
Gitlab::Auth::CurrentUserMode.new(user).enable_admin_mode!(password: user.password)
end
it 'returns true' do
expect(user.full_private_access?).to be_truthy expect(user.full_private_access?).to be_truthy
end end
end end
end
end
describe '.ghost' do describe '.ghost' do
it "creates a ghost user if one isn't already present" do it "creates a ghost user if one isn't already present" do
......
...@@ -2,8 +2,9 @@ ...@@ -2,8 +2,9 @@
require 'spec_helper' require 'spec_helper'
describe BasePolicy do describe BasePolicy, :do_not_mock_admin_mode do
include ExternalAuthorizationServiceHelpers include ExternalAuthorizationServiceHelpers
include AdminModeHelper
describe '.class_for' do describe '.class_for' do
it 'detects policy class based on the subject ancestors' do it 'detects policy class based on the subject ancestors' do
...@@ -36,8 +37,42 @@ describe BasePolicy do ...@@ -36,8 +37,42 @@ describe BasePolicy do
it { is_expected.not_to be_allowed(:read_cross_project) } it { is_expected.not_to be_allowed(:read_cross_project) }
it 'allows admins' do context 'for admins' do
expect(described_class.new(build(:admin), nil)).to be_allowed(:read_cross_project) let(:current_user) { build(:admin) }
subject { described_class.new(current_user, nil) }
it 'allowed when in admin mode' do
enable_admin_mode!(current_user)
is_expected.to be_allowed(:read_cross_project)
end
it 'prevented when not in admin mode' do
is_expected.not_to be_allowed(:read_cross_project)
end
end
end
end
describe 'full private access' do
let(:current_user) { create(:user) }
subject { described_class.new(current_user, nil) }
it { is_expected.not_to be_allowed(:read_all_resources) }
context 'for admins' do
let(:current_user) { build(:admin) }
it 'allowed when in admin mode' do
enable_admin_mode!(current_user)
is_expected.to be_allowed(:read_all_resources)
end
it 'prevented when not in admin mode' do
is_expected.not_to be_allowed(:read_all_resources)
end 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