summaryrefslogtreecommitdiff
path: root/lib/api/helpers.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/api/helpers.rb')
-rw-r--r--lib/api/helpers.rb358
1 files changed, 149 insertions, 209 deletions
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 67473f300c9..2c73a6fdc4e 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -1,78 +1,56 @@
module API
module Helpers
- PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN"
- PRIVATE_TOKEN_PARAM = :private_token
- SUDO_HEADER = "HTTP_SUDO"
- SUDO_PARAM = :sudo
-
- def to_boolean(value)
- return true if value =~ /^(true|t|yes|y|1|on)$/i
- return false if value =~ /^(false|f|no|n|0|off)$/i
+ include Gitlab::Utils
+ include Helpers::Pagination
- nil
- end
+ SUDO_HEADER = "HTTP_SUDO".freeze
+ SUDO_PARAM = :sudo
- def private_token
- params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]
+ def declared_params(options = {})
+ options = { include_parent_namespaces: false }.merge(options)
+ declared(params, options).to_h.symbolize_keys
end
- def warden
- env['warden']
- end
+ def current_user
+ return @current_user if defined?(@current_user)
- # Check the Rails session for valid authentication details
- #
- # Until CSRF protection is added to the API, disallow this method for
- # state-changing endpoints
- def find_user_from_warden
- warden.try(:authenticate) if %w[GET HEAD].include?(env['REQUEST_METHOD'])
- end
+ @current_user = initial_current_user
- def find_user_by_private_token
- token = private_token
- return nil unless token.present?
+ sudo!
- User.find_by_authentication_token(token) || User.find_by_personal_access_token(token)
+ @current_user
end
- def current_user
- @current_user ||= find_user_by_private_token
- @current_user ||= doorkeeper_guard
- @current_user ||= find_user_from_warden
+ def sudo?
+ initial_current_user != current_user
+ end
- unless @current_user && Gitlab::UserAccess.new(@current_user).allowed?
- return nil
- end
+ def user_project
+ @project ||= find_project!(params[:id])
+ end
- identifier = sudo_identifier()
+ def available_labels
+ @available_labels ||= LabelsFinder.new(current_user, project_id: user_project.id).execute
+ end
- # If the sudo is the current user do nothing
- if identifier && !(@current_user.id == identifier || @current_user.username == identifier)
- forbidden!('Must be admin to use sudo') 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?
+ def find_user(id)
+ if id =~ /^\d+$/
+ User.find_by(id: id)
+ else
+ User.find_by(username: id)
end
-
- @current_user
end
- def sudo_identifier
- identifier ||= params[SUDO_PARAM] || env[SUDO_HEADER]
-
- # Regex for integers
- if !!(identifier =~ /\A[0-9]+\z/)
- identifier.to_i
+ def find_project(id)
+ if id =~ /^\d+$/
+ Project.find_by(id: id)
else
- identifier
+ Project.find_by_full_path(id)
end
end
- def user_project
- @project ||= find_project(params[:id])
- end
-
- def find_project(id)
- project = Project.find_with_namespace(id) || Project.find_by(id: id)
+ def find_project!(id)
+ project = find_project(id)
if can?(current_user, :read_project, project)
project
@@ -81,34 +59,16 @@ module API
end
end
- 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
+ def find_group(id)
+ if id =~ /^\d+$/
+ Group.find_by(id: id)
+ else
+ Group.find_by_full_path(id)
end
end
- def find_group(id)
- group = Group.find_by(path: id) || Group.find_by(id: id)
+ def find_group!(id)
+ group = find_group(id)
if can?(current_user, :read_group, group)
group
@@ -118,24 +78,35 @@ module API
end
def find_project_label(id)
- label = user_project.labels.find_by_id(id) || user_project.labels.find_by_title(id)
+ label = available_labels.find_by_id(id) || available_labels.find_by_title(id)
label || not_found!('Label')
end
- def find_project_issue(id)
- issue = user_project.issues.find(id)
- not_found! unless can?(current_user, :read_issue, issue)
- issue
+ def find_project_issue(iid)
+ IssuesFinder.new(current_user, project_id: user_project.id).find_by!(iid: iid)
end
- def paginate(relation)
- relation.page(params[:page]).per(params[:per_page].to_i).tap do |data|
- add_pagination_headers(data)
- end
+ def find_project_merge_request(iid)
+ MergeRequestsFinder.new(current_user, project_id: user_project.id).find_by!(iid: iid)
+ end
+
+ def find_project_snippet(id)
+ finder_params = { project: user_project }
+ SnippetsFinder.new(current_user, finder_params).execute.find(id)
+ end
+
+ def find_merge_request_with_access(iid, access_level = :read_merge_request)
+ merge_request = user_project.merge_requests.find_by!(iid: iid)
+ authorize! access_level, merge_request
+ merge_request
end
def authenticate!
- unauthorized! unless current_user
+ unauthorized! unless current_user && can?(initial_current_user, :access_api)
+ end
+
+ def authenticate_non_get!
+ authenticate! unless %w[GET HEAD].include?(route.request_method)
end
def authenticate_by_gitlab_shell_token!
@@ -146,10 +117,11 @@ module API
end
def authenticated_as_admin!
- forbidden! unless current_user.is_admin?
+ authenticate!
+ forbidden! unless current_user.admin?
end
- def authorize!(action, subject = nil)
+ def authorize!(action, subject = :global)
forbidden! unless can?(current_user, action, subject)
end
@@ -167,7 +139,7 @@ module API
end
end
- def can?(object, action, subject)
+ def can?(object, action, subject = :global)
Ability.allowed?(object, action, subject)
end
@@ -186,68 +158,21 @@ module API
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)
+ if params_hash[key].present? || (params_hash.key?(key) && params_hash[key] == false)
attrs[key] = params_hash[key]
end
end
ActionController::Parameters.new(attrs).permit!
end
- # Helper method for validating all labels against its names
- 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
-
- # 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
- 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
-
- 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
-
def filter_by_iid(items, iid)
items.where(iid: iid)
end
+ def filter_by_search(items, text)
+ items.search(text)
+ end
+
# error helpers
def forbidden!(reason = nil)
@@ -293,14 +218,22 @@ module API
render_api_error!('204 No Content', 204)
end
+ def accepted!
+ render_api_error!('202 Accepted', 202)
+ end
+
def render_validation_error!(model)
if model.errors.any?
render_api_error!(model.errors.messages || '400 Bad Request', 400)
end
end
+ def render_spam_error!
+ render_api_error!({ error: 'Spam detected' }, 400)
+ end
+
def render_api_error!(message, status)
- error!({ 'message' => message }, status)
+ error!({ 'message' => message }, status, header)
end
def handle_api_exception(exception)
@@ -321,41 +254,21 @@ module API
rack_response({ 'message' => '500 Internal Server Error' }.to_json, 500)
end
- # Projects helpers
-
- def filter_projects(projects)
- # If the archived parameter is passed, limit results accordingly
- if params[:archived].present?
- projects = projects.where(archived: to_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
-
- def project_order_by
- order_fields = %w(id name path created_at updated_at last_activity_at)
+ # project helpers
- if order_fields.include?(params['order_by'])
- params['order_by']
- else
- 'created_at'
- end
+ def reorder_projects(projects)
+ projects.reorder(params[:order_by] => params[:sort])
end
- def project_sort
- if params["sort"] == 'asc'
- :asc
- else
- :desc
- end
+ def project_finder_params
+ finder_params = {}
+ finder_params[:owned] = true if params[:owned].present?
+ finder_params[:non_public] = true if params[:membership].present?
+ finder_params[:starred] = true if params[:starred].present?
+ finder_params[:visibility_level] = Gitlab::VisibilityLevel.level_value(params[:visibility]) if params[:visibility]
+ finder_params[:archived] = params[:archived]
+ finder_params[:search] = params[:search] if params[:search]
+ finder_params
end
# file helpers
@@ -378,7 +291,7 @@ module API
UploadedFile.new(
file_path,
params["#{field}.name"],
- params["#{field}.type"] || 'application/octet-stream',
+ params["#{field}.type"] || 'application/octet-stream'
)
end
@@ -394,42 +307,77 @@ module API
header['X-Sendfile'] = path
body
else
- file FileStreamer.new(path)
+ file path
+ end
+ end
+
+ def present_artifacts!(artifacts_file)
+ return not_found! unless artifacts_file.exists?
+
+ if artifacts_file.file_storage?
+ present_file!(artifacts_file.path, artifacts_file.filename)
+ else
+ redirect_to(artifacts_file.url)
end
end
private
- def add_pagination_headers(paginated_data)
- header 'X-Total', paginated_data.total_count.to_s
- header 'X-Total-Pages', paginated_data.total_pages.to_s
- header 'X-Per-Page', paginated_data.limit_value.to_s
- header 'X-Page', paginated_data.current_page.to_s
- header 'X-Next-Page', paginated_data.next_page.to_s
- header 'X-Prev-Page', paginated_data.prev_page.to_s
- header 'Link', pagination_links(paginated_data)
+ def private_token
+ params[APIGuard::PRIVATE_TOKEN_PARAM] || env[APIGuard::PRIVATE_TOKEN_HEADER]
end
- def pagination_links(paginated_data)
- request_url = request.url.split('?').first
- request_params = params.clone
- request_params[:per_page] = paginated_data.limit_value
+ def warden
+ env['warden']
+ end
- links = []
+ # Check the Rails session for valid authentication details
+ #
+ # Until CSRF protection is added to the API, disallow this method for
+ # state-changing endpoints
+ def find_user_from_warden
+ warden.try(:authenticate) if %w[GET HEAD].include?(env['REQUEST_METHOD'])
+ end
- request_params[:page] = paginated_data.current_page - 1
- links << %(<#{request_url}?#{request_params.to_query}>; rel="prev") unless paginated_data.first_page?
+ def initial_current_user
+ return @initial_current_user if defined?(@initial_current_user)
+ Gitlab::Auth::UniqueIpsLimiter.limit_user! do
+ @initial_current_user ||= find_user_by_private_token(scopes: @scopes)
+ @initial_current_user ||= doorkeeper_guard(scopes: @scopes)
+ @initial_current_user ||= find_user_from_warden
- request_params[:page] = paginated_data.current_page + 1
- links << %(<#{request_url}?#{request_params.to_query}>; rel="next") unless paginated_data.last_page?
+ unless @initial_current_user && Gitlab::UserAccess.new(@initial_current_user).allowed?
+ @initial_current_user = nil
+ end
- request_params[:page] = 1
- links << %(<#{request_url}?#{request_params.to_query}>; rel="first")
+ @initial_current_user
+ end
+ end
+
+ def sudo!
+ return unless sudo_identifier
+ return unless initial_current_user
+
+ unless initial_current_user.admin?
+ forbidden!('Must be admin to use sudo')
+ end
- request_params[:page] = paginated_data.total_pages
- links << %(<#{request_url}?#{request_params.to_query}>; rel="last")
+ # Only private tokens should be used for the SUDO feature
+ unless private_token == initial_current_user.private_token
+ forbidden!('Private token must be specified in order to use sudo')
+ end
+
+ sudoed_user = find_user(sudo_identifier)
- links.join(', ')
+ if sudoed_user
+ @current_user = sudoed_user
+ else
+ not_found!("No user id or username for: #{sudo_identifier}")
+ end
+ end
+
+ def sudo_identifier
+ @sudo_identifier ||= params[SUDO_PARAM] || env[SUDO_HEADER]
end
def secret_token
@@ -446,14 +394,6 @@ module API
header(*Gitlab::Workhorse.send_git_archive(repository, ref: ref, format: format))
end
- def issue_entity(project)
- if project.has_external_issue_tracker?
- Entities::ExternalIssue
- else
- Entities::Issue
- end
- end
-
# The Grape Error Middleware only has access to env but no params. We workaround this by
# defining a method that returns the right value.
def define_params_for_grape_middleware