summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Release Tools Bot <delivery-team+release-tools@gitlab.com>2022-06-30 17:04:24 +0000
committerGitLab Release Tools Bot <delivery-team+release-tools@gitlab.com>2022-06-30 17:04:24 +0000
commit36f4d31276f007359068c72362bf1ff7c691933a (patch)
treeeb47da84d0a03fdd1b8440eb2ce8f5821f11a1e2
parent0847e5f57325e0518d8cd4ffe303e5b7a2cec307 (diff)
parent02cdf459a13199039eb98ad555442958157faf7c (diff)
downloadgitlab-ce-36f4d31276f007359068c72362bf1ff7c691933a.tar.gz
Merge remote-tracking branch 'dev/14-10-stable' into 14-10-stable
-rw-r--r--.rubocop_todo/layout/line_length.yml1
-rw-r--r--CHANGELOG.md22
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--Gemfile.lock2
-rw-r--r--VERSION2
-rw-r--r--app/assets/javascripts/error_tracking_settings/utils.js3
-rw-r--r--app/assets/javascripts/projects/settings/access_dropdown.js2
-rw-r--r--app/controllers/projects/error_tracking_controller.rb1
-rw-r--r--app/controllers/projects/settings/operations_controller.rb2
-rw-r--r--app/finders/ci/runner_jobs_finder.rb12
-rw-r--r--app/finders/packages/conan/package_finder.rb2
-rw-r--r--app/graphql/resolvers/ci/config_resolver.rb2
-rw-r--r--app/helpers/integrations_helper.rb21
-rw-r--r--app/helpers/projects_helper.rb1
-rw-r--r--app/helpers/timeboxes_helper.rb4
-rw-r--r--app/models/ci/project_mirror.rb2
-rw-r--r--app/models/clusters/applications/runner.rb2
-rw-r--r--app/models/concerns/enums/ci/commit_status.rb3
-rw-r--r--app/models/error_tracking/project_error_tracking_setting.rb24
-rw-r--r--app/models/group_group_link.rb2
-rw-r--r--app/models/user.rb14
-rw-r--r--app/policies/project_policy.rb28
-rw-r--r--app/presenters/commit_status_presenter.rb3
-rw-r--r--app/serializers/member_user_entity.rb14
-rw-r--r--app/services/auth/container_registry_authentication_service.rb6
-rw-r--r--app/services/projects/operations/update_service.rb3
-rw-r--r--app/views/shared/milestones/_milestone.html.haml2
-rw-r--r--app/views/shared/milestones/_sidebar.html.haml2
-rw-r--r--config/initializers/net_http_response_patch.rb46
-rw-r--r--db/migrate/20220520120637_add_installable_conan_packages_index_to_packages.rb25
-rw-r--r--db/migrate/20220525084153_add_sentry_project_id_to_project_error_tracking_settings.rb7
-rw-r--r--db/schema_migrations/202205201206371
-rw-r--r--db/schema_migrations/202205250841531
-rw-r--r--db/structure.sql5
-rw-r--r--doc/api/runners.md3
-rw-r--r--doc/operations/error_tracking.md2
-rw-r--r--doc/update/index.md3
-rw-r--r--doc/user/group/index.md3
-rw-r--r--lib/api/ci/runners.rb2
-rw-r--r--lib/api/helpers/label_helpers.rb14
-rw-r--r--lib/bulk_imports/projects/graphql/get_project_query.rb12
-rw-r--r--lib/bulk_imports/projects/transformers/project_attributes_transformer.rb14
-rw-r--r--lib/error_tracking/sentry_client/event.rb1
-rw-r--r--lib/gitlab/buffered_io.rb6
-rw-r--r--lib/gitlab/ci/status/build/failed.rb3
-rw-r--r--lib/gitlab/error_tracking/error_event.rb2
-rw-r--r--lib/gitlab/import_export/decompressed_archive_size_validator.rb20
-rw-r--r--lib/gitlab/jira/dvcs.rb3
-rw-r--r--locale/gitlab.pot3
-rw-r--r--qa/Gemfile.lock2
-rw-r--r--spec/controllers/projects/error_tracking_controller_spec.rb58
-rw-r--r--spec/db/schema_spec.rb1
-rw-r--r--spec/factories/error_tracking/error.rb2
-rw-r--r--spec/factories/project_error_tracking_settings.rb1
-rw-r--r--spec/finders/ci/runner_jobs_finder_spec.rb53
-rw-r--r--spec/finders/packages/conan/package_finder_spec.rb51
-rw-r--r--spec/fixtures/api/schemas/entities/member.json2
-rw-r--r--spec/fixtures/api/schemas/entities/member_user_default.json35
-rw-r--r--spec/fixtures/api/schemas/entities/member_user_for_admin_member.json (renamed from spec/fixtures/api/schemas/entities/member_user.json)0
-rw-r--r--spec/frontend/projects/settings/access_dropdown_spec.js17
-rw-r--r--spec/graphql/resolvers/ci/config_resolver_spec.rb98
-rw-r--r--spec/helpers/integrations_helper_spec.rb15
-rw-r--r--spec/helpers/projects_helper_spec.rb1
-rw-r--r--spec/helpers/timeboxes_helper_spec.rb19
-rw-r--r--spec/initializers/net_http_response_patch_spec.rb79
-rw-r--r--spec/lib/bulk_imports/projects/pipelines/project_pipeline_spec.rb26
-rw-r--r--spec/lib/bulk_imports/projects/transformers/project_attributes_transformer_spec.rb21
-rw-r--r--spec/lib/gitlab/buffered_io_spec.rb64
-rw-r--r--spec/lib/gitlab/import_export/decompressed_archive_size_validator_spec.rb59
-rw-r--r--spec/models/error_tracking/project_error_tracking_setting_spec.rb77
-rw-r--r--spec/models/user_spec.rb35
-rw-r--r--spec/policies/project_policy_spec.rb139
-rw-r--r--spec/requests/api/ci/runners_spec.rb17
-rw-r--r--spec/requests/api/labels_spec.rb61
-rw-r--r--spec/requests/jira_routing_spec.rb54
-rw-r--r--spec/serializers/member_user_entity_spec.rb70
-rw-r--r--spec/services/bulk_imports/file_decompression_service_spec.rb3
-rw-r--r--spec/services/issues/create_service_spec.rb2
-rw-r--r--spec/support/shared_contexts/project_features_shared_context.rb28
-rw-r--r--spec/support/shared_contexts/sentry_error_tracking_shared_context.rb2
-rw-r--r--spec/support/shared_examples/policies/project_policy_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb67
-rw-r--r--spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb6
-rw-r--r--tooling/overcommit/Gemfile.lock2
84 files changed, 1234 insertions, 306 deletions
diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml
index 8a922a1f163..f49d99360a2 100644
--- a/.rubocop_todo/layout/line_length.yml
+++ b/.rubocop_todo/layout/line_length.yml
@@ -1979,7 +1979,6 @@ Layout/LineLength:
- 'ee/spec/features/groups/iterations/user_edits_iteration_spec.rb'
- 'ee/spec/features/groups/iterations/user_views_iteration_cadence_spec.rb'
- 'ee/spec/features/groups/iterations/user_views_iteration_spec.rb'
- - 'ee/spec/features/groups/members/manage_groups_spec.rb'
- 'ee/spec/features/groups/members/manage_members_spec.rb'
- 'ee/spec/features/groups/members/override_ldap_memberships_spec.rb'
- 'ee/spec/features/groups/saml_providers_spec.rb'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6f665427a51..52ae31c7c3f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,28 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 14.10.5 (2022-06-30)
+
+### Security (17 changes)
+
+- [Fix group IP restrictions not enforced for container registry requests](gitlab-org/security/gitlab@b146ad7b8c6fba9d3c5bea365ff8afd49949dcb0) ([merge request](gitlab-org/security/gitlab!2552))
+- [Update rack gem to version 2.2.3.1](gitlab-org/security/gitlab@09ebb50ceee5a2226c1f70fa1d6c25391d51dda6) ([merge request](gitlab-org/security/gitlab!2554))
+- [Gitlab Runner version upgrade](gitlab-org/security/gitlab@c91bfdb4f96e70e377a84b99c4edaa2fdecb8e16) ([merge request](gitlab-org/security/gitlab!2567))
+- [Update ProjectAttributesTransformer to use fixed number of attributes](gitlab-org/security/gitlab@6f892fb2a4b84473c3796533551f915c16cf77d9) ([merge request](gitlab-org/security/gitlab!2549))
+- [Escape deploy key title to prevent XSS](gitlab-org/security/gitlab@153a7c447e03a509b7f06ac7381f4f9db414c9ea) ([merge request](gitlab-org/security/gitlab!2494))
+- [Sanitize ZenTao breadcrumb links](gitlab-org/security/gitlab@530c7be82ae90138898ff99008d994b1c85d8cf1) ([merge request](gitlab-org/security/gitlab!2557))
+- [Fix permissions in the project labels API](gitlab-org/security/gitlab@f2c71f64c258bef9f56f4892d11a4dbf20d668e6) ([merge request](gitlab-org/security/gitlab!2534))
+- [Security fix sentry issue leaks and access level check](gitlab-org/security/gitlab@c644d94f58e30e1a9d87521b039a347412f0fead) ([merge request](gitlab-org/security/gitlab!2501))
+- [Check permissions before exposing user two factor enabled](gitlab-org/security/gitlab@8a623e8a4fdbd3421ac3ae0e37e156b7d3b04970) ([merge request](gitlab-org/security/gitlab!2525))
+- [Filter milestone release by user access](gitlab-org/security/gitlab@d7d6431a52808107a71f15d29e856eef2cb313e5) ([merge request](gitlab-org/security/gitlab!2537))
+- [Fix the required access level in the Conan packages finder](gitlab-org/security/gitlab@756fb242c4d6acf6cfd95fa39f37410eaf009747) ([merge request](gitlab-org/security/gitlab!2485))
+- [Allow inviting only groups with subset of allowed domains to groups](gitlab-org/security/gitlab@ca50492a32a2e367b0bc75dae0f91dc52d23b2ed) ([merge request](gitlab-org/security/gitlab!2512))
+- [Fix open redirect vulnerability](gitlab-org/security/gitlab@1450068a44d67af3cbe09fedcc4b1e9b4ea2e586) ([merge request](gitlab-org/security/gitlab!2540))
+- [Adds a filter based on user access to Runner jobs endpoint](gitlab-org/security/gitlab@dafaf3e50e8b1a18ff362cbb60e9482c9d60fc33) ([merge request](gitlab-org/security/gitlab!2497))
+- [Prevent runners from picking IP restricted jobs](gitlab-org/security/gitlab@0fad0cdde00b68c2a0f19ffa2681b438fcad4097) ([merge request](gitlab-org/security/gitlab!2503))
+- [Restrict CI lint access to pipeline creators](gitlab-org/security/gitlab@c5b79e969f10e3604eff16a9edef716e700cd201) ([merge request](gitlab-org/security/gitlab!2515))
+- [Catch endless headers when reading HTTP responses](gitlab-org/security/gitlab@65379002bd7a0259c425455c937b110bd96096dc) ([merge request](gitlab-org/security/gitlab!2529))
+
## 14.10.4 (2022-06-01)
### Security (7 changes)
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index e8ebae9ebab..ce21932daf4 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-14.10.4 \ No newline at end of file
+14.10.5 \ No newline at end of file
diff --git a/Gemfile.lock b/Gemfile.lock
index 9a67aa1b017..067f0ccfadf 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -958,7 +958,7 @@ GEM
pyu-ruby-sasl (0.0.3.3)
raabro (1.1.6)
racc (1.6.0)
- rack (2.2.3)
+ rack (2.2.3.1)
rack-accept (0.4.5)
rack (>= 0.4)
rack-attack (6.3.0)
diff --git a/VERSION b/VERSION
index e8ebae9ebab..ce21932daf4 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-14.10.4 \ No newline at end of file
+14.10.5 \ No newline at end of file
diff --git a/app/assets/javascripts/error_tracking_settings/utils.js b/app/assets/javascripts/error_tracking_settings/utils.js
index 7ef5f7bbd34..47a42dc3742 100644
--- a/app/assets/javascripts/error_tracking_settings/utils.js
+++ b/app/assets/javascripts/error_tracking_settings/utils.js
@@ -1,4 +1,4 @@
-export const projectKeys = ['name', 'organizationName', 'organizationSlug', 'slug'];
+export const projectKeys = ['id', 'name', 'organizationName', 'organizationSlug', 'slug'];
export const transformFrontendSettings = ({
apiHost,
@@ -9,6 +9,7 @@ export const transformFrontendSettings = ({
}) => {
const project = selectedProject
? {
+ sentry_project_id: selectedProject.id,
slug: selectedProject.slug,
name: selectedProject.name,
organization_name: selectedProject.organizationName,
diff --git a/app/assets/javascripts/projects/settings/access_dropdown.js b/app/assets/javascripts/projects/settings/access_dropdown.js
index 7fb7a416dca..79dfa166b1a 100644
--- a/app/assets/javascripts/projects/settings/access_dropdown.js
+++ b/app/assets/javascripts/projects/settings/access_dropdown.js
@@ -537,7 +537,7 @@ export default class AccessDropdown {
return `
<li>
<a href="#" class="${isActiveClass}">
- <strong>${key.title}</strong>
+ <strong>${escape(key.title)}</strong>
<p>
${sprintf(
__('Owned by %{image_tag}'),
diff --git a/app/controllers/projects/error_tracking_controller.rb b/app/controllers/projects/error_tracking_controller.rb
index 06383d26133..d2e36ef5496 100644
--- a/app/controllers/projects/error_tracking_controller.rb
+++ b/app/controllers/projects/error_tracking_controller.rb
@@ -4,6 +4,7 @@ class Projects::ErrorTrackingController < Projects::ErrorTracking::BaseControlle
respond_to :json
before_action :authorize_read_sentry_issue!
+ before_action :authorize_update_sentry_issue!, only: %i[update]
before_action :set_issue_id, only: :details
before_action only: [:index] do
diff --git a/app/controllers/projects/settings/operations_controller.rb b/app/controllers/projects/settings/operations_controller.rb
index 43c72b358db..199173cb641 100644
--- a/app/controllers/projects/settings/operations_controller.rb
+++ b/app/controllers/projects/settings/operations_controller.rb
@@ -143,7 +143,7 @@ module Projects
:integrated,
:api_host,
:token,
- project: [:slug, :name, :organization_slug, :organization_name]
+ project: [:slug, :name, :organization_slug, :organization_name, :sentry_project_id]
],
grafana_integration_attributes: [:token, :grafana_url, :enabled],
diff --git a/app/finders/ci/runner_jobs_finder.rb b/app/finders/ci/runner_jobs_finder.rb
index 9dc3c2a2427..b659eda6646 100644
--- a/app/finders/ci/runner_jobs_finder.rb
+++ b/app/finders/ci/runner_jobs_finder.rb
@@ -6,13 +6,15 @@ module Ci
ALLOWED_INDEXED_COLUMNS = %w[id].freeze
- def initialize(runner, params = {})
+ def initialize(runner, current_user, params = {})
@runner = runner
+ @user = current_user
@params = params
end
def execute
items = @runner.builds
+ items = by_permission(items)
items = by_status(items)
sort_items(items)
end
@@ -20,6 +22,14 @@ module Ci
private
# rubocop: disable CodeReuse/ActiveRecord
+ def by_permission(items)
+ return items if @user.can_read_all_resources?
+
+ items.for_project(@user.authorized_project_mirrors(Gitlab::Access::REPORTER).select(:project_id))
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ # rubocop: disable CodeReuse/ActiveRecord
def by_status(items)
return items unless Ci::HasStatus::AVAILABLE_STATUSES.include?(params[:status])
diff --git a/app/finders/packages/conan/package_finder.rb b/app/finders/packages/conan/package_finder.rb
index 8ebdd358ba6..210b37635b3 100644
--- a/app/finders/packages/conan/package_finder.rb
+++ b/app/finders/packages/conan/package_finder.rb
@@ -25,7 +25,7 @@ module Packages
end
def projects_visible_to_current_user
- ::Project.public_or_visible_to_user(current_user)
+ ::Project.public_or_visible_to_user(current_user, ::Gitlab::Access::REPORTER)
end
end
end
diff --git a/app/graphql/resolvers/ci/config_resolver.rb b/app/graphql/resolvers/ci/config_resolver.rb
index f9d60650443..ccde39522c4 100644
--- a/app/graphql/resolvers/ci/config_resolver.rb
+++ b/app/graphql/resolvers/ci/config_resolver.rb
@@ -12,7 +12,7 @@ module Resolvers
Should not be requested more than once per request.
MD
- authorize :read_pipeline
+ authorize :create_pipeline
argument :project_path, GraphQL::Types::ID,
required: true,
diff --git a/app/helpers/integrations_helper.rb b/app/helpers/integrations_helper.rb
index b960ed46ba9..471ca425f83 100644
--- a/app/helpers/integrations_helper.rb
+++ b/app/helpers/integrations_helper.rb
@@ -159,27 +159,6 @@ module IntegrationsHelper
!Gitlab.com?
end
- def jira_issue_breadcrumb_link(issue_reference)
- link_to '', { class: 'gl-display-flex gl-align-items-center gl-white-space-nowrap' } do
- icon = image_tag image_path('illustrations/logos/jira.svg'), width: 15, height: 15, class: 'gl-mr-2'
- [icon, html_escape(issue_reference)].join.html_safe
- end
- end
-
- def zentao_issue_breadcrumb_link(issue)
- link_to issue[:web_url], { target: '_blank', rel: 'noopener noreferrer', class: 'gl-display-flex gl-align-items-center gl-white-space-nowrap' } do
- icon = image_tag image_path('logos/zentao.svg'), width: 15, height: 15, class: 'gl-mr-2'
- [icon, html_escape(issue[:id])].join.html_safe
- end
- end
-
- def zentao_issues_show_data
- {
- issues_show_path: project_integrations_zentao_issue_path(@project, params[:id], format: :json),
- issues_list_path: project_integrations_zentao_issues_path(@project)
- }
- end
-
extend self
private
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 21c7a54670c..1e427efe9d3 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -298,6 +298,7 @@ module ProjectsHelper
setting.organization_slug.blank?
{
+ sentry_project_id: setting.sentry_project_id,
name: setting.project_name,
organization_name: setting.organization_name,
organization_slug: setting.organization_slug,
diff --git a/app/helpers/timeboxes_helper.rb b/app/helpers/timeboxes_helper.rb
index c81fbcbfd11..39993bbfb44 100644
--- a/app/helpers/timeboxes_helper.rb
+++ b/app/helpers/timeboxes_helper.rb
@@ -153,11 +153,11 @@ module TimeboxesHelper
n_("%{releases} release", "%{releases} releases", count) % { releases: count }
end
- def recent_releases_with_counts(milestone)
+ def recent_releases_with_counts(milestone, user)
total_count = milestone.releases.size
return [[], 0, 0] if total_count == 0
- recent_releases = milestone.releases.recent.to_a
+ recent_releases = milestone.releases.recent.filter { |release| Ability.allowed?(user, :read_release, release) }
more_count = total_count - recent_releases.size
[recent_releases, total_count, more_count]
end
diff --git a/app/models/ci/project_mirror.rb b/app/models/ci/project_mirror.rb
index 9000d1791a6..15a161d5b7c 100644
--- a/app/models/ci/project_mirror.rb
+++ b/app/models/ci/project_mirror.rb
@@ -4,6 +4,8 @@ module Ci
# This model represents a shadow table of the main database's projects table.
# It allows us to navigate the project and namespace hierarchy on the ci database.
class ProjectMirror < ApplicationRecord
+ include FromUnion
+
belongs_to :project
scope :by_namespace_id, -> (namespace_id) { where(namespace_id: namespace_id) }
diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb
index e62b6fa5fc5..74b09f935eb 100644
--- a/app/models/clusters/applications/runner.rb
+++ b/app/models/clusters/applications/runner.rb
@@ -3,7 +3,7 @@
module Clusters
module Applications
class Runner < ApplicationRecord
- VERSION = '0.39.0'
+ VERSION = '0.39.2'
self.table_name = 'clusters_applications_runners'
diff --git a/app/models/concerns/enums/ci/commit_status.rb b/app/models/concerns/enums/ci/commit_status.rb
index 312b88a4d6d..445277a7a7c 100644
--- a/app/models/concerns/enums/ci/commit_status.rb
+++ b/app/models/concerns/enums/ci/commit_status.rb
@@ -35,7 +35,8 @@ module Enums
bridge_pipeline_is_child_pipeline: 1_006, # not used anymore, but cannot be deleted because of old data
downstream_pipeline_creation_failed: 1_007,
secrets_provider_not_found: 1_008,
- reached_max_descendant_pipelines_depth: 1_009
+ reached_max_descendant_pipelines_depth: 1_009,
+ ip_restriction_failure: 1_010
}
end
end
diff --git a/app/models/error_tracking/project_error_tracking_setting.rb b/app/models/error_tracking/project_error_tracking_setting.rb
index 3ecfb895dac..30382a1c205 100644
--- a/app/models/error_tracking/project_error_tracking_setting.rb
+++ b/app/models/error_tracking/project_error_tracking_setting.rb
@@ -125,17 +125,22 @@ module ErrorTracking
def issue_details(opts = {})
with_reactive_cache('issue_details', opts.stringify_keys) do |result|
+ ensure_issue_belongs_to_project!(result[:issue].project_id)
result
end
end
def issue_latest_event(opts = {})
with_reactive_cache('issue_latest_event', opts.stringify_keys) do |result|
+ ensure_issue_belongs_to_project!(result[:latest_event].project_id)
result
end
end
def update_issue(opts = {})
+ issue_to_be_updated = sentry_client.issue_details(issue_id: opts[:issue_id])
+ ensure_issue_belongs_to_project!(issue_to_be_updated.project_id)
+
handle_exceptions do
{ updated: sentry_client.update_issue(opts) }
end
@@ -177,6 +182,25 @@ module ErrorTracking
private
+ def ensure_issue_belongs_to_project!(project_id_from_api)
+ raise 'The Sentry issue appers to be outside of the configured Sentry project' if Integer(project_id_from_api) != ensure_sentry_project_id!
+ end
+
+ def ensure_sentry_project_id!
+ return sentry_project_id if sentry_project_id.present?
+
+ raise("Couldn't find project: #{organization_name} / #{project_name} on Sentry") if sentry_project.nil?
+
+ update!(sentry_project_id: sentry_project.id)
+ sentry_project_id
+ end
+
+ def sentry_project
+ strong_memoize(:sentry_project) do
+ sentry_client.projects.find { |project| project.name == project_name && project.organization_name == organization_name }
+ end
+ end
+
def add_gitlab_issue_details(issue)
issue.gitlab_commit = match_gitlab_commit(issue.first_release_version)
issue.gitlab_commit_path = project_commit_path(project, issue.gitlab_commit) if issue.gitlab_commit
diff --git a/app/models/group_group_link.rb b/app/models/group_group_link.rb
index b0020f097b5..a70110c4076 100644
--- a/app/models/group_group_link.rb
+++ b/app/models/group_group_link.rb
@@ -41,3 +41,5 @@ class GroupGroupLink < ApplicationRecord
Gitlab::Access.human_access(self.group_access)
end
end
+
+GroupGroupLink.prepend_mod_with('GroupGroupLink')
diff --git a/app/models/user.rb b/app/models/user.rb
index 26d47de4f00..25ac394793b 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1646,6 +1646,14 @@ class User < ApplicationRecord
true
end
+ def authorized_project_mirrors(level)
+ projects = Ci::ProjectMirror.by_project_id(ci_project_mirrors_for_project_members(level))
+
+ namespace_projects = Ci::ProjectMirror.by_namespace_id(ci_namespace_mirrors_for_group_members(level).select(:namespace_id))
+
+ Ci::ProjectMirror.from_union([projects, namespace_projects])
+ end
+
def ci_owned_runners
@ci_owned_runners ||= begin
if ci_owned_runners_cross_joins_fix_enabled?
@@ -2116,6 +2124,10 @@ class User < ApplicationRecord
end
# rubocop: enable CodeReuse/ServiceClass
+ def ci_project_mirrors_for_project_members(level)
+ project_members.where('access_level >= ?', level).pluck(:source_id)
+ end
+
def notification_email_verified
return if notification_email.blank? || temp_oauth_email?
@@ -2267,7 +2279,7 @@ class User < ApplicationRecord
end
def ci_owned_project_runners_from_project_members
- project_ids = project_members.where('access_level >= ?', Gitlab::Access::MAINTAINER).pluck(:source_id)
+ project_ids = ci_project_mirrors_for_project_members(Gitlab::Access::MAINTAINER)
Ci::Runner
.joins(:runner_projects)
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 493afd91364..7c40d02b44e 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -54,7 +54,13 @@ class ProjectPolicy < BasePolicy
desc "Container registry is disabled"
condition(:container_registry_disabled, scope: :subject) do
- !access_allowed_to?(:container_registry)
+ if user.is_a?(DeployToken)
+ (!user.read_registry? && !user.write_registry?) ||
+ user.revoked? ||
+ !project.container_registry_enabled?
+ else
+ !access_allowed_to?(:container_registry)
+ end
end
desc "Container registry is enabled for everyone with access to the project"
@@ -83,6 +89,16 @@ class ProjectPolicy < BasePolicy
user.is_a?(DeployKey) && user.can_push_to?(project)
end
+ desc "Deploy token with read_container_image scope"
+ condition(:read_container_image_deploy_token) do
+ user.is_a?(DeployToken) && user.has_access_to?(project) && user.read_registry?
+ end
+
+ desc "Deploy token with create_container_image scope"
+ condition(:create_container_image_deploy_token) do
+ user.is_a?(DeployToken) && user.has_access_to?(project) && user.write_registry?
+ end
+
desc "Deploy token with read_package_registry scope"
condition(:read_package_registry_deploy_token) do
user.is_a?(DeployToken) && user.has_access_to?(project) && user.read_package_registry
@@ -297,7 +313,6 @@ class ProjectPolicy < BasePolicy
enable :read_deployment
enable :read_merge_request
enable :read_sentry_issue
- enable :update_sentry_issue
enable :read_prometheus
enable :read_metrics_dashboard_annotation
enable :metrics_dashboard
@@ -413,6 +428,7 @@ class ProjectPolicy < BasePolicy
enable :admin_feature_flags_user_lists
enable :update_escalation_status
enable :read_secure_files
+ enable :update_sentry_issue
end
rule { can?(:developer_access) & user_confirmed? }.policy do
@@ -685,6 +701,14 @@ class ProjectPolicy < BasePolicy
enable :push_code
end
+ rule { read_container_image_deploy_token }.policy do
+ enable :read_container_image
+ end
+
+ rule { create_container_image_deploy_token }.policy do
+ enable :create_container_image
+ end
+
rule { read_package_registry_deploy_token }.policy do
enable :read_package
enable :read_project
diff --git a/app/presenters/commit_status_presenter.rb b/app/presenters/commit_status_presenter.rb
index fdfcc896bf8..675288da35b 100644
--- a/app/presenters/commit_status_presenter.rb
+++ b/app/presenters/commit_status_presenter.rb
@@ -30,7 +30,8 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
trace_size_exceeded: 'The job log size limit was reached',
builds_disabled: 'The CI/CD is disabled for this project',
environment_creation_failure: 'This job could not be executed because it would create an environment with an invalid parameter.',
- deployment_rejected: 'This deployment job was rejected.'
+ deployment_rejected: 'This deployment job was rejected.',
+ ip_restriction_failure: "This job could not be executed because group IP address restrictions are enabled, and the runner's IP address is not in the allowed range."
}.freeze
TROUBLESHOOTING_DOC = {
diff --git a/app/serializers/member_user_entity.rb b/app/serializers/member_user_entity.rb
index b3d8efc9143..6a01c5bb297 100644
--- a/app/serializers/member_user_entity.rb
+++ b/app/serializers/member_user_entity.rb
@@ -16,7 +16,7 @@ class MemberUserEntity < UserEntity
user.blocked?
end
- expose :two_factor_enabled do |user|
+ expose :two_factor_enabled, if: -> (user) { current_user_can_manage_members? || current_user?(user) } do |user|
user.two_factor_enabled?
end
@@ -25,6 +25,18 @@ class MemberUserEntity < UserEntity
user.status.emoji
end
end
+
+ private
+
+ def current_user_can_manage_members?
+ return false unless options[:source]
+
+ Ability.allowed?(options[:current_user], :"admin_#{options[:source].to_ability_name}_member", options[:source])
+ end
+
+ def current_user?(user)
+ options[:current_user] == user
+ end
end
MemberUserEntity.prepend_mod_with('MemberUserEntity')
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index 6d6d8641d9d..e806bef46fe 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -215,15 +215,13 @@ module Auth
def deploy_token_can_pull?(requested_project)
has_authentication_ability?(:read_container_image) &&
deploy_token.present? &&
- deploy_token.has_access_to?(requested_project) &&
- deploy_token.read_registry?
+ can?(deploy_token, :read_container_image, requested_project)
end
def deploy_token_can_push?(requested_project)
has_authentication_ability?(:create_container_image) &&
deploy_token.present? &&
- deploy_token.has_access_to?(requested_project) &&
- deploy_token.write_registry?
+ can?(deploy_token, :create_container_image, requested_project)
end
##
diff --git a/app/services/projects/operations/update_service.rb b/app/services/projects/operations/update_service.rb
index b66435d013b..d8686f16dc5 100644
--- a/app/services/projects/operations/update_service.rb
+++ b/app/services/projects/operations/update_service.rb
@@ -90,7 +90,8 @@ module Projects
api_url: api_url,
enabled: settings[:enabled],
project_name: settings.dig(:project, :name),
- organization_name: settings.dig(:project, :organization_name)
+ organization_name: settings.dig(:project, :organization_name),
+ sentry_project_id: settings.dig(:project, :sentry_project_id)
}
}
params[:error_tracking_setting_attributes][:token] = settings[:token] unless /\A\*+\z/.match?(settings[:token]) # Don't update token if we receive masked value
diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml
index 4e06b7902bd..4aecc0f6bd0 100644
--- a/app/views/shared/milestones/_milestone.html.haml
+++ b/app/views/shared/milestones/_milestone.html.haml
@@ -14,7 +14,7 @@
- if milestone.due_date || milestone.start_date
.text-tertiary.gl-mb-2
= milestone_date_range(milestone)
- - recent_releases, total_count, more_count = recent_releases_with_counts(milestone)
+ - recent_releases, total_count, more_count = recent_releases_with_counts(milestone, current_user)
- unless total_count == 0
.text-tertiary.gl-mb-2.milestone-release-links
= sprite_icon("rocket", size: 12)
diff --git a/app/views/shared/milestones/_sidebar.html.haml b/app/views/shared/milestones/_sidebar.html.haml
index a1e94172ec3..ed5373fa58c 100644
--- a/app/views/shared/milestones/_sidebar.html.haml
+++ b/app/views/shared/milestones/_sidebar.html.haml
@@ -138,7 +138,7 @@
= milestone.merge_requests.merged.count
- if project
- - recent_releases, total_count, more_count = recent_releases_with_counts(milestone)
+ - recent_releases, total_count, more_count = recent_releases_with_counts(milestone, current_user)
.block.releases
.sidebar-collapsed-icon.has-tooltip{ title: milestone_releases_tooltip_text(milestone), data: { container: 'body', placement: 'left', boundary: 'viewport' } }
%strong
diff --git a/config/initializers/net_http_response_patch.rb b/config/initializers/net_http_response_patch.rb
new file mode 100644
index 00000000000..4ffe227a3fd
--- /dev/null
+++ b/config/initializers/net_http_response_patch.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module Net
+ class HTTPResponse
+ # rubocop: disable Cop/LineBreakAfterGuardClauses
+ # rubocop: disable Cop/LineBreakAroundConditionalBlock
+ # rubocop: disable Layout/EmptyLineAfterGuardClause
+ # rubocop: disable Style/AndOr
+ # rubocop: disable Style/CharacterLiteral
+ # rubocop: disable Style/InfiniteLoop
+
+ # Original method:
+ # https://github.com/ruby/ruby/blob/v2_7_5/lib/net/http/response.rb#L54-L69
+ #
+ # Our changes:
+ # - Pass along the `start_time` to `Gitlab::BufferedIo`, so we can raise a timeout
+ # if reading the headers takes too long.
+ # - Limit the regexes to avoid ReDoS attacks.
+ def self.each_response_header(sock)
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ key = value = nil
+ while true
+ line = sock.is_a?(Gitlab::BufferedIo) ? sock.readuntil("\n", true, start_time) : sock.readuntil("\n", true)
+ line = line.sub(/\s{0,10}\z/, '')
+ break if line.empty?
+ if line[0] == ?\s or line[0] == ?\t and value
+ # :nocov:
+ value << ' ' unless value.empty?
+ value << line.strip
+ # :nocov:
+ else
+ yield key, value if key
+ key, value = line.strip.split(/\s{0,10}:\s{0,10}/, 2)
+ raise Net::HTTPBadResponse, 'wrong header line format' if value.nil?
+ end
+ end
+ yield key, value if key
+ end
+ # rubocop: enable Cop/LineBreakAfterGuardClauses
+ # rubocop: enable Cop/LineBreakAroundConditionalBlock
+ # rubocop: enable Layout/EmptyLineAfterGuardClause
+ # rubocop: enable Style/AndOr
+ # rubocop: enable Style/CharacterLiteral
+ # rubocop: enable Style/InfiniteLoop
+ end
+end
diff --git a/db/migrate/20220520120637_add_installable_conan_packages_index_to_packages.rb b/db/migrate/20220520120637_add_installable_conan_packages_index_to_packages.rb
new file mode 100644
index 00000000000..b26d1f5429a
--- /dev/null
+++ b/db/migrate/20220520120637_add_installable_conan_packages_index_to_packages.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class AddInstallableConanPackagesIndexToPackages < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'idx_installable_conan_pkgs_on_project_id_id'
+ # as defined by Packages::Package.package_types
+ CONAN_PACKAGE_TYPE = 3
+
+ # as defined by Packages::Package::INSTALLABLE_STATUSES
+ DEFAULT_STATUS = 0
+ HIDDEN_STATUS = 1
+
+ def up
+ where = "package_type = #{CONAN_PACKAGE_TYPE} AND status IN (#{DEFAULT_STATUS}, #{HIDDEN_STATUS})"
+ add_concurrent_index :packages_packages,
+ [:project_id, :id],
+ where: where,
+ name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :packages_packages, INDEX_NAME
+ end
+end
diff --git a/db/migrate/20220525084153_add_sentry_project_id_to_project_error_tracking_settings.rb b/db/migrate/20220525084153_add_sentry_project_id_to_project_error_tracking_settings.rb
new file mode 100644
index 00000000000..248dd128bec
--- /dev/null
+++ b/db/migrate/20220525084153_add_sentry_project_id_to_project_error_tracking_settings.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddSentryProjectIdToProjectErrorTrackingSettings < Gitlab::Database::Migration[2.0]
+ def change
+ add_column :project_error_tracking_settings, :sentry_project_id, :bigint
+ end
+end
diff --git a/db/schema_migrations/20220520120637 b/db/schema_migrations/20220520120637
new file mode 100644
index 00000000000..f379ef0d581
--- /dev/null
+++ b/db/schema_migrations/20220520120637
@@ -0,0 +1 @@
+1fdb60b1c72b687aa8bede083ac7038097d538dc815e334d74296b1d39c2acb8 \ No newline at end of file
diff --git a/db/schema_migrations/20220525084153 b/db/schema_migrations/20220525084153
new file mode 100644
index 00000000000..dbf7eaa0c93
--- /dev/null
+++ b/db/schema_migrations/20220525084153
@@ -0,0 +1 @@
+1f03beba0775e2a4eead512819592f590b02b70096cee250dfcdf426440cb5f5 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index eaae14bebb8..334d59db237 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -19156,7 +19156,8 @@ CREATE TABLE project_error_tracking_settings (
encrypted_token_iv character varying,
project_name character varying,
organization_name character varying,
- integrated boolean DEFAULT true NOT NULL
+ integrated boolean DEFAULT true NOT NULL,
+ sentry_project_id bigint
);
CREATE TABLE project_export_jobs (
@@ -26544,6 +26545,8 @@ CREATE UNIQUE INDEX idx_environment_merge_requests_unique_index ON deployment_me
CREATE INDEX idx_geo_con_rep_updated_events_on_container_repository_id ON geo_container_repository_updated_events USING btree (container_repository_id);
+CREATE INDEX idx_installable_conan_pkgs_on_project_id_id ON packages_packages USING btree (project_id, id) WHERE ((package_type = 3) AND (status = ANY (ARRAY[0, 1])));
+
CREATE INDEX idx_installable_helm_pkgs_on_project_id_id ON packages_packages USING btree (project_id, id);
CREATE INDEX idx_installable_npm_pkgs_on_project_id_name_version_id ON packages_packages USING btree (project_id, name, version, id) WHERE ((package_type = 2) AND (status = 0));
diff --git a/doc/api/runners.md b/doc/api/runners.md
index 304f2494f70..6ecdd620d38 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -359,7 +359,8 @@ and will be removed in [GitLab 16.0](https://gitlab.com/gitlab-org/gitlab/-/issu
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/15432) in GitLab 10.3.
-List jobs that are being processed or were processed by specified runner.
+List jobs that are being processed or were processed by the specified runner. The list of jobs is limited
+to projects where the user has at least the Reporter role.
```plaintext
GET /runners/:id/jobs
diff --git a/doc/operations/error_tracking.md b/doc/operations/error_tracking.md
index 907c59adacb..3e47a86d74b 100644
--- a/doc/operations/error_tracking.md
+++ b/doc/operations/error_tracking.md
@@ -106,7 +106,7 @@ button and a link to the GitLab issue displays within the error detail section.
## Taking Action on errors
-You can take action on Sentry Errors from within the GitLab UI.
+You can take action on Sentry Errors from within the GitLab UI. Marking errors ignored or resolved require at least Developer role.
### Ignoring errors
diff --git a/doc/update/index.md b/doc/update/index.md
index 003c0fc02f7..b2d31489ed4 100644
--- a/doc/update/index.md
+++ b/doc/update/index.md
@@ -407,6 +407,9 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
- The upgrade to GitLab 14.10 executes a [concurrent index drop](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84308) of unneeded
entries from the `ci_job_artifacts` database table. This could potentially run for multiple minutes, especially if the table has a lot of
traffic and the migration is unable to acquire a lock. It is advised to let this process finish as restarting may result in data loss.
+- Unauthenticated requests to the [`ciConfig` GraphQL field](../api/graphql/reference/index.md#queryciconfig) are no longer supported.
+ Before you upgrade to GitLab 15.1, add an [access token](../api/index.md#authentication) to your requests.
+ The user creating the token must have [permission](../user/permissions.md) to create pipelines in the project.
### 14.9.0
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index 085cd054c14..4d22bb045d2 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -640,6 +640,7 @@ To restrict group access by IP address:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7297) in GitLab 12.2.
> - Support for specifying multiple email domains [added](https://gitlab.com/gitlab-org/gitlab/-/issues/33143) in GitLab 13.1.
> - Support for restricting access to projects in the group [added](https://gitlab.com/gitlab-org/gitlab/-/issues/14004) in GitLab 14.1.2.
+> - Support for restricting group memberships to groups with a subset of the allowed email domains [added](https://gitlab.com/gitlab-org/gitlab/-/issues/354791) in GitLab 15.0.1
You can prevent users with email addresses in specific domains from being added to a group and its projects.
@@ -662,6 +663,8 @@ The most popular public email domains cannot be restricted, such as:
- `hotmail.com`, `hotmail.co.uk`, `hotmail.fr`
- `msn.com`, `live.com`, `outlook.com`
+When you share a group, both the source and target namespaces must allow the domains of the members' email addresses.
+
## Group file templates **(PREMIUM)**
Use group file templates to share a set of templates for common file
diff --git a/lib/api/ci/runners.rb b/lib/api/ci/runners.rb
index 3c9e887e751..a4c6f9b6822 100644
--- a/lib/api/ci/runners.rb
+++ b/lib/api/ci/runners.rb
@@ -127,7 +127,7 @@ module API
runner = get_runner(params[:id])
authenticate_list_runners_jobs!(runner)
- jobs = ::Ci::RunnerJobsFinder.new(runner, params).execute
+ jobs = ::Ci::RunnerJobsFinder.new(runner, current_user, params).execute
present paginate(jobs), with: Entities::Ci::JobBasicWithProject
end
diff --git a/lib/api/helpers/label_helpers.rb b/lib/api/helpers/label_helpers.rb
index 02613cbf9b9..8572cc89e71 100644
--- a/lib/api/helpers/label_helpers.rb
+++ b/lib/api/helpers/label_helpers.rb
@@ -82,8 +82,14 @@ module API
params.delete(:label_id)
params.delete(:name)
- label = ::Labels::UpdateService.new(declared_params(include_missing: false)).execute(label)
- render_validation_error!(label) unless label.valid?
+ update_params = declared_params(include_missing: false)
+
+ if update_params.present?
+ authorize! :admin_label, label
+
+ label = ::Labels::UpdateService.new(update_params).execute(label)
+ render_validation_error!(label) unless label.valid?
+ end
if parent.is_a?(Project) && update_priority
if priority.nil?
@@ -97,10 +103,10 @@ module API
end
def delete_label(parent)
- authorize! :admin_label, parent
-
label = find_label(parent, params_id_or_title, include_ancestor_groups: false)
+ authorize! :admin_label, label
+
destroy_conditionally!(label)
end
diff --git a/lib/bulk_imports/projects/graphql/get_project_query.rb b/lib/bulk_imports/projects/graphql/get_project_query.rb
index b3d7f3f4683..76475893ac1 100644
--- a/lib/bulk_imports/projects/graphql/get_project_query.rb
+++ b/lib/bulk_imports/projects/graphql/get_project_query.rb
@@ -10,20 +10,8 @@ module BulkImports
<<-'GRAPHQL'
query($full_path: ID!) {
project(fullPath: $full_path) {
- description
visibility
- archived
created_at: createdAt
- shared_runners_enabled: sharedRunnersEnabled
- container_registry_enabled: containerRegistryEnabled
- only_allow_merge_if_pipeline_succeeds: onlyAllowMergeIfPipelineSucceeds
- only_allow_merge_if_all_discussions_are_resolved: onlyAllowMergeIfAllDiscussionsAreResolved
- request_access_enabled: requestAccessEnabled
- printing_merge_request_link_enabled: printingMergeRequestLinkEnabled
- remove_source_branch_after_merge: removeSourceBranchAfterMerge
- autoclose_referenced_issues: autocloseReferencedIssues
- suggestion_commit_message: suggestionCommitMessage
- wiki_enabled: wikiEnabled
}
}
GRAPHQL
diff --git a/lib/bulk_imports/projects/transformers/project_attributes_transformer.rb b/lib/bulk_imports/projects/transformers/project_attributes_transformer.rb
index 24c55d8dbb1..38730a7723b 100644
--- a/lib/bulk_imports/projects/transformers/project_attributes_transformer.rb
+++ b/lib/bulk_imports/projects/transformers/project_attributes_transformer.rb
@@ -7,16 +7,18 @@ module BulkImports
PROJECT_IMPORT_TYPE = 'gitlab_project_migration'
def transform(context, data)
+ project = {}
entity = context.entity
visibility = data.delete('visibility')
- data['name'] = entity.destination_name
- data['path'] = entity.destination_name.parameterize
- data['import_type'] = PROJECT_IMPORT_TYPE
- data['visibility_level'] = Gitlab::VisibilityLevel.string_options[visibility] if visibility.present?
- data['namespace_id'] = Namespace.find_by_full_path(entity.destination_namespace)&.id if entity.destination_namespace.present?
+ project[:name] = entity.destination_name
+ project[:path] = entity.destination_name.parameterize
+ project[:created_at] = data['created_at']
+ project[:import_type] = PROJECT_IMPORT_TYPE
+ project[:visibility_level] = Gitlab::VisibilityLevel.string_options[visibility] if visibility.present?
+ project[:namespace_id] = Namespace.find_by_full_path(entity.destination_namespace)&.id if entity.destination_namespace.present?
- data.transform_keys!(&:to_sym)
+ project
end
end
end
diff --git a/lib/error_tracking/sentry_client/event.rb b/lib/error_tracking/sentry_client/event.rb
index 5343eb7df57..1db31abeeb2 100644
--- a/lib/error_tracking/sentry_client/event.rb
+++ b/lib/error_tracking/sentry_client/event.rb
@@ -15,6 +15,7 @@ module ErrorTracking
stack_trace = parse_stack_trace(event)
Gitlab::ErrorTracking::ErrorEvent.new(
+ project_id: event['projectID'],
issue_id: event['groupID'],
date_received: event['dateReceived'],
stack_trace_entries: stack_trace
diff --git a/lib/gitlab/buffered_io.rb b/lib/gitlab/buffered_io.rb
index 91d5d8acc52..b76c8191fa2 100644
--- a/lib/gitlab/buffered_io.rb
+++ b/lib/gitlab/buffered_io.rb
@@ -14,6 +14,7 @@ module Gitlab
HEADER_READ_TIMEOUT = 20
+ # rubocop: disable Style/RedundantBegin
# rubocop: disable Style/RedundantReturn
# rubocop: disable Cop/LineBreakAfterGuardClauses
# rubocop: disable Layout/EmptyLineAfterGuardClause
@@ -21,9 +22,7 @@ module Gitlab
# Original method:
# https://github.com/ruby/ruby/blob/cdb7d699d0641e8f081d590d06d07887ac09961f/lib/net/protocol.rb#L190-L200
override :readuntil
- def readuntil(terminator, ignore_eof = false)
- start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
-
+ def readuntil(terminator, ignore_eof = false, start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC))
begin
until idx = @rbuf.index(terminator)
if (elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) > HEADER_READ_TIMEOUT
@@ -39,6 +38,7 @@ module Gitlab
return rbuf_consume(@rbuf.size)
end
end
+ # rubocop: disable Style/RedundantBegin
# rubocop: enable Style/RedundantReturn
# rubocop: enable Cop/LineBreakAfterGuardClauses
# rubocop: enable Layout/EmptyLineAfterGuardClause
diff --git a/lib/gitlab/ci/status/build/failed.rb b/lib/gitlab/ci/status/build/failed.rb
index 5dd28157b1f..1a074c1af53 100644
--- a/lib/gitlab/ci/status/build/failed.rb
+++ b/lib/gitlab/ci/status/build/failed.rb
@@ -35,7 +35,8 @@ module Gitlab
trace_size_exceeded: 'log size limit exceeded',
builds_disabled: 'project builds are disabled',
environment_creation_failure: 'environment creation failure',
- deployment_rejected: 'deployment rejected'
+ deployment_rejected: 'deployment rejected',
+ ip_restriction_failure: 'IP address restriction failure'
}.freeze
private_constant :REASONS
diff --git a/lib/gitlab/error_tracking/error_event.rb b/lib/gitlab/error_tracking/error_event.rb
index d80289f6bc9..590fb82883b 100644
--- a/lib/gitlab/error_tracking/error_event.rb
+++ b/lib/gitlab/error_tracking/error_event.rb
@@ -7,7 +7,7 @@ module Gitlab
class ErrorEvent
include ActiveModel::Model
- attr_accessor :issue_id, :date_received, :stack_trace_entries, :gitlab_project
+ attr_accessor :issue_id, :date_received, :stack_trace_entries, :gitlab_project, :project_id
def self.declarative_policy_class
'ErrorTracking::BasePolicy'
diff --git a/lib/gitlab/import_export/decompressed_archive_size_validator.rb b/lib/gitlab/import_export/decompressed_archive_size_validator.rb
index 61b37256964..a185eb4df1c 100644
--- a/lib/gitlab/import_export/decompressed_archive_size_validator.rb
+++ b/lib/gitlab/import_export/decompressed_archive_size_validator.rb
@@ -8,6 +8,8 @@ module Gitlab
DEFAULT_MAX_BYTES = 10.gigabytes.freeze
TIMEOUT_LIMIT = 210.seconds
+ ServiceError = Class.new(StandardError)
+
def initialize(archive_path:, max_bytes: self.class.max_bytes)
@archive_path = archive_path
@max_bytes = max_bytes
@@ -29,6 +31,8 @@ module Gitlab
pgrp = nil
valid_archive = true
+ validate_archive_path
+
Timeout.timeout(TIMEOUT_LIMIT) do
stdin, stdout, stderr, wait_thr = Open3.popen3(command, pgroup: true)
stdin.close
@@ -78,15 +82,29 @@ module Gitlab
false
end
+ def validate_archive_path
+ Gitlab::Utils.check_path_traversal!(@archive_path)
+
+ raise(ServiceError, 'Archive path is not a string') unless @archive_path.is_a?(String)
+ raise(ServiceError, 'Archive path is a symlink') if File.lstat(@archive_path).symlink?
+ raise(ServiceError, 'Archive path is not a file') unless File.file?(@archive_path)
+ end
+
def command
"gzip -dc #{@archive_path} | wc -c"
end
def log_error(error)
+ archive_size = begin
+ File.size(@archive_path)
+ rescue StandardError
+ nil
+ end
+
Gitlab::Import::Logger.info(
message: error,
import_upload_archive_path: @archive_path,
- import_upload_archive_size: File.size(@archive_path)
+ import_upload_archive_size: archive_size
)
end
end
diff --git a/lib/gitlab/jira/dvcs.rb b/lib/gitlab/jira/dvcs.rb
index ddf2cd76709..41a039674b3 100644
--- a/lib/gitlab/jira/dvcs.rb
+++ b/lib/gitlab/jira/dvcs.rb
@@ -38,7 +38,8 @@ module Gitlab
# @param [String] namespace
def self.restore_full_path(namespace:, project:)
if project.include?(ENCODED_SLASH)
- project.gsub(ENCODED_SLASH, SLASH)
+ # Replace multiple slashes with single ones to make sure the redirect stays on the same host
+ project.gsub(ENCODED_SLASH, SLASH).gsub(%r{\/{2,}}, '/')
else
"#{namespace}/#{project}"
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index edad17df272..5ecd033296b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -20839,6 +20839,9 @@ msgstr ""
msgid "Invited"
msgstr ""
+msgid "Invited group allowed email domains must contain a subset of the allowed email domains of the root ancestor group. Go to the group's 'Settings &gt; General' page and check 'Restrict membership by email domain'."
+msgstr ""
+
msgid "Invocations"
msgstr ""
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index c4809a17f66..675d163fe9d 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -251,7 +251,7 @@ GEM
pry (~> 0.10)
public_suffix (4.0.6)
racc (1.6.0)
- rack (2.2.3)
+ rack (2.2.3.1)
rack-test (1.1.0)
rack (>= 1.0, < 3)
rainbow (3.0.0)
diff --git a/spec/controllers/projects/error_tracking_controller_spec.rb b/spec/controllers/projects/error_tracking_controller_spec.rb
index b4f21e070c6..6598d9b173c 100644
--- a/spec/controllers/projects/error_tracking_controller_spec.rb
+++ b/spec/controllers/projects/error_tracking_controller_spec.rb
@@ -297,40 +297,54 @@ RSpec.describe Projects::ErrorTrackingController do
put :update, params: issue_params(issue_id: issue_id, status: 'resolved', format: :json)
end
- before do
- expect(ErrorTracking::IssueUpdateService)
- .to receive(:new).with(project, user, permitted_params)
- .and_return(issue_update_service)
- end
-
describe 'format json' do
- context 'update result is successful' do
+ context 'when user is a reporter' do
before do
- expect(issue_update_service).to receive(:execute)
- .and_return(status: :success, updated: true, closed_issue_iid: non_existing_record_iid)
+ project.add_reporter(user)
+ end
+ it 'returns 404 error' do
update_issue
- end
- it 'returns a success' do
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('error_tracking/update_issue')
+ expect(response).to have_gitlab_http_status(:not_found)
end
end
- context 'update result is erroneous' do
- let(:error_message) { 'error message' }
-
+ context 'when user has access to update' do
before do
- expect(issue_update_service).to receive(:execute)
- .and_return(status: :error, message: error_message)
+ expect(ErrorTracking::IssueUpdateService)
+ .to receive(:new).with(project, user, permitted_params)
+ .and_return(issue_update_service)
+ end
- update_issue
+ context 'when update result is successful' do
+ before do
+ expect(issue_update_service).to receive(:execute)
+ .and_return(status: :success, updated: true, closed_issue_iid: non_existing_record_iid)
+
+ update_issue
+ end
+
+ it 'returns a success' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('error_tracking/update_issue')
+ end
end
- it 'returns 400 with message' do
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['message']).to eq(error_message)
+ context 'update result is erroneous' do
+ let(:error_message) { 'error message' }
+
+ before do
+ expect(issue_update_service).to receive(:execute)
+ .and_return(status: :error, message: error_message)
+
+ update_issue
+ end
+
+ it 'returns 400 with message' do
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to eq(error_message)
+ end
end
end
end
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
index 04f73050ea5..bb7298bc122 100644
--- a/spec/db/schema_spec.rb
+++ b/spec/db/schema_spec.rb
@@ -72,6 +72,7 @@ RSpec.describe 'Database schema' do
oauth_applications: %w[owner_id],
product_analytics_events_experimental: %w[event_id txn_id user_id],
project_build_artifacts_size_refreshes: %w[last_job_artifact_id],
+ project_error_tracking_settings: %w[sentry_project_id],
project_group_links: %w[group_id],
project_statistics: %w[namespace_id],
projects: %w[creator_id ci_id mirror_user_id],
diff --git a/spec/factories/error_tracking/error.rb b/spec/factories/error_tracking/error.rb
index bebdffb3614..62d16244122 100644
--- a/spec/factories/error_tracking/error.rb
+++ b/spec/factories/error_tracking/error.rb
@@ -13,7 +13,7 @@ FactoryBot.define do
message { 'message' }
culprit { 'culprit' }
external_url { 'http://example.com/id' }
- project_id { 'project1' }
+ project_id { '111111' }
project_name { 'project name' }
project_slug { 'project_name' }
short_id { 'ID' }
diff --git a/spec/factories/project_error_tracking_settings.rb b/spec/factories/project_error_tracking_settings.rb
index ed743d8283c..a8ad1af6345 100644
--- a/spec/factories/project_error_tracking_settings.rb
+++ b/spec/factories/project_error_tracking_settings.rb
@@ -9,6 +9,7 @@ FactoryBot.define do
project_name { 'Sentry Project' }
organization_name { 'Sentry Org' }
integrated { false }
+ sentry_project_id { 10 }
trait :disabled do
enabled { false }
diff --git a/spec/finders/ci/runner_jobs_finder_spec.rb b/spec/finders/ci/runner_jobs_finder_spec.rb
index 3569582d70f..755b21ec08f 100644
--- a/spec/finders/ci/runner_jobs_finder_spec.rb
+++ b/spec/finders/ci/runner_jobs_finder_spec.rb
@@ -5,12 +5,17 @@ require 'spec_helper'
RSpec.describe Ci::RunnerJobsFinder do
let(:project) { create(:project) }
let(:runner) { create(:ci_runner, :instance) }
+ let(:user) { create(:user) }
+ let(:params) { {} }
- subject { described_class.new(runner, params).execute }
+ subject { described_class.new(runner, user, params).execute }
+
+ before do
+ project.add_developer(user)
+ end
describe '#execute' do
context 'when params is empty' do
- let(:params) { {} }
let!(:job) { create(:ci_build, runner: runner, project: project) }
let!(:job1) { create(:ci_build, project: project) }
@@ -20,6 +25,50 @@ RSpec.describe Ci::RunnerJobsFinder do
end
end
+ context 'when the user has guest access' do
+ it 'does not returns jobs the user does not have permission to see' do
+ another_project = create(:project)
+ job = create(:ci_build, runner: runner, project: another_project)
+
+ another_project.add_guest(user)
+
+ is_expected.not_to match_array(job)
+ end
+ end
+
+ context 'when the user has permission to read all resources' do
+ let(:user) { create(:user, :admin) }
+
+ it 'returns all the jobs assigned to a runner' do
+ jobs = create_list(:ci_build, 5, runner: runner, project: project)
+
+ is_expected.to match_array(jobs)
+ end
+ end
+
+ context 'when the user has different access levels in different projects' do
+ it 'returns only the jobs the user has permission to see' do
+ guest_project = create(:project)
+ reporter_project = create(:project)
+
+ _guest_jobs = create_list(:ci_build, 2, runner: runner, project: guest_project)
+ reporter_jobs = create_list(:ci_build, 3, runner: runner, project: reporter_project)
+
+ guest_project.add_guest(user)
+ reporter_project.add_reporter(user)
+
+ is_expected.to match_array(reporter_jobs)
+ end
+ end
+
+ context 'when the user has reporter access level or greater' do
+ it 'returns jobs assigned to the Runner that the user has accesss to' do
+ jobs = create_list(:ci_build, 3, runner: runner, project: project)
+
+ is_expected.to match_array(jobs)
+ end
+ end
+
context 'when params contains status' do
Ci::HasStatus::AVAILABLE_STATUSES.each do |target_status|
context "when status is #{target_status}" do
diff --git a/spec/finders/packages/conan/package_finder_spec.rb b/spec/finders/packages/conan/package_finder_spec.rb
index b26f8900090..6848786818b 100644
--- a/spec/finders/packages/conan/package_finder_spec.rb
+++ b/spec/finders/packages/conan/package_finder_spec.rb
@@ -2,22 +2,53 @@
require 'spec_helper'
RSpec.describe ::Packages::Conan::PackageFinder do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be_with_reload(:project) { create(:project) }
let_it_be(:user) { create(:user) }
- let_it_be(:project) { create(:project, :public) }
+ let_it_be(:private_project) { create(:project, :private) }
+
+ let_it_be(:conan_package) { create(:conan_package, project: project) }
+ let_it_be(:conan_package2) { create(:conan_package, project: project) }
+ let_it_be(:errored_package) { create(:conan_package, :error, project: project) }
+ let_it_be(:private_package) { create(:conan_package, project: private_project) }
describe '#execute' do
- let!(:conan_package) { create(:conan_package, project: project) }
- let!(:conan_package2) { create(:conan_package, project: project) }
+ let(:query) { "#{conan_package.name.split('/').first[0, 3]}%" }
+ let(:finder) { described_class.new(user, query: query) }
+
+ subject { finder.execute }
+
+ where(:visibility, :role, :packages_visible) do
+ :private | :maintainer | true
+ :private | :developer | true
+ :private | :reporter | true
+ :private | :guest | false
+ :private | :anonymous | false
+
+ :internal | :maintainer | true
+ :internal | :developer | true
+ :internal | :reporter | true
+ :internal | :guest | true
+ :internal | :anonymous | false
+
+ :public | :maintainer | true
+ :public | :developer | true
+ :public | :reporter | true
+ :public | :guest | true
+ :public | :anonymous | true
+ end
- subject { described_class.new(user, query: query).execute }
+ with_them do
+ let(:expected_packages) { packages_visible ? [conan_package, conan_package2] : [] }
+ let(:user) { role == :anonymous ? nil : super() }
- context 'packages that are not installable' do
- let!(:conan_package3) { create(:conan_package, :error, project: project) }
- let!(:non_visible_project) { create(:project, :private) }
- let!(:non_visible_conan_package) { create(:conan_package, project: non_visible_project) }
- let(:query) { "#{conan_package.name.split('/').first[0, 3]}%" }
+ before do
+ project.update_column(:visibility_level, Gitlab::VisibilityLevel.string_options[visibility.to_s])
+ project.add_user(user, role) unless role == :anonymous
+ end
- it { is_expected.to eq [conan_package, conan_package2] }
+ it { is_expected.to eq(expected_packages) }
end
end
end
diff --git a/spec/fixtures/api/schemas/entities/member.json b/spec/fixtures/api/schemas/entities/member.json
index dec98123e85..88f7d87b269 100644
--- a/spec/fixtures/api/schemas/entities/member.json
+++ b/spec/fixtures/api/schemas/entities/member.json
@@ -53,7 +53,7 @@
},
"user": {
"allOf": [
- { "$ref": "member_user.json" }
+ { "$ref": "member_user_default.json" }
]
},
"state": { "type": "integer" },
diff --git a/spec/fixtures/api/schemas/entities/member_user_default.json b/spec/fixtures/api/schemas/entities/member_user_default.json
new file mode 100644
index 00000000000..e0b3dba5699
--- /dev/null
+++ b/spec/fixtures/api/schemas/entities/member_user_default.json
@@ -0,0 +1,35 @@
+{
+ "type": "object",
+ "required": [
+ "id",
+ "name",
+ "username",
+ "created_at",
+ "last_activity_on",
+ "avatar_url",
+ "web_url",
+ "blocked",
+ "show_status"
+ ],
+ "properties": {
+ "id": { "type": "integer" },
+ "name": { "type": "string" },
+ "username": { "type": "string" },
+ "created_at": { "type": ["string"] },
+ "avatar_url": { "type": ["string", "null"] },
+ "web_url": { "type": "string" },
+ "blocked": { "type": "boolean" },
+ "two_factor_enabled": { "type": "boolean" },
+ "availability": { "type": ["string", "null"] },
+ "last_activity_on": { "type": ["string", "null"] },
+ "status": {
+ "type": "object",
+ "required": ["emoji"],
+ "properties": {
+ "emoji": { "type": "string" }
+ },
+ "additionalProperties": false
+ },
+ "show_status": { "type": "boolean" }
+ }
+}
diff --git a/spec/fixtures/api/schemas/entities/member_user.json b/spec/fixtures/api/schemas/entities/member_user_for_admin_member.json
index 0750e81e115..0750e81e115 100644
--- a/spec/fixtures/api/schemas/entities/member_user.json
+++ b/spec/fixtures/api/schemas/entities/member_user_for_admin_member.json
diff --git a/spec/frontend/projects/settings/access_dropdown_spec.js b/spec/frontend/projects/settings/access_dropdown_spec.js
index 236968a3736..b98c7cdf2a0 100644
--- a/spec/frontend/projects/settings/access_dropdown_spec.js
+++ b/spec/frontend/projects/settings/access_dropdown_spec.js
@@ -154,4 +154,21 @@ describe('AccessDropdown', () => {
expect(template).not.toContain(user.name);
});
});
+
+ describe('deployKeyRowHtml', () => {
+ const deployKey = {
+ id: 1,
+ title: 'title <script>alert(document.domain)</script>',
+ fullname: 'fullname <script>alert(document.domain)</script>',
+ avatar_url: '',
+ username: '',
+ };
+
+ it('escapes deploy key title and fullname', () => {
+ const template = dropdown.deployKeyRowHtml(deployKey);
+
+ expect(template).not.toContain(deployKey.title);
+ expect(template).not.toContain(deployKey.fullname);
+ });
+ });
});
diff --git a/spec/graphql/resolvers/ci/config_resolver_spec.rb b/spec/graphql/resolvers/ci/config_resolver_spec.rb
index 3ff6d8f4347..570f547292c 100644
--- a/spec/graphql/resolvers/ci/config_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/config_resolver_spec.rb
@@ -7,24 +7,13 @@ RSpec.describe Resolvers::Ci::ConfigResolver do
describe '#resolve' do
let_it_be(:user) { create(:user) }
- let_it_be(:project) { create(:project, :repository, creator: user, namespace: user.namespace) }
+ let_it_be(:project) { create(:project, :repository) }
let_it_be(:sha) { nil }
let_it_be(:content) do
File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci_includes.yml'))
end
- let(:ci_lint) do
- ci_lint_double = instance_double(::Gitlab::Ci::Lint)
- allow(ci_lint_double).to receive(:validate).and_return(fake_result)
-
- ci_lint_double
- end
-
- before do
- allow(::Gitlab::Ci::Lint).to receive(:new).and_return(ci_lint)
- end
-
subject(:response) do
resolve(described_class,
args: { project_path: project.full_path, content: content, sha: sha },
@@ -49,51 +38,76 @@ RSpec.describe Resolvers::Ci::ConfigResolver do
end
end
- context 'with a valid .gitlab-ci.yml' do
- context 'with a sha' do
- let(:sha) { '1231231' }
+ context 'when the user can create a pipeline' do
+ let(:ci_lint) do
+ ci_lint_double = instance_double(::Gitlab::Ci::Lint)
+ allow(ci_lint_double).to receive(:validate).and_return(fake_result)
- it_behaves_like 'a valid config file'
+ ci_lint_double
end
- context 'without a sha' do
- it_behaves_like 'a valid config file'
+ before do
+ allow(::Gitlab::Ci::Lint).to receive(:new).and_return(ci_lint)
+
+ project.add_developer(user)
end
- end
- context 'with an invalid .gitlab-ci.yml' do
- let(:content) { 'invalid' }
+ context 'with a valid .gitlab-ci.yml' do
+ context 'with a sha' do
+ let(:sha) { '1231231' }
- let(:fake_result) do
- Gitlab::Ci::Lint::Result.new(
- jobs: [],
- merged_yaml: content,
- errors: ['Invalid configuration format'],
- warnings: []
- )
+ it_behaves_like 'a valid config file'
+ end
+
+ context 'without a sha' do
+ it_behaves_like 'a valid config file'
+ end
end
- it 'responds with errors about invalid syntax' do
- expect(response[:status]).to eq(:invalid)
- expect(response[:errors]).to eq(['Invalid configuration format'])
+ context 'with an invalid .gitlab-ci.yml' do
+ let(:content) { 'invalid' }
+
+ let(:fake_result) do
+ Gitlab::Ci::Lint::Result.new(
+ jobs: [],
+ merged_yaml: content,
+ errors: ['Invalid configuration format'],
+ warnings: []
+ )
+ end
+
+ it 'responds with errors about invalid syntax' do
+ expect(response[:status]).to eq(:invalid)
+ expect(response[:errors]).to match_array(['Invalid configuration format'])
+ end
end
- end
- context 'with an invalid SHA' do
- let_it_be(:sha) { ':' }
+ context 'with an invalid SHA' do
+ let_it_be(:sha) { ':' }
- let(:ci_lint) do
- ci_lint_double = instance_double(::Gitlab::Ci::Lint)
- allow(ci_lint_double).to receive(:validate).and_raise(GRPC::InvalidArgument)
+ let(:ci_lint) do
+ ci_lint_double = instance_double(::Gitlab::Ci::Lint)
+ allow(ci_lint_double).to receive(:validate).and_raise(GRPC::InvalidArgument)
- ci_lint_double
+ ci_lint_double
+ end
+
+ it 'logs the invalid SHA to Sentry' do
+ expect(Gitlab::ErrorTracking).to receive(:track_and_raise_exception)
+ .with(GRPC::InvalidArgument, sha: ':')
+
+ response
+ end
end
+ end
- it 'logs the invalid SHA to Sentry' do
- expect(Gitlab::ErrorTracking).to receive(:track_and_raise_exception)
- .with(GRPC::InvalidArgument, sha: ':')
+ context 'when the user cannot create a pipeline' do
+ before do
+ project.add_guest(user)
+ end
- response
+ it 'returns an error stating that the user cannot access the linting' do
+ expect { response }.to raise_error(::Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
end
diff --git a/spec/helpers/integrations_helper_spec.rb b/spec/helpers/integrations_helper_spec.rb
index 3bedc1d8aec..874f9d4870c 100644
--- a/spec/helpers/integrations_helper_spec.rb
+++ b/spec/helpers/integrations_helper_spec.rb
@@ -149,19 +149,4 @@ RSpec.describe IntegrationsHelper do
end
end
end
-
- describe '#jira_issue_breadcrumb_link' do
- let(:issue_reference) { nil }
-
- subject { helper.jira_issue_breadcrumb_link(issue_reference) }
-
- context 'when issue_reference contains HTML' do
- let(:issue_reference) { "<script>alert('XSS')</script>" }
-
- it 'escapes issue reference' do
- is_expected.not_to include(issue_reference)
- is_expected.to include(html_escape(issue_reference))
- end
- end
- end
end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 1cf36fd69cf..acb833ed3f4 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -52,6 +52,7 @@ RSpec.describe ProjectsHelper do
context 'api_url present' do
let(:json) do
{
+ sentry_project_id: error_tracking_setting.sentry_project_id,
name: error_tracking_setting.project_name,
organization_name: error_tracking_setting.organization_name,
organization_slug: error_tracking_setting.organization_slug,
diff --git a/spec/helpers/timeboxes_helper_spec.rb b/spec/helpers/timeboxes_helper_spec.rb
index e31f2df7372..f9fb40a616b 100644
--- a/spec/helpers/timeboxes_helper_spec.rb
+++ b/spec/helpers/timeboxes_helper_spec.rb
@@ -38,4 +38,23 @@ RSpec.describe TimeboxesHelper do
end
end
end
+
+ describe "#recent_releases_with_counts" do
+ let_it_be(:milestone) { create(:milestone) }
+ let_it_be(:project) { milestone.project }
+ let_it_be(:user) { create(:user) }
+
+ subject { helper.recent_releases_with_counts(milestone, user) }
+
+ before do
+ project.add_developer(user)
+ end
+
+ it "returns releases with counts" do
+ _old_releases = create_list(:release, 2, project: project, milestones: [milestone])
+ recent_public_releases = create_list(:release, 3, project: project, milestones: [milestone], released_at: '2022-01-01T18:00:00Z')
+
+ is_expected.to match([match_array(recent_public_releases), 5, 2])
+ end
+ end
end
diff --git a/spec/initializers/net_http_response_patch_spec.rb b/spec/initializers/net_http_response_patch_spec.rb
new file mode 100644
index 00000000000..3bd0d8c3907
--- /dev/null
+++ b/spec/initializers/net_http_response_patch_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Net::HTTPResponse patch header read timeout' do
+ describe '.each_response_header' do
+ let(:server_response) do
+ <<~EOS
+ Content-Type: text/html
+ Header-Two: foo
+
+ Hello World
+ EOS
+ end
+
+ before do
+ stub_const('Gitlab::BufferedIo::HEADER_READ_TIMEOUT', 0.1)
+ end
+
+ subject(:each_response_header) { Net::HTTPResponse.each_response_header(socket) { |k, v| } }
+
+ context 'with Net::BufferedIO' do
+ let(:socket) { Net::BufferedIO.new(StringIO.new(server_response)) }
+
+ it 'does not forward start time to the socket' do
+ allow(socket).to receive(:readuntil).and_call_original
+ expect(socket).to receive(:readuntil).with("\n", true)
+
+ each_response_header
+ end
+
+ context 'when the response contains many consecutive spaces' do
+ before do
+ expect(socket).to receive(:readuntil).and_return(
+ "a: #{' ' * 100_000} b",
+ ''
+ )
+ end
+
+ it 'has no regex backtracking issues' do
+ Timeout.timeout(1) do
+ each_response_header
+ end
+ end
+ end
+ end
+
+ context 'with Gitlab::BufferedIo' do
+ let(:mock_io) { StringIO.new(server_response) }
+ let(:socket) { Gitlab::BufferedIo.new(mock_io) }
+
+ it 'forwards start time to the socket' do
+ allow(socket).to receive(:readuntil).and_call_original
+ expect(socket).to receive(:readuntil).with("\n", true, kind_of(Numeric))
+
+ each_response_header
+ end
+
+ context 'when the response contains an infinite number of headers' do
+ before do
+ read_counter = 0
+
+ allow(mock_io).to receive(:read_nonblock) do
+ read_counter += 1
+ raise 'Test did not raise HeaderReadTimeout' if read_counter > 10
+
+ sleep 0.01
+ +"Yet-Another-Header: foo\n"
+ end
+ end
+
+ it 'raises a timeout error' do
+ expect { each_response_header }.to raise_error(Gitlab::HTTP::HeaderReadTimeout,
+ /Request timed out after reading headers for 0\.[0-9]+ seconds/)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/bulk_imports/projects/pipelines/project_pipeline_spec.rb b/spec/lib/bulk_imports/projects/pipelines/project_pipeline_spec.rb
index c53c0849931..567a0a4fcc3 100644
--- a/spec/lib/bulk_imports/projects/pipelines/project_pipeline_spec.rb
+++ b/spec/lib/bulk_imports/projects/pipelines/project_pipeline_spec.rb
@@ -25,18 +25,7 @@ RSpec.describe BulkImports::Projects::Pipelines::ProjectPipeline do
let(:project_data) do
{
'visibility' => 'private',
- 'created_at' => 10.days.ago,
- 'archived' => false,
- 'shared_runners_enabled' => true,
- 'container_registry_enabled' => true,
- 'only_allow_merge_if_pipeline_succeeds' => true,
- 'only_allow_merge_if_all_discussions_are_resolved' => true,
- 'request_access_enabled' => true,
- 'printing_merge_request_link_enabled' => true,
- 'remove_source_branch_after_merge' => true,
- 'autoclose_referenced_issues' => true,
- 'suggestion_commit_message' => 'message',
- 'wiki_enabled' => true
+ 'created_at' => '2016-08-12T09:41:03'
}
end
@@ -58,17 +47,8 @@ RSpec.describe BulkImports::Projects::Pipelines::ProjectPipeline do
expect(imported_project).not_to be_nil
expect(imported_project.group).to eq(group)
- expect(imported_project.suggestion_commit_message).to eq('message')
- expect(imported_project.archived?).to eq(project_data['archived'])
- expect(imported_project.shared_runners_enabled?).to eq(project_data['shared_runners_enabled'])
- expect(imported_project.container_registry_enabled?).to eq(project_data['container_registry_enabled'])
- expect(imported_project.only_allow_merge_if_pipeline_succeeds?).to eq(project_data['only_allow_merge_if_pipeline_succeeds'])
- expect(imported_project.only_allow_merge_if_all_discussions_are_resolved?).to eq(project_data['only_allow_merge_if_all_discussions_are_resolved'])
- expect(imported_project.request_access_enabled?).to eq(project_data['request_access_enabled'])
- expect(imported_project.printing_merge_request_link_enabled?).to eq(project_data['printing_merge_request_link_enabled'])
- expect(imported_project.remove_source_branch_after_merge?).to eq(project_data['remove_source_branch_after_merge'])
- expect(imported_project.autoclose_referenced_issues?).to eq(project_data['autoclose_referenced_issues'])
- expect(imported_project.wiki_enabled?).to eq(project_data['wiki_enabled'])
+ expect(imported_project.visibility).to eq(project_data['visibility'])
+ expect(imported_project.created_at).to eq(project_data['created_at'])
end
end
diff --git a/spec/lib/bulk_imports/projects/transformers/project_attributes_transformer_spec.rb b/spec/lib/bulk_imports/projects/transformers/project_attributes_transformer_spec.rb
index 822bb9a5605..a1d77b9732d 100644
--- a/spec/lib/bulk_imports/projects/transformers/project_attributes_transformer_spec.rb
+++ b/spec/lib/bulk_imports/projects/transformers/project_attributes_transformer_spec.rb
@@ -25,8 +25,8 @@ RSpec.describe BulkImports::Projects::Transformers::ProjectAttributesTransformer
let(:data) do
{
- 'name' => 'source_name',
- 'visibility' => 'private'
+ 'visibility' => 'private',
+ 'created_at' => '2016-11-18T09:29:42.634Z'
}
end
@@ -76,8 +76,21 @@ RSpec.describe BulkImports::Projects::Transformers::ProjectAttributesTransformer
end
end
- it 'converts all keys to symbols' do
- expect(transformed_data.keys).to contain_exactly(:name, :path, :import_type, :visibility_level, :namespace_id)
+ context 'when data has extra keys' do
+ it 'returns a fixed number of keys' do
+ data = {
+ 'visibility' => 'private',
+ 'created_at' => '2016-11-18T09:29:42.634Z',
+ 'my_key' => 'my_key',
+ 'another_key' => 'another_key',
+ 'last_key' => 'last_key'
+ }
+
+ transformed_data = described_class.new.transform(context, data)
+
+ expect(transformed_data.keys)
+ .to contain_exactly(:created_at, :import_type, :name, :namespace_id, :path, :visibility_level)
+ end
end
end
end
diff --git a/spec/lib/gitlab/buffered_io_spec.rb b/spec/lib/gitlab/buffered_io_spec.rb
index f8896abd46e..c6939b819e2 100644
--- a/spec/lib/gitlab/buffered_io_spec.rb
+++ b/spec/lib/gitlab/buffered_io_spec.rb
@@ -1,54 +1,50 @@
-# rubocop:disable Style/FrozenStringLiteralComment
+# frozen_string_literal: true
+
require 'spec_helper'
RSpec.describe Gitlab::BufferedIo do
describe '#readuntil' do
- let(:never_ending_tcp_socket) do
- Class.new do
- def initialize(*_)
- @read_counter = 0
- end
+ let(:mock_io) { StringIO.new('a') }
+ let(:start_time) { Process.clock_gettime(Process::CLOCK_MONOTONIC) }
- def setsockopt(*_); end
+ before do
+ stub_const('Gitlab::BufferedIo::HEADER_READ_TIMEOUT', 0.1)
+ end
- def closed?
- false
- end
+ subject(:readuntil) do
+ Gitlab::BufferedIo.new(mock_io).readuntil('a', false, start_time)
+ end
- def close
- true
- end
+ it 'does not raise a timeout error' do
+ expect { readuntil }.not_to raise_error
+ end
- def to_io
- StringIO.new('Hello World!')
- end
+ context 'when the response contains infinitely long headers' do
+ before do
+ read_counter = 0
- def write_nonblock(data, *_)
- data.size
- end
+ allow(mock_io).to receive(:read_nonblock) do |buffer_size, *_|
+ read_counter += 1
+ raise 'Test did not raise HeaderReadTimeout' if read_counter > 10
- def read_nonblock(buffer_size, *_)
sleep 0.01
- @read_counter += 1
-
- raise 'Test did not raise HeaderReadTimeout' if @read_counter > 10
-
'H' * buffer_size
end
end
- end
- before do
- stub_const('Gitlab::BufferedIo::HEADER_READ_TIMEOUT', 0.1)
- end
+ it 'raises a timeout error' do
+ expect { readuntil }.to raise_error(Gitlab::HTTP::HeaderReadTimeout, /Request timed out after reading headers for 0\.[0-9]+ seconds/)
+ end
- subject(:readuntil) do
- Gitlab::BufferedIo.new(never_ending_tcp_socket.new).readuntil('a')
- end
+ context 'when not passing start_time' do
+ subject(:readuntil) do
+ Gitlab::BufferedIo.new(mock_io).readuntil('a', false)
+ end
- it 'raises a timeout error' do
- expect { readuntil }.to raise_error(Gitlab::HTTP::HeaderReadTimeout, /Request timed out after reading headers for 0\.[0-9]+ seconds/)
+ it 'raises a timeout error' do
+ expect { readuntil }.to raise_error(Gitlab::HTTP::HeaderReadTimeout, /Request timed out after reading headers for 0\.[0-9]+ seconds/)
+ end
+ end
end
end
end
-# rubocop:enable Style/FrozenStringLiteralComment
diff --git a/spec/lib/gitlab/import_export/decompressed_archive_size_validator_spec.rb b/spec/lib/gitlab/import_export/decompressed_archive_size_validator_spec.rb
index fe3b638d20f..dea584e5019 100644
--- a/spec/lib/gitlab/import_export/decompressed_archive_size_validator_spec.rb
+++ b/spec/lib/gitlab/import_export/decompressed_archive_size_validator_spec.rb
@@ -86,6 +86,65 @@ RSpec.describe Gitlab::ImportExport::DecompressedArchiveSizeValidator do
include_examples 'logs raised exception and terminates validator process group'
end
end
+
+ context 'archive path validation' do
+ let(:filesize) { nil }
+
+ before do
+ expect(Gitlab::Import::Logger)
+ .to receive(:info)
+ .with(
+ import_upload_archive_path: filepath,
+ import_upload_archive_size: filesize,
+ message: error_message
+ )
+ end
+
+ context 'when archive path is traversed' do
+ let(:filepath) { '/foo/../bar' }
+ let(:error_message) { 'Invalid path' }
+
+ it 'returns false' do
+ expect(subject.valid?).to eq(false)
+ end
+ end
+
+ context 'when archive path is not a string' do
+ let(:filepath) { 123 }
+ let(:error_message) { 'Archive path is not a string' }
+
+ it 'returns false' do
+ expect(subject.valid?).to eq(false)
+ end
+ end
+
+ context 'which archive path is a symlink' do
+ let(:filepath) { File.join(Dir.tmpdir, 'symlink') }
+ let(:error_message) { 'Archive path is a symlink' }
+
+ before do
+ FileUtils.ln_s(filepath, filepath, force: true)
+ end
+
+ it 'returns false' do
+ expect(subject.valid?).to eq(false)
+ end
+ end
+
+ context 'when archive path is not a file' do
+ let(:filepath) { Dir.mktmpdir }
+ let(:filesize) { File.size(filepath) }
+ let(:error_message) { 'Archive path is not a file' }
+
+ after do
+ FileUtils.rm_rf(filepath)
+ end
+
+ it 'returns false' do
+ expect(subject.valid?).to eq(false)
+ end
+ end
+ end
end
def create_compressed_file
diff --git a/spec/models/error_tracking/project_error_tracking_setting_spec.rb b/spec/models/error_tracking/project_error_tracking_setting_spec.rb
index 2939a40a84f..15b6b45eaba 100644
--- a/spec/models/error_tracking/project_error_tracking_setting_spec.rb
+++ b/spec/models/error_tracking/project_error_tracking_setting_spec.rb
@@ -10,7 +10,9 @@ RSpec.describe ErrorTracking::ProjectErrorTrackingSetting do
let(:sentry_client) { instance_double(ErrorTracking::SentryClient) }
- subject(:setting) { build(:project_error_tracking_setting, project: project) }
+ let(:sentry_project_id) { 10 }
+
+ subject(:setting) { build(:project_error_tracking_setting, project: project, sentry_project_id: sentry_project_id) }
describe 'Associations' do
it { is_expected.to belong_to(:project) }
@@ -270,7 +272,7 @@ RSpec.describe ErrorTracking::ProjectErrorTrackingSetting do
end
describe '#issue_details' do
- let(:issue) { build(:error_tracking_sentry_detailed_error) }
+ let(:issue) { build(:error_tracking_sentry_detailed_error, project_id: sentry_project_id) }
let(:commit_id) { issue.first_release_version }
let(:result) { subject.issue_details(opts) }
let(:opts) { { issue_id: 1 } }
@@ -317,12 +319,33 @@ RSpec.describe ErrorTracking::ProjectErrorTrackingSetting do
end
end
+ describe '#issue_latest_event' do
+ let(:error_event) { build(:error_tracking_sentry_error_event, project_id: sentry_project_id) }
+ let(:result) { subject.issue_latest_event(opts) }
+ let(:opts) { { issue_id: 1 } }
+
+ before do
+ stub_reactive_cache(subject, error_event, {})
+ synchronous_reactive_cache(subject)
+
+ allow(subject).to receive(:sentry_client).and_return(sentry_client)
+ allow(sentry_client).to receive(:issue_latest_event).with(opts).and_return(error_event)
+ end
+
+ it 'returns the error event' do
+ expect(result[:latest_event].project_id).to eq(sentry_project_id)
+ end
+ end
+
describe '#update_issue' do
let(:result) { subject.update_issue(**opts) }
let(:opts) { { issue_id: 1, params: {} } }
before do
allow(subject).to receive(:sentry_client).and_return(sentry_client)
+ allow(sentry_client).to receive(:issue_details)
+ .with({ issue_id: 1 })
+ .and_return(Gitlab::ErrorTracking::DetailedError.new(project_id: sentry_project_id))
end
context 'when sentry response is successful' do
@@ -344,6 +367,56 @@ RSpec.describe ErrorTracking::ProjectErrorTrackingSetting do
expect(result).to eq(error: 'Unexpected Error')
end
end
+
+ context 'when sentry_project_id is not set' do
+ let(:sentry_projects) do
+ [
+ Gitlab::ErrorTracking::Project.new(
+ id: 1111,
+ name: 'Some Project',
+ organization_name: 'Org'
+ ),
+ Gitlab::ErrorTracking::Project.new(
+ id: sentry_project_id,
+ name: setting.project_name,
+ organization_name: setting.organization_name
+ )
+ ]
+ end
+
+ context 'when sentry_project_id is not set' do
+ before do
+ setting.update!(sentry_project_id: nil)
+
+ allow(sentry_client).to receive(:projects).and_return(sentry_projects)
+ allow(sentry_client).to receive(:update_issue).with(opts).and_return(true)
+ end
+
+ it 'tries to backfill it from sentry API' do
+ expect(result).to eq(updated: true)
+
+ expect(setting.reload.sentry_project_id).to eq(sentry_project_id)
+ end
+
+ context 'when the project cannot be found on sentry' do
+ before do
+ sentry_projects.pop
+ end
+
+ it 'raises error' do
+ expect { result }.to raise_error(/Couldn't find project/)
+ end
+ end
+ end
+
+ context 'when mismatching sentry_project_id is detected' do
+ it 'raises error' do
+ setting.update!(sentry_project_id: sentry_project_id + 1)
+
+ expect { result }.to raise_error(/The Sentry issue appers to be outside/)
+ end
+ end
+ end
end
describe 'slugs' do
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index bc425b15c6e..6fe4b3a815f 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -4045,6 +4045,41 @@ RSpec.describe User do
end
end
+ describe '#authorized_project_mirrors' do
+ it 'returns project mirrors where the user has access equal to or above the given level' do
+ guest_project = create(:project)
+ reporter_project = create(:project)
+ maintainer_project = create(:project)
+
+ guest_group = create(:group)
+ reporter_group = create(:group)
+ maintainer_group = create(:group)
+
+ _guest_group_project = create(:project, group: guest_group)
+ reporter_group_project = create(:project, group: reporter_group)
+ maintainer_group_project = create(:project, group: maintainer_group)
+
+ user = create(:user)
+
+ guest_project.add_guest(user)
+ reporter_project.add_reporter(user)
+ maintainer_project.add_maintainer(user)
+
+ guest_group.add_guest(user)
+ reporter_group.add_reporter(user)
+ maintainer_group.add_maintainer(user)
+
+ project_mirrors = user.authorized_project_mirrors(Gitlab::Access::REPORTER)
+
+ expect(project_mirrors.pluck(:project_id)).to contain_exactly(
+ reporter_group_project.id,
+ maintainer_group_project.id,
+ reporter_project.id,
+ maintainer_project.id
+ )
+ end
+ end
+
shared_context '#ci_owned_runners' do
let(:user) { create(:user) }
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index b77ccb83509..5a79315b6ef 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -1023,25 +1023,117 @@ RSpec.describe ProjectPolicy do
subject { described_class.new(deploy_token, project) }
- context 'a deploy token with read_package_registry scope' do
- let(:deploy_token) { create(:deploy_token, read_package_registry: true) }
+ context 'private project' do
+ let(:project) { private_project }
- it { is_expected.to be_allowed(:read_package) }
- it { is_expected.to be_allowed(:read_project) }
- it { is_expected.to be_disallowed(:create_package) }
+ context 'a deploy token with read_registry scope' do
+ let(:deploy_token) { create(:deploy_token, read_registry: true, write_registry: false) }
- it_behaves_like 'package access with repository disabled'
+ it { is_expected.to be_allowed(:read_container_image) }
+ it { is_expected.to be_disallowed(:create_container_image) }
+
+ context 'with registry disabled' do
+ include_context 'registry disabled via project features'
+
+ it { is_expected.to be_disallowed(:read_container_image) }
+ it { is_expected.to be_disallowed(:create_container_image) }
+ end
+ end
+
+ context 'a deploy token with write_registry scope' do
+ let(:deploy_token) { create(:deploy_token, read_registry: false, write_registry: true) }
+
+ it { is_expected.to be_disallowed(:read_container_image) }
+ it { is_expected.to be_allowed(:create_container_image) }
+
+ context 'with registry disabled' do
+ include_context 'registry disabled via project features'
+
+ it { is_expected.to be_disallowed(:read_container_image) }
+ it { is_expected.to be_disallowed(:create_container_image) }
+ end
+ end
+
+ context 'a deploy token with no registry scope' do
+ let(:deploy_token) { create(:deploy_token, read_registry: false, write_registry: false) }
+
+ it { is_expected.to be_disallowed(:read_container_image) }
+ it { is_expected.to be_disallowed(:create_container_image) }
+ end
+
+ context 'a deploy token with read_package_registry scope' do
+ let(:deploy_token) { create(:deploy_token, read_repository: false, read_registry: false, read_package_registry: true) }
+
+ it { is_expected.to be_allowed(:read_project) }
+ it { is_expected.to be_allowed(:read_package) }
+ it { is_expected.to be_disallowed(:create_package) }
+
+ it_behaves_like 'package access with repository disabled'
+ end
+
+ context 'a deploy token with write_package_registry scope' do
+ let(:deploy_token) { create(:deploy_token, read_repository: false, read_registry: false, write_package_registry: true) }
+
+ it { is_expected.to be_allowed(:create_package) }
+ it { is_expected.to be_allowed(:read_package) }
+ it { is_expected.to be_allowed(:read_project) }
+ it { is_expected.to be_disallowed(:destroy_package) }
+
+ it_behaves_like 'package access with repository disabled'
+ end
end
- context 'a deploy token with write_package_registry scope' do
- let(:deploy_token) { create(:deploy_token, write_package_registry: true) }
+ context 'public project' do
+ let(:project) { public_project }
- it { is_expected.to be_allowed(:create_package) }
- it { is_expected.to be_allowed(:read_package) }
- it { is_expected.to be_allowed(:read_project) }
- it { is_expected.to be_disallowed(:destroy_package) }
+ context 'a deploy token with read_registry scope' do
+ let(:deploy_token) { create(:deploy_token, read_registry: true, write_registry: false) }
- it_behaves_like 'package access with repository disabled'
+ it { is_expected.to be_allowed(:read_container_image) }
+ it { is_expected.to be_disallowed(:create_container_image) }
+
+ context 'with registry disabled' do
+ include_context 'registry disabled via project features'
+
+ it { is_expected.to be_disallowed(:read_container_image) }
+ it { is_expected.to be_disallowed(:create_container_image) }
+ end
+
+ context 'with registry private' do
+ include_context 'registry set to private via project features'
+
+ it { is_expected.to be_allowed(:read_container_image) }
+ it { is_expected.to be_disallowed(:create_container_image) }
+ end
+ end
+
+ context 'a deploy token with write_registry scope' do
+ let(:deploy_token) { create(:deploy_token, read_registry: false, write_registry: true) }
+
+ it { is_expected.to be_allowed(:read_container_image) }
+ it { is_expected.to be_allowed(:create_container_image) }
+
+ context 'with registry disabled' do
+ include_context 'registry disabled via project features'
+
+ it { is_expected.to be_disallowed(:read_container_image) }
+ it { is_expected.to be_disallowed(:create_container_image) }
+ end
+
+ context 'with registry private' do
+ include_context 'registry set to private via project features'
+
+ it { is_expected.to be_allowed(:read_container_image) }
+ it { is_expected.to be_allowed(:create_container_image) }
+ end
+ end
+
+ context 'a deploy token with no registry scope' do
+ let(:deploy_token) { create(:deploy_token, read_registry: false, write_registry: false) }
+
+ it { is_expected.to be_disallowed(:read_container_image) }
+ it { is_expected.to be_disallowed(:create_container_image) }
+ end
end
end
@@ -2102,4 +2194,25 @@ RSpec.describe ProjectPolicy do
it { is_expected.to be_disallowed(:register_project_runners) }
end
end
+
+ describe 'update_sentry_issue' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:role, :allowed) do
+ :owner | true
+ :maintainer | true
+ :developer | true
+ :reporter | false
+ :guest | false
+ end
+
+ let(:project) { public_project }
+ let(:current_user) { public_send(role) }
+
+ with_them do
+ it do
+ expect(subject.can?(:update_sentry_issue)).to be(allowed)
+ end
+ end
+ end
end
diff --git a/spec/requests/api/ci/runners_spec.rb b/spec/requests/api/ci/runners_spec.rb
index d6ebc197ab0..587a6921087 100644
--- a/spec/requests/api/ci/runners_spec.rb
+++ b/spec/requests/api/ci/runners_spec.rb
@@ -804,6 +804,23 @@ RSpec.describe API::Ci::Runners do
expect(json_response).to be_an(Array)
expect(json_response.length).to eq(2)
end
+
+ context 'when user does not have authorization to see all jobs' do
+ it 'shows only jobs it has permission to see' do
+ create(:ci_build, :running, runner: two_projects_runner, project: project)
+ create(:ci_build, :running, runner: two_projects_runner, project: project2)
+
+ project.add_guest(user2)
+ project2.add_maintainer(user2)
+ get api("/runners/#{two_projects_runner.id}/jobs", user2)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+
+ expect(json_response).to be_an(Array)
+ expect(json_response.length).to eq(1)
+ end
+ end
end
context 'when valid status is provided' do
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index 48f2c45bd98..c1217292d5c 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -483,6 +483,29 @@ RSpec.describe API::Labels do
let(:request) { api("/projects/#{project.id}/labels", user) }
let(:params) { { name: valid_label_title_1 } }
end
+
+ context 'with group label' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:group_label) { create(:group_label, title: valid_group_label_title_1, group: group) }
+
+ before do
+ project.update!(group: group)
+ end
+
+ it 'returns 401 if user does not have access' do
+ delete api("/projects/#{project.id}/labels/#{group_label.id}", user)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ it 'returns 204 if user has access' do
+ group.add_developer(user)
+
+ delete api("/projects/#{project.id}/labels/#{group_label.id}", user)
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ end
+ end
end
describe 'PUT /projects/:id/labels' do
@@ -537,6 +560,44 @@ RSpec.describe API::Labels do
expect(response).to have_gitlab_http_status(:bad_request)
end
+
+ context 'with group label' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:group_label) { create(:group_label, title: valid_group_label_title_1, group: group) }
+
+ before do
+ project.update!(group: group)
+ end
+
+ it 'allows updating of group label priority' do
+ put api("/projects/#{project.id}/labels/#{group_label.id}", user), params: { priority: 5 }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['priority']).to eq(5)
+ end
+
+ it 'returns 401 when updating other fields' do
+ put api("/projects/#{project.id}/labels/#{group_label.id}", user), params: {
+ priority: 5,
+ new_name: 'new label name'
+ }
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ it 'returns 200 when user has access to the group label' do
+ group.add_developer(user)
+
+ put api("/projects/#{project.id}/labels/#{group_label.id}", user), params: {
+ priority: 5,
+ new_name: 'new label name'
+ }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['priority']).to eq(5)
+ expect(json_response['name']).to eq('new label name')
+ end
+ end
end
describe 'PUT /projects/:id/labels/promote' do
diff --git a/spec/requests/jira_routing_spec.rb b/spec/requests/jira_routing_spec.rb
index a627eea33a8..e0e170044de 100644
--- a/spec/requests/jira_routing_spec.rb
+++ b/spec/requests/jira_routing_spec.rb
@@ -25,27 +25,49 @@ RSpec.describe 'Jira referenced paths', type: :request do
expect(response).to redirect_to(redirect_path)
end
- context 'with encoded subgroup path' do
- where(:jira_path, :redirect_path) do
- '/group/group@sub_group@sub_group_project' | '/group/sub_group/sub_group_project'
- '/group@sub_group/group@sub_group@sub_group_project' | '/group/sub_group/sub_group_project'
- '/group/group@sub_group@sub_group_project/commit/1234567' | '/group/sub_group/sub_group_project/commit/1234567'
- '/group/group@sub_group@sub_group_project/tree/1234567' | '/group/sub_group/sub_group_project/-/tree/1234567'
+ shared_examples 'redirects to jira path' do
+ it 'redirects to canonical path with legacy prefix' do
+ redirects_to_canonical_path "/-/jira#{jira_path}", redirect_path
end
- with_them do
- context 'with legacy prefix' do
- it 'redirects to canonical path' do
- redirects_to_canonical_path "/-/jira#{jira_path}", redirect_path
- end
- end
-
- it 'redirects to canonical path' do
- redirects_to_canonical_path jira_path, redirect_path
- end
+ it 'redirects to canonical path' do
+ redirects_to_canonical_path jira_path, redirect_path
end
end
+ let(:jira_path) { '/group/group@sub_group@sub_group_project' }
+ let(:redirect_path) { '/group/sub_group/sub_group_project' }
+
+ it_behaves_like 'redirects to jira path'
+
+ context 'contains @ before the first /' do
+ let(:jira_path) { '/group@sub_group/group@sub_group@sub_group_project' }
+ let(:redirect_path) { '/group/sub_group/sub_group_project' }
+
+ it_behaves_like 'redirects to jira path'
+ end
+
+ context 'including commit path' do
+ let(:jira_path) { '/group/group@sub_group@sub_group_project/commit/1234567' }
+ let(:redirect_path) { '/group/sub_group/sub_group_project/commit/1234567' }
+
+ it_behaves_like 'redirects to jira path'
+ end
+
+ context 'including tree path' do
+ let(:jira_path) { '/group/group@sub_group@sub_group_project/tree/1234567' }
+ let(:redirect_path) { '/group/sub_group/sub_group_project/-/tree/1234567' }
+
+ it_behaves_like 'redirects to jira path'
+ end
+
+ context 'malicious path' do
+ let(:jira_path) { '/group/@@malicious.server' }
+ let(:redirect_path) { '/malicious.server' }
+
+ it_behaves_like 'redirects to jira path'
+ end
+
context 'regular paths with legacy prefix' do
where(:jira_path, :redirect_path) do
'/-/jira/group/group_project' | '/group/group_project'
diff --git a/spec/serializers/member_user_entity_spec.rb b/spec/serializers/member_user_entity_spec.rb
index 0e6d4bcc3fb..85f29845d65 100644
--- a/spec/serializers/member_user_entity_spec.rb
+++ b/spec/serializers/member_user_entity_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe MemberUserEntity do
let(:entity_hash) { entity.as_json }
it 'matches json schema' do
- expect(entity.to_json).to match_schema('entities/member_user')
+ expect(entity.to_json).to match_schema('entities/member_user_default')
end
it 'correctly exposes `avatar_url`' do
@@ -27,10 +27,8 @@ RSpec.describe MemberUserEntity do
expect(entity_hash[:blocked]).to be(true)
end
- it 'correctly exposes `two_factor_enabled`' do
- allow(user).to receive(:two_factor_enabled?).and_return(true)
-
- expect(entity_hash[:two_factor_enabled]).to be(true)
+ it 'does not expose `two_factor_enabled` by default' do
+ expect(entity_hash[:two_factor_enabled]).to be(nil)
end
it 'correctly exposes `status.emoji`' do
@@ -44,4 +42,66 @@ RSpec.describe MemberUserEntity do
it 'correctly exposes `last_activity_on`' do
expect(entity_hash[:last_activity_on]).to be(user.last_activity_on)
end
+
+ context 'when options includes a source' do
+ let(:current_user) { create(:user) }
+ let(:options) { { current_user: current_user, source: source } }
+ let(:entity) { described_class.new(user, options) }
+
+ shared_examples 'correctly exposes user two_factor_enabled' do
+ context 'when the current_user has a role lower than minimum manage member role' do
+ before do
+ source.add_user(current_user, Gitlab::Access::DEVELOPER)
+ end
+
+ it 'does not expose user two_factor_enabled' do
+ expect(entity_hash[:two_factor_enabled]).to be(nil)
+ end
+
+ it 'matches json schema' do
+ expect(entity.to_json).to match_schema('entities/member_user_default')
+ end
+ end
+
+ context 'when the current user has a minimum manage member role or higher' do
+ before do
+ source.add_user(current_user, minimum_manage_member_role)
+ end
+
+ it 'matches json schema' do
+ expect(entity.to_json).to match_schema('entities/member_user_for_admin_member')
+ end
+
+ it 'exposes user two_factor_enabled' do
+ expect(entity_hash[:two_factor_enabled]).to be(false)
+ end
+ end
+
+ context 'when the current user is self' do
+ let(:current_user) { user }
+
+ it 'exposes user two_factor_enabled' do
+ expect(entity_hash[:two_factor_enabled]).to be(false)
+ end
+
+ it 'matches json schema' do
+ expect(entity.to_json).to match_schema('entities/member_user_for_admin_member')
+ end
+ end
+ end
+
+ context 'when the source is a group' do
+ let(:source) { create(:group) }
+ let(:minimum_manage_member_role) { Gitlab::Access::OWNER }
+
+ it_behaves_like 'correctly exposes user two_factor_enabled'
+ end
+
+ context 'when the source is a project' do
+ let(:source) { create(:project) }
+ let(:minimum_manage_member_role) { Gitlab::Access::MAINTAINER }
+
+ it_behaves_like 'correctly exposes user two_factor_enabled'
+ end
+ end
end
diff --git a/spec/services/bulk_imports/file_decompression_service_spec.rb b/spec/services/bulk_imports/file_decompression_service_spec.rb
index 1d6aa79a37f..77348428d60 100644
--- a/spec/services/bulk_imports/file_decompression_service_spec.rb
+++ b/spec/services/bulk_imports/file_decompression_service_spec.rb
@@ -80,7 +80,8 @@ RSpec.describe BulkImports::FileDecompressionService do
subject { described_class.new(tmpdir: tmpdir, filename: 'symlink.gz') }
it 'raises an error and removes the file' do
- expect { subject.execute }.to raise_error(described_class::ServiceError, 'Invalid file')
+ expect { subject.execute }
+ .to raise_error(BulkImports::FileDecompressionService::ServiceError, 'File decompression error')
expect(File.exist?(symlink)).to eq(false)
end
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index 6b7b72d83fc..724970e27c0 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -294,7 +294,7 @@ RSpec.describe Issues::CreateService do
context 'user is reporter or above' do
before do
- project.add_reporter(user)
+ project.add_developer(user)
end
it 'assigns the sentry error' do
diff --git a/spec/support/shared_contexts/project_features_shared_context.rb b/spec/support/shared_contexts/project_features_shared_context.rb
new file mode 100644
index 00000000000..40d9cb29c14
--- /dev/null
+++ b/spec/support/shared_contexts/project_features_shared_context.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'repository disabled via project features' do
+ before do
+ project.project_feature.update_columns(
+ # Disable merge_requests and builds as well, since merge_requests and
+ # builds cannot have higher visibility than repository.
+ merge_requests_access_level: ProjectFeature::DISABLED,
+ builds_access_level: ProjectFeature::DISABLED,
+ repository_access_level: ProjectFeature::DISABLED)
+ end
+end
+
+RSpec.shared_context 'registry disabled via project features' do
+ before do
+ project.project_feature.update_columns(
+ container_registry_access_level: ProjectFeature::DISABLED
+ )
+ end
+end
+
+RSpec.shared_context 'registry set to private via project features' do
+ before do
+ project.project_feature.update_columns(
+ container_registry_access_level: ProjectFeature::PRIVATE
+ )
+ end
+end
diff --git a/spec/support/shared_contexts/sentry_error_tracking_shared_context.rb b/spec/support/shared_contexts/sentry_error_tracking_shared_context.rb
index 3453f954c9d..e473ac21499 100644
--- a/spec/support/shared_contexts/sentry_error_tracking_shared_context.rb
+++ b/spec/support/shared_contexts/sentry_error_tracking_shared_context.rb
@@ -16,6 +16,6 @@ RSpec.shared_context 'sentry error tracking context' do
before do
expect(project).to receive(:error_tracking_setting).at_least(:once).and_return(error_tracking_setting)
- project.add_reporter(user)
+ project.add_developer(user)
end
end
diff --git a/spec/support/shared_examples/policies/project_policy_shared_examples.rb b/spec/support/shared_examples/policies/project_policy_shared_examples.rb
index 63e4d458ad4..c4083df47e2 100644
--- a/spec/support/shared_examples/policies/project_policy_shared_examples.rb
+++ b/spec/support/shared_examples/policies/project_policy_shared_examples.rb
@@ -345,16 +345,7 @@ RSpec.shared_examples 'project policies as admin without admin mode' do
end
RSpec.shared_examples 'package access with repository disabled' do
- context 'when repository is disabled' do
- before do
- project.project_feature.update!(
- # Disable merge_requests and builds as well, since merge_requests and
- # builds cannot have higher visibility than repository.
- merge_requests_access_level: ProjectFeature::DISABLED,
- builds_access_level: ProjectFeature::DISABLED,
- repository_access_level: ProjectFeature::DISABLED)
- end
+ include_context 'repository disabled via project features'
- it { is_expected.to be_allowed(:read_package) }
- end
+ it { is_expected.to be_allowed(:read_package) }
end
diff --git a/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb
index 135fa4cf5a4..e6b0772aec1 100644
--- a/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb
@@ -19,33 +19,66 @@ RSpec.shared_examples 'conan ping endpoint' do
end
RSpec.shared_examples 'conan search endpoint' do
- before do
- project.update_column(:visibility_level, Gitlab::VisibilityLevel::PUBLIC)
-
- # Do not pass the HTTP_AUTHORIZATION header,
- # in order to test that this public project's packages
- # are visible to anonymous search.
- get api(url), params: params
- end
+ using RSpec::Parameterized::TableSyntax
subject { json_response['results'] }
- context 'returns packages with a matching name' do
- let(:params) { { q: package.conan_recipe } }
+ context 'with a public project' do
+ before do
+ project.update!(visibility: 'public')
+
+ # Do not pass the HTTP_AUTHORIZATION header,
+ # in order to test that this public project's packages
+ # are visible to anonymous search.
+ get api(url), params: params
+ end
+
+ context 'returns packages with a matching name' do
+ let(:params) { { q: package.conan_recipe } }
+
+ it { is_expected.to contain_exactly(package.conan_recipe) }
+ end
+
+ context 'returns packages using a * wildcard' do
+ let(:params) { { q: "#{package.name[0, 3]}*" } }
- it { is_expected.to contain_exactly(package.conan_recipe) }
+ it { is_expected.to contain_exactly(package.conan_recipe) }
+ end
+
+ context 'does not return non-matching packages' do
+ let(:params) { { q: "foo" } }
+
+ it { is_expected.to be_blank }
+ end
end
- context 'returns packages using a * wildcard' do
+ context 'with a private project' do
let(:params) { { q: "#{package.name[0, 3]}*" } }
- it { is_expected.to contain_exactly(package.conan_recipe) }
- end
+ where(:role, :packages_visible) do
+ :maintainer | true
+ :developer | true
+ :reporter | true
+ :guest | false
+ :anonymous | false
+ end
- context 'does not return non-matching packages' do
- let(:params) { { q: "foo" } }
+ with_them do
+ before do
+ project.update!(visibility: 'private')
+ project.team.truncate
+ user.project_authorizations.delete_all
+ project.add_user(user, role) unless role == :anonymous
+
+ get api(url), params: params, headers: headers
+ end
- it { is_expected.to be_blank }
+ if params[:packages_visible]
+ it { is_expected.to contain_exactly(package.conan_recipe) }
+ else
+ it { is_expected.to be_blank }
+ end
+ end
end
end
diff --git a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
index 7677e5d8cb2..f18869fb380 100644
--- a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
@@ -142,9 +142,9 @@ RSpec.shared_examples 'logs an auth warning' do |requested_actions|
requested_project_path: project.full_path,
requested_actions: requested_actions,
authorized_actions: [],
- user_id: current_user.id,
- username: current_user.username
- }
+ user_id: current_user&.id,
+ username: current_user&.username
+ }.compact
end
it do
diff --git a/tooling/overcommit/Gemfile.lock b/tooling/overcommit/Gemfile.lock
index 13c611439b6..a4b6f62a979 100644
--- a/tooling/overcommit/Gemfile.lock
+++ b/tooling/overcommit/Gemfile.lock
@@ -34,7 +34,7 @@ GEM
parallel (1.19.2)
parser (2.7.2.0)
ast (~> 2.4.1)
- rack (2.2.3)
+ rack (2.2.3.1)
rainbow (3.0.0)
regexp_parser (1.8.2)
rexml (3.2.4)