Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/security_configuration')
-rw-r--r--app/assets/javascripts/security_configuration/components/app.vue32
-rw-r--r--app/assets/javascripts/security_configuration/components/auto_dev_ops_alert.vue1
-rw-r--r--app/assets/javascripts/security_configuration/components/constants.js2
-rw-r--r--app/assets/javascripts/security_configuration/components/training_provider_list.vue130
-rw-r--r--app/assets/javascripts/security_configuration/graphql/configure_security_training_providers.mutation.graphql9
-rw-r--r--app/assets/javascripts/security_configuration/index.js30
-rw-r--r--app/assets/javascripts/security_configuration/resolver.js56
7 files changed, 195 insertions, 65 deletions
diff --git a/app/assets/javascripts/security_configuration/components/app.vue b/app/assets/javascripts/security_configuration/components/app.vue
index 75d2b324623..d228f77f27d 100644
--- a/app/assets/javascripts/security_configuration/components/app.vue
+++ b/app/assets/javascripts/security_configuration/components/app.vue
@@ -27,6 +27,9 @@ export const i18n = {
securityConfiguration: __('Security Configuration'),
vulnerabilityManagement: s__('SecurityConfiguration|Vulnerability Management'),
securityTraining: s__('SecurityConfiguration|Security training'),
+ securityTrainingDescription: s__(
+ 'SecurityConfiguration|Enable security training to help your developers learn how to fix vulnerabilities. Developers can view security training from selected educational providers, relevant to the detected vulnerability.',
+ ),
};
export default {
@@ -160,8 +163,12 @@ export default {
</template>
</user-callout-dismisser>
- <gl-tabs content-class="gl-pt-0">
- <gl-tab data-testid="security-testing-tab" :title="$options.i18n.securityTesting">
+ <gl-tabs content-class="gl-pt-0" sync-active-tab-with-query-params lazy>
+ <gl-tab
+ data-testid="security-testing-tab"
+ :title="$options.i18n.securityTesting"
+ query-param-value="security-testing"
+ >
<auto-dev-ops-enabled-alert
v-if="shouldShowAutoDevopsEnabledAlert"
class="gl-mt-3"
@@ -185,9 +192,12 @@ export default {
{{ $options.i18n.description }}
</p>
<p v-if="canViewCiHistory">
- <gl-link data-testid="security-view-history-link" :href="gitlabCiHistoryPath">{{
- $options.i18n.configurationHistory
- }}</gl-link>
+ <gl-link
+ data-testid="security-view-history-link"
+ data-qa-selector="security_configuration_history_link"
+ :href="gitlabCiHistoryPath"
+ >{{ $options.i18n.configurationHistory }}</gl-link
+ >
</p>
</template>
@@ -203,7 +213,11 @@ export default {
</template>
</section-layout>
</gl-tab>
- <gl-tab data-testid="compliance-testing-tab" :title="$options.i18n.compliance">
+ <gl-tab
+ data-testid="compliance-testing-tab"
+ :title="$options.i18n.compliance"
+ query-param-value="compliance-testing"
+ >
<section-layout :heading="$options.i18n.compliance">
<template #description>
<p>
@@ -241,8 +255,14 @@ export default {
v-if="glFeatures.secureVulnerabilityTraining"
data-testid="vulnerability-management-tab"
:title="$options.i18n.vulnerabilityManagement"
+ query-param-value="vulnerability-management"
>
<section-layout :heading="$options.i18n.securityTraining">
+ <template #description>
+ <p>
+ {{ $options.i18n.securityTrainingDescription }}
+ </p>
+ </template>
<template #features>
<training-provider-list />
</template>
diff --git a/app/assets/javascripts/security_configuration/components/auto_dev_ops_alert.vue b/app/assets/javascripts/security_configuration/components/auto_dev_ops_alert.vue
index ce6a1b4888b..315f676e659 100644
--- a/app/assets/javascripts/security_configuration/components/auto_dev_ops_alert.vue
+++ b/app/assets/javascripts/security_configuration/components/auto_dev_ops_alert.vue
@@ -28,6 +28,7 @@ export default {
variant="info"
:primary-button-link="autoDevopsPath"
:primary-button-text="$options.i18n.primaryButtonText"
+ data-qa-selector="autodevops_container"
@dismiss="dismissMethod"
>
<gl-sprintf :message="$options.i18n.body">
diff --git a/app/assets/javascripts/security_configuration/components/constants.js b/app/assets/javascripts/security_configuration/components/constants.js
index dd8ba72ad1f..034dba29196 100644
--- a/app/assets/javascripts/security_configuration/components/constants.js
+++ b/app/assets/javascripts/security_configuration/components/constants.js
@@ -254,7 +254,7 @@ export const securityFeatures = [
helpPath: COVERAGE_FUZZING_HELP_PATH,
configurationHelpPath: COVERAGE_FUZZING_CONFIG_HELP_PATH,
type: REPORT_TYPE_COVERAGE_FUZZING,
- secondary: gon?.features?.corpusManagement
+ secondary: gon?.features?.corpusManagementUi
? {
type: REPORT_TYPE_CORPUS_MANAGEMENT,
name: CORPUS_MANAGEMENT_NAME,
diff --git a/app/assets/javascripts/security_configuration/components/training_provider_list.vue b/app/assets/javascripts/security_configuration/components/training_provider_list.vue
index 509377a63e8..ca4596e16b3 100644
--- a/app/assets/javascripts/security_configuration/components/training_provider_list.vue
+++ b/app/assets/javascripts/security_configuration/components/training_provider_list.vue
@@ -1,21 +1,39 @@
<script>
-import { GlCard, GlToggle, GlLink, GlSkeletonLoader } from '@gitlab/ui';
+import { GlAlert, GlCard, GlToggle, GlLink, GlSkeletonLoader } from '@gitlab/ui';
+import { __ } from '~/locale';
import securityTrainingProvidersQuery from '../graphql/security_training_providers.query.graphql';
+import configureSecurityTrainingProvidersMutation from '../graphql/configure_security_training_providers.mutation.graphql';
+
+const i18n = {
+ providerQueryErrorMessage: __(
+ 'Could not fetch training providers. Please refresh the page, or try again later.',
+ ),
+ configMutationErrorMessage: __(
+ 'Could not save configuration. Please refresh the page, or try again later.',
+ ),
+};
export default {
components: {
+ GlAlert,
GlCard,
GlToggle,
GlLink,
GlSkeletonLoader,
},
+ inject: ['projectPath'],
apollo: {
securityTrainingProviders: {
query: securityTrainingProvidersQuery,
+ error() {
+ this.errorMessage = this.$options.i18n.providerQueryErrorMessage;
+ },
},
},
data() {
return {
+ errorMessage: '',
+ toggleLoading: false,
securityTrainingProviders: [],
};
},
@@ -24,38 +42,92 @@ export default {
return this.$apollo.queries.securityTrainingProviders.loading;
},
},
+ methods: {
+ toggleProvider(selectedProviderId) {
+ const toggledProviders = this.securityTrainingProviders.map((provider) => ({
+ ...provider,
+ ...(provider.id === selectedProviderId && { isEnabled: !provider.isEnabled }),
+ }));
+
+ const enabledProviderIds = toggledProviders
+ .filter(({ isEnabled }) => isEnabled)
+ .map(({ id }) => id);
+
+ this.storeEnabledProviders(toggledProviders, enabledProviderIds);
+ },
+ async storeEnabledProviders(toggledProviders, enabledProviderIds) {
+ this.toggleLoading = true;
+
+ try {
+ const {
+ data: {
+ configureSecurityTrainingProviders: { errors = [] },
+ },
+ } = await this.$apollo.mutate({
+ mutation: configureSecurityTrainingProvidersMutation,
+ variables: {
+ input: {
+ enabledProviders: enabledProviderIds,
+ fullPath: this.projectPath,
+ },
+ },
+ });
+
+ if (errors.length > 0) {
+ // throwing an error here means we can handle scenarios within the `catch` block below
+ throw new Error();
+ }
+ } catch {
+ this.errorMessage = this.$options.i18n.configMutationErrorMessage;
+ } finally {
+ this.toggleLoading = false;
+ }
+ },
+ },
+ i18n,
};
</script>
<template>
- <div
- v-if="isLoading"
- class="gl-bg-white gl-py-6 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100"
- >
- <gl-skeleton-loader :width="350" :height="44">
- <rect width="200" height="8" x="10" y="0" rx="4" />
- <rect width="300" height="8" x="10" y="15" rx="4" />
- <rect width="100" height="8" x="10" y="35" rx="4" />
- </gl-skeleton-loader>
- </div>
- <ul v-else class="gl-list-style-none gl-m-0 gl-p-0">
- <li
- v-for="{ id, isEnabled, name, description, url } in securityTrainingProviders"
- :key="id"
- class="gl-mb-6"
+ <div>
+ <gl-alert v-if="errorMessage" variant="danger" :dismissible="false" class="gl-mb-6">
+ {{ errorMessage }}
+ </gl-alert>
+ <div
+ v-if="isLoading"
+ class="gl-bg-white gl-py-6 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100"
>
- <gl-card>
- <div class="gl-display-flex">
- <gl-toggle :value="isEnabled" :label="__('Training mode')" label-position="hidden" />
- <div class="gl-ml-5">
- <h3 class="gl-font-lg gl-m-0 gl-mb-2">{{ name }}</h3>
- <p>
- {{ description }}
- <gl-link :href="url" target="_blank">{{ __('Learn more.') }}</gl-link>
- </p>
+ <gl-skeleton-loader :width="350" :height="44">
+ <rect width="200" height="8" x="10" y="0" rx="4" />
+ <rect width="300" height="8" x="10" y="15" rx="4" />
+ <rect width="100" height="8" x="10" y="35" rx="4" />
+ </gl-skeleton-loader>
+ </div>
+ <ul v-else class="gl-list-style-none gl-m-0 gl-p-0">
+ <li
+ v-for="{ id, isEnabled, name, description, url } in securityTrainingProviders"
+ :key="id"
+ class="gl-mb-6"
+ >
+ <gl-card>
+ <div class="gl-display-flex">
+ <gl-toggle
+ :value="isEnabled"
+ :label="__('Training mode')"
+ label-position="hidden"
+ :is-loading="toggleLoading"
+ @change="toggleProvider(id)"
+ />
+ <div class="gl-ml-5">
+ <h3 class="gl-font-lg gl-m-0 gl-mb-2">{{ name }}</h3>
+ <p>
+ {{ description }}
+ <gl-link :href="url" target="_blank">{{ __('Learn more.') }}</gl-link>
+ </p>
+ </div>
</div>
- </div>
- </gl-card>
- </li>
- </ul>
+ </gl-card>
+ </li>
+ </ul>
+ </div>
</template>
diff --git a/app/assets/javascripts/security_configuration/graphql/configure_security_training_providers.mutation.graphql b/app/assets/javascripts/security_configuration/graphql/configure_security_training_providers.mutation.graphql
new file mode 100644
index 00000000000..660e0fadafb
--- /dev/null
+++ b/app/assets/javascripts/security_configuration/graphql/configure_security_training_providers.mutation.graphql
@@ -0,0 +1,9 @@
+mutation configureSecurityTrainingProviders($input: configureSecurityTrainingProvidersInput!) {
+ configureSecurityTrainingProviders(input: $input) @client {
+ errors
+ securityTrainingProviders {
+ id
+ isEnabled
+ }
+ }
+}
diff --git a/app/assets/javascripts/security_configuration/index.js b/app/assets/javascripts/security_configuration/index.js
index c86ff1a58f2..24c0585e077 100644
--- a/app/assets/javascripts/security_configuration/index.js
+++ b/app/assets/javascripts/security_configuration/index.js
@@ -2,38 +2,10 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import { parseBooleanDataAttributes } from '~/lib/utils/dom_utils';
-import { __ } from '~/locale';
import SecurityConfigurationApp from './components/app.vue';
import { securityFeatures, complianceFeatures } from './components/constants';
import { augmentFeatures } from './utils';
-
-// Note: this is behind a feature flag and only a placeholder
-// until the actual GraphQL fields have been added
-// https://gitlab.com/gitlab-org/gi tlab/-/issues/346480
-export const tempResolvers = {
- Query: {
- securityTrainingProviders() {
- return [
- {
- __typename: 'SecurityTrainingProvider',
- id: 101,
- name: __('Kontra'),
- description: __('Interactive developer security education.'),
- url: 'https://application.security/',
- isEnabled: false,
- },
- {
- __typename: 'SecurityTrainingProvider',
- id: 102,
- name: __('SecureCodeWarrior'),
- description: __('Security training with guide and learning pathways.'),
- url: 'https://www.securecodewarrior.com/',
- isEnabled: true,
- },
- ];
- },
- },
-};
+import tempResolvers from './resolver';
export const initSecurityConfiguration = (el) => {
if (!el) {
diff --git a/app/assets/javascripts/security_configuration/resolver.js b/app/assets/javascripts/security_configuration/resolver.js
new file mode 100644
index 00000000000..93175d4a3d1
--- /dev/null
+++ b/app/assets/javascripts/security_configuration/resolver.js
@@ -0,0 +1,56 @@
+import produce from 'immer';
+import { __ } from '~/locale';
+import securityTrainingProvidersQuery from './graphql/security_training_providers.query.graphql';
+
+// Note: this is behind a feature flag and only a placeholder
+// until the actual GraphQL fields have been added
+// https://gitlab.com/gitlab-org/gi tlab/-/issues/346480
+export default {
+ Query: {
+ securityTrainingProviders() {
+ return [
+ {
+ __typename: 'SecurityTrainingProvider',
+ id: 101,
+ name: __('Kontra'),
+ description: __('Interactive developer security education.'),
+ url: 'https://application.security/',
+ isEnabled: false,
+ },
+ {
+ __typename: 'SecurityTrainingProvider',
+ id: 102,
+ name: __('SecureCodeWarrior'),
+ description: __('Security training with guide and learning pathways.'),
+ url: 'https://www.securecodewarrior.com/',
+ isEnabled: true,
+ },
+ ];
+ },
+ },
+
+ Mutation: {
+ configureSecurityTrainingProviders: (
+ _,
+ { input: { enabledProviders, primaryProvider } },
+ { cache },
+ ) => {
+ const sourceData = cache.readQuery({
+ query: securityTrainingProvidersQuery,
+ });
+
+ const data = produce(sourceData.securityTrainingProviders, (draftData) => {
+ /* eslint-disable no-param-reassign */
+ draftData.forEach((provider) => {
+ provider.isPrimary = provider.id === primaryProvider;
+ provider.isEnabled =
+ provider.id === primaryProvider || enabledProviders.includes(provider.id);
+ });
+ });
+ return {
+ __typename: 'configureSecurityTrainingProvidersPayload',
+ securityTrainingProviders: data,
+ };
+ },
+ },
+};