Commit d3f6049e authored by Jiaan Louw's avatar Jiaan Louw Committed by Paul Slaughter

Prevent admin delete user form bypass

**How?**
The native form submission is now prevented

**Also:**
The corresponding spec file was refactored for readability and strength

https://gitlab.com/gitlab-org/gitlab/merge_requests/17343
parent a2f62b53
......@@ -109,7 +109,7 @@ export default {
<template>
<p v-html="text"></p>
<p v-html="confirmationTextLabel"></p>
<form ref="form" :action="deleteUserUrl" method="post">
<form ref="form" :action="deleteUserUrl" method="post" @submit.prevent>
<input ref="method" type="hidden" name="_method" value="delete" />
<input :value="csrfToken" type="hidden" name="authenticity_token" />
<gl-form-input
......
---
title: Fix delete user dialog bypass caused by hitting enter
merge_request: 17343
author:
type: fixed
......@@ -3,26 +3,46 @@ import { GlButton, GlFormInput } from '@gitlab/ui';
import DeleteUserModal from '~/pages/admin/users/components/delete_user_modal.vue';
import ModalStub from './stubs/modal_stub';
const TEST_DELETE_USER_URL = 'delete-url';
const TEST_BLOCK_USER_URL = 'block-url';
const TEST_CSRF = 'csrf';
describe('User Operation confirmation modal', () => {
let wrapper;
let formSubmitSpy;
const findButton = variant =>
wrapper
.findAll(GlButton)
.filter(w => w.attributes('variant') === variant)
.at(0);
const findForm = () => wrapper.find('form');
const findUsernameInput = () => wrapper.find(GlFormInput);
const findPrimaryButton = () => findButton('danger');
const findSecondaryButton = () => findButton('warning');
const findAuthenticityToken = () => new FormData(findForm().element).get('authenticity_token');
const getUsername = () => findUsernameInput().attributes('value');
const getMethodParam = () => new FormData(findForm().element).get('_method');
const getFormAction = () => findForm().attributes('action');
const setUsername = username => {
findUsernameInput().vm.$emit('input', username);
};
const username = 'username';
const badUsername = 'bad_username';
const createComponent = (props = {}) => {
wrapper = shallowMount(DeleteUserModal, {
propsData: {
username,
title: 'title',
content: 'content',
action: 'action',
secondaryAction: 'secondaryAction',
deleteUserUrl: 'delete-url',
blockUserUrl: 'block-url',
username: 'username',
csrfToken: 'csrf',
deleteUserUrl: TEST_DELETE_USER_URL,
blockUserUrl: TEST_BLOCK_USER_URL,
csrfToken: TEST_CSRF,
...props,
},
stubs: {
......@@ -32,6 +52,10 @@ describe('User Operation confirmation modal', () => {
});
};
beforeEach(() => {
formSubmitSpy = jest.spyOn(HTMLFormElement.prototype, 'submit').mockImplementation();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
......@@ -42,44 +66,84 @@ describe('User Operation confirmation modal', () => {
expect(wrapper.element).toMatchSnapshot();
});
it.each`
variant | prop | action
${'danger'} | ${'deleteUserUrl'} | ${'delete'}
${'warning'} | ${'blockUserUrl'} | ${'block'}
`('closing modal with $variant button triggers $action', ({ variant, prop }) => {
createComponent();
const form = wrapper.find('form');
jest.spyOn(form.element, 'submit').mockReturnValue();
const modalButton = findButton(variant);
modalButton.vm.$emit('click');
return wrapper.vm.$nextTick().then(() => {
expect(form.element.submit).toHaveBeenCalled();
expect(form.element.action).toContain(wrapper.props(prop));
expect(new FormData(form.element).get('authenticity_token')).toEqual(
wrapper.props('csrfToken'),
);
describe('on created', () => {
beforeEach(() => {
createComponent();
});
it('has disabled buttons', () => {
expect(findPrimaryButton().attributes('disabled')).toBeTruthy();
expect(findSecondaryButton().attributes('disabled')).toBeTruthy();
});
});
it('disables buttons by default', () => {
createComponent();
const blockButton = findButton('warning');
const deleteButton = findButton('danger');
expect(blockButton.attributes().disabled).toBeTruthy();
expect(deleteButton.attributes().disabled).toBeTruthy();
describe('with incorrect username', () => {
beforeEach(() => {
createComponent();
setUsername(badUsername);
return wrapper.vm.$nextTick();
});
it('shows incorrect username', () => {
expect(getUsername()).toEqual(badUsername);
});
it('has disabled buttons', () => {
expect(findPrimaryButton().attributes('disabled')).toBeTruthy();
expect(findSecondaryButton().attributes('disabled')).toBeTruthy();
});
});
it('enables button when username is typed', () => {
createComponent({
username: 'some-username',
describe('with correct username', () => {
beforeEach(() => {
createComponent();
setUsername(username);
return wrapper.vm.$nextTick();
});
it('shows correct username', () => {
expect(getUsername()).toEqual(username);
});
it('has enabled buttons', () => {
expect(findPrimaryButton().attributes('disabled')).toBeFalsy();
expect(findSecondaryButton().attributes('disabled')).toBeFalsy();
});
wrapper.find(GlFormInput).vm.$emit('input', 'some-username');
const blockButton = findButton('warning');
const deleteButton = findButton('danger');
return wrapper.vm.$nextTick().then(() => {
expect(blockButton.attributes().disabled).toBeFalsy();
expect(deleteButton.attributes().disabled).toBeFalsy();
describe('when primary action is submitted', () => {
beforeEach(() => {
findPrimaryButton().vm.$emit('click');
return wrapper.vm.$nextTick();
});
it('clears the input', () => {
expect(getUsername()).toEqual('');
});
it('has correct form attributes and calls submit', () => {
expect(getFormAction()).toBe(TEST_DELETE_USER_URL);
expect(getMethodParam()).toBe('delete');
expect(findAuthenticityToken()).toBe(TEST_CSRF);
expect(formSubmitSpy).toHaveBeenCalled();
});
});
describe('when secondary action is submitted', () => {
beforeEach(() => {
findSecondaryButton().vm.$emit('click');
return wrapper.vm.$nextTick();
});
it('has correct form attributes and calls submit', () => {
expect(getFormAction()).toBe(TEST_BLOCK_USER_URL);
expect(getMethodParam()).toBe('put');
expect(findAuthenticityToken()).toBe(TEST_CSRF);
expect(formSubmitSpy).toHaveBeenCalled();
});
});
});
});
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