File: git_http_controller.rb

package info (click to toggle)
gitlab 17.6.5-19
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 629,368 kB
  • sloc: ruby: 1,915,304; javascript: 557,307; sql: 60,639; xml: 6,509; sh: 4,567; makefile: 1,239; python: 406
file content (152 lines) | stat: -rw-r--r-- 4,226 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# frozen_string_literal: true

module Repositories
  class GitHttpController < Repositories::GitHttpClientController
    include WorkhorseRequest

    before_action :access_check
    prepend_before_action :deny_head_requests, only: [:info_refs]

    rescue_from Gitlab::GitAccess::ForbiddenError, with: :render_403_with_exception
    rescue_from JWT::DecodeError, with: :render_403_with_exception
    rescue_from Gitlab::GitAccess::NotFoundError, with: :render_404_with_exception
    rescue_from Gitlab::GitAccessProject::CreationError, with: :render_422_with_exception
    rescue_from Gitlab::GitAccess::TimeoutError, with: :render_503_with_exception
    rescue_from GRPC::Unavailable do |e|
      render_503_with_exception(e, message: 'The git server, Gitaly, is not available at this time. Please contact your administrator.')
    end

    # GET /foo/bar.git/info/refs?service=git-upload-pack (git pull)
    # GET /foo/bar.git/info/refs?service=git-receive-pack (git push)
    def info_refs
      log_user_activity if upload_pack?

      render_ok
    end

    # POST /foo/bar.git/git-upload-pack (git pull)
    def git_upload_pack
      update_fetch_statistics

      render_ok
    end

    # POST /foo/bar.git/git-receive-pack" (git push)
    def git_receive_pack
      render_ok
    end

    # POST /foo/bar.git/ssh-upload-pack" (git pull via SSH)
    def ssh_upload_pack
      render plain: "Not found", status: :not_found
    end

    # POST /foo/bar.git/ssh-receive-pack" (git push via SSH)
    def ssh_receive_pack
      render plain: "Not found", status: :not_found
    end

    private

    def deny_head_requests
      head :forbidden if request.head?
    end

    def download_request?
      upload_pack?
    end

    def upload_pack?
      git_command == 'git-upload-pack'
    end

    def git_command
      case action_name
      when 'info_refs'
        params[:service]
      when 'ssh_upload_pack'
        'git-upload-pack'
      when 'ssh_receive_pack'
        'git-receive-pack'
      else
        action_name.dasherize
      end
    end

    def render_ok
      set_workhorse_internal_api_content_type
      render json: Gitlab::Workhorse.git_http_ok(repository, repo_type, user, action_name)
    end

    def render_403_with_exception(exception)
      render plain: exception.message, status: :forbidden
    end

    def render_404_with_exception(exception)
      render plain: exception.message, status: :not_found
    end

    def render_422_with_exception(exception)
      render plain: exception.message, status: :unprocessable_entity
    end

    def render_503_with_exception(exception, message: nil)
      render plain: message || exception.message, status: :service_unavailable
    end

    def update_fetch_statistics
      return unless project
      return if Gitlab::Database.read_only?
      return unless repo_type.project?
      return if Feature.enabled?(:disable_git_http_fetch_writes)

      Projects::FetchStatisticsIncrementService.new(project).execute
    end

    def access
      @access ||= access_klass.new(access_actor, container, 'http',
        authentication_abilities: authentication_abilities,
        repository_path: repository_path,
        redirected_path: redirected_path,
        auth_result_type: auth_result_type)
    end

    def access_actor
      return user if user

      :ci if ci?
    end

    def access_check
      access.check(git_command, Gitlab::GitAccess::ANY)

      @project = @container = access.container if repo_type.project? && !container
    end

    def access_klass
      @access_klass ||= repo_type.access_checker_class
    end

    def log_user_activity
      Users::ActivityService.new(author: user, project: project, namespace: project&.namespace).execute

      return unless project && user

      Gitlab::EventStore.publish(
        Users::ActivityEvent.new(data: {
          user_id: user.id,
          namespace_id: project.root_ancestor.id
        })
      )
    end

    def append_info_to_payload(payload)
      super

      payload[:metadata] ||= {}
      payload[:metadata][:repository_storage] = project&.repository_storage
    end
  end
end

Repositories::GitHttpController.prepend_mod_with('Repositories::GitHttpController')