Commit 6c8c0c0e authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'djadmin-edit-new-dast-site-profile' into 'master'

Add ability to edit DAST site profiles with new fields

See merge request gitlab-org/gitlab!51583
parents 8febfb1c 7291ce5a
<script>
import { GlFormGroup, GlFormInput, GlFormCheckbox } from '@gitlab/ui';
import { initFormField } from 'ee/security_configuration/utils';
import { __ } from '~/locale';
import validation from '~/vue_shared/directives/validation';
export default {
......@@ -13,7 +14,7 @@ export default {
validation: validation(),
},
props: {
fields: {
value: {
type: Object,
required: false,
default: () => ({}),
......@@ -26,32 +27,41 @@ export default {
},
data() {
const {
authEnabled,
authenticationUrl,
userName,
enabled,
url,
username,
password,
// default to commonly used names for `userName` and `password` fields in authentcation forms
userNameFormField = 'username',
passwordFormField = 'password',
} = this.fields;
// default to commonly used names for `username` and `password` fields in authentcation forms
usernameField = 'username',
passwordField = 'password',
} = this.value.fields;
const isEditMode = Object.keys(this.value.fields).length > 0;
return {
form: {
state: false,
fields: {
authEnabled: initFormField({ value: authEnabled, skipValidation: true }),
authenticationUrl: initFormField({ value: authenticationUrl }),
userName: initFormField({ value: userName }),
password: initFormField({ value: password }),
userNameFormField: initFormField({ value: userNameFormField }),
passwordFormField: initFormField({ value: passwordFormField }),
enabled: initFormField({ value: enabled, skipValidation: true }),
url: initFormField({ value: url }),
username: initFormField({ value: username }),
password: isEditMode
? initFormField({ value: password, required: false, skipValidation: true })
: initFormField({ value: password }),
usernameField: initFormField({ value: usernameField }),
passwordField: initFormField({ value: passwordField }),
},
},
isEditMode,
isSensitiveFieldRequired: !isEditMode,
};
},
computed: {
showValidationOrInEditMode() {
return this.showValidation || Object.keys(this.fields).length > 0;
return this.showValidation || this.isEditMode;
},
sensitiveFieldPlaceholder() {
return this.isEditMode ? __('[Unchanged]') : '';
},
},
watch: {
......@@ -68,41 +78,41 @@ export default {
<template>
<section>
<gl-form-group :label="s__('DastProfiles|Authentication')">
<gl-form-checkbox v-model="form.fields.authEnabled.value">{{
<gl-form-checkbox v-model="form.fields.enabled.value" data-testid="auth-enable-checkbox">{{
s__('DastProfiles|Enable Authentication')
}}</gl-form-checkbox>
</gl-form-group>
<div v-if="form.fields.authEnabled.value" data-testid="auth-form">
<div v-if="form.fields.enabled.value" data-testid="auth-form">
<div class="row">
<gl-form-group
:label="s__('DastProfiles|Authentication URL')"
:invalid-feedback="form.fields.authenticationUrl.feedback"
:invalid-feedback="form.fields.url.feedback"
class="col-md-6"
>
<gl-form-input
v-model="form.fields.authenticationUrl.value"
v-model="form.fields.url.value"
v-validation:[showValidationOrInEditMode]
name="authenticationUrl"
name="url"
type="url"
required
:state="form.fields.authenticationUrl.state"
:state="form.fields.url.state"
/>
</gl-form-group>
</div>
<div class="row">
<gl-form-group
:label="s__('DastProfiles|Username')"
:invalid-feedback="form.fields.userName.feedback"
:invalid-feedback="form.fields.username.feedback"
class="col-md-6"
>
<gl-form-input
v-model="form.fields.userName.value"
v-model="form.fields.username.value"
v-validation:[showValidationOrInEditMode]
autocomplete="off"
name="userName"
name="username"
type="text"
required
:state="form.fields.userName.state"
:state="form.fields.username.state"
/>
</gl-form-group>
<gl-form-group
......@@ -116,7 +126,8 @@ export default {
autocomplete="off"
name="password"
type="password"
required
:placeholder="sensitiveFieldPlaceholder"
:required="isSensitiveFieldRequired"
:state="form.fields.password.state"
/>
</gl-form-group>
......@@ -124,30 +135,30 @@ export default {
<div class="row">
<gl-form-group
:label="s__('DastProfiles|Username form field')"
:invalid-feedback="form.fields.userNameFormField.feedback"
:invalid-feedback="form.fields.usernameField.feedback"
class="col-md-6"
>
<gl-form-input
v-model="form.fields.userNameFormField.value"
v-model="form.fields.usernameField.value"
v-validation:[showValidationOrInEditMode]
name="userNameFormField"
name="usernameField"
type="text"
required
:state="form.fields.userNameFormField.state"
:state="form.fields.usernameField.state"
/>
</gl-form-group>
<gl-form-group
:label="s__('DastProfiles|Password form field')"
:invalid-feedback="form.fields.passwordFormField.feedback"
:invalid-feedback="form.fields.passwordField.feedback"
class="col-md-6"
>
<gl-form-input
v-model="form.fields.passwordFormField.value"
v-model="form.fields.passwordField.value"
v-validation:[showValidationOrInEditMode]
name="passwordFormField"
name="passwordField"
type="text"
required
:state="form.fields.passwordFormField.state"
:state="form.fields.passwordField.state"
/>
</gl-form-group>
</div>
......
......@@ -56,7 +56,7 @@ export default {
},
},
data() {
const { name = '', targetUrl = '', excludedUrls = '', requestHeaders = '' } =
const { name = '', targetUrl = '', excludedUrls = '', requestHeaders = '', auth = {} } =
this.siteProfile || {};
const form = {
......@@ -76,7 +76,7 @@ export default {
return {
form,
authSection: {},
authSection: { fields: auth },
initialFormValues: serializeFormObject(form.fields),
isLoading: false,
hasAlert: false,
......@@ -126,7 +126,7 @@ export default {
onSubmit() {
const isAuthEnabled =
this.glFeatures.securityDastSiteProfilesAdditionalFields &&
this.authSection.fields.authEnabled.value;
this.authSection.fields.enabled.value;
this.form.showValidation = true;
......@@ -143,7 +143,7 @@ export default {
fullPath: this.fullPath,
...(this.isEdit ? { id: this.siteProfile.id } : {}),
...serializeFormObject(this.form.fields),
...(isAuthEnabled ? serializeFormObject(this.authSection.fields) : {}),
auth: isAuthEnabled ? serializeFormObject(this.authSection.fields) : {},
},
};
......
......@@ -5,5 +5,7 @@
.js-dast-site-profile-form{ data: { full_path: @project.path_with_namespace,
profiles_library_path: project_security_configuration_dast_profiles_path(@project, anchor: 'site-profiles'),
site_profile: { id: @site_profile.to_global_id.to_s, name: @site_profile.name, target_url: @site_profile.dast_site.url }.to_json,
site_profile: { id: @site_profile.to_global_id.to_s, name: @site_profile.name, target_url: @site_profile.dast_site.url,
excluded_urls: 'https://example.com/logout', request_headers: 'new-header',
auth: { enabled: true, url: 'https://example.com', username: 'admin', usernameField: 'username', passwordField: 'password' }}.to_json,
on_demand_scans_path: Feature.enabled?(:dast_saved_scans, @project, default_enabled: :yaml) ? new_project_on_demand_scan_path(@project) : project_on_demand_scans_path(@project) } }
......@@ -528,9 +528,9 @@ describe('OnDemandScansForm', () => {
describe('site profile summary', () => {
const [authEnabledProfile] = siteProfiles;
const selectSiteProfile = (profile) => {
const selectSiteProfile = async (profile) => {
subject.find(SiteProfileSelector).vm.$emit('input', profile.id);
return subject.vm.$nextTick();
await subject.vm.$nextTick();
};
beforeEach(() => {
......
......@@ -35,6 +35,7 @@ export const siteProfiles = [
usernameField: 'username',
passwordField: 'password',
username: 'admin',
password: 'password',
},
excludedUrls: 'https://foo.com/logout,https://foo.com/send_mail',
requestHeaders: 'log-identifier: dast-active-scan',
......
......@@ -6,11 +6,11 @@ import { extendedWrapper } from 'helpers/vue_test_utils_helper';
describe('DastSiteAuthSection', () => {
let wrapper;
const createComponent = ({ fields } = {}) => {
const createComponent = ({ fields = {} } = {}) => {
wrapper = extendedWrapper(
mount(DastSiteAuthSection, {
propsData: {
fields,
value: { fields },
},
}),
);
......@@ -40,9 +40,9 @@ describe('DastSiteAuthSection', () => {
describe('authentication toggle', () => {
it.each([true, false])(
'is set correctly when the "authEnabled" field is set to "%s"',
'is set correctly when the "enabled" field is set to "%s"',
(authEnabled) => {
createComponent({ fields: { authEnabled } });
createComponent({ fields: { enabled: authEnabled } });
expect(findAuthCheckbox().vm.$attrs.checked).toBe(authEnabled);
},
);
......@@ -57,7 +57,7 @@ describe('DastSiteAuthSection', () => {
'makes the component emit an "input" event when changed',
async (enabled) => {
await setAuthentication({ enabled });
expect(getLatestInputEventPayload().fields.authEnabled.value).toBe(enabled);
expect(getLatestInputEventPayload().fields.enabled.value).toBe(enabled);
},
);
});
......@@ -68,11 +68,11 @@ describe('DastSiteAuthSection', () => {
});
const inputFieldsWithValues = {
authenticationUrl: 'http://www.gitlab.com',
userName: 'foo',
url: 'http://www.gitlab.com',
username: 'foo',
password: 'foo',
userNameFormField: 'foo',
passwordFormField: 'foo',
usernameField: 'foo',
passwordField: 'foo',
};
const inputFieldNames = Object.keys(inputFieldsWithValues);
......
......@@ -11,6 +11,7 @@ import dastSiteProfileUpdateMutation from 'ee/security_configuration/dast_site_p
import { siteProfiles } from 'ee_jest/on_demand_scans/mocks/mock_data';
import * as responses from 'ee_jest/security_configuration/dast_site_profiles_form/mock_data/apollo_mock';
import { TEST_HOST } from 'helpers/test_constants';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import * as urlUtility from '~/lib/utils/url_utility';
......@@ -44,24 +45,33 @@ describe('DastSiteProfileForm', () => {
const withinComponent = () => within(wrapper.element);
const findForm = () => wrapper.find(GlForm);
const findByTestId = (testId) => wrapper.find(`[data-testid="${testId}"]`);
const findProfileNameInput = () => findByTestId('profile-name-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 findCancelButton = () => findByTestId('dast-site-profile-form-cancel-button');
const findCancelModal = () => wrapper.find(GlModal);
const findForm = () => wrapper.findComponent(GlForm);
const findAuthSection = () => wrapper.findComponent(DastSiteAuthSection);
const findCancelModal = () => wrapper.findComponent(GlModal);
const findByNameAttribute = (name) => wrapper.find(`[name="${name}"]`);
const findProfileNameInput = () => wrapper.findByTestId('profile-name-input');
const findTargetUrlInput = () => wrapper.findByTestId('target-url-input');
const findExcludedUrlsInput = () => wrapper.findByTestId('excluded-urls-input');
const findRequestHeadersInput = () => wrapper.findByTestId('request-headers-input');
const findAuthCheckbox = () => wrapper.findByTestId('auth-enable-checkbox');
const findSubmitButton = () => wrapper.findByTestId('dast-site-profile-form-submit-button');
const findCancelButton = () => wrapper.findByTestId('dast-site-profile-form-cancel-button');
const findAlert = () => wrapper.findByTestId('dast-site-profile-form-alert');
const submitForm = () => findForm().vm.$emit('submit', { preventDefault: () => {} });
const findAlert = () => findByTestId('dast-site-profile-form-alert');
const setFieldValue = async (field, value) => {
await field.setValue(value);
field.trigger('blur');
};
const setAuthFieldsValues = async ({ enabled, ...fields }) => {
await findAuthCheckbox().setChecked(enabled);
Object.keys(fields).forEach((field) => {
findByNameAttribute(field).setValue(fields[field]);
});
};
const mockClientFactory = (handlers) => {
const mockClient = createMockClient();
......@@ -109,7 +119,7 @@ describe('DastSiteProfileForm', () => {
},
);
wrapper = mountFn(DastSiteProfileForm, mountOpts);
wrapper = extendedWrapper(mountFn(DastSiteProfileForm, mountOpts));
};
const createComponent = componentFactory();
const createFullComponent = componentFactory(mount);
......@@ -189,6 +199,7 @@ describe('DastSiteProfileForm', () => {
await setFieldValue(findTargetUrlInput(), targetUrl);
await setFieldValue(findExcludedUrlsInput(), excludedUrls);
await setFieldValue(findRequestHeadersInput(), requestHeaders);
await setAuthFieldsValues(siteProfileOne.auth);
submitForm();
};
......@@ -209,6 +220,7 @@ describe('DastSiteProfileForm', () => {
excludedUrls,
requestHeaders,
fullPath,
auth: siteProfileOne.auth,
...mutationVars,
},
});
......
......@@ -34828,6 +34828,9 @@ msgstr ""
msgid "[No reason]"
msgstr ""
msgid "[Unchanged]"
msgstr ""
msgid "`end_time` should not exceed one month after `start_time`"
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