Commit c02fa277 authored by Gosia Ksionek's avatar Gosia Ksionek Committed by Imre Farkas

Add policies and first method

WIP

WIP

Add group method and specs

Add failing specs

Add specs to api call
parent f1321c8a
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
= render 'groups/settings/default_branch_protection', f: f, group: @group = render 'groups/settings/default_branch_protection', f: f, group: @group
= render 'groups/settings/project_creation_level', f: f, group: @group = render 'groups/settings/project_creation_level', f: f, group: @group
= render 'groups/settings/subgroup_creation_level', f: f, group: @group = render 'groups/settings/subgroup_creation_level', f: f, group: @group
= render_if_exists 'groups/settings/prevent_forking', f: f, group: @group
= render 'groups/settings/two_factor_auth', f: f = render 'groups/settings/two_factor_auth', f: f
= render_if_exists 'groups/personal_access_token_expiration_policy', f: f, group: @group = render_if_exists 'groups/personal_access_token_expiration_policy', f: f, group: @group
= render_if_exists 'groups/member_lock_setting', f: f, group: @group = render_if_exists 'groups/member_lock_setting', f: f, group: @group
......
---
title: Add Prevent forking outside group feature
merge_request: 36848
author:
type: added
# frozen_string_literal: true
class AddPreventForkingToNamespaceSettings < ActiveRecord::Migration[6.0]
DOWNTIME = false
def change
add_column :namespace_settings, :prevent_forking_outside_group, :boolean, null: false, default: false
end
end
dbb84d05cfe6d2ef143b9321b2b089c66d705f01ced64756032622f64f8e3eed
\ No newline at end of file
...@@ -13262,7 +13262,8 @@ CREATE TABLE public.namespace_root_storage_statistics ( ...@@ -13262,7 +13262,8 @@ CREATE TABLE public.namespace_root_storage_statistics (
CREATE TABLE public.namespace_settings ( CREATE TABLE public.namespace_settings (
created_at timestamp with time zone NOT NULL, created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL, updated_at timestamp with time zone NOT NULL,
namespace_id integer NOT NULL namespace_id integer NOT NULL,
prevent_forking_outside_group boolean DEFAULT false NOT NULL
); );
CREATE TABLE public.namespace_statistics ( CREATE TABLE public.namespace_statistics (
......
...@@ -750,6 +750,7 @@ PUT /groups/:id ...@@ -750,6 +750,7 @@ PUT /groups/:id
| `file_template_project_id` | integer | no | **(PREMIUM)** The ID of a project to load custom file templates from. | | `file_template_project_id` | integer | no | **(PREMIUM)** The ID of a project to load custom file templates from. |
| `shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Pipeline minutes quota for this group (included in plan). Can be `nil` (default; inherit system default), `0` (unlimited) or `> 0` | | `shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Pipeline minutes quota for this group (included in plan). Can be `nil` (default; inherit system default), `0` (unlimited) or `> 0` |
| `extra_shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Extra pipeline minutes quota for this group (purchased in addition to the minutes included in the plan). | | `extra_shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Extra pipeline minutes quota for this group (purchased in addition to the minutes included in the plan). |
| `prevent_forking_outside_group` | boolean | no | **(PREMIUM)** When enabled, users can **not** fork projects from this group to external namespaces
NOTE: **Note:** NOTE: **Note:**
The `projects` and `shared_projects` attributes in the response are deprecated and will be [removed in API v5](https://gitlab.com/gitlab-org/gitlab/-/issues/213797). The `projects` and `shared_projects` attributes in the response are deprecated and will be [removed in API v5](https://gitlab.com/gitlab-org/gitlab/-/issues/213797).
......
...@@ -668,6 +668,23 @@ To enable delayed deletion of projects: ...@@ -668,6 +668,23 @@ To enable delayed deletion of projects:
1. Expand the **Permissions, LFS, 2FA** section, and check **Enable delayed project removal**. 1. Expand the **Permissions, LFS, 2FA** section, and check **Enable delayed project removal**.
1. Click **Save changes**. 1. Click **Save changes**.
#### Prevent project forking outside group **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/216987) in GitLab 13.3.
By default, projects within a group can be forked.
Optionally, on [Premium or Silver](https://about.gitlab.com/pricing/) or higher tiers,
you can prevent the projects within a group from being forked outside of the current top-level group.
Previously this setting was available only for groups enforcing group managed account. This setting will be
removed from SAML setting page and migrated to group setting, but in the interim period of changes both of those settings will be taken into consideration, if even one is set to `true` then it will be assumed group does not allow forking projects outside.
To enable prevent project forking:
1. Navigate to the top-level group's **Settings > General** page.
1. Expand the **Permissions, LFS, 2FA** section, and check **Prevent project forking outside current group**.
1. Click **Save changes**.
### Advanced settings ### Advanced settings
- **Projects**: View all projects within that group, add members to each project, - **Projects**: View all projects within that group, add members to each project,
......
...@@ -4,6 +4,7 @@ module EE ...@@ -4,6 +4,7 @@ module EE
module GroupsController module GroupsController
extend ActiveSupport::Concern extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
include PreventForkingHelper
prepended do prepended do
alias_method :ee_authorize_admin_group!, :authorize_admin_group! alias_method :ee_authorize_admin_group!, :authorize_admin_group!
...@@ -71,6 +72,7 @@ module EE ...@@ -71,6 +72,7 @@ module EE
params_ee << :max_pages_size if can?(current_user, :update_max_pages_size) params_ee << :max_pages_size if can?(current_user, :update_max_pages_size)
params_ee << :max_personal_access_token_lifetime if current_group&.personal_access_token_expiration_policy_available? params_ee << :max_personal_access_token_lifetime if current_group&.personal_access_token_expiration_policy_available?
params_ee << :delayed_project_removal if current_group&.feature_available?(:adjourned_deletion_for_projects_and_groups) params_ee << :delayed_project_removal if current_group&.feature_available?(:adjourned_deletion_for_projects_and_groups)
params_ee << :prevent_forking_outside_group if can_change_prevent_forking?(current_user, current_group)
end end
end end
......
...@@ -11,14 +11,9 @@ module EE ...@@ -11,14 +11,9 @@ module EE
targets = super targets = super
root_group = project.group&.root_ancestor root_group = project.group&.root_ancestor
return targets unless root_group&.prevent_forking_outside_group?
return targets unless root_group&.saml_provider targets.where(id: root_group.self_and_descendants)
if root_group.saml_provider.prohibited_outer_forks?
targets = targets.where(id: root_group.self_and_descendants)
end
targets
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
end end
......
# frozen_string_literal: true
module PreventForkingHelper
def can_change_prevent_forking?(current_user, group)
can?(current_user, :change_prevent_group_forking, group)
end
end
...@@ -386,6 +386,15 @@ module EE ...@@ -386,6 +386,15 @@ module EE
owners.pluck(:email) owners.pluck(:email)
end end
# this method will be delegated to namespace_settings, but as we need to wait till
# all groups will have namespace_settings created via background migration,
# we need to serve it from this class
def prevent_forking_outside_group?
return namespace_settings.prevent_forking_outside_group? if namespace_settings
root_ancestor.saml_provider&.prohibited_outer_forks?
end
private private
def custom_project_templates_group_allowed def custom_project_templates_group_allowed
......
...@@ -3,5 +3,15 @@ ...@@ -3,5 +3,15 @@
module EE module EE
module NamespaceSetting module NamespaceSetting
extend ActiveSupport::Concern extend ActiveSupport::Concern
delegate :root_ancestor, to: :namespace
def prevent_forking_outside_group?
saml_setting = root_ancestor.saml_provider&.prohibited_outer_forks?
return saml_setting unless namespace.feature_available?(:group_forking_protection)
saml_setting || root_ancestor.namespace_settings&.prevent_forking_outside_group
end
end end
end end
...@@ -79,6 +79,7 @@ class License < ApplicationRecord ...@@ -79,6 +79,7 @@ class License < ApplicationRecord
github_project_service_integration github_project_service_integration
group_allowed_email_domains group_allowed_email_domains
group_coverage_reports group_coverage_reports
group_forking_protection
group_ip_restriction group_ip_restriction
group_merge_request_analytics group_merge_request_analytics
group_project_templates group_project_templates
......
...@@ -45,6 +45,10 @@ module EE ...@@ -45,6 +45,10 @@ module EE
@subject.feature_available?(:security_dashboard) @subject.feature_available?(:security_dashboard)
end end
condition(:prevent_group_forking_available) do
@subject.feature_available?(:group_forking_protection)
end
condition(:needs_new_sso_session) do condition(:needs_new_sso_session) do
sso_enforcement_prevents_access? sso_enforcement_prevents_access?
end end
...@@ -135,6 +139,10 @@ module EE ...@@ -135,6 +139,10 @@ module EE
enable :read_group_cycle_analytics, :create_group_stage, :read_group_stage, :update_group_stage, :delete_group_stage enable :read_group_cycle_analytics, :create_group_stage, :read_group_stage, :update_group_stage, :delete_group_stage
end end
rule { owner & ~has_parent & prevent_group_forking_available }.policy do
enable :change_prevent_group_forking
end
rule { can?(:read_group) & dependency_proxy_available } rule { can?(:read_group) & dependency_proxy_available }
.enable :read_dependency_proxy .enable :read_dependency_proxy
......
...@@ -93,6 +93,7 @@ module EE ...@@ -93,6 +93,7 @@ module EE
def handle_changes def handle_changes
handle_allowed_email_domains_update handle_allowed_email_domains_update
handle_ip_restriction_update handle_ip_restriction_update
handle_settings_update
end end
def handle_ip_restriction_update def handle_ip_restriction_update
...@@ -111,6 +112,13 @@ module EE ...@@ -111,6 +112,13 @@ module EE
AllowedEmailDomains::UpdateService.new(current_user, group, comma_separated_domains).execute AllowedEmailDomains::UpdateService.new(current_user, group, comma_separated_domains).execute
end end
def handle_settings_update
settings_params = params.slice(:prevent_forking_outside_group)
params.delete(:prevent_forking_outside_group)
NamespaceSettings::UpdateService.new(current_user, group, settings_params).execute
end
def log_audit_event def log_audit_event
EE::Audit::GroupChangesAuditor.new(current_user, group).execute EE::Audit::GroupChangesAuditor.new(current_user, group).execute
end end
......
# frozen_string_literal: true
module EE
# This class is responsible for updating the namespace settings of a specific group.
module NamespaceSettings
class UpdateService
include ::Gitlab::Allowable
def initialize(current_user, group, settings)
@current_user = current_user
@group = group
@settings_params = settings
end
def execute
unless valid?
group.errors.add(:prevent_forking_outside_group, s_('GroupSettings|Prevent forking setting was not saved'))
return
end
if group.namespace_settings
group.namespace_settings.attributes = settings_params
else
group.build_namespace_settings(settings_params)
end
end
private
attr_reader :current_user, :group, :settings_params
def valid?
if settings_params.key?(:prevent_forking_outside_group)
can_update_prevent_forking?
else
true
end
end
def can_update_prevent_forking?
can?(current_user, :change_prevent_group_forking, group)
end
end
end
end
%h5= _('Prevent project forking outside current group')
.form-group.gl-mb-3
.form-check
= f.check_box :prevent_forking_outside_group, checked: group.prevent_forking_outside_group?, class: 'form-check-input', disabled: !can_change_prevent_forking?(current_user, group)
= f.label :prevent_forking_outside_group, class: 'form-check-label' do
%span.gl-display-block= s_('GroupSettings|Prevent forking outside of the group')
%span.text-muted= s_('GroupSettings|This setting will prevent group members from forking projects outside of the group.')
...@@ -9,6 +9,8 @@ module EE ...@@ -9,6 +9,8 @@ module EE
prepended do prepended do
expose :shared_runners_minutes_limit expose :shared_runners_minutes_limit
expose :extra_shared_runners_minutes_limit expose :extra_shared_runners_minutes_limit
expose :prevent_forking_outside_group?,
as: :prevent_forking_outside_group
end end
end end
end end
......
...@@ -43,6 +43,9 @@ module EE ...@@ -43,6 +43,9 @@ module EE
params.delete(:file_template_project_id) unless params.delete(:file_template_project_id) unless
group.feature_available?(:custom_file_templates_for_namespace) group.feature_available?(:custom_file_templates_for_namespace)
params.delete(:prevent_forking_outside_group) unless
can?(current_user, :change_prevent_group_forking, group)
super super
end end
......
...@@ -18,6 +18,7 @@ module EE ...@@ -18,6 +18,7 @@ module EE
params :optional_update_params_ee do params :optional_update_params_ee do
optional :file_template_project_id, type: Integer, desc: 'The ID of a project to use for custom templates in this group' optional :file_template_project_id, type: Integer, desc: 'The ID of a project to use for custom templates in this group'
optional :prevent_forking_outside_group, type: ::Grape::API::Boolean, desc: 'Prevent forking projects inside this group to external namespaces'
end end
params :optional_projects_params_ee do params :optional_projects_params_ee do
......
...@@ -537,5 +537,44 @@ RSpec.describe GroupsController do ...@@ -537,5 +537,44 @@ RSpec.describe GroupsController do
end end
end end
end end
context 'when `prevent_forking_outside_group` is specified' do
using RSpec::Parameterized::TableSyntax
subject { put :update, params: params }
shared_examples_for 'updates the attribute if needed' do
it 'updates the attribute' do
subject
expect(response).to have_gitlab_http_status(:found)
expect(group.reload.prevent_forking_outside_group?).to eq(result)
end
end
context 'authenticated as group owner' do
where(:feature_enabled, :prevent_forking_outside_group, :result) do
false | false | nil
false | true | nil
true | false | false
true | true | true
end
with_them do
let(:params) do
{ id: group.to_param, group: { prevent_forking_outside_group: prevent_forking_outside_group } }
end
before do
group.add_owner(user)
sign_in(user)
stub_licensed_features(group_forking_protection: feature_enabled)
end
it_behaves_like 'updates the attribute if needed'
end
end
end
end end
end end
...@@ -19,10 +19,14 @@ RSpec.describe ForkTargetsFinder do ...@@ -19,10 +19,14 @@ RSpec.describe ForkTargetsFinder do
project_group.add_reporter(user) project_group.add_reporter(user)
outer_group.add_owner(user) outer_group.add_owner(user)
inner_subgroup.add_owner(user) inner_subgroup.add_owner(user)
stub_licensed_features(group_saml: true) stub_licensed_features(group_saml: true, group_forking_protection: true)
end end
context 'when project root group prohibits outer forks' do context 'when project root group prohibits outer forks' do
context 'when it is configured on saml level' do
before do
create(:namespace_settings, namespace: project_group, prevent_forking_outside_group: false)
end
let(:project_group) do let(:project_group) do
create(:saml_provider, :enforced_group_managed_accounts, prohibited_outer_forks: true).group create(:saml_provider, :enforced_group_managed_accounts, prohibited_outer_forks: true).group
end end
...@@ -31,7 +35,6 @@ RSpec.describe ForkTargetsFinder do ...@@ -31,7 +35,6 @@ RSpec.describe ForkTargetsFinder do
expect(fork_targets).to be_a(ActiveRecord::Relation) expect(fork_targets).to be_a(ActiveRecord::Relation)
expect(fork_targets).to match_array([inner_subgroup]) expect(fork_targets).to match_array([inner_subgroup])
end end
end
context 'when project root does not prohibit outer forks' do context 'when project root does not prohibit outer forks' do
let(:project_group) do let(:project_group) do
...@@ -44,4 +47,36 @@ RSpec.describe ForkTargetsFinder do ...@@ -44,4 +47,36 @@ RSpec.describe ForkTargetsFinder do
end end
end end
end end
context 'when it is configured on group level' do
let(:project_group) do
create(:group)
end
let(:user) { create :user }
context 'when project root prohibits outer forks' do
before do
create(:namespace_settings, namespace: project_group, prevent_forking_outside_group: true)
end
it 'returns namespaces with the same root group as project one only' do
expect(fork_targets).to be_a(ActiveRecord::Relation)
expect(fork_targets).to match_array([inner_subgroup])
end
end
context 'when project root does not prohibit outer forks' do
before do
create(:namespace_settings, namespace: project_group, prevent_forking_outside_group: false)
end
it 'returns outer namespaces as well as inner' do
expect(fork_targets).to be_a(ActiveRecord::Relation)
expect(fork_targets).to match_array([outer_group, inner_subgroup, user.namespace])
end
end
end
end
end
end end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe PreventForkingHelper do
let(:group) { create :group }
let(:owner) { group.owner }
it 'calls proper ability method' do
expect(helper).to receive(:can?).with(owner, :change_prevent_group_forking, group)
helper.can_change_prevent_forking?(owner, group)
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe NamespaceSetting do
let(:group) { create(:group) }
describe '#prevent_forking_outside_group?' do
context 'with feature available' do
before do
stub_licensed_features(group_forking_protection: true)
end
context 'group with no associated saml provider' do
let(:setting) { create(:namespace_settings, namespace: group, prevent_forking_outside_group: true) }
it 'returns namespace setting' do
expect(setting.prevent_forking_outside_group?).to eq(true)
end
end
context 'group with associated saml provider' do
before do
stub_licensed_features(group_saml: true, group_forking_protection: true)
end
context 'when it is configured to true on saml level' do
let(:setting) { create(:namespace_settings, namespace: group, prevent_forking_outside_group: true) }
before do
create(:saml_provider, :enforced_group_managed_accounts, prohibited_outer_forks: true, group: group)
end
it 'returns true' do
expect(setting.prevent_forking_outside_group?).to eq(true)
end
end
context 'when it is configured to false on saml level' do
let(:setting) { create(:namespace_settings, namespace: group, prevent_forking_outside_group: false) }
before do
create(:saml_provider, :enforced_group_managed_accounts, prohibited_outer_forks: false, group: group)
end
it 'returns false' do
expect(setting.prevent_forking_outside_group?).to eq(false)
end
context 'when setting is configured on namespace level' do
let(:setting) { create(:namespace_settings, namespace: group, prevent_forking_outside_group: true) }
it 'returns namespace setting' do
expect(setting.prevent_forking_outside_group?).to eq(true)
end
end
end
end
end
context 'without feature available' do
let(:setting) { create(:namespace_settings, namespace: group, prevent_forking_outside_group: true) }
it 'returns false' do
expect(setting.prevent_forking_outside_group?).to be_falsey
end
context 'when saml setting is available' do
before do
stub_licensed_features(group_saml: true)
end
context 'when it is configured to true on saml level' do
let(:setting) { create(:namespace_settings, namespace: group, prevent_forking_outside_group: false) }
before do
create(:saml_provider, :enforced_group_managed_accounts, prohibited_outer_forks: true, group: group)
end
it 'returns true' do
expect(setting.prevent_forking_outside_group?).to eq(true)
end
end
context 'when it is configured to false on saml level' do
let(:setting) { create(:namespace_settings, namespace: group, prevent_forking_outside_group: false) }
before do
create(:saml_provider, :enforced_group_managed_accounts, prohibited_outer_forks: false, group: group)
end
it 'returns false' do
expect(setting.prevent_forking_outside_group?).to eq(false)
end
end
end
end
end
end
...@@ -630,6 +630,46 @@ RSpec.describe GroupPolicy do ...@@ -630,6 +630,46 @@ RSpec.describe GroupPolicy do
end end
end end
describe 'change_prevent_group_forking' do
context 'when feature is disabled' do
context 'with owner' do
let(:current_user) { owner }
it { is_expected.to be_disallowed(:change_prevent_group_forking) }
end
context 'with maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:change_prevent_group_forking) }
end
end
context 'when feature is enabled' do
before do
stub_licensed_features(group_forking_protection: true)
end
context 'with owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:change_prevent_group_forking) }
context 'when group has parent' do
let(:group) { create(:group, :private, parent: create(:group)) }
it { is_expected.to be_disallowed(:change_prevent_group_forking) }
end
end
context 'with maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:change_prevent_group_forking) }
end
end
end
describe 'read_group_security_dashboard & create_vulnerability_export' do describe 'read_group_security_dashboard & create_vulnerability_export' do
let(:abilities) { %i(read_group_security_dashboard create_vulnerability_export) } let(:abilities) { %i(read_group_security_dashboard create_vulnerability_export) }
......
...@@ -247,6 +247,36 @@ RSpec.describe API::Groups do ...@@ -247,6 +247,36 @@ RSpec.describe API::Groups do
end end
end end
end end
context 'prevent_forking_outside_group' do
using RSpec::Parameterized::TableSyntax
context 'authenticated as group owner' do
where(:feature_enabled, :prevent_forking_outside_group, :result) do
false | false | nil
false | true | nil
true | false | false
true | true | true
end
with_them do
let(:params) { { prevent_forking_outside_group: prevent_forking_outside_group } }
before do
group.add_owner(user)
stub_licensed_features(group_forking_protection: feature_enabled)
end
it 'updates the attribute as expected' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['prevent_forking_outside_group']).to eq(result)
end
end
end
end
end end
describe "POST /groups" do describe "POST /groups" do
......
...@@ -948,7 +948,7 @@ RSpec.describe API::Projects do ...@@ -948,7 +948,7 @@ RSpec.describe API::Projects do
end end
before do before do
stub_licensed_features(group_saml: true) stub_licensed_features(group_saml: true, group_forking_protection: true)
end end
context 'and target namespace is outer' do context 'and target namespace is outer' do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe EE::NamespaceSettings::UpdateService do
let(:group) { create(:group) }
let(:user) { create(:user) }
subject { described_class.new(user, group, params).execute }
describe '#execute' do
before do
create(:namespace_settings, namespace: group, prevent_forking_outside_group: false)
end
context 'as a normal user' do
let(:params) { { prevent_forking_outside_group: true } }
it 'does not change settings' do
subject
expect { group.save! }
.not_to(change { group.namespace_settings.prevent_forking_outside_group })
end
it 'registers an error' do
subject
expect(group.errors[:prevent_forking_outside_group]).to include('Prevent forking setting was not saved')
end
end
context 'as a group owner' do
before do
group.add_owner(user)
end
context 'for a group that does not have prevent forking feature' do
let(:params) { { prevent_forking_outside_group: true } }
it 'does not change settings' do
subject
expect { group.save! }
.not_to(change { group.namespace_settings.prevent_forking_outside_group })
end
it 'registers an error' do
subject
expect(group.errors[:prevent_forking_outside_group]).to include('Prevent forking setting was not saved')
end
end
context 'for a group that has prevent forking feature' do
let(:params) { { prevent_forking_outside_group: true } }
before do
stub_licensed_features(group_forking_protection: true)
end
it 'changes settings' do
subject
group.save!
expect(group.namespace_settings.reload.prevent_forking_outside_group).to eq(true)
end
end
end
end
end
...@@ -11995,6 +11995,12 @@ msgstr "" ...@@ -11995,6 +11995,12 @@ msgstr ""
msgid "GroupSettings|Please choose a group URL with no special characters." msgid "GroupSettings|Please choose a group URL with no special characters."
msgstr "" msgstr ""
msgid "GroupSettings|Prevent forking outside of the group"
msgstr ""
msgid "GroupSettings|Prevent forking setting was not saved"
msgstr ""
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups" msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "" msgstr ""
...@@ -12031,6 +12037,9 @@ msgstr "" ...@@ -12031,6 +12037,9 @@ msgstr ""
msgid "GroupSettings|This setting will prevent group members from being notified if the group is mentioned." msgid "GroupSettings|This setting will prevent group members from being notified if the group is mentioned."
msgstr "" msgstr ""
msgid "GroupSettings|This setting will prevent group members from forking projects outside of the group."
msgstr ""
msgid "GroupSettings|Transfer group" msgid "GroupSettings|Transfer group"
msgstr "" msgstr ""
...@@ -17890,6 +17899,9 @@ msgstr "" ...@@ -17890,6 +17899,9 @@ msgstr ""
msgid "Prevent environment from auto-stopping" msgid "Prevent environment from auto-stopping"
msgstr "" msgstr ""
msgid "Prevent project forking outside current group"
msgstr ""
msgid "Prevent users from changing their profile name" msgid "Prevent users from changing their profile name"
msgstr "" msgstr ""
......
# frozen_string_literal: true
FactoryBot.define do
factory :namespace_settings, class: 'NamespaceSetting' do
namespace
end
end
...@@ -30,6 +30,10 @@ FactoryBot.define do ...@@ -30,6 +30,10 @@ FactoryBot.define do
association :root_storage_statistics, factory: :namespace_root_storage_statistics association :root_storage_statistics, factory: :namespace_root_storage_statistics
end end
trait :with_namespace_settings do
association :namespace_settings, factory: :namespace_settings
end
# Construct a hierarchy underneath the namespace. # Construct a hierarchy underneath the namespace.
# Each namespace will have `children` amount of children, # Each namespace will have `children` amount of children,
# and `depth` levels of descendants. # and `depth` levels of descendants.
......
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