summaryrefslogtreecommitdiff
path: root/lib/gitlab/git_access_snippet.rb
blob: 70db4271469f6ca768977819e85eb94450bf0d51 (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
# frozen_string_literal: true

module Gitlab
  class GitAccessSnippet < GitAccess
    extend ::Gitlab::Utils::Override

    ERROR_MESSAGES = {
      authentication_mechanism: 'The authentication mechanism is not supported.',
      read_snippet: 'You are not allowed to read this snippet.',
      update_snippet: 'You are not allowed to update this snippet.',
      snippet_not_found: 'The snippet you were looking for could not be found.',
      repository_not_found: 'The snippet repository you were looking for could not be found.'
    }.freeze

    attr_reader :snippet

    alias_method :container, :snippet

    def initialize(actor, snippet, protocol, **kwargs)
      @snippet = snippet

      super(actor, snippet&.project, protocol, **kwargs)

      @auth_result_type = nil
      @authentication_abilities &= [:download_code, :push_code]
    end

    def check(cmd, changes)
      # TODO: Investigate if expanding actor/authentication types are needed.
      # https://gitlab.com/gitlab-org/gitlab/issues/202190
      if actor && !actor.is_a?(User) && !actor.instance_of?(Key)
        raise ForbiddenError, ERROR_MESSAGES[:authentication_mechanism]
      end

      check_snippet_accessibility!

      super
    end

    private

    override :check_project!
    def check_project!(cmd, changes)
      return unless snippet.is_a?(ProjectSnippet)

      check_namespace!
      check_project_accessibility!
      add_project_moved_message!
    end

    override :check_push_access!
    def check_push_access!
      raise ForbiddenError, ERROR_MESSAGES[:update_snippet] unless user

      check_change_access!
    end

    def check_snippet_accessibility!
      if snippet.blank?
        raise NotFoundError, ERROR_MESSAGES[:snippet_not_found]
      end
    end

    override :can_read_project?
    def can_read_project?
      return true if user&.migration_bot?

      super
    end

    override :check_download_access!
    def check_download_access!
      passed = guest_can_download_code? || user_can_download_code?

      unless passed
        raise ForbiddenError, ERROR_MESSAGES[:read_snippet]
      end
    end

    override :guest_can_download_code?
    def guest_can_download_code?
      Guest.can?(:read_snippet, snippet)
    end

    override :user_can_download_code?
    def user_can_download_code?
      authentication_abilities.include?(:download_code) && user_access.can_do_action?(:read_snippet)
    end

    override :check_change_access!
    def check_change_access!
      unless user_access.can_do_action?(:update_snippet)
        raise ForbiddenError, ERROR_MESSAGES[:update_snippet]
      end

      check_size_before_push!

      changes_list.each do |change|
        # If user does not have access to make at least one change, cancel all
        # push by allowing the exception to bubble up
        check_single_change_access(change)
      end

      check_push_size!
    end

    def check_single_change_access(change)
      Checks::SnippetCheck.new(change, logger: logger).validate!
      Checks::PushFileCountCheck.new(change, repository: repository, limit: Snippet.max_file_limit(user), logger: logger).validate!
    rescue Checks::TimedLogger::TimeoutError
      raise TimeoutError, logger.full_message
    end

    override :check_repository_existence!
    def check_repository_existence!
      unless repository.exists?
        raise NotFoundError, ERROR_MESSAGES[:repository_not_found]
      end
    end

    override :user_access
    def user_access
      @user_access ||= UserAccessSnippet.new(user, snippet: snippet)
    end

    # TODO: Implement EE/Geo https://gitlab.com/gitlab-org/gitlab/issues/205629
    override :check_custom_action
    def check_custom_action(cmd)
      nil
    end

    override :check_size_limit?
    def check_size_limit?
      return false if user&.migration_bot?

      super
    end
  end
end