git_access.rb 5.9 KB
Newer Older
1 2 3 4 5
module Gitlab
  class GitAccess
    DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive }
    PUSH_COMMANDS = %w{ git-receive-pack }

6
    attr_reader :actor, :project, :protocol
7

8
    def initialize(actor, project, protocol)
9 10
      @actor    = actor
      @project  = project
11
      @protocol = protocol
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
    end

    def user
      return @user if defined?(@user)

      @user =
        case actor
        when User
          actor
        when DeployKey
          nil
        when Key
          actor.user
        end
    end

    def deploy_key
      actor if actor.is_a?(DeployKey)
    end

    def can_push_to_branch?(ref)
      return false unless user

      if project.protected_branch?(ref) && !project.developers_can_push_to_protected_branch?(ref)
        user.can?(:push_code_to_protected_branches, project)
      else
        user.can?(:push_code, project)
      end
    end

    def can_read_project?
      if user
        user.can?(:read_project, project)
      elsif deploy_key
        deploy_key.projects.include?(project)
      else
        false
      end
50 51 52
    end

    def check(cmd, changes = nil)
53 54
      return build_status_object(false, "Git access over #{protocol.upcase} is not allowed") unless protocol_allowed?

D
Douwe Maan 已提交
55 56 57 58
      unless actor
        return build_status_object(false, "No user or key was provided.")
      end

59
      if user && !user_allowed?
D
Douwe Maan 已提交
60 61 62
        return build_status_object(false, "Your account has been blocked.")
      end

63
      unless project && can_read_project?
D
Douwe Maan 已提交
64 65 66
        return build_status_object(false, 'The project you were looking for could not be found.')
      end

67 68
      case cmd
      when *DOWNLOAD_COMMANDS
69
        download_access_check
70
      when *PUSH_COMMANDS
71
        push_access_check(changes)
72
      else
D
Douwe Maan 已提交
73
        build_status_object(false, "The command you're trying to execute is not allowed.")
74 75 76
      end
    end

77 78 79 80
    def download_access_check
      if user
        user_download_access_check
      elsif deploy_key
D
Douwe Maan 已提交
81
        build_status_object(true)
82 83 84 85 86
      else
        raise 'Wrong actor'
      end
    end

87 88 89 90
    def push_access_check(changes)
      if user
        user_push_access_check(changes)
      elsif deploy_key
D
Douwe Maan 已提交
91
        build_status_object(false, "Deploy keys are not allowed to push code.")
92 93 94 95 96 97
      else
        raise 'Wrong actor'
      end
    end

    def user_download_access_check
98
      unless user.can?(:download_code, project)
D
Douwe Maan 已提交
99
        return build_status_object(false, "You are not allowed to download code from this project.")
100 101
      end

D
Douwe Maan 已提交
102
      build_status_object(true)
103 104 105
    end

    def user_push_access_check(changes)
106 107 108 109 110
      if changes.blank?
        return build_status_object(true)
      end

      unless project.repository.exists?
D
Douwe Maan 已提交
111
        return build_status_object(false, "A repository for this project does not exist yet.")
112
      end
113 114 115 116

      changes = changes.lines if changes.kind_of?(String)

      # Iterate over all changes to find if user allowed all of them to be applied
117
      changes.map(&:strip).reject(&:blank?).each do |change|
118
        status = change_access_check(change)
119
        unless status.allowed?
120
          # If user does not have access to make at least one change - cancel all push
121
          return status
122 123 124
        end
      end

125
      build_status_object(true)
126 127
    end

128 129 130 131 132
    def can_user_do_action?(action)
      @permission_cache ||= {}
      @permission_cache[action] ||= user.can?(action, project)
    end

133
    def change_access_check(change)
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
      oldrev, newrev, ref = change.split(' ')

      action =
        if project.protected_branch?(branch_name(ref))
          protected_branch_action(oldrev, newrev, branch_name(ref))
        elsif (tag_ref = tag_name(ref)) && protected_tag?(tag_ref)
          # Prevent any changes to existing git tag unless user has permissions
          :admin_project
        else
          :push_code
        end

      unless can_user_do_action?(action)
        status =
          case action
          when :force_push_code_to_protected_branches
            build_status_object(false, "You are not allowed to force push code to a protected branch on this project.")
          when :remove_protected_branches
            build_status_object(false, "You are not allowed to deleted protected branches from this project.")
          when :push_code_to_protected_branches
            build_status_object(false, "You are not allowed to push code to protected branches on this project.")
          when :admin_project
            build_status_object(false, "You are not allowed to change existing tags on this project.")
          else # :push_code
            build_status_object(false, "You are not allowed to push code to this project.")
          end
        return status
      end

      build_status_object(true)
    end

    def forced_push?(oldrev, newrev)
      Gitlab::ForcePushCheck.force_push?(project, oldrev, newrev)
168 169
    end

170 171 172 173
    def protocol_allowed?
      Gitlab::ProtocolAccess.allowed?(protocol)
    end

174 175
    private

176 177 178 179 180 181 182 183 184 185 186 187
    def protected_branch_action(oldrev, newrev, branch_name)
      # we dont allow force push to protected branch
      if forced_push?(oldrev, newrev)
        :force_push_code_to_protected_branches
      elsif Gitlab::Git.blank_ref?(newrev)
        # and we dont allow remove of protected branch
        :remove_protected_branches
      elsif project.developers_can_push_to_protected_branch?(branch_name)
        :push_code
      else
        :push_code_to_protected_branches
      end
188 189
    end

190 191
    def protected_tag?(tag_name)
      project.repository.tag_exists?(tag_name)
192
    end
193

194 195 196 197 198 199 200 201
    def user_allowed?
      Gitlab::UserAccess.allowed?(user)
    end

    def branch_name(ref)
      ref = ref.to_s
      if Gitlab::Git.branch_ref?(ref)
        Gitlab::Git.ref_name(ref)
202
      else
203
        nil
204 205
      end
    end
206

207 208 209 210 211 212 213
    def tag_name(ref)
      ref = ref.to_s
      if Gitlab::Git.tag_ref?(ref)
        Gitlab::Git.ref_name(ref)
      else
        nil
      end
T
Timothy Andrew 已提交
214 215
    end

216 217
    protected

218 219 220
    def build_status_object(status, message = '')
      GitAccessStatus.new(status, message)
    end
221 222
  end
end