Commit aad07ccb authored by Alexander Turinske's avatar Alexander Turinske

Prevent changing of an existing policy's type

- users should not be able to switch an existing
  policy's type
- they should instead delete the policy and
  create a new one
- abstract out code
- add tests
parent 034d36ae
...@@ -20,3 +20,8 @@ export const COLORS = { ...@@ -20,3 +20,8 @@ export const COLORS = {
// otherwise they could get out of sync. // otherwise they could get out of sync.
// See https://gitlab.com/gitlab-org/gitlab-ui/issues/554. // See https://gitlab.com/gitlab-org/gitlab-ui/issues/554.
export { dateFormats as DATE_FORMATS } from 'ee/analytics/shared/constants'; export { dateFormats as DATE_FORMATS } from 'ee/analytics/shared/constants';
export const POLICY_KINDS = {
ciliumNetwork: 'CiliumNetworkPolicy',
scanExecution: 'scanner_profile',
};
...@@ -37,7 +37,7 @@ export default { ...@@ -37,7 +37,7 @@ export default {
<template> <template>
<base-policy :policy="policy"> <base-policy :policy="policy">
<template #type>{{ s__('NetworkPolicies|Network policy') }}</template> <template #type>{{ s__('NetworkPolicies|Network') }}</template>
<template #default="{ enforcementStatusLabel }"> <template #default="{ enforcementStatusLabel }">
<div v-if="policy"> <div v-if="policy">
......
<script> <script>
import { GlButton, GlDrawer } from '@gitlab/ui'; import { GlButton, GlDrawer } from '@gitlab/ui';
import { getContentWrapperHeight } from '../../utils'; import { getContentWrapperHeight, getPolicyKind } from '../../utils';
import { import { POLICY_KINDS } from '../constants';
CiliumNetworkPolicyKind,
ScanExecutionPolicyKind,
} from '../policy_editor/network_policy/lib';
import CiliumNetworkPolicy from './cilium_network_policy.vue'; import CiliumNetworkPolicy from './cilium_network_policy.vue';
import ScanExecutionPolicy from './scan_execution_policy.vue'; import ScanExecutionPolicy from './scan_execution_policy.vue';
const policyComponent = { const policyComponent = {
[CiliumNetworkPolicyKind]: CiliumNetworkPolicy, [POLICY_KINDS.ciliumNetwork]: CiliumNetworkPolicy,
[ScanExecutionPolicyKind]: ScanExecutionPolicy, [POLICY_KINDS.scanExecution]: ScanExecutionPolicy,
}; };
export default { export default {
...@@ -36,13 +33,7 @@ export default { ...@@ -36,13 +33,7 @@ export default {
}, },
computed: { computed: {
policyKind() { policyKind() {
if (this.policy?.manifest?.includes(CiliumNetworkPolicyKind)) { return getPolicyKind(this.policy);
return CiliumNetworkPolicyKind;
}
if (this.policy?.manifest?.includes(ScanExecutionPolicyKind)) {
return ScanExecutionPolicyKind;
}
return null;
}, },
policyComponent() { policyComponent() {
return policyComponent[this.policyKind] || null; return policyComponent[this.policyKind] || null;
......
...@@ -12,10 +12,10 @@ export const EDITOR_MODES = [ ...@@ -12,10 +12,10 @@ export const EDITOR_MODES = [
{ value: EDITOR_MODE_YAML, text: s__('NetworkPolicies|.yaml mode') }, { value: EDITOR_MODE_YAML, text: s__('NetworkPolicies|.yaml mode') },
]; ];
export const POLICY_TYPES = { export const POLICY_KIND_OPTIONS = {
networkPolicy: { network: {
value: 'networkPolicy', value: 'network',
text: s__('NetworkPolicies|Network Policy'), text: s__('NetworkPolicies|Network'),
component: 'network-policy-editor', component: 'network-policy-editor',
shouldShowEnvironmentPicker: true, shouldShowEnvironmentPicker: true,
}, },
...@@ -23,7 +23,6 @@ export const POLICY_TYPES = { ...@@ -23,7 +23,6 @@ export const POLICY_TYPES = {
value: 'scanExecution', value: 'scanExecution',
text: s__('NetworkPolicies|Scan Execution'), text: s__('NetworkPolicies|Scan Execution'),
component: 'scan-execution-policy-editor', component: 'scan-execution-policy-editor',
shouldShowMergeRequestButton: true,
}, },
}; };
......
...@@ -13,9 +13,6 @@ export const RuleTypeEntity = 'NetworkPolicyRuleEntity'; ...@@ -13,9 +13,6 @@ export const RuleTypeEntity = 'NetworkPolicyRuleEntity';
export const RuleTypeCIDR = 'NetworkPolicyRuleCIDR'; export const RuleTypeCIDR = 'NetworkPolicyRuleCIDR';
export const RuleTypeFQDN = 'NetworkPolicyRuleFQDN'; export const RuleTypeFQDN = 'NetworkPolicyRuleFQDN';
export const CiliumNetworkPolicyKind = 'CiliumNetworkPolicy';
export const ScanExecutionPolicyKind = 'scanner_profile';
export const EntityTypes = { export const EntityTypes = {
ALL: 'all', ALL: 'all',
HOST: 'host', HOST: 'host',
......
import { safeDump } from 'js-yaml'; import { safeDump } from 'js-yaml';
import { EndpointMatchModeAny, DisabledByLabel, CiliumNetworkPolicyKind } from './constants'; import { POLICY_KINDS } from 'ee/threat_monitoring/components/constants';
import { EndpointMatchModeAny, DisabledByLabel } from './constants';
import { ruleSpec } from './rules'; import { ruleSpec } from './rules';
import { labelSelector } from './utils'; import { labelSelector } from './utils';
...@@ -47,7 +48,7 @@ export default function toYaml(policy) { ...@@ -47,7 +48,7 @@ export default function toYaml(policy) {
const policySpec = { const policySpec = {
apiVersion: 'cilium.io/v2', apiVersion: 'cilium.io/v2',
kind: CiliumNetworkPolicyKind, kind: POLICY_KINDS.ciliumNetwork,
}; };
if (description?.length > 0) { if (description?.length > 0) {
......
...@@ -3,7 +3,7 @@ import { GlFormGroup, GlFormSelect } from '@gitlab/ui'; ...@@ -3,7 +3,7 @@ import { GlFormGroup, GlFormSelect } from '@gitlab/ui';
import { mapActions } from 'vuex'; import { mapActions } from 'vuex';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import EnvironmentPicker from '../environment_picker.vue'; import EnvironmentPicker from '../environment_picker.vue';
import { POLICY_TYPES } from './constants'; import { POLICY_KIND_OPTIONS } from './constants';
import NetworkPolicyEditor from './network_policy/network_policy_editor.vue'; import NetworkPolicyEditor from './network_policy/network_policy_editor.vue';
import ScanExecutionPolicyEditor from './scan_execution_policy/scan_execution_policy_editor.vue'; import ScanExecutionPolicyEditor from './scan_execution_policy/scan_execution_policy_editor.vue';
...@@ -25,18 +25,18 @@ export default { ...@@ -25,18 +25,18 @@ export default {
}, },
data() { data() {
return { return {
policyType: POLICY_TYPES.networkPolicy.value, policyType: POLICY_KIND_OPTIONS.network.value,
}; };
}, },
computed: { computed: {
policyComponent() { policyComponent() {
return POLICY_TYPES[this.policyType].component; return POLICY_KIND_OPTIONS[this.policyType].component;
}, },
shouldAllowPolicyTypeSelection() { shouldAllowPolicyTypeSelection() {
return this.glFeatures.scanExecutionPolicyUi; return !this.existingPolicy && this.glFeatures.scanExecutionPolicyUi;
}, },
shouldShowEnvironmentPicker() { shouldShowEnvironmentPicker() {
return POLICY_TYPES[this.policyType].shouldShowEnvironmentPicker; return POLICY_KIND_OPTIONS[this.policyType].shouldShowEnvironmentPicker;
}, },
}, },
created() { created() {
...@@ -48,7 +48,7 @@ export default { ...@@ -48,7 +48,7 @@ export default {
this.policyType = type; this.policyType = type;
}, },
}, },
policyTypes: Object.values(POLICY_TYPES), policyTypes: Object.values(POLICY_KIND_OPTIONS),
}; };
</script> </script>
......
...@@ -23,16 +23,12 @@ export default { ...@@ -23,16 +23,12 @@ export default {
}, },
}, },
data() { data() {
const policy = this.existingPolicy
? fromYaml(this.existingPolicy.manifest)
: fromYaml(DEFAULT_SCAN_EXECUTION_POLICY);
const yamlEditorValue = this.existingPolicy const yamlEditorValue = this.existingPolicy
? removeUnnecessaryDashes(this.existingPolicy.manifest) ? removeUnnecessaryDashes(this.existingPolicy.manifest)
: DEFAULT_SCAN_EXECUTION_POLICY; : DEFAULT_SCAN_EXECUTION_POLICY;
return { return {
policy, policy: fromYaml(yamlEditorValue),
yamlEditorValue, yamlEditorValue,
}; };
}, },
......
import { POLICY_KINDS } from './components/constants';
/** /**
* Get the height of the wrapper page element * Get the height of the wrapper page element
* This height can be used to determine where the highest element goes in a page * This height can be used to determine where the highest element goes in a page
...@@ -10,6 +12,21 @@ export const getContentWrapperHeight = (contentWrapperClass) => { ...@@ -10,6 +12,21 @@ export const getContentWrapperHeight = (contentWrapperClass) => {
return wrapperEl ? `${wrapperEl.offsetTop}px` : ''; return wrapperEl ? `${wrapperEl.offsetTop}px` : '';
}; };
/**
* Get a policy's type
* @param {Object} policy policy information including a manifest in yaml
* @returns {String|null} policy type if available
*/
export const getPolicyKind = (policy) => {
if (policy?.manifest?.includes(POLICY_KINDS.ciliumNetwork)) {
return POLICY_KINDS.ciliumNetwork;
}
if (policy?.manifest?.includes(POLICY_KINDS.scanExecution)) {
return POLICY_KINDS.scanExecution;
}
return null;
};
/** /**
* Removes inital line dashes from a policy YAML that is received from the API, which * Removes inital line dashes from a policy YAML that is received from the API, which
* is not required for the user. * is not required for the user.
......
...@@ -11,7 +11,7 @@ exports[`CiliumNetworkPolicy component supported YAML renders policy preview tab ...@@ -11,7 +11,7 @@ exports[`CiliumNetworkPolicy component supported YAML renders policy preview tab
<p <p
data-testid="policy-type" data-testid="policy-type"
> >
Network policy Network
</p> </p>
<div> <div>
...@@ -58,7 +58,7 @@ exports[`CiliumNetworkPolicy component unsupported YAML renders policy preview t ...@@ -58,7 +58,7 @@ exports[`CiliumNetworkPolicy component unsupported YAML renders policy preview t
<p <p
data-testid="policy-type" data-testid="policy-type"
> >
Network policy Network
</p> </p>
<!----> <!---->
......
import { GlFormSelect } from '@gitlab/ui'; import { GlFormSelect } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import EnvironmentPicker from 'ee/threat_monitoring/components/environment_picker.vue'; import EnvironmentPicker from 'ee/threat_monitoring/components/environment_picker.vue';
import { POLICY_TYPES } from 'ee/threat_monitoring/components/policy_editor/constants'; import { POLICY_KIND_OPTIONS } from 'ee/threat_monitoring/components/policy_editor/constants';
import NetworkPolicyEditor from 'ee/threat_monitoring/components/policy_editor/network_policy/network_policy_editor.vue'; import NetworkPolicyEditor from 'ee/threat_monitoring/components/policy_editor/network_policy/network_policy_editor.vue';
import PolicyEditor from 'ee/threat_monitoring/components/policy_editor/policy_editor.vue'; import PolicyEditor from 'ee/threat_monitoring/components/policy_editor/policy_editor.vue';
import createStore from 'ee/threat_monitoring/store'; import createStore from 'ee/threat_monitoring/store';
import { mockL3Manifest } from '../../mocks/mock_data';
describe('PolicyEditor component', () => { describe('PolicyEditor component', () => {
let store; let store;
...@@ -20,11 +21,7 @@ describe('PolicyEditor component', () => { ...@@ -20,11 +21,7 @@ describe('PolicyEditor component', () => {
jest.spyOn(store, 'dispatch').mockImplementation(() => Promise.resolve()); jest.spyOn(store, 'dispatch').mockImplementation(() => Promise.resolve());
wrapper = shallowMount(PolicyEditor, { wrapper = shallowMount(PolicyEditor, {
propsData: { propsData,
threatMonitoringPath: '/threat-monitoring',
projectId: '21',
...propsData,
},
provide, provide,
store, store,
stubs: { GlFormSelect }, stubs: { GlFormSelect },
...@@ -42,10 +39,10 @@ describe('PolicyEditor component', () => { ...@@ -42,10 +39,10 @@ describe('PolicyEditor component', () => {
expect(findEnvironmentPicker().exists()).toBe(true); expect(findEnvironmentPicker().exists()).toBe(true);
}); });
it('renders the form select', () => { it('renders the disabled form select', () => {
const formSelect = findFormSelect(); const formSelect = findFormSelect();
expect(formSelect.exists()).toBe(true); expect(formSelect.exists()).toBe(true);
expect(formSelect.attributes('value')).toBe(POLICY_TYPES.networkPolicy.value); expect(formSelect.attributes('value')).toBe(POLICY_KIND_OPTIONS.network.value);
expect(formSelect.attributes('disabled')).toBe('true'); expect(formSelect.attributes('disabled')).toBe('true');
}); });
...@@ -54,6 +51,19 @@ describe('PolicyEditor component', () => { ...@@ -54,6 +51,19 @@ describe('PolicyEditor component', () => {
}); });
}); });
describe('when an existing policy is present', () => {
beforeEach(() => {
factory({ propsData: { existingPolicy: { manifest: mockL3Manifest } } });
});
it('renders the disabled form select', () => {
const formSelect = findFormSelect();
expect(formSelect.exists()).toBe(true);
expect(formSelect.attributes('value')).toBe(POLICY_KIND_OPTIONS.network.value);
expect(formSelect.attributes('disabled')).toBe('true');
});
});
describe('with "scanExecutionPolicyUi" feature flag enabled', () => { describe('with "scanExecutionPolicyUi" feature flag enabled', () => {
beforeEach(() => { beforeEach(() => {
factory({ provide: { glFeatures: { scanExecutionPolicyUi: true } } }); factory({ provide: { glFeatures: { scanExecutionPolicyUi: true } } });
......
...@@ -182,6 +182,20 @@ export const mockAlertDetails = { ...@@ -182,6 +182,20 @@ export const mockAlertDetails = {
monitorTool: 'Cilium', monitorTool: 'Cilium',
}; };
export const mockDastScanExecutionManifest = `type: scan_execution_policy
name: 'Test Dast'
description: 'This is a good test'
enabled: false
rules:
- type: pipeline
branches:
- main
actions:
- scan: dast
site_profile: 'required_site_profile'
scanner_profile: 'required_scanner_profile'
`;
export const mockL7Manifest = `apiVersion: cilium.io/v2 export const mockL7Manifest = `apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy kind: CiliumNetworkPolicy
metadata: metadata:
......
import { getContentWrapperHeight, removeUnnecessaryDashes } from 'ee/threat_monitoring/utils'; import { POLICY_KINDS } from 'ee/threat_monitoring/components/constants';
import {
getContentWrapperHeight,
getPolicyKind,
removeUnnecessaryDashes,
} from 'ee/threat_monitoring/utils';
import { setHTMLFixture } from 'helpers/fixtures'; import { setHTMLFixture } from 'helpers/fixtures';
import { mockL3Manifest, mockDastScanExecutionManifest } from './mocks/mock_data';
describe('Threat Monitoring Utils', () => { describe('Threat Monitoring Utils', () => {
describe('getContentWrapperHeight', () => { describe('getContentWrapperHeight', () => {
...@@ -24,6 +30,19 @@ describe('Threat Monitoring Utils', () => { ...@@ -24,6 +30,19 @@ describe('Threat Monitoring Utils', () => {
}); });
}); });
describe('getPolicyKind', () => {
it.each`
input | output
${{}} | ${null}
${{ manifest: '' }} | ${null}
${{ manifest: 'ciliumNetworkPolicy' }} | ${null}
${{ manifest: mockL3Manifest }} | ${POLICY_KINDS.ciliumNetwork}
${{ manifest: mockDastScanExecutionManifest }} | ${POLICY_KINDS.scanExecution}
`('returns $output when used on $input', ({ input, output }) => {
expect(getPolicyKind(input)).toBe(output);
});
});
describe('removeUnnecessaryDashes', () => { describe('removeUnnecessaryDashes', () => {
it.each` it.each`
input | output input | output
......
...@@ -21758,10 +21758,7 @@ msgstr "" ...@@ -21758,10 +21758,7 @@ msgstr ""
msgid "NetworkPolicies|Namespace" msgid "NetworkPolicies|Namespace"
msgstr "" msgstr ""
msgid "NetworkPolicies|Network Policy" msgid "NetworkPolicies|Network"
msgstr ""
msgid "NetworkPolicies|Network policy"
msgstr "" msgstr ""
msgid "NetworkPolicies|Network traffic" msgid "NetworkPolicies|Network traffic"
......
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