Commit 79a05e37 authored by Mark Florian's avatar Mark Florian Committed by Natalia Tepluhina

Begin laying framework for Threat Monitoring UI

Part of [WAF statistics reporting][1].

This sets up some of the frontend framework for the Threat Monitoring
page, including a basic store and the root app component.

As this feature is not yet complete, it is disabled by default behind
the `threat_monitoring` feature flag.

[1]: https://gitlab.com/gitlab-org/gitlab/issues/14707
parent a2aee704
import initThreatMonitoringApp from 'ee/threat_monitoring';
document.addEventListener('DOMContentLoaded', initThreatMonitoringApp);
<script>
import { mapActions } from 'vuex';
import { GlAlert, GlEmptyState, GlIcon, GlLink } from '@gitlab/ui';
import { s__ } from '~/locale';
export default {
name: 'ThreatMonitoring',
components: {
GlAlert,
GlEmptyState,
GlIcon,
GlLink,
},
props: {
isWafSetup: {
type: Boolean,
required: true,
},
endpoint: {
type: String,
required: true,
},
emptyStateSvgPath: {
type: String,
required: true,
},
documentationPath: {
type: String,
required: true,
},
},
data() {
return {
showAlert: true,
};
},
created() {
if (this.isWafSetup) {
this.setEndpoint(this.endpoint);
}
},
methods: {
...mapActions('threatMonitoring', ['setEndpoint']),
dismissAlert() {
this.showAlert = false;
},
},
emptyStateDescription: s__(
`ThreatMonitoring|A Web Application Firewall (WAF) provides monitoring and
rules to protect production applications. GitLab adds the modsecurity WAF
plug-in when you install the Ingress app in your Kubernetes cluster.`,
),
alertText: s__(
`ThreatMonitoring|The graph below is an overview of traffic coming to your
application. View the documentation for instructions on how to access the
WAF logs to see what type of malicious traffic is trying to access your
app.`,
),
};
</script>
<template>
<gl-empty-state
v-if="!isWafSetup"
:title="s__('ThreatMonitoring|Web Application Firewall not enabled')"
:description="$options.emptyStateDescription"
:svg-path="emptyStateSvgPath"
:primary-button-link="documentationPath"
:primary-button-text="__('Learn more')"
/>
<section v-else>
<gl-alert
v-if="showAlert"
class="my-3"
variant="tip"
:secondary-button-link="documentationPath"
:secondary-button-text="__('View documentation')"
@dismiss="dismissAlert"
>
{{ $options.alertText }}
</gl-alert>
<header class="my-3">
<h2 class="h4 mb-1">
{{ s__('ThreatMonitoring|Threat Monitoring') }}
<gl-link
target="_blank"
:href="documentationPath"
:aria-label="s__('ThreatMonitoring|Threat Monitoring help page link')"
>
<gl-icon name="question" />
</gl-link>
</h2>
</header>
</section>
</template>
import Vue from 'vue';
import ThreatMonitoringApp from './components/app.vue';
import createStore from './store';
import { parseBoolean } from '~/lib/utils/common_utils';
export default () => {
const el = document.querySelector('#js-threat-monitoring-app');
const { isWafSetup, endpoint, emptyStateSvgPath, documentationPath } = el.dataset;
const store = createStore();
return new Vue({
el,
store,
render(createElement) {
return createElement(ThreatMonitoringApp, {
props: {
isWafSetup: parseBoolean(isWafSetup),
endpoint,
emptyStateSvgPath,
documentationPath,
},
});
},
});
};
import Vue from 'vue';
import Vuex from 'vuex';
import threatMonitoring from './modules/threat_monitoring';
Vue.use(Vuex);
export default () =>
new Vuex.Store({
modules: {
threatMonitoring: threatMonitoring(),
},
});
import * as types from './mutation_types';
// eslint-disable-next-line import/prefer-default-export
export const setEndpoint = ({ commit }, endpoint) => commit(types.SET_ENDPOINT, endpoint);
import * as actions from './actions';
import mutations from './mutations';
import state from './state';
export default () => ({
namespaced: true,
actions,
mutations,
state,
});
// eslint-disable-next-line import/prefer-default-export
export const SET_ENDPOINT = 'SET_ENDPOINT';
import * as types from './mutation_types';
export default {
[types.SET_ENDPOINT](state, payload) {
state.endpoint = payload;
},
};
- breadcrumb_title _("Threat Monitoring")
- page_title _("Threat Monitoring")
- breadcrumb_title s_("ThreatMonitoring|Threat Monitoring")
- page_title s_("ThreatMonitoring|Threat Monitoring")
#js-threat-monitoring-app
#js-threat-monitoring-app{ data: { documentation_path: help_page_path('user/clusters/applications', anchor: 'web-application-firewall-modsecurity'),
empty_state_svg_path: image_path('illustrations/security-dashboard-empty-state.svg'),
is_waf_setup: 'false',
endpoint: 'dummy',
} }
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ThreatMonitoringApp component given the WAF is not set up shows only the empty state 1`] = `
<glemptystate-stub
description="A Web Application Firewall (WAF) provides monitoring and rules to protect production applications. GitLab adds the modsecurity WAF plug-in when you install the Ingress app in your Kubernetes cluster."
primarybuttonlink="/docs"
primarybuttontext="Learn more"
svgpath="/svgs"
title="Web Application Firewall not enabled"
/>
`;
exports[`ThreatMonitoringApp component given the WAF is set up dismissing the alert hides the alert 1`] = `
<section>
<!---->
<header
class="my-3"
>
<h2
class="h4 mb-1"
>
Threat Monitoring
<gllink-stub
aria-label="Threat Monitoring help page link"
href="/docs"
target="_blank"
>
<glicon-stub
name="question"
size="16"
/>
</gllink-stub>
</h2>
</header>
</section>
`;
exports[`ThreatMonitoringApp component given the WAF is set up shows the alert and header 1`] = `
<section>
<glalert-stub
class="my-3"
dismissible="true"
dismisslabel="Dismiss"
primarybuttonlink=""
primarybuttontext=""
secondarybuttonlink="/docs"
secondarybuttontext="View documentation"
title=""
variant="tip"
>
The graph below is an overview of traffic coming to your application. View the documentation for instructions on how to access the WAF logs to see what type of malicious traffic is trying to access your app.
</glalert-stub>
<header
class="my-3"
>
<h2
class="h4 mb-1"
>
Threat Monitoring
<gllink-stub
aria-label="Threat Monitoring help page link"
href="/docs"
target="_blank"
>
<glicon-stub
name="question"
size="16"
/>
</gllink-stub>
</h2>
</header>
</section>
`;
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlAlert } from '@gitlab/ui';
import { TEST_HOST } from 'helpers/test_constants';
import createStore from 'ee/threat_monitoring/store';
import ThreatMonitoringApp from 'ee/threat_monitoring/components/app.vue';
const localVue = createLocalVue();
const endpoint = TEST_HOST;
const emptyStateSvgPath = '/svgs';
const documentationPath = '/docs';
describe('ThreatMonitoringApp component', () => {
let store;
let wrapper;
const factory = propsData => {
store = createStore();
jest.spyOn(store, 'dispatch');
wrapper = shallowMount(ThreatMonitoringApp, {
localVue,
propsData,
store,
sync: false,
});
};
afterEach(() => {
wrapper.destroy();
});
describe('given the WAF is not set up', () => {
beforeEach(() => {
factory({
isWafSetup: false,
endpoint,
emptyStateSvgPath,
documentationPath,
});
});
it('does not dispatch any store actions', () => {
expect(store.dispatch).not.toHaveBeenCalled();
});
it('shows only the empty state', () => {
expect(wrapper.element).toMatchSnapshot();
});
});
describe('given the WAF is set up', () => {
beforeEach(() => {
factory({
isWafSetup: true,
endpoint,
emptyStateSvgPath,
documentationPath,
});
});
it('sets the endpoint on creation', () => {
expect(store.dispatch).toHaveBeenCalledWith('threatMonitoring/setEndpoint', endpoint);
});
it('shows the alert and header', () => {
expect(wrapper.element).toMatchSnapshot();
});
describe('dismissing the alert', () => {
beforeEach(() => {
wrapper.find(GlAlert).vm.$emit('dismiss');
});
it('hides the alert', () => {
expect(wrapper.element).toMatchSnapshot();
});
});
});
});
import testAction from 'helpers/vuex_action_helper';
import { TEST_HOST } from 'helpers/test_constants';
import * as actions from 'ee/threat_monitoring/store/modules/threat_monitoring/actions';
import * as types from 'ee/threat_monitoring/store/modules/threat_monitoring/mutation_types';
import getInitialState from 'ee/threat_monitoring/store/modules/threat_monitoring/state';
describe('Threat Monitoring actions', () => {
describe('setEndpoint', () => {
it('commits the SET_ENDPOINT mutation', () =>
testAction(
actions.setEndpoint,
TEST_HOST,
getInitialState(),
[
{
type: types.SET_ENDPOINT,
payload: TEST_HOST,
},
],
[],
));
});
});
import { TEST_HOST } from 'helpers/test_constants';
import * as types from 'ee/threat_monitoring/store/modules/threat_monitoring/mutation_types';
import mutations from 'ee/threat_monitoring/store/modules/threat_monitoring/mutations';
import getInitialState from 'ee/threat_monitoring/store/modules/threat_monitoring/state';
describe('Threat Monitoring mutations', () => {
let state;
beforeEach(() => {
state = getInitialState();
});
describe(types.SET_ENDPOINT, () => {
it('sets the endpoint', () => {
mutations[types.SET_ENDPOINT](state, TEST_HOST);
expect(state.endpoint).toBe(TEST_HOST);
});
});
});
......@@ -18257,6 +18257,21 @@ msgstr ""
msgid "Threat Monitoring"
msgstr ""
msgid "ThreatMonitoring|A Web Application Firewall (WAF) provides monitoring and rules to protect production applications. GitLab adds the modsecurity WAF plug-in when you install the Ingress app in your Kubernetes cluster."
msgstr ""
msgid "ThreatMonitoring|The graph below is an overview of traffic coming to your application. View the documentation for instructions on how to access the WAF logs to see what type of malicious traffic is trying to access your app."
msgstr ""
msgid "ThreatMonitoring|Threat Monitoring"
msgstr ""
msgid "ThreatMonitoring|Threat Monitoring help page link"
msgstr ""
msgid "ThreatMonitoring|Web Application Firewall not enabled"
msgstr ""
msgid "Thursday"
msgstr ""
......
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