Commit 8f819a5f authored by Lee Tickett's avatar Lee Tickett Committed by Jan Provaznik

Add Requirements Visibility Setting RUN AS-IF-FOSS

parent 06b92492
...@@ -68,6 +68,11 @@ export default { ...@@ -68,6 +68,11 @@ export default {
required: false, required: false,
default: false, default: false,
}, },
requirementsAvailable: {
type: Boolean,
required: false,
default: false,
},
visibilityHelpPath: { visibilityHelpPath: {
type: String, type: String,
required: false, required: false,
...@@ -132,6 +137,7 @@ export default { ...@@ -132,6 +137,7 @@ export default {
snippetsAccessLevel: featureAccessLevel.EVERYONE, snippetsAccessLevel: featureAccessLevel.EVERYONE,
pagesAccessLevel: featureAccessLevel.EVERYONE, pagesAccessLevel: featureAccessLevel.EVERYONE,
metricsDashboardAccessLevel: featureAccessLevel.PROJECT_MEMBERS, metricsDashboardAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
requirementsAccessLevel: featureAccessLevel.EVERYONE,
containerRegistryEnabled: true, containerRegistryEnabled: true,
lfsEnabled: true, lfsEnabled: true,
requestAccessEnabled: true, requestAccessEnabled: true,
...@@ -234,6 +240,10 @@ export default { ...@@ -234,6 +240,10 @@ export default {
featureAccessLevel.PROJECT_MEMBERS, featureAccessLevel.PROJECT_MEMBERS,
this.metricsDashboardAccessLevel, this.metricsDashboardAccessLevel,
); );
this.requirementsAccessLevel = Math.min(
featureAccessLevel.PROJECT_MEMBERS,
this.requirementsAccessLevel,
);
if (this.pagesAccessLevel === featureAccessLevel.EVERYONE) { if (this.pagesAccessLevel === featureAccessLevel.EVERYONE) {
// When from Internal->Private narrow access for only members // When from Internal->Private narrow access for only members
this.pagesAccessLevel = featureAccessLevel.PROJECT_MEMBERS; this.pagesAccessLevel = featureAccessLevel.PROJECT_MEMBERS;
...@@ -257,6 +267,9 @@ export default { ...@@ -257,6 +267,9 @@ export default {
this.pagesAccessLevel = featureAccessLevel.EVERYONE; this.pagesAccessLevel = featureAccessLevel.EVERYONE;
if (this.metricsDashboardAccessLevel === featureAccessLevel.PROJECT_MEMBERS) if (this.metricsDashboardAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
this.metricsDashboardAccessLevel = featureAccessLevel.EVERYONE; this.metricsDashboardAccessLevel = featureAccessLevel.EVERYONE;
if (this.requirementsAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
this.requirementsAccessLevel = featureAccessLevel.EVERYONE;
this.highlightChanges(); this.highlightChanges();
} }
}, },
...@@ -481,6 +494,18 @@ export default { ...@@ -481,6 +494,18 @@ export default {
/> />
</project-setting-row> </project-setting-row>
</div> </div>
<project-setting-row
v-if="requirementsAvailable"
ref="requirements-settings"
:label="s__('ProjectSettings|Requirements')"
:help-text="s__('ProjectSettings|Requirements management system for this project')"
>
<project-feature-setting
v-model="requirementsAccessLevel"
:options="featureAccessLevelOptions"
name="project[project_feature_attributes][requirements_access_level]"
/>
</project-setting-row>
<project-setting-row <project-setting-row
ref="wiki-settings" ref="wiki-settings"
:label="s__('ProjectSettings|Wiki')" :label="s__('ProjectSettings|Wiki')"
......
...@@ -2,6 +2,7 @@ export default { ...@@ -2,6 +2,7 @@ export default {
data() { data() {
return { return {
packagesEnabled: false, packagesEnabled: false,
requirementsEnabled: false,
}; };
}, },
watch: { watch: {
......
...@@ -381,6 +381,20 @@ class ProjectsController < Projects::ApplicationController ...@@ -381,6 +381,20 @@ class ProjectsController < Projects::ApplicationController
.merge(import_url_params) .merge(import_url_params)
end end
def project_feature_attributes
%i[
builds_access_level
issues_access_level
forking_access_level
merge_requests_access_level
repository_access_level
snippets_access_level
wiki_access_level
pages_access_level
metrics_dashboard_access_level
]
end
def project_params_attributes def project_params_attributes
[ [
:allow_merge_on_skipped_pipeline, :allow_merge_on_skipped_pipeline,
...@@ -418,23 +432,11 @@ class ProjectsController < Projects::ApplicationController ...@@ -418,23 +432,11 @@ class ProjectsController < Projects::ApplicationController
:suggestion_commit_message, :suggestion_commit_message,
:packages_enabled, :packages_enabled,
:service_desk_enabled, :service_desk_enabled,
project_feature_attributes: %i[
builds_access_level
issues_access_level
forking_access_level
merge_requests_access_level
repository_access_level
snippets_access_level
wiki_access_level
pages_access_level
metrics_dashboard_access_level
],
project_setting_attributes: %i[ project_setting_attributes: %i[
show_default_award_emojis show_default_award_emojis
squash_option squash_option
] ]
] ] + [project_feature_attributes: project_feature_attributes]
end end
def project_params_create_attributes def project_params_create_attributes
......
...@@ -37,7 +37,8 @@ module Featurable ...@@ -37,7 +37,8 @@ module Featurable
class_methods do class_methods do
def set_available_features(available_features = []) def set_available_features(available_features = [])
@available_features = available_features @available_features ||= []
@available_features += available_features
class_eval do class_eval do
available_features.each do |feature| available_features.each do |feature|
......
...@@ -88,3 +88,5 @@ module ProjectFeaturesCompatibility ...@@ -88,3 +88,5 @@ module ProjectFeaturesCompatibility
project_feature.__send__(:write_attribute, field, value) # rubocop:disable GitlabSecurity/PublicSend project_feature.__send__(:write_attribute, field, value) # rubocop:disable GitlabSecurity/PublicSend
end end
end end
ProjectFeaturesCompatibility.prepend_if_ee('EE::ProjectFeaturesCompatibility')
# frozen_string_literal: true
class AddRequirementsAccessLevelToProjectFeatures < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
with_lock_retries do
add_column :project_features, :requirements_access_level, :integer, default: 20, null: false
end
end
def down
with_lock_retries do
remove_column :project_features, :requirements_access_level, :integer
end
end
end
ced03562d300f99abf687c258a25bf280a6c4f1798a893ee8a79189c09f19e6e
\ No newline at end of file
...@@ -14992,7 +14992,8 @@ CREATE TABLE project_features ( ...@@ -14992,7 +14992,8 @@ CREATE TABLE project_features (
repository_access_level integer DEFAULT 20 NOT NULL, repository_access_level integer DEFAULT 20 NOT NULL,
pages_access_level integer NOT NULL, pages_access_level integer NOT NULL,
forking_access_level integer, forking_access_level integer,
metrics_dashboard_access_level integer metrics_dashboard_access_level integer,
requirements_access_level integer DEFAULT 20 NOT NULL
); );
CREATE SEQUENCE project_features_id_seq CREATE SEQUENCE project_features_id_seq
......
...@@ -1081,6 +1081,7 @@ POST /projects ...@@ -1081,6 +1081,7 @@ POST /projects
| `only_allow_merge_if_pipeline_succeeds` | boolean | **{dotted-circle}** No | Set whether merge requests can only be merged with successful jobs. | | `only_allow_merge_if_pipeline_succeeds` | boolean | **{dotted-circle}** No | Set whether merge requests can only be merged with successful jobs. |
| `packages_enabled` | boolean | **{dotted-circle}** No | Enable or disable packages repository feature. | | `packages_enabled` | boolean | **{dotted-circle}** No | Enable or disable packages repository feature. |
| `pages_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, `enabled`, or `public`. | | `pages_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, `enabled`, or `public`. |
| `requirements_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, `enabled` or `public` |
| `path` | string | **{check-circle}** Yes (if name isn't provided) | Repository name for new project. Generated based on name if not provided (generated as lowercase with dashes). | | `path` | string | **{check-circle}** Yes (if name isn't provided) | Repository name for new project. Generated based on name if not provided (generated as lowercase with dashes). |
| `printing_merge_request_link_enabled` | boolean | **{dotted-circle}** No | Show link to create/view merge request when pushing from the command line. | | `printing_merge_request_link_enabled` | boolean | **{dotted-circle}** No | Show link to create/view merge request when pushing from the command line. |
| `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project members. | | `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project members. |
...@@ -1150,6 +1151,7 @@ POST /projects/user/:user_id ...@@ -1150,6 +1151,7 @@ POST /projects/user/:user_id
| `only_allow_merge_if_pipeline_succeeds` | boolean | **{dotted-circle}** No | Set whether merge requests can only be merged with successful jobs. | | `only_allow_merge_if_pipeline_succeeds` | boolean | **{dotted-circle}** No | Set whether merge requests can only be merged with successful jobs. |
| `packages_enabled` | boolean | **{dotted-circle}** No | Enable or disable packages repository feature. | | `packages_enabled` | boolean | **{dotted-circle}** No | Enable or disable packages repository feature. |
| `pages_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, `enabled`, or `public`. | | `pages_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, `enabled`, or `public`. |
| `requirements_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, `enabled` or `public` |
| `path` | string | **{dotted-circle}** No | Custom repository name for new project. By default generated based on name. | | `path` | string | **{dotted-circle}** No | Custom repository name for new project. By default generated based on name. |
| `printing_merge_request_link_enabled` | boolean | **{dotted-circle}** No | Show link to create/view merge request when pushing from the command line. | | `printing_merge_request_link_enabled` | boolean | **{dotted-circle}** No | Show link to create/view merge request when pushing from the command line. |
| `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project-members. | | `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project-members. |
...@@ -1225,6 +1227,7 @@ PUT /projects/:id ...@@ -1225,6 +1227,7 @@ PUT /projects/:id
| `only_mirror_protected_branches` **(STARTER)** | boolean | **{dotted-circle}** No | Only mirror protected branches. | | `only_mirror_protected_branches` **(STARTER)** | boolean | **{dotted-circle}** No | Only mirror protected branches. |
| `packages_enabled` | boolean | **{dotted-circle}** No | Enable or disable packages repository feature. | | `packages_enabled` | boolean | **{dotted-circle}** No | Enable or disable packages repository feature. |
| `pages_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, `enabled`, or `public`. | | `pages_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, `enabled`, or `public`. |
| `requirements_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, `enabled` or `public` |
| `path` | string | **{dotted-circle}** No | Custom repository name for the project. By default generated based on name. | | `path` | string | **{dotted-circle}** No | Custom repository name for the project. By default generated based on name. |
| `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project members. | | `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project members. |
| `remove_source_branch_after_merge` | boolean | **{dotted-circle}** No | Enable `Delete source branch` option by default for all new merge requests. | | `remove_source_branch_after_merge` | boolean | **{dotted-circle}** No | Enable `Delete source branch` option by default for all new merge requests. |
......
...@@ -72,6 +72,7 @@ Use the switches to enable or disable the following features: ...@@ -72,6 +72,7 @@ Use the switches to enable or disable the following features:
| **Snippets** | ✓ | Enables [sharing of code and text](../../snippets.md) | | **Snippets** | ✓ | Enables [sharing of code and text](../../snippets.md) |
| **Pages** | ✓ | Allows you to [publish static websites](../pages/) | | **Pages** | ✓ | Allows you to [publish static websites](../pages/) |
| **Metrics Dashboard** | ✓ | Control access to [metrics dashboard](../integrations/prometheus.md) | **Metrics Dashboard** | ✓ | Control access to [metrics dashboard](../integrations/prometheus.md)
| **Requirements** | ✓ | Control access to [Requirements Management](../requirements/index.md)
Some features depend on others: Some features depend on others:
......
...@@ -2,6 +2,7 @@ export default { ...@@ -2,6 +2,7 @@ export default {
data() { data() {
return { return {
packagesEnabled: true, packagesEnabled: true,
requirementsEnabled: true,
}; };
}, },
watch: { watch: {
......
...@@ -47,6 +47,11 @@ module EE ...@@ -47,6 +47,11 @@ module EE
end end
end end
override :project_feature_attributes
def project_feature_attributes
super + [:requirements_access_level]
end
override :project_params_attributes override :project_params_attributes
def project_params_attributes def project_params_attributes
super + project_params_ee super + project_params_ee
......
...@@ -39,9 +39,27 @@ module EE ...@@ -39,9 +39,27 @@ module EE
nav_tabs << :project_insights nav_tabs << :project_insights
end end
if can?(current_user, :read_requirement, project)
nav_tabs << :requirements
end
nav_tabs nav_tabs
end end
override :project_permissions_settings
def project_permissions_settings(project)
super.merge(
requirementsAccessLevel: project.requirements_access_level
)
end
override :project_permissions_panel_data
def project_permissions_panel_data(project)
super.merge(
requirementsAvailable: project.feature_available?(:requirements)
)
end
override :default_url_to_repo override :default_url_to_repo
def default_url_to_repo(project = @project) def default_url_to_repo(project = @project)
case default_clone_protocol case default_clone_protocol
......
# frozen_string_literal: true
module EE
module ProjectFeaturesCompatibility
extend ActiveSupport::Concern
# TODO: remove in API v5, replaced by *_access_level
def requirements_enabled=(value)
write_feature_attribute_boolean(:requirements_access_level, value)
end
def requirements_access_level=(value)
write_feature_attribute_string(:requirements_access_level, value)
end
end
end
...@@ -190,6 +190,8 @@ module EE ...@@ -190,6 +190,8 @@ module EE
delegate :auto_rollback_enabled, :auto_rollback_enabled=, :auto_rollback_enabled?, to: :ci_cd_settings delegate :auto_rollback_enabled, :auto_rollback_enabled=, :auto_rollback_enabled?, to: :ci_cd_settings
delegate :closest_gitlab_subscription, to: :namespace delegate :closest_gitlab_subscription, to: :namespace
delegate :requirements_access_level, to: :project_feature, allow_nil: true
validates :repository_size_limit, validates :repository_size_limit,
numericality: { only_integer: true, greater_than_or_equal_to: 0, allow_nil: true } numericality: { only_integer: true, greater_than_or_equal_to: 0, allow_nil: true }
validates :max_pages_size, validates :max_pages_size,
...@@ -206,6 +208,17 @@ module EE ...@@ -206,6 +208,17 @@ module EE
validates :mirror_user, presence: true validates :mirror_user, presence: true
end end
# Because we use default_value_for we need to be sure
# requirements_enabled= method does exist even if we rollback migration.
# Otherwise many tests from spec/migrations will fail.
def requirements_enabled=(value)
if has_attribute?(:requirements_enabled)
write_attribute(:requirements_enabled, value)
end
end
default_value_for :requirements_enabled, true
accepts_nested_attributes_for :status_page_setting, update_only: true, allow_destroy: true accepts_nested_attributes_for :status_page_setting, update_only: true, allow_destroy: true
accepts_nested_attributes_for :compliance_framework_setting, update_only: true, allow_destroy: true accepts_nested_attributes_for :compliance_framework_setting, update_only: true, allow_destroy: true
......
...@@ -4,11 +4,17 @@ module EE ...@@ -4,11 +4,17 @@ module EE
module ProjectFeature module ProjectFeature
extend ActiveSupport::Concern extend ActiveSupport::Concern
EE_FEATURES = %i(requirements).freeze
prepended do prepended do
set_available_features(EE_FEATURES)
# Ensure changes to project visibility settings go to elasticsearch # Ensure changes to project visibility settings go to elasticsearch
after_commit on: :update do after_commit on: :update do
project.maintain_elasticsearch_update if project.maintaining_elasticsearch? project.maintain_elasticsearch_update if project.maintaining_elasticsearch?
end end
default_value_for :requirements_access_level, value: Featurable::ENABLED, allows_nil: false
end end
end end
end end
...@@ -16,7 +16,7 @@ module EE ...@@ -16,7 +16,7 @@ module EE
condition(:iterations_available) { @subject.feature_available?(:iterations) } condition(:iterations_available) { @subject.feature_available?(:iterations) }
with_scope :subject with_scope :subject
condition(:requirements_available) { @subject.feature_available?(:requirements) } condition(:requirements_available) { @subject.feature_available?(:requirements) & feature_available?(:requirements) }
condition(:compliance_framework_available) { @subject.feature_available?(:compliance_framework, @user) } condition(:compliance_framework_available) { @subject.feature_available?(:compliance_framework, @user) }
......
- return unless can?(current_user, :read_requirement, project) - return unless project_nav_tab? :requirements
= nav_link(path: 'requirements#index') do = nav_link(path: 'requirements#index') do
= link_to project_requirements_management_requirements_path(project), class: 'qa-project-requirements-link' do = link_to project_requirements_management_requirements_path(project), class: 'qa-project-requirements-link' do
......
---
title: Add requirements visibility access project setting
merge_request: 46532
author: Lee Tickett
type: added
...@@ -28,6 +28,9 @@ module EE ...@@ -28,6 +28,9 @@ module EE
expose :marked_for_deletion_on, if: ->(project, _) { project.feature_available?(:adjourned_deletion_for_projects_and_groups) } do |project, _| expose :marked_for_deletion_on, if: ->(project, _) { project.feature_available?(:adjourned_deletion_for_projects_and_groups) } do |project, _|
project.marked_for_deletion_at project.marked_for_deletion_at
end end
expose :requirements_enabled do |project, options|
project.feature_available?(:requirements, options[:current_user])
end
expose :compliance_frameworks do |project, _| expose :compliance_frameworks do |project, _|
[project.compliance_framework_setting&.compliance_management_framework&.name].compact [project.compliance_framework_setting&.compliance_management_framework&.name].compact
end end
......
...@@ -13,7 +13,8 @@ module EE ...@@ -13,7 +13,8 @@ module EE
:builds_access_level, :builds_access_level,
:repository_access_level, :repository_access_level,
:pages_access_level, :pages_access_level,
:metrics_dashboard_access_level].freeze :metrics_dashboard_access_level,
:requirements_access_level].freeze
def initialize(current_user, model, project) def initialize(current_user, model, project)
@project = project @project = project
......
...@@ -3,52 +3,86 @@ ...@@ -3,52 +3,86 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Projects::RequirementsManagement::RequirementsController do RSpec.describe Projects::RequirementsManagement::RequirementsController do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
subject { get :index, params: { namespace_id: project.namespace, project_id: project } } subject { get :index, params: { namespace_id: project.namespace, project_id: project } }
describe 'GET #index' do describe 'GET #index' do
context 'with authorized user' do context 'private project' do
before do let(:project) { create(:project) }
project.add_developer(user)
sign_in(user)
end
context 'when feature is available' do context 'with authorized user' do
before do before do
stub_licensed_features(requirements: true) project.add_developer(user)
sign_in(user)
end end
it 'renders the index template' do context 'when feature is available' do
subject before do
stub_licensed_features(requirements: true)
end
expect(response).to have_gitlab_http_status(:ok) it 'renders the index template' do
expect(response).to render_template(:index) subject
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:index)
end
end
context 'when feature is not available' do
before do
stub_licensed_features(requirements: false)
end
it 'returns 404' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end end
end end
context 'when feature is not available' do context 'with unauthorized user' do
before do before do
stub_licensed_features(requirements: false) sign_in(user)
end end
it 'returns 404' do context 'when feature is available' do
before do
stub_licensed_features(requirements: true)
end
it 'returns 404' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
context 'with anonymous user' do
it 'returns 302' do
subject subject
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:found)
expect(response).to redirect_to(new_user_session_path)
end end
end end
end end
context 'with unauthorized user' do context 'public project' do
let(:project) { create(:project, :public) }
before do before do
sign_in(user) stub_licensed_features(requirements: true)
end end
context 'when feature is available' do context 'with requirements disabled' do
before do before do
stub_licensed_features(requirements: true) project.project_feature.update!({ requirements_access_level: ::ProjectFeature::DISABLED })
project.add_developer(user)
sign_in(user)
end end
it 'returns 404' do it 'returns 404' do
...@@ -57,14 +91,52 @@ RSpec.describe Projects::RequirementsManagement::RequirementsController do ...@@ -57,14 +91,52 @@ RSpec.describe Projects::RequirementsManagement::RequirementsController do
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
end end
end
context 'with anonymous user' do context 'with requirements visible to project memebers' do
it 'returns 302' do before do
subject project.project_feature.update!({ requirements_access_level: ::ProjectFeature::PRIVATE })
end
context 'with authorized user' do
before do
project.add_developer(user)
sign_in(user)
end
it 'renders the index template' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:index)
end
end
context 'with unauthorized user' do
before do
sign_in(user)
end
it 'returns 404' do
subject
expect(response).to have_gitlab_http_status(:found) expect(response).to have_gitlab_http_status(:not_found)
expect(response).to redirect_to(new_user_session_path) end
end
end
context 'with requirements visible to everyone' do
before do
project.project_feature.update!({ requirements_access_level: ::ProjectFeature::ENABLED })
end
context 'with anonymous user' do
it 'renders the index template' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:index)
end
end
end end
end end
end end
......
# frozen_string_literal: true # frozen_string_literal: true
RSpec.shared_examples 'resource with requirement permissions' do RSpec.shared_examples 'resource with requirement permissions' do
include AdminModeHelper
let(:all_permissions) do let(:all_permissions) do
[:read_requirement, :create_requirement, :admin_requirement, [:read_requirement, :create_requirement, :admin_requirement,
:update_requirement, :destroy_requirement, :update_requirement, :destroy_requirement,
...@@ -77,6 +79,82 @@ RSpec.shared_examples 'resource with requirement permissions' do ...@@ -77,6 +79,82 @@ RSpec.shared_examples 'resource with requirement permissions' do
it { is_expected.to be_disallowed(*all_permissions) } it { is_expected.to be_disallowed(*all_permissions) }
end end
end end
context 'when access level is disabled' do
before do
parent = resource.is_a?(Project) ? resource : resource.resource_parent
parent.project_feature.update!(requirements_access_level: ProjectFeature::DISABLED)
end
context 'with owner' do
let(:current_user) { owner }
it { is_expected.to be_disallowed(*all_permissions) }
end
context 'with admin' do
let(:current_user) { admin }
it { is_expected.to be_disallowed(*all_permissions) }
end
end
context 'when access level is private' do
before do
parent = resource.is_a?(Project) ? resource : resource.resource_parent
parent.project_feature.update!(requirements_access_level: ProjectFeature::PRIVATE)
end
context 'with admin user' do
let(:current_user) { admin }
it { is_expected.to be_disallowed(*all_permissions) }
context 'with admin mode enabled' do
before do
enable_admin_mode!(current_user)
end
it_behaves_like 'user with read only permissions'
end
end
context 'with owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(*all_permissions) }
end
context 'with maintainer' do
let(:current_user) { maintainer }
it_behaves_like 'user with manage permissions'
end
context 'with developer' do
let(:current_user) { developer }
it_behaves_like 'user with manage permissions'
end
context 'with reporter' do
let(:current_user) { reporter }
it_behaves_like 'user with manage permissions'
end
context 'with guest' do
let(:current_user) { guest }
it_behaves_like 'user with read only permissions'
end
context 'with non member' do
let(:current_user) { create(:user) }
it { is_expected.to be_disallowed(*all_permissions) }
end
end
end end
context 'when requirements feature is disabled' do context 'when requirements feature is disabled' do
......
...@@ -21108,6 +21108,12 @@ msgstr "" ...@@ -21108,6 +21108,12 @@ msgstr ""
msgid "ProjectSettings|Require" msgid "ProjectSettings|Require"
msgstr "" msgstr ""
msgid "ProjectSettings|Requirements"
msgstr ""
msgid "ProjectSettings|Requirements management system for this project"
msgstr ""
msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests." msgid "ProjectSettings|Set the default behavior and availability of this option in merge requests. Changes made are also applied to existing merge requests."
msgstr "" msgstr ""
......
...@@ -681,13 +681,7 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do ...@@ -681,13 +681,7 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do
end end
it 'overrides project feature access levels' do it 'overrides project feature access levels' do
access_level_keys = project.project_feature.attributes.keys.select { |a| a =~ /_access_level/ } access_level_keys = ProjectFeature.available_features.map { |feature| ProjectFeature.access_level_attribute(feature) }
# `pages_access_level` is not included, since it is not available in the public API
# and has a dependency on project's visibility level
# see ProjectFeature model
access_level_keys.delete('pages_access_level')
disabled_access_levels = Hash[access_level_keys.collect { |item| [item, 'disabled'] }] disabled_access_levels = Hash[access_level_keys.collect { |item| [item, 'disabled'] }]
project.create_import_data(data: { override_params: disabled_access_levels }) project.create_import_data(data: { override_params: disabled_access_levels })
......
...@@ -575,6 +575,7 @@ ProjectFeature: ...@@ -575,6 +575,7 @@ ProjectFeature:
- repository_access_level - repository_access_level
- pages_access_level - pages_access_level
- metrics_dashboard_access_level - metrics_dashboard_access_level
- requirements_access_level
- created_at - created_at
- updated_at - updated_at
ProtectedBranch::MergeAccessLevel: ProtectedBranch::MergeAccessLevel:
......
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