Commit 28418db9 authored by Dheeraj Joshi's avatar Dheeraj Joshi Committed by Kushal Pandya

Add new fields to DAST Site Profile Form

  * Integrate Auth Component
  * Add excluded url & request header fields
  * Put the changes behind the FF
parent 18a0f49a
<script> <script>
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { GlAlert, GlButton, GlForm, GlFormGroup, GlFormInput, GlModal } from '@gitlab/ui'; import {
GlAlert,
GlButton,
GlForm,
GlFormGroup,
GlFormInput,
GlModal,
GlFormTextarea,
} from '@gitlab/ui';
import { initFormField } from 'ee/security_configuration/utils'; import { initFormField } from 'ee/security_configuration/utils';
import * as Sentry from '~/sentry/wrapper'; import * as Sentry from '~/sentry/wrapper';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import { redirectTo } from '~/lib/utils/url_utility'; import { redirectTo } from '~/lib/utils/url_utility';
import { serializeFormObject } from '~/lib/utils/forms'; import { serializeFormObject } from '~/lib/utils/forms';
import validation from '~/vue_shared/directives/validation'; import validation from '~/vue_shared/directives/validation';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import DastSiteAuthSection from './dast_site_auth_section.vue';
import dastSiteProfileCreateMutation from '../graphql/dast_site_profile_create.mutation.graphql'; import dastSiteProfileCreateMutation from '../graphql/dast_site_profile_create.mutation.graphql';
import dastSiteProfileUpdateMutation from '../graphql/dast_site_profile_update.mutation.graphql'; import dastSiteProfileUpdateMutation from '../graphql/dast_site_profile_update.mutation.graphql';
...@@ -19,10 +29,13 @@ export default { ...@@ -19,10 +29,13 @@ export default {
GlFormGroup, GlFormGroup,
GlFormInput, GlFormInput,
GlModal, GlModal,
GlFormTextarea,
DastSiteAuthSection,
}, },
directives: { directives: {
validation: validation(), validation: validation(),
}, },
mixins: [glFeatureFlagsMixin()],
props: { props: {
fullPath: { fullPath: {
type: String, type: String,
...@@ -39,7 +52,8 @@ export default { ...@@ -39,7 +52,8 @@ export default {
}, },
}, },
data() { data() {
const { name = '', targetUrl = '' } = this.siteProfile || {}; const { name = '', targetUrl = '', excludedUrls = '', requestHeaders = '' } =
this.siteProfile || {};
const form = { const form = {
state: false, state: false,
...@@ -47,11 +61,18 @@ export default { ...@@ -47,11 +61,18 @@ export default {
fields: { fields: {
profileName: initFormField({ value: name }), profileName: initFormField({ value: name }),
targetUrl: initFormField({ value: targetUrl }), targetUrl: initFormField({ value: targetUrl }),
excludedUrls: initFormField({ value: excludedUrls, required: false, skipValidation: true }),
requestHeaders: initFormField({
value: requestHeaders,
required: false,
skipValidation: true,
}),
}, },
}; };
return { return {
form, form,
authSection: {},
initialFormValues: serializeFormObject(form.fields), initialFormValues: serializeFormObject(form.fields),
isLoading: false, isLoading: false,
hasAlert: false, hasAlert: false,
...@@ -94,9 +115,13 @@ export default { ...@@ -94,9 +115,13 @@ export default {
}, },
methods: { methods: {
onSubmit() { onSubmit() {
const isAuthEnabled =
this.glFeatures.securityDastSiteProfilesAdditionalFields &&
this.authSection.fields.authEnabled.value;
this.form.showValidation = true; this.form.showValidation = true;
if (!this.form.state) { if (!this.form.state || (isAuthEnabled && !this.authSection.state)) {
return; return;
} }
...@@ -109,6 +134,7 @@ export default { ...@@ -109,6 +134,7 @@ export default {
fullPath: this.fullPath, fullPath: this.fullPath,
...(this.isEdit ? { id: this.siteProfile.id } : {}), ...(this.isEdit ? { id: this.siteProfile.id } : {}),
...serializeFormObject(this.form.fields), ...serializeFormObject(this.form.fields),
...(isAuthEnabled ? serializeFormObject(this.authSection.fields) : {}),
}, },
}; };
...@@ -200,7 +226,7 @@ export default { ...@@ -200,7 +226,7 @@ export default {
/> />
</gl-form-group> </gl-form-group>
<hr /> <hr class="gl-border-gray-100" />
<gl-form-group <gl-form-group
data-testid="target-url-input-group" data-testid="target-url-input-group"
...@@ -219,9 +245,38 @@ export default { ...@@ -219,9 +245,38 @@ export default {
/> />
</gl-form-group> </gl-form-group>
<hr /> <div v-if="glFeatures.securityDastSiteProfilesAdditionalFields" class="row">
<gl-form-group
:label="s__('DastProfiles|Excluded URLs (Optional)')"
:invalid-feedback="form.fields.excludedUrls.feedback"
class="col-md-6"
>
<gl-form-textarea
v-model="form.fields.excludedUrls.value"
data-testid="excluded-urls-input"
/>
</gl-form-group>
<gl-form-group
:label="s__('DastProfiles|Additional request headers (Optional)')"
:invalid-feedback="form.fields.requestHeaders.feedback"
class="col-md-6"
>
<gl-form-textarea
v-model="form.fields.requestHeaders.value"
data-testid="request-headers-input"
/>
</gl-form-group>
</div>
<dast-site-auth-section
v-if="glFeatures.securityDastSiteProfilesAdditionalFields"
v-model="authSection"
:show-validation="form.showValidation"
/>
<hr class="gl-border-gray-100" />
<div class="gl-mt-6 gl-pt-6">
<gl-button <gl-button
type="submit" type="submit"
variant="success" variant="success"
...@@ -231,10 +286,13 @@ export default { ...@@ -231,10 +286,13 @@ export default {
> >
{{ s__('DastProfiles|Save profile') }} {{ s__('DastProfiles|Save profile') }}
</gl-button> </gl-button>
<gl-button data-testid="dast-site-profile-form-cancel-button" @click="onCancelClicked"> <gl-button
class="gl-ml-2"
data-testid="dast-site-profile-form-cancel-button"
@click="onCancelClicked"
>
{{ __('Cancel') }} {{ __('Cancel') }}
</gl-button> </gl-button>
</div>
<gl-modal <gl-modal
:ref="$options.modalId" :ref="$options.modalId"
......
...@@ -3,7 +3,10 @@ ...@@ -3,7 +3,10 @@
module Projects module Projects
module Security module Security
class DastSiteProfilesController < Projects::ApplicationController class DastSiteProfilesController < Projects::ApplicationController
before_action :authorize_read_on_demand_scans! before_action do
authorize_read_on_demand_scans!
push_frontend_feature_flag(:security_dast_site_profiles_additional_fields, @project, default_enabled: :yaml)
end
feature_category :dynamic_application_security_testing feature_category :dynamic_application_security_testing
......
...@@ -5,6 +5,7 @@ import merge from 'lodash/merge'; ...@@ -5,6 +5,7 @@ import merge from 'lodash/merge';
import { createMockClient } from 'mock-apollo-client'; import { createMockClient } from 'mock-apollo-client';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import DastSiteProfileForm from 'ee/security_configuration/dast_site_profiles_form/components/dast_site_profile_form.vue'; import DastSiteProfileForm from 'ee/security_configuration/dast_site_profiles_form/components/dast_site_profile_form.vue';
import DastSiteAuthSection from 'ee/security_configuration/dast_site_profiles_form/components/dast_site_auth_section.vue';
import dastSiteProfileCreateMutation from 'ee/security_configuration/dast_site_profiles_form/graphql/dast_site_profile_create.mutation.graphql'; import dastSiteProfileCreateMutation from 'ee/security_configuration/dast_site_profiles_form/graphql/dast_site_profile_create.mutation.graphql';
import dastSiteProfileUpdateMutation from 'ee/security_configuration/dast_site_profiles_form/graphql/dast_site_profile_update.mutation.graphql'; import dastSiteProfileUpdateMutation from 'ee/security_configuration/dast_site_profiles_form/graphql/dast_site_profile_update.mutation.graphql';
import { siteProfiles } from 'ee_jest/on_demand_scans/mocks/mock_data'; import { siteProfiles } from 'ee_jest/on_demand_scans/mocks/mock_data';
...@@ -21,6 +22,8 @@ const fullPath = 'group/project'; ...@@ -21,6 +22,8 @@ const fullPath = 'group/project';
const profilesLibraryPath = `${TEST_HOST}/${fullPath}/-/security/configuration/dast_profiles`; const profilesLibraryPath = `${TEST_HOST}/${fullPath}/-/security/configuration/dast_profiles`;
const profileName = 'My DAST site profile'; const profileName = 'My DAST site profile';
const targetUrl = 'http://example.com'; const targetUrl = 'http://example.com';
const excludedUrls = 'http://example.com/logout';
const requestHeaders = 'my-new-header=something';
const defaultProps = { const defaultProps = {
profilesLibraryPath, profilesLibraryPath,
...@@ -43,6 +46,9 @@ describe('DastSiteProfileForm', () => { ...@@ -43,6 +46,9 @@ describe('DastSiteProfileForm', () => {
const findByTestId = testId => wrapper.find(`[data-testid="${testId}"]`); const findByTestId = testId => wrapper.find(`[data-testid="${testId}"]`);
const findProfileNameInput = () => findByTestId('profile-name-input'); const findProfileNameInput = () => findByTestId('profile-name-input');
const findTargetUrlInput = () => findByTestId('target-url-input'); const findTargetUrlInput = () => findByTestId('target-url-input');
const findAuthSection = () => wrapper.find(DastSiteAuthSection);
const findExcludedUrlsInput = () => findByTestId('excluded-urls-input');
const findRequestHeadersInput = () => findByTestId('request-headers-input');
const findSubmitButton = () => findByTestId('dast-site-profile-form-submit-button'); const findSubmitButton = () => findByTestId('dast-site-profile-form-submit-button');
const findCancelButton = () => findByTestId('dast-site-profile-form-cancel-button'); const findCancelButton = () => findByTestId('dast-site-profile-form-cancel-button');
const findCancelModal = () => wrapper.find(GlModal); const findCancelModal = () => wrapper.find(GlModal);
...@@ -88,6 +94,11 @@ describe('DastSiteProfileForm', () => { ...@@ -88,6 +94,11 @@ describe('DastSiteProfileForm', () => {
{}, {},
{ {
propsData: defaultProps, propsData: defaultProps,
provide: {
glFeatures: {
securityDastSiteProfilesAdditionalFields: true,
},
},
}, },
options, options,
{ {
...@@ -132,6 +143,18 @@ describe('DastSiteProfileForm', () => { ...@@ -132,6 +143,18 @@ describe('DastSiteProfileForm', () => {
}); });
}); });
describe('additional fields', () => {
beforeEach(() => {
createFullComponent();
});
it('should render correctly', () => {
expect(findAuthSection().exists()).toBe(true);
expect(findExcludedUrlsInput().exists()).toBe(true);
expect(findRequestHeadersInput().exists()).toBe(true);
});
});
describe.each` describe.each`
title | siteProfile | mutationVars | mutationKind title | siteProfile | mutationVars | mutationKind
${'New site profile'} | ${null} | ${{}} | ${'dastSiteProfileCreate'} ${'New site profile'} | ${null} | ${{}} | ${'dastSiteProfileCreate'}
...@@ -159,6 +182,8 @@ describe('DastSiteProfileForm', () => { ...@@ -159,6 +182,8 @@ describe('DastSiteProfileForm', () => {
const fillAndSubmitForm = async () => { const fillAndSubmitForm = async () => {
await setFieldValue(findProfileNameInput(), profileName); await setFieldValue(findProfileNameInput(), profileName);
await setFieldValue(findTargetUrlInput(), targetUrl); await setFieldValue(findTargetUrlInput(), targetUrl);
await setFieldValue(findExcludedUrlsInput(), excludedUrls);
await setFieldValue(findRequestHeadersInput(), requestHeaders);
submitForm(); submitForm();
}; };
...@@ -176,6 +201,8 @@ describe('DastSiteProfileForm', () => { ...@@ -176,6 +201,8 @@ describe('DastSiteProfileForm', () => {
input: { input: {
profileName, profileName,
targetUrl, targetUrl,
excludedUrls,
requestHeaders,
fullPath, fullPath,
...mutationVars, ...mutationVars,
}, },
...@@ -266,4 +293,22 @@ describe('DastSiteProfileForm', () => { ...@@ -266,4 +293,22 @@ describe('DastSiteProfileForm', () => {
}); });
}); });
}); });
describe('when feature flag is off', () => {
beforeEach(() => {
createFullComponent({
provide: {
glFeatures: {
securityDastSiteProfilesAdditionalFields: false,
},
},
});
});
it('should not render additional fields', () => {
expect(findAuthSection().exists()).toBe(false);
expect(findExcludedUrlsInput().exists()).toBe(false);
expect(findRequestHeadersInput().exists()).toBe(false);
});
});
}); });
...@@ -8617,6 +8617,9 @@ msgstr "" ...@@ -8617,6 +8617,9 @@ msgstr ""
msgid "DastProfiles|Active" msgid "DastProfiles|Active"
msgstr "" msgstr ""
msgid "DastProfiles|Additional request headers (Optional)"
msgstr ""
msgid "DastProfiles|Are you sure you want to delete this profile?" msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr "" msgstr ""
...@@ -8686,6 +8689,9 @@ msgstr "" ...@@ -8686,6 +8689,9 @@ msgstr ""
msgid "DastProfiles|Excluded URLs" msgid "DastProfiles|Excluded URLs"
msgstr "" msgstr ""
msgid "DastProfiles|Excluded URLs (Optional)"
msgstr ""
msgid "DastProfiles|Hide debug messages" msgid "DastProfiles|Hide debug messages"
msgstr "" 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