summaryrefslogtreecommitdiff
path: root/lib/gitlab/git_access_snippet.rb
blob: d05ffe9b5085623299ad677e7ef25e3cba6e30a5 (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
# 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 :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_COUNT, 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
  end
end