Module: API::Helpers

Defined in:
lib/api/helpers.rb

Constant Summary collapse

PRIVATE_TOKEN_HEADER =
"HTTP_PRIVATE_TOKEN"
PRIVATE_TOKEN_PARAM =
:private_token
SUDO_HEADER =
"HTTP_SUDO"
SUDO_PARAM =
:sudo

Instance Method Summary collapse

Instance Method Details

#attributes_for_keys(keys, custom_params = nil) ⇒ Object


152
153
154
155
156
157
158
159
160
161
# File 'lib/api/helpers.rb', line 152

def attributes_for_keys(keys, custom_params = nil)
  params_hash = custom_params || params
  attrs = {}
  keys.each do |key|
    if params_hash[key].present? or (params_hash.has_key?(key) and params_hash[key] == false)
      attrs[key] = params_hash[key]
    end
  end
  ActionController::Parameters.new(attrs).permit!
end

#authenticate!Object


104
105
106
# File 'lib/api/helpers.rb', line 104

def authenticate!
  unauthorized! unless current_user
end

#authenticate_by_gitlab_shell_token!Object


108
109
110
111
112
113
# File 'lib/api/helpers.rb', line 108

def authenticate_by_gitlab_shell_token!
  input = params['secret_token'].try(:chomp)
  unless Devise.secure_compare(secret_token, input)
    unauthorized!
  end
end

#authenticated_as_admin!Object


115
116
117
# File 'lib/api/helpers.rb', line 115

def authenticated_as_admin!
  forbidden! unless current_user.is_admin?
end

#authorize!(action, subject) ⇒ Object


119
120
121
# File 'lib/api/helpers.rb', line 119

def authorize!(action, subject)
  forbidden! unless abilities.allowed?(current_user, action, subject)
end

#authorize_admin_projectObject


127
128
129
# File 'lib/api/helpers.rb', line 127

def authorize_admin_project
  authorize! :admin_project, user_project
end

#authorize_push_projectObject


123
124
125
# File 'lib/api/helpers.rb', line 123

def authorize_push_project
  authorize! :push_code, user_project
end

#bad_request!(attribute) ⇒ Object


230
231
232
233
234
# File 'lib/api/helpers.rb', line 230

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

#can?(object, action, subject) ⇒ Boolean

Returns:

  • (Boolean)

137
138
139
# File 'lib/api/helpers.rb', line 137

def can?(object, action, subject)
  abilities.allowed?(object, action, subject)
end

#conflict!(message = nil) ⇒ Object


251
252
253
# File 'lib/api/helpers.rb', line 251

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

#current_userObject


12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/api/helpers.rb', line 12

def current_user
  private_token = (params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]).to_s
  @current_user ||= (User.find_by(authentication_token: private_token) || doorkeeper_guard)

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

  identifier = sudo_identifier()

  # If the sudo is the current user do nothing
  if identifier && !(@current_user.id == identifier || @current_user.username == identifier)
    render_api_error!('403 Forbidden: Must be admin to use sudo', 403) unless @current_user.is_admin?
    @current_user = User.by_username_or_id(identifier)
    not_found!("No user id or username for: #{identifier}") if @current_user.nil?
  end

  @current_user
end

#datetime_attributes!(*keys) ⇒ Object

Checks the occurrences of datetime attributes, each attribute if present in the params hash must be in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ) or a Bad Request error is invoked.

Parameters:

keys (required) - An array consisting of elements that must be parseable as dates from the params hash

191
192
193
194
195
196
197
198
199
200
# File 'lib/api/helpers.rb', line 191

def datetime_attributes!(*keys)
  keys.each do |key|
    begin
      params[key] = Time.xmlschema(params[key]) if params[key].present?
    rescue ArgumentError
      message = "\"" + key.to_s + "\" must be a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ"
      render_api_error!(message, 400)
    end
  end
end

#file_to_large!Object


255
256
257
# File 'lib/api/helpers.rb', line 255

def file_to_large!
  render_api_error!('413 Request Entity Too Large', 413)
end

#filter_by_iid(items, iid) ⇒ Object


218
219
220
# File 'lib/api/helpers.rb', line 218

def filter_by_iid(items, iid)
  items.where(iid: iid)
end

#filter_projects(projects) ⇒ Object

Projects helpers


275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/api/helpers.rb', line 275

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[:visibility].present?
    projects = projects.search_by_visibility(params[:visibility])
  end

  projects.reorder(project_order_by => project_sort)
end

#find_group(id) ⇒ Object


84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/api/helpers.rb', line 84

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
    not_found!('Group')
  end
end

#find_project(id) ⇒ Object


48
49
50
51
52
53
54
55
56
# File 'lib/api/helpers.rb', line 48

def find_project(id)
  project = Project.find_with_namespace(id) || Project.find_by(id: id)

  if project && can?(current_user, :read_project, project)
    project
  else
    nil
  end
end

#forbidden!(reason = nil) ⇒ Object

error helpers


224
225
226
227
228
# File 'lib/api/helpers.rb', line 224

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

#issuable_order_byObject


202
203
204
205
206
207
208
# File 'lib/api/helpers.rb', line 202

def issuable_order_by
  if params["order_by"] == 'updated_at'
    'updated_at'
  else
    'created_at'
  end
end

#issuable_sortObject


210
211
212
213
214
215
216
# File 'lib/api/helpers.rb', line 210

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

#not_allowed!Object


247
248
249
# File 'lib/api/helpers.rb', line 247

def not_allowed!
  render_api_error!('405 Method Not Allowed', 405)
end

#not_found!(resource = nil) ⇒ Object


236
237
238
239
240
241
# File 'lib/api/helpers.rb', line 236

def not_found!(resource = nil)
  message = ["404"]
  message << resource if resource
  message << "Not Found"
  render_api_error!(message.join(' '), 404)
end

#not_modified!Object


259
260
261
# File 'lib/api/helpers.rb', line 259

def not_modified!
  render_api_error!('304 Not Modified', 304)
end

#paginate(relation) ⇒ Object


98
99
100
101
102
# File 'lib/api/helpers.rb', line 98

def paginate(relation)
  relation.page(params[:page]).per(params[:per_page].to_i).tap do |data|
    add_pagination_headers(data)
  end
end

#parse_boolean(value) ⇒ Object


8
9
10
# File 'lib/api/helpers.rb', line 8

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

#present_file!(path, filename, content_type = 'application/octet-stream') ⇒ Object


334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# File 'lib/api/helpers.rb', line 334

def present_file!(path, filename, content_type = 'application/octet-stream')
  filename ||= File.basename(path)
  header['Content-Disposition'] = "attachment; filename=#{filename}"
  header['Content-Transfer-Encoding'] = 'binary'
  content_type content_type

  # Support download acceleration
  case headers['X-Sendfile-Type']
  when 'X-Sendfile'
    header['X-Sendfile'] = path
    body
  else
    file FileStreamer.new(path)
  end
end

#project_order_byObject


292
293
294
295
296
297
298
299
300
# File 'lib/api/helpers.rb', line 292

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

#project_serviceObject


58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/api/helpers.rb', line 58

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

#project_sortObject


302
303
304
305
306
307
308
# File 'lib/api/helpers.rb', line 302

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

#render_api_error!(message, status) ⇒ Object


269
270
271
# File 'lib/api/helpers.rb', line 269

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

#render_validation_error!(model) ⇒ Object


263
264
265
266
267
# File 'lib/api/helpers.rb', line 263

def render_validation_error!(model)
  if model.errors.any?
    render_api_error!(model.errors.messages || '400 Bad Request', 400)
  end
end

#require_gitlab_workhorse!Object


131
132
133
134
135
# File 'lib/api/helpers.rb', line 131

def require_gitlab_workhorse!
  unless env['HTTP_GITLAB_WORKHORSE'].present?
    forbidden!('Request should be executed via GitLab Workhorse')
  end
end

#required_attributes!(keys) ⇒ Object

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

146
147
148
149
150
# File 'lib/api/helpers.rb', line 146

def required_attributes!(keys)
  keys.each do |key|
    bad_request!(key) unless params[key].present?
  end
end

#send_service(service_method) ⇒ Object


74
75
76
# File 'lib/api/helpers.rb', line 74

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

#service_attributesObject


78
79
80
81
82
# File 'lib/api/helpers.rb', line 78

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

#sudo_identifierObject


32
33
34
35
36
37
38
39
40
41
# File 'lib/api/helpers.rb', line 32

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

  # Regex for integers
  if !!(identifier =~ /^[0-9]+$/)
    identifier.to_i
  else
    identifier
  end
end

#unauthorized!Object


243
244
245
# File 'lib/api/helpers.rb', line 243

def unauthorized!
  render_api_error!('401 Unauthorized', 401)
end

#uploaded_file(field, uploads_path) ⇒ Object

file helpers


312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
# File 'lib/api/helpers.rb', line 312

def uploaded_file(field, uploads_path)
  if params[field]
    bad_request!("#{field} is not a file") unless params[field].respond_to?(:filename)
    return params[field]
  end

  return nil unless params["#{field}.path"] && params["#{field}.name"]

  # sanitize file paths
  # this requires all paths to exist
  required_attributes! %W(#{field}.path)
  uploads_path = File.realpath(uploads_path)
  file_path = File.realpath(params["#{field}.path"])
  bad_request!('Bad file path') unless file_path.start_with?(uploads_path)

  UploadedFile.new(
    file_path,
    params["#{field}.name"],
    params["#{field}.type"] || 'application/octet-stream',
  )
end

#user_projectObject


43
44
45
46
# File 'lib/api/helpers.rb', line 43

def user_project
  @project ||= find_project(params[:id])
  @project || not_found!("Project")
end

#validate_access_level?(level) ⇒ Boolean

Returns:

  • (Boolean)

182
183
184
# File 'lib/api/helpers.rb', line 182

def validate_access_level?(level)
  Gitlab::Access.options_with_owner.values.include? level.to_i
end

#validate_label_params(params) ⇒ Object

Helper method for validating all labels against its names


164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/api/helpers.rb', line 164

def validate_label_params(params)
  errors = {}

  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)

      if label.invalid?
        errors[label.title] = label.errors
      end
    end
  end

  errors
end