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:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-03-09 18:08:59 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-03-09 18:08:59 +0300
commit6f2b1c32f3ccf422575f591b42372534502dcd72 (patch)
tree2ed532687d73e290f07c760825c02a2ecbaa5416 /app/assets/javascripts/captcha
parentb90d8b54a4d623e52cf1d4318023e3b18d13dd5b (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/captcha')
-rw-r--r--app/assets/javascripts/captcha/captcha_modal.vue10
-rw-r--r--app/assets/javascripts/captcha/captcha_modal_axios_interceptor.js37
-rw-r--r--app/assets/javascripts/captcha/unsolved_captcha_error.js10
-rw-r--r--app/assets/javascripts/captcha/wait_for_captcha_to_be_solved.js53
4 files changed, 109 insertions, 1 deletions
diff --git a/app/assets/javascripts/captcha/captcha_modal.vue b/app/assets/javascripts/captcha/captcha_modal.vue
index e6c73bc9643..a98a52a3130 100644
--- a/app/assets/javascripts/captcha/captcha_modal.vue
+++ b/app/assets/javascripts/captcha/captcha_modal.vue
@@ -41,10 +41,17 @@ export default {
}
},
},
+ mounted() {
+ // If this is true, we need to present the captcha modal to the user.
+ // When the modal is shown we will also initialize and render the form.
+ if (this.needsCaptchaResponse) {
+ this.$refs.modal.show();
+ }
+ },
methods: {
emitReceivedCaptchaResponse(captchaResponse) {
- this.$emit('receivedCaptchaResponse', captchaResponse);
this.$refs.modal.hide();
+ this.$emit('receivedCaptchaResponse', captchaResponse);
},
emitNullReceivedCaptchaResponse() {
this.emitReceivedCaptchaResponse(null);
@@ -103,6 +110,7 @@ export default {
:action-cancel="{ text: __('Cancel') }"
@shown="shown"
@hide="hide"
+ @hidden="$emit('hidden')"
>
<div ref="captcha"></div>
<p>{{ __('We want to be sure it is you, please confirm you are not a robot.') }}</p>
diff --git a/app/assets/javascripts/captcha/captcha_modal_axios_interceptor.js b/app/assets/javascripts/captcha/captcha_modal_axios_interceptor.js
new file mode 100644
index 00000000000..c9eac44eb28
--- /dev/null
+++ b/app/assets/javascripts/captcha/captcha_modal_axios_interceptor.js
@@ -0,0 +1,37 @@
+const supportedMethods = ['patch', 'post', 'put'];
+
+export function registerCaptchaModalInterceptor(axios) {
+ return axios.interceptors.response.use(
+ (response) => {
+ return response;
+ },
+ (err) => {
+ if (
+ supportedMethods.includes(err?.config?.method) &&
+ err?.response?.data?.needs_captcha_response
+ ) {
+ const { data } = err.response;
+ const captchaSiteKey = data.captcha_site_key;
+ const spamLogId = data.spam_log_id;
+ // eslint-disable-next-line promise/no-promise-in-callback
+ return import('~/captcha/wait_for_captcha_to_be_solved')
+ .then(({ waitForCaptchaToBeSolved }) => waitForCaptchaToBeSolved(captchaSiteKey))
+ .then((captchaResponse) => {
+ const errConfig = err.config;
+ const originalData = JSON.parse(errConfig.data);
+ return axios({
+ method: errConfig.method,
+ url: errConfig.url,
+ data: {
+ ...originalData,
+ captcha_response: captchaResponse,
+ spam_log_id: spamLogId,
+ },
+ });
+ });
+ }
+
+ return Promise.reject(err);
+ },
+ );
+}
diff --git a/app/assets/javascripts/captcha/unsolved_captcha_error.js b/app/assets/javascripts/captcha/unsolved_captcha_error.js
new file mode 100644
index 00000000000..1e5c2a4d852
--- /dev/null
+++ b/app/assets/javascripts/captcha/unsolved_captcha_error.js
@@ -0,0 +1,10 @@
+import { __ } from '~/locale';
+
+class UnsolvedCaptchaError extends Error {
+ constructor(message) {
+ super(message || __('You must solve the CAPTCHA in order to submit'));
+ this.name = 'UnsolvedCaptchaError';
+ }
+}
+
+export default UnsolvedCaptchaError;
diff --git a/app/assets/javascripts/captcha/wait_for_captcha_to_be_solved.js b/app/assets/javascripts/captcha/wait_for_captcha_to_be_solved.js
new file mode 100644
index 00000000000..0fd0f571d3b
--- /dev/null
+++ b/app/assets/javascripts/captcha/wait_for_captcha_to_be_solved.js
@@ -0,0 +1,53 @@
+import Vue from 'vue';
+import CaptchaModal from '~/captcha/captcha_modal.vue';
+import UnsolvedCaptchaError from '~/captcha/unsolved_captcha_error';
+
+/**
+ * Opens a Captcha Modal with provided captchaSiteKey.
+ *
+ * Returns a Promise which resolves if the captcha is solved correctly, and rejects
+ * if the captcha process is aborted.
+ *
+ * @param captchaSiteKey
+ * @returns {Promise}
+ */
+export function waitForCaptchaToBeSolved(captchaSiteKey) {
+ return new Promise((resolve, reject) => {
+ let captchaModalElement = document.createElement('div');
+
+ document.body.append(captchaModalElement);
+
+ let captchaModalVueInstance = new Vue({
+ el: captchaModalElement,
+ render: (createElement) => {
+ return createElement(CaptchaModal, {
+ props: {
+ captchaSiteKey,
+ needsCaptchaResponse: true,
+ },
+ on: {
+ hidden: () => {
+ // Cleaning up the modal from the DOM
+ captchaModalVueInstance.$destroy();
+ captchaModalVueInstance.$el.remove();
+ captchaModalElement.remove();
+
+ captchaModalElement = null;
+ captchaModalVueInstance = null;
+ },
+ receivedCaptchaResponse: (captchaResponse) => {
+ if (captchaResponse) {
+ resolve(captchaResponse);
+ } else {
+ // reject the promise with a custom exception, allowing consuming apps to
+ // adjust their error handling, if appropriate.
+ const error = new UnsolvedCaptchaError();
+ reject(error);
+ }
+ },
+ },
+ });
+ },
+ });
+ });
+}