diff options
author | Krasimir Angelov <kangelov@gitlab.com> | 2019-09-04 10:07:49 +1200 |
---|---|---|
committer | Krasimir Angelov <kangelov@gitlab.com> | 2019-09-04 10:07:49 +1200 |
commit | c4b804c6dbf0183e0cec1cb95b7da743b21ff2c4 (patch) | |
tree | 201810b2721fc05bdafc65fd3cb9fdc1dd947151 | |
parent | 89409a1925d65d4a62b523b5a7c0650287250cb5 (diff) | |
download | gitlab-ce-c4b804c6dbf0183e0cec1cb95b7da743b21ff2c4.tar.gz |
Rename API::Internal class to API::Internal::Base61927-internal-api-namespace
so that we can use API::Internal namespace.
Related to https://gitlab.com/gitlab-org/gitlab-ce/issues/61927.
-rw-r--r-- | .rubocop_todo.yml | 2 | ||||
-rw-r--r-- | lib/api/api.rb | 2 | ||||
-rw-r--r-- | lib/api/internal.rb | 294 | ||||
-rw-r--r-- | lib/api/internal/base.rb | 296 | ||||
-rw-r--r-- | spec/requests/api/internal/base_spec.rb (renamed from spec/requests/api/internal_spec.rb) | 2 |
5 files changed, 299 insertions, 297 deletions
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index be147d72f71..f1f8ff6e862 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -218,7 +218,7 @@ Lint/UriEscapeUnescape: - 'app/models/project_services/drone_ci_service.rb' - 'spec/lib/google_api/auth_spec.rb' - 'spec/requests/api/files_spec.rb' - - 'spec/requests/api/internal_spec.rb' + - 'spec/requests/api/internal/base_spec.rb' # Offense count: 1 # Configuration parameters: CheckForMethodsWithNoSideEffects. diff --git a/lib/api/api.rb b/lib/api/api.rb index 219ed45eff6..aa6a67d817a 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -118,7 +118,7 @@ module API mount ::API::GroupContainerRepositories mount ::API::GroupVariables mount ::API::ImportGithub - mount ::API::Internal + mount ::API::Internal::Base mount ::API::Issues mount ::API::JobArtifacts mount ::API::Jobs diff --git a/lib/api/internal.rb b/lib/api/internal.rb deleted file mode 100644 index 088ea5bd79a..00000000000 --- a/lib/api/internal.rb +++ /dev/null @@ -1,294 +0,0 @@ -# frozen_string_literal: true - -module API - # Internal access API - class Internal < Grape::API - before { authenticate_by_gitlab_shell_token! } - - helpers ::API::Helpers::InternalHelpers - helpers ::Gitlab::Identifier - - UNKNOWN_CHECK_RESULT_ERROR = 'Unknown check result'.freeze - - helpers do - def response_with_status(code: 200, success: true, message: nil, **extra_options) - status code - { status: success, message: message }.merge(extra_options).compact - end - - def lfs_authentication_url(project) - # This is a separate method so that EE can alter its behaviour more - # easily. - project.http_url_to_repo - end - end - - namespace 'internal' do - # Check if git command is allowed for project - # - # Params: - # key_id - ssh key id for Git over SSH - # user_id - user id for Git over HTTP or over SSH in keyless SSH CERT mode - # username - user name for Git over SSH in keyless SSH cert mode - # protocol - Git access protocol being used, e.g. HTTP or SSH - # project - project full_path (not path on disk) - # action - git action (git-upload-pack or git-receive-pack) - # changes - changes as "oldrev newrev ref", see Gitlab::ChangesList - # rubocop: disable CodeReuse/ActiveRecord - post "/allowed" do - # Stores some Git-specific env thread-safely - env = parse_env - Gitlab::Git::HookEnv.set(gl_repository, env) if project - - actor = - if params[:key_id] - Key.find_by(id: params[:key_id]) - elsif params[:user_id] - User.find_by(id: params[:user_id]) - elsif params[:username] - UserFinder.new(params[:username]).find_by_username - end - - protocol = params[:protocol] - - actor.update_last_used_at if actor.is_a?(Key) - user = - if actor.is_a?(Key) - actor.user - else - actor - end - - access_checker_klass = repo_type.access_checker_class - access_checker = access_checker_klass.new(actor, project, - protocol, authentication_abilities: ssh_authentication_abilities, - namespace_path: namespace_path, project_path: project_path, - redirected_path: redirected_path) - - check_result = begin - result = access_checker.check(params[:action], params[:changes]) - @project ||= access_checker.project - result - rescue Gitlab::GitAccess::UnauthorizedError => e - break response_with_status(code: 401, success: false, message: e.message) - rescue Gitlab::GitAccess::TimeoutError => e - break response_with_status(code: 503, success: false, message: e.message) - rescue Gitlab::GitAccess::NotFoundError => e - break response_with_status(code: 404, success: false, message: e.message) - end - - log_user_activity(actor) - - case check_result - when ::Gitlab::GitAccessResult::Success - payload = { - gl_repository: gl_repository, - gl_project_path: gl_project_path, - gl_id: Gitlab::GlId.gl_id(user), - gl_username: user&.username, - git_config_options: [], - gitaly: gitaly_payload(params[:action]), - gl_console_messages: check_result.console_messages - } - - # Custom option for git-receive-pack command - receive_max_input_size = Gitlab::CurrentSettings.receive_max_input_size.to_i - if receive_max_input_size > 0 - payload[:git_config_options] << "receive.maxInputSize=#{receive_max_input_size.megabytes}" - end - - response_with_status(**payload) - when ::Gitlab::GitAccessResult::CustomAction - response_with_status(code: 300, message: check_result.message, payload: check_result.payload) - else - response_with_status(code: 500, success: false, message: UNKNOWN_CHECK_RESULT_ERROR) - end - end - # rubocop: enable CodeReuse/ActiveRecord - - # rubocop: disable CodeReuse/ActiveRecord - post "/lfs_authenticate" do - status 200 - - if params[:key_id] - actor = Key.find(params[:key_id]) - actor.update_last_used_at - elsif params[:user_id] - actor = User.find_by(id: params[:user_id]) - raise ActiveRecord::RecordNotFound.new("No such user id!") unless actor - else - raise ActiveRecord::RecordNotFound.new("No key_id or user_id passed!") - end - - Gitlab::LfsToken - .new(actor) - .authentication_payload(lfs_authentication_url(project)) - end - # rubocop: enable CodeReuse/ActiveRecord - - get "/merge_request_urls" do - merge_request_urls - end - - # - # Get a ssh key using the fingerprint - # - # rubocop: disable CodeReuse/ActiveRecord - get "/authorized_keys" do - fingerprint = params.fetch(:fingerprint) do - Gitlab::InsecureKeyFingerprint.new(params.fetch(:key)).fingerprint - end - key = Key.find_by(fingerprint: fingerprint) - not_found!("Key") if key.nil? - present key, with: Entities::SSHKey - end - # rubocop: enable CodeReuse/ActiveRecord - - # - # Discover user by ssh key, user id or username - # - # rubocop: disable CodeReuse/ActiveRecord - get "/discover" do - if params[:key_id] - key = Key.find(params[:key_id]) - user = key.user - elsif params[:user_id] - user = User.find_by(id: params[:user_id]) - elsif params[:username] - user = UserFinder.new(params[:username]).find_by_username - end - - present user, with: Entities::UserSafe - end - # rubocop: enable CodeReuse/ActiveRecord - - get "/check" do - { - api_version: API.version, - gitlab_version: Gitlab::VERSION, - gitlab_rev: Gitlab.revision, - redis: redis_ping - } - end - - get "/broadcast_messages" do - if messages = BroadcastMessage.current - present messages, with: Entities::BroadcastMessage - else - [] - end - end - - get "/broadcast_message" do - if message = BroadcastMessage.current&.last - present message, with: Entities::BroadcastMessage - else - {} - end - end - - # rubocop: disable CodeReuse/ActiveRecord - post '/two_factor_recovery_codes' do - status 200 - - if params[:key_id] - key = Key.find_by(id: params[:key_id]) - - if key - key.update_last_used_at - else - break { 'success' => false, 'message' => 'Could not find the given key' } - end - - if key.is_a?(DeployKey) - break { success: false, message: 'Deploy keys cannot be used to retrieve recovery codes' } - end - - user = key.user - - unless user - break { success: false, message: 'Could not find a user for the given key' } - end - elsif params[:user_id] - user = User.find_by(id: params[:user_id]) - - unless user - break { success: false, message: 'Could not find the given user' } - end - end - - unless user.two_factor_enabled? - break { success: false, message: 'Two-factor authentication is not enabled for this user' } - end - - codes = nil - - ::Users::UpdateService.new(current_user, user: user).execute! do |user| - codes = user.generate_otp_backup_codes! - end - - { success: true, recovery_codes: codes } - end - # rubocop: enable CodeReuse/ActiveRecord - - post '/pre_receive' do - status 200 - - reference_counter_increased = Gitlab::ReferenceCounter.new(params[:gl_repository]).increase - - { reference_counter_increased: reference_counter_increased } - end - - post "/notify_post_receive" do - status 200 - - # TODO: Re-enable when Gitaly is processing the post-receive notification - # return unless Gitlab::GitalyClient.enabled? - # - # begin - # repository = wiki? ? project.wiki.repository : project.repository - # Gitlab::GitalyClient::NotificationService.new(repository.raw_repository).post_receive - # rescue GRPC::Unavailable => e - # render_api_error!(e, 500) - # end - end - - post '/post_receive' do - status 200 - - response = Gitlab::InternalPostReceive::Response.new - user = identify(params[:identifier]) - project = Gitlab::GlRepository.parse(params[:gl_repository]).first - push_options = Gitlab::PushOptions.new(params[:push_options]) - - response.reference_counter_decreased = Gitlab::ReferenceCounter.new(params[:gl_repository]).decrease - - PostReceive.perform_async(params[:gl_repository], params[:identifier], - params[:changes], push_options.as_json) - - mr_options = push_options.get(:merge_request) - if mr_options.present? - message = process_mr_push_options(mr_options, project, user, params[:changes]) - response.add_alert_message(message) - end - - broadcast_message = BroadcastMessage.current&.last&.message - response.add_alert_message(broadcast_message) - - response.add_merge_request_urls(merge_request_urls) - - # A user is not guaranteed to be returned; an orphaned write deploy - # key could be used - if user - redirect_message = Gitlab::Checks::ProjectMoved.fetch_message(user.id, project.id) - project_created_message = Gitlab::Checks::ProjectCreated.fetch_message(user.id, project.id) - - response.add_basic_message(redirect_message) - response.add_basic_message(project_created_message) - end - - present response, with: Entities::InternalPostReceive::Response - end - end - end -end diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb new file mode 100644 index 00000000000..622032b8355 --- /dev/null +++ b/lib/api/internal/base.rb @@ -0,0 +1,296 @@ +# frozen_string_literal: true + +module API + # Internal access API + module Internal + class Base < Grape::API + before { authenticate_by_gitlab_shell_token! } + + helpers ::API::Helpers::InternalHelpers + helpers ::Gitlab::Identifier + + UNKNOWN_CHECK_RESULT_ERROR = 'Unknown check result'.freeze + + helpers do + def response_with_status(code: 200, success: true, message: nil, **extra_options) + status code + { status: success, message: message }.merge(extra_options).compact + end + + def lfs_authentication_url(project) + # This is a separate method so that EE can alter its behaviour more + # easily. + project.http_url_to_repo + end + end + + namespace 'internal' do + # Check if git command is allowed for project + # + # Params: + # key_id - ssh key id for Git over SSH + # user_id - user id for Git over HTTP or over SSH in keyless SSH CERT mode + # username - user name for Git over SSH in keyless SSH cert mode + # protocol - Git access protocol being used, e.g. HTTP or SSH + # project - project full_path (not path on disk) + # action - git action (git-upload-pack or git-receive-pack) + # changes - changes as "oldrev newrev ref", see Gitlab::ChangesList + # rubocop: disable CodeReuse/ActiveRecord + post "/allowed" do + # Stores some Git-specific env thread-safely + env = parse_env + Gitlab::Git::HookEnv.set(gl_repository, env) if project + + actor = + if params[:key_id] + Key.find_by(id: params[:key_id]) + elsif params[:user_id] + User.find_by(id: params[:user_id]) + elsif params[:username] + UserFinder.new(params[:username]).find_by_username + end + + protocol = params[:protocol] + + actor.update_last_used_at if actor.is_a?(Key) + user = + if actor.is_a?(Key) + actor.user + else + actor + end + + access_checker_klass = repo_type.access_checker_class + access_checker = access_checker_klass.new(actor, project, + protocol, authentication_abilities: ssh_authentication_abilities, + namespace_path: namespace_path, project_path: project_path, + redirected_path: redirected_path) + + check_result = begin + result = access_checker.check(params[:action], params[:changes]) + @project ||= access_checker.project + result + rescue Gitlab::GitAccess::UnauthorizedError => e + break response_with_status(code: 401, success: false, message: e.message) + rescue Gitlab::GitAccess::TimeoutError => e + break response_with_status(code: 503, success: false, message: e.message) + rescue Gitlab::GitAccess::NotFoundError => e + break response_with_status(code: 404, success: false, message: e.message) + end + + log_user_activity(actor) + + case check_result + when ::Gitlab::GitAccessResult::Success + payload = { + gl_repository: gl_repository, + gl_project_path: gl_project_path, + gl_id: Gitlab::GlId.gl_id(user), + gl_username: user&.username, + git_config_options: [], + gitaly: gitaly_payload(params[:action]), + gl_console_messages: check_result.console_messages + } + + # Custom option for git-receive-pack command + receive_max_input_size = Gitlab::CurrentSettings.receive_max_input_size.to_i + if receive_max_input_size > 0 + payload[:git_config_options] << "receive.maxInputSize=#{receive_max_input_size.megabytes}" + end + + response_with_status(**payload) + when ::Gitlab::GitAccessResult::CustomAction + response_with_status(code: 300, message: check_result.message, payload: check_result.payload) + else + response_with_status(code: 500, success: false, message: UNKNOWN_CHECK_RESULT_ERROR) + end + end + # rubocop: enable CodeReuse/ActiveRecord + + # rubocop: disable CodeReuse/ActiveRecord + post "/lfs_authenticate" do + status 200 + + if params[:key_id] + actor = Key.find(params[:key_id]) + actor.update_last_used_at + elsif params[:user_id] + actor = User.find_by(id: params[:user_id]) + raise ActiveRecord::RecordNotFound.new("No such user id!") unless actor + else + raise ActiveRecord::RecordNotFound.new("No key_id or user_id passed!") + end + + Gitlab::LfsToken + .new(actor) + .authentication_payload(lfs_authentication_url(project)) + end + # rubocop: enable CodeReuse/ActiveRecord + + get "/merge_request_urls" do + merge_request_urls + end + + # + # Get a ssh key using the fingerprint + # + # rubocop: disable CodeReuse/ActiveRecord + get "/authorized_keys" do + fingerprint = params.fetch(:fingerprint) do + Gitlab::InsecureKeyFingerprint.new(params.fetch(:key)).fingerprint + end + key = Key.find_by(fingerprint: fingerprint) + not_found!("Key") if key.nil? + present key, with: Entities::SSHKey + end + # rubocop: enable CodeReuse/ActiveRecord + + # + # Discover user by ssh key, user id or username + # + # rubocop: disable CodeReuse/ActiveRecord + get "/discover" do + if params[:key_id] + key = Key.find(params[:key_id]) + user = key.user + elsif params[:user_id] + user = User.find_by(id: params[:user_id]) + elsif params[:username] + user = UserFinder.new(params[:username]).find_by_username + end + + present user, with: Entities::UserSafe + end + # rubocop: enable CodeReuse/ActiveRecord + + get "/check" do + { + api_version: API.version, + gitlab_version: Gitlab::VERSION, + gitlab_rev: Gitlab.revision, + redis: redis_ping + } + end + + get "/broadcast_messages" do + if messages = BroadcastMessage.current + present messages, with: Entities::BroadcastMessage + else + [] + end + end + + get "/broadcast_message" do + if message = BroadcastMessage.current&.last + present message, with: Entities::BroadcastMessage + else + {} + end + end + + # rubocop: disable CodeReuse/ActiveRecord + post '/two_factor_recovery_codes' do + status 200 + + if params[:key_id] + key = Key.find_by(id: params[:key_id]) + + if key + key.update_last_used_at + else + break { 'success' => false, 'message' => 'Could not find the given key' } + end + + if key.is_a?(DeployKey) + break { success: false, message: 'Deploy keys cannot be used to retrieve recovery codes' } + end + + user = key.user + + unless user + break { success: false, message: 'Could not find a user for the given key' } + end + elsif params[:user_id] + user = User.find_by(id: params[:user_id]) + + unless user + break { success: false, message: 'Could not find the given user' } + end + end + + unless user.two_factor_enabled? + break { success: false, message: 'Two-factor authentication is not enabled for this user' } + end + + codes = nil + + ::Users::UpdateService.new(current_user, user: user).execute! do |user| + codes = user.generate_otp_backup_codes! + end + + { success: true, recovery_codes: codes } + end + # rubocop: enable CodeReuse/ActiveRecord + + post '/pre_receive' do + status 200 + + reference_counter_increased = Gitlab::ReferenceCounter.new(params[:gl_repository]).increase + + { reference_counter_increased: reference_counter_increased } + end + + post "/notify_post_receive" do + status 200 + + # TODO: Re-enable when Gitaly is processing the post-receive notification + # return unless Gitlab::GitalyClient.enabled? + # + # begin + # repository = wiki? ? project.wiki.repository : project.repository + # Gitlab::GitalyClient::NotificationService.new(repository.raw_repository).post_receive + # rescue GRPC::Unavailable => e + # render_api_error!(e, 500) + # end + end + + post '/post_receive' do + status 200 + + response = Gitlab::InternalPostReceive::Response.new + user = identify(params[:identifier]) + project = Gitlab::GlRepository.parse(params[:gl_repository]).first + push_options = Gitlab::PushOptions.new(params[:push_options]) + + response.reference_counter_decreased = Gitlab::ReferenceCounter.new(params[:gl_repository]).decrease + + PostReceive.perform_async(params[:gl_repository], params[:identifier], + params[:changes], push_options.as_json) + + mr_options = push_options.get(:merge_request) + if mr_options.present? + message = process_mr_push_options(mr_options, project, user, params[:changes]) + response.add_alert_message(message) + end + + broadcast_message = BroadcastMessage.current&.last&.message + response.add_alert_message(broadcast_message) + + response.add_merge_request_urls(merge_request_urls) + + # A user is not guaranteed to be returned; an orphaned write deploy + # key could be used + if user + redirect_message = Gitlab::Checks::ProjectMoved.fetch_message(user.id, project.id) + project_created_message = Gitlab::Checks::ProjectCreated.fetch_message(user.id, project.id) + + response.add_basic_message(redirect_message) + response.add_basic_message(project_created_message) + end + + present response, with: Entities::InternalPostReceive::Response + end + end + end + end +end diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal/base_spec.rb index c94f6d22e74..a56527073c7 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal/base_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe API::Internal do +describe API::Internal::Base do set(:user) { create(:user) } let(:key) { create(:key, user: user) } set(:project) { create(:project, :repository, :wiki_repo) } |