Commit 29cd67c4 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'replace_checkbox_by_toggle_for_modsecurity' into 'master'

Replace checkbox by toggle for modsecurity setup

See merge request gitlab-org/gitlab!26720
parents 9fd19faf 994a31e8
...@@ -256,6 +256,7 @@ export default class Clusters { ...@@ -256,6 +256,7 @@ export default class Clusters {
eventHub.$on('uninstallApplication', data => this.uninstallApplication(data)); eventHub.$on('uninstallApplication', data => this.uninstallApplication(data));
eventHub.$on('setCrossplaneProviderStack', data => this.setCrossplaneProviderStack(data)); eventHub.$on('setCrossplaneProviderStack', data => this.setCrossplaneProviderStack(data));
eventHub.$on('setIngressModSecurityEnabled', data => this.setIngressModSecurityEnabled(data)); eventHub.$on('setIngressModSecurityEnabled', data => this.setIngressModSecurityEnabled(data));
eventHub.$on('resetIngressModSecurityEnabled', id => this.resetIngressModSecurityEnabled(id));
// Add event listener to all the banner close buttons // Add event listener to all the banner close buttons
this.addBannerCloseHandler(this.unreachableContainer, 'unreachable'); this.addBannerCloseHandler(this.unreachableContainer, 'unreachable');
this.addBannerCloseHandler(this.authenticationFailureContainer, 'authentication_failure'); this.addBannerCloseHandler(this.authenticationFailureContainer, 'authentication_failure');
...@@ -270,6 +271,7 @@ export default class Clusters { ...@@ -270,6 +271,7 @@ export default class Clusters {
eventHub.$off('setCrossplaneProviderStack'); eventHub.$off('setCrossplaneProviderStack');
eventHub.$off('uninstallApplication'); eventHub.$off('uninstallApplication');
eventHub.$off('setIngressModSecurityEnabled'); eventHub.$off('setIngressModSecurityEnabled');
eventHub.$off('resetIngressModSecurityEnabled');
} }
initPolling(method, successCallback, errorCallback) { initPolling(method, successCallback, errorCallback) {
...@@ -523,6 +525,10 @@ export default class Clusters { ...@@ -523,6 +525,10 @@ export default class Clusters {
this.store.updateAppProperty(id, 'modsecurity_enabled', modSecurityEnabled); this.store.updateAppProperty(id, 'modsecurity_enabled', modSecurityEnabled);
} }
resetIngressModSecurityEnabled(id) {
this.store.updateAppProperty(id, 'isEditingModSecurityEnabled', false);
}
destroy() { destroy() {
this.destroyed = true; this.destroyed = true;
......
...@@ -119,9 +119,6 @@ export default { ...@@ -119,9 +119,6 @@ export default {
ingressInstalled() { ingressInstalled() {
return this.applications.ingress.status === APPLICATION_STATUS.INSTALLED; return this.applications.ingress.status === APPLICATION_STATUS.INSTALLED;
}, },
ingressEnableModsecurity() {
return this.applications.ingress.modsecurity_enabled;
},
ingressExternalEndpoint() { ingressExternalEndpoint() {
return this.applications.ingress.externalIp || this.applications.ingress.externalHostname; return this.applications.ingress.externalIp || this.applications.ingress.externalHostname;
}, },
......
<script> <script>
import _ from 'lodash'; import _ from 'lodash';
import { __ } from '../../locale'; import { __ } from '../../locale';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import { APPLICATION_STATUS, INGRESS } from '~/clusters/constants'; import { APPLICATION_STATUS, INGRESS } from '~/clusters/constants';
import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui'; import { GlAlert, GlSprintf, GlLink, GlToggle, GlButton } from '@gitlab/ui';
import eventHub from '~/clusters/event_hub'; import eventHub from '~/clusters/event_hub';
import modSecurityLogo from 'images/cluster_app_logos/modsecurity.png';
const { UPDATING, UNINSTALLING } = APPLICATION_STATUS; const { UPDATING, UNINSTALLING, INSTALLING, INSTALLED, UPDATED } = APPLICATION_STATUS;
export default { export default {
title: 'ModSecurity Web Application Firewall',
modsecurityUrl: 'https://modsecurity.org/about.html',
components: { components: {
LoadingButton,
GlAlert, GlAlert,
GlSprintf, GlSprintf,
GlLink, GlLink,
GlToggle,
GlButton,
}, },
props: { props: {
ingress: { ingress: {
...@@ -26,6 +29,10 @@ export default { ...@@ -26,6 +29,10 @@ export default {
default: '', default: '',
}, },
}, },
data: () => ({
modSecurityLogo,
hasValueChanged: false,
}),
computed: { computed: {
modSecurityEnabled: { modSecurityEnabled: {
get() { get() {
...@@ -36,6 +43,11 @@ export default { ...@@ -36,6 +43,11 @@ export default {
id: INGRESS, id: INGRESS,
modSecurityEnabled: isEnabled, modSecurityEnabled: isEnabled,
}); });
if (this.hasValueChanged) {
this.resetStatus();
} else {
this.hasValueChanged = true;
}
}, },
}, },
ingressModSecurityDescription() { ingressModSecurityDescription() {
...@@ -45,13 +57,21 @@ export default { ...@@ -45,13 +57,21 @@ export default {
return [UPDATING].includes(this.ingress.status); return [UPDATING].includes(this.ingress.status);
}, },
saveButtonDisabled() { saveButtonDisabled() {
return [UNINSTALLING, UPDATING].includes(this.ingress.status); return [UNINSTALLING, UPDATING, INSTALLING].includes(this.ingress.status);
}, },
saveButtonLabel() { saveButtonLabel() {
return this.saving ? __('Saving') : __('Save changes'); return this.saving ? __('Saving') : __('Save changes');
}, },
ingressInstalled() { /**
return this.ingress.installed; * Returns true either when:
* - The application is getting updated.
* - The user has changed some of the settings for an application which is
* neither getting installed nor updated.
*/
showButtons() {
return (
this.saving || (this.hasValueChanged && [INSTALLED, UPDATED].includes(this.ingress.status))
);
}, },
}, },
methods: { methods: {
...@@ -60,6 +80,11 @@ export default { ...@@ -60,6 +80,11 @@ export default {
id: INGRESS, id: INGRESS,
params: { modsecurity_enabled: this.ingress.modsecurity_enabled }, params: { modsecurity_enabled: this.ingress.modsecurity_enabled },
}); });
this.resetStatus();
},
resetStatus() {
eventHub.$emit('resetIngressModSecurityEnabled', INGRESS);
this.hasValueChanged = false;
}, },
}, },
}; };
...@@ -75,42 +100,65 @@ export default { ...@@ -75,42 +100,65 @@ export default {
@dismiss="alert = null" @dismiss="alert = null"
> >
{{ {{
s__('ClusterIntegration|Something went wrong while updating the Web Application Firewall.') s__(
'ClusterIntegration|Something went wrong while trying to save your settings. Please try again.',
)
}} }}
</gl-alert> </gl-alert>
<div class="form-group"> <div class="gl-responsive-table-row-layout" role="row">
<div class="form-check form-check-inline"> <div class="table-section append-right-8 section-align-top" role="gridcell">
<input <img
v-model="modSecurityEnabled" :src="modSecurityLogo"
type="checkbox" :alt="`${$options.title} logo`"
autocomplete="off" class="cluster-application-logo avatar s40"
class="form-check-input"
/> />
<label class="form-check-label label-bold" for="ingress-enable-modsecurity">
{{ s__('ClusterIntegration|Enable Web Application Firewall') }}
</label>
</div> </div>
<p class="form-text text-muted"> <div class="table-section section-wrap" role="gridcell">
<strong> <strong>
<gl-sprintf <gl-link :href="$options.modsecurityUrl" target="_blank">{{ $options.title }} </gl-link>
:message="s__('ClusterIntegration|Learn more about %{linkStart}ModSecurity%{linkEnd}')"
>
<template #link="{ content }">
<gl-link :href="ingressModSecurityDescription" target="_blank"
>{{ content }}
</gl-link>
</template>
</gl-sprintf>
</strong> </strong>
</p> <div class="form-group">
<loading-button <p class="form-text text-muted">
v-if="ingressInstalled" <strong>
class="btn-success mt-1" <gl-sprintf
:loading="saving" :message="
:disabled="saveButtonDisabled" s__(
:label="saveButtonLabel" 'ClusterIntegration|Real-time web application monitoring, logging and access control. %{linkStart}More information%{linkEnd}',
@click="updateApplication" )
/> "
>
<template #link="{ content }">
<gl-link :href="ingressModSecurityDescription" target="_blank"
>{{ content }}
</gl-link>
</template>
</gl-sprintf>
</strong>
</p>
<div class="form-check form-check-inline mt-3">
<gl-toggle
v-model="modSecurityEnabled"
:label-on="__('Enabled')"
:label-off="__('Disabled')"
:disabled="saveButtonDisabled"
label-position="right"
/>
</div>
<div v-if="showButtons">
<gl-button
class="btn-success inline mr-1"
:loading="saving"
:disabled="saveButtonDisabled"
@click="updateApplication"
>
{{ saveButtonLabel }}
</gl-button>
<gl-button :disabled="saveButtonDisabled" @click="resetStatus">
{{ __('Cancel') }}
</gl-button>
</div>
</div>
</div>
</div> </div>
</div> </div>
</template> </template>
...@@ -211,9 +211,7 @@ export default class ClusterStore { ...@@ -211,9 +211,7 @@ export default class ClusterStore {
this.state.applications.ingress.externalIp = serverAppEntry.external_ip; this.state.applications.ingress.externalIp = serverAppEntry.external_ip;
this.state.applications.ingress.externalHostname = serverAppEntry.external_hostname; this.state.applications.ingress.externalHostname = serverAppEntry.external_hostname;
if (!this.state.applications.ingress.isEditingModSecurityEnabled) { if (!this.state.applications.ingress.isEditingModSecurityEnabled) {
this.state.applications.ingress.modsecurity_enabled = this.state.applications.ingress.modsecurity_enabled = serverAppEntry.modsecurity_enabled;
serverAppEntry.modsecurity_enabled ||
this.state.applications.ingress.modsecurity_enabled;
} }
} else if (appId === CERT_MANAGER) { } else if (appId === CERT_MANAGER) {
this.state.applications.cert_manager.email = this.state.applications.cert_manager.email =
......
...@@ -16,7 +16,7 @@ module Clusters ...@@ -16,7 +16,7 @@ module Clusters
include AfterCommitQueue include AfterCommitQueue
default_value_for :ingress_type, :nginx default_value_for :ingress_type, :nginx
default_value_for :modsecurity_enabled, false default_value_for :modsecurity_enabled, true
default_value_for :version, VERSION default_value_for :version, VERSION
enum ingress_type: { enum ingress_type: {
......
---
title: Replace checkbox by toggle for ModSecurity on Cluster App Page
merge_request: 26720
author:
type: changed
...@@ -4249,9 +4249,6 @@ msgstr "" ...@@ -4249,9 +4249,6 @@ msgstr ""
msgid "ClusterIntegration|Enable Cloud Run for Anthos" msgid "ClusterIntegration|Enable Cloud Run for Anthos"
msgstr "" msgstr ""
msgid "ClusterIntegration|Enable Web Application Firewall"
msgstr ""
msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster." msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
msgstr "" msgstr ""
...@@ -4429,9 +4426,6 @@ msgstr "" ...@@ -4429,9 +4426,6 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}." msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr "" msgstr ""
msgid "ClusterIntegration|Learn more about %{linkStart}ModSecurity%{linkEnd}"
msgstr ""
msgid "ClusterIntegration|Learn more about %{startLink}Regions %{externalLinkIcon}%{endLink}." msgid "ClusterIntegration|Learn more about %{startLink}Regions %{externalLinkIcon}%{endLink}."
msgstr "" msgstr ""
...@@ -4564,6 +4558,9 @@ msgstr "" ...@@ -4564,6 +4558,9 @@ msgstr ""
msgid "ClusterIntegration|Read our %{link_start}help page%{link_end} on Kubernetes cluster integration." msgid "ClusterIntegration|Read our %{link_start}help page%{link_end} on Kubernetes cluster integration."
msgstr "" msgstr ""
msgid "ClusterIntegration|Real-time web application monitoring, logging and access control. %{linkStart}More information%{linkEnd}"
msgstr ""
msgid "ClusterIntegration|Region" msgid "ClusterIntegration|Region"
msgstr "" msgstr ""
...@@ -4711,13 +4708,13 @@ msgstr "" ...@@ -4711,13 +4708,13 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}" msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr "" msgstr ""
msgid "ClusterIntegration|Something went wrong while uninstalling %{title}" msgid "ClusterIntegration|Something went wrong while trying to save your settings. Please try again."
msgstr "" msgstr ""
msgid "ClusterIntegration|Something went wrong while updating Knative domain name." msgid "ClusterIntegration|Something went wrong while uninstalling %{title}"
msgstr "" msgstr ""
msgid "ClusterIntegration|Something went wrong while updating the Web Application Firewall." msgid "ClusterIntegration|Something went wrong while updating Knative domain name."
msgstr "" msgstr ""
msgid "ClusterIntegration|Specifying a domain will allow you to use Auto Review Apps and Auto Deploy stages for %{auto_devops_start}Auto DevOps%{auto_devops_end}. The domain should have a wildcard DNS configured matching the domain." msgid "ClusterIntegration|Specifying a domain will allow you to use Auto Review Apps and Auto Deploy stages for %{auto_devops_start}Auto DevOps%{auto_devops_end}. The domain should have a wildcard DNS configured matching the domain."
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import IngressModsecuritySettings from '~/clusters/components/ingress_modsecurity_settings.vue'; import IngressModsecuritySettings from '~/clusters/components/ingress_modsecurity_settings.vue';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import { APPLICATION_STATUS, INGRESS } from '~/clusters/constants'; import { APPLICATION_STATUS, INGRESS } from '~/clusters/constants';
import { GlAlert } from '@gitlab/ui'; import { GlAlert, GlToggle } from '@gitlab/ui';
import eventHub from '~/clusters/event_hub'; import eventHub from '~/clusters/event_hub';
const { UPDATING } = APPLICATION_STATUS; const { UPDATING } = APPLICATION_STATUS;
...@@ -27,32 +26,55 @@ describe('IngressModsecuritySettings', () => { ...@@ -27,32 +26,55 @@ describe('IngressModsecuritySettings', () => {
}); });
}; };
const findSaveButton = () => wrapper.find(LoadingButton); const findSaveButton = () => wrapper.find('.btn-success');
const findModSecurityCheckbox = () => wrapper.find('input').element; const findCancelButton = () => wrapper.find('[variant="secondary"]');
const findModSecurityToggle = () => wrapper.find(GlToggle);
describe('when ingress is installed', () => { describe('when ingress is installed', () => {
beforeEach(() => { beforeEach(() => {
createComponent({ installed: true }); createComponent({ installed: true, status: 'installed' });
jest.spyOn(eventHub, '$emit'); jest.spyOn(eventHub, '$emit');
}); });
it('renders save button', () => { it('does not render save and cancel buttons', () => {
expect(findSaveButton().exists()).toBe(true); expect(findSaveButton().exists()).toBe(false);
expect(findModSecurityCheckbox().checked).toBe(false); expect(findCancelButton().exists()).toBe(false);
}); });
describe('and the save changes button is clicked', () => { describe('with toggle changed by the user', () => {
beforeEach(() => { beforeEach(() => {
findSaveButton().vm.$emit('click'); findModSecurityToggle().vm.$emit('change');
});
it('renders both save and cancel buttons', () => {
expect(findSaveButton().exists()).toBe(true);
expect(findCancelButton().exists()).toBe(true);
}); });
it('triggers save event and pass current modsecurity value', () => describe('and the save changes button is clicked', () => {
wrapper.vm.$nextTick().then(() => { beforeEach(() => {
findSaveButton().vm.$emit('click');
});
it('triggers save event and pass current modsecurity value', () => {
expect(eventHub.$emit).toHaveBeenCalledWith('updateApplication', { expect(eventHub.$emit).toHaveBeenCalledWith('updateApplication', {
id: INGRESS, id: INGRESS,
params: { modsecurity_enabled: false }, params: { modsecurity_enabled: false },
}); });
})); });
});
describe('and the cancel button is clicked', () => {
beforeEach(() => {
findCancelButton().vm.$emit('click');
});
it('triggers reset event and hides both cancel and save changes button', () => {
expect(eventHub.$emit).toHaveBeenCalledWith('resetIngressModSecurityEnabled', INGRESS);
expect(findSaveButton().exists()).toBe(false);
expect(findCancelButton().exists()).toBe(false);
});
});
}); });
it('triggers set event to be propagated with the current modsecurity value', () => { it('triggers set event to be propagated with the current modsecurity value', () => {
...@@ -79,7 +101,7 @@ describe('IngressModsecuritySettings', () => { ...@@ -79,7 +101,7 @@ describe('IngressModsecuritySettings', () => {
}); });
it('renders save button with "Saving" label', () => { it('renders save button with "Saving" label', () => {
expect(findSaveButton().props('label')).toBe('Saving'); expect(findSaveButton().text()).toBe('Saving');
}); });
}); });
...@@ -101,7 +123,7 @@ describe('IngressModsecuritySettings', () => { ...@@ -101,7 +123,7 @@ describe('IngressModsecuritySettings', () => {
it('does not render the save button', () => { it('does not render the save button', () => {
expect(findSaveButton().exists()).toBe(false); expect(findSaveButton().exists()).toBe(false);
expect(findModSecurityCheckbox().checked).toBe(false); expect(findModSecurityToggle().props('value')).toBe(false);
}); });
}); });
}); });
...@@ -20,6 +20,7 @@ const CLUSTERS_MOCK_DATA = { ...@@ -20,6 +20,7 @@ const CLUSTERS_MOCK_DATA = {
external_ip: null, external_ip: null,
external_hostname: null, external_hostname: null,
can_uninstall: false, can_uninstall: false,
modsecurity_enabled: false,
}, },
{ {
name: 'runner', name: 'runner',
......
...@@ -177,6 +177,7 @@ describe Clusters::Applications::Ingress do ...@@ -177,6 +177,7 @@ describe Clusters::Applications::Ingress do
context 'when modsecurity_enabled is disabled' do context 'when modsecurity_enabled is disabled' do
before do before do
allow(subject).to receive(:cluster).and_return(cluster) allow(subject).to receive(:cluster).and_return(cluster)
allow(subject).to receive(:modsecurity_enabled).and_return(false)
end end
it 'excludes modsecurity module enablement' do it 'excludes modsecurity module enablement' do
......
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