New upstream version 15.1.4+ds1

This commit is contained in:
Pirate Praveen 2022-07-29 14:14:30 +02:00
parent dd76d996b0
commit d151e2b7fa
75 changed files with 1538 additions and 364 deletions

View File

@ -2,6 +2,29 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 15.1.4 (2022-07-28)
### Security (18 changes)
- [Security datadog integration leaking](gitlab-org/security/gitlab@1aadbf61796ff95b4716fb8ef335c5a4dbdf8c6a) ([merge request](gitlab-org/security/gitlab!2594))
- [Prevent users who cannot admin a public project from viewing deploy keys](gitlab-org/security/gitlab@a69bec0e58f4ee0df0774dcde1ce1817d41daa7c) ([merge request](gitlab-org/security/gitlab!2641))
- [Add additional condition to accept invitation](gitlab-org/security/gitlab@b3480d20512d61b761da2a8772ec2bfe32182169) ([merge request](gitlab-org/security/gitlab!2655))
- [Update GITLAB_PAGES_VERSION](gitlab-org/security/gitlab@6161c0445e4908cdc152bee4be8b9d223df9f1d0) ([merge request](gitlab-org/security/gitlab!2584))
- [Add html_escape to build_details_entity](gitlab-org/security/gitlab@c0a82385320e144b55e400eff96f39aa56af33ef) ([merge request](gitlab-org/security/gitlab!2610))
- [Check permissions when filtering by contact or organization](gitlab-org/security/gitlab@5b8f2da656ec7839614d4a49f35778ed00b31b2b) ([merge request](gitlab-org/security/gitlab!2645))
- [Use author to run subscribed pipeline](gitlab-org/security/gitlab@982917161408cfe4c3191f352bb1d390dfa3c9fd) ([merge request](gitlab-org/security/gitlab!2558))
- [Remove prohibited branches after project import](gitlab-org/security/gitlab@48864bf7539176eb385f5ce792c8e369a2b79fe6) ([merge request](gitlab-org/security/gitlab!2589))
- [Remove feature flag `ci_yaml_limit_size`](gitlab-org/security/gitlab@b532b82ef2a0ef5b045932a56ffc06281df7a1d8) ([merge request](gitlab-org/security/gitlab!2630))
- [Maintainer can change the visibility of Project and Group](gitlab-org/security/gitlab@9556769a3a2fe2c030eb55e87b1465638d315358) ([merge request](gitlab-org/security/gitlab!2618))
- [Do not link unverified secondary emails with any users](gitlab-org/security/gitlab@331b1d6b590b01c3d45332a6c8e09def249c627e) ([merge request](gitlab-org/security/gitlab!2627))
- [Forbid exchanging access token for ROP flow to users required 2FA setup](gitlab-org/security/gitlab@15bc29c4ab878365356e59c0c8d2e0f361b71e70) ([merge request](gitlab-org/security/gitlab!2621))
- [Remove todos from confidential notes when user loses access](gitlab-org/security/gitlab@9672296987190cc4b08b1f32f3c45086880b06bc) ([merge request](gitlab-org/security/gitlab!2608))
- [Remove group_bot_user and group_access_token after group delete](gitlab-org/security/gitlab@9b1a5e7101e1addee8c3429f4937980020c5f6b3) ([merge request](gitlab-org/security/gitlab!2634))
- [Protect integration secrets](gitlab-org/security/gitlab@4920ac9b10e237cca5d279cbd3cf60daa829a71b) ([merge request](gitlab-org/security/gitlab!2585))
- [Protect Grafana and Sentry integrations](gitlab-org/security/gitlab@a5d33edb1c557e9bbd0a1a79c81e851c1d740e37) ([merge request](gitlab-org/security/gitlab!2576))
- [Fix IDOR in Jira issue show action](gitlab-org/security/gitlab@8b62e0c09c33c44f508266d7b8db4892105124ac) ([merge request](gitlab-org/security/gitlab!2648))
- [Limit proxied requests to Grafana API](gitlab-org/security/gitlab@4580e61bc22b04eda13e293c5868a26a1a6ee571) ([merge request](gitlab-org/security/gitlab!2597))
## 15.1.3 (2022-07-19)
### Added (1 change)

View File

@ -1 +1 @@
15.1.3
15.1.4

View File

@ -1 +1 @@
1.59.0
1.59.1

View File

@ -1 +1 @@
15.1.3
15.1.4

View File

@ -5,6 +5,7 @@ class AutocompleteController < ApplicationController
skip_before_action :authenticate_user!, only: [:users, :award_emojis, :merge_request_target_branches]
before_action :check_search_rate_limit!, only: [:users, :projects]
before_action :authorize_admin_project, only: :deploy_keys_with_owners
feature_category :users, [:users, :user]
feature_category :projects, [:projects]
@ -69,6 +70,10 @@ class AutocompleteController < ApplicationController
private
def authorize_admin_project
render_403 unless Ability.allowed?(current_user, :admin_project, project)
end
def project
@project ||= Autocomplete::ProjectFinder
.new(current_user, params)

View File

@ -478,10 +478,14 @@ class IssuableFinder
end
def by_crm_contact(items)
return items unless can_filter_by_crm_contact?
Issuables::CrmContactFilter.new(params: original_params).filter(items)
end
def by_crm_organization(items)
return items unless can_filter_by_crm_organization?
Issuables::CrmOrganizationFilter.new(params: original_params).filter(items)
end
@ -494,4 +498,20 @@ class IssuableFinder
def feature_flag_scope
params.group || params.project
end
def can_filter_by_crm_contact?
current_user&.can?(:read_crm_contact, root_group)
end
def can_filter_by_crm_organization?
current_user&.can?(:read_crm_organization, root_group)
end
def root_group
strong_memoize(:root_group) do
base_group = params.group || params.project&.group
base_group&.root_ancestor
end
end
end

View File

@ -44,6 +44,8 @@ module ErrorTracking
key: Settings.attr_encrypted_db_key_base_32,
algorithm: 'aes-256-gcm'
before_validation :reset_token
after_save :clear_reactive_cache!
# When a user enables the integrated error tracking
@ -182,6 +184,12 @@ module ErrorTracking
private
def reset_token
if api_url_changed? && !encrypted_token_changed?
self.token = nil
end
end
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

View File

@ -18,6 +18,8 @@ class GrafanaIntegration < ApplicationRecord
validates :enabled, inclusion: { in: [true, false] }
before_validation :reset_token
scope :enabled, -> { where(enabled: true) }
def client
@ -36,6 +38,12 @@ class GrafanaIntegration < ApplicationRecord
private
def reset_token
if grafana_url_changed? && !encrypted_token_changed?
self.token = nil
end
end
def token
decrypt(:token, encrypted_token)
end

View File

@ -22,6 +22,7 @@ class WebHookLog < ApplicationRecord
validates :web_hook, presence: true
before_save :obfuscate_basic_auth
before_save :redact_author_email
def self.recent
where('created_at >= ?', 2.days.ago.beginning_of_day)
@ -52,4 +53,10 @@ class WebHookLog < ApplicationRecord
def obfuscate_basic_auth
self.url = safe_url
end
def redact_author_email
return unless self.request_data.dig('commit', 'author', 'email').present?
self.request_data['commit']['author']['email'] = _('[REDACTED]')
end
end

View File

@ -2,8 +2,35 @@
module Integrations
class Campfire < Integration
prop_accessor :token, :subdomain, :room
SUBDOMAIN_REGEXP = %r{\A[a-z](?:[a-z0-9-]*[a-z0-9])?\z}i.freeze
validates :token, presence: true, if: :activated?
validates :room,
allow_blank: true,
numericality: { only_integer: true, greater_than: 0 }
validates :subdomain,
allow_blank: true,
format: { with: SUBDOMAIN_REGEXP }, length: { in: 1..63 }
field :token,
type: 'password',
title: -> { s_('Campfire token') },
help: -> { s_('CampfireService|API authentication token from Campfire.') },
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
placeholder: '',
required: true
field :subdomain,
title: -> { s_('Campfire subdomain (optional)') },
placeholder: '',
exposes_secrets: true,
help: -> { s_('CampfireService|The %{code_open}.campfirenow.com%{code_close} subdomain.') % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe } }
field :room,
title: -> { s_('Campfire room ID (optional)') },
placeholder: '123456',
help: -> { s_('CampfireService|From the end of the room URL.') }
def title
'Campfire'
@ -22,35 +49,6 @@ module Integrations
'campfire'
end
def fields
[
{
type: 'password',
name: 'token',
title: _('Campfire token'),
help: s_('CampfireService|API authentication token from Campfire.'),
non_empty_password_title: s_('ProjectService|Enter new token'),
non_empty_password_help: s_('ProjectService|Leave blank to use your current token.'),
placeholder: '',
required: true
},
{
type: 'text',
name: 'subdomain',
title: _('Campfire subdomain (optional)'),
placeholder: '',
help: s_('CampfireService|The %{code_open}.campfirenow.com%{code_close} subdomain.') % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
},
{
type: 'text',
name: 'room',
title: _('Campfire room ID (optional)'),
placeholder: '123456',
help: s_('CampfireService|From the end of the room URL.')
}
]
end
def self.supported_events
%w(push)
end

View File

@ -13,6 +13,7 @@ module Integrations
field :drone_url,
title: -> { s_('ProjectService|Drone server URL') },
placeholder: 'http://drone.example.com',
exposes_secrets: true,
required: true
field :token,

View File

@ -222,7 +222,9 @@ module Integrations
# support any events.
end
def find_issue(issue_key, rendered_fields: false, transitions: false)
def find_issue(issue_key, rendered_fields: false, transitions: false, restrict_project_key: false)
return if restrict_project_key && parse_project_from_issue_key(issue_key) != project_key
expands = []
expands << 'renderedFields' if rendered_fields
expands << 'transitions' if transitions
@ -320,6 +322,10 @@ module Integrations
private
def parse_project_from_issue_key(issue_key)
issue_key.gsub(Gitlab::Regex.jira_issue_key_project_key_extraction_regex, '')
end
def branch_name(commit)
commit.first_ref_by_oid(project.repository)
end

View File

@ -5,7 +5,27 @@ module Integrations
include HasWebHook
extend Gitlab::Utils::Override
prop_accessor :username, :token, :server
field :username,
title: -> { s_('Username') },
help: -> { s_('Enter your Packagist username.') },
placeholder: '',
required: true
field :token,
type: 'password',
title: -> { s_('Token') },
help: -> { s_('Enter your Packagist token.') },
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
placeholder: '',
required: true
field :server,
title: -> { s_('Server (optional)') },
help: -> { s_('Enter your Packagist server. Defaults to https://packagist.org.') },
placeholder: 'https://packagist.org',
exposes_secrets: true,
required: false
validates :username, presence: true, if: :activated?
validates :token, presence: true, if: :activated?
@ -22,37 +42,6 @@ module Integrations
'packagist'
end
def fields
[
{
type: 'text',
name: 'username',
title: _('Username'),
help: s_('Enter your Packagist username.'),
placeholder: '',
required: true
},
{
type: 'password',
name: 'token',
title: _('Token'),
help: s_('Enter your Packagist token.'),
non_empty_password_title: s_('ProjectService|Enter new token'),
non_empty_password_help: s_('ProjectService|Leave blank to use your current token.'),
placeholder: '',
required: true
},
{
type: 'text',
name: 'server',
title: _('Server (optional)'),
help: s_('Enter your Packagist server. Defaults to https://packagist.org.'),
placeholder: 'https://packagist.org',
required: false
}
]
end
def self.supported_events
%w(push merge_request tag_push)
end

View File

@ -1,10 +1,33 @@
# frozen_string_literal: true
module Integrations
class Zentao < Integration
class Zentao < BaseIssueTracker
include Gitlab::Routing
data_field :url, :api_url, :api_token, :zentao_product_xid
self.field_storage = :data_fields
field :url,
title: -> { s_('ZentaoIntegration|ZenTao Web URL') },
placeholder: 'https://www.zentao.net',
help: -> { s_('ZentaoIntegration|Base URL of the ZenTao instance.') },
exposes_secrets: true,
required: true
field :api_url,
title: -> { s_('ZentaoIntegration|ZenTao API URL (optional)') },
help: -> { s_('ZentaoIntegration|If different from Web URL.') },
exposes_secrets: true
field :api_token,
type: 'password',
title: -> { s_('ZentaoIntegration|ZenTao API token') },
non_empty_password_title: -> { s_('ZentaoIntegration|Enter new ZenTao API token') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
required: true
field :zentao_product_xid,
title: -> { s_('ZentaoIntegration|ZenTao Product ID') },
required: true
validates :url, public_url: true, presence: true, if: :activated?
validates :api_url, public_url: true, allow_blank: true
@ -19,6 +42,17 @@ module Integrations
zentao_tracker_data || self.build_zentao_tracker_data
end
alias_method :project_url, :url
def set_default_data
return unless issues_tracker.present?
return if url
data_fields.url ||= issues_tracker['url']
data_fields.api_url ||= issues_tracker['api_url']
end
def title
'ZenTao'
end
@ -47,39 +81,6 @@ module Integrations
%w()
end
def fields
[
{
type: 'text',
name: 'url',
title: s_('ZentaoIntegration|ZenTao Web URL'),
placeholder: 'https://www.zentao.net',
help: s_('ZentaoIntegration|Base URL of the ZenTao instance.'),
required: true
},
{
type: 'text',
name: 'api_url',
title: s_('ZentaoIntegration|ZenTao API URL (optional)'),
help: s_('ZentaoIntegration|If different from Web URL.')
},
{
type: 'password',
name: 'api_token',
title: s_('ZentaoIntegration|ZenTao API token'),
non_empty_password_title: s_('ZentaoIntegration|Enter new ZenTao API token'),
non_empty_password_help: s_('ProjectService|Leave blank to use your current token.'),
required: true
},
{
type: 'text',
name: 'zentao_product_xid',
title: s_('ZentaoIntegration|ZenTao Product ID'),
required: true
}
]
end
private
def client

View File

@ -2023,6 +2023,7 @@ class Project < ApplicationRecord
end
def after_import
repository.remove_prohibited_branches
repository.expire_content_cache
wiki.repository.expire_content_cache

View File

@ -1168,6 +1168,16 @@ class Repository
@cache ||= Gitlab::RepositoryCache.new(self)
end
def remove_prohibited_branches
return unless exists?
prohibited_branches = raw_repository.branch_names.select { |name| name.match(/\A\h{40}\z/) }
return if prohibited_branches.blank?
prohibited_branches.each { |name| raw_repository.delete_branch(name) }
end
private
# TODO Genericize finder, later split this on finders by Ref or Oid

View File

@ -70,8 +70,6 @@ class Snippet < ApplicationRecord
},
if: :content_changed?
validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values }
after_create :create_statistics
# Scopes

View File

@ -74,6 +74,7 @@ class Todo < ApplicationRecord
scope :for_commit, -> (id) { where(commit_id: id) }
scope :with_entity_associations, -> { preload(:target, :author, :note, group: :route, project: [:route, { namespace: [:route, :owner] }]) }
scope :joins_issue_and_assignees, -> { left_joins(issue: :assignees) }
scope :for_internal_notes, -> { joins(:note).where(note: { confidential: true }) }
enum resolved_by_action: { system_done: 0, api_all_done: 1, api_done: 2, mark_all_done: 3, mark_done: 4 }, _prefix: :resolved_by

View File

@ -602,23 +602,24 @@ class User < ApplicationRecord
end
end
# Find a User by their primary email or any associated secondary email
# Find a User by their primary email or any associated confirmed secondary email
def find_by_any_email(email, confirmed: false)
return unless email
by_any_email(email, confirmed: confirmed).take
end
# Returns a relation containing all the users for the given email addresses
# Returns a relation containing all found users by their primary email
# or any associated confirmed secondary email
#
# @param emails [String, Array<String>] email addresses to check
# @param confirmed [Boolean] Only return users where the email is confirmed
# @param confirmed [Boolean] Only return users where the primary email is confirmed
def by_any_email(emails, confirmed: false)
from_users = by_user_email(emails)
from_users = from_users.confirmed if confirmed
from_emails = by_emails(emails)
from_emails = from_emails.confirmed.merge(Email.confirmed) if confirmed
from_emails = by_emails(emails).merge(Email.confirmed)
from_emails = from_emails.confirmed if confirmed
items = [from_users, from_emails]
@ -723,6 +724,7 @@ class User < ApplicationRecord
matched_by_email_user_id = email_table
.project(email_table[:user_id])
.where(email_table[:email].eq(email_address))
.where(email_table[:confirmed_at].not_eq(nil))
.take(1) # at most 1 record as there is a unique constraint
where(
@ -1435,7 +1437,7 @@ class User < ApplicationRecord
all_emails = []
all_emails << email unless temp_oauth_email?
all_emails << private_commit_email if include_private_email
all_emails.concat(emails.map(&:email))
all_emails.concat(emails.filter_map { |email| email.email if email.confirmed? })
all_emails.uniq
end

View File

@ -151,7 +151,7 @@ class BuildDetailsEntity < Ci::JobEntity
# We do not return the invalid_dependencies for all scenarios see https://gitlab.com/gitlab-org/gitlab/-/issues/287772#note_914406387
punctuation = invalid_dependencies.empty? ? '.' : ': '
_("This job could not start because it could not retrieve the needed artifacts%{punctuation}%{invalid_dependencies}") %
{ invalid_dependencies: invalid_dependencies, punctuation: punctuation }
{ invalid_dependencies: html_escape(invalid_dependencies), punctuation: punctuation }
end
def help_message(docs_url)

View File

@ -5,7 +5,7 @@ module UpdateVisibilityLevel
def valid_visibility_level_change?(target, new_visibility)
return true unless new_visibility
new_visibility_level = Gitlab::VisibilityLevel.level_value(new_visibility)
new_visibility_level = Gitlab::VisibilityLevel.level_value(new_visibility, fallback_value: nil)
if new_visibility_level != target.visibility_level_value
unless can?(current_user, :change_visibility_level, target) &&

View File

@ -15,6 +15,10 @@ module Grafana
self.reactive_cache_work_type = :external_dependency
self.reactive_cache_worker_finder = ->(_id, *args) { from_cache(*args) }
SUPPORTED_DATASOURCE_PATTERN = %r{\A\d+\z}.freeze
SUPPORTED_PROXY_PATH = Gitlab::Metrics::Dashboard::Stages::GrafanaFormatter::PROXY_PATH
attr_accessor :project, :datasource_id, :proxy_path, :query_params
# @param project_id [Integer] Project id for which grafana is configured.
@ -38,6 +42,7 @@ module Grafana
end
def execute
return cannot_proxy_response unless can_proxy?
return cannot_proxy_response unless client
with_reactive_cache(*cache_key) { |result| result }
@ -69,6 +74,11 @@ module Grafana
private
def can_proxy?
SUPPORTED_PROXY_PATH == proxy_path &&
SUPPORTED_DATASOURCE_PATTERN.match?(datasource_id)
end
def client
project.grafana_integration&.client
end

View File

@ -35,6 +35,8 @@ module Groups
user_ids_for_project_authorizations_refresh = obtain_user_ids_for_project_authorizations_refresh
destroy_group_bots
group.destroy
if user_ids_for_project_authorizations_refresh.present?
@ -76,6 +78,19 @@ module Groups
group.users_ids_of_direct_members
end
# rubocop:disable CodeReuse/ActiveRecord
def destroy_group_bots
bot_ids = group.members_and_requesters.joins(:user).merge(User.project_bot).pluck(:user_id)
current_user_id = current_user.id
group.run_after_commit do
bot_ids.each do |user_id|
DeleteUserWorker.perform_async(current_user_id, user_id, skip_authorization: true)
end
end
end
# rubocop:enable CodeReuse/ActiveRecord
end
end

View File

@ -41,11 +41,20 @@ module Todos
end
def remove_confidential_resource_todos
# Deletes todos for confidential issues
Todo
.for_target(confidential_issues.select(:id))
.for_type(Issue.name)
.for_user(user)
.delete_all
# Deletes todos for internal notes on unauthorized projects
Todo
.for_type(Issue.name)
.for_internal_notes
.for_project(non_authorized_reporter_projects) # Only Reporter+ can read internal notes
.for_user(user)
.delete_all
end
def remove_project_todos

View File

@ -1,8 +0,0 @@
---
name: ci_yaml_limit_size
introduced_by_url: https://dev.gitlab.org/gitlab/gitlabhq/-/merge_requests/3126
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/29875
milestone: '12.0'
type: development
group: group::pipeline authoring
default_enabled: true

View File

@ -144,6 +144,9 @@ ci_subscriptions_projects:
- table: projects
column: upstream_project_id
on_delete: async_delete
- table: users
column: author_id
on_delete: async_delete
ci_triggers:
- table: users
column: owner_id

View File

@ -24,7 +24,11 @@ Doorkeeper.configure do
resource_owner_from_credentials do |routes|
user = Gitlab::Auth.find_with_user_password(params[:username], params[:password], increment_failed_attempts: true)
user unless user.try(:two_factor_enabled?)
next unless user
next if user.two_factor_enabled? || Gitlab::Auth::TwoFactorAuthVerifier.new(user).two_factor_authentication_enforced?
user
end
# If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below.

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
class AddAuthorToCiSubscriptionsProjects < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
INDEX_NAME = 'index_ci_subscriptions_projects_author_id'
def up
unless column_exists?(:ci_subscriptions_projects, :author_id)
add_column :ci_subscriptions_projects, :author_id, :bigint
end
add_concurrent_index :ci_subscriptions_projects, :author_id, name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :ci_subscriptions_projects, INDEX_NAME
remove_column :ci_subscriptions_projects, :author_id
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
class AddIndexWithTargetTypeToTodos < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
INDEX_FOR_PROJECTS_NAME = 'index_requirements_project_id_user_id_id_and_target_type'
INDEX_FOR_TARGET_TYPE_NAME = 'index_requirements_user_id_and_target_type'
def up
add_concurrent_index :todos, [:project_id, :user_id, :id, :target_type], name: INDEX_FOR_PROJECTS_NAME
add_concurrent_index :todos, [:user_id, :target_type], name: INDEX_FOR_TARGET_TYPE_NAME
end
def down
remove_concurrent_index_by_name :todos, INDEX_FOR_PROJECTS_NAME
remove_concurrent_index_by_name :todos, INDEX_FOR_TARGET_TYPE_NAME
end
end

View File

@ -0,0 +1,23 @@
# frozen_string_literal: true
class RemoveDeprecatedIndexesFromTodos < Gitlab::Database::Migration[2.0]
disable_ddl_transaction!
PROJECTS_INDEX = 'index_todos_on_project_id_and_user_id_and_id'
USERS_INDEX = 'index_todos_on_user_id'
# These indexes are deprecated in favor of two new ones
# added in a previous migration:
#
# * index_requirements_project_id_user_id_target_type_and_id
# * index_requirements_user_id_and_target_type
def up
remove_concurrent_index_by_name :todos, PROJECTS_INDEX
remove_concurrent_index_by_name :todos, USERS_INDEX
end
def down
add_concurrent_index :todos, [:project_id, :user_id, :id], name: PROJECTS_INDEX
add_concurrent_index :todos, :user_id, name: USERS_INDEX
end
end

View File

@ -0,0 +1 @@
8726707f9f4bb8d256886c592b6a11ba8487de24f5340c837800f5ce0c32df9d

View File

@ -0,0 +1 @@
f6638435457f57f5c566e107de4f4557a1d87b5dd27acc9e5345999197d18e6e

View File

@ -0,0 +1 @@
ff9ad44a43be82867da8e0f51e68a2284065cab6b2eb7cf6496108dce1cdd657

View File

@ -13202,7 +13202,8 @@ ALTER SEQUENCE ci_stages_id_seq OWNED BY ci_stages.id;
CREATE TABLE ci_subscriptions_projects (
id bigint NOT NULL,
downstream_project_id bigint NOT NULL,
upstream_project_id bigint NOT NULL
upstream_project_id bigint NOT NULL,
author_id bigint
);
CREATE SEQUENCE ci_subscriptions_projects_id_seq
@ -27529,6 +27530,8 @@ CREATE INDEX index_ci_stages_on_pipeline_id_and_position ON ci_stages USING btre
CREATE INDEX index_ci_stages_on_project_id ON ci_stages USING btree (project_id);
CREATE INDEX index_ci_subscriptions_projects_author_id ON ci_subscriptions_projects USING btree (author_id);
CREATE INDEX index_ci_subscriptions_projects_on_upstream_project_id ON ci_subscriptions_projects USING btree (upstream_project_id);
CREATE UNIQUE INDEX index_ci_subscriptions_projects_unique_subscription ON ci_subscriptions_projects USING btree (downstream_project_id, upstream_project_id);
@ -29261,6 +29264,10 @@ CREATE INDEX index_requirements_on_title_trigram ON requirements USING gin (titl
CREATE INDEX index_requirements_on_updated_at ON requirements USING btree (updated_at);
CREATE INDEX index_requirements_project_id_user_id_id_and_target_type ON todos USING btree (project_id, user_id, id, target_type);
CREATE INDEX index_requirements_user_id_and_target_type ON todos USING btree (user_id, target_type);
CREATE INDEX index_resource_iteration_events_on_issue_id ON resource_iteration_events USING btree (issue_id);
CREATE INDEX index_resource_iteration_events_on_iteration_id ON resource_iteration_events USING btree (iteration_id);
@ -29565,12 +29572,8 @@ CREATE INDEX index_todos_on_note_id ON todos USING btree (note_id);
CREATE INDEX index_todos_on_project_id_and_id ON todos USING btree (project_id, id);
CREATE INDEX index_todos_on_project_id_and_user_id_and_id ON todos USING btree (project_id, user_id, id);
CREATE INDEX index_todos_on_target_type_and_target_id ON todos USING btree (target_type, target_id);
CREATE INDEX index_todos_on_user_id ON todos USING btree (user_id);
CREATE INDEX index_todos_on_user_id_and_id_done ON todos USING btree (user_id, id) WHERE ((state)::text = 'done'::text);
CREATE INDEX index_todos_on_user_id_and_id_pending ON todos USING btree (user_id, id) WHERE ((state)::text = 'pending'::text);

View File

@ -747,12 +747,6 @@ You can change these limits in the [GitLab Rails console](operations/rails_conso
ApplicationSetting.update!(max_yaml_depth: 125)
```
To disable this limitation entirely, disable the feature flag in the console:
```ruby
Feature.disable(:ci_yaml_limit_size)
```
### Limit dotenv variables
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/321552) in GitLab 14.5.

View File

@ -41,8 +41,6 @@ module Gitlab
end
def too_big?
return false unless Feature.enabled?(:ci_yaml_limit_size)
!deep_size.valid?
end

View File

@ -418,6 +418,10 @@ module Gitlab
@jira_issue_key_regex ||= /[A-Z][A-Z_0-9]+-\d+/
end
def jira_issue_key_project_key_extraction_regex
@jira_issue_key_project_key_extraction_regex ||= /-\d+/
end
def jira_transition_id_regex
@jira_transition_id_regex ||= /\d+/
end

View File

@ -18,6 +18,8 @@ module Gitlab
scope :public_to_user, -> (user = nil) do
where(visibility_level: VisibilityLevel.levels_for_user(user))
end
alias_method :visibility_level=, :visibility=
end
PRIVATE = 0 unless const_defined?(:PRIVATE)
@ -108,10 +110,10 @@ module Gitlab
options.key(level.to_i) || s_('VisibilityLevel|Unknown')
end
def level_value(level)
def level_value(level, fallback_value: PRIVATE)
return level.to_i if level.to_i.to_s == level.to_s && string_options.key(level.to_i)
string_options[level] || PRIVATE
string_options[level] || fallback_value
end
def string_level(level)

View File

@ -10,8 +10,8 @@ require_relative 'api/default_options'
# Each desired feature flag state is specified as 'feature-flag=state'. This allows us to run package-and-qa with the
# feature flag set to the desired state.
#
# For example, if the specified files included `config/feature_flags/development/ci_yaml_limit_size.yml` and the desired
# state as specified by the second argument was enabled, the value returned would be `ci_yaml_limit_size=enabled`
# For example, if the specified files included `config/feature_flags/development/ci_awesome_feature.yml` and the desired
# state as specified by the second argument was enabled, the value returned would be `ci_awesome_feature=enabled`
class GetFeatureFlagsFromFiles
def initialize(options)

View File

@ -378,43 +378,27 @@ RSpec.describe AutocompleteController do
end
context 'GET deploy_keys_with_owners' do
let!(:deploy_key) { create(:deploy_key, user: user) }
let!(:deploy_keys_project) { create(:deploy_keys_project, :write_access, project: project, deploy_key: deploy_key) }
let_it_be(:public_project) { create(:project, :public) }
let_it_be(:user) { create(:user) }
let_it_be(:deploy_key) { create(:deploy_key, user: user) }
let_it_be(:deploy_keys_project) do
create(:deploy_keys_project, :write_access, project: public_project, deploy_key: deploy_key)
end
context 'unauthorized user' do
it 'returns a not found response' do
get(:deploy_keys_with_owners, params: { project_id: project.id })
get(:deploy_keys_with_owners, params: { project_id: public_project.id })
expect(response).to have_gitlab_http_status(:redirect)
end
end
context 'when the user who can read the project is logged in' do
context 'when the user is logged in' do
before do
sign_in(user)
end
context 'and they cannot read the project' do
it 'returns a not found response' do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(false)
get(:deploy_keys_with_owners, params: { project_id: project.id })
expect(response).to have_gitlab_http_status(:not_found)
end
end
it 'renders the deploy key in a json payload, with its owner' do
get(:deploy_keys_with_owners, params: { project_id: project.id })
expect(json_response.count).to eq(1)
expect(json_response.first['title']).to eq(deploy_key.title)
expect(json_response.first['owner']['id']).to eq(deploy_key.user.id)
expect(json_response.first['deploy_keys_projects']).to be_nil
end
context 'with an unknown project' do
context 'with a non-existing project' do
it 'returns a not found response' do
get(:deploy_keys_with_owners, params: { project_id: 9999 })
@ -422,19 +406,46 @@ RSpec.describe AutocompleteController do
end
end
context 'and the user cannot read the owner of the key' do
before do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_user, deploy_key.user).and_return(false)
context 'with an existing project' do
context 'when user cannot admin project' do
it 'returns a forbidden response' do
get(:deploy_keys_with_owners, params: { project_id: public_project.id })
expect(response).to have_gitlab_http_status(:forbidden)
end
end
it 'returns a payload without owner' do
get(:deploy_keys_with_owners, params: { project_id: project.id })
context 'when user can admin project' do
before do
public_project.add_maintainer(user)
end
expect(json_response.count).to eq(1)
expect(json_response.first['title']).to eq(deploy_key.title)
expect(json_response.first['owner']).to be_nil
expect(json_response.first['deploy_keys_projects']).to be_nil
context 'and user can read owner of key' do
it 'renders the deploy keys in a json payload, with owner' do
get(:deploy_keys_with_owners, params: { project_id: public_project.id })
expect(json_response.count).to eq(1)
expect(json_response.first['title']).to eq(deploy_key.title)
expect(json_response.first['owner']['id']).to eq(deploy_key.user.id)
expect(json_response.first['deploy_keys_projects']).to be_nil
end
end
context 'and user cannot read owner of key' do
before do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_user, deploy_key.user).and_return(false)
end
it 'returns a payload without owner' do
get(:deploy_keys_with_owners, params: { project_id: public_project.id })
expect(json_response.count).to eq(1)
expect(json_response.first['title']).to eq(deploy_key.title)
expect(json_response.first['owner']).to be_nil
expect(json_response.first['deploy_keys_projects']).to be_nil
end
end
end
end
end

View File

@ -143,14 +143,28 @@ RSpec.describe InvitesController do
context 'when user exists with the invited email as secondary email' do
before do
secondary_email = create(:email, user: user, email: 'foo@example.com')
member.update!(invite_email: secondary_email.email)
end
it 'is redirected to a new session with invite email param' do
request
context 'when secondary email is confirmed' do
let(:secondary_email) { create(:email, :confirmed, user: user, email: 'foo@example.com') }
expect(response).to redirect_to(new_user_session_path(invite_email: member.invite_email))
it 'is redirected to a new session with invite email param' do
request
expect(response).to redirect_to(new_user_session_path(invite_email: member.invite_email))
end
end
context 'when secondary email is unconfirmed' do
let(:secondary_email) { create(:email, user: user, email: 'foo@example.com') }
it 'is redirected to a new registration with invite email param and flash message', :aggregate_failures do
request
expect(response).to redirect_to(new_user_registration_path(invite_email: member.invite_email))
expect(flash[:notice]).to eq 'To accept this invitation, create an account or sign in.'
end
end
end

View File

@ -61,7 +61,7 @@ RSpec.describe 'GPG signed commits' do
let(:user_2) do
create(:user, email: GpgHelpers::User2.emails.first, username: 'bette.cartwright', name: 'Bette Cartwright').tap do |user|
# secondary, unverified email
create :email, user: user, email: GpgHelpers::User2.emails.last
create :email, user: user, email: 'mail@koffeinfrei.org'
end
end
@ -83,10 +83,11 @@ RSpec.describe 'GPG signed commits' do
end
end
it 'unverified signature: user email does not match the committer email, but is the same user' do
it 'unverified signature: gpg key email does not match the committer_email but is the same user when the committer_email belongs to the user as a confirmed secondary email' do
user_2_key
user_2.emails.find_by(email: 'mail@koffeinfrei.org').confirm
visit project_commit_path(project, GpgHelpers::DIFFERING_EMAIL_SHA)
visit project_commit_path(project, GpgHelpers::SIGNED_COMMIT_SHA)
wait_for_all_requests
page.find('.gpg-status-box', text: 'Unverified').click
@ -99,7 +100,7 @@ RSpec.describe 'GPG signed commits' do
end
end
it 'unverified signature: user email does not match the committer email' do
it 'unverified signature: gpg key email does not match the committer_email when the committer_email belongs to the user as a unconfirmed secondary email' do
user_2_key
visit project_commit_path(project, GpgHelpers::SIGNED_COMMIT_SHA)

View File

@ -30,6 +30,9 @@ RSpec.describe Resolvers::IssuesResolver do
before_all do
project.add_developer(current_user)
project.add_reporter(reporter)
create(:crm_settings, group: group, enabled: true)
create(:label_link, label: label1, target: issue1)
create(:label_link, label: label1, target: issue2)
create(:label_link, label: label2, target: issue2)
@ -399,6 +402,8 @@ RSpec.describe Resolvers::IssuesResolver do
let_it_be(:crm_issue3) { create(:issue, project: project) }
before_all do
group.add_developer(current_user)
create(:issue_customer_relations_contact, issue: crm_issue1, contact: contact1)
create(:issue_customer_relations_contact, issue: crm_issue2, contact: contact2)
create(:issue_customer_relations_contact, issue: crm_issue3, contact: contact3)
@ -631,13 +636,13 @@ RSpec.describe Resolvers::IssuesResolver do
end
it 'finds a specific issue with iid', :request_store do
result = batch_sync(max_queries: 7) { resolve_issues(iid: issue1.iid).to_a }
result = batch_sync(max_queries: 8) { resolve_issues(iid: issue1.iid).to_a }
expect(result).to contain_exactly(issue1)
end
it 'batches queries that only include IIDs', :request_store do
result = batch_sync(max_queries: 7) do
result = batch_sync(max_queries: 8) do
[issue1, issue2]
.map { |issue| resolve_issues(iid: issue.iid.to_s) }
.flat_map(&:to_a)
@ -647,7 +652,7 @@ RSpec.describe Resolvers::IssuesResolver do
end
it 'finds a specific issue with iids', :request_store do
result = batch_sync(max_queries: 7) do
result = batch_sync(max_queries: 8) do
resolve_issues(iids: [issue1.iid]).to_a
end

View File

@ -120,12 +120,6 @@ RSpec.describe Gitlab::Config::Loader::Yaml do
it 'returns false' do
expect(loader).not_to be_valid
end
it 'returns true if "ci_yaml_limit_size" feature flag is disabled' do
stub_feature_flags(ci_yaml_limit_size: false)
expect(loader).to be_valid
end
end
describe '#load!' do

View File

@ -274,7 +274,46 @@ RSpec.describe Gitlab::Gpg::Commit do
it_behaves_like 'returns the cached signature on second call'
end
context 'user email does not match the committer email, but is the same user' do
context 'gpg key email does not match the committer_email but is the same user when the committer_email belongs to the user as a confirmed secondary email' do
let!(:commit) { create :commit, project: project, sha: commit_sha, committer_email: GpgHelpers::User2.emails.first }
let(:user) do
create(:user, email: GpgHelpers::User1.emails.first).tap do |user|
create :email, :confirmed, user: user, email: GpgHelpers::User2.emails.first
end
end
let!(:gpg_key) do
create :gpg_key, key: GpgHelpers::User1.public_key, user: user
end
before do
allow(Gitlab::Git::Commit).to receive(:extract_signature_lazily)
.with(Gitlab::Git::Repository, commit_sha)
.and_return(
[
GpgHelpers::User1.signed_commit_signature,
GpgHelpers::User1.signed_commit_base_data
]
)
end
it 'returns an invalid signature' do
expect(described_class.new(commit).signature).to have_attributes(
commit_sha: commit_sha,
project: project,
gpg_key: gpg_key,
gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid,
gpg_key_user_name: GpgHelpers::User1.names.first,
gpg_key_user_email: GpgHelpers::User1.emails.first,
verification_status: 'same_user_different_email'
)
end
it_behaves_like 'returns the cached signature on second call'
end
context 'gpg key email does not match the committer_email when the committer_email belongs to the user as a unconfirmed secondary email' do
let!(:commit) { create :commit, project: project, sha: commit_sha, committer_email: GpgHelpers::User2.emails.first }
let(:user) do
@ -306,7 +345,7 @@ RSpec.describe Gitlab::Gpg::Commit do
gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid,
gpg_key_user_name: GpgHelpers::User1.names.first,
gpg_key_user_email: GpgHelpers::User1.emails.first,
verification_status: 'same_user_different_email'
verification_status: 'other_user'
)
end

View File

@ -571,7 +571,6 @@ project:
- remove_source_branch_after_merge
- deleting_user
- upstream_projects
- downstream_projects
- upstream_project_subscriptions
- downstream_project_subscriptions
- service_desk_setting

View File

@ -20,17 +20,30 @@ RSpec.describe Gitlab::LegacyGithubImport::UserFormatter do
expect(user.gitlab_id).to eq gl_user.id
end
it 'returns GitLab user id when user primary email matches GitHub email' do
it 'returns GitLab user id when user confirmed primary email matches GitHub email' do
gl_user = create(:user, email: octocat.email)
expect(user.gitlab_id).to eq gl_user.id
end
it 'returns GitLab user id when any of user linked emails matches GitHub email' do
it 'returns GitLab user id when user unconfirmed primary email matches GitHub email' do
gl_user = create(:user, :unconfirmed, email: octocat.email)
expect(user.gitlab_id).to eq gl_user.id
end
it 'returns GitLab user id when user confirmed secondary email matches GitHub email' do
gl_user = create(:user, email: 'johndoe@example.com')
create(:email, :confirmed, user: gl_user, email: octocat.email)
expect(user.gitlab_id).to eq gl_user.id
end
it 'returns nil when user unconfirmed secondary email matches GitHub email' do
gl_user = create(:user, email: 'johndoe@example.com')
create(:email, user: gl_user, email: octocat.email)
expect(user.gitlab_id).to eq gl_user.id
expect(user.gitlab_id).to be_nil
end
end

View File

@ -4,20 +4,52 @@ require 'spec_helper'
RSpec.describe Gitlab::VisibilityLevel do
describe '.level_value' do
it 'converts "public" to integer value' do
expect(described_class.level_value('public')).to eq(Gitlab::VisibilityLevel::PUBLIC)
where(:string_value, :integer_value) do
[
['private', described_class::PRIVATE],
['internal', described_class::INTERNAL],
['public', described_class::PUBLIC]
]
end
it 'converts string integer to integer value' do
expect(described_class.level_value('20')).to eq(20)
with_them do
it "converts '#{params[:string_value]}' to integer value #{params[:integer_value]}" do
expect(described_class.level_value(string_value)).to eq(integer_value)
end
it "converts string integer '#{params[:integer_value]}' to integer value #{params[:integer_value]}" do
expect(described_class.level_value(integer_value.to_s)).to eq(integer_value)
end
it 'defaults to PRIVATE when string integer value is not valid' do
expect(described_class.level_value(integer_value.to_s + 'r')).to eq(described_class::PRIVATE)
expect(described_class.level_value(integer_value.to_s + ' ')).to eq(described_class::PRIVATE)
expect(described_class.level_value('r' + integer_value.to_s)).to eq(described_class::PRIVATE)
end
it 'defaults to PRIVATE when string value is not valid' do
expect(described_class.level_value(string_value.capitalize)).to eq(described_class::PRIVATE)
expect(described_class.level_value(string_value + ' ')).to eq(described_class::PRIVATE)
expect(described_class.level_value(string_value + 'r')).to eq(described_class::PRIVATE)
end
end
it 'defaults to PRIVATE when string value is not valid' do
expect(described_class.level_value('invalid')).to eq(Gitlab::VisibilityLevel::PRIVATE)
expect(described_class.level_value('invalid')).to eq(described_class::PRIVATE)
end
it 'defaults to PRIVATE when integer value is not valid' do
expect(described_class.level_value(100)).to eq(Gitlab::VisibilityLevel::PRIVATE)
expect(described_class.level_value(100)).to eq(described_class::PRIVATE)
end
context 'when `fallback_value` is set to `nil`' do
it 'returns `nil` when string value is not valid' do
expect(described_class.level_value('invalid', fallback_value: nil)).to be_nil
end
it 'returns `nil` when integer value is not valid' do
expect(described_class.level_value(100, fallback_value: nil)).to be_nil
end
end
end

View File

@ -226,27 +226,45 @@ RSpec.describe Commit do
end
describe '#committer' do
context 'with a confirmed e-mail' do
it 'returns the user' do
user = create(:user, email: commit.committer_email)
context "when committer_email is the user's primary email" do
context 'when the user email is confirmed' do
let!(:user) { create(:user, email: commit.committer_email) }
expect(commit.committer).to eq(user)
it 'returns the user' do
expect(commit.committer).to eq(user)
expect(commit.committer(confirmed: false)).to eq(user)
end
end
context 'when the user email is unconfirmed' do
let!(:user) { create(:user, :unconfirmed, email: commit.committer_email) }
it 'returns the user according to confirmed argument' do
expect(commit.committer).to be_nil
expect(commit.committer(confirmed: false)).to eq(user)
end
end
end
context 'with an unconfirmed e-mail' do
let(:user) { create(:user) }
context "when committer_email is the user's secondary email" do
let!(:user) { create(:user) }
before do
create(:email, user: user, email: commit.committer_email)
context 'when the user email is confirmed' do
let!(:email) { create(:email, :confirmed, user: user, email: commit.committer_email) }
it 'returns the user' do
expect(commit.committer).to eq(user)
expect(commit.committer(confirmed: false)).to eq(user)
end
end
it 'returns no user' do
expect(commit.committer).to be_nil
end
context 'when the user email is unconfirmed' do
let!(:email) { create(:email, user: user, email: commit.committer_email) }
it 'returns the user' do
expect(commit.committer(confirmed: false)).to eq(user)
it 'does not return the user' do
expect(commit.committer).to be_nil
expect(commit.committer(confirmed: false)).to be_nil
end
end
end
end

View File

@ -121,6 +121,38 @@ RSpec.describe ErrorTracking::ProjectErrorTrackingSetting do
end
end
end
describe 'before_validation :reset_token' do
context 'when a token was previously set' do
subject { create(:project_error_tracking_setting, project: project) }
it 'resets token if url changed' do
subject.api_url = 'http://sentry.com/api/0/projects/org-slug/proj-slug/'
expect(subject).not_to be_valid
expect(subject.token).to be_nil
end
it "does not reset token if new url is set together with the same token" do
subject.api_url = 'http://sentrytest.com/api/0/projects/org-slug/proj-slug/'
current_token = subject.token
subject.token = current_token
expect(subject).to be_valid
expect(subject.token).to eq(current_token)
expect(subject.api_url).to eq('http://sentrytest.com/api/0/projects/org-slug/proj-slug/')
end
it 'does not reset token if new url is set together with a new token' do
subject.api_url = 'http://sentrytest.com/api/0/projects/org-slug/proj-slug/'
subject.token = 'token'
expect(subject).to be_valid
expect(subject.token).to eq('token')
expect(subject.api_url).to eq('http://sentrytest.com/api/0/projects/org-slug/proj-slug/')
end
end
end
end
describe '.extract_sentry_external_url' do

View File

@ -86,4 +86,38 @@ RSpec.describe GrafanaIntegration do
end
end
end
describe 'Callbacks' do
describe 'before_validation :reset_token' do
context 'when a token was previously set' do
subject(:grafana_integration) { create(:grafana_integration) }
it 'resets token if url changed' do
grafana_integration.grafana_url = 'http://gitlab1.com'
expect(grafana_integration).not_to be_valid
expect(grafana_integration.send(:token)).to be_nil
end
it "does not reset token if new url is set together with the same token" do
grafana_integration.grafana_url = 'http://gitlab_edited.com'
current_token = grafana_integration.send(:token)
grafana_integration.token = current_token
expect(grafana_integration).to be_valid
expect(grafana_integration.send(:token)).to eq(current_token)
expect(grafana_integration.grafana_url).to eq('http://gitlab_edited.com')
end
it 'does not reset token if new url is set together with a new token' do
grafana_integration.grafana_url = 'http://gitlab_edited.com'
grafana_integration.token = 'token'
expect(grafana_integration).to be_valid
expect(grafana_integration.send(:token)).to eq('token')
expect(grafana_integration.grafana_url).to eq('http://gitlab_edited.com')
end
end
end
end
end

View File

@ -30,15 +30,12 @@ RSpec.describe WebHookLog do
end
describe '#save' do
let(:web_hook_log) { build(:web_hook_log, url: url) }
let(:url) { 'http://example.com' }
subject { web_hook_log.save! }
it { is_expected.to eq(true) }
context 'with basic auth credentials' do
let(:url) { 'http://test:123@example.com'}
let(:web_hook_log) { build(:web_hook_log, url: 'http://test:123@example.com') }
subject { web_hook_log.save! }
it { is_expected.to eq(true) }
it 'obfuscates the basic auth credentials' do
subject
@ -46,6 +43,30 @@ RSpec.describe WebHookLog do
expect(web_hook_log.url).to eq('http://*****:*****@example.com')
end
end
context 'with author email' do
let(:author) { create(:user) }
let(:web_hook_log) { create(:web_hook_log, request_data: data) }
let(:data) do
{
commit: {
author: {
name: author.name,
email: author.email
}
}
}.deep_stringify_keys
end
it "redacts author's email" do
expect(web_hook_log.request_data['commit']).to match a_hash_including(
'author' => {
'name' => author.name,
'email' => _('[REDACTED]')
}
)
end
end
end
describe '.delete_batch_for' do

View File

@ -5,7 +5,17 @@ require 'spec_helper'
RSpec.describe Integrations::Campfire do
include StubRequests
it_behaves_like Integrations::ResetSecretFields do
let(:integration) { described_class.new }
end
describe 'Validations' do
it { is_expected.to validate_numericality_of(:room).is_greater_than(0).only_integer }
it { is_expected.to validate_length_of(:subdomain).is_at_most(63) }
it { is_expected.to allow_value("foo").for(:subdomain) }
it { is_expected.not_to allow_value("foo.bar").for(:subdomain) }
it { is_expected.not_to allow_value("foo.bar/#").for(:subdomain) }
context 'when integration is active' do
before do
subject.active = true

View File

@ -7,6 +7,10 @@ RSpec.describe Integrations::DroneCi, :use_clean_rails_memory_store_caching do
subject(:integration) { described_class.new }
it_behaves_like Integrations::ResetSecretFields do
let(:integration) { subject }
end
describe 'validations' do
context 'active' do
before do

View File

@ -12,6 +12,7 @@ RSpec.describe Integrations::Jira do
let(:api_url) { 'http://api-jira.example.com' }
let(:username) { 'jira-username' }
let(:password) { 'jira-password' }
let(:project_key) { nil }
let(:transition_id) { 'test27' }
let(:server_info_results) { { 'deploymentType' => 'Cloud' } }
let(:jira_integration) do
@ -19,7 +20,8 @@ RSpec.describe Integrations::Jira do
project: project,
url: url,
username: username,
password: password
password: password,
project_key: project_key
)
end
@ -533,6 +535,22 @@ RSpec.describe Integrations::Jira do
expect(WebMock).to have_requested(:get, issue_url)
end
end
context 'with restricted restrict_project_key option' do
subject(:find_issue) { jira_integration.find_issue(issue_key, restrict_project_key: true) }
it { is_expected.to eq(nil) }
context 'and project_key matches' do
let(:project_key) { 'JIRA' }
it 'calls the Jira API to get the issue' do
find_issue
expect(WebMock).to have_requested(:get, issue_url)
end
end
end
end
describe '#close_issue' do

View File

@ -29,6 +29,10 @@ RSpec.describe Integrations::Packagist do
let(:hook_url) { "#{packagist_server}/api/update-package?username=#{packagist_username}&apiToken=#{packagist_token}" }
end
it_behaves_like Integrations::ResetSecretFields do
let(:integration) { described_class.new(packagist_params) }
end
describe '#execute' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }

View File

@ -9,6 +9,31 @@ RSpec.describe Integrations::Zentao do
let(:zentao_product_xid) { '3' }
let(:zentao_integration) { create(:zentao_integration) }
it_behaves_like Integrations::ResetSecretFields do
let(:integration) { zentao_integration }
end
describe 'set_default_data' do
let(:project) { create(:project, :repository) }
context 'when gitlab.yml was initialized' do
it 'is prepopulated with the settings' do
settings = {
'zentao' => {
'url' => 'http://zentao.sample/projects/project_a',
'api_url' => 'http://zentao.sample/api'
}
}
allow(Gitlab.config).to receive(:issues_tracker).and_return(settings)
integration = project.create_zentao_integration(active: true)
expect(integration.url).to eq('http://zentao.sample/projects/project_a')
expect(integration.api_url).to eq('http://zentao.sample/api')
end
end
end
describe '#create' do
let(:project) { create(:project, :repository) }
let(:params) do

View File

@ -5617,6 +5617,7 @@ RSpec.describe Project, factory_default: :keep do
let(:import_state) { create(:import_state, project: project) }
it 'runs the correct hooks' do
expect(project.repository).to receive(:remove_prohibited_branches)
expect(project.repository).to receive(:expire_content_cache)
expect(project.wiki.repository).to receive(:expire_content_cache)
expect(import_state).to receive(:finish)

View File

@ -3360,4 +3360,62 @@ RSpec.describe Repository do
end
end
end
describe '#remove_prohibited_branches' do
let(:branch_name) { '37fd3601be4c25497a39fa2e6a206e09e759d597' }
before do
allow(repository.raw_repository).to receive(:branch_names).and_return([branch_name])
end
context 'when prohibited branch exists' do
it 'deletes prohibited branch' do
expect(repository.raw_repository).to receive(:delete_branch).with(branch_name)
repository.remove_prohibited_branches
end
end
shared_examples 'does not delete branch' do
it 'returns without removing the branch' do
expect(repository.raw_repository).not_to receive(:delete_branch)
repository.remove_prohibited_branches
end
end
context 'when branch name is 40-characters long but not hexadecimal' do
let(:branch_name) { '37fd3601be4c25497a39fa2e6a206e09e759d59s' }
include_examples 'does not delete branch'
end
context 'when branch name is hexadecimal' do
context 'when branch name is less than 40-characters long' do
let(:branch_name) { '37fd3601be4c25497a39fa2e6a206e09e759d' }
include_examples 'does not delete branch'
end
context 'when branch name is more than 40-characters long' do
let(:branch_name) { '37fd3601be4c25497a39fa2e6a206e09e759dfdfd' }
include_examples 'does not delete branch'
end
end
context 'when prohibited branch does not exist' do
let(:branch_name) { 'main' }
include_examples 'does not delete branch'
end
context 'when raw repository does not exist' do
before do
allow(repository).to receive(:exists?).and_return(false)
end
include_examples 'does not delete branch'
end
end
end

View File

@ -36,8 +36,6 @@ RSpec.describe Snippet do
it { is_expected.to validate_presence_of(:content) }
it { is_expected.to validate_inclusion_of(:visibility_level).in_array(Gitlab::VisibilityLevel.values) }
it do
allow(Gitlab::CurrentSettings).to receive(:snippet_size_limit).and_return(1)

View File

@ -475,4 +475,14 @@ RSpec.describe Todo do
it { is_expected.to contain_exactly(user1.id, user2.id) }
end
describe '.for_internal_notes' do
it 'returns todos created from internal notes' do
internal_note = create(:note, confidential: true )
todo = create(:todo, note: internal_note)
create(:todo)
expect(described_class.for_internal_notes).to contain_exactly(todo)
end
end
end

View File

@ -2634,6 +2634,14 @@ RSpec.describe User do
expect(described_class.find_by_any_email(private_email, confirmed: true)).to eq(user)
end
it 'finds user through private commit email when user is unconfirmed' do
user = create(:user, :unconfirmed)
private_email = user.private_commit_email
expect(described_class.find_by_any_email(private_email)).to eq(user)
expect(described_class.find_by_any_email(private_email, confirmed: true)).to eq(user)
end
it 'finds by primary email' do
user = create(:user, email: 'foo@example.com')
@ -2641,6 +2649,13 @@ RSpec.describe User do
expect(described_class.find_by_any_email(user.email, confirmed: true)).to eq user
end
it 'finds by primary email when user is unconfirmed according to confirmed argument' do
user = create(:user, :unconfirmed, email: 'foo@example.com')
expect(described_class.find_by_any_email(user.email)).to eq user
expect(described_class.find_by_any_email(user.email, confirmed: true)).to be_nil
end
it 'finds by uppercased email' do
user = create(:user, email: 'foo@example.com')
@ -2649,35 +2664,47 @@ RSpec.describe User do
end
context 'finds by secondary email' do
let(:user) { email.user }
context 'when primary email is confirmed' do
let(:user) { email.user }
context 'primary email confirmed' do
context 'secondary email confirmed' do
context 'when secondary email is confirmed' do
let!(:email) { create(:email, :confirmed, email: 'foo@example.com') }
it 'finds user respecting the confirmed flag' do
it 'finds user' do
expect(described_class.find_by_any_email(email.email)).to eq user
expect(described_class.find_by_any_email(email.email, confirmed: true)).to eq user
end
end
context 'secondary email not confirmed' do
context 'when secondary email is unconfirmed' do
let!(:email) { create(:email, email: 'foo@example.com') }
it 'finds user respecting the confirmed flag' do
expect(described_class.find_by_any_email(email.email)).to eq user
it 'does not find user' do
expect(described_class.find_by_any_email(email.email)).to be_nil
expect(described_class.find_by_any_email(email.email, confirmed: true)).to be_nil
end
end
end
context 'primary email not confirmed' do
context 'when primary email is unconfirmed' do
let(:user) { create(:user, :unconfirmed) }
let!(:email) { create(:email, :confirmed, user: user, email: 'foo@example.com') }
it 'finds user respecting the confirmed flag' do
expect(described_class.find_by_any_email(email.email)).to eq user
expect(described_class.find_by_any_email(email.email, confirmed: true)).to be_nil
context 'when secondary email is confirmed' do
let!(:email) { create(:email, :confirmed, user: user, email: 'foo@example.com') }
it 'finds user according to confirmed argument' do
expect(described_class.find_by_any_email(email.email)).to eq user
expect(described_class.find_by_any_email(email.email, confirmed: true)).to be_nil
end
end
context 'when secondary email is unconfirmed' do
let!(:email) { create(:email, user: user, email: 'foo@example.com') }
it 'does not find user' do
expect(described_class.find_by_any_email(email.email)).to be_nil
expect(described_class.find_by_any_email(email.email, confirmed: true)).to be_nil
end
end
end
end
@ -2685,13 +2712,6 @@ RSpec.describe User do
it 'returns nil when nothing found' do
expect(described_class.find_by_any_email('')).to be_nil
end
it 'returns nil when user is not confirmed' do
user = create(:user, :unconfirmed, email: 'foo@example.com')
expect(described_class.find_by_any_email(user.email, confirmed: false)).to eq(user)
expect(described_class.find_by_any_email(user.email, confirmed: true)).to be_nil
end
end
describe '.by_any_email' do
@ -2700,32 +2720,99 @@ RSpec.describe User do
.to be_a_kind_of(ActiveRecord::Relation)
end
it 'returns a relation of users' do
user = create(:user)
expect(described_class.by_any_email(user.email)).to eq([user])
it 'returns empty relation of users when nothing found' do
expect(described_class.by_any_email('')).to be_empty
end
it 'returns a relation of users for confirmed users' do
it 'returns a relation of users for confirmed primary emails' do
user = create(:user)
expect(described_class.by_any_email(user.email, confirmed: true)).to eq([user])
expect(described_class.by_any_email(user.email)).to match_array([user])
expect(described_class.by_any_email(user.email, confirmed: true)).to match_array([user])
end
it 'finds user through a private commit email' do
it 'returns a relation of users for unconfirmed primary emails according to confirmed argument' do
user = create(:user, :unconfirmed)
expect(described_class.by_any_email(user.email)).to match_array([user])
expect(described_class.by_any_email(user.email, confirmed: true)).to be_empty
end
it 'finds users through private commit emails' do
user = create(:user)
private_email = user.private_commit_email
expect(described_class.by_any_email(private_email)).to eq([user])
expect(described_class.by_any_email(private_email, confirmed: true)).to eq([user])
expect(described_class.by_any_email(private_email)).to match_array([user])
expect(described_class.by_any_email(private_email, confirmed: true)).to match_array([user])
end
it 'finds unconfirmed users through private commit emails' do
user = create(:user, :unconfirmed)
private_email = user.private_commit_email
expect(described_class.by_any_email(private_email)).to match_array([user])
expect(described_class.by_any_email(private_email, confirmed: true)).to match_array([user])
end
it 'finds user through a private commit email in an array' do
user = create(:user)
private_email = user.private_commit_email
expect(described_class.by_any_email([private_email])).to eq([user])
expect(described_class.by_any_email([private_email], confirmed: true)).to eq([user])
expect(described_class.by_any_email([private_email])).to match_array([user])
expect(described_class.by_any_email([private_email], confirmed: true)).to match_array([user])
end
it 'finds by uppercased email' do
user = create(:user, email: 'foo@example.com')
expect(described_class.by_any_email(user.email.upcase)).to match_array([user])
expect(described_class.by_any_email(user.email.upcase, confirmed: true)).to match_array([user])
end
context 'finds by secondary email' do
context 'when primary email is confirmed' do
let(:user) { email.user }
context 'when secondary email is confirmed' do
let!(:email) { create(:email, :confirmed, email: 'foo@example.com') }
it 'finds user' do
expect(described_class.by_any_email(email.email)).to match_array([user])
expect(described_class.by_any_email(email.email, confirmed: true)).to match_array([user])
end
end
context 'when secondary email is unconfirmed' do
let!(:email) { create(:email, email: 'foo@example.com') }
it 'does not find user' do
expect(described_class.by_any_email(email.email)).to be_empty
expect(described_class.by_any_email(email.email, confirmed: true)).to be_empty
end
end
end
context 'when primary email is unconfirmed' do
let(:user) { create(:user, :unconfirmed) }
context 'when secondary email is confirmed' do
let!(:email) { create(:email, :confirmed, user: user, email: 'foo@example.com') }
it 'finds user according to confirmed argument' do
expect(described_class.by_any_email(email.email)).to match_array([user])
expect(described_class.by_any_email(email.email, confirmed: true)).to be_empty
end
end
context 'when secondary email is unconfirmed' do
let!(:email) { create(:email, user: user, email: 'foo@example.com') }
it 'does not find user' do
expect(described_class.by_any_email(email.email)).to be_empty
expect(described_class.by_any_email(email.email, confirmed: true)).to be_empty
end
end
end
end
end
@ -2739,7 +2826,10 @@ RSpec.describe User do
let_it_be(:user2) { create(:user, name: 'user name', username: 'username', email: 'someemail@example.com') }
let_it_be(:user3) { create(:user, name: 'us', username: 'se', email: 'foo@example.com') }
let_it_be(:email) { create(:email, user: user, email: 'alias@example.com') }
let_it_be(:unconfirmed_user) { create(:user, :unconfirmed, name: 'not verified', username: 'notverified') }
let_it_be(:unconfirmed_secondary_email) { create(:email, user: user, email: 'alias@example.com') }
let_it_be(:confirmed_secondary_email) { create(:email, :confirmed, user: user, email: 'alias2@example.com') }
describe 'name user and email relative ordering' do
let_it_be(:named_alexander) { create(:user, name: 'Alexander Person', username: 'abcd', email: 'abcd@example.com') }
@ -2797,16 +2887,25 @@ RSpec.describe User do
it 'does not return users with a matching private email' do
expect(described_class.search(user.email)).to be_empty
expect(described_class.search(email.email)).to be_empty
expect(described_class.search(unconfirmed_secondary_email.email)).to be_empty
expect(described_class.search(confirmed_secondary_email.email)).to be_empty
end
context 'with private emails search' do
it 'returns users with matching private email' do
it 'returns users with matching private primary email' do
expect(described_class.search(user.email, with_private_emails: true)).to match_array([user])
end
it 'returns users with matching private secondary email' do
expect(described_class.search(email.email, with_private_emails: true)).to match_array([user])
it 'returns users with matching private unconfirmed primary email' do
expect(described_class.search(unconfirmed_user.email, with_private_emails: true)).to match_array([unconfirmed_user])
end
it 'returns users with matching private confirmed secondary email' do
expect(described_class.search(confirmed_secondary_email.email, with_private_emails: true)).to match_array([user])
end
it 'does not return users with matching private unconfirmed secondary email' do
expect(described_class.search(unconfirmed_secondary_email.email, with_private_emails: true)).to be_empty
end
end
end
@ -3049,47 +3148,108 @@ RSpec.describe User do
describe '#accept_pending_invitations!' do
let(:user) { create(:user, email: 'user@email.com') }
let(:confirmed_secondary_email) { create(:email, :confirmed, email: 'confirmedsecondary@example.com', user: user) }
let(:unconfirmed_secondary_email) { create(:email, email: 'unconfirmedsecondary@example.com', user: user) }
let!(:project_member_invite) { create(:project_member, :invited, invite_email: user.email) }
let!(:group_member_invite) { create(:group_member, :invited, invite_email: user.email) }
let!(:external_project_member_invite) { create(:project_member, :invited, invite_email: 'external@email.com') }
let!(:external_group_member_invite) { create(:group_member, :invited, invite_email: 'external@email.com') }
let!(:project_member_invite_via_confirmed_secondary_email) { create(:project_member, :invited, invite_email: confirmed_secondary_email.email) }
let!(:group_member_invite_via_confirmed_secondary_email) { create(:group_member, :invited, invite_email: confirmed_secondary_email.email) }
let!(:project_member_invite_via_unconfirmed_secondary_email) { create(:project_member, :invited, invite_email: unconfirmed_secondary_email.email) }
let!(:group_member_invite_via_unconfirmed_secondary_email) { create(:group_member, :invited, invite_email: unconfirmed_secondary_email.email) }
it 'accepts all the user members pending invitations and returns the accepted_members' do
accepted_members = user.accept_pending_invitations!
expect(accepted_members).to match_array([project_member_invite, group_member_invite])
expect(accepted_members).to match_array(
[
project_member_invite,
group_member_invite,
project_member_invite_via_confirmed_secondary_email,
group_member_invite_via_confirmed_secondary_email
]
)
expect(group_member_invite.reload).not_to be_invite
expect(project_member_invite.reload).not_to be_invite
expect(external_project_member_invite.reload).to be_invite
expect(external_group_member_invite.reload).to be_invite
expect(project_member_invite_via_confirmed_secondary_email.reload).not_to be_invite
expect(group_member_invite_via_confirmed_secondary_email.reload).not_to be_invite
expect(project_member_invite_via_unconfirmed_secondary_email.reload).to be_invite
expect(group_member_invite_via_unconfirmed_secondary_email.reload).to be_invite
end
end
describe '#all_emails' do
let(:user) { create(:user) }
let!(:email_confirmed) { create :email, user: user, confirmed_at: Time.current }
let!(:email_unconfirmed) { create :email, user: user }
let!(:unconfirmed_secondary_email) { create(:email, user: user) }
let!(:confirmed_secondary_email) { create(:email, :confirmed, user: user) }
it 'returns all emails' do
expect(user.all_emails).to contain_exactly(
user.email,
user.private_commit_email,
confirmed_secondary_email.email
)
end
context 'when the primary email is confirmed' do
it 'includes the primary email' do
expect(user.all_emails).to include(user.email)
end
end
context 'when the primary email is unconfirmed' do
let!(:user) { create(:user, :unconfirmed) }
it 'includes the primary email' do
expect(user.all_emails).to include(user.email)
end
end
context 'when the primary email is temp email for oauth' do
let!(:user) { create(:omniauth_user, :unconfirmed, email: 'temp-email-for-oauth-user@gitlab.localhost') }
it 'does not include the primary email' do
expect(user.all_emails).not_to include(user.email)
end
end
context 'when `include_private_email` is true' do
it 'returns all emails' do
expect(user.reload.all_emails).to contain_exactly(
user.email,
user.private_commit_email,
email_unconfirmed.email,
email_confirmed.email
)
it 'includes the private commit email' do
expect(user.all_emails).to include(user.private_commit_email)
end
end
context 'when `include_private_email` is false' do
it 'does not include the private commit email' do
expect(user.reload.all_emails(include_private_email: false)).to contain_exactly(
user.email,
email_unconfirmed.email,
email_confirmed.email
expect(user.all_emails(include_private_email: false)).not_to include(
user.private_commit_email
)
end
end
context 'when the secondary email is confirmed' do
it 'includes the secondary email' do
expect(user.all_emails).to include(confirmed_secondary_email.email)
end
end
context 'when the secondary email is unconfirmed' do
it 'does not include the secondary email' do
expect(user.all_emails).not_to include(unconfirmed_secondary_email.email)
end
end
end
describe '#verified_emails' do

View File

@ -7,6 +7,7 @@ RSpec.describe API::Invitations do
let_it_be(:developer) { create(:user) }
let_it_be(:access_requester) { create(:user) }
let_it_be(:stranger) { create(:user) }
let_it_be(:unconfirmed_stranger) { create(:user, :unconfirmed) }
let(:email) { 'email1@example.com' }
let(:email2) { 'email2@example.com' }
@ -78,6 +79,46 @@ RSpec.describe API::Invitations do
end.to change { source.members.invite.count }.by(1)
end
it 'adds a new member by confirmed primary email' do
expect do
post invitations_url(source, maintainer),
params: { email: stranger.email, access_level: Member::DEVELOPER }
expect(response).to have_gitlab_http_status(:created)
end.to change { source.members.non_invite.count }.by(1)
end
it 'adds a new member by unconfirmed primary email' do
expect do
post invitations_url(source, maintainer),
params: { email: unconfirmed_stranger.email, access_level: Member::DEVELOPER }
expect(response).to have_gitlab_http_status(:created)
end.to change { source.members.non_invite.count }.by(1)
end
it 'adds a new member by confirmed secondary email' do
secondary_email = create(:email, :confirmed, email: 'secondary@example.com', user: stranger)
expect do
post invitations_url(source, maintainer),
params: { email: secondary_email.email, access_level: Member::DEVELOPER }
expect(response).to have_gitlab_http_status(:created)
end.to change { source.members.non_invite.count }.by(1)
end
it 'adds a new member as an invite for unconfirmed secondary email' do
secondary_email = create(:email, email: 'secondary@example.com', user: stranger)
expect do
post invitations_url(source, maintainer),
params: { email: secondary_email.email, access_level: Member::DEVELOPER }
expect(response).to have_gitlab_http_status(:created)
end.to change { source.members.invite.count }.by(1).and change { source.members.non_invite.count }.by(0)
end
it 'adds a new member by user_id' do
expect do
post invitations_url(source, maintainer),

View File

@ -25,6 +25,40 @@ RSpec.describe 'OAuth tokens' do
end
end
context 'when 2FA enforced' do
let_it_be(:user) { create(:user, otp_grace_period_started_at: 1.day.ago) }
before do
stub_application_setting(require_two_factor_authentication: true)
end
context 'when grace period expired' do
before do
stub_application_setting(two_factor_grace_period: 0)
end
it 'does not create an access token' do
request_oauth_token(user, client_basic_auth_header(client))
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('invalid_grant')
end
end
context 'when grace period is not expired' do
before do
stub_application_setting(two_factor_grace_period: 72)
end
it 'creates an access token' do
request_oauth_token(user, client_basic_auth_header(client))
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['access_token']).not_to be_nil
end
end
end
context 'when user does not have 2FA enabled' do
context 'when no client credentials provided' do
it 'creates an access token' do

View File

@ -1198,6 +1198,81 @@ RSpec.describe API::Users do
end
end
context 'when user with a primary email exists' do
context 'when the primary email is confirmed' do
let!(:confirmed_user) { create(:user, email: 'foo@example.com') }
it 'returns 409 conflict error' do
expect do
post api('/users', admin),
params: {
name: 'foo',
email: confirmed_user.email,
password: 'password',
username: 'TEST'
}
end.to change { User.count }.by(0)
expect(response).to have_gitlab_http_status(:conflict)
expect(json_response['message']).to eq('Email has already been taken')
end
end
context 'when the primary email is unconfirmed' do
let!(:unconfirmed_user) { create(:user, :unconfirmed, email: 'foo@example.com') }
it 'returns 409 conflict error' do
expect do
post api('/users', admin),
params: {
name: 'foo',
email: unconfirmed_user.email,
password: 'password',
username: 'TEST'
}
end.to change { User.count }.by(0)
expect(response).to have_gitlab_http_status(:conflict)
expect(json_response['message']).to eq('Email has already been taken')
end
end
end
context 'when user with a secondary email exists' do
context 'when the secondary email is confirmed' do
let!(:email) { create(:email, :confirmed, email: 'foo@example.com') }
it 'returns 409 conflict error' do
expect do
post api('/users', admin),
params: {
name: 'foo',
email: email.email,
password: 'password',
username: 'TEST'
}
end.to change { User.count }.by(0)
expect(response).to have_gitlab_http_status(:conflict)
expect(json_response['message']).to eq('Email has already been taken')
end
end
context 'when the secondary email is unconfirmed' do
let!(:email) { create(:email, email: 'foo@example.com') }
it 'does not create user' do
expect do
post api('/users', admin),
params: {
name: 'foo',
email: email.email,
password: 'password',
username: 'TEST'
}
end.to change { User.count }.by(0)
expect(response).to have_gitlab_http_status(:bad_request)
end
end
end
context "scopes" do
let(:user) { admin }
let(:path) { '/users' }
@ -1554,6 +1629,54 @@ RSpec.describe API::Users do
expect(@user.reload.username).to eq(@user.username)
end
end
context 'when user with a primary email exists' do
context 'when the primary email is confirmed' do
let!(:confirmed_user) { create(:user, email: 'foo@example.com') }
it 'returns 409 conflict error' do
put api("/users/#{user.id}", admin), params: { email: confirmed_user.email }
expect(response).to have_gitlab_http_status(:conflict)
expect(user.reload.email).not_to eq(confirmed_user.email)
end
end
context 'when the primary email is unconfirmed' do
let!(:unconfirmed_user) { create(:user, :unconfirmed, email: 'foo@example.com') }
it 'returns 409 conflict error' do
put api("/users/#{user.id}", admin), params: { email: unconfirmed_user.email }
expect(response).to have_gitlab_http_status(:conflict)
expect(user.reload.email).not_to eq(unconfirmed_user.email)
end
end
end
context 'when user with a secondary email exists' do
context 'when the secondary email is confirmed' do
let!(:email) { create(:email, :confirmed, email: 'foo@example.com') }
it 'returns 409 conflict error' do
put api("/users/#{user.id}", admin), params: { email: email.email }
expect(response).to have_gitlab_http_status(:conflict)
expect(user.reload.email).not_to eq(email.email)
end
end
context 'when the secondary email is unconfirmed' do
let!(:email) { create(:email, email: 'foo@example.com') }
it 'does not update email' do
put api("/users/#{user.id}", admin), params: { email: email.email }
expect(response).to have_gitlab_http_status(:bad_request)
expect(user.reload.email).not_to eq(email.email)
end
end
end
end
describe "PUT /user/:id/credit_card_validation" do
@ -2118,6 +2241,50 @@ RSpec.describe API::Users do
expect(json_response['confirmed_at']).not_to be_nil
end
context 'when user with a primary email exists' do
context 'when the primary email is confirmed' do
let!(:confirmed_user) { create(:user, email: 'foo@example.com') }
it 'returns 400 error' do
post api("/users/#{user.id}/emails", admin), params: { email: confirmed_user.email }
expect(response).to have_gitlab_http_status(:bad_request)
end
end
context 'when the primary email is unconfirmed' do
let!(:unconfirmed_user) { create(:user, :unconfirmed, email: 'foo@example.com') }
it 'returns 400 error' do
post api("/users/#{user.id}/emails", admin), params: { email: unconfirmed_user.email }
expect(response).to have_gitlab_http_status(:bad_request)
end
end
end
context 'when user with a secondary email exists' do
context 'when the secondary email is confirmed' do
let!(:email) { create(:email, :confirmed, email: 'foo@example.com') }
it 'returns 400 error' do
post api("/users/#{user.id}/emails", admin), params: { email: email.email }
expect(response).to have_gitlab_http_status(:bad_request)
end
end
context 'when the secondary email is unconfirmed' do
let!(:email) { create(:email, email: 'foo@example.com') }
it 'returns 400 error' do
post api("/users/#{user.id}/emails", admin), params: { email: email.email }
expect(response).to have_gitlab_http_status(:bad_request)
end
end
end
end
describe 'GET /user/:id/emails' do

View File

@ -170,6 +170,24 @@ RSpec.describe BuildDetailsEntity do
expect(message).to include('could not retrieve the needed artifacts.')
end
end
context 'when dependency contains invalid dependency names' do
invalid_name = 'XSS<a href=# data-disable-with="<img src=x onerror=alert(document.domain)>">'
let!(:test1) { create(:ci_build, :success, :expired, pipeline: pipeline, name: invalid_name, stage_idx: 0) }
let!(:build) { create(:ci_build, :pending, pipeline: pipeline, stage_idx: 1, options: { dependencies: [invalid_name] }) }
before do
build.pipeline.unlocked!
build.drop!(:missing_dependency_failure)
end
it { is_expected.to include(failure_reason: 'missing_dependency_failure') }
it 'escapes the invalid dependency names' do
escaped_name = html_escape(invalid_name)
expect(message).to include(escaped_name)
end
end
end
context 'when a build has environment with latest deployment' do

View File

@ -50,12 +50,8 @@ RSpec.describe Grafana::ProxyService do
describe '#execute' do
subject(:result) { service.execute }
context 'when grafana integration is not configured' do
before do
allow(project).to receive(:grafana_integration).and_return(nil)
end
it 'returns error' do
shared_examples 'missing proxy support' do
it 'returns API not supported error' do
expect(result).to eq(
status: :error,
message: 'Proxy support for this API is not available currently'
@ -63,6 +59,40 @@ RSpec.describe Grafana::ProxyService do
end
end
context 'with unsupported proxy path' do
where(:proxy_path) do
%w[
/api/vl/query_range
api/vl/query_range/
api/vl/labels
api/v2/query_range
../../../org/users
]
end
with_them do
include_examples 'missing proxy support'
end
end
context 'with unsupported datasource_id' do
where(:datasource_id) do
['', '-1', '1str', 'str1', '../../1', '1/../..', "1\n1"]
end
with_them do
include_examples 'missing proxy support'
end
end
context 'when grafana integration is not configured' do
before do
allow(project).to receive(:grafana_integration).and_return(nil)
end
include_examples 'missing proxy support'
end
context 'with caching', :use_clean_rails_memory_store_caching do
context 'when value not present in cache' do
it 'returns nil' do

View File

@ -35,6 +35,20 @@ RSpec.describe Groups::DestroyService do
it { expect(NotificationSetting.unscoped.all).not_to include(notification_setting) }
end
context 'bot tokens', :sidekiq_might_not_need_inline do
it 'removes group bot', :aggregate_failures do
bot = create(:user, :project_bot)
group.add_developer(bot)
token = create(:personal_access_token, user: bot)
destroy_group(group, user, async)
expect(PersonalAccessToken.find_by(id: token.id)).to be_nil
expect(User.find_by(id: bot.id)).to be_nil
expect(User.find_by(id: user.id)).not_to be_nil
end
end
context 'mattermost team', :sidekiq_might_not_need_inline do
let!(:chat_team) { create(:chat_team, namespace: group) }

View File

@ -242,6 +242,69 @@ RSpec.describe Groups::UpdateService do
end
end
context 'when user is not group owner' do
context 'when group is private' do
before do
private_group.add_maintainer(user)
end
it 'does not update the group to public' do
result = described_class.new(private_group, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC).execute
expect(result).to eq(false)
expect(private_group.errors.count).to eq(1)
expect(private_group).to be_private
end
it 'does not update the group to public with tricky value' do
result = described_class.new(private_group, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC.to_s + 'r').execute
expect(result).to eq(false)
expect(private_group.errors.count).to eq(1)
expect(private_group).to be_private
end
end
context 'when group is public' do
before do
public_group.add_maintainer(user)
end
it 'does not update the group to private' do
result = described_class.new(public_group, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE).execute
expect(result).to eq(false)
expect(public_group.errors.count).to eq(1)
expect(public_group).to be_public
end
it 'does not update the group to private with invalid string value' do
result = described_class.new(public_group, user, visibility_level: 'invalid').execute
expect(result).to eq(false)
expect(public_group.errors.count).to eq(1)
expect(public_group).to be_public
end
it 'does not update the group to private with valid string value' do
result = described_class.new(public_group, user, visibility_level: 'private').execute
expect(result).to eq(false)
expect(public_group.errors.count).to eq(1)
expect(public_group).to be_public
end
# See https://gitlab.com/gitlab-org/gitlab/-/issues/359910
it 'does not update the group to private because of Active Record typecasting' do
result = described_class.new(public_group, user, visibility_level: 'public').execute
expect(result).to eq(true)
expect(public_group.errors.count).to eq(0)
expect(public_group).to be_public
end
end
end
context 'when updating #emails_disabled' do
let(:service) { described_class.new(internal_group, user, emails_disabled: true) }

View File

@ -30,8 +30,8 @@ RSpec.describe Members::InviteService, :aggregate_failures, :clean_gitlab_redis_
end
end
context 'when email belongs to an existing user as a secondary email' do
let(:secondary_email) { create(:email, email: 'secondary@example.com', user: project_user) }
context 'when email belongs to an existing user as a confirmed secondary email' do
let(:secondary_email) { create(:email, :confirmed, email: 'secondary@example.com', user: project_user) }
let(:params) { { email: secondary_email.email } }
it 'adds an existing user to members', :aggregate_failures do
@ -42,6 +42,18 @@ RSpec.describe Members::InviteService, :aggregate_failures, :clean_gitlab_redis_
end
end
context 'when email belongs to an existing user as an unconfirmed secondary email' do
let(:unconfirmed_secondary_email) { create(:email, email: 'secondary@example.com', user: project_user) }
let(:params) { { email: unconfirmed_secondary_email.email } }
it 'does not link the email with any user and successfully creates a member as an invite for that email' do
expect_to_create_members(count: 1)
expect(result[:status]).to eq(:success)
expect(project.users).not_to include project_user
expect(project.members.last).to be_invite
end
end
context 'when invites are passed as array' do
context 'with emails' do
let(:params) { { email: %w[email@example.org email2@example.org] } }
@ -291,6 +303,19 @@ RSpec.describe Members::InviteService, :aggregate_failures, :clean_gitlab_redis_
end
end
context 'with unconfirmed primary email' do
let_it_be(:unconfirmed_user) { create(:user, :unconfirmed) }
let(:params) { { email: unconfirmed_user.email } }
it 'adds an existing user to members' do
expect_to_create_members(count: 1)
expect(result[:status]).to eq(:success)
expect(project.users).to include unconfirmed_user
expect(project.members.last).not_to be_invite
end
end
context 'with user_id' do
let(:params) { { user_id: project_user.id } }
@ -375,8 +400,8 @@ RSpec.describe Members::InviteService, :aggregate_failures, :clean_gitlab_redis_
expect(existing_member.reset.access_level).to eq ProjectMember::MAINTAINER
end
context 'when email belongs to an existing user as a secondary email' do
let(:secondary_email) { create(:email, email: 'secondary@example.com', user: existing_member.user) }
context 'when email belongs to an existing user as a confirmed secondary email' do
let(:secondary_email) { create(:email, :confirmed, email: 'secondary@example.com', user: existing_member.user) }
let(:params) { { email: "#{secondary_email.email}" } }
it 'allows re-invite to an already invited email' do

View File

@ -306,6 +306,11 @@ RSpec.describe Projects::Operations::UpdateService do
let(:params) do
{
error_tracking_setting_attributes: {
api_host: 'https://sentrytest.gitlab.com/',
project: {
slug: 'sentry-project',
organization_slug: 'sentry-org'
},
enabled: false,
token: '*' * 8
}
@ -313,7 +318,7 @@ RSpec.describe Projects::Operations::UpdateService do
end
before do
create(:project_error_tracking_setting, project: project, token: 'token')
create(:project_error_tracking_setting, project: project, token: 'token', api_url: 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project/')
end
it 'does not update token' do

View File

@ -120,6 +120,65 @@ RSpec.describe Projects::UpdateService do
end
end
context 'when user is not project owner' do
let_it_be(:maintainer) { create(:user) }
before do
project.add_maintainer(maintainer)
end
context 'when project is private' do
it 'does not update the project to public' do
result = update_project(project, maintainer, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
expect(result).to eq({ status: :error, message: 'New visibility level not allowed!' })
expect(project).to be_private
end
it 'does not update the project to public with tricky value' do
result = update_project(project, maintainer, visibility_level: Gitlab::VisibilityLevel::PUBLIC.to_s + 'r')
expect(result).to eq({ status: :error, message: 'New visibility level not allowed!' })
expect(project).to be_private
end
end
context 'when project is public' do
before do
project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
end
it 'does not update the project to private' do
result = update_project(project, maintainer, visibility_level: Gitlab::VisibilityLevel::PRIVATE)
expect(result).to eq({ status: :error, message: 'New visibility level not allowed!' })
expect(project).to be_public
end
it 'does not update the project to private with invalid string value' do
result = update_project(project, maintainer, visibility_level: 'invalid')
expect(result).to eq({ status: :error, message: 'New visibility level not allowed!' })
expect(project).to be_public
end
it 'does not update the project to private with valid string value' do
result = update_project(project, maintainer, visibility_level: 'private')
expect(result).to eq({ status: :error, message: 'New visibility level not allowed!' })
expect(project).to be_public
end
# See https://gitlab.com/gitlab-org/gitlab/-/issues/359910
it 'does not update the project to private because of Active Record typecasting' do
result = update_project(project, maintainer, visibility_level: 'public')
expect(result).to eq({ status: :success })
expect(project).to be_public
end
end
end
context 'when updating shared runners' do
context 'can enable shared runners' do
let(:group) { create(:group, shared_runners_enabled: true) }

View File

@ -3,21 +3,24 @@
require 'spec_helper'
RSpec.describe Todos::Destroy::EntityLeaveService do
let_it_be(:user, reload: true) { create(:user) }
let_it_be(:user2, reload: true) { create(:user) }
let_it_be(:user, reload: true) { create(:user) }
let_it_be(:user2, reload: true) { create(:user) }
let_it_be_with_refind(:group) { create(:group, :private) }
let_it_be(:project) { create(:project, :private, group: group) }
let(:group) { create(:group, :private) }
let(:project) { create(:project, :private, group: group) }
let(:issue) { create(:issue, project: project) }
let(:issue_c) { create(:issue, project: project, confidential: true) }
let!(:todo_group_user) { create(:todo, user: user, group: group) }
let!(:todo_group_user2) { create(:todo, user: user2, group: group) }
let(:mr) { create(:merge_request, source_project: project) }
let!(:todo_mr_user) { create(:todo, user: user, target: mr, project: project) }
let!(:todo_issue_user) { create(:todo, user: user, target: issue, project: project) }
let!(:todo_issue_c_user) { create(:todo, user: user, target: issue_c, project: project) }
let(:issue) { create(:issue, project: project) }
let(:issue_c) { create(:issue, project: project, confidential: true) }
let!(:todo_group_user) { create(:todo, user: user, group: group) }
let!(:todo_group_user2) { create(:todo, user: user2, group: group) }
let(:mr) { create(:merge_request, source_project: project) }
let!(:todo_mr_user) { create(:todo, user: user, target: mr, project: project) }
let!(:todo_issue_user) { create(:todo, user: user, target: issue, project: project) }
let!(:todo_issue_c_user) { create(:todo, user: user, target: issue_c, project: project) }
let!(:todo_issue_c_user2) { create(:todo, user: user2, target: issue_c, project: project) }
let(:internal_note) { create(:note, noteable: issue, project: project, confidential: true ) }
let!(:todo_for_internal_note) do
create(:todo, user: user, target: issue, project: project, note: internal_note)
end
shared_examples 'using different access permissions' do
before do
@ -34,20 +37,28 @@ RSpec.describe Todos::Destroy::EntityLeaveService do
it { does_not_remove_any_todos }
end
shared_examples 'removes only confidential issues todos' do
it { removes_only_confidential_issues_todos }
shared_examples 'removes confidential issues and internal notes todos' do
it { removes_confidential_issues_and_internal_notes_todos }
end
shared_examples 'removes only internal notes todos' do
it { removes_only_internal_notes_todos }
end
def does_not_remove_any_todos
expect { subject }.not_to change { Todo.count }
end
def removes_only_confidential_issues_todos
expect { subject }.to change { Todo.count }.from(6).to(5)
def removes_only_internal_notes_todos
expect { subject }.to change { Todo.count }.from(7).to(6)
end
def removes_confidential_issues_and_merge_request_todos
expect { subject }.to change { Todo.count }.from(6).to(4)
def removes_confidential_issues_and_internal_notes_todos
expect { subject }.to change { Todo.count }.from(7).to(5)
end
def removes_confidential_issues_and_internal_notes_and_merge_request_todos
expect { subject }.to change { Todo.count }.from(7).to(4)
expect(user.todos).to match_array([todo_issue_user, todo_group_user])
end
@ -70,7 +81,7 @@ RSpec.describe Todos::Destroy::EntityLeaveService do
context 'when project is private' do
context 'when user is not a member of the project' do
it 'removes project todos for the provided user' do
expect { subject }.to change { Todo.count }.from(6).to(3)
expect { subject }.to change { Todo.count }.from(7).to(3)
expect(user.todos).to match_array([todo_group_user])
expect(user2.todos).to match_array([todo_issue_c_user2, todo_group_user2])
@ -81,11 +92,11 @@ RSpec.describe Todos::Destroy::EntityLeaveService do
where(:group_access, :project_access, :method_name) do
[
[nil, :reporter, :does_not_remove_any_todos],
[nil, :guest, :removes_confidential_issues_and_merge_request_todos],
[nil, :guest, :removes_confidential_issues_and_internal_notes_and_merge_request_todos],
[:reporter, nil, :does_not_remove_any_todos],
[:guest, nil, :removes_confidential_issues_and_merge_request_todos],
[:guest, nil, :removes_confidential_issues_and_internal_notes_and_merge_request_todos],
[:guest, :reporter, :does_not_remove_any_todos],
[:guest, :guest, :removes_confidential_issues_and_merge_request_todos]
[:guest, :guest, :removes_confidential_issues_and_internal_notes_and_merge_request_todos]
]
end
@ -97,11 +108,12 @@ RSpec.describe Todos::Destroy::EntityLeaveService do
# a private project in an internal/public group is valid
context 'when project is private in an internal/public group' do
let(:group) { create(:group, :internal) }
let_it_be(:group) { create(:group, :internal) }
let_it_be(:project) { create(:project, :private, group: group) }
context 'when user is not a member of the project' do
it 'removes project todos for the provided user' do
expect { subject }.to change { Todo.count }.from(6).to(3)
expect { subject }.to change { Todo.count }.from(7).to(3)
expect(user.todos).to match_array([todo_group_user])
expect(user2.todos).to match_array([todo_issue_c_user2, todo_group_user2])
@ -112,11 +124,11 @@ RSpec.describe Todos::Destroy::EntityLeaveService do
where(:group_access, :project_access, :method_name) do
[
[nil, :reporter, :does_not_remove_any_todos],
[nil, :guest, :removes_confidential_issues_and_merge_request_todos],
[nil, :guest, :removes_confidential_issues_and_internal_notes_and_merge_request_todos],
[:reporter, nil, :does_not_remove_any_todos],
[:guest, nil, :removes_confidential_issues_and_merge_request_todos],
[:guest, nil, :removes_confidential_issues_and_internal_notes_and_merge_request_todos],
[:guest, :reporter, :does_not_remove_any_todos],
[:guest, :guest, :removes_confidential_issues_and_merge_request_todos]
[:guest, :guest, :removes_confidential_issues_and_internal_notes_and_merge_request_todos]
]
end
@ -142,7 +154,7 @@ RSpec.describe Todos::Destroy::EntityLeaveService do
context 'confidential issues' do
context 'when a user is not an author of confidential issue' do
it_behaves_like 'removes only confidential issues todos'
it_behaves_like 'removes confidential issues and internal notes todos'
end
context 'when a user is an author of confidential issue' do
@ -150,7 +162,7 @@ RSpec.describe Todos::Destroy::EntityLeaveService do
issue_c.update!(author: user)
end
it_behaves_like 'does not remove any todos'
it_behaves_like 'removes only internal notes todos'
end
context 'when a user is an assignee of confidential issue' do
@ -158,18 +170,18 @@ RSpec.describe Todos::Destroy::EntityLeaveService do
issue_c.assignees << user
end
it_behaves_like 'does not remove any todos'
it_behaves_like 'removes only internal notes todos'
end
context 'access permissions' do
where(:group_access, :project_access, :method_name) do
[
[nil, :reporter, :does_not_remove_any_todos],
[nil, :guest, :removes_only_confidential_issues_todos],
[nil, :guest, :removes_confidential_issues_and_internal_notes_todos],
[:reporter, nil, :does_not_remove_any_todos],
[:guest, nil, :removes_only_confidential_issues_todos],
[:guest, nil, :removes_confidential_issues_and_internal_notes_todos],
[:guest, :reporter, :does_not_remove_any_todos],
[:guest, :guest, :removes_only_confidential_issues_todos]
[:guest, :guest, :removes_confidential_issues_and_internal_notes_todos]
]
end
@ -186,7 +198,7 @@ RSpec.describe Todos::Destroy::EntityLeaveService do
end
it 'removes only users issue todos' do
expect { subject }.to change { Todo.count }.from(6).to(5)
expect { subject }.to change { Todo.count }.from(7).to(5)
end
end
end
@ -199,7 +211,7 @@ RSpec.describe Todos::Destroy::EntityLeaveService do
context 'when group is private' do
context 'when a user leaves a group' do
it 'removes group and subproject todos for the user' do
expect { subject }.to change { Todo.count }.from(6).to(2)
expect { subject }.to change { Todo.count }.from(7).to(2)
expect(user.todos).to be_empty
expect(user2.todos).to match_array([todo_issue_c_user2, todo_group_user2])
@ -210,11 +222,11 @@ RSpec.describe Todos::Destroy::EntityLeaveService do
where(:group_access, :project_access, :method_name) do
[
[nil, :reporter, :does_not_remove_any_todos],
[nil, :guest, :removes_confidential_issues_and_merge_request_todos],
[nil, :guest, :removes_confidential_issues_and_internal_notes_and_merge_request_todos],
[:reporter, nil, :does_not_remove_any_todos],
[:guest, nil, :removes_confidential_issues_and_merge_request_todos],
[:guest, nil, :removes_confidential_issues_and_internal_notes_and_merge_request_todos],
[:guest, :reporter, :does_not_remove_any_todos],
[:guest, :guest, :removes_confidential_issues_and_merge_request_todos]
[:guest, :guest, :removes_confidential_issues_and_internal_notes_and_merge_request_todos]
]
end
@ -224,12 +236,12 @@ RSpec.describe Todos::Destroy::EntityLeaveService do
end
context 'with nested groups' do
let(:parent_group) { create(:group, :public) }
let(:parent_subgroup) { create(:group)}
let(:subgroup) { create(:group, :private, parent: group) }
let(:subgroup2) { create(:group, :private, parent: group) }
let(:subproject) { create(:project, group: subgroup) }
let(:subproject2) { create(:project, group: subgroup2) }
let_it_be_with_refind(:parent_group) { create(:group, :public) }
let_it_be_with_refind(:parent_subgroup) { create(:group) }
let_it_be(:subgroup) { create(:group, :private, parent: group) }
let_it_be(:subgroup2) { create(:group, :private, parent: group) }
let_it_be(:subproject) { create(:project, group: subgroup) }
let_it_be(:subproject2) { create(:project, group: subgroup2) }
let!(:todo_subproject_user) { create(:todo, user: user, project: subproject) }
let!(:todo_subproject2_user) { create(:todo, user: user, project: subproject2) }
@ -238,6 +250,10 @@ RSpec.describe Todos::Destroy::EntityLeaveService do
let!(:todo_subproject_user2) { create(:todo, user: user2, project: subproject) }
let!(:todo_subpgroup_user2) { create(:todo, user: user2, group: subgroup) }
let!(:todo_parent_group_user) { create(:todo, user: user, group: parent_group) }
let(:subproject_internal_note) { create(:note, noteable: issue, project: project, confidential: true ) }
let!(:todo_for_internal_subproject_note) do
create(:todo, user: user, target: issue, project: project, note: subproject_internal_note)
end
before do
group.update!(parent: parent_group)
@ -245,7 +261,7 @@ RSpec.describe Todos::Destroy::EntityLeaveService do
context 'when the user is not a member of any groups/projects' do
it 'removes todos for the user including subprojects todos' do
expect { subject }.to change { Todo.count }.from(13).to(5)
expect { subject }.to change { Todo.count }.from(15).to(5)
expect(user.todos).to eq([todo_parent_group_user])
expect(user2.todos)
@ -269,7 +285,7 @@ RSpec.describe Todos::Destroy::EntityLeaveService do
end
it 'does not remove group and subproject todos' do
expect { subject }.to change { Todo.count }.from(13).to(8)
expect { subject }.to change { Todo.count }.from(15).to(8)
expect(user.todos)
.to match_array(
@ -288,7 +304,7 @@ RSpec.describe Todos::Destroy::EntityLeaveService do
end
it 'does not remove subproject and group todos' do
expect { subject }.to change { Todo.count }.from(13).to(8)
expect { subject }.to change { Todo.count }.from(15).to(8)
expect(user.todos)
.to match_array(
@ -319,13 +335,13 @@ RSpec.describe Todos::Destroy::EntityLeaveService do
context 'access permissions' do
where(:group_access, :project_access, :method_name) do
[
[nil, nil, :removes_only_confidential_issues_todos],
[nil, nil, :removes_confidential_issues_and_internal_notes_todos],
[nil, :reporter, :does_not_remove_any_todos],
[nil, :guest, :removes_only_confidential_issues_todos],
[nil, :guest, :removes_confidential_issues_and_internal_notes_todos],
[:reporter, nil, :does_not_remove_any_todos],
[:guest, nil, :removes_only_confidential_issues_todos],
[:guest, nil, :removes_confidential_issues_and_internal_notes_todos],
[:guest, :reporter, :does_not_remove_any_todos],
[:guest, :guest, :removes_only_confidential_issues_todos]
[:guest, :guest, :removes_confidential_issues_and_internal_notes_todos]
]
end

View File

@ -66,6 +66,8 @@ Integration.available_integration_names.each do |integration|
hash.merge!(k => 'foo@bar.com')
elsif (integration == 'slack' || integration == 'mattermost') && k == :labels_to_be_notified_behavior
hash.merge!(k => "match_any")
elsif integration == 'campfire' && k = :room
hash.merge!(k => '1234')
else
hash.merge!(k => "someword")
end

View File

@ -903,42 +903,65 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
end
end
context 'filtering by crm contact' do
let_it_be(:contact1) { create(:contact, group: group) }
let_it_be(:contact2) { create(:contact, group: group) }
context 'crm filtering' do
let_it_be(:root_group) { create(:group) }
let_it_be(:group) { create(:group, parent: root_group) }
let_it_be(:project_crm) { create(:project, :public, group: group) }
let_it_be(:organization) { create(:organization, group: root_group) }
let_it_be(:contact1) { create(:contact, group: root_group, organization: organization) }
let_it_be(:contact2) { create(:contact, group: root_group, organization: organization) }
let_it_be(:contact1_item1) { create(factory, project: project1) }
let_it_be(:contact1_item2) { create(factory, project: project1) }
let_it_be(:contact2_item1) { create(factory, project: project1) }
let_it_be(:contact1_item1) { create(factory, project: project_crm) }
let_it_be(:contact1_item2) { create(factory, project: project_crm) }
let_it_be(:contact2_item1) { create(factory, project: project_crm) }
let_it_be(:item_no_contact) { create(factory, project: project_crm) }
let(:params) { { crm_contact_id: contact1.id } }
it 'returns for that contact' do
create(:issue_customer_relations_contact, issue: contact1_item1, contact: contact1)
create(:issue_customer_relations_contact, issue: contact1_item2, contact: contact1)
create(:issue_customer_relations_contact, issue: contact2_item1, contact: contact2)
expect(items).to contain_exactly(contact1_item1, contact1_item2)
let_it_be(:all_project_issues) do
[contact1_item1, contact1_item2, contact2_item1, item_no_contact]
end
end
context 'filtering by crm organization' do
let_it_be(:organization) { create(:organization, group: group) }
let_it_be(:contact1) { create(:contact, group: group, organization: organization) }
let_it_be(:contact2) { create(:contact, group: group, organization: organization) }
before do
create(:crm_settings, group: root_group, enabled: true)
let_it_be(:contact1_item1) { create(factory, project: project1) }
let_it_be(:contact1_item2) { create(factory, project: project1) }
let_it_be(:contact2_item1) { create(factory, project: project1) }
let(:params) { { crm_organization_id: organization.id } }
it 'returns for that contact' do
create(:issue_customer_relations_contact, issue: contact1_item1, contact: contact1)
create(:issue_customer_relations_contact, issue: contact1_item2, contact: contact1)
create(:issue_customer_relations_contact, issue: contact2_item1, contact: contact2)
end
expect(items).to contain_exactly(contact1_item1, contact1_item2, contact2_item1)
context 'filtering by crm contact' do
let(:params) { { project_id: project_crm.id, crm_contact_id: contact1.id } }
context 'when the user can read crm contacts' do
it 'returns for that contact' do
root_group.add_reporter(user)
expect(items).to contain_exactly(contact1_item1, contact1_item2)
end
end
context 'when the user can not read crm contacts' do
it 'does not filter by contact' do
expect(items).to match_array(all_project_issues)
end
end
end
context 'filtering by crm organization' do
let(:params) { { project_id: project_crm.id, crm_organization_id: organization.id } }
context 'when the user can read crm organization' do
it 'returns for that organization' do
root_group.add_reporter(user)
expect(items).to contain_exactly(contact1_item1, contact1_item2, contact2_item1)
end
end
context 'when the user can not read crm organization' do
it 'does not filter by organization' do
expect(items).to match_array(all_project_issues)
end
end
end
end