Commit 2969cfe5 authored by Arturo Herrero's avatar Arturo Herrero

Merge branch 'jnnkl-security-compliance-core' into 'master'

Render Static Security & Compliance page for non-Ultimate users [RUN ALL RSPEC] [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!50282
parents d3602916 fe63b388
# frozen_string_literal: true
module Projects
module Security
class ConfigurationController < Projects::ApplicationController
feature_category :static_application_security_testing
def show
return render_404 unless feature_enabled?
render_403 unless can?(current_user, :read_security_configuration, project)
end
private
def feature_enabled?
::Feature.enabled?(:secure_security_and_compliance_configuration_page_on_ce, @project, default_enabled: :yaml)
end
end
end
end
Projects::Security::ConfigurationController.prepend_if_ee('EE::Projects::Security::ConfigurationController')
...@@ -139,6 +139,10 @@ module ProjectsHelper ...@@ -139,6 +139,10 @@ module ProjectsHelper
project_nav_tabs.include? name project_nav_tabs.include? name
end end
def any_project_nav_tab?(tabs)
tabs.any? { |tab| project_nav_tab?(tab) }
end
def project_for_deploy_key(deploy_key) def project_for_deploy_key(deploy_key)
if deploy_key.has_access_to?(@project) if deploy_key.has_access_to?(@project)
@project @project
...@@ -374,6 +378,20 @@ module ProjectsHelper ...@@ -374,6 +378,20 @@ module ProjectsHelper
private private
def can_read_security_configuration?(project, current_user)
::Feature.enabled?(:secure_security_and_compliance_configuration_page_on_ce, @subject, default_enabled: :yaml) &&
can?(current_user, :read_security_configuration, project)
end
def get_project_security_nav_tabs(project, current_user)
if can_read_security_configuration?(project, current_user)
[:security_and_compliance, :security_configuration]
else
[]
end
end
# rubocop:disable Metrics/CyclomaticComplexity
def get_project_nav_tabs(project, current_user) def get_project_nav_tabs(project, current_user)
nav_tabs = [:home] nav_tabs = [:home]
...@@ -382,6 +400,8 @@ module ProjectsHelper ...@@ -382,6 +400,8 @@ module ProjectsHelper
nav_tabs << :releases if can?(current_user, :read_release, project) nav_tabs << :releases if can?(current_user, :read_release, project)
end end
nav_tabs += get_project_security_nav_tabs(project, current_user)
if project.repo_exists? && can?(current_user, :read_merge_request, project) if project.repo_exists? && can?(current_user, :read_merge_request, project)
nav_tabs << :merge_requests nav_tabs << :merge_requests
end end
...@@ -415,6 +435,7 @@ module ProjectsHelper ...@@ -415,6 +435,7 @@ module ProjectsHelper
nav_tabs nav_tabs
end end
# rubocop:enable Metrics/CyclomaticComplexity
def package_nav_tabs(project, current_user) def package_nav_tabs(project, current_user)
[].tap do |tabs| [].tap do |tabs|
...@@ -695,6 +716,12 @@ module ProjectsHelper ...@@ -695,6 +716,12 @@ module ProjectsHelper
"#{request.path}?#{options.to_param}" "#{request.path}?#{options.to_param}"
end end
def sidebar_security_configuration_paths
%w[
projects/security/configuration#show
]
end
def sidebar_projects_paths def sidebar_projects_paths
%w[ %w[
projects#show projects#show
...@@ -759,6 +786,10 @@ module ProjectsHelper ...@@ -759,6 +786,10 @@ module ProjectsHelper
] ]
end end
def sidebar_security_paths
%w[projects/security/configuration#show]
end
def user_can_see_auto_devops_implicitly_enabled_banner?(project, user) def user_can_see_auto_devops_implicitly_enabled_banner?(project, user)
Ability.allowed?(user, :admin_project, project) && Ability.allowed?(user, :admin_project, project) &&
project.has_auto_devops_implicitly_enabled? && project.has_auto_devops_implicitly_enabled? &&
......
...@@ -581,6 +581,10 @@ class ProjectPolicy < BasePolicy ...@@ -581,6 +581,10 @@ class ProjectPolicy < BasePolicy
enable :read_issue_link enable :read_issue_link
end end
rule { can?(:developer_access) }.policy do
enable :read_security_configuration
end
# Design abilities could also be prevented in the issue policy. # Design abilities could also be prevented in the issue policy.
rule { design_management_disabled }.policy do rule { design_management_disabled }.policy do
prevent :read_design prevent :read_design
......
- top_level_link = project_security_configuration_path(@project)
- top_level_qa_selector = 'security_configuration_link'
- if any_project_nav_tab?([:security_configuration])
= nav_link(path: sidebar_security_paths) do
= link_to top_level_link, data: { qa_selector: top_level_qa_selector } do
.nav-icon-container
= sprite_icon('shield')
%span.nav-item-name
= _('Security & Compliance')
%ul.sidebar-sub-level-items
= nav_link(path: sidebar_security_paths, html_options: { class: "fly-out-top-item" } ) do
= link_to top_level_link do
%strong.fly-out-top-item-name
= _('Security & Compliance')
%li.divider.fly-out-top-item
- if project_nav_tab?(:security_configuration)
= nav_link(path: sidebar_security_configuration_paths) do
= link_to project_security_configuration_path(@project), title: _('Configuration'), data: { qa_selector: 'security_configuration_link'} do
%span= _('Configuration')
- breadcrumb_title _("Security Configuration")
- page_title _("Security Configuration")
#js-security-configuration-static
---
name: secure_security_and_compliance_configuration_page_on_ce
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50282
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/294076
milestone: '13.9'
type: development
group: group::static analysis
default_enabled: false
...@@ -37,6 +37,10 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -37,6 +37,10 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end end
end end
namespace :security do
resource :configuration, only: [:show], controller: :configuration
end
resources :artifacts, only: [:index, :destroy] resources :artifacts, only: [:index, :destroy]
resources :packages, only: [:index, :show, :destroy], module: :packages resources :packages, only: [:index, :show, :destroy], module: :packages
......
...@@ -4,6 +4,11 @@ import SecurityConfigurationApp from './components/app.vue'; ...@@ -4,6 +4,11 @@ import SecurityConfigurationApp from './components/app.vue';
export default function init() { export default function init() {
const el = document.getElementById('js-security-configuration'); const el = document.getElementById('js-security-configuration');
if (!el) {
return null;
}
const { const {
autoDevopsHelpPagePath, autoDevopsHelpPagePath,
autoDevopsPath, autoDevopsPath,
......
# frozen_string_literal: true
module EE
module Projects
module Security
module ConfigurationController
extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override
prepended do
alias_method :vulnerable, :project
before_action :ensure_security_dashboard_feature_enabled!, except: [:show]
before_action :authorize_read_security_dashboard!, except: [:show]
before_action only: [:show] do
push_frontend_feature_flag(:security_auto_fix, project, default_enabled: false)
push_frontend_feature_flag(:sast_configuration_ui, project, default_enabled: true)
push_frontend_feature_flag(:api_fuzzing_configuration_ui, project, default_enabled: :yaml)
end
before_action only: [:auto_fix] do
check_feature_flag!
authorize_modify_auto_fix_setting!
end
feature_category :static_application_security_testing
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
override :show
def show
return super unless security_dashboard_feature_enabled? && can_read_security_dashboard?
@configuration = ::Projects::Security::ConfigurationPresenter.new(project,
auto_fix_permission: auto_fix_authorized?,
current_user: current_user)
respond_to do |format|
format.html
format.json do
render status: :ok, json: @configuration.to_h
end
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def auto_fix
service = ::Security::Configuration::SaveAutoFixService.new(project, auto_fix_params[:feature])
return respond_422 unless service.execute(enabled: auto_fix_params[:enabled])
render status: :ok, json: auto_fix_settings
end
private
def auto_fix_authorized?
can?(current_user, :modify_auto_fix_setting, project)
end
def auto_fix_params
@auto_fix_params ||= begin
fix_params = params.permit(:feature, :enabled)
feature = fix_params[:feature]
fix_params[:feature] = feature.blank? ? 'all' : feature.to_s
fix_params
end
end
def check_auto_fix_permissions!
render_403 unless auto_fix_authorized?
end
def check_feature_flag!
render_404 if ::Feature.disabled?(:security_auto_fix, project)
end
def auto_fix_settings
setting = project.security_setting
{
dependency_scanning: setting.auto_fix_dependency_scanning,
container_scanning: setting.auto_fix_container_scanning
}
end
def security_dashboard_feature_enabled?
vulnerable.feature_available?(:security_dashboard)
end
def can_read_security_dashboard?
can?(current_user, :read_project_security_dashboard, vulnerable)
end
def ensure_security_dashboard_feature_enabled!
render_404 unless security_dashboard_feature_enabled?
end
def authorize_read_security_dashboard!
render_403 unless can_read_security_dashboard?
end
end
end
end
end
# frozen_string_literal: true
module Projects
module Security
class ConfigurationController < Projects::ApplicationController
include SecurityDashboardsPermissions
alias_method :vulnerable, :project
before_action only: [:show] do
push_frontend_feature_flag(:security_auto_fix, project, default_enabled: false)
push_frontend_feature_flag(:sast_configuration_ui, project, default_enabled: true)
push_frontend_feature_flag(:api_fuzzing_configuration_ui, project, default_enabled: :yaml)
end
before_action only: [:auto_fix] do
check_feature_flag!
authorize_modify_auto_fix_setting!
end
feature_category :static_application_security_testing
def show
@configuration = ConfigurationPresenter.new(project,
auto_fix_permission: auto_fix_authorized?,
current_user: current_user)
respond_to do |format|
format.html
format.json do
render status: :ok, json: @configuration.to_h
end
end
end
def auto_fix
service = ::Security::Configuration::SaveAutoFixService.new(project, auto_fix_params[:feature])
return respond_422 unless service.execute(enabled: auto_fix_params[:enabled])
render status: :ok, json: auto_fix_settings
end
private
def auto_fix_authorized?
can?(current_user, :modify_auto_fix_setting, project)
end
def auto_fix_params
return @auto_fix_params if @auto_fix_params
@auto_fix_params = params.permit(:feature, :enabled)
feature = @auto_fix_params[:feature]
@auto_fix_params[:feature] = feature.blank? ? 'all' : feature.to_s
@auto_fix_params
end
def check_auto_fix_permissions!
render_403 unless auto_fix_authorized?
end
def check_feature_flag!
render_404 if Feature.disabled?(:security_auto_fix, project)
end
def auto_fix_settings
setting = project.security_setting
{
dependency_scanning: setting.auto_fix_dependency_scanning,
container_scanning: setting.auto_fix_container_scanning
}
end
end
end
end
...@@ -27,8 +27,6 @@ module EE ...@@ -27,8 +27,6 @@ module EE
def get_project_nav_tabs(project, current_user) def get_project_nav_tabs(project, current_user)
nav_tabs = super nav_tabs = super
nav_tabs += get_project_security_nav_tabs(project, current_user)
if can?(current_user, :read_code_review_analytics, project) if can?(current_user, :read_code_review_analytics, project)
nav_tabs << :code_review nav_tabs << :code_review
end end
...@@ -160,9 +158,9 @@ module EE ...@@ -160,9 +158,9 @@ module EE
@project.feature_available?(:merge_trains) @project.feature_available?(:merge_trains)
end end
override :sidebar_security_paths
def sidebar_security_paths def sidebar_security_paths
%w[ super + %w[
projects/security/configuration#show
projects/security/sast_configuration#show projects/security/sast_configuration#show
projects/security/api_fuzzing_configuration#show projects/security/api_fuzzing_configuration#show
projects/security/vulnerabilities#show projects/security/vulnerabilities#show
...@@ -199,9 +197,9 @@ module EE ...@@ -199,9 +197,9 @@ module EE
] ]
end end
override :sidebar_security_configuration_paths
def sidebar_security_configuration_paths def sidebar_security_configuration_paths
%w[ super + %w[
projects/security/configuration#show
projects/security/sast_configuration#show projects/security/sast_configuration#show
projects/security/api_fuzzing_configuration#show projects/security/api_fuzzing_configuration#show
projects/security/dast_profiles#show projects/security/dast_profiles#show
...@@ -324,14 +322,20 @@ module EE ...@@ -324,14 +322,20 @@ module EE
private private
override :can_read_security_configuration?
def can_read_security_configuration?(project, current_user)
super || (project.feature_available?(:security_dashboard) &&
can?(current_user, :read_project_security_dashboard, project))
end
override :get_project_security_nav_tabs
def get_project_security_nav_tabs(project, current_user) def get_project_security_nav_tabs(project, current_user)
return [] unless can?(current_user, :access_security_and_compliance, project) return [] unless can?(current_user, :access_security_and_compliance, project)
nav_tabs = [:security_and_compliance] nav_tabs = super.union([:security_and_compliance])
if can?(current_user, :read_project_security_dashboard, project) if can?(current_user, :read_project_security_dashboard, project)
nav_tabs << :security nav_tabs << :security
nav_tabs << :security_configuration
end end
if can?(current_user, :read_on_demand_scans, @project) if can?(current_user, :read_on_demand_scans, @project)
......
- on_demand_scans_path = Feature.enabled?(:dast_saved_scans, @project, default_enabled: :yaml) ? new_project_on_demand_scan_path(@project) : project_on_demand_scans_path(@project) - on_demand_scans_path = Feature.enabled?(:dast_saved_scans, @project, default_enabled: :yaml) ? new_project_on_demand_scan_path(@project) : project_on_demand_scans_path(@project)
- if any_project_nav_tab?([:security, :dependencies, :licenses, :audit_events]) - if any_project_nav_tab?([:security, :security_configuration, :dependencies, :licenses, :audit_events])
= nav_link(path: sidebar_security_paths) do = nav_link(path: sidebar_security_paths) do
= link_to top_level_link(@project), data: { qa_selector: top_level_qa_selector(@project) } do = link_to top_level_link(@project), data: { qa_selector: top_level_qa_selector(@project) } do
.nav-icon-container .nav-icon-container
......
- breadcrumb_title _("Security Configuration") - breadcrumb_title _("Security Configuration")
- page_title _("Security Configuration") - page_title _("Security Configuration")
#js-security-configuration{ data: { **@configuration.to_html_data_attribute, - if @configuration.nil?
auto_fix_help_path: '/', = render_ce 'projects/security/configuration/show'
toggle_autofix_setting_endpoint: 'configuration/auto_fix', - else
container_scanning_help_path: help_page_path('user/application_security/container_scanning/index'), #js-security-configuration{ data: { **@configuration.to_html_data_attribute,
dependency_scanning_help_path: help_page_path('user/application_security/dependency_scanning/index') } } auto_fix_help_path: '/',
toggle_autofix_setting_endpoint: 'configuration/auto_fix',
container_scanning_help_path: help_page_path('user/application_security/container_scanning/index'),
dependency_scanning_help_path: help_page_path('user/application_security/dependency_scanning/index') } }
...@@ -63,7 +63,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -63,7 +63,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources :dashboard, only: [:index], controller: :dashboard resources :dashboard, only: [:index], controller: :dashboard
resources :vulnerability_report, only: [:index], controller: :vulnerability_report resources :vulnerability_report, only: [:index], controller: :vulnerability_report
resource :configuration, only: [:show], controller: :configuration do resource :configuration, only: [], controller: :configuration do
post :auto_fix, on: :collection post :auto_fix, on: :collection
resource :corpus_management, only: [:show], controller: :corpus_management resource :corpus_management, only: [:show], controller: :corpus_management
resource :sast, only: [:show, :create], controller: :sast_configuration resource :sast, only: [:show, :create], controller: :sast_configuration
......
...@@ -7,71 +7,113 @@ RSpec.describe Projects::Security::ConfigurationController do ...@@ -7,71 +7,113 @@ RSpec.describe Projects::Security::ConfigurationController do
let(:project) { create(:project, :repository, namespace: group) } let(:project) { create(:project, :repository, namespace: group) }
describe 'GET #show' do describe 'GET #show' do
using RSpec::Parameterized::TableSyntax
subject(:request) { get :show, params: { namespace_id: project.namespace, project_id: project } } subject(:request) { get :show, params: { namespace_id: project.namespace, project_id: project } }
it_behaves_like SecurityDashboardsPermissions do let(:user) { create(:user) }
let(:vulnerable) { project }
let(:security_dashboard_action) { request }
end
context 'with user' do render_views
let(:user) { create(:user) }
render_views where(:user_role, :security_dashboard_enabled, :ce_flag_enabled, :status, :selector) do
:guest | false | false | :not_found | nil
:guest | false | true | :forbidden | nil
:guest | true | false | :not_found | nil
:guest | true | true | :forbidden | nil
:developer | false | false | :not_found | nil
:developer | false | true | :ok | '#js-security-configuration-static'
:developer | true | false | :ok | '#js-security-configuration'
:developer | true | true | :ok | '#js-security-configuration'
end
with_them do
before do before do
stub_licensed_features(security_dashboard: true) stub_licensed_features(security_dashboard: security_dashboard_enabled)
stub_feature_flags(secure_security_and_compliance_configuration_page_on_ce: ce_flag_enabled)
group.add_developer(user) group.send("add_#{user_role}", user)
sign_in(user) sign_in(user)
end end
it 'responds in json format when requested' do it 'responds with the correct status' do
get :show, params: { namespace_id: project.namespace, project_id: project, format: :json } request
types = %w(sast dast dast_profiles dependency_scanning container_scanning secret_detection coverage_fuzzing license_scanning api_fuzzing) expect(response).to have_gitlab_http_status(status)
expect(response).to have_gitlab_http_status(:ok) unless selector.nil?
expect(json_response['features'].map { |f| f['type'] }).to match_array(types) expect(response).to render_template(:show)
expect(json_response['auto_fix_enabled']).to include({ 'dependency_scanning' => true, 'container_scanning' => true }) expect(response.body).to have_css(selector)
end
end end
end
it "renders data on the project's security configuration" do context 'with developer and security dashboard feature enabled' do
request let(:flag) { :secure_security_and_compliance_configuration_page_on_ce }
expect(response).to have_gitlab_http_status(:ok) # The tests in this context should be unaffected by this feature flag,
expect(response).to render_template(:show) # and should behave identically whether this is enabled or disabled.
expect(response.body).to have_css( where(:flag_enabled) do
'div#js-security-configuration'\ [
"[data-auto-devops-help-page-path=\"#{help_page_path('topics/autodevops/index')}\"]"\ [true],
"[data-help-page-path=\"#{help_page_path('user/application_security/index')}\"]"\ [false]
"[data-latest-pipeline-path=\"#{help_page_path('ci/pipelines')}\"]" ]
)
end end
context 'when the latest pipeline used Auto DevOps' do with_them do
let!(:pipeline) do before do
create( stub_feature_flags(flag => flag_enabled)
:ci_pipeline, stub_licensed_features(security_dashboard: true)
:auto_devops_source,
project: project, group.add_developer(user)
ref: project.default_branch, sign_in(user)
sha: project.commit.sha
)
end end
it 'reports that Auto DevOps is enabled' do it 'responds in json format when requested' do
get :show, params: { namespace_id: project.namespace, project_id: project, format: :json }
types = %w(sast dast dast_profiles dependency_scanning container_scanning secret_detection coverage_fuzzing license_scanning api_fuzzing)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['features'].map { |f| f['type'] }).to match_array(types)
expect(json_response['auto_fix_enabled']).to include({ 'dependency_scanning' => true, 'container_scanning' => true })
end
it "renders data on the project's security configuration" do
request request
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:show)
expect(response.body).to have_css( expect(response.body).to have_css(
'div#js-security-configuration'\ 'div#js-security-configuration'\
'[data-auto-devops-enabled]'\
"[data-auto-devops-help-page-path=\"#{help_page_path('topics/autodevops/index')}\"]"\ "[data-auto-devops-help-page-path=\"#{help_page_path('topics/autodevops/index')}\"]"\
"[data-help-page-path=\"#{help_page_path('user/application_security/index')}\"]"\ "[data-help-page-path=\"#{help_page_path('user/application_security/index')}\"]"\
"[data-latest-pipeline-path=\"#{project_pipeline_path(project, pipeline)}\"]" "[data-latest-pipeline-path=\"#{help_page_path('ci/pipelines')}\"]"
) )
end end
context 'when the latest pipeline used Auto DevOps' do
let!(:pipeline) do
create(
:ci_pipeline,
:auto_devops_source,
project: project,
ref: project.default_branch,
sha: project.commit.sha
)
end
it 'reports that Auto DevOps is enabled' do
request
expect(response).to have_gitlab_http_status(:ok)
expect(response.body).to have_css(
'div#js-security-configuration'\
'[data-auto-devops-enabled]'\
"[data-auto-devops-help-page-path=\"#{help_page_path('topics/autodevops/index')}\"]"\
"[data-help-page-path=\"#{help_page_path('user/application_security/index')}\"]"\
"[data-latest-pipeline-path=\"#{project_pipeline_path(project, pipeline)}\"]"
)
end
end
end end
end end
end end
......
...@@ -302,7 +302,8 @@ RSpec.describe ProjectsHelper do ...@@ -302,7 +302,8 @@ RSpec.describe ProjectsHelper do
describe 'Security & Compliance tabs' do describe 'Security & Compliance tabs' do
where(:ability, :nav_tabs) do where(:ability, :nav_tabs) do
:read_project_security_dashboard | [:security, :security_configuration] :read_project_security_dashboard | [:security]
:read_security_configuration | [:security_configuration]
:read_on_demand_scans | [:on_demand_scans] :read_on_demand_scans | [:on_demand_scans]
:read_dependencies | [:dependencies] :read_dependencies | [:dependencies]
:read_licenses | [:licenses] :read_licenses | [:licenses]
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::Security::ConfigurationController, 'routing' do
let(:base_params) { { namespace_id: 'gitlab', project_id: 'gitlabhq' } }
before do
allow(Project).to receive(:find_by_full_path).with('gitlab/gitlabhq', any_args).and_return(true)
end
it 'to #show' do
expect(get('/gitlab/gitlabhq/-/security/configuration')).to route_to('projects/security/configuration#show', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
it 'to #auto_fix' do
expect(post('/gitlab/gitlabhq/-/security/configuration/auto_fix')).to route_to('projects/security/configuration#auto_fix', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
end
...@@ -93,6 +93,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -93,6 +93,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
allow(view).to receive(:can?).with(nil, :read_dependencies, project).and_return(can_read_dependencies) allow(view).to receive(:can?).with(nil, :read_dependencies, project).and_return(can_read_dependencies)
allow(view).to receive(:can?).with(nil, :read_project_security_dashboard, project).and_return(can_read_dashboard) allow(view).to receive(:can?).with(nil, :read_project_security_dashboard, project).and_return(can_read_dashboard)
allow(view).to receive(:can?).with(nil, :read_project_audit_events, project).and_return(can_read_project_audit_events) allow(view).to receive(:can?).with(nil, :read_project_audit_events, project).and_return(can_read_project_audit_events)
allow(view).to receive(:can?).with(nil, :read_security_configuration, project).and_return(can_read_security_configuration)
allow(view).to receive(:can?).with(nil, :access_security_and_compliance, project).and_return(can_access_security_and_compliance) allow(view).to receive(:can?).with(nil, :access_security_and_compliance, project).and_return(can_access_security_and_compliance)
render render
...@@ -105,24 +106,25 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -105,24 +106,25 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
let(:can_read_dashboard) { true } let(:can_read_dashboard) { true }
let(:can_read_dependencies) { true } let(:can_read_dependencies) { true }
let(:can_read_project_audit_events) { true } let(:can_read_project_audit_events) { true }
let(:can_read_security_configuration) { true }
it 'top level navigation link is visible' do it 'top level navigation link is not visible' do
expect(rendered).not_to have_link('Security & Compliance', href: project_security_dashboard_index_path(project)) expect(rendered).not_to have_link('Security & Compliance', href: project_security_dashboard_index_path(project))
end end
it 'security dashboard link is visible' do it 'security dashboard link is not visible' do
expect(rendered).not_to have_link('Security Dashboard', href: project_security_dashboard_index_path(project)) expect(rendered).not_to have_link('Security Dashboard', href: project_security_dashboard_index_path(project))
end end
it 'security configuration link is visible' do it 'security configuration link is not visible' do
expect(rendered).not_to have_link('Configuration', href: project_security_configuration_path(project)) expect(rendered).not_to have_link('Configuration', href: project_security_configuration_path(project))
end end
it 'dependency list link is visible' do it 'dependency list link is not visible' do
expect(rendered).not_to have_link('Dependency List', href: project_dependencies_path(project)) expect(rendered).not_to have_link('Dependency List', href: project_dependencies_path(project))
end end
it 'audit events link is visible' do it 'audit events link is not visible' do
expect(rendered).not_to have_link('Audit Events', href: project_audit_events_path(project)) expect(rendered).not_to have_link('Audit Events', href: project_audit_events_path(project))
end end
end end
...@@ -135,6 +137,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -135,6 +137,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
let(:can_read_dashboard) { true } let(:can_read_dashboard) { true }
let(:can_read_dependencies) { true } let(:can_read_dependencies) { true }
let(:can_read_project_audit_events) { true } let(:can_read_project_audit_events) { true }
let(:can_read_security_configuration) { true }
it 'top level navigation link is visible' do it 'top level navigation link is visible' do
expect(rendered).to have_link('Security & Compliance', href: project_security_dashboard_index_path(project)) expect(rendered).to have_link('Security & Compliance', href: project_security_dashboard_index_path(project))
...@@ -161,6 +164,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -161,6 +164,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
let(:can_read_dashboard) { true } let(:can_read_dashboard) { true }
let(:can_read_dependencies) { false } let(:can_read_dependencies) { false }
let(:can_read_project_audit_events) { false } let(:can_read_project_audit_events) { false }
let(:can_read_security_configuration) { true }
it 'top level navigation link is visible' do it 'top level navigation link is visible' do
expect(rendered).to have_link('Security & Compliance', href: project_security_dashboard_index_path(project)) expect(rendered).to have_link('Security & Compliance', href: project_security_dashboard_index_path(project))
...@@ -187,6 +191,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -187,6 +191,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
let(:can_read_dashboard) { false } let(:can_read_dashboard) { false }
let(:can_read_dependencies) { true } let(:can_read_dependencies) { true }
let(:can_read_project_audit_events) { false } let(:can_read_project_audit_events) { false }
let(:can_read_security_configuration) { false }
it 'top level navigation link is visible' do it 'top level navigation link is visible' do
expect(rendered).to have_link('Security & Compliance', href: project_dependencies_path(project)) expect(rendered).to have_link('Security & Compliance', href: project_dependencies_path(project))
...@@ -213,6 +218,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -213,6 +218,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
let(:can_read_dashboard) { false } let(:can_read_dashboard) { false }
let(:can_read_dependencies) { false } let(:can_read_dependencies) { false }
let(:can_read_project_audit_events) { true } let(:can_read_project_audit_events) { true }
let(:can_read_security_configuration) { false }
it 'top level navigation link is visible' do it 'top level navigation link is visible' do
expect(rendered).to have_link('Security & Compliance', href: project_audit_events_path(project)) expect(rendered).to have_link('Security & Compliance', href: project_audit_events_path(project))
...@@ -239,6 +245,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -239,6 +245,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
let(:can_read_dependencies) { false } let(:can_read_dependencies) { false }
let(:can_read_dashboard) { false } let(:can_read_dashboard) { false }
let(:can_read_project_audit_events) { false } let(:can_read_project_audit_events) { false }
let(:can_read_security_configuration) { false }
it 'top level navigation link is visible' do it 'top level navigation link is visible' do
expect(rendered).not_to have_link('Security & Compliance', href: project_security_dashboard_index_path(project)) expect(rendered).not_to have_link('Security & Compliance', href: project_security_dashboard_index_path(project))
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::Security::ConfigurationController do
let(:project) { create(:project, :public) }
let(:user) { create(:user) }
before do
sign_in(user)
end
describe 'GET show' do
context 'when feature flag is disabled' do
before do
stub_feature_flags(secure_security_and_compliance_configuration_page_on_ce: false)
end
it 'renders not found' do
get :show, params: { namespace_id: project.namespace, project_id: project }
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when feature flag is enabled' do
context 'when user has guest access' do
before do
project.add_guest(user)
end
it 'denies access' do
get :show, params: { namespace_id: project.namespace, project_id: project }
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when user has developer access' do
before do
project.add_developer(user)
end
it 'grants access' do
get :show, params: { namespace_id: project.namespace, project_id: project }
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:show)
end
end
end
end
end
...@@ -398,6 +398,45 @@ RSpec.describe ProjectsHelper do ...@@ -398,6 +398,45 @@ RSpec.describe ProjectsHelper do
helper.send(:get_project_nav_tabs, project, user) helper.send(:get_project_nav_tabs, project, user)
end end
context 'Security & Compliance tabs' do
before do
stub_feature_flags(secure_security_and_compliance_configuration_page_on_ce: feature_flag_enabled)
allow(helper).to receive(:can?).with(user, :read_security_configuration, project).and_return(can_read_security_configuration)
end
context 'when user cannot read security configuration' do
let(:can_read_security_configuration) { false }
context 'when feature flag is disabled' do
let(:feature_flag_enabled) { false }
it { is_expected.not_to include(:security_configuration) }
end
context 'when feature flag is enabled' do
let(:feature_flag_enabled) { true }
it { is_expected.not_to include(:security_configuration) }
end
end
context 'when user can read security configuration' do
let(:can_read_security_configuration) { true }
context 'when feature flag is disabled' do
let(:feature_flag_enabled) { false }
it { is_expected.not_to include(:security_configuration) }
end
context 'when feature flag is enabled' do
let(:feature_flag_enabled) { true }
it { is_expected.to include(:security_configuration) }
end
end
end
context 'when builds feature is enabled' do context 'when builds feature is enabled' do
before do before do
allow(project).to receive(:builds_enabled?).and_return(true) allow(project).to receive(:builds_enabled?).and_return(true)
......
...@@ -865,6 +865,28 @@ RSpec.describe ProjectPolicy do ...@@ -865,6 +865,28 @@ RSpec.describe ProjectPolicy do
end end
end end
context 'security configuration feature' do
%w(guest reporter).each do |role|
context role do
let(:current_user) { send(role) }
it 'prevents reading security configuration' do
expect_disallowed(:read_security_configuration)
end
end
end
%w(developer maintainer owner).each do |role|
context role do
let(:current_user) { send(role) }
it 'allows reading security configuration' do
expect_allowed(:read_security_configuration)
end
end
end
end
describe 'design permissions' do describe 'design permissions' do
let(:current_user) { guest } let(:current_user) { guest }
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::Security::ConfigurationController, 'routing' do
let(:base_params) { { namespace_id: 'gitlab', project_id: 'gitlabhq' } }
before do
allow(Project).to receive(:find_by_full_path).with('gitlab/gitlabhq', any_args).and_return(true)
end
it 'routes to #show' do
expect(get('/gitlab/gitlabhq/-/security/configuration')).to route_to('projects/security/configuration#show', namespace_id: 'gitlab', project_id: 'gitlabhq')
end
end
...@@ -18,7 +18,8 @@ RSpec.shared_context 'project navbar structure' do ...@@ -18,7 +18,8 @@ RSpec.shared_context 'project navbar structure' do
{ {
nav_item: _('Security & Compliance'), nav_item: _('Security & Compliance'),
nav_sub_items: [ nav_sub_items: [
_('Audit Events') _('Configuration'),
(_('Audit Events') if Gitlab.ee?)
] ]
} }
end end
...@@ -71,7 +72,7 @@ RSpec.shared_context 'project navbar structure' do ...@@ -71,7 +72,7 @@ RSpec.shared_context 'project navbar structure' do
_('Schedules') _('Schedules')
] ]
}, },
(security_and_compliance_nav_item if Gitlab.ee?), security_and_compliance_nav_item,
{ {
nav_item: _('Operations'), nav_item: _('Operations'),
nav_sub_items: [ nav_sub_items: [
...@@ -190,7 +191,7 @@ RSpec.shared_context 'group navbar structure' do ...@@ -190,7 +191,7 @@ RSpec.shared_context 'group navbar structure' do
nav_item: _('Merge Requests'), nav_item: _('Merge Requests'),
nav_sub_items: [] nav_sub_items: []
}, },
(security_and_compliance_nav_item if Gitlab.ee?), security_and_compliance_nav_item,
(push_rules_nav_item if Gitlab.ee?), (push_rules_nav_item if Gitlab.ee?),
{ {
nav_item: _('Kubernetes'), nav_item: _('Kubernetes'),
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'layouts/nav/sidebar/_project_security_link' do
let_it_be_with_reload(:project) { create(:project) }
context 'on security configuration' do
before do
assign(:project, project)
allow(controller).to receive(:controller_name).and_return('configuration')
allow(controller).to receive(:controller_path).and_return('projects/security/configuration')
allow(controller).to receive(:action_name).and_return('show')
allow(view).to receive(:any_project_nav_tab?).and_return(true)
allow(view).to receive(:project_nav_tab?).and_return(true)
end
it 'activates Security & Compliance tab' do
render
expect(rendered).to have_css('li.active', text: 'Security & Compliance')
end
it 'activates Configuration sub tab' do
render
expect(rendered).to have_css('.sidebar-sub-level-items > li.active', text: 'Configuration')
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