PageRenderTime 26ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/app/controllers/import/bitbucket_server_controller.rb

https://gitlab.com/markglenfletcher/gitlab-ee
Ruby | 156 lines | 109 code | 31 blank | 16 comment | 5 complexity | d31b54c2e35eac6bf95e9357dbc20563 MD5 | raw file
  1. # frozen_string_literal: true
  2. class Import::BitbucketServerController < Import::BaseController
  3. include ActionView::Helpers::SanitizeHelper
  4. before_action :verify_bitbucket_server_import_enabled
  5. before_action :bitbucket_auth, except: [:new, :configure]
  6. before_action :validate_import_params, only: [:create]
  7. # As a basic sanity check to prevent URL injection, restrict project
  8. # repository input and repository slugs to allowed characters. For Bitbucket:
  9. #
  10. # Project keys must start with a letter and may only consist of ASCII letters, numbers and underscores (A-Z, a-z, 0-9, _).
  11. #
  12. # Repository names are limited to 128 characters. They must start with a
  13. # letter or number and may contain spaces, hyphens, underscores, and periods.
  14. # (https://community.atlassian.com/t5/Answers-Developer-Questions/stash-repository-names/qaq-p/499054)
  15. #
  16. # Bitbucket Server starts personal project names with a tilde.
  17. VALID_BITBUCKET_PROJECT_CHARS = /\A~?[\w\-\.\s]+\z/.freeze
  18. VALID_BITBUCKET_CHARS = /\A[\w\-\.\s]+\z/.freeze
  19. def new
  20. end
  21. def create
  22. repo = bitbucket_client.repo(@project_key, @repo_slug)
  23. unless repo
  24. return render json: { errors: _("Project %{project_repo} could not be found") % { project_repo: "#{@project_key}/#{@repo_slug}" } }, status: :unprocessable_entity
  25. end
  26. project_name = params[:new_name].presence || repo.name
  27. namespace_path = params[:new_namespace].presence || current_user.username
  28. target_namespace = find_or_create_namespace(namespace_path, current_user)
  29. if current_user.can?(:create_projects, target_namespace)
  30. project = Gitlab::BitbucketServerImport::ProjectCreator.new(@project_key, @repo_slug, repo, project_name, target_namespace, current_user, credentials).execute
  31. if project.persisted?
  32. render json: ProjectSerializer.new.represent(project)
  33. else
  34. render json: { errors: project_save_error(project) }, status: :unprocessable_entity
  35. end
  36. else
  37. render json: { errors: _('This namespace has already been taken! Please choose another one.') }, status: :unprocessable_entity
  38. end
  39. rescue BitbucketServer::Connection::ConnectionError => error
  40. render json: { errors: _("Unable to connect to server: %{error}") % { error: error } }, status: :unprocessable_entity
  41. end
  42. def configure
  43. session[personal_access_token_key] = params[:personal_access_token]
  44. session[bitbucket_server_username_key] = params[:bitbucket_username]
  45. session[bitbucket_server_url_key] = params[:bitbucket_server_url]
  46. redirect_to status_import_bitbucket_server_path
  47. end
  48. # rubocop: disable CodeReuse/ActiveRecord
  49. def status
  50. @collection = bitbucket_client.repos(page_offset: page_offset, limit: limit_per_page, filter: sanitized_filter_param)
  51. @repos, @incompatible_repos = @collection.partition { |repo| repo.valid? }
  52. # Use the import URL to filter beyond what BaseService#find_already_added_projects
  53. @already_added_projects = filter_added_projects('bitbucket_server', @repos.map(&:browse_url))
  54. already_added_projects_names = @already_added_projects.pluck(:import_source)
  55. @repos.reject! { |repo| already_added_projects_names.include?(repo.browse_url) }
  56. rescue BitbucketServer::Connection::ConnectionError => error
  57. flash[:alert] = _("Unable to connect to server: %{error}") % { error: error }
  58. clear_session_data
  59. redirect_to new_import_bitbucket_server_path
  60. end
  61. # rubocop: enable CodeReuse/ActiveRecord
  62. def jobs
  63. render json: find_jobs('bitbucket_server')
  64. end
  65. private
  66. # rubocop: disable CodeReuse/ActiveRecord
  67. def filter_added_projects(import_type, import_sources)
  68. current_user.created_projects.where(import_type: import_type, import_source: import_sources).includes(:import_state)
  69. end
  70. # rubocop: enable CodeReuse/ActiveRecord
  71. def bitbucket_client
  72. @bitbucket_client ||= BitbucketServer::Client.new(credentials)
  73. end
  74. def validate_import_params
  75. @project_key = params[:project]
  76. @repo_slug = params[:repository]
  77. return render_validation_error('Missing project key') unless @project_key.present? && @repo_slug.present?
  78. return render_validation_error('Missing repository slug') unless @repo_slug.present?
  79. return render_validation_error('Invalid project key') unless @project_key =~ VALID_BITBUCKET_PROJECT_CHARS
  80. return render_validation_error('Invalid repository slug') unless @repo_slug =~ VALID_BITBUCKET_CHARS
  81. end
  82. def render_validation_error(message)
  83. render json: { errors: message }, status: :unprocessable_entity
  84. end
  85. def bitbucket_auth
  86. unless session[bitbucket_server_url_key].present? &&
  87. session[bitbucket_server_username_key].present? &&
  88. session[personal_access_token_key].present?
  89. redirect_to new_import_bitbucket_server_path
  90. end
  91. end
  92. def verify_bitbucket_server_import_enabled
  93. render_404 unless bitbucket_server_import_enabled?
  94. end
  95. def bitbucket_server_url_key
  96. :bitbucket_server_url
  97. end
  98. def bitbucket_server_username_key
  99. :bitbucket_server_username
  100. end
  101. def personal_access_token_key
  102. :bitbucket_server_personal_access_token
  103. end
  104. def clear_session_data
  105. session[bitbucket_server_url_key] = nil
  106. session[bitbucket_server_username_key] = nil
  107. session[personal_access_token_key] = nil
  108. end
  109. def credentials
  110. {
  111. base_uri: session[bitbucket_server_url_key],
  112. user: session[bitbucket_server_username_key],
  113. password: session[personal_access_token_key]
  114. }
  115. end
  116. def page_offset
  117. [0, params[:page].to_i].max
  118. end
  119. def limit_per_page
  120. BitbucketServer::Paginator::PAGE_LENGTH
  121. end
  122. def sanitized_filter_param
  123. sanitize(params[:filter])
  124. end
  125. end