2019-02-15 15:39:39 +05:30
|
|
|
import $ from 'jquery';
|
2020-03-09 13:42:32 +05:30
|
|
|
import { escape as esc } from 'lodash';
|
2019-02-15 15:39:39 +05:30
|
|
|
import { __ } from '~/locale';
|
|
|
|
import axios from '~/lib/utils/axios_utils';
|
|
|
|
import Flash from '~/flash';
|
|
|
|
import { backOff } from '~/lib/utils/common_utils';
|
|
|
|
import AUTH_METHOD from './constants';
|
|
|
|
|
|
|
|
export default class SSHMirror {
|
|
|
|
constructor(formSelector) {
|
|
|
|
this.backOffRequestCounter = 0;
|
|
|
|
|
|
|
|
this.$form = $(formSelector);
|
|
|
|
|
|
|
|
this.$repositoryUrl = this.$form.find('.js-repo-url');
|
|
|
|
this.$knownHosts = this.$form.find('.js-known-hosts');
|
|
|
|
|
|
|
|
this.$sectionSSHHostKeys = this.$form.find('.js-ssh-host-keys-section');
|
|
|
|
this.$hostKeysInformation = this.$form.find('.js-fingerprint-ssh-info');
|
|
|
|
this.$btnDetectHostKeys = this.$form.find('.js-detect-host-keys');
|
|
|
|
this.$btnSSHHostsShowAdvanced = this.$form.find('.btn-show-advanced');
|
|
|
|
this.$dropdownAuthType = this.$form.find('.js-mirror-auth-type');
|
2019-07-07 11:18:12 +05:30
|
|
|
this.$hiddenAuthType = this.$form.find('.js-hidden-mirror-auth-type');
|
2019-02-15 15:39:39 +05:30
|
|
|
|
|
|
|
this.$wellAuthTypeChanging = this.$form.find('.js-well-changing-auth');
|
|
|
|
this.$wellPasswordAuth = this.$form.find('.js-well-password-auth');
|
|
|
|
}
|
|
|
|
|
|
|
|
init() {
|
|
|
|
this.handleRepositoryUrlInput(true);
|
|
|
|
|
|
|
|
this.$repositoryUrl.on('keyup', () => this.handleRepositoryUrlInput());
|
|
|
|
this.$knownHosts.on('keyup', e => this.handleSSHKnownHostsInput(e));
|
|
|
|
this.$dropdownAuthType.on('change', e => this.handleAuthTypeChange(e));
|
|
|
|
this.$btnDetectHostKeys.on('click', e => this.handleDetectHostKeys(e));
|
|
|
|
this.$btnSSHHostsShowAdvanced.on('click', e => this.handleSSHHostsAdvanced(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Method to monitor Git Repository URL input
|
|
|
|
*/
|
|
|
|
handleRepositoryUrlInput(forceMatch) {
|
|
|
|
const protocol = this.$repositoryUrl.val().split('://')[0];
|
|
|
|
const protRegEx = /http|git/;
|
|
|
|
|
|
|
|
// Validate URL and verify if it consists only supported protocols
|
|
|
|
if (forceMatch || this.$form.get(0).checkValidity()) {
|
|
|
|
const isSsh = protocol === 'ssh';
|
|
|
|
// Hide/Show SSH Host keys section only for SSH URLs
|
|
|
|
this.$sectionSSHHostKeys.collapse(isSsh ? 'show' : 'hide');
|
|
|
|
this.$btnDetectHostKeys.enable();
|
|
|
|
|
|
|
|
// Verify if URL is http, https or git and hide/show Auth type dropdown
|
|
|
|
// as we don't support auth type SSH for non-SSH URLs
|
|
|
|
const matchesProtocol = protRegEx.test(protocol);
|
|
|
|
this.$dropdownAuthType.attr('disabled', matchesProtocol);
|
|
|
|
|
|
|
|
if (forceMatch && isSsh) {
|
|
|
|
this.$dropdownAuthType.val(AUTH_METHOD.SSH);
|
|
|
|
this.toggleAuthWell(AUTH_METHOD.SSH);
|
|
|
|
} else {
|
|
|
|
this.$dropdownAuthType.val(AUTH_METHOD.PASSWORD);
|
|
|
|
this.toggleAuthWell(AUTH_METHOD.PASSWORD);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Click event handler to detect SSH Host key and fingerprints from
|
|
|
|
* provided Git Repository URL.
|
|
|
|
*/
|
|
|
|
handleDetectHostKeys() {
|
|
|
|
const projectMirrorSSHEndpoint = this.$form.data('project-mirror-ssh-endpoint');
|
|
|
|
const repositoryUrl = this.$repositoryUrl.val();
|
|
|
|
const currentKnownHosts = this.$knownHosts.val();
|
|
|
|
const $btnLoadSpinner = this.$btnDetectHostKeys.find('.js-spinner');
|
|
|
|
|
|
|
|
// Disable button while we make request
|
|
|
|
this.$btnDetectHostKeys.disable();
|
|
|
|
$btnLoadSpinner.removeClass('d-none');
|
|
|
|
|
|
|
|
// Make backOff polling to get data
|
|
|
|
backOff((next, stop) => {
|
|
|
|
axios
|
|
|
|
.get(
|
|
|
|
`${projectMirrorSSHEndpoint}?ssh_url=${repositoryUrl}&compare_host_keys=${encodeURIComponent(
|
|
|
|
currentKnownHosts,
|
|
|
|
)}`,
|
|
|
|
)
|
|
|
|
.then(({ data, status }) => {
|
|
|
|
if (status === 204) {
|
|
|
|
this.backOffRequestCounter += 1;
|
|
|
|
if (this.backOffRequestCounter < 3) {
|
|
|
|
next();
|
|
|
|
} else {
|
|
|
|
stop(data);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
stop(data);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch(stop);
|
|
|
|
})
|
|
|
|
.then(res => {
|
|
|
|
$btnLoadSpinner.addClass('d-none');
|
|
|
|
// Once data is received, we show verification info along with Host keys and fingerprints
|
|
|
|
this.$hostKeysInformation
|
|
|
|
.find('.js-fingerprint-verification')
|
|
|
|
.collapse(res.host_keys_changed ? 'hide' : 'show');
|
|
|
|
if (res.known_hosts && res.fingerprints) {
|
|
|
|
this.showSSHInformation(res);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch(({ response }) => {
|
|
|
|
// Show failure message when there's an error and re-enable Detect host keys button
|
|
|
|
const failureMessage = response.data
|
|
|
|
? response.data.message
|
|
|
|
: __('An error occurred while detecting host keys');
|
|
|
|
Flash(failureMessage);
|
|
|
|
|
|
|
|
$btnLoadSpinner.addClass('hidden');
|
|
|
|
this.$btnDetectHostKeys.enable();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Method to monitor known hosts textarea input
|
|
|
|
*/
|
|
|
|
handleSSHKnownHostsInput() {
|
|
|
|
// Strike-out fingerprints and remove verification info if `known hosts` value is altered
|
|
|
|
this.$hostKeysInformation.find('.js-fingerprints-list').addClass('invalidate');
|
|
|
|
this.$hostKeysInformation.find('.js-fingerprint-verification').collapse('hide');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Click event handler for `Show advanced` button under SSH Host keys section
|
|
|
|
*/
|
|
|
|
handleSSHHostsAdvanced() {
|
|
|
|
const $knownHost = this.$sectionSSHHostKeys.find('.js-ssh-known-hosts');
|
|
|
|
const toggleShowAdvanced = $knownHost.hasClass('show');
|
|
|
|
|
|
|
|
$knownHost.collapse('toggle');
|
|
|
|
this.$btnSSHHostsShowAdvanced.toggleClass('show-advanced', toggleShowAdvanced);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Authentication method dropdown change event listener
|
|
|
|
*/
|
|
|
|
handleAuthTypeChange() {
|
|
|
|
const selectedAuthType = this.$dropdownAuthType.val();
|
|
|
|
|
|
|
|
this.$wellPasswordAuth.collapse('hide');
|
2019-07-07 11:18:12 +05:30
|
|
|
this.updateHiddenAuthType(selectedAuthType);
|
2019-07-31 22:56:46 +05:30
|
|
|
this.toggleAuthWell(selectedAuthType);
|
2019-02-15 15:39:39 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Method to parse SSH Host keys data and render it
|
|
|
|
* under SSH host keys section
|
|
|
|
*/
|
|
|
|
showSSHInformation(sshHostKeys) {
|
|
|
|
const $fingerprintsList = this.$hostKeysInformation.find('.js-fingerprints-list');
|
|
|
|
let fingerprints = '';
|
|
|
|
sshHostKeys.fingerprints.forEach(fingerprint => {
|
2020-03-09 13:42:32 +05:30
|
|
|
const escFingerprints = esc(fingerprint.fingerprint);
|
2019-02-15 15:39:39 +05:30
|
|
|
fingerprints += `<code>${escFingerprints}</code>`;
|
|
|
|
});
|
|
|
|
|
|
|
|
this.$hostKeysInformation.collapse('show');
|
|
|
|
$fingerprintsList.removeClass('invalidate');
|
|
|
|
$fingerprintsList.html(fingerprints);
|
|
|
|
this.$sectionSSHHostKeys.find('.js-known-hosts').val(sshHostKeys.known_hosts);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Toggle Auth type information container based on provided `authType`
|
|
|
|
*/
|
|
|
|
toggleAuthWell(authType) {
|
|
|
|
this.$wellPasswordAuth.collapse(authType === AUTH_METHOD.PASSWORD ? 'show' : 'hide');
|
2019-07-07 11:18:12 +05:30
|
|
|
this.updateHiddenAuthType(authType);
|
|
|
|
}
|
|
|
|
|
|
|
|
updateHiddenAuthType(authType) {
|
|
|
|
this.$hiddenAuthType.val(authType);
|
|
|
|
this.$hiddenAuthType.prop('disabled', authType === AUTH_METHOD.SSH);
|
2019-02-15 15:39:39 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
destroy() {
|
|
|
|
this.$repositoryUrl.off('keyup');
|
|
|
|
this.$form.find('.js-known-hosts').off('keyup');
|
|
|
|
this.$dropdownAuthType.off('change');
|
|
|
|
this.$btnDetectHostKeys.off('click');
|
|
|
|
this.$btnSSHHostsShowAdvanced.off('click');
|
|
|
|
}
|
|
|
|
}
|