All files / app/assets/javascripts/captcha captcha_modal.vue

95% Statements 19/20
66.67% Branches 4/6
100% Functions 10/10
95% Lines 19/20

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119                    3x   3x                                   14x               11x 11x             14x           9x 9x     7x         11x 11x     11x   8x                     3x 3x     3x                       16x 4x           84x                                  
<script>
// NOTE 1: This is similar to recaptcha_modal.vue, but it directly uses the reCAPTCHA Javascript API
// (https://developers.google.com/recaptcha/docs/display#js_api) and gl-modal, rather than relying
// on the form-based ReCAPTCHA HTML being pre-rendered by the backend and using deprecated-modal.
 
// NOTE 2: Even though this modal currently only supports reCAPTCHA, we use 'captcha' instead
// of 'recaptcha' throughout the code, so that we can easily add support for future alternative
// captcha implementations other than reCAPTCHA (e.g. FriendlyCaptcha) without having to
// change the references in the code or API.
 
import { GlModal } from '@gitlab/ui';
import { uniqueId } from 'lodash';
import { initRecaptchaScript } from '~/captcha/init_recaptcha_script';
 
export default {
  components: {
    GlModal,
  },
  props: {
    needsCaptchaResponse: {
      type: Boolean,
      required: false,
      default: false,
    },
    captchaSiteKey: {
      type: String,
      required: true,
    },
  },
  data() {
    return {
      modalId: uniqueId('captcha-modal-'),
    };
  },
  watch: {
    needsCaptchaResponse(newNeedsCaptchaResponse) {
      // 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.
E      if (newNeedsCaptchaResponse) {
        this.$refs.modal.show();
      }
    },
  },
  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.
I    if (this.needsCaptchaResponse) {
      this.$refs.modal.show();
    }
  },
  methods: {
    emitReceivedCaptchaResponse(captchaResponse) {
      this.$refs.modal.hide();
      this.$emit('receivedCaptchaResponse', captchaResponse);
    },
    emitNullReceivedCaptchaResponse() {
      this.emitReceivedCaptchaResponse(null);
    },
    /**
     * handler for when modal is shown
     */
    shown() {
      const containerRef = this.$refs.captcha;
 
      // NOTE: This is the only bit that is specific to Google's reCAPTCHA captcha implementation.
      initRecaptchaScript()
        .then((grecaptcha) => {
          grecaptcha.render(containerRef, {
            sitekey: this.captchaSiteKey,
            // This callback will emit and let the parent handle the response
            callback: this.emitReceivedCaptchaResponse,
            // TODO: Also need to handle expired-callback and error-callback
            //   See https://gitlab.com/gitlab-org/gitlab/-/issues/217722#future-follow-on-issuesmrs
          });
        })
        .catch((e) => {
          // TODO: flash the error or notify the user some other way
          //   See https://gitlab.com/gitlab-org/gitlab/-/issues/217722#future-follow-on-issuesmrs
          this.emitNullReceivedCaptchaResponse();
          this.$refs.modal.hide();
 
          // eslint-disable-next-line no-console
          console.error(
            '[gitlab] an unexpected exception was caught while initializing captcha',
            e,
          );
        });
    },
    /**
     * handler for when modal is about to hide
     */
    hide(bvModalEvent) {
      // If hide() was called without any argument, the value of trigger will be null.
      // See https://bootstrap-vue.org/docs/components/modal#prevent-closing
      if (bvModalEvent.trigger) {
        this.emitNullReceivedCaptchaResponse();
      }
    },
  },
};
</script>
<template>
  <!-- Note: The action-cancel button isn't necessary for the functionality of the modal, but   -->
  <!-- there must be at least one button or focusable element, or the gl-modal fails to render. -->
  <!-- We could modify gl-model to remove this requirement.                                     -->
  <gl-modal
    ref="modal"
    :modal-id="modalId"
    :title="__('Please solve the captcha')"
    :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>
  </gl-modal>
</template>