diff options
author | Yorick Peterse <yorickpeterse@gmail.com> | 2019-03-04 19:44:46 +0100 |
---|---|---|
committer | Yorick Peterse <yorickpeterse@gmail.com> | 2019-03-04 19:44:46 +0100 |
commit | 59db98a0cabea4421434655d7f7873110363d21a (patch) | |
tree | f3f72bca43724e230090aeae7aa0c15e56b707bf /lib | |
parent | 5c80bbb33c12490bc5fa711642a40fc16bdb79a4 (diff) | |
parent | 025015048f7eaad29ee7816c6040fb3e0c06eb8d (diff) | |
download | gitlab-ce-59db98a0cabea4421434655d7f7873110363d21a.tar.gz |
Merge dev master into GitLab.com master
Diffstat (limited to 'lib')
20 files changed, 199 insertions, 68 deletions
diff --git a/lib/api/commits.rb b/lib/api/commits.rb index 8defc59224d..d0a9debda5b 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -318,10 +318,18 @@ module API use :pagination end get ':id/repository/commits/:sha/merge_requests', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do + authorize! :read_merge_request, user_project + commit = user_project.commit(params[:sha]) not_found! 'Commit' unless commit - present paginate(commit.merge_requests), with: Entities::MergeRequestBasic + commit_merge_requests = MergeRequestsFinder.new( + current_user, + project_id: user_project.id, + commit_sha: commit.sha + ).execute + + present paginate(commit_merge_requests), with: Entities::MergeRequestBasic end desc "Get a commit's GPG signature" do diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 18f15632f2b..5176e9713c1 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -156,7 +156,7 @@ module API class BasicProjectDetails < ProjectIdentity include ::API::ProjectsRelationBuilder - expose :default_branch + expose :default_branch, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) } # Avoids an N+1 query: https://github.com/mbleigh/acts-as-taggable-on/issues/91#issuecomment-168273770 expose :tag_list do |project| # project.tags.order(:name).pluck(:name) is the most suitable option @@ -261,7 +261,7 @@ module API expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) } expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] } expose :public_builds, as: :public_jobs - expose :ci_config_path + expose :ci_config_path, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) } expose :shared_with_groups do |project, options| SharedGroup.represent(project.project_group_links, options) end @@ -270,8 +270,9 @@ module API expose :only_allow_merge_if_all_discussions_are_resolved expose :printing_merge_request_link_enabled expose :merge_method - - expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics + expose :statistics, using: 'API::Entities::ProjectStatistics', if: -> (project, options) { + options[:statistics] && Ability.allowed?(options[:current_user], :download_code, project) + } # rubocop: disable CodeReuse/ActiveRecord def self.preload_relation(projects_relation, options = {}) diff --git a/lib/api/environments.rb b/lib/api/environments.rb index 0278c6c54a5..5b0f3b914cb 100644 --- a/lib/api/environments.rb +++ b/lib/api/environments.rb @@ -22,7 +22,7 @@ module API get ':id/environments' do authorize! :read_environment, user_project - present paginate(user_project.environments), with: Entities::Environment + present paginate(user_project.environments), with: Entities::Environment, current_user: current_user end desc 'Creates a new environment' do @@ -40,7 +40,7 @@ module API environment = user_project.environments.create(declared_params) if environment.persisted? - present environment, with: Entities::Environment + present environment, with: Entities::Environment, current_user: current_user else render_validation_error!(environment) end @@ -63,7 +63,7 @@ module API update_params = declared_params(include_missing: false).extract!(:name, :external_url) if environment.update(update_params) - present environment, with: Entities::Environment + present environment, with: Entities::Environment, current_user: current_user else render_validation_error!(environment) end @@ -99,7 +99,7 @@ module API environment.stop_with_action!(current_user) status 200 - present environment, with: Entities::Environment + present environment, with: Entities::Environment, current_user: current_user end end end diff --git a/lib/api/helpers/notes_helpers.rb b/lib/api/helpers/notes_helpers.rb index 216b2c45741..795dca5cf03 100644 --- a/lib/api/helpers/notes_helpers.rb +++ b/lib/api/helpers/notes_helpers.rb @@ -70,14 +70,7 @@ module API def find_noteable(parent, noteables_str, noteable_id) noteable = public_send("find_#{parent}_#{noteables_str.singularize}", noteable_id) # rubocop:disable GitlabSecurity/PublicSend - readable = - if noteable.is_a?(Commit) - # for commits there is not :read_commit policy, check if user - # has :read_note permission on the commit's project - can?(current_user, :read_note, user_project) - else - can?(current_user, noteable_read_ability_name(noteable), noteable) - end + readable = can?(current_user, noteable_read_ability_name(noteable), noteable) return not_found!(noteables_str) unless readable @@ -89,12 +82,11 @@ module API end def create_note(noteable, opts) - policy_object = noteable.is_a?(Commit) ? user_project : noteable - authorize!(:create_note, policy_object) + authorize!(:create_note, noteable) parent = noteable_parent(noteable) - opts.delete(:created_at) unless current_user.can?(:set_note_created_at, policy_object) + opts.delete(:created_at) unless current_user.can?(:set_note_created_at, noteable) opts[:updated_at] = opts[:created_at] if opts[:created_at] diff --git a/lib/api/projects.rb b/lib/api/projects.rb index b23fe6cd4e7..91501ba4d36 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -184,7 +184,8 @@ module API if project.saved? present project, with: Entities::Project, - user_can_admin_project: can?(current_user, :admin_project, project) + user_can_admin_project: can?(current_user, :admin_project, project), + current_user: current_user else if project.errors[:limit_reached].present? error!(project.errors[:limit_reached], 403) @@ -217,7 +218,8 @@ module API if project.saved? present project, with: Entities::Project, - user_can_admin_project: can?(current_user, :admin_project, project) + user_can_admin_project: can?(current_user, :admin_project, project), + current_user: current_user else render_validation_error!(project) end @@ -281,7 +283,8 @@ module API conflict!(forked_project.errors.messages) else present forked_project, with: Entities::Project, - user_can_admin_project: can?(current_user, :admin_project, forked_project) + user_can_admin_project: can?(current_user, :admin_project, forked_project), + current_user: current_user end end @@ -330,7 +333,8 @@ module API if result[:status] == :success present user_project, with: Entities::Project, - user_can_admin_project: can?(current_user, :admin_project, user_project) + user_can_admin_project: can?(current_user, :admin_project, user_project), + current_user: current_user else render_validation_error!(user_project) end @@ -344,7 +348,7 @@ module API ::Projects::UpdateService.new(user_project, current_user, archived: true).execute - present user_project, with: Entities::Project + present user_project, with: Entities::Project, current_user: current_user end desc 'Unarchive a project' do @@ -355,7 +359,7 @@ module API ::Projects::UpdateService.new(@project, current_user, archived: false).execute - present user_project, with: Entities::Project + present user_project, with: Entities::Project, current_user: current_user end desc 'Star a project' do @@ -368,7 +372,7 @@ module API current_user.toggle_star(user_project) user_project.reload - present user_project, with: Entities::Project + present user_project, with: Entities::Project, current_user: current_user end end @@ -380,7 +384,7 @@ module API current_user.toggle_star(user_project) user_project.reload - present user_project, with: Entities::Project + present user_project, with: Entities::Project, current_user: current_user else not_modified! end @@ -420,7 +424,7 @@ module API result = ::Projects::ForkService.new(fork_from_project, current_user).execute(user_project) if result - present user_project.reload, with: Entities::Project + present user_project.reload, with: Entities::Project, current_user: current_user else render_api_error!("Project already forked", 409) if user_project.forked? end @@ -442,27 +446,24 @@ module API end params do requires :group_id, type: Integer, desc: 'The ID of a group' - requires :group_access, type: Integer, values: Gitlab::Access.values, desc: 'The group access level' + requires :group_access, type: Integer, values: Gitlab::Access.values, as: :link_group_access, desc: 'The group access level' optional :expires_at, type: Date, desc: 'Share expiration date' end post ":id/share" do authorize! :admin_project, user_project group = Group.find_by_id(params[:group_id]) - unless group && can?(current_user, :read_group, group) - not_found!('Group') - end - unless user_project.allowed_to_share_with_group? break render_api_error!("The project sharing with group is disabled", 400) end - link = user_project.project_group_links.new(declared_params(include_missing: false)) + result = ::Projects::GroupLinks::CreateService.new(user_project, current_user, declared_params(include_missing: false)) + .execute(group) - if link.save - present link, with: Entities::ProjectGroupLink + if result[:status] == :success + present result[:link], with: Entities::ProjectGroupLink else - render_api_error!(link.errors.full_messages.first, 409) + render_api_error!(result[:message], result[:http_status]) end end @@ -526,7 +527,7 @@ module API result = ::Projects::TransferService.new(user_project, current_user).execute(namespace) if result - present user_project, with: Entities::Project + present user_project, with: Entities::Project, current_user: current_user else render_api_error!("Failed to transfer project #{user_project.errors.messages}", 400) end diff --git a/lib/api/release/links.rb b/lib/api/release/links.rb index e3072684ef7..5d1b40e3bff 100644 --- a/lib/api/release/links.rb +++ b/lib/api/release/links.rb @@ -8,6 +8,8 @@ module API RELEASE_ENDPOINT_REQUIREMETS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS .merge(tag_name: API::NO_SLASH_URL_PART_REGEX) + before { authorize! :read_release, user_project } + params do requires :id, type: String, desc: 'The ID of a project' end diff --git a/lib/constraints/project_url_constrainer.rb b/lib/constraints/project_url_constrainer.rb index eadfbf7bc01..d41490d2ebd 100644 --- a/lib/constraints/project_url_constrainer.rb +++ b/lib/constraints/project_url_constrainer.rb @@ -2,12 +2,13 @@ module Constraints class ProjectUrlConstrainer - def matches?(request) + def matches?(request, existence_check: true) namespace_path = request.params[:namespace_id] project_path = request.params[:project_id] || request.params[:id] full_path = [namespace_path, project_path].join('/') return false unless ProjectPathValidator.valid_path?(full_path) + return true unless existence_check # We intentionally allow SELECT(*) here so result of this query can be used # as cache for further Project.find_by_full_path calls within request diff --git a/lib/gitlab/auth/omniauth_identity_linker_base.rb b/lib/gitlab/auth/omniauth_identity_linker_base.rb index 253445570f2..c620fc5d6bd 100644 --- a/lib/gitlab/auth/omniauth_identity_linker_base.rb +++ b/lib/gitlab/auth/omniauth_identity_linker_base.rb @@ -12,7 +12,7 @@ module Gitlab end def link - save if identity.new_record? + save if unlinked? end def changed? @@ -35,6 +35,10 @@ module Gitlab @changed = identity.save end + def unlinked? + identity.new_record? + end + # rubocop: disable CodeReuse/ActiveRecord def identity @identity ||= current_user.identities diff --git a/lib/gitlab/dependency_linker/base_linker.rb b/lib/gitlab/dependency_linker/base_linker.rb index ac2efe598b4..ffad00fa7d7 100644 --- a/lib/gitlab/dependency_linker/base_linker.rb +++ b/lib/gitlab/dependency_linker/base_linker.rb @@ -4,6 +4,7 @@ module Gitlab module DependencyLinker class BaseLinker URL_REGEX = %r{https?://[^'" ]+}.freeze + GIT_INVALID_URL_REGEX = /^git\+#{URL_REGEX}/.freeze REPO_REGEX = %r{[^/'" ]+/[^/'" ]+}.freeze class_attribute :file_type @@ -29,8 +30,25 @@ module Gitlab highlighted_lines.join.html_safe end + def external_url(name, external_ref) + return if external_ref =~ GIT_INVALID_URL_REGEX + + case external_ref + when /\A#{URL_REGEX}\z/ + external_ref + when /\A#{REPO_REGEX}\z/ + github_url(external_ref) + else + package_url(name) + end + end + private + def package_url(_name) + raise NotImplementedError + end + def link_dependencies raise NotImplementedError end diff --git a/lib/gitlab/dependency_linker/composer_json_linker.rb b/lib/gitlab/dependency_linker/composer_json_linker.rb index 22d2bead891..4b8862b31ee 100644 --- a/lib/gitlab/dependency_linker/composer_json_linker.rb +++ b/lib/gitlab/dependency_linker/composer_json_linker.rb @@ -8,8 +8,8 @@ module Gitlab private def link_packages - link_packages_at_key("require", &method(:package_url)) - link_packages_at_key("require-dev", &method(:package_url)) + link_packages_at_key("require") + link_packages_at_key("require-dev") end def package_url(name) diff --git a/lib/gitlab/dependency_linker/gemfile_linker.rb b/lib/gitlab/dependency_linker/gemfile_linker.rb index 8ab219c4962..c6e02248b0a 100644 --- a/lib/gitlab/dependency_linker/gemfile_linker.rb +++ b/lib/gitlab/dependency_linker/gemfile_linker.rb @@ -3,8 +3,14 @@ module Gitlab module DependencyLinker class GemfileLinker < MethodLinker + class_attribute :package_keyword + + self.package_keyword = :gem self.file_type = :gemfile + GITHUB_REGEX = /(github:|:github\s*=>)\s*['"](?<name>[^'"]+)['"]/.freeze + GIT_REGEX = /(git:|:git\s*=>)\s*['"](?<name>#{URL_REGEX})['"]/.freeze + private def link_dependencies @@ -14,21 +20,35 @@ module Gitlab def link_urls # Link `github: "user/repo"` to https://github.com/user/repo - link_regex(/(github:|:github\s*=>)\s*['"](?<name>[^'"]+)['"]/, &method(:github_url)) + link_regex(GITHUB_REGEX, &method(:github_url)) # Link `git: "https://gitlab.example.com/user/repo"` to https://gitlab.example.com/user/repo - link_regex(/(git:|:git\s*=>)\s*['"](?<name>#{URL_REGEX})['"]/, &:itself) + link_regex(GIT_REGEX, &:itself) # Link `source "https://rubygems.org"` to https://rubygems.org link_method_call('source', URL_REGEX, &:itself) end def link_packages - # Link `gem "package_name"` to https://rubygems.org/gems/package_name - link_method_call('gem') do |name| - "https://rubygems.org/gems/#{name}" + packages = parse_packages + + return if packages.blank? + + packages.each do |package| + link_method_call('gem', package.name) do + external_url(package.name, package.external_ref) + end end end + + def package_url(name) + "https://rubygems.org/gems/#{name}" + end + + def parse_packages + parser = Gitlab::DependencyLinker::Parser::Gemfile.new(plain_text) + parser.parse(keyword: self.class.package_keyword) + end end end end diff --git a/lib/gitlab/dependency_linker/gemspec_linker.rb b/lib/gitlab/dependency_linker/gemspec_linker.rb index b924ea86d89..94c2b375cf9 100644 --- a/lib/gitlab/dependency_linker/gemspec_linker.rb +++ b/lib/gitlab/dependency_linker/gemspec_linker.rb @@ -11,7 +11,7 @@ module Gitlab link_method_call('homepage', URL_REGEX, &:itself) link_method_call('license', &method(:license_url)) - link_method_call(%w[name add_dependency add_runtime_dependency add_development_dependency]) do |name| + link_method_call(%w[add_dependency add_runtime_dependency add_development_dependency]) do |name| "https://rubygems.org/gems/#{name}" end end diff --git a/lib/gitlab/dependency_linker/method_linker.rb b/lib/gitlab/dependency_linker/method_linker.rb index d4d85bb3390..33899a931c6 100644 --- a/lib/gitlab/dependency_linker/method_linker.rb +++ b/lib/gitlab/dependency_linker/method_linker.rb @@ -23,18 +23,22 @@ module Gitlab # link_method_call('name') # # Will link `package` in `self.name = "package"` def link_method_call(method_name, value = nil, &url_proc) + regex = method_call_regex(method_name, value) + + link_regex(regex, &url_proc) + end + + def method_call_regex(method_name, value = nil) method_name = regexp_for_value(method_name) value = regexp_for_value(value) - regex = %r{ + %r{ #{method_name} # Method name \s* # Whitespace [(=]? # Opening brace or equals sign \s* # Whitespace ['"](?<name>#{value})['"] # Package name in quotes }x - - link_regex(regex, &url_proc) end end end diff --git a/lib/gitlab/dependency_linker/package.rb b/lib/gitlab/dependency_linker/package.rb new file mode 100644 index 00000000000..8a509bbd562 --- /dev/null +++ b/lib/gitlab/dependency_linker/package.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Gitlab + module DependencyLinker + class Package + attr_reader :name, :git_ref, :github_ref + + def initialize(name, git_ref, github_ref) + @name = name + @git_ref = git_ref + @github_ref = github_ref + end + + def external_ref + @git_ref || @github_ref + end + end + end +end diff --git a/lib/gitlab/dependency_linker/package_json_linker.rb b/lib/gitlab/dependency_linker/package_json_linker.rb index 578e25f806a..6857f2a4fa2 100644 --- a/lib/gitlab/dependency_linker/package_json_linker.rb +++ b/lib/gitlab/dependency_linker/package_json_linker.rb @@ -8,7 +8,6 @@ module Gitlab private def link_dependencies - link_json('name', json["name"], &method(:package_url)) link_json('license', &method(:license_url)) link_json(%w[homepage url], URL_REGEX, &:itself) @@ -16,25 +15,19 @@ module Gitlab end def link_packages - link_packages_at_key("dependencies", &method(:package_url)) - link_packages_at_key("devDependencies", &method(:package_url)) + link_packages_at_key("dependencies") + link_packages_at_key("devDependencies") end - def link_packages_at_key(key, &url_proc) + def link_packages_at_key(key) dependencies = json[key] return unless dependencies dependencies.each do |name, version| - link_json(name, version, link: :key, &url_proc) - - link_json(name) do |value| - case value - when /\A#{URL_REGEX}\z/ - value - when /\A#{REPO_REGEX}\z/ - github_url(value) - end - end + external_url = external_url(name, version) + + link_json(name, version, link: :key) { external_url } + link_json(name) { external_url } end end diff --git a/lib/gitlab/dependency_linker/parser/gemfile.rb b/lib/gitlab/dependency_linker/parser/gemfile.rb new file mode 100644 index 00000000000..7f755375cea --- /dev/null +++ b/lib/gitlab/dependency_linker/parser/gemfile.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Gitlab + module DependencyLinker + module Parser + class Gemfile < MethodLinker + GIT_REGEX = Gitlab::DependencyLinker::GemfileLinker::GIT_REGEX + GITHUB_REGEX = Gitlab::DependencyLinker::GemfileLinker::GITHUB_REGEX + + def initialize(plain_text) + @plain_text = plain_text + end + + # Returns a list of Gitlab::DependencyLinker::Package + # + # keyword - The package definition keyword, e.g. `:gem` for + # Gemfile parsing, `:pod` for Podfile. + def parse(keyword:) + plain_lines.each_with_object([]) do |line, packages| + name = fetch(line, method_call_regex(keyword)) + + next unless name + + git_ref = fetch(line, GIT_REGEX) + github_ref = fetch(line, GITHUB_REGEX) + + packages << Gitlab::DependencyLinker::Package.new(name, git_ref, github_ref) + end + end + + private + + def fetch(line, regex, group: :name) + match = line.match(regex) + match[group] if match + end + end + end + end +end diff --git a/lib/gitlab/dependency_linker/podfile_linker.rb b/lib/gitlab/dependency_linker/podfile_linker.rb index def9b04cca9..a20d285da79 100644 --- a/lib/gitlab/dependency_linker/podfile_linker.rb +++ b/lib/gitlab/dependency_linker/podfile_linker.rb @@ -5,12 +5,21 @@ module Gitlab class PodfileLinker < GemfileLinker include Cocoapods + self.package_keyword = :pod self.file_type = :podfile private def link_packages - link_method_call('pod', &method(:package_url)) + packages = parse_packages + + return unless packages + + packages.each do |package| + link_method_call('pod', package.name) do + external_url(package.name, package.external_ref) + end + end end end end diff --git a/lib/gitlab/dependency_linker/podspec_linker.rb b/lib/gitlab/dependency_linker/podspec_linker.rb index 6b1758c5a43..14abd3999c4 100644 --- a/lib/gitlab/dependency_linker/podspec_linker.rb +++ b/lib/gitlab/dependency_linker/podspec_linker.rb @@ -19,7 +19,7 @@ module Gitlab link_method_call('license', &method(:license_url)) link_regex(/license\s*=\s*\{\s*(type:|:type\s*=>)\s*#{STRING_REGEX}/, &method(:license_url)) - link_method_call(%w[name dependency], &method(:package_url)) + link_method_call('dependency', &method(:package_url)) end end end diff --git a/lib/gitlab/import_export/merge_request_parser.rb b/lib/gitlab/import_export/merge_request_parser.rb index 040a70d6775..deb2f59f05f 100644 --- a/lib/gitlab/import_export/merge_request_parser.rb +++ b/lib/gitlab/import_export/merge_request_parser.rb @@ -20,6 +20,17 @@ module Gitlab create_target_branch unless branch_exists?(@merge_request.target_branch) end + # The merge_request_diff associated with the current @merge_request might + # be invalid. Than means, when the @merge_request object is saved, the + # @merge_request.merge_request_diff won't. This can leave the merge request + # in an invalid state, because a merge request must have an associated + # merge request diff. + # In this change, if the associated merge request diff is invalid, we set + # it to nil. This change, in association with the after callback + # :ensure_merge_request_diff in the MergeRequest class, makes that + # when the merge request is going to be created and it doesn't have + # one, a default one will be generated. + @merge_request.merge_request_diff = nil unless @merge_request.merge_request_diff&.valid? @merge_request end diff --git a/lib/gitlab/kubernetes/kube_client.rb b/lib/gitlab/kubernetes/kube_client.rb index 624c2c67551..de14df56555 100644 --- a/lib/gitlab/kubernetes/kube_client.rb +++ b/lib/gitlab/kubernetes/kube_client.rb @@ -82,6 +82,8 @@ module Gitlab def initialize(api_prefix, **kubeclient_options) @api_prefix = api_prefix @kubeclient_options = kubeclient_options.merge(http_max_redirects: 0) + + validate_url! end def create_or_update_cluster_role_binding(resource) @@ -118,6 +120,12 @@ module Gitlab private + def validate_url! + return if Gitlab::CurrentSettings.allow_local_requests_from_hooks_and_services? + + Gitlab::UrlBlocker.validate!(api_prefix, allow_local_network: false) + end + def cluster_role_binding_exists?(resource) get_cluster_role_binding(resource.metadata.name) rescue ::Kubeclient::ResourceNotFoundError |