2018-11-18 11:00:15 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
class Import::BitbucketServerController < Import::BaseController
|
2020-06-23 00:09:42 +05:30
|
|
|
extend ::Gitlab::Utils::Override
|
|
|
|
|
2019-10-12 21:52:04 +05:30
|
|
|
include ActionView::Helpers::SanitizeHelper
|
|
|
|
|
2018-11-18 11:00:15 +05:30
|
|
|
before_action :verify_bitbucket_server_import_enabled
|
|
|
|
before_action :bitbucket_auth, except: [:new, :configure]
|
2020-07-28 23:09:34 +05:30
|
|
|
before_action :normalize_import_params, only: [:create]
|
2018-11-18 11:00:15 +05:30
|
|
|
before_action :validate_import_params, only: [:create]
|
|
|
|
|
2020-06-23 00:09:42 +05:30
|
|
|
rescue_from BitbucketServer::Connection::ConnectionError, with: :bitbucket_connection_error
|
|
|
|
|
2018-11-18 11:00:15 +05:30
|
|
|
# As a basic sanity check to prevent URL injection, restrict project
|
|
|
|
# repository input and repository slugs to allowed characters. For Bitbucket:
|
|
|
|
#
|
|
|
|
# Project keys must start with a letter and may only consist of ASCII letters, numbers and underscores (A-Z, a-z, 0-9, _).
|
|
|
|
#
|
|
|
|
# Repository names are limited to 128 characters. They must start with a
|
|
|
|
# letter or number and may contain spaces, hyphens, underscores, and periods.
|
|
|
|
# (https://community.atlassian.com/t5/Answers-Developer-Questions/stash-repository-names/qaq-p/499054)
|
2019-03-02 22:35:43 +05:30
|
|
|
#
|
|
|
|
# Bitbucket Server starts personal project names with a tilde.
|
2019-07-31 22:56:46 +05:30
|
|
|
VALID_BITBUCKET_PROJECT_CHARS = /\A~?[\w\-\.\s]+\z/.freeze
|
|
|
|
VALID_BITBUCKET_CHARS = /\A[\w\-\.\s]+\z/.freeze
|
2018-11-18 11:00:15 +05:30
|
|
|
|
|
|
|
def new
|
|
|
|
end
|
|
|
|
|
|
|
|
def create
|
2020-06-23 00:09:42 +05:30
|
|
|
repo = client.repo(@project_key, @repo_slug)
|
2018-11-18 11:00:15 +05:30
|
|
|
|
|
|
|
unless repo
|
2019-07-07 11:18:12 +05:30
|
|
|
return render json: { errors: _("Project %{project_repo} could not be found") % { project_repo: "#{@project_key}/#{@repo_slug}" } }, status: :unprocessable_entity
|
2018-11-18 11:00:15 +05:30
|
|
|
end
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
result = Import::BitbucketServerService.new(client, current_user, params).execute(credentials)
|
2018-11-18 11:00:15 +05:30
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
if result[:status] == :success
|
|
|
|
render json: ProjectSerializer.new.represent(result[:project], serializer: :import)
|
2018-11-18 11:00:15 +05:30
|
|
|
else
|
2020-07-28 23:09:34 +05:30
|
|
|
render json: { errors: result[:message] }, status: result[:http_status]
|
2018-11-18 11:00:15 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def configure
|
|
|
|
session[personal_access_token_key] = params[:personal_access_token]
|
2020-07-28 23:09:34 +05:30
|
|
|
session[bitbucket_server_username_key] = params[:bitbucket_server_username]
|
2018-11-18 11:00:15 +05:30
|
|
|
session[bitbucket_server_url_key] = params[:bitbucket_server_url]
|
|
|
|
|
2022-08-13 15:12:31 +05:30
|
|
|
redirect_to status_import_bitbucket_server_path(namespace_id: params[:namespace_id])
|
2018-11-18 11:00:15 +05:30
|
|
|
end
|
|
|
|
|
2022-07-16 23:28:13 +05:30
|
|
|
# We need to re-expose controller's internal method 'status' as action.
|
|
|
|
# rubocop:disable Lint/UselessMethodDefinition
|
2018-11-18 11:00:15 +05:30
|
|
|
def status
|
2020-07-28 23:09:34 +05:30
|
|
|
super
|
2018-11-18 11:00:15 +05:30
|
|
|
end
|
2022-07-16 23:28:13 +05:30
|
|
|
# rubocop:enable Lint/UselessMethodDefinition
|
2020-06-23 00:09:42 +05:30
|
|
|
|
|
|
|
protected
|
|
|
|
|
|
|
|
override :importable_repos
|
|
|
|
def importable_repos
|
2023-03-17 16:20:25 +05:30
|
|
|
bitbucket_repos.filter(&:valid?)
|
2020-06-23 00:09:42 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
override :incompatible_repos
|
|
|
|
def incompatible_repos
|
2023-03-17 16:20:25 +05:30
|
|
|
bitbucket_repos.reject(&:valid?)
|
2020-06-23 00:09:42 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
override :provider_name
|
|
|
|
def provider_name
|
|
|
|
:bitbucket_server
|
|
|
|
end
|
|
|
|
|
|
|
|
override :provider_url
|
|
|
|
def provider_url
|
|
|
|
session[bitbucket_server_url_key]
|
|
|
|
end
|
|
|
|
|
2018-11-18 11:00:15 +05:30
|
|
|
private
|
|
|
|
|
2020-06-23 00:09:42 +05:30
|
|
|
def client
|
|
|
|
@client ||= BitbucketServer::Client.new(credentials)
|
|
|
|
end
|
|
|
|
|
|
|
|
def bitbucket_repos
|
|
|
|
@bitbucket_repos ||= client.repos(page_offset: page_offset, limit: limit_per_page, filter: sanitized_filter_param).to_a
|
2018-11-18 11:00:15 +05:30
|
|
|
end
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
def normalize_import_params
|
|
|
|
project_key, repo_slug = params[:repo_id].split('/')
|
|
|
|
params[:bitbucket_server_project] = project_key
|
|
|
|
params[:bitbucket_server_repo] = repo_slug
|
|
|
|
end
|
|
|
|
|
2018-11-18 11:00:15 +05:30
|
|
|
def validate_import_params
|
2020-07-28 23:09:34 +05:30
|
|
|
@project_key = params[:bitbucket_server_project]
|
|
|
|
@repo_slug = params[:bitbucket_server_repo]
|
2018-11-18 11:00:15 +05:30
|
|
|
|
|
|
|
return render_validation_error('Missing project key') unless @project_key.present? && @repo_slug.present?
|
|
|
|
return render_validation_error('Missing repository slug') unless @repo_slug.present?
|
2019-03-02 22:35:43 +05:30
|
|
|
return render_validation_error('Invalid project key') unless @project_key =~ VALID_BITBUCKET_PROJECT_CHARS
|
2018-11-18 11:00:15 +05:30
|
|
|
return render_validation_error('Invalid repository slug') unless @repo_slug =~ VALID_BITBUCKET_CHARS
|
|
|
|
end
|
|
|
|
|
|
|
|
def render_validation_error(message)
|
|
|
|
render json: { errors: message }, status: :unprocessable_entity
|
|
|
|
end
|
|
|
|
|
|
|
|
def bitbucket_auth
|
|
|
|
unless session[bitbucket_server_url_key].present? &&
|
|
|
|
session[bitbucket_server_username_key].present? &&
|
|
|
|
session[personal_access_token_key].present?
|
2022-08-13 15:12:31 +05:30
|
|
|
redirect_to new_import_bitbucket_server_path(namespace_id: params[:namespace_id])
|
2018-11-18 11:00:15 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def verify_bitbucket_server_import_enabled
|
|
|
|
render_404 unless bitbucket_server_import_enabled?
|
|
|
|
end
|
|
|
|
|
|
|
|
def bitbucket_server_url_key
|
|
|
|
:bitbucket_server_url
|
|
|
|
end
|
|
|
|
|
|
|
|
def bitbucket_server_username_key
|
|
|
|
:bitbucket_server_username
|
|
|
|
end
|
|
|
|
|
|
|
|
def personal_access_token_key
|
|
|
|
:bitbucket_server_personal_access_token
|
|
|
|
end
|
|
|
|
|
|
|
|
def clear_session_data
|
|
|
|
session[bitbucket_server_url_key] = nil
|
|
|
|
session[bitbucket_server_username_key] = nil
|
|
|
|
session[personal_access_token_key] = nil
|
|
|
|
end
|
|
|
|
|
|
|
|
def credentials
|
|
|
|
{
|
|
|
|
base_uri: session[bitbucket_server_url_key],
|
|
|
|
user: session[bitbucket_server_username_key],
|
|
|
|
password: session[personal_access_token_key]
|
|
|
|
}
|
|
|
|
end
|
2018-12-13 13:39:08 +05:30
|
|
|
|
|
|
|
def page_offset
|
|
|
|
[0, params[:page].to_i].max
|
|
|
|
end
|
|
|
|
|
|
|
|
def limit_per_page
|
|
|
|
BitbucketServer::Paginator::PAGE_LENGTH
|
|
|
|
end
|
2019-10-12 21:52:04 +05:30
|
|
|
|
2020-06-23 00:09:42 +05:30
|
|
|
def bitbucket_connection_error(error)
|
|
|
|
flash[:alert] = _("Unable to connect to server: %{error}") % { error: error }
|
|
|
|
clear_session_data
|
|
|
|
|
|
|
|
respond_to do |format|
|
|
|
|
format.json do
|
|
|
|
render json: {
|
|
|
|
error: {
|
|
|
|
message: _("Unable to connect to server: %{error}") % { error: error },
|
|
|
|
redirect: new_import_bitbucket_server_path
|
|
|
|
}
|
|
|
|
}, status: :unprocessable_entity
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2018-11-18 11:00:15 +05:30
|
|
|
end
|