Commit b4ea0859 authored by Phil Hughes's avatar Phil Hughes

Merge branch 'jnnkl-security-config-redesign-ee-baseline' into 'master'

Bring redesigned security-configuration page to EE

See merge request gitlab-org/gitlab!65171
parents 384861b8 66e23443
import { initStaticSecurityConfiguration } from '~/security_configuration';
import { initCESecurityConfiguration } from '~/security_configuration';
initStaticSecurityConfiguration(document.querySelector('#js-security-configuration-static'));
initCESecurityConfiguration(document.querySelector('#js-security-configuration-static'));
......@@ -112,6 +112,7 @@ export default {
<feature-card
v-for="feature in augmentedSecurityFeatures"
:key="feature.type"
data-testid="security-testing-card"
:feature="feature"
class="gl-mb-6"
/>
......
......@@ -7,11 +7,7 @@ import { securityFeatures, complianceFeatures } from './components/constants';
import RedesignedSecurityConfigurationApp from './components/redesigned_app.vue';
import { augmentFeatures } from './utils';
export const initStaticSecurityConfiguration = (el) => {
if (!el) {
return null;
}
export const initRedesignedSecurityConfiguration = (el) => {
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
......@@ -26,33 +22,50 @@ export const initStaticSecurityConfiguration = (el) => {
gitlabCiHistoryPath,
} = el.dataset;
if (gon.features.securityConfigurationRedesign) {
const { augmentedSecurityFeatures, augmentedComplianceFeatures } = augmentFeatures(
securityFeatures,
complianceFeatures,
features ? JSON.parse(features) : [],
);
const { augmentedSecurityFeatures, augmentedComplianceFeatures } = augmentFeatures(
securityFeatures,
complianceFeatures,
features ? JSON.parse(features) : [],
);
return new Vue({
el,
apolloProvider,
provide: {
projectPath,
upgradePath,
},
render(createElement) {
return createElement(RedesignedSecurityConfigurationApp, {
props: {
augmentedComplianceFeatures,
augmentedSecurityFeatures,
latestPipelinePath,
gitlabCiHistoryPath,
...parseBooleanDataAttributes(el, ['gitlabCiPresent']),
},
});
},
});
};
export const initCESecurityConfiguration = (el) => {
if (!el) {
return null;
}
return new Vue({
el,
apolloProvider,
provide: {
projectPath,
upgradePath,
},
render(createElement) {
return createElement(RedesignedSecurityConfigurationApp, {
props: {
augmentedComplianceFeatures,
augmentedSecurityFeatures,
latestPipelinePath,
gitlabCiHistoryPath,
...parseBooleanDataAttributes(el, ['gitlabCiPresent']),
},
});
},
});
if (gon.features?.securityConfigurationRedesign) {
return initRedesignedSecurityConfiguration(el);
}
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
});
const { projectPath, upgradePath } = el.dataset;
return new Vue({
el,
apolloProvider,
......
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
export const augmentFeatures = (securityFeatures, complianceFeatures, features = []) => {
const featuresByType = features.reduce((acc, feature) => {
acc[feature.type] = feature;
acc[feature.type] = convertObjectPropsToCamelCase(feature, { deep: true });
return acc;
}, {});
......
......@@ -74,6 +74,7 @@ export default {
<template>
<gl-button
v-if="!feature.configured"
data-testid="configure-via-mr-button"
:loading="isLoading"
:variant="variant"
:category="category"
......
- breadcrumb_title _("Security Configuration")
- page_title _("Security Configuration")
- redesign_enabled = ::Feature.enabled?(:security_configuration_redesign, default_enabled: :yaml)
- redesign_enabled = ::Feature.enabled?(:security_configuration_redesign, @project, default_enabled: :yaml)
- @content_class = "limit-container-width" unless fluid_layout || !redesign_enabled
#js-security-configuration-static{ data: { project_path: @project.full_path, upgrade_path: security_upgrade_path } }
---
name: security_configuration_redesign_ee
introduced_by_url:
rollout_issue_url:
milestone: '14.1'
type: development
group: group::analyzer frontend
default_enabled: false
......@@ -28,10 +28,15 @@ For each security control the page displays:
## UI redesign
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/326926) in 14.0 for GitLab Free and Premium, behind a feature flag, disabled by default.
> - Enabled on GitLab.com.
> - Enabled on GitLab.com for Free & Premium.
> - Recommended for production use.
> - It can be enabled or disabled for a single project.
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-ui-redesign). **(FREE SELF)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/333109) in 14.1 for GitLab Ultimate, behind a feature flag, disabled by default.
> - Disabled on GitLab.com.
> - Not recommended for production use.
> - It can be enabled or disabled for a single project.
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-ui-redesign-for-ultimate). **(ULTIMATE SELF)**
WARNING:
This feature might not be available to you. Check the **version history** note above for details.
......@@ -98,3 +103,28 @@ Feature.disable(:security_configuration_redesign)
# For a single project
Feature.disable(:security_configuration_redesign, Project.find(<project id>))
```
## Enable or disable UI redesign for Ultimate **(ULTIMATE SELF)**
The Security Configuration redesign is under development, and is not ready for
production use. It is deployed behind a feature flag that is **disabled by
default**.
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md) can enable it.
To enable it:
```ruby
# For the instance
Feature.enable(:security_configuration_redesign_ee)
# For a single project
Feature.enable(:security_configuration_redesign_ee, Project.find(<project id>))
```
To disable it:
```ruby
# For the instance
Feature.disable(:security_configuration_redesign_ee)
# For a single project
Feature.disable(:security_configuration_redesign_ee, Project.find(<project id>))
```
import { initSecurityConfiguration } from 'ee/security_configuration';
import { initStaticSecurityConfiguration } from '~/security_configuration';
import { initCESecurityConfiguration } from '~/security_configuration';
const el = document.querySelector('#js-security-configuration');
if (el) {
initSecurityConfiguration(el);
} else {
initStaticSecurityConfiguration(document.querySelector('#js-security-configuration-static'));
initCESecurityConfiguration(document.querySelector('#js-security-configuration-static'));
}
import Vue from 'vue';
import { parseBooleanDataAttributes } from '~/lib/utils/dom_utils';
import { initRedesignedSecurityConfiguration } from '~/security_configuration';
import SecurityConfigurationApp from './components/app.vue';
export const initSecurityConfiguration = (el) => {
......@@ -7,6 +8,10 @@ export const initSecurityConfiguration = (el) => {
return null;
}
if (gon.features?.securityConfigurationRedesignEE) {
return initRedesignedSecurityConfiguration(el);
}
const {
autoDevopsHelpPagePath,
autoDevopsPath,
......
......@@ -24,6 +24,10 @@ module EE
end
feature_category :static_application_security_testing
before_action only: [:show] do
push_frontend_feature_flag(:security_configuration_redesign_ee, project, default_enabled: :yaml)
end
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
......
......@@ -75,7 +75,8 @@ module Projects
{
type: type,
configured: configured,
configuration_path: configuration_path(type)
configuration_path: configuration_path(type),
available: feature_available(type)
}
end
......@@ -95,6 +96,13 @@ module Projects
api_fuzzing: project_security_configuration_api_fuzzing_path(project)
}[type]
end
def feature_available(type)
# SAST and Secret Detection are always available, but this isn't
# reflected by our license model yet.
# TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/333113
%w[sast secret_detection].include?(type) || project.licensed_feature_available?(type)
end
end
end
end
- breadcrumb_title _("Security Configuration")
- page_title _("Security Configuration")
- redesign_enabled = ::Feature.enabled?(:security_configuration_redesign_ee, @project, default_enabled: :yaml)
- @content_class = "limit-container-width" unless fluid_layout || !redesign_enabled
- if @configuration.nil?
= render_ce 'projects/security/configuration/show'
......
......@@ -15,9 +15,72 @@ RSpec.describe 'User sees Security Configuration table', :js do
sign_in(user)
end
context 'with security_dashboard feature available' do
context 'with security_dashboard feature available and redesign feature flag on' do
before do
stub_licensed_features(security_dashboard: true, sast: true, dast: true)
end
context 'with no SAST report' do
it 'shows SAST is not enabled' do
visit(project_security_configuration_path(project))
within_sast_card do
expect(page).to have_text('SAST')
expect(page).to have_text('Not enabled')
expect(page).to have_link('Enable SAST')
end
end
end
context 'with SAST report' do
before do
create(:ci_build, :sast, pipeline: pipeline, status: 'success')
end
it 'shows SAST is enabled' do
visit(project_security_configuration_path(project))
within_sast_card do
expect(page).to have_text('SAST')
expect(page).to have_text('Enabled')
expect(page).to have_link('Configure SAST')
end
end
end
context 'with no DAST report' do
it 'shows DAST is not enabled' do
visit(project_security_configuration_path(project))
within_dast_card do
expect(page).to have_text('DAST')
expect(page).to have_text('Not enabled')
expect(page).to have_link('Enable DAST')
end
end
end
context 'with DAST report' do
before do
create(:ci_build, :dast, pipeline: pipeline, status: 'success')
end
it 'shows DAST is enabled' do
visit(project_security_configuration_path(project))
within_dast_card do
expect(page).to have_text('DAST')
expect(page).to have_text('Enabled')
expect(page).to have_link('Configure DAST')
end
end
end
end
context 'with security_dashboard feature available and redesign feature flag off' do
before do
stub_licensed_features(security_dashboard: true)
stub_feature_flags(security_configuration_redesign_ee: false)
end
context 'with no SAST report' do
......@@ -27,7 +90,7 @@ RSpec.describe 'User sees Security Configuration table', :js do
within_sast_row do
expect(page).to have_text('SAST')
expect(page).to have_text('Not enabled')
expect(page).to have_css('[data-testid="enable-button"]')
expect(page).to have_link('Enable')
end
end
end
......@@ -43,7 +106,7 @@ RSpec.describe 'User sees Security Configuration table', :js do
within_sast_row do
expect(page).to have_text('SAST')
expect(page).to have_text('Enabled')
expect(page).to have_css('[data-testid="configure-button"]')
expect(page).to have_link('Configure')
end
end
end
......@@ -55,7 +118,7 @@ RSpec.describe 'User sees Security Configuration table', :js do
within_dast_row do
expect(page).to have_text('DAST')
expect(page).to have_text('Not enabled')
expect(page).to have_css('[data-testid="enable-button"]')
expect(page).to have_link('Enable')
end
end
end
......@@ -71,7 +134,7 @@ RSpec.describe 'User sees Security Configuration table', :js do
within_dast_row do
expect(page).to have_text('DAST')
expect(page).to have_text('Enabled')
expect(page).to have_css('[data-testid="configure-button"]')
expect(page).to have_link('Configure')
end
end
......@@ -97,4 +160,16 @@ RSpec.describe 'User sees Security Configuration table', :js do
yield
end
end
def within_sast_card
within '[data-testid="security-testing-card"]:nth-of-type(1)' do
yield
end
end
def within_dast_card
within '[data-testid="security-testing-card"]:nth-of-type(2)' do
yield
end
end
end
......@@ -17,6 +17,7 @@ RSpec.describe Projects::Security::ConfigurationPresenter do
before do
project.add_maintainer(current_user)
stub_licensed_features(licensed_scan_types.to_h { |type| [type, true] })
end
describe '#to_h' do
......@@ -265,7 +266,8 @@ RSpec.describe Projects::Security::ConfigurationPresenter do
{
"type" => type.to_s,
"configured" => configured,
"configuration_path" => configuration_path
"configuration_path" => configuration_path,
"available" => licensed_scan_types.include?(type)
}
end
......@@ -277,4 +279,8 @@ RSpec.describe Projects::Security::ConfigurationPresenter do
api_fuzzing: project_security_configuration_api_fuzzing_path(project)
}[type]
end
def licensed_scan_types
::Security::SecurityJobsFinder.allowed_job_types + ::Security::LicenseComplianceJobsFinder.allowed_job_types - [:cluster_image_scanning]
end
end
......@@ -35,7 +35,15 @@ const mockValidCustomFeature = [
{
name: 'SAST',
type: 'SAST',
customfield: 'customvalue',
customField: 'customvalue',
},
];
const mockValidCustomFeatureSnakeCase = [
{
name: 'SAST',
type: 'SAST',
custom_field: 'customvalue',
},
];
......@@ -79,3 +87,15 @@ describe('returns an object with augmentedSecurityFeatures and augmentedComplian
).toEqual(expectedOutputCustomFeature);
});
});
describe('returns an object with camelcased keys', () => {
it('given a customfeature in snakecase', () => {
expect(
augmentFeatures(
mockSecurityFeatures,
mockComplianceFeatures,
mockValidCustomFeatureSnakeCase,
),
).toEqual(expectedOutputCustomFeature);
});
});
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