Commit a42803b2 authored by Markus Koller's avatar Markus Koller

Move policies for group wikis into EE

This wasn't decided yet when we started working on it, but the feature
also hasn't been released yet so there shouldn't be any implications
for this move.

This also refactors `wiki_policies_shared_examples.rb` to verify the
permissions in all situations, and fixes a bug where
`:download_wiki_code` should be available on public or internal groups,
even if the user is not logged in or a member.
parent 6424af9e
...@@ -478,16 +478,6 @@ class Group < Namespace ...@@ -478,16 +478,6 @@ class Group < Namespace
false false
end end
def wiki_access_level
# TODO: Remove this method once we implement group-level features.
# https://gitlab.com/gitlab-org/gitlab/-/issues/208412
if Feature.enabled?(:group_wiki, self)
ProjectFeature::ENABLED
else
ProjectFeature::DISABLED
end
end
private private
def update_two_factor_requirement def update_two_factor_requirement
......
# frozen_string_literal: true # frozen_string_literal: true
class GroupPolicy < BasePolicy class GroupPolicy < BasePolicy
include CrudPolicyHelpers
include FindGroupProjects include FindGroupProjects
desc "Group is public" desc "Group is public"
...@@ -43,23 +42,15 @@ class GroupPolicy < BasePolicy ...@@ -43,23 +42,15 @@ class GroupPolicy < BasePolicy
@subject.subgroup_creation_level == ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS @subject.subgroup_creation_level == ::Gitlab::Access::MAINTAINER_SUBGROUP_ACCESS
end end
desc "Group has wiki disabled"
condition(:wiki_disabled, score: 32) { !feature_available?(:wiki) }
rule { public_group }.policy do rule { public_group }.policy do
enable :read_group enable :read_group
enable :read_package enable :read_package
enable :read_wiki
end end
rule { logged_in_viewable }.policy do rule { logged_in_viewable }.enable :read_group
enable :read_group
enable :read_wiki
end
rule { guest }.policy do rule { guest }.policy do
enable :read_group enable :read_group
enable :read_wiki
enable :upload_file enable :upload_file
end end
...@@ -87,13 +78,11 @@ class GroupPolicy < BasePolicy ...@@ -87,13 +78,11 @@ class GroupPolicy < BasePolicy
enable :create_metrics_dashboard_annotation enable :create_metrics_dashboard_annotation
enable :delete_metrics_dashboard_annotation enable :delete_metrics_dashboard_annotation
enable :update_metrics_dashboard_annotation enable :update_metrics_dashboard_annotation
enable :create_wiki
end end
rule { reporter }.policy do rule { reporter }.policy do
enable :reporter_access enable :reporter_access
enable :read_container_image enable :read_container_image
enable :download_wiki_code
enable :admin_label enable :admin_label
enable :admin_list enable :admin_list
enable :admin_issue enable :admin_issue
...@@ -112,7 +101,6 @@ class GroupPolicy < BasePolicy ...@@ -112,7 +101,6 @@ class GroupPolicy < BasePolicy
enable :destroy_deploy_token enable :destroy_deploy_token
enable :read_deploy_token enable :read_deploy_token
enable :create_deploy_token enable :create_deploy_token
enable :admin_wiki
end end
rule { owner }.policy do rule { owner }.policy do
...@@ -159,11 +147,6 @@ class GroupPolicy < BasePolicy ...@@ -159,11 +147,6 @@ class GroupPolicy < BasePolicy
rule { maintainer & can?(:create_projects) }.enable :transfer_projects rule { maintainer & can?(:create_projects) }.enable :transfer_projects
rule { wiki_disabled }.policy do
prevent(*create_read_update_admin_destroy(:wiki))
prevent(:download_wiki_code)
end
def access_level def access_level
return GroupMember::NO_ACCESS if @user.nil? return GroupMember::NO_ACCESS if @user.nil?
...@@ -173,21 +156,6 @@ class GroupPolicy < BasePolicy ...@@ -173,21 +156,6 @@ class GroupPolicy < BasePolicy
def lookup_access_level! def lookup_access_level!
@subject.max_member_access_for_user(@user) @subject.max_member_access_for_user(@user)
end end
# TODO: Extract this into a helper shared with ProjectPolicy, once we implement group-level features.
# https://gitlab.com/gitlab-org/gitlab/-/issues/208412
def feature_available?(feature)
return false unless feature == :wiki
case @subject.wiki_access_level
when ProjectFeature::DISABLED
false
when ProjectFeature::PRIVATE
admin? || access_level >= ProjectFeature.required_minimum_access_level(feature)
else
true
end
end
end end
GroupPolicy.prepend_if_ee('EE::GroupPolicy') GroupPolicy.prepend_if_ee('EE::GroupPolicy')
...@@ -359,6 +359,16 @@ module EE ...@@ -359,6 +359,16 @@ module EE
end end
end end
def wiki_access_level
# TODO: Remove this method once we implement group-level features.
# https://gitlab.com/gitlab-org/gitlab/-/issues/208412
if ::Feature.enabled?(:group_wiki, self)
::ProjectFeature::ENABLED
else
::ProjectFeature::DISABLED
end
end
private private
def custom_project_templates_group_allowed def custom_project_templates_group_allowed
......
...@@ -6,6 +6,8 @@ module EE ...@@ -6,6 +6,8 @@ module EE
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
prepended do prepended do
include CrudPolicyHelpers
with_scope :subject with_scope :subject
condition(:ldap_synced) { @subject.ldap_synced? } condition(:ldap_synced) { @subject.ldap_synced? }
condition(:epics_available) { @subject.feature_available?(:epics) } condition(:epics_available) { @subject.feature_available?(:epics) }
...@@ -67,6 +69,13 @@ module EE ...@@ -67,6 +69,13 @@ module EE
License.feature_available?(:cluster_health) License.feature_available?(:cluster_health)
end end
rule { public_group | logged_in_viewable }.policy do
enable :read_wiki
enable :download_wiki_code
end
rule { guest }.enable :read_wiki
rule { reporter }.policy do rule { reporter }.policy do
enable :admin_list enable :admin_list
enable :admin_board enable :admin_board
...@@ -74,11 +83,13 @@ module EE ...@@ -74,11 +83,13 @@ module EE
enable :view_productivity_analytics enable :view_productivity_analytics
enable :view_type_of_work_charts enable :view_type_of_work_charts
enable :read_group_timelogs enable :read_group_timelogs
enable :download_wiki_code
end end
rule { maintainer }.policy do rule { maintainer }.policy do
enable :create_jira_connect_subscription enable :create_jira_connect_subscription
enable :maintainer_access enable :maintainer_access
enable :admin_wiki
end end
rule { owner }.policy do rule { owner }.policy do
...@@ -152,6 +163,7 @@ module EE ...@@ -152,6 +163,7 @@ module EE
end end
rule { developer }.policy do rule { developer }.policy do
enable :create_wiki
enable :admin_merge_request enable :admin_merge_request
end end
...@@ -181,6 +193,14 @@ module EE ...@@ -181,6 +193,14 @@ module EE
rule { ~(admin | allow_to_manage_default_branch_protection) }.policy do rule { ~(admin | allow_to_manage_default_branch_protection) }.policy do
prevent :update_default_branch_protection prevent :update_default_branch_protection
end end
desc "Group has wiki disabled"
condition(:wiki_disabled, score: 32) { !feature_available?(:wiki) }
rule { wiki_disabled }.policy do
prevent(*create_read_update_admin_destroy(:wiki))
prevent(:download_wiki_code)
end
end end
override :lookup_access_level! override :lookup_access_level!
...@@ -190,6 +210,21 @@ module EE ...@@ -190,6 +210,21 @@ module EE
super super
end end
# TODO: Extract this into a helper shared with ProjectPolicy, once we implement group-level features.
# https://gitlab.com/gitlab-org/gitlab/-/issues/208412
def feature_available?(feature)
return false unless feature == :wiki
case subject.wiki_access_level
when ::ProjectFeature::DISABLED
false
when ::ProjectFeature::PRIVATE
admin? || access_level >= ::ProjectFeature.required_minimum_access_level(feature)
else
true
end
end
def ldap_lock_bypassable? def ldap_lock_bypassable?
return false unless ::Feature.enabled?(:ldap_settings_unlock_groups_by_owners) return false unless ::Feature.enabled?(:ldap_settings_unlock_groups_by_owners)
return false unless ::Gitlab::CurrentSettings.allow_group_owners_to_manage_ldap? return false unless ::Gitlab::CurrentSettings.allow_group_owners_to_manage_ldap?
......
...@@ -837,4 +837,27 @@ describe GroupPolicy do ...@@ -837,4 +837,27 @@ describe GroupPolicy do
end end
end end
end end
it_behaves_like 'model with wiki policies' do
let_it_be(:container) { create(:group) }
let_it_be(:user) { owner }
def set_access_level(access_level)
allow(container).to receive(:wiki_access_level).and_return(access_level)
end
before do
stub_feature_flags(group_wiki: true)
end
context 'when the feature flag is disabled' do
before do
stub_feature_flags(group_wiki: false)
end
it 'does not include the wiki permissions' do
expect_disallowed(*wiki_permissions[:all])
end
end
end
end end
...@@ -655,26 +655,4 @@ describe GroupPolicy do ...@@ -655,26 +655,4 @@ describe GroupPolicy do
end end
end end
end end
it_behaves_like 'model with wiki policies' do
let(:container) { create(:group) }
def set_access_level(access_level)
allow(container).to receive(:wiki_access_level).and_return(access_level)
end
before do
stub_feature_flags(group_wiki: true)
end
context 'when the feature flag is disabled' do
before do
stub_feature_flags(group_wiki: false)
end
it 'does not include the wiki permissions' do
expect_disallowed(*permissions)
end
end
end
end end
...@@ -124,6 +124,7 @@ describe ProjectPolicy do ...@@ -124,6 +124,7 @@ describe ProjectPolicy do
it_behaves_like 'model with wiki policies' do it_behaves_like 'model with wiki policies' do
let(:container) { project } let(:container) { project }
let_it_be(:user) { owner }
def set_access_level(access_level) def set_access_level(access_level)
project.project_feature.update_attribute(:wiki_access_level, access_level) project.project_feature.update_attribute(:wiki_access_level, access_level)
......
...@@ -14,17 +14,16 @@ RSpec.shared_context 'GroupPolicy context' do ...@@ -14,17 +14,16 @@ RSpec.shared_context 'GroupPolicy context' do
%i[ %i[
read_label read_group upload_file read_namespace read_group_activity read_label read_group upload_file read_namespace read_group_activity
read_group_issues read_group_boards read_group_labels read_group_milestones read_group_issues read_group_boards read_group_labels read_group_milestones
read_group_merge_requests read_wiki read_group_merge_requests
] ]
end end
let(:read_group_permissions) { %i[read_label read_list read_milestone read_board] } let(:read_group_permissions) { %i[read_label read_list read_milestone read_board] }
let(:reporter_permissions) { %i[admin_label read_container_image read_metrics_dashboard_annotation download_wiki_code] } let(:reporter_permissions) { %i[admin_label read_container_image read_metrics_dashboard_annotation] }
let(:developer_permissions) { %i[admin_milestone create_metrics_dashboard_annotation delete_metrics_dashboard_annotation update_metrics_dashboard_annotation create_wiki] } let(:developer_permissions) { %i[admin_milestone create_metrics_dashboard_annotation delete_metrics_dashboard_annotation update_metrics_dashboard_annotation] }
let(:maintainer_permissions) do let(:maintainer_permissions) do
%i[ %i[
create_projects create_projects
read_cluster create_cluster update_cluster admin_cluster add_cluster read_cluster create_cluster update_cluster admin_cluster add_cluster
admin_wiki
] ]
end end
let(:owner_permissions) do let(:owner_permissions) do
......
# frozen_string_literal: true # frozen_string_literal: true
RSpec.shared_examples 'model with wiki policies' do RSpec.shared_examples 'model with wiki policies' do
let(:container) { raise NotImplementedError } include ProjectHelpers
let(:permissions) { %i(read_wiki create_wiki update_wiki admin_wiki download_wiki_code) }
# TODO: Remove this helper once we implement group features
# https://gitlab.com/gitlab-org/gitlab/-/issues/208412
def set_access_level(access_level)
raise NotImplementedError
end
subject { described_class.new(owner, container) }
context 'when the feature is disabled' do
before do
set_access_level(ProjectFeature::DISABLED)
end
it 'does not include the wiki permissions' do let(:container) { raise NotImplementedError }
expect_disallowed(*permissions) let(:user) { raise NotImplementedError }
end
context 'when there is an external wiki' do subject { described_class.new(user, container) }
it 'does not include the wiki permissions' do
allow(container).to receive(:has_external_wiki?).and_return(true)
expect_disallowed(*permissions) let_it_be(:wiki_permissions) do
end {}.tap do |permissions|
permissions[:guest] = %i[read_wiki]
permissions[:reporter] = permissions[:guest] + %i[download_wiki_code]
permissions[:developer] = permissions[:reporter] + %i[create_wiki]
permissions[:maintainer] = permissions[:developer] + %i[admin_wiki]
permissions[:all] = permissions[:maintainer]
end end
end end
describe 'read_wiki' do using RSpec::Parameterized::TableSyntax
subject { described_class.new(user, container) }
where(:container_level, :access_level, :membership, :access) do
member_roles = %i[guest developer] :public | :enabled | :admin | :all
stranger_roles = %i[anonymous non_member] :public | :enabled | :maintainer | :maintainer
:public | :enabled | :developer | :developer
user_roles = stranger_roles + member_roles :public | :enabled | :reporter | :reporter
:public | :enabled | :guest | :guest
:public | :enabled | :non_member | :guest
:public | :enabled | :anonymous | :guest
:public | :private | :admin | :all
:public | :private | :maintainer | :maintainer
:public | :private | :developer | :developer
:public | :private | :reporter | :reporter
:public | :private | :guest | :guest
:public | :private | :non_member | nil
:public | :private | :anonymous | nil
:public | :disabled | :admin | nil
:public | :disabled | :maintainer | nil
:public | :disabled | :developer | nil
:public | :disabled | :reporter | nil
:public | :disabled | :guest | nil
:public | :disabled | :non_member | nil
:public | :disabled | :anonymous | nil
:internal | :enabled | :admin | :all
:internal | :enabled | :maintainer | :maintainer
:internal | :enabled | :developer | :developer
:internal | :enabled | :reporter | :reporter
:internal | :enabled | :guest | :guest
:internal | :enabled | :non_member | :guest
:internal | :enabled | :anonymous | nil
:internal | :private | :admin | :all
:internal | :private | :maintainer | :maintainer
:internal | :private | :developer | :developer
:internal | :private | :reporter | :reporter
:internal | :private | :guest | :guest
:internal | :private | :non_member | nil
:internal | :private | :anonymous | nil
:internal | :disabled | :admin | nil
:internal | :disabled | :maintainer | nil
:internal | :disabled | :developer | nil
:internal | :disabled | :reporter | nil
:internal | :disabled | :guest | nil
:internal | :disabled | :non_member | nil
:internal | :disabled | :anonymous | nil
:private | :private | :admin | :all
:private | :private | :maintainer | :maintainer
:private | :private | :developer | :developer
:private | :private | :reporter | :reporter
:private | :private | :guest | :guest
:private | :private | :non_member | nil
:private | :private | :anonymous | nil
:private | :disabled | :admin | nil
:private | :disabled | :maintainer | nil
:private | :disabled | :developer | nil
:private | :disabled | :reporter | nil
:private | :disabled | :guest | nil
:private | :disabled | :non_member | nil
:private | :disabled | :anonymous | nil
end
# When a user is anonymous, their `current_user == nil` with_them do
let(:user) { create(:user) unless user_role == :anonymous } let(:user) { create_user_from_membership(container, membership) }
let(:allowed_permissions) { wiki_permissions[access].dup || [] }
let(:disallowed_permissions) { wiki_permissions[:all] - allowed_permissions }
before do before do
container.visibility = container_visibility container.visibility = container_level.to_s
set_access_level(wiki_access_level) set_access_level(ProjectFeature.access_level_from_str(access_level.to_s))
container.add_user(user, user_role) if member_roles.include?(user_role)
end
title = ->(container_visibility, wiki_access_level, user_role) do
[
"container is #{Gitlab::VisibilityLevel.level_name container_visibility}",
"wiki is #{ProjectFeature.str_from_access_level wiki_access_level}",
"user is #{user_role}"
].join(', ')
end
describe 'Situations where :read_wiki is always false' do
where(case_names: title,
container_visibility: Gitlab::VisibilityLevel.options.values,
wiki_access_level: [ProjectFeature::DISABLED],
user_role: user_roles)
with_them do
it { is_expected.to be_disallowed(:read_wiki) }
end
end
describe 'Situations where :read_wiki is always true' do
where(case_names: title,
container_visibility: [Gitlab::VisibilityLevel::PUBLIC],
wiki_access_level: [ProjectFeature::ENABLED],
user_role: user_roles)
with_them do if allowed_permissions.any? && [container_level, access_level, membership] != [:private, :private, :guest]
it { is_expected.to be_allowed(:read_wiki) } allowed_permissions << :download_wiki_code
end end
end end
describe 'Situations where :read_wiki requires membership' do it 'allows actions based on membership' do
context 'the wiki is private, and the user is a member' do expect_allowed(*allowed_permissions)
where(case_names: title, expect_disallowed(*disallowed_permissions)
container_visibility: [Gitlab::VisibilityLevel::PUBLIC,
Gitlab::VisibilityLevel::INTERNAL],
wiki_access_level: [ProjectFeature::PRIVATE],
user_role: member_roles)
with_them do
it { is_expected.to be_allowed(:read_wiki) }
end
end
context 'the wiki is private, and the user is not member' do
where(case_names: title,
container_visibility: [Gitlab::VisibilityLevel::PUBLIC,
Gitlab::VisibilityLevel::INTERNAL],
wiki_access_level: [ProjectFeature::PRIVATE],
user_role: stranger_roles)
with_them do
it { is_expected.to be_disallowed(:read_wiki) }
end
end
context 'the wiki is enabled, and the user is a member' do
where(case_names: title,
container_visibility: [Gitlab::VisibilityLevel::PRIVATE],
wiki_access_level: [ProjectFeature::ENABLED],
user_role: member_roles)
with_them do
it { is_expected.to be_allowed(:read_wiki) }
end
end
context 'the wiki is enabled, and the user is not a member' do
where(case_names: title,
container_visibility: [Gitlab::VisibilityLevel::PRIVATE],
wiki_access_level: [ProjectFeature::ENABLED],
user_role: stranger_roles)
with_them do
it { is_expected.to be_disallowed(:read_wiki) }
end
end
end end
end
describe 'Situations where :read_wiki prohibits anonymous access' do # TODO: Remove this helper once we implement group features
context 'the user is not anonymous' do # https://gitlab.com/gitlab-org/gitlab/-/issues/208412
where(case_names: title, def set_access_level(access_level)
container_visibility: [Gitlab::VisibilityLevel::INTERNAL], raise NotImplementedError
wiki_access_level: [ProjectFeature::ENABLED, ProjectFeature::PUBLIC],
user_role: user_roles.reject { |u| u == :anonymous })
with_them do
it { is_expected.to be_allowed(:read_wiki) }
end
end
context 'the user is anonymous' do
where(case_names: title,
container_visibility: [Gitlab::VisibilityLevel::INTERNAL],
wiki_access_level: [ProjectFeature::ENABLED, ProjectFeature::PUBLIC],
user_role: %i[anonymous])
with_them do
it { is_expected.to be_disallowed(:read_wiki) }
end
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