summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/graphql/resolvers/users/participants_resolver.rb13
-rw-r--r--app/graphql/types/issue_type.rb3
-rw-r--r--app/graphql/types/merge_request_type.rb3
-rw-r--r--app/models/application_setting.rb2
-rw-r--r--app/models/application_setting_implementation.rb8
-rw-r--r--app/models/ci/build.rb2
-rw-r--r--app/models/concerns/participable.rb18
-rw-r--r--app/services/ci/parse_dotenv_artifact_service.rb2
-rw-r--r--app/views/projects/buttons/_dropdown.html.haml42
-rw-r--r--app/views/projects/pipeline_schedules/edit.html.haml2
-rw-r--r--app/views/projects/tags/_tag.html.haml10
-rw-r--r--config/feature_flags/development/verify_participants_access.yml8
-rw-r--r--db/migrate/20211126115449_encrypt_static_objects_external_storage_auth_token.rb29
-rw-r--r--db/schema_migrations/202111261154491
-rw-r--r--lib/api/issues.rb2
-rw-r--r--lib/api/merge_requests.rb2
-rw-r--r--lib/gitlab/config/entry/undefined.rb4
-rw-r--r--lib/gitlab/database/gitlab_loose_foreign_keys.yml4
-rw-r--r--locale/gitlab.pot50
-rw-r--r--qa/qa/page/project/show.rb4
-rw-r--r--spec/db/schema_spec.rb1
-rw-r--r--spec/features/admin/admin_settings_spec.rb18
-rw-r--r--spec/features/projects/tags/user_edits_tags_spec.rb3
-rw-r--r--spec/graphql/resolvers/users/participants_resolver_spec.rb54
-rw-r--r--spec/lib/gitlab/config/entry/undefined_spec.rb6
-rw-r--r--spec/migrations/20211126115449_encrypt_static_objects_external_storage_auth_token_spec.rb56
-rw-r--r--spec/models/application_setting_spec.rb26
-rw-r--r--spec/models/ci/build_spec.rb2
-rw-r--r--spec/models/concerns/participable_spec.rb74
-rw-r--r--spec/requests/api/issues/get_project_issues_spec.rb2
-rw-r--r--spec/services/ci/parse_dotenv_artifact_service_spec.rb14
-rw-r--r--spec/support/shared_examples/requests/api/issuable_participants_examples.rb30
-rw-r--r--spec/views/projects/buttons/_dropdown.html.haml_spec.rb42
34 files changed, 426 insertions, 113 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 44d2da1a434..21ff8d35786 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-034cc7332fc1ebf67599f7f9e98e1588bc6d1823
+4ba8618078d9107d52c0d735f76286ab0b113a8a
diff --git a/app/graphql/resolvers/users/participants_resolver.rb b/app/graphql/resolvers/users/participants_resolver.rb
new file mode 100644
index 00000000000..9e87b60fa34
--- /dev/null
+++ b/app/graphql/resolvers/users/participants_resolver.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Users
+ class ParticipantsResolver < BaseResolver
+ type Types::UserType.connection_type, null: true
+
+ def resolve(**args)
+ object.visible_participants(current_user)
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb
index 3b0f93d8dc1..498569f11ca 100644
--- a/app/graphql/types/issue_type.rb
+++ b/app/graphql/types/issue_type.rb
@@ -80,7 +80,8 @@ module Types
description: 'Relative position of the issue (used for positioning in epic tree and issue boards).'
field :participants, Types::UserType.connection_type, null: true, complexity: 5,
- description: 'List of participants in the issue.'
+ description: 'List of participants in the issue.',
+ resolver: Resolvers::Users::ParticipantsResolver
field :emails_disabled, GraphQL::Types::Boolean, null: false,
method: :project_emails_disabled?,
description: 'Indicates if a project has email notifications disabled: `true` if email notifications are disabled.'
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index 8ad313758e5..bd78643eb3d 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -148,7 +148,8 @@ module Types
field :author, Types::UserType, null: true,
description: 'User who created this merge request.'
field :participants, Types::UserType.connection_type, null: true, complexity: 15,
- description: 'Participants in the merge request. This includes the author, assignees, reviewers, and users mentioned in notes.'
+ description: 'Participants in the merge request. This includes the author, assignees, reviewers, and users mentioned in notes.',
+ resolver: Resolvers::Users::ParticipantsResolver
field :subscribed, GraphQL::Types::Boolean, method: :subscribed?, null: false, complexity: 5,
description: 'Indicates if the currently logged in user is subscribed to this merge request.'
field :labels, Types::LabelType.connection_type, null: true, complexity: 5,
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index af5796d682f..06a5b95f40e 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -21,7 +21,7 @@ class ApplicationSetting < ApplicationRecord
add_authentication_token_field :runners_registration_token, encrypted: -> { Feature.enabled?(:application_settings_tokens_optional_encryption) ? :optional : :required }
add_authentication_token_field :health_check_access_token
- add_authentication_token_field :static_objects_external_storage_auth_token
+ add_authentication_token_field :static_objects_external_storage_auth_token, encrypted: :optional
belongs_to :self_monitoring_project, class_name: "Project", foreign_key: 'instance_administration_project_id'
belongs_to :push_rule
diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb
index 54ec8b2c3e4..5e20aac3b92 100644
--- a/app/models/application_setting_implementation.rb
+++ b/app/models/application_setting_implementation.rb
@@ -363,6 +363,14 @@ module ApplicationSettingImplementation
super(levels&.map { |level| Gitlab::VisibilityLevel.level_value(level) })
end
+ def static_objects_external_storage_auth_token=(token)
+ if token.present?
+ set_static_objects_external_storage_auth_token(token)
+ else
+ self.static_objects_external_storage_auth_token_encrypted = nil
+ end
+ end
+
def performance_bar_allowed_group
Group.find_by_id(performance_bar_allowed_group_id)
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 54967910f4d..428e440afba 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -59,7 +59,7 @@ module Ci
has_one :runner_session, class_name: 'Ci::BuildRunnerSession', validate: true, inverse_of: :build
has_one :trace_metadata, class_name: 'Ci::BuildTraceMetadata', inverse_of: :build
- has_many :terraform_state_versions, class_name: 'Terraform::StateVersion', dependent: :nullify, inverse_of: :build, foreign_key: :ci_build_id # rubocop:disable Cop/ActiveRecordDependent
+ has_many :terraform_state_versions, class_name: 'Terraform::StateVersion', inverse_of: :build, foreign_key: :ci_build_id
accepts_nested_attributes_for :runner_session, update_only: true
accepts_nested_attributes_for :job_variables
diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb
index 25410a859e9..1663aa6c886 100644
--- a/app/models/concerns/participable.rb
+++ b/app/models/concerns/participable.rb
@@ -60,6 +60,15 @@ module Participable
filtered_participants_hash[user]
end
+ # Returns only participants visible for the user
+ #
+ # Returns an Array of User instances.
+ def visible_participants(user)
+ return participants(user) unless Feature.enabled?(:verify_participants_access, project, default_enabled: :yaml)
+
+ filter_by_ability(raw_participants(user, verify_access: true))
+ end
+
# Checks if the user is a participant in a discussion.
#
# This method processes attributes of objects in breadth-first order.
@@ -84,8 +93,7 @@ module Participable
end
end
- def raw_participants(current_user = nil)
- current_user ||= author
+ def raw_participants(current_user = nil, verify_access: false)
ext = Gitlab::ReferenceExtractor.new(project, current_user)
participants = Set.new
process = [self]
@@ -97,6 +105,8 @@ module Participable
when User
participants << source
when Participable
+ next unless !verify_access || source_visible_to_user?(source, current_user)
+
source.class.participant_attrs.each do |attr|
if attr.respond_to?(:call)
source.instance_exec(current_user, ext, &attr)
@@ -116,6 +126,10 @@ module Participable
participants.merge(ext.users)
end
+ def source_visible_to_user?(source, user)
+ Ability.allowed?(user, "read_#{source.model_name.element}".to_sym, source)
+ end
+
def filter_by_ability(participants)
case self
when PersonalSnippet
diff --git a/app/services/ci/parse_dotenv_artifact_service.rb b/app/services/ci/parse_dotenv_artifact_service.rb
index 725ecbcce5d..94f7496ed04 100644
--- a/app/services/ci/parse_dotenv_artifact_service.rb
+++ b/app/services/ci/parse_dotenv_artifact_service.rb
@@ -14,7 +14,7 @@ module Ci
Ci::JobVariable.bulk_insert!(variables)
success
- rescue SizeLimitError, ParserError, ActiveRecord::RecordInvalid => error
+ rescue SizeLimitError, ParserError, ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique => error
Gitlab::ErrorTracking.track_exception(error, job_id: artifact.job_id)
error(error.message, :bad_request)
end
diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml
deleted file mode 100644
index 12ce4667e1a..00000000000
--- a/app/views/projects/buttons/_dropdown.html.haml
+++ /dev/null
@@ -1,42 +0,0 @@
-- can_create_issue = show_new_issue_link?(@project)
-- can_create_project_snippet = can?(current_user, :create_snippet, @project)
-- can_push_code = can?(current_user, :push_code, @project)
-- create_mr_from_new_fork = can?(current_user, :fork_project, @project) && can?(current_user, :create_merge_request_in, @project)
-- merge_project = merge_request_source_project_for_project(@project)
-
-- show_menu = can_create_issue || can_create_project_snippet || can_push_code || create_mr_from_new_fork || merge_project
-
-- if show_menu
- .project-action-button.dropdown.inline<
- %a.btn.btn-default.gl-button.dropdown-toggle.has-tooltip.qa-create-new-dropdown{ href: '#', title: _('Create new...'), 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => _('Create new...'), 'data-display' => 'static' }
- = sprite_icon('plus', css_class: 'gl-icon')
- = sprite_icon("chevron-down", css_class: 'gl-icon')
- %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown
- - if can_create_issue || merge_project || can_create_project_snippet
- %li.dropdown-header= _('This project')
-
- - if can_create_issue
- %li= link_to _('New issue'), new_project_issue_path(@project)
-
- - if merge_project
- %li= link_to _('New merge request'), project_new_merge_request_path(merge_project)
-
- - if can_create_project_snippet
- %li= link_to _('New snippet'), new_project_snippet_path(@project)
-
- - if can_push_code
- %li.dropdown-header= _('This repository')
-
- - if can_push_code
- %li.qa-new-file-option= link_to _('New file'), project_new_blob_path(@project, @project.default_branch_or_main)
- - unless @project.empty_repo?
- %li= link_to _('New branch'), new_project_branch_path(@project)
- %li= link_to _('New tag'), new_project_tag_path(@project)
- - elsif can_collaborate_with_project?(@project)
- %li= link_to _('New file'), project_new_blob_path(@project, @project.default_branch_or_main)
- - elsif create_mr_from_new_fork
- - continue_params = { to: project_new_blob_path(@project, @project.default_branch_or_main),
- notice: edit_in_new_fork_notice,
- notice_now: edit_in_new_fork_notice_now }
- - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_params)
- %li= link_to _('New file'), fork_path, method: :post
diff --git a/app/views/projects/pipeline_schedules/edit.html.haml b/app/views/projects/pipeline_schedules/edit.html.haml
index 29896500ea1..51f0c58330d 100644
--- a/app/views/projects/pipeline_schedules/edit.html.haml
+++ b/app/views/projects/pipeline_schedules/edit.html.haml
@@ -4,7 +4,7 @@
- add_page_specific_style 'page_bundles/pipeline_schedules'
%h3.page-title
- = _("Edit Pipeline Schedule %{id}") % { id: @schedule.id }
+ = _("Edit Pipeline Schedule")
%hr
= render "form"
diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml
index 83a3cac487f..d2a41dcdb90 100644
--- a/app/views/projects/tags/_tag.html.haml
+++ b/app/views/projects/tags/_tag.html.haml
@@ -2,7 +2,7 @@
- release = @releases.find { |release| release.tag == tag.name }
- commit_status = @tag_pipeline_statuses[tag.name] unless @tag_pipeline_statuses.nil?
-%li.flex-row.js-tag-list{ class: "gl-white-space-normal!" }
+%li.flex-row.js-tag-list{ class: "gl-white-space-normal! gl-align-items-flex-start!" }
.row-main-content
= sprite_icon('tag')
= link_to tag.name, project_tag_path(@project, tag.name), class: 'item-title ref-name'
@@ -11,10 +11,6 @@
%span.badge.badge-success.gl-ml-2.gl-badge.sm.badge-pill
= s_('TagsPage|protected')
- - if tag.message.present?
- &nbsp;
- = strip_signature(tag.message)
-
- if commit
.block-truncated
= render 'projects/branches/commit', commit: commit, project: @project
@@ -28,6 +24,10 @@
= _("Release")
= link_to release.name, project_releases_path(@project, anchor: release.tag), class: 'gl-text-blue-600!'
+ - if tag.message.present?
+ %pre.wrap
+ = strip_signature(tag.message)
+
.row-fixed-content.controls.flex-row
- if tag.has_signature?
= render partial: 'projects/commit/signature', object: tag.signature
diff --git a/config/feature_flags/development/verify_participants_access.yml b/config/feature_flags/development/verify_participants_access.yml
new file mode 100644
index 00000000000..8857003fd47
--- /dev/null
+++ b/config/feature_flags/development/verify_participants_access.yml
@@ -0,0 +1,8 @@
+---
+name: verify_participants_access
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74906
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/347407
+milestone: '14.6'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/db/migrate/20211126115449_encrypt_static_objects_external_storage_auth_token.rb b/db/migrate/20211126115449_encrypt_static_objects_external_storage_auth_token.rb
new file mode 100644
index 00000000000..9ce034b0065
--- /dev/null
+++ b/db/migrate/20211126115449_encrypt_static_objects_external_storage_auth_token.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class EncryptStaticObjectsExternalStorageAuthToken < Gitlab::Database::Migration[1.0]
+ class ApplicationSetting < ActiveRecord::Base
+ self.table_name = 'application_settings'
+
+ scope :encrypted_token_is_null, -> { where(static_objects_external_storage_auth_token_encrypted: nil) }
+ scope :encrypted_token_is_not_null, -> { where.not(static_objects_external_storage_auth_token_encrypted: nil) }
+ scope :plaintext_token_is_not_null, -> { where.not(static_objects_external_storage_auth_token: nil) }
+ end
+
+ def up
+ ApplicationSetting.reset_column_information
+
+ ApplicationSetting.encrypted_token_is_null.plaintext_token_is_not_null.find_each do |application_setting|
+ token_encrypted = Gitlab::CryptoHelper.aes256_gcm_encrypt(application_setting.static_objects_external_storage_auth_token)
+ application_setting.update!(static_objects_external_storage_auth_token_encrypted: token_encrypted)
+ end
+ end
+
+ def down
+ ApplicationSetting.reset_column_information
+
+ ApplicationSetting.encrypted_token_is_not_null.find_each do |application_setting|
+ token = Gitlab::CryptoHelper.aes256_gcm_decrypt(application_setting.static_objects_external_storage_auth_token_encrypted)
+ application_setting.update!(static_objects_external_storage_auth_token: token, static_objects_external_storage_auth_token_encrypted: nil)
+ end
+ end
+end
diff --git a/db/schema_migrations/20211126115449 b/db/schema_migrations/20211126115449
new file mode 100644
index 00000000000..693dfb46149
--- /dev/null
+++ b/db/schema_migrations/20211126115449
@@ -0,0 +1 @@
+2e6e432ecf7b2c885905fd4df6b57fa99b324f56cb0850d9fc792b4a9b363423 \ No newline at end of file
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 06d5e4cb504..0f91cc4a837 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -472,7 +472,7 @@ module API
end
get ':id/issues/:issue_iid/participants' do
issue = find_project_issue(params[:issue_iid])
- participants = ::Kaminari.paginate_array(issue.participants)
+ participants = ::Kaminari.paginate_array(issue.visible_participants(current_user))
present paginate(participants), with: Entities::UserBasic, current_user: current_user, project: user_project
end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 063fff4023a..3f39af7f909 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -282,7 +282,7 @@ module API
get ':id/merge_requests/:merge_request_iid/participants', feature_category: :code_review, urgency: :low do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
- participants = ::Kaminari.paginate_array(merge_request.participants)
+ participants = ::Kaminari.paginate_array(merge_request.visible_participants(current_user))
present paginate(participants), with: Entities::UserBasic
end
diff --git a/lib/gitlab/config/entry/undefined.rb b/lib/gitlab/config/entry/undefined.rb
index 5f708abc80c..55393890693 100644
--- a/lib/gitlab/config/entry/undefined.rb
+++ b/lib/gitlab/config/entry/undefined.rb
@@ -31,6 +31,10 @@ module Gitlab
false
end
+ def type
+ nil
+ end
+
def inspect
"#<#{self.class.name}>"
end
diff --git a/lib/gitlab/database/gitlab_loose_foreign_keys.yml b/lib/gitlab/database/gitlab_loose_foreign_keys.yml
index aac1af4cd0f..782ce61b8ad 100644
--- a/lib/gitlab/database/gitlab_loose_foreign_keys.yml
+++ b/lib/gitlab/database/gitlab_loose_foreign_keys.yml
@@ -41,3 +41,7 @@ pages_deployments:
- table: ci_builds
column: ci_build_id
on_delete: async_nullify
+terraform_state_versions:
+ - table: ci_builds
+ column: ci_build_id
+ on_delete: async_nullify
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 323287837f3..d841baad094 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -9932,9 +9932,6 @@ msgstr ""
msgid "Create new project"
msgstr ""
-msgid "Create new..."
-msgstr ""
-
msgid "Create or import your first project"
msgstr ""
@@ -12665,7 +12662,7 @@ msgstr ""
msgid "Edit Password"
msgstr ""
-msgid "Edit Pipeline Schedule %{id}"
+msgid "Edit Pipeline Schedule"
msgstr ""
msgid "Edit Release"
@@ -38792,12 +38789,21 @@ msgstr ""
msgid "VulnerabilityManagement|%{statusStart}Resolved%{statusEnd} %{timeago} by %{user}"
msgstr ""
+msgid "VulnerabilityManagement|A removed or remediated vulnerability"
+msgstr ""
+
msgid "VulnerabilityManagement|A true-positive and will fix"
msgstr ""
+msgid "VulnerabilityManagement|A verified true-positive vulnerability"
+msgstr ""
+
msgid "VulnerabilityManagement|Add vulnerability finding"
msgstr ""
+msgid "VulnerabilityManagement|An unverified non-confirmed finding"
+msgstr ""
+
msgid "VulnerabilityManagement|Change status"
msgstr ""
@@ -38825,6 +38831,9 @@ msgstr ""
msgid "VulnerabilityManagement|Requires assessment"
msgstr ""
+msgid "VulnerabilityManagement|Select a method"
+msgstr ""
+
msgid "VulnerabilityManagement|Something went wrong while trying to delete the comment. Please try again later."
msgstr ""
@@ -38894,9 +38903,18 @@ msgstr ""
msgid "Vulnerability|Additional Info"
msgstr ""
+msgid "Vulnerability|Bug Bounty"
+msgstr ""
+
+msgid "Vulnerability|CVSS v3"
+msgstr ""
+
msgid "Vulnerability|Class"
msgstr ""
+msgid "Vulnerability|Code Review"
+msgstr ""
+
msgid "Vulnerability|Comments"
msgstr ""
@@ -38912,21 +38930,33 @@ msgstr ""
msgid "Vulnerability|Description"
msgstr ""
+msgid "Vulnerability|Details"
+msgstr ""
+
msgid "Vulnerability|Detected"
msgstr ""
+msgid "Vulnerability|Detection method"
+msgstr ""
+
msgid "Vulnerability|Download"
msgstr ""
msgid "Vulnerability|Evidence"
msgstr ""
+msgid "Vulnerability|External Security Report"
+msgstr ""
+
msgid "Vulnerability|False positive detected"
msgstr ""
msgid "Vulnerability|File"
msgstr ""
+msgid "Vulnerability|GitLab Security Report"
+msgstr ""
+
msgid "Vulnerability|Identifier"
msgstr ""
@@ -38936,6 +38966,9 @@ msgstr ""
msgid "Vulnerability|Image"
msgstr ""
+msgid "Vulnerability|Information related how the vulnerability was discovered and its impact to the system."
+msgstr ""
+
msgid "Vulnerability|Links"
msgstr ""
@@ -38960,6 +38993,15 @@ msgstr ""
msgid "Vulnerability|Scanner Provider"
msgstr ""
+msgid "Vulnerability|Security Audit"
+msgstr ""
+
+msgid "Vulnerability|Select a severity"
+msgstr ""
+
+msgid "Vulnerability|Set the status of the vulnerability finding based on the information available to you."
+msgstr ""
+
msgid "Vulnerability|Severity"
msgstr ""
diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb
index 65a1f726a8a..8074b6f833b 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -45,10 +45,6 @@ module QA
element :tree_holder, '.tree-holder' # rubocop:disable QA/ElementWithPattern
end
- view 'app/views/projects/buttons/_dropdown.html.haml' do
- element :create_new_dropdown
- end
-
view 'app/views/projects/buttons/_fork.html.haml' do
element :fork_label, "%span= s_('ProjectOverview|Fork')" # rubocop:disable QA/ElementWithPattern
element :fork_link, "link_to new_project_fork_path(@project)" # rubocop:disable QA/ElementWithPattern
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
index e8a0e63b0d7..a8b28b32bd7 100644
--- a/spec/db/schema_spec.rb
+++ b/spec/db/schema_spec.rb
@@ -81,7 +81,6 @@ RSpec.describe 'Database schema' do
subscriptions: %w[user_id subscribable_id],
suggestions: %w[commit_id],
taggings: %w[tag_id taggable_id tagger_id],
- terraform_state_versions: %w[ci_build_id],
timelogs: %w[user_id],
todos: %w[target_id commit_id],
uploads: %w[model_id],
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 0a39baca259..29323c604ef 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -445,6 +445,24 @@ RSpec.describe 'Admin updates settings' do
expect(current_settings.repository_storages_weighted).to eq('default' => 50)
end
+
+ context 'External storage for repository static objects' do
+ it 'changes Repository external storage settings' do
+ encrypted_token = Gitlab::CryptoHelper.aes256_gcm_encrypt('OldToken')
+ current_settings.update_attribute :static_objects_external_storage_auth_token_encrypted, encrypted_token
+
+ visit repository_admin_application_settings_path
+
+ page.within('.as-repository-static-objects') do
+ fill_in 'application_setting_static_objects_external_storage_url', with: 'http://example.com'
+ fill_in 'application_setting_static_objects_external_storage_auth_token', with: 'Token'
+ click_button 'Save changes'
+ end
+
+ expect(current_settings.static_objects_external_storage_url).to eq('http://example.com')
+ expect(current_settings.static_objects_external_storage_auth_token).to eq('Token')
+ end
+ end
end
context 'Reporting page' do
diff --git a/spec/features/projects/tags/user_edits_tags_spec.rb b/spec/features/projects/tags/user_edits_tags_spec.rb
index 9f66b7274e8..17080043b6d 100644
--- a/spec/features/projects/tags/user_edits_tags_spec.rb
+++ b/spec/features/projects/tags/user_edits_tags_spec.rb
@@ -21,7 +21,8 @@ RSpec.describe 'Project > Tags', :js do
context 'page with tags list' do
it 'shows tag name' do
- expect(page).to have_content 'v1.1.0 Version 1.1.0'
+ expect(page).to have_content 'v1.1.0'
+ expect(page).to have_content 'Version 1.1.0'
end
it 'shows tag edit button' do
diff --git a/spec/graphql/resolvers/users/participants_resolver_spec.rb b/spec/graphql/resolvers/users/participants_resolver_spec.rb
new file mode 100644
index 00000000000..c857eb9f63d
--- /dev/null
+++ b/spec/graphql/resolvers/users/participants_resolver_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::Users::ParticipantsResolver do
+ include GraphqlHelpers
+
+ describe '#resolve' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:guest) { create(:user) }
+ let_it_be(:project) { create(:project, :public) }
+ let_it_be(:issue) { create(:issue, project: project) }
+ let_it_be(:note) do
+ create(
+ :note,
+ :confidential,
+ project: project,
+ noteable: issue,
+ author: create(:user)
+ )
+ end
+
+ subject(:resolved_items) { resolve(described_class, args: {}, ctx: { current_user: current_user }, obj: issue)&.items }
+
+ before do
+ project.add_guest(guest)
+ project.add_developer(user)
+ end
+
+ context 'when current user is not set' do
+ let(:current_user) { nil }
+
+ it 'returns only publicly visible participants for this user' do
+ is_expected.to match_array([issue.author])
+ end
+ end
+
+ context 'when current user does not have enough permissions' do
+ let(:current_user) { guest }
+
+ it 'returns only publicly visible participants for this user' do
+ is_expected.to match_array([issue.author])
+ end
+ end
+
+ context 'when current user has access to confidential notes' do
+ let(:current_user) { user }
+
+ it 'returns all participants for this user' do
+ is_expected.to match_array([issue.author, note.author])
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/config/entry/undefined_spec.rb b/spec/lib/gitlab/config/entry/undefined_spec.rb
index 36faabd8e31..31e0f9487aa 100644
--- a/spec/lib/gitlab/config/entry/undefined_spec.rb
+++ b/spec/lib/gitlab/config/entry/undefined_spec.rb
@@ -40,4 +40,10 @@ RSpec.describe Gitlab::Config::Entry::Undefined do
expect(entry.specified?).to eq false
end
end
+
+ describe '#type' do
+ it 'returns nil' do
+ expect(entry.type).to eq nil
+ end
+ end
end
diff --git a/spec/migrations/20211126115449_encrypt_static_objects_external_storage_auth_token_spec.rb b/spec/migrations/20211126115449_encrypt_static_objects_external_storage_auth_token_spec.rb
new file mode 100644
index 00000000000..bc8b7c56676
--- /dev/null
+++ b/spec/migrations/20211126115449_encrypt_static_objects_external_storage_auth_token_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe EncryptStaticObjectsExternalStorageAuthToken, :migration do
+ let(:application_settings) do
+ Class.new(ActiveRecord::Base) do
+ self.table_name = 'application_settings'
+ end
+ end
+
+ context 'when static_objects_external_storage_auth_token is not set' do
+ it 'does nothing' do
+ application_settings.create!
+
+ reversible_migration do |migration|
+ migration.before -> {
+ settings = application_settings.first
+
+ expect(settings.static_objects_external_storage_auth_token).to be_nil
+ expect(settings.static_objects_external_storage_auth_token_encrypted).to be_nil
+ }
+
+ migration.after -> {
+ settings = application_settings.first
+
+ expect(settings.static_objects_external_storage_auth_token).to be_nil
+ expect(settings.static_objects_external_storage_auth_token_encrypted).to be_nil
+ }
+ end
+ end
+ end
+
+ context 'when static_objects_external_storage_auth_token is set' do
+ it 'encrypts static_objects_external_storage_auth_token' do
+ settings = application_settings.create!
+ settings.update_column(:static_objects_external_storage_auth_token, 'Test')
+
+ reversible_migration do |migration|
+ migration.before -> {
+ settings = application_settings.first
+
+ expect(settings.static_objects_external_storage_auth_token).to eq('Test')
+ expect(settings.static_objects_external_storage_auth_token_encrypted).to be_nil
+ }
+ migration.after -> {
+ settings = application_settings.first
+
+ expect(settings.static_objects_external_storage_auth_token).to eq('Test')
+ expect(settings.static_objects_external_storage_auth_token_encrypted).to be_present
+ }
+ end
+ end
+ end
+end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 8ad83da61f3..8f6e94cc46e 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -1239,4 +1239,30 @@ RSpec.describe ApplicationSetting do
expect(subject.kroki_formats_excalidraw).to eq(true)
end
end
+
+ describe '#static_objects_external_storage_auth_token=' do
+ subject { setting.static_objects_external_storage_auth_token = token }
+
+ let(:token) { 'Test' }
+
+ it 'stores an encrypted version of the token' do
+ subject
+
+ expect(setting[:static_objects_external_storage_auth_token]).to be_nil
+ expect(setting[:static_objects_external_storage_auth_token_encrypted]).to be_present
+ expect(setting.static_objects_external_storage_auth_token).to eq('Test')
+ end
+
+ context 'when token is empty' do
+ let(:token) { '' }
+
+ it 'removes an encrypted version of the token' do
+ subject
+
+ expect(setting[:static_objects_external_storage_auth_token]).to be_nil
+ expect(setting[:static_objects_external_storage_auth_token_encrypted]).to be_nil
+ expect(setting.static_objects_external_storage_auth_token).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 6f5bb75314e..ad2d646edc9 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe Ci::Build do
it { is_expected.to have_one(:deployment) }
it { is_expected.to have_one(:runner_session) }
it { is_expected.to have_one(:trace_metadata) }
- it { is_expected.to have_many(:terraform_state_versions).dependent(:nullify).inverse_of(:build) }
+ it { is_expected.to have_many(:terraform_state_versions).inverse_of(:build) }
it { is_expected.to validate_presence_of(:ref) }
diff --git a/spec/models/concerns/participable_spec.rb b/spec/models/concerns/participable_spec.rb
index 903c7ae16b6..50cf7377b99 100644
--- a/spec/models/concerns/participable_spec.rb
+++ b/spec/models/concerns/participable_spec.rb
@@ -51,7 +51,9 @@ RSpec.describe Participable do
end
it 'supports attributes returning another Participable' do
- other_model = Class.new { include Participable }
+ other_model = Class.new do
+ include Participable
+ end
other_model.participant(:bar)
model.participant(:foo)
@@ -115,6 +117,76 @@ RSpec.describe Participable do
end
end
+ describe '#visible_participants' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(anything, :read_class, anything) { readable }
+ end
+
+ let(:readable) { true }
+
+ it 'returns the list of participants' do
+ model.participant(:foo)
+ model.participant(:bar)
+
+ user1 = build(:user)
+ user2 = build(:user)
+ user3 = build(:user)
+ project = build(:project, :public)
+ instance = model.new
+
+ allow(instance).to receive_message_chain(:model_name, :element) { 'class' }
+ expect(instance).to receive(:foo).and_return(user2)
+ expect(instance).to receive(:bar).and_return(user3)
+ expect(instance).to receive(:project).thrice.and_return(project)
+
+ participants = instance.visible_participants(user1)
+
+ expect(participants).to include(user2)
+ expect(participants).to include(user3)
+ end
+
+ context 'when Participable is not readable by the user' do
+ let(:readable) { false }
+
+ it 'does not return unavailable participants' do
+ model.participant(:bar)
+
+ instance = model.new
+ user1 = build(:user)
+ user2 = build(:user)
+ project = build(:project, :public)
+
+ allow(instance).to receive_message_chain(:model_name, :element) { 'class' }
+ allow(instance).to receive(:bar).and_return(user2)
+ expect(instance).to receive(:project).thrice.and_return(project)
+
+ expect(instance.visible_participants(user1)).to be_empty
+ end
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(verify_participants_access: false)
+ end
+
+ it 'returns unavailable participants' do
+ model.participant(:bar)
+
+ instance = model.new
+ user1 = build(:user)
+ user2 = build(:user)
+ project = build(:project, :public)
+
+ allow(instance).to receive_message_chain(:model_name, :element) { 'class' }
+ allow(instance).to receive(:bar).and_return(user2)
+ expect(instance).to receive(:project).thrice.and_return(project)
+
+ expect(instance.visible_participants(user1)).to match_array([user2])
+ end
+ end
+ end
+ end
+
describe '#participant?' do
let(:instance) { model.new }
diff --git a/spec/requests/api/issues/get_project_issues_spec.rb b/spec/requests/api/issues/get_project_issues_spec.rb
index 07fa1d40f7b..9948e13e9ae 100644
--- a/spec/requests/api/issues/get_project_issues_spec.rb
+++ b/spec/requests/api/issues/get_project_issues_spec.rb
@@ -873,7 +873,7 @@ RSpec.describe API::Issues do
end
it 'returns 404 if the issue is confidential' do
- post api("/projects/#{project.id}/issues/#{confidential_issue.iid}/participants", non_member)
+ get api("/projects/#{project.id}/issues/#{confidential_issue.iid}/participants", non_member)
expect(response).to have_gitlab_http_status(:not_found)
end
diff --git a/spec/services/ci/parse_dotenv_artifact_service_spec.rb b/spec/services/ci/parse_dotenv_artifact_service_spec.rb
index c4040a426f2..ba71ddab40e 100644
--- a/spec/services/ci/parse_dotenv_artifact_service_spec.rb
+++ b/spec/services/ci/parse_dotenv_artifact_service_spec.rb
@@ -23,6 +23,20 @@ RSpec.describe Ci::ParseDotenvArtifactService do
hash_including('key' => 'KEY2', 'value' => 'VAR2'))
end
+ context 'when dotenv variables are conflicting against manual variables' do
+ before do
+ create(:ci_job_variable, job: build, key: 'KEY1')
+ end
+
+ it 'returns an error message that there is a duplicate variable' do
+ subject
+
+ expect(subject[:status]).to eq(:error)
+ expect(subject[:message]).to include("Key (key, job_id)=(KEY1, #{build.id}) already exists.")
+ expect(subject[:http_status]).to eq(:bad_request)
+ end
+ end
+
context 'when parse error happens' do
before do
allow(service).to receive(:scan_line!) { raise described_class::ParserError, 'Invalid Format' }
diff --git a/spec/support/shared_examples/requests/api/issuable_participants_examples.rb b/spec/support/shared_examples/requests/api/issuable_participants_examples.rb
index 673d7741017..c5e5803c0a7 100644
--- a/spec/support/shared_examples/requests/api/issuable_participants_examples.rb
+++ b/spec/support/shared_examples/requests/api/issuable_participants_examples.rb
@@ -28,4 +28,34 @@ RSpec.shared_examples 'issuable participants endpoint' do
expect(response).to have_gitlab_http_status(:not_found)
end
+
+ context 'with a confidential note' do
+ let!(:note) do
+ create(
+ :note,
+ :confidential,
+ project: project,
+ noteable: entity,
+ author: create(:user)
+ )
+ end
+
+ it 'returns a full list of participants' do
+ get api("/projects/#{project.id}/#{area}/#{entity.iid}/participants", user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ participant_ids = json_response.map { |el| el['id'] }
+ expect(participant_ids).to match_array([entity.author_id, note.author_id])
+ end
+
+ context 'when user cannot see a confidential note' do
+ it 'returns a limited list of participants' do
+ get api("/projects/#{project.id}/#{area}/#{entity.iid}/participants", create(:user))
+
+ expect(response).to have_gitlab_http_status(:ok)
+ participant_ids = json_response.map { |el| el['id'] }
+ expect(participant_ids).to match_array([entity.author_id])
+ end
+ end
+ end
end
diff --git a/spec/views/projects/buttons/_dropdown.html.haml_spec.rb b/spec/views/projects/buttons/_dropdown.html.haml_spec.rb
deleted file mode 100644
index fc9d7c3ea91..00000000000
--- a/spec/views/projects/buttons/_dropdown.html.haml_spec.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'projects/buttons/_dropdown' do
- let(:user) { create(:user) }
-
- context 'user with all abilities' do
- before do
- assign(:project, project)
-
- allow(view).to receive(:current_user).and_return(user)
- allow(view).to receive(:can?).with(user, :push_code, project).and_return(true)
- allow(view).to receive(:can_collaborate_with_project?).and_return(true)
- end
-
- context 'empty repository' do
- let(:project) { create(:project, :empty_repo) }
-
- it 'has a link to create a new file' do
- render
-
- expect(view).to render_template('projects/buttons/_dropdown')
- expect(rendered).to have_link('New file')
- end
-
- it 'does not have a link to create a new branch' do
- render
-
- expect(view).to render_template('projects/buttons/_dropdown')
- expect(rendered).not_to have_link('New branch')
- end
-
- it 'does not have a link to create a new tag' do
- render
-
- expect(view).to render_template('projects/buttons/_dropdown')
- expect(rendered).not_to have_link('New tag')
- end
- end
- end
-end