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? -   - = 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