PageRenderTime 25ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/app/controllers/import/github_controller.rb

https://gitlab.com/klml/gitlab-ee
Ruby | 268 lines | 206 code | 55 blank | 7 comment | 14 complexity | babafb8b4cf7cba902ce1c5ad738c852 MD5 | raw file
  1. # frozen_string_literal: true
  2. class Import::GithubController < Import::BaseController
  3. extend ::Gitlab::Utils::Override
  4. include ImportHelper
  5. include ActionView::Helpers::SanitizeHelper
  6. before_action :verify_import_enabled
  7. before_action :provider_auth, only: [:status, :realtime_changes, :create]
  8. before_action :expire_etag_cache, only: [:status, :create]
  9. OAuthConfigMissingError = Class.new(StandardError)
  10. rescue_from OAuthConfigMissingError, with: :missing_oauth_config
  11. rescue_from Octokit::Unauthorized, with: :provider_unauthorized
  12. rescue_from Octokit::TooManyRequests, with: :provider_rate_limit
  13. rescue_from Gitlab::GithubImport::RateLimitError, with: :rate_limit_threshold_exceeded
  14. PAGE_LENGTH = 25
  15. def new
  16. if !ci_cd_only? && github_import_configured? && logged_in_with_provider?
  17. go_to_provider_for_permissions
  18. elsif session[access_token_key]
  19. redirect_to status_import_url
  20. end
  21. end
  22. def callback
  23. session[access_token_key] = get_token(params[:code])
  24. redirect_to status_import_url
  25. end
  26. def personal_access_token
  27. session[access_token_key] = params[:personal_access_token]&.strip
  28. redirect_to status_import_url
  29. end
  30. def status
  31. # Request repos to display error page if provider token is invalid
  32. # Improving in https://gitlab.com/gitlab-org/gitlab-foss/issues/55585
  33. client_repos
  34. super
  35. end
  36. def create
  37. result = Import::GithubService.new(client, current_user, import_params).execute(access_params, provider_name)
  38. if result[:status] == :success
  39. render json: serialized_imported_projects(result[:project])
  40. else
  41. render json: { errors: result[:message] }, status: result[:http_status]
  42. end
  43. end
  44. def realtime_changes
  45. super
  46. end
  47. protected
  48. # rubocop: disable CodeReuse/ActiveRecord
  49. override :importable_repos
  50. def importable_repos
  51. already_added_projects_names = already_added_projects.pluck(:import_source)
  52. client_repos.reject { |repo| already_added_projects_names.include?(repo.full_name) }
  53. end
  54. # rubocop: enable CodeReuse/ActiveRecord
  55. override :incompatible_repos
  56. def incompatible_repos
  57. []
  58. end
  59. override :provider_name
  60. def provider_name
  61. :github
  62. end
  63. override :provider_url
  64. def provider_url
  65. strong_memoize(:provider_url) do
  66. oauth_config&.dig('url').presence || 'https://github.com'
  67. end
  68. end
  69. private
  70. def import_params
  71. params.permit(permitted_import_params)
  72. end
  73. def permitted_import_params
  74. [:repo_id, :new_name, :target_namespace]
  75. end
  76. def serialized_imported_projects(projects = already_added_projects)
  77. ProjectSerializer.new.represent(projects, serializer: :import, provider_url: provider_url)
  78. end
  79. def expire_etag_cache
  80. Gitlab::EtagCaching::Store.new.tap do |store|
  81. store.touch(realtime_changes_path)
  82. end
  83. end
  84. def client
  85. @client ||= if Feature.enabled?(:remove_legacy_github_client)
  86. Gitlab::GithubImport::Client.new(session[access_token_key])
  87. else
  88. Gitlab::LegacyGithubImport::Client.new(session[access_token_key], **client_options)
  89. end
  90. end
  91. def client_repos
  92. @client_repos ||= if Feature.enabled?(:remove_legacy_github_client)
  93. if sanitized_filter_param
  94. client.search_repos_by_name(sanitized_filter_param, pagination_options)[:items]
  95. else
  96. client.octokit.repos(nil, pagination_options)
  97. end
  98. else
  99. filtered(client.repos)
  100. end
  101. end
  102. def sanitized_filter_param
  103. super
  104. @filter = @filter&.tr(' ', '')&.tr(':', '')
  105. end
  106. def oauth_client
  107. raise OAuthConfigMissingError unless oauth_config
  108. @oauth_client ||= ::OAuth2::Client.new(
  109. oauth_config.app_id,
  110. oauth_config.app_secret,
  111. oauth_options.merge(ssl: { verify: oauth_config['verify_ssl'] })
  112. )
  113. end
  114. def oauth_config
  115. @oauth_config ||= Gitlab::Auth::OAuth::Provider.config_for('github')
  116. end
  117. def oauth_options
  118. if oauth_config
  119. oauth_config.dig('args', 'client_options').deep_symbolize_keys
  120. else
  121. OmniAuth::Strategies::GitHub.default_options[:client_options].symbolize_keys
  122. end
  123. end
  124. def authorize_url
  125. if Feature.enabled?(:remove_legacy_github_client)
  126. oauth_client.auth_code.authorize_url(
  127. redirect_uri: callback_import_url,
  128. scope: 'repo, user, user:email'
  129. )
  130. else
  131. client.authorize_url(callback_import_url)
  132. end
  133. end
  134. def get_token(code)
  135. if Feature.enabled?(:remove_legacy_github_client)
  136. oauth_client.auth_code.get_token(code).token
  137. else
  138. client.get_token(code)
  139. end
  140. end
  141. def verify_import_enabled
  142. render_404 unless import_enabled?
  143. end
  144. def go_to_provider_for_permissions
  145. redirect_to authorize_url
  146. end
  147. def import_enabled?
  148. __send__("#{provider_name}_import_enabled?") # rubocop:disable GitlabSecurity/PublicSend
  149. end
  150. def realtime_changes_path
  151. public_send("realtime_changes_import_#{provider_name}_path", format: :json) # rubocop:disable GitlabSecurity/PublicSend
  152. end
  153. def new_import_url
  154. public_send("new_import_#{provider_name}_url", extra_import_params) # rubocop:disable GitlabSecurity/PublicSend
  155. end
  156. def status_import_url
  157. public_send("status_import_#{provider_name}_url", extra_import_params) # rubocop:disable GitlabSecurity/PublicSend
  158. end
  159. def callback_import_url
  160. public_send("users_import_#{provider_name}_callback_url", extra_import_params) # rubocop:disable GitlabSecurity/PublicSend
  161. end
  162. def provider_unauthorized
  163. session[access_token_key] = nil
  164. redirect_to new_import_url,
  165. alert: "Access denied to your #{Gitlab::ImportSources.title(provider_name.to_s)} account."
  166. end
  167. def provider_rate_limit(exception)
  168. reset_time = Time.zone.at(exception.response_headers['x-ratelimit-reset'].to_i)
  169. session[access_token_key] = nil
  170. redirect_to new_import_url,
  171. alert: _("GitHub API rate limit exceeded. Try again after %{reset_time}") % { reset_time: reset_time }
  172. end
  173. def missing_oauth_config
  174. session[access_token_key] = nil
  175. redirect_to new_import_url,
  176. alert: _('Missing OAuth configuration for GitHub.')
  177. end
  178. def access_token_key
  179. :"#{provider_name}_access_token"
  180. end
  181. def access_params
  182. { github_access_token: session[access_token_key] }
  183. end
  184. # rubocop: disable CodeReuse/ActiveRecord
  185. def logged_in_with_provider?
  186. current_user.identities.exists?(provider: provider_name)
  187. end
  188. # rubocop: enable CodeReuse/ActiveRecord
  189. def provider_auth
  190. if !ci_cd_only? && session[access_token_key].blank?
  191. go_to_provider_for_permissions
  192. end
  193. end
  194. def ci_cd_only?
  195. %w[1 true].include?(params[:ci_cd_only])
  196. end
  197. def client_options
  198. { wait_for_rate_limit_reset: false }
  199. end
  200. def extra_import_params
  201. {}
  202. end
  203. def rate_limit_threshold_exceeded
  204. head :too_many_requests
  205. end
  206. def pagination_options
  207. {
  208. page: [1, params[:page].to_i].max,
  209. per_page: PAGE_LENGTH
  210. }
  211. end
  212. end
  213. Import::GithubController.prepend_mod_with('Import::GithubController')