helpers.rb 8.18 KB
Newer Older
1
module API
Nihad Abbasov's avatar
Nihad Abbasov committed
2
  module APIHelpers
3 4 5 6 7
    PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN"
    PRIVATE_TOKEN_PARAM = :private_token
    SUDO_HEADER ="HTTP_SUDO"
    SUDO_PARAM = :sudo

8 9 10 11
    def parse_boolean(value)
      [ true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON' ].include?(value)
    end

Nihad Abbasov's avatar
Nihad Abbasov committed
12
    def current_user
13
      private_token = (params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]).to_s
Valery Sizov's avatar
Valery Sizov committed
14
      @current_user ||= (User.find_by(authentication_token: private_token) || doorkeeper_guard)
15 16 17 18 19

      unless @current_user && Gitlab::UserAccess.allowed?(@current_user)
        return nil
      end

20
      identifier = sudo_identifier()
21

22
      # If the sudo is the current user do nothing
23
      if identifier && !(@current_user.id == identifier || @current_user.username == identifier)
24
        render_api_error!('403 Forbidden: Must be admin to use sudo', 403) unless @current_user.is_admin?
Nihad Abbasov's avatar
Nihad Abbasov committed
25
        @current_user = User.by_username_or_id(identifier)
Nihad Abbasov's avatar
Nihad Abbasov committed
26
        not_found!("No user id or username for: #{identifier}") if @current_user.nil?
27
      end
28

29 30 31 32
      @current_user
    end

    def sudo_identifier()
33
      identifier ||= params[SUDO_PARAM] ||= env[SUDO_HEADER]
34

35
      # Regex for integers
36
      if !!(identifier =~ /^[0-9]+$/)
37 38 39 40
        identifier.to_i
      else
        identifier
      end
Nihad Abbasov's avatar
Nihad Abbasov committed
41 42
    end

Nihad Abbasov's avatar
Nihad Abbasov committed
43
    def user_project
44
      @project ||= find_project(params[:id])
45
      @project || not_found!("Project")
46 47
    end

48
    def find_project(id)
49
      project = Project.find_with_namespace(id) || Project.find_by(id: id)
50 51 52

      if project && can?(current_user, :read_project, project)
        project
53
      else
54
        nil
55
      end
Nihad Abbasov's avatar
Nihad Abbasov committed
56 57
    end

Kirilll Zaitsev's avatar
Kirilll Zaitsev committed
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
    def project_service
      @project_service ||= begin
        underscored_service = params[:service_slug].underscore

        if Service.available_services_names.include?(underscored_service)
          user_project.build_missing_services

          service_method = "#{underscored_service}_service"
          
          send_service(service_method)
        end
      end
   
      @project_service || not_found!("Service")
    end

    def send_service(service_method)
      user_project.send(service_method)
    end

    def service_attributes
      @service_attributes ||= project_service.fields.inject([]) do |arr, hash|
        arr << hash[:name].to_sym
      end
    end

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
    def find_group(id)
      begin
        group = Group.find(id)
      rescue ActiveRecord::RecordNotFound
        group = Group.find_by!(path: id)
      end

      if can?(current_user, :read_group, group)
        group
      else
        forbidden!("#{current_user.username} lacks sufficient "\
        "access to #{group.name}")
      end
    end

99 100 101 102 103 104
    def paginate(relation)
      per_page  = params[:per_page].to_i
      paginated = relation.page(params[:page]).per(per_page)
      add_pagination_headers(paginated, per_page)

      paginated
Nihad Abbasov's avatar
Nihad Abbasov committed
105 106
    end

Nihad Abbasov's avatar
Nihad Abbasov committed
107
    def authenticate!
108
      unauthorized! unless current_user
Nihad Abbasov's avatar
Nihad Abbasov committed
109
    end
randx's avatar
randx committed
110

111
    def authenticate_by_gitlab_shell_token!
112 113 114 115
      input = params['secret_token'].try(:chomp)
      unless Devise.secure_compare(secret_token, input)
        unauthorized!
      end
116 117
    end

118 119 120 121
    def authenticated_as_admin!
      forbidden! unless current_user.is_admin?
    end

122
    def authorize!(action, subject)
randx's avatar
randx committed
123
      unless abilities.allowed?(current_user, action, subject)
124
        forbidden!
randx's avatar
randx committed
125 126 127
      end
    end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
128 129 130 131
    def authorize_push_project
      authorize! :push_code, user_project
    end

132 133 134 135
    def authorize_admin_project
      authorize! :admin_project, user_project
    end

136 137 138 139
    def can?(object, action, subject)
      abilities.allowed?(object, action, subject)
    end

140 141 142 143 144 145 146 147 148 149 150
    # Checks the occurrences of required attributes, each attribute must be present in the params hash
    # or a Bad Request error is invoked.
    #
    # Parameters:
    #   keys (required) - A hash consisting of keys that must be present
    def required_attributes!(keys)
      keys.each do |key|
        bad_request!(key) unless params[key].present?
      end
    end

Valery Sizov's avatar
Valery Sizov committed
151 152
    def attributes_for_keys(keys, custom_params = nil)
      params_hash = custom_params || params
Alex Denisov's avatar
Alex Denisov committed
153 154
      attrs = {}
      keys.each do |key|
155 156 157
        if params[key].present? or (params.has_key?(key) and params[key] == false)
          attrs[key] = params[key]
        end
Alex Denisov's avatar
Alex Denisov committed
158
      end
159
      ActionController::Parameters.new(attrs).permit!
Alex Denisov's avatar
Alex Denisov committed
160 161
    end

162 163
    # Helper method for validating all labels against its names
    def validate_label_params(params)
164 165
      errors = {}

166 167 168 169 170
      if params[:labels].present?
        params[:labels].split(',').each do |label_name|
          label = user_project.labels.create_with(
            color: Label::DEFAULT_COLOR).find_or_initialize_by(
              title: label_name.strip)
171

172
          if label.invalid?
173
            errors[label.title] = label.errors
174 175 176
          end
        end
      end
177 178

      errors
179 180
    end

181 182 183 184
    def validate_access_level?(level)
      Gitlab::Access.options_with_owner.values.include? level.to_i
    end

185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
    def issuable_order_by
      if params["order_by"] == 'updated_at'
        'updated_at'
      else
        'created_at'
      end
    end

    def issuable_sort
      if params["sort"] == 'asc'
        :asc
      else
        :desc
      end
    end

201 202 203 204
    def filter_by_iid(items, iid)
      items.where(iid: iid)
    end

205 206
    # error helpers

207 208 209 210
    def forbidden!(reason = nil)
      message = ['403 Forbidden']
      message << " - #{reason}" if reason
      render_api_error!(message.join(' '), 403)
211 212
    end

213 214 215 216 217 218
    def bad_request!(attribute)
      message = ["400 (Bad request)"]
      message << "\"" + attribute.to_s + "\" not given"
      render_api_error!(message.join(' '), 400)
    end

219 220 221 222
    def not_found!(resource = nil)
      message = ["404"]
      message << resource if resource
      message << "Not Found"
Alex Denisov's avatar
Alex Denisov committed
223
      render_api_error!(message.join(' '), 404)
224 225 226
    end

    def unauthorized!
Alex Denisov's avatar
Alex Denisov committed
227
      render_api_error!('401 Unauthorized', 401)
228 229 230
    end

    def not_allowed!
231 232 233 234 235 236 237 238
      render_api_error!('405 Method Not Allowed', 405)
    end

    def conflict!(message = nil)
      render_api_error!(message || '409 Conflict', 409)
    end

    def render_validation_error!(model)
239
      if model.errors.any?
240 241
        render_api_error!(model.errors.messages || '400 Bad Request', 400)
      end
Alex Denisov's avatar
Alex Denisov committed
242 243 244
    end

    def render_api_error!(message, status)
245
      error!({ 'message' => message }, status)
246 247
    end

Valery Sizov's avatar
Valery Sizov committed
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
    # Projects helpers

    def filter_projects(projects)
      # If the archived parameter is passed, limit results accordingly
      if params[:archived].present?
        projects = projects.where(archived: parse_boolean(params[:archived]))
      end

      if params[:search].present?
        projects = projects.search(params[:search])
      end

      if params[:ci_enabled_first].present?
        projects.includes(:gitlab_ci_service).
          reorder("services.active DESC, projects.#{project_order_by} #{project_sort}")
      else
        projects.reorder(project_order_by => project_sort)
      end
    end

    def project_order_by
      order_fields = %w(id name path created_at updated_at last_activity_at)

      if order_fields.include?(params['order_by'])
        params['order_by']
      else
        'created_at'
      end
    end

    def project_sort
      if params["sort"] == 'asc'
        :asc
      else
        :desc
      end
    end

286
    private
randx's avatar
randx committed
287

288 289 290 291 292 293 294 295 296 297 298 299
    def add_pagination_headers(paginated, per_page)
      request_url = request.url.split('?').first

      links = []
      links << %(<#{request_url}?page=#{paginated.current_page - 1}&per_page=#{per_page}>; rel="prev") unless paginated.first_page?
      links << %(<#{request_url}?page=#{paginated.current_page + 1}&per_page=#{per_page}>; rel="next") unless paginated.last_page?
      links << %(<#{request_url}?page=1&per_page=#{per_page}>; rel="first")
      links << %(<#{request_url}?page=#{paginated.total_pages}&per_page=#{per_page}>; rel="last")

      header 'Link', links.join(', ')
    end

randx's avatar
randx committed
300 301
    def abilities
      @abilities ||= begin
302 303 304 305
                       abilities = Six.new
                       abilities << Ability
                       abilities
                     end
randx's avatar
randx committed
306
    end
307 308

    def secret_token
309
      File.read(Gitlab.config.gitlab_shell.secret_file).chomp
310
    end
Vinnie Okada's avatar
Vinnie Okada committed
311 312 313 314 315

    def handle_member_errors(errors)
      error!(errors[:access_level], 422) if errors[:access_level].any?
      not_found!(errors)
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
316 317
  end
end