File: endpoint.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 (104 lines) | stat: -rw-r--r-- 3,972 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
# frozen_string_literal: true

module API
  module Concerns
    module VirtualRegistries
      module Packages
        module Endpoint
          extend ActiveSupport::Concern

          NO_BROWSER_EXECUTION_RESPONSE_HEADERS = { 'Content-Security-Policy' => "default-src 'none'" }.freeze
          MAJOR_BROWSERS = %i[webkit firefox ie edge opera chrome].freeze
          WEB_BROWSER_ERROR_MESSAGE = 'This endpoint is not meant to be accessed by a web browser.'
          UPSTREAM_GID_HEADER = 'X-Gitlab-Virtual-Registry-Upstream-Global-Id'
          MAX_FILE_SIZE = 5.gigabytes

          included do
            helpers do
              def require_non_web_browser!
                browser = ::Browser.new(request.user_agent)
                bad_request!(WEB_BROWSER_ERROR_MESSAGE) if MAJOR_BROWSERS.any? { |b| browser.method(:"#{b}?").call }
              end

              def send_successful_response_from(service_response:)
                action, action_params = service_response.to_h.values_at(:action, :action_params)
                case action
                when :workhorse_upload_url
                  workhorse_upload_url(**action_params.slice(:url, :upstream))
                when :download_file
                  present_carrierwave_file!(
                    action_params[:file],
                    content_type: action_params[:content_type],
                    content_disposition: 'inline'
                  )
                when :download_digest
                  content_type 'text/plain'
                  env['api.format'] = :binary # to return data as-is
                  body action_params[:digest]
                end
              end

              def send_error_response_from!(service_response:)
                case service_response.reason
                when :unauthorized
                  unauthorized!
                when :file_not_found_on_upstreams, :digest_not_found_in_cached_responses
                  not_found!(service_response.message)
                else
                  bad_request!(service_response.message)
                end
              end

              def workhorse_upload_url(url:, upstream:)
                allow_localhost = Gitlab.dev_or_test_env? ||
                  Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services?
                allowed_uris = ObjectStoreSettings.enabled_endpoint_uris
                send_workhorse_headers(
                  Gitlab::Workhorse.send_dependency(
                    upstream.headers,
                    url,
                    response_headers: NO_BROWSER_EXECUTION_RESPONSE_HEADERS,
                    allow_localhost: allow_localhost,
                    allowed_uris: allowed_uris,
                    ssrf_filter: true,
                    upload_config: {
                      headers: { UPSTREAM_GID_HEADER => upstream.to_global_id.to_s },
                      authorized_upload_response: authorized_upload_response
                    }
                  )
                )
              end

              def authorized_upload_response
                ::VirtualRegistries::CachedResponseUploader.workhorse_authorize(
                  has_length: true,
                  maximum_size: MAX_FILE_SIZE,
                  use_final_store_path: true,
                  final_store_path_root_id: registry.id
                )
              end

              def send_workhorse_headers(headers)
                header(*headers)
                env['api.format'] = :binary
                content_type 'application/octet-stream'
                status :ok
                body ''
              end

              def ok_empty_response
                status :ok
                env['api.format'] = :binary # to return data as-is
                body ''
              end
            end

            after_validation do
              require_non_web_browser!
            end
          end
        end
      end
    end
  end
end