Welcome to mirror list, hosted at ThFree Co, Russian Federation.

internal_helpers.rb « helpers « api « lib - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: f66f899c98b6a61ff5a5e3f1e1a9d27d5e6dab86 (plain)
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# frozen_string_literal: true

module API
  module Helpers
    module InternalHelpers
      attr_reader :redirected_path

      UNKNOWN_CHECK_RESULT_ERROR = 'Unknown check result'

      delegate :wiki?, to: :repo_type

      def actor
        @actor ||= Support::GitAccessActor.from_params(params)
      end

      # rubocop:disable Gitlab/ModuleWithInstanceVariables
      def repo_type
        parse_repo_path unless defined?(@repo_type)
        @repo_type
      end

      def project
        parse_repo_path unless defined?(@project)
        @project
      end

      def container
        parse_repo_path unless defined?(@container)
        @container
      end
      # rubocop:enable Gitlab/ModuleWithInstanceVariables

      def access_check_result
        with_admin_mode_bypass!(actor.user&.id) do
          access_check!(actor, params)
        end
      rescue Gitlab::GitAccess::ForbiddenError => e
        # The return code needs to be 401. If we return 403
        # the custom message we return won't be shown to the user
        # and, instead, the default message 'GitLab: API is not accessible'
        # will be displayed
        response_with_status(code: 401, success: false, message: e.message)
      rescue Gitlab::GitAccess::TimeoutError => e
        response_with_status(code: 503, success: false, message: e.message)
      rescue Gitlab::GitAccess::NotFoundError => e
        response_with_status(code: 404, success: false, message: e.message)
      end

      # rubocop:disable Gitlab/ModuleWithInstanceVariables
      def access_check!(actor, params)
        access_checker = access_checker_for(actor, params[:protocol])
        access_checker.check(params[:action], params[:changes]).tap do |result|
          break result if @project || !repo_type.project?

          # If we have created a project directly from a git push
          # we have to assign its value to both @project and @container
          @project = @container = access_checker.container
        end
      end
      # rubocop:enable Gitlab/ModuleWithInstanceVariables

      def access_checker_for(actor, protocol)
        access_checker_klass.new(actor.key_or_user, container, protocol,
          authentication_abilities: ssh_authentication_abilities,
          repository_path: repository_path,
          redirected_path: redirected_path)
      end

      def access_checker_klass
        repo_type.access_checker_class
      end

      def ssh_authentication_abilities
        [
          :read_project,
          :download_code,
          :push_code
        ]
      end

      def parse_env
        return {} if params[:env].blank?

        Gitlab::Json.parse(params[:env])
      rescue JSON::ParserError
        {}
      end

      def log_user_activity(actor)
        commands = Gitlab::GitAccess::DOWNLOAD_COMMANDS
        commands += Gitlab::GitAccess::PUSH_COMMANDS if Feature.enabled?(:log_user_git_push_activity)

        return unless commands.include?(params[:action])

        ::Users::ActivityService.new(author: actor, namespace: project&.namespace, project: project).execute
      end

      def redis_ping
        result = Gitlab::Redis::SharedState.with { |redis| redis.ping }

        result == 'PONG'
      rescue StandardError => e
        Gitlab::AppLogger.warn("GitLab: An unexpected error occurred in pinging to Redis: #{e}")
        false
      end

      def response_with_status(code: 200, success: true, message: nil, **extra_options)
        status code
        { status: success, message: message }.merge(extra_options).compact
      end

      def unsuccessful_response?(response)
        response.is_a?(Hash) && response[:status] == false
      end

      def with_admin_mode_bypass!(actor_id, &block)
        return yield unless Gitlab::CurrentSettings.admin_mode

        Gitlab::Auth::CurrentUserMode.bypass_session!(actor_id, &block)
      end

      def send_git_audit_streaming_event(msg)
        # Defined in EE
      end

      private

      def repository_path
        if container
          "#{container.full_path}.git"
        elsif params[:project]
          # When the project doesn't exist, we still need to pass on the path
          # to support auto-creation in `GitAccessProject`.
          #
          # For consistency with the Git HTTP controllers, we normalize the path
          # to remove a leading slash and ensure a trailing `.git`.
          #
          # NOTE: For GitLab Shell, `params[:project]` is the full repository path
          # from the SSH command, with an optional trailing `.git`.
          "#{params[:project].delete_prefix('/').delete_suffix('.git')}.git"
        end
      end

      # rubocop:disable Gitlab/ModuleWithInstanceVariables
      def parse_repo_path
        @container, @project, @repo_type, @redirected_path =
          if params[:gl_repository]
            Gitlab::GlRepository.parse(params[:gl_repository])
          elsif params[:project]
            Gitlab::RepoPath.parse(params[:project])
          end
      end
      # rubocop:enable Gitlab/ModuleWithInstanceVariables

      # Repository id to pass between components that don't share/don't have
      # access to the same filesystem mounts
      def gl_repository
        repo_type.identifier_for_container(container)
      end

      def gl_repository_path
        repository.full_path
      end

      # Return the repository for the detected type and container
      #
      # @returns [Repository]
      def repository
        @repository ||= repo_type.repository_for(container)
      end

      # Return the Gitaly Address if it is enabled
      def gitaly_payload(action)
        return unless %w[git-receive-pack git-upload-pack git-upload-archive].include?(action)

        {
          repository: repository.gitaly_repository.to_h,
          address: Gitlab::GitalyClient.address(repository.shard),
          token: Gitlab::GitalyClient.token(repository.shard),
          features: Feature::Gitaly.server_feature_flags(
            user: ::Feature::Gitaly.user_actor(actor.user),
            repository: repository,
            project: ::Feature::Gitaly.project_actor(repository.container),
            group: ::Feature::Gitaly.group_actor(repository.container)
          )
        }
      end
    end
  end
end

API::Helpers::InternalHelpers.prepend_mod