Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-08-31 21:10:43 +00:00
parent 34d6e7c91b
commit d9115c7779
73 changed files with 737 additions and 322 deletions

View File

@ -2,6 +2,20 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 14.2.2 (2021-08-31)
### Security (9 changes)
- [Prevent non-admins from configuring Jira connect app](gitlab-org/security/gitlab@1bc56361c9daa90accea65836d5a424168a2c544) ([merge request](gitlab-org/security/gitlab!1697))
- [Only create jira connect NS subscriptions for admins](gitlab-org/security/gitlab@c160da2cb32a5774fef149155cfd397981bf9173) ([merge request](gitlab-org/security/gitlab!1698))
- [Update apollo_upload_server dependency](gitlab-org/security/gitlab@5ef659b8c9a5a7338830171c62943d3b8bb16410) ([merge request](gitlab-org/security/gitlab!1699))
- [Ensure shared group members lose project access after group deletion](gitlab-org/security/gitlab@c94e934234a90f82e7fe291ed0f1d6a763b9a977) ([merge request](gitlab-org/security/gitlab!1683))
- [Update Import/Export to use public email when mapping users](gitlab-org/security/gitlab@13fb902c55c2dfe7ec2bf35f58a9cb3d93905d9a) ([merge request](gitlab-org/security/gitlab!1669)) **GitLab Enterprise Edition**
- [Require sign in for .keys endpoint on non-public instances](gitlab-org/security/gitlab@0979dd458e8fa0d4f5e184ef0b9ea042d79f6c14) ([merge request](gitlab-org/security/gitlab!1676))
- [Inherit user external status while creating project bots](gitlab-org/security/gitlab@93062909ffc093cb8f718a3ea3f2976292a9b9af) ([merge request](gitlab-org/security/gitlab!1675))
- [Escape issue reference and title for Jira issues](gitlab-org/security/gitlab@d25ef8599ec03ee80ef1bff7067b2269836400cf) ([merge request](gitlab-org/security/gitlab!1673)) **GitLab Enterprise Edition**
- [Fix stored XSS vulnerability in Datadog settings form](gitlab-org/security/gitlab@23b98dac7864992898992a153950247ac6ccb933) ([merge request](gitlab-org/security/gitlab!1670))
## 14.2.1 (2021-08-23) ## 14.2.1 (2021-08-23)
### Fixed (1 change) ### Fixed (1 change)
@ -563,6 +577,19 @@ entry.
- [Add helpful text to URL group validation and limit text](gitlab-org/gitlab@59a5a6266cb0d5434596170ffa36e4e74b8d2c2c) ([merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65369)) **GitLab Enterprise Edition** - [Add helpful text to URL group validation and limit text](gitlab-org/gitlab@59a5a6266cb0d5434596170ffa36e4e74b8d2c2c) ([merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65369)) **GitLab Enterprise Edition**
- [Refactor external storage admin area configuration UI and docs](gitlab-org/gitlab@497ba4fc8f4ec1d234c9f5f1ec5c69712b8c7cb3) ([merge request](gitlab-org/gitlab!66219)) - [Refactor external storage admin area configuration UI and docs](gitlab-org/gitlab@497ba4fc8f4ec1d234c9f5f1ec5c69712b8c7cb3) ([merge request](gitlab-org/gitlab!66219))
## 14.1.4 (2021-08-31)
### Security (8 changes)
- [Update apollo_upload_server dependency](gitlab-org/security/gitlab@34e7e3b7590fd76d0f618091551651e8065edfd2) ([merge request](gitlab-org/security/gitlab!1700))
- [Ensure shared group members lose project access after group deletion](gitlab-org/security/gitlab@4a7b8203776b719c06186c1b189a8cf21572fcd4) ([merge request](gitlab-org/security/gitlab!1684))
- [Fix stored XSS vulnerability in Datadog settings form](gitlab-org/security/gitlab@0906814af604e7fcab54a96bccadcba11207387d) ([merge request](gitlab-org/security/gitlab!1671))
- [Inherit user external status while creating project bots](gitlab-org/security/gitlab@d5a26c4145d917b5b49e207e03669d2b7e4ee617) ([merge request](gitlab-org/security/gitlab!1665))
- [Escape issue reference and title for Jira issues](gitlab-org/security/gitlab@4153444b76421ddf3a7fd21f1fc0500700a4e263) ([merge request](gitlab-org/security/gitlab!1662)) **GitLab Enterprise Edition**
- [Require sign in for .keys endpoint on non-public instances](gitlab-org/security/gitlab@b090b3f6dee6d21d93595c5e46e6c5c7fc30f1fb) ([merge request](gitlab-org/security/gitlab!1658))
- [Only create jira connect NS subscriptions for admins](gitlab-org/security/gitlab@3f2040c0e2c90f3fcafdbf0f86bd2591bd458dff) ([merge request](gitlab-org/security/gitlab!1648))
- [Prevent non-admins from configuring Jira connect app](gitlab-org/security/gitlab@fa864c0a2eaf450033f4c594cea07d9f24144cd6) ([merge request](gitlab-org/security/gitlab!1644))
## 14.1.3 (2021-08-17) ## 14.1.3 (2021-08-17)
### Fixed (2 changes) ### Fixed (2 changes)
@ -1181,6 +1208,20 @@ entry.
- [Remove diffs gradual load feature flag](gitlab-org/gitlab@027d7c4327b5b6205a84281239027273517bf81b) ([merge request](gitlab-org/gitlab!55478)) - [Remove diffs gradual load feature flag](gitlab-org/gitlab@027d7c4327b5b6205a84281239027273517bf81b) ([merge request](gitlab-org/gitlab!55478))
- [Remove partial index for Hashed Storage migration](gitlab-org/gitlab@3ed017a1023d7b0941a7606b69e6caee8d22f15c) ([merge request](gitlab-org/gitlab!62920)) - [Remove partial index for Hashed Storage migration](gitlab-org/gitlab@3ed017a1023d7b0941a7606b69e6caee8d22f15c) ([merge request](gitlab-org/gitlab!62920))
## 14.0.9 (2021-08-31)
### Security (9 changes)
- [Update apollo_upload_server dependency](gitlab-org/security/gitlab@ced741d93fa664f0c152f524949258bf969b7667) ([merge request](gitlab-org/security/gitlab!1701))
- [Ensure shared group members lose project access after group deletion](gitlab-org/security/gitlab@3a41f4e29c01188aaaf01ab5e3deec2a9eeed18e) ([merge request](gitlab-org/security/gitlab!1685))
- [Fix stored XSS vulnerability in Datadog settings form](gitlab-org/security/gitlab@269e5bf96b5e97c3b8e6f6b8b3f593d958de2ecb) ([merge request](gitlab-org/security/gitlab!1672))
- [Inherit user external status while creating project bots](gitlab-org/security/gitlab@5bae4e53bd4c363270b2fc2e308b81d2a2a388a6) ([merge request](gitlab-org/security/gitlab!1666))
- [Escape issue reference and title for Jira issues](gitlab-org/security/gitlab@0397f2b393d563559c49c39c0ba1d192d08a10d7) ([merge request](gitlab-org/security/gitlab!1663)) **GitLab Enterprise Edition**
- [Require sign in for .keys endpoint on non-public instances](gitlab-org/security/gitlab@13a7f6001f663b3745159fa37b518ba4a43355bd) ([merge request](gitlab-org/security/gitlab!1659))
- [Update Import/Export to use public email when mapping users](gitlab-org/security/gitlab@f3d1b800af55986cef83aeaf4df1312e3070f0c5) ([merge request](gitlab-org/security/gitlab!1654)) **GitLab Enterprise Edition**
- [Only create jira connect NS subscriptions for admins](gitlab-org/security/gitlab@34bdcd45f24eaa051702834fb6c3568e45721004) ([merge request](gitlab-org/security/gitlab!1647))
- [Prevent non-admins from configuring Jira connect app](gitlab-org/security/gitlab@4af692246224b1cd1e2fe3c6d0ac2613c0f8fe39) ([merge request](gitlab-org/security/gitlab!1643))
## 14.0.8 (2021-08-25) ## 14.0.8 (2021-08-25)
### Fixed (1 change) ### Fixed (1 change)

View File

@ -101,7 +101,7 @@ gem 'graphql', '~> 1.11.8'
# TODO: remove app/views/graphiql/rails/editors/show.html.erb when https://github.com/rmosolgo/graphiql-rails/pull/71 is released: # TODO: remove app/views/graphiql/rails/editors/show.html.erb when https://github.com/rmosolgo/graphiql-rails/pull/71 is released:
# https://gitlab.com/gitlab-org/gitlab/issues/31747 # https://gitlab.com/gitlab-org/gitlab/issues/31747
gem 'graphiql-rails', '~> 1.4.10' gem 'graphiql-rails', '~> 1.4.10'
gem 'apollo_upload_server', '~> 2.0.2' gem 'apollo_upload_server', '~> 2.1.0'
gem 'graphql-docs', '~> 1.6.0', group: [:development, :test] gem 'graphql-docs', '~> 1.6.0', group: [:development, :test]
gem 'graphlient', '~> 0.4.0' # Used by BulkImport feature (group::import) gem 'graphlient', '~> 0.4.0' # Used by BulkImport feature (group::import)

View File

@ -80,9 +80,9 @@ GEM
aes_key_wrap (1.1.0) aes_key_wrap (1.1.0)
akismet (3.0.0) akismet (3.0.0)
android_key_attestation (0.3.0) android_key_attestation (0.3.0)
apollo_upload_server (2.0.2) apollo_upload_server (2.1.0)
actionpack (>= 4.2)
graphql (>= 1.8) graphql (>= 1.8)
rails (>= 4.2)
asana (0.10.3) asana (0.10.3)
faraday (~> 1.0) faraday (~> 1.0)
faraday_middleware (~> 1.0) faraday_middleware (~> 1.0)
@ -1391,7 +1391,7 @@ DEPENDENCIES
acts-as-taggable-on (~> 7.0) acts-as-taggable-on (~> 7.0)
addressable (~> 2.8) addressable (~> 2.8)
akismet (~> 3.0) akismet (~> 3.0)
apollo_upload_server (~> 2.0.2) apollo_upload_server (~> 2.1.0)
asana (~> 0.10.3) asana (~> 0.10.3)
asciidoctor (~> 2.0.10) asciidoctor (~> 2.0.10)
asciidoctor-include-ext (~> 0.3.1) asciidoctor-include-ext (~> 0.3.1)

View File

@ -39,7 +39,7 @@ export default {
return this.value.map((i) => i.type); return this.value.map((i) => i.type);
}, },
tokens() { tokens() {
const tokens = [ return [
{ {
type: this.$options.userType, type: this.$options.userType,
icon: 'user', icon: 'user',
@ -77,20 +77,15 @@ export default {
token: PipelineStatusToken, token: PipelineStatusToken,
operators: OPERATOR_IS_ONLY, operators: OPERATOR_IS_ONLY,
}, },
]; {
if (gon.features.pipelineSourceFilter) {
tokens.push({
type: this.$options.sourceType, type: this.$options.sourceType,
icon: 'trigger-source', icon: 'trigger-source',
title: s__('Pipeline|Source'), title: s__('Pipeline|Source'),
unique: true, unique: true,
token: PipelineSourceToken, token: PipelineSourceToken,
operators: OPERATOR_IS_ONLY, operators: OPERATOR_IS_ONLY,
}); },
} ];
return tokens;
}, },
parsedParams() { parsedParams() {
return map(this.params, (val, key) => ({ return map(this.params, (val, key) => ({

View File

@ -47,7 +47,13 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController
postInstallPage: { postInstallPage: {
key: 'gitlab-configuration', key: 'gitlab-configuration',
name: { value: 'GitLab Configuration' }, name: { value: 'GitLab Configuration' },
url: relative_to_base_path(jira_connect_subscriptions_path) url: relative_to_base_path(jira_connect_subscriptions_path),
conditions: [
{
condition: 'user_is_admin',
invert: false
}
]
} }
} }

View File

@ -38,12 +38,30 @@ class JiraConnect::ApplicationController < ApplicationController
end end
def installation_from_jwt def installation_from_jwt
return unless auth_token
strong_memoize(:installation_from_jwt) do strong_memoize(:installation_from_jwt) do
next unless claims['iss']
JiraConnectInstallation.find_by_client_key(claims['iss'])
end
end
def claims
strong_memoize(:claims) do
next {} unless auth_token
# Decode without verification to get `client_key` in `iss` # Decode without verification to get `client_key` in `iss`
payload, _ = Atlassian::Jwt.decode(auth_token, nil, false) payload, _ = Atlassian::Jwt.decode(auth_token, nil, false)
JiraConnectInstallation.find_by_client_key(payload['iss']) payload
end
end
def jira_user
strong_memoize(:jira_user) do
next unless installation_from_jwt
next unless claims['sub']
# This only works for Jira Cloud installations.
installation_from_jwt.client.user_info(claims['sub'])
end end
end end

View File

@ -44,7 +44,9 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController
def destroy def destroy
subscription = current_jira_installation.subscriptions.find(params[:id]) subscription = current_jira_installation.subscriptions.find(params[:id])
if subscription.destroy if !jira_user&.site_admin?
render json: { error: 'forbidden' }, status: :forbidden
elsif subscription.destroy
render json: { success: true } render json: { success: true }
else else
render json: { error: subscription.errors.full_messages.join(', ') }, status: :unprocessable_entity render json: { error: subscription.errors.full_messages.join(', ') }, status: :unprocessable_entity
@ -54,7 +56,7 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController
private private
def create_service def create_service
JiraConnectSubscriptions::CreateService.new(current_jira_installation, current_user, namespace_path: params['namespace_path']) JiraConnectSubscriptions::CreateService.new(current_jira_installation, current_user, namespace_path: params['namespace_path'], jira_user: jira_user)
end end
def allow_rendering_in_iframe def allow_rendering_in_iframe

View File

@ -14,10 +14,6 @@ class Projects::PipelinesController < Projects::ApplicationController
before_action :authorize_update_pipeline!, only: [:retry, :cancel] before_action :authorize_update_pipeline!, only: [:retry, :cancel]
before_action :ensure_pipeline, only: [:show, :downloadable_artifacts] before_action :ensure_pipeline, only: [:show, :downloadable_artifacts]
before_action do
push_frontend_feature_flag(:pipeline_source_filter, project, type: :development, default_enabled: :yaml)
end
# Will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/225596 # Will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/225596
before_action :redirect_for_legacy_scope_filter, only: [:index], if: -> { request.format.html? } before_action :redirect_for_legacy_scope_filter, only: [:index], if: -> { request.format.html? }
@ -297,10 +293,7 @@ class Projects::PipelinesController < Projects::ApplicationController
end end
def index_params def index_params
permitted_params = [:scope, :username, :ref, :status] params.permit(:scope, :username, :ref, :status, :source)
permitted_params << :source if Feature.enabled?(:pipeline_source_filter, project, default_enabled: :yaml)
params.permit(*permitted_params)
end end
def enable_code_quality_walkthrough_experiment def enable_code_quality_walkthrough_experiment

View File

@ -20,7 +20,7 @@ class UsersController < ApplicationController
skip_before_action :authenticate_user! skip_before_action :authenticate_user!
prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) } prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) }
before_action :user, except: [:exists, :ssh_keys] before_action :user, except: [:exists]
before_action :authorize_read_user_profile!, before_action :authorize_read_user_profile!,
only: [:calendar, :calendar_activities, :groups, :projects, :contributed, :starred, :snippets, :followers, :following] only: [:calendar, :calendar_activities, :groups, :projects, :contributed, :starred, :snippets, :followers, :following]
@ -44,12 +44,7 @@ class UsersController < ApplicationController
# Get all keys of a user(params[:username]) in a text format # Get all keys of a user(params[:username]) in a text format
# Helpful for sysadmins to put in respective servers # Helpful for sysadmins to put in respective servers
#
# Uses `UserFinder` rather than `find_routable!` because this endpoint should
# be publicly available regardless of instance visibility settings.
def ssh_keys def ssh_keys
user = UserFinder.new(params[:username]).find_by_username
render plain: user.all_ssh_keys.join("\n") render plain: user.all_ssh_keys.join("\n")
end end

View File

@ -29,8 +29,7 @@ module Ci
items = by_username(items) items = by_username(items)
items = by_yaml_errors(items) items = by_yaml_errors(items)
items = by_updated_at(items) items = by_updated_at(items)
items = by_source(items)
items = by_source(items) if Feature.enabled?(:pipeline_source_filter, project, default_enabled: :yaml)
sort_items(items) sort_items(items)
end end

View File

@ -137,7 +137,7 @@ module IntegrationsHelper
def jira_issue_breadcrumb_link(issue_reference) def jira_issue_breadcrumb_link(issue_reference)
link_to '', { class: 'gl-display-flex gl-align-items-center gl-white-space-nowrap' } do link_to '', { class: 'gl-display-flex gl-align-items-center gl-white-space-nowrap' } do
icon = image_tag image_path('illustrations/logos/jira.svg'), width: 15, height: 15, class: 'gl-mr-2' icon = image_tag image_path('illustrations/logos/jira.svg'), width: 15, height: 15, class: 'gl-mr-2'
[icon, issue_reference].join.html_safe [icon, html_escape(issue_reference)].join.html_safe
end end
end end

View File

@ -169,7 +169,7 @@ module DesignManagement
@link_reference_pattern ||= begin @link_reference_pattern ||= begin
path_segment = %r{issues/#{Gitlab::Regex.issue}/designs} path_segment = %r{issues/#{Gitlab::Regex.issue}/designs}
ext = Regexp.new(Regexp.union(SAFE_IMAGE_EXT + DANGEROUS_IMAGE_EXT).source, Regexp::IGNORECASE) ext = Regexp.new(Regexp.union(SAFE_IMAGE_EXT + DANGEROUS_IMAGE_EXT).source, Regexp::IGNORECASE)
valid_char = %r{[^/\s]} # any char that is not a forward slash or whitespace valid_char = %r{[[:word:]\.\-\+]}
filename_pattern = %r{ filename_pattern = %r{
(?<url_filename> #{valid_char}+ \. #{ext}) (?<url_filename> #{valid_char}+ \. #{ext})
}x }x

View File

@ -8,7 +8,6 @@ module Integrations
DEFAULT_DOMAIN = 'datadoghq.com' DEFAULT_DOMAIN = 'datadoghq.com'
URL_TEMPLATE = 'https://webhooks-http-intake.logs.%{datadog_domain}/api/v2/webhook' URL_TEMPLATE = 'https://webhooks-http-intake.logs.%{datadog_domain}/api/v2/webhook'
URL_TEMPLATE_API_KEYS = 'https://app.%{datadog_domain}/account/settings#api'
URL_API_KEYS_DOCS = "https://docs.#{DEFAULT_DOMAIN}/account_management/api-app-keys/" URL_API_KEYS_DOCS = "https://docs.#{DEFAULT_DOMAIN}/account_management/api-app-keys/"
SUPPORTED_EVENTS = %w[ SUPPORTED_EVENTS = %w[
@ -90,7 +89,7 @@ module Integrations
help: ERB::Util.html_escape( help: ERB::Util.html_escape(
s_('DatadogIntegration|%{linkOpen}API key%{linkClose} used for authentication with Datadog.') s_('DatadogIntegration|%{linkOpen}API key%{linkClose} used for authentication with Datadog.')
) % { ) % {
linkOpen: '<a href="%s" target="_blank" rel="noopener noreferrer">'.html_safe % api_keys_url, linkOpen: %Q{<a href="#{URL_API_KEYS_DOCS}" target="_blank" rel="noopener noreferrer">}.html_safe,
linkClose: '</a>'.html_safe linkClose: '</a>'.html_safe
}, },
required: true required: true
@ -132,12 +131,6 @@ module Integrations
url.to_s url.to_s
end end
def api_keys_url
return URL_API_KEYS_DOCS unless datadog_site.presence
sprintf(URL_TEMPLATE_API_KEYS, datadog_domain: datadog_domain)
end
def execute(data) def execute(data)
object_kind = data[:object_kind] object_kind = data[:object_kind]
object_kind = 'job' if object_kind == 'build' object_kind = 'job' if object_kind == 'build'

View File

@ -20,4 +20,8 @@ class JiraConnectInstallation < ApplicationRecord
id: JiraConnectSubscription.for_project(project) id: JiraConnectSubscription.for_project(project)
}) })
} }
def client
Atlassian::JiraConnect::Client.new(base_url, shared_secret)
end
end end

View File

@ -29,14 +29,7 @@ module Groups
group.chat_team&.remove_mattermost_team(current_user) group.chat_team&.remove_mattermost_team(current_user)
# If any other groups are shared with the group that is being destroyed, user_ids_for_project_authorizations_refresh = obtain_user_ids_for_project_authorizations_refresh
# we should specifically trigger update of all project authorizations
# for users that are the direct members of this group.
# If not, the project authorization records of these users to projects within the shared groups
# will never be removed, causing inconsistencies with access permissions.
if any_other_groups_are_shared_with_this_group?
user_ids_for_project_authorizations_refresh = group.users_ids_of_direct_members
end
group.destroy group.destroy
@ -52,9 +45,33 @@ module Groups
private private
def any_other_groups_are_shared_with_this_group? def any_groups_shared_with_this_group?
group.shared_group_links.any? group.shared_group_links.any?
end end
def any_projects_shared_with_this_group?
group.project_group_links.any?
end
# Destroying a group automatically destroys all project authorizations directly
# associated with the group and descendents. However, project authorizations
# for projects and groups this group is shared with are not. Without a manual
# refresh, the project authorization records of these users to shared projects
# and projects within the shared groups will never be removed, causing
# inconsistencies with access permissions.
#
# This method retrieves the user IDs that need to be refreshed. If only
# groups are shared with this group, only direct members need to be refreshed.
# If projects are also shared with the group, direct members *and* shared
# members of other groups need to be refreshed.
# `Group#user_ids_for_project_authorizations` returns both direct and shared
# members' user IDs.
def obtain_user_ids_for_project_authorizations_refresh
return unless any_projects_shared_with_this_group? || any_groups_shared_with_this_group?
return group.user_ids_for_project_authorizations if any_projects_shared_with_this_group?
group.users_ids_of_direct_members
end
end end
end end

View File

@ -5,8 +5,11 @@ module JiraConnectSubscriptions
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
MERGE_REQUEST_SYNC_BATCH_SIZE = 20 MERGE_REQUEST_SYNC_BATCH_SIZE = 20
MERGE_REQUEST_SYNC_BATCH_DELAY = 1.minute.freeze MERGE_REQUEST_SYNC_BATCH_DELAY = 1.minute.freeze
NOT_SITE_ADMIN = 'The Jira user is not a site administrator.'
def execute def execute
return error(NOT_SITE_ADMIN, 403) unless can_administer_jira?
unless namespace && can?(current_user, :create_jira_connect_subscription, namespace) unless namespace && can?(current_user, :create_jira_connect_subscription, namespace)
return error('Invalid namespace. Please make sure you have sufficient permissions', 401) return error('Invalid namespace. Please make sure you have sufficient permissions', 401)
end end
@ -16,6 +19,10 @@ module JiraConnectSubscriptions
private private
def can_administer_jira?
@params[:jira_user]&.site_admin?
end
def create_subscription def create_subscription
subscription = JiraConnectSubscription.new(installation: jira_connect_installation, namespace: namespace) subscription = JiraConnectSubscription.new(installation: jira_connect_installation, namespace: namespace)

View File

@ -16,6 +16,8 @@ module ResourceAccessTokens
return error(user.errors.full_messages.to_sentence) unless user.persisted? return error(user.errors.full_messages.to_sentence) unless user.persisted?
user.update!(external: true) if current_user.external?
access_level = params[:access_level] || Gitlab::Access::MAINTAINER access_level = params[:access_level] || Gitlab::Access::MAINTAINER
member = create_membership(resource, user, access_level) member = create_membership(resource, user, access_level)

View File

@ -1,8 +0,0 @@
---
name: pipeline_source_filter
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67846
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/338347
milestone: '14.2'
type: development
group: group::pipeline execution
default_enabled: false

View File

@ -1146,14 +1146,22 @@ production: &base
# # Use multipart uploads when file size reaches 100MB, see # # Use multipart uploads when file size reaches 100MB, see
# # http://docs.aws.amazon.com/AmazonS3/latest/dev/uploadobjusingmpu.html # # http://docs.aws.amazon.com/AmazonS3/latest/dev/uploadobjusingmpu.html
# multipart_chunk_size: 104857600 # multipart_chunk_size: 104857600
# # Turns on AWS Server-Side Encryption with Amazon S3-Managed Keys for backups, this is optional # # Specifies Amazon S3 storage class to use for backups (optional)
# # encryption: 'AES256'
# # Turns on AWS Server-Side Encryption with Amazon Customer-Provided Encryption Keys for backups, this is optional
# # This should be set to the 256-bit encryption key for Amazon S3 to use to encrypt or decrypt your data.
# # 'encryption' must also be set in order for this to have any effect.
# # encryption_key: '<key>'
# # Specifies Amazon S3 storage class to use for backups, this is optional
# # storage_class: 'STANDARD' # # storage_class: 'STANDARD'
# # Turns on AWS Server-Side Encryption with Amazon Customer-Provided Encryption Keys for backups, this is optional
# # 'encryption' must be set in order for this to have any effect.
# # 'encryption_key' should be set to the 256-bit encryption key for Amazon S3 to use to encrypt or decrypt your data.
# # encryption: 'AES256'
# # encryption_key: '<key>'
# #
# # Turns on AWS Server-Side Encryption with Amazon S3-Managed keys (optional)
# # https://docs.aws.amazon.com/AmazonS3/latest/userguide/serv-side-encryption.html
# # For SSE-S3, set 'server_side_encryption' to 'AES256'.
# # For SS3-KMS, set 'server_side_encryption' to 'aws:kms'. Set
# # 'server_side_encryption_kms_key_id' to the ARN of customer master key.
# # storage_options:
# # server_side_encryption: 'aws:kms'
# # server_side_encryption_kms_key_id: 'arn:aws:kms:YOUR-KEY-ID-HERE'
## Pseudonymizer exporter ## Pseudonymizer exporter
pseudonymizer: pseudonymizer:

View File

@ -0,0 +1,5 @@
# frozen_string_literal: true
require 'apollo_upload_server'
ApolloUploadServer::Middleware.strict_mode = true

View File

@ -0,0 +1,33 @@
# frozen_string_literal: true
class UpdateExternalProjectBots < ActiveRecord::Migration[6.1]
include Gitlab::Database::MigrationHelpers
class User < ActiveRecord::Base
self.table_name = 'users'
end
disable_ddl_transaction!
TMP_INDEX_NAME = 'tmp_idx_update_external_project_bots'
def up
add_concurrent_index('users', 'id', name: TMP_INDEX_NAME, where: 'external = true')
ids = ActiveRecord::Base.connection
.execute("SELECT u.id FROM users u JOIN users u2 on u2.id = u.created_by_id WHERE u.user_type = 6 AND u2.external = true")
.map { |result| result['id'] }
ids.each_slice(10) do |group|
UpdateExternalProjectBots::User.where(id: group).update_all(external: true)
end
remove_concurrent_index_by_name('users', TMP_INDEX_NAME)
end
def down
remove_concurrent_index_by_name('users', TMP_INDEX_NAME) if index_exists_by_name?('users', TMP_INDEX_NAME)
# This migration is irreversible
end
end

View File

@ -0,0 +1 @@
f6f5e081672fb42adde980fa12f696f5d8fd11921ee52c1472b3d745bb11a5ff

View File

@ -32,6 +32,7 @@ GET /projects/:id/pipelines
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
| `scope` | string | no | The scope of pipelines, one of: `running`, `pending`, `finished`, `branches`, `tags` | | `scope` | string | no | The scope of pipelines, one of: `running`, `pending`, `finished`, `branches`, `tags` |
| `status` | string | no | The status of pipelines, one of: `created`, `waiting_for_resource`, `preparing`, `pending`, `running`, `success`, `failed`, `canceled`, `skipped`, `manual`, `scheduled` | | `status` | string | no | The status of pipelines, one of: `created`, `waiting_for_resource`, `preparing`, `pending`, `running`, `success`, `failed`, `canceled`, `skipped`, `manual`, `scheduled` |
| `source` | string | no | In [GitLab 14.3 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/325439), how the pipeline was triggered, one of: `push`, `web`, `trigger`, `schedule`, `api`, `external`, `pipeline`, `chat`, `webide`, `merge_request_event`, `external_pull_request_event`, `parent_pipeline`, `ondemand_dast_scan`, or `ondemand_dast_validation`. |
| `ref` | string | no | The ref of pipelines | | `ref` | string | no | The ref of pipelines |
| `sha` | string | no | The SHA of pipelines | | `sha` | string | no | The SHA of pipelines |
| `yaml_errors`| boolean | no | Returns pipelines with invalid configurations | | `yaml_errors`| boolean | no | Returns pipelines with invalid configurations |
@ -55,6 +56,7 @@ Example of response
"iid": 12, "iid": 12,
"project_id": 1, "project_id": 1,
"status": "pending", "status": "pending",
"soure": "push",
"ref": "new-pipeline", "ref": "new-pipeline",
"sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a", "sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
"web_url": "https://example.com/foo/bar/pipelines/47", "web_url": "https://example.com/foo/bar/pipelines/47",
@ -66,6 +68,7 @@ Example of response
"iid": 13, "iid": 13,
"project_id": 1, "project_id": 1,
"status": "pending", "status": "pending",
"soure": "web",
"ref": "new-pipeline", "ref": "new-pipeline",
"sha": "eb94b618fb5865b26e80fdd8ae531b7a63ad851a", "sha": "eb94b618fb5865b26e80fdd8ae531b7a63ad851a",
"web_url": "https://example.com/foo/bar/pipelines/48", "web_url": "https://example.com/foo/bar/pipelines/48",

View File

@ -122,6 +122,7 @@ you can filter the pipeline list by:
- Branch name - Branch name
- Status ([GitLab 13.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/217617)) - Status ([GitLab 13.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/217617))
- Tag ([GitLab 13.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/217617)) - Tag ([GitLab 13.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/217617))
- Source ([GitLab 14.3 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/338347))
[Starting in GitLab 14.2](https://gitlab.com/gitlab-org/gitlab/-/issues/26621), you can change the [Starting in GitLab 14.2](https://gitlab.com/gitlab-org/gitlab/-/issues/26621), you can change the
pipeline column to display the pipeline ID or the pipeline IID. pipeline column to display the pipeline ID or the pipeline IID.

View File

@ -259,7 +259,7 @@ Only include the following attributes for the models specified:
included_attributes: included_attributes:
user: user:
- :id - :id
- :email - :public_email
# ... # ...
``` ```

View File

@ -6,6 +6,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# GitLab.com for Jira Cloud app **(FREE)** # GitLab.com for Jira Cloud app **(FREE)**
NOTE:
Only Jira users with administrator level access are able to install or configure
the GitLab app for Jira Cloud.
## GitLab.com for Jira Cloud app **(FREE SAAS)** ## GitLab.com for Jira Cloud app **(FREE SAAS)**
You can integrate GitLab.com and Jira Cloud using the You can integrate GitLab.com and Jira Cloud using the
@ -39,7 +43,8 @@ For a walkthrough of the integration with GitLab.com for Jira Cloud app, watch
![Sign in to GitLab.com in GitLab.com for Jira Cloud app](img/jira_dev_panel_setup_com_3_v13_9.png) ![Sign in to GitLab.com in GitLab.com for Jira Cloud app](img/jira_dev_panel_setup_com_3_v13_9.png)
1. Select **Add namespace** to open the list of available namespaces. 1. Select **Add namespace** to open the list of available namespaces.
1. Identify the namespace you want to link, and select **Link**. 1. Identify the namespace you want to link, and select **Link**. Only Jira site
administrators are permitted to add or remove namespaces for an installation.
![Link namespace in GitLab.com for Jira Cloud app](img/jira_dev_panel_setup_com_4_v13_9.png) ![Link namespace in GitLab.com for Jira Cloud app](img/jira_dev_panel_setup_com_4_v13_9.png)

View File

@ -44,7 +44,7 @@ Note the following:
- Group members are exported as project members, as long as the user has - Group members are exported as project members, as long as the user has
a maintainer or administrator role in the group where the exported project lives. a maintainer or administrator role in the group where the exported project lives.
- Project members with the [Owner role](../../permissions.md) are imported as Maintainers. - Project members with the [Owner role](../../permissions.md) are imported as Maintainers.
- Imported users can be mapped by their primary email on self-managed instances, if an administrative user (not an owner) does the import. - Imported users can be mapped by their public email on self-managed instances, if an administrative user (not an owner) does the import.
Additionally, the user must be an existing member of the namespace, or the user can be added as a Additionally, the user must be an existing member of the namespace, or the user can be added as a
member of the project for contributions to be mapped. member of the project for contributions to be mapped.
Otherwise, a supplementary comment is left to mention that the original author and Otherwise, a supplementary comment is left to mention that the original author and

View File

@ -4,11 +4,9 @@ module API
module Entities module Entities
module Ci module Ci
class PipelineBasic < Grape::Entity class PipelineBasic < Grape::Entity
expose :id, :project_id, :sha, :ref, :status expose :id, :project_id, :sha, :ref, :status, :source
expose :created_at, :updated_at expose :created_at, :updated_at
expose :source, if: ->(pipeline, options) { ::Feature.enabled?(:pipeline_source_filter, options[:project], default_enabled: :yaml) }
expose :web_url do |pipeline, _options| expose :web_url do |pipeline, _options|
Gitlab::Routing.url_helpers.project_pipeline_url(pipeline.project, pipeline) Gitlab::Routing.url_helpers.project_pipeline_url(pipeline.project, pipeline)
end end

View File

@ -30,8 +30,21 @@ module Atlassian
responses.compact responses.compact
end end
def user_info(account_id)
r = get('/rest/api/3/user', { accountId: account_id, expand: 'groups' })
JiraUser.new(r.parsed_response) if r.code == 200
end
private private
def get(path, query_params)
uri = URI.join(@base_uri, path)
uri.query = URI.encode_www_form(query_params)
self.class.get(uri, headers: headers(uri, 'GET'))
end
def store_ff_info(project:, feature_flags:, **opts) def store_ff_info(project:, feature_flags:, **opts)
items = feature_flags.map { |flag| ::Atlassian::JiraConnect::Serializers::FeatureFlagEntity.represent(flag, opts) } items = feature_flags.map { |flag| ::Atlassian::JiraConnect::Serializers::FeatureFlagEntity.represent(flag, opts) }
items.reject! { |item| item.issue_keys.empty? } items.reject! { |item| item.issue_keys.empty? }
@ -99,10 +112,11 @@ module Atlassian
self.class.post(uri, headers: headers(uri), body: metadata.merge(payload).to_json) self.class.post(uri, headers: headers(uri), body: metadata.merge(payload).to_json)
end end
def headers(uri) def headers(uri, http_method = 'POST')
{ {
'Authorization' => "JWT #{jwt_token('POST', uri)}", 'Authorization' => "JWT #{jwt_token(http_method, uri)}",
'Content-Type' => 'application/json' 'Content-Type' => 'application/json',
'Accept' => 'application/json'
} }
end end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
module Atlassian
module JiraConnect
class JiraUser
def initialize(data)
@data = data
end
def site_admin?
groups = @data.dig('groups', 'items')
return false unless groups
groups.any? { |g| g['name'] == 'site-admins' }
end
end
end
end

View File

@ -47,10 +47,12 @@ module Backup
return return
end end
directory = connect_to_remote_directory(Gitlab.config.backup.upload) directory = connect_to_remote_directory
upload = directory.files.create(create_attributes)
if directory.files.create(create_attributes) if upload
progress.puts "done".color(:green) progress.puts "done".color(:green)
upload
else else
puts "uploading backup to #{remote_directory} failed".color(:red) puts "uploading backup to #{remote_directory} failed".color(:red)
raise Backup::Error, 'Backup failed' raise Backup::Error, 'Backup failed'
@ -206,11 +208,16 @@ module Backup
@backup_file_list.map {|item| item.gsub("#{FILE_NAME_SUFFIX}", "")} @backup_file_list.map {|item| item.gsub("#{FILE_NAME_SUFFIX}", "")}
end end
def connect_to_remote_directory(options) def object_storage_config
config = ObjectStorage::Config.new(options) @object_storage_config ||= begin
config.load_provider config = ObjectStorage::Config.new(Gitlab.config.backup.upload)
config.load_provider
config
end
end
connection = ::Fog::Storage.new(config.credentials) def connect_to_remote_directory
connection = ::Fog::Storage.new(object_storage_config.credentials)
# We only attempt to create the directory for local backups. For AWS # We only attempt to create the directory for local backups. For AWS
# and other cloud providers, we cannot guarantee the user will have # and other cloud providers, we cannot guarantee the user will have
@ -280,10 +287,8 @@ module Backup
key: remote_target, key: remote_target,
body: File.open(File.join(backup_path, tar_file)), body: File.open(File.join(backup_path, tar_file)),
multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size, multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size,
encryption: Gitlab.config.backup.upload.encryption,
encryption_key: Gitlab.config.backup.upload.encryption_key,
storage_class: Gitlab.config.backup.upload.storage_class storage_class: Gitlab.config.backup.upload.storage_class
} }.merge(encryption_attributes)
# Google bucket-only policies prevent setting an ACL. In any case, by default, # Google bucket-only policies prevent setting an ACL. In any case, by default,
# all objects are set to the default ACL, which is project-private: # all objects are set to the default ACL, which is project-private:
@ -293,6 +298,19 @@ module Backup
attrs attrs
end end
def encryption_attributes
return object_storage_config.fog_attributes if object_storage_config.aws_server_side_encryption_enabled?
# Use customer-managed keys. Also, this preserves
# backward-compatibility for existing usages of `SSE-S3` that
# don't set `backup.upload.storage_options.server_side_encryption`
# to `'AES256'`.
{
encryption_key: Gitlab.config.backup.upload.encryption_key,
encryption: Gitlab.config.backup.upload.encryption
}
end
def google_provider? def google_provider?
Gitlab.config.backup.upload.connection&.provider&.downcase == 'google' Gitlab.config.backup.upload.connection&.provider&.downcase == 'google'
end end

View File

@ -20,7 +20,7 @@ tree:
included_attributes: included_attributes:
user: user:
- :id - :id
- :email - :public_email
- :username - :username
author: author:
- :name - :name

View File

@ -20,7 +20,7 @@ tree:
included_attributes: included_attributes:
user: user:
- :id - :id
- :email - :public_email
- :username - :username
author: author:
- :name - :name

View File

@ -19,7 +19,8 @@ module Gitlab
@exported_members.inject(missing_keys_tracking_hash) do |hash, member| @exported_members.inject(missing_keys_tracking_hash) do |hash, member|
if member['user'] if member['user']
old_user_id = member['user']['id'] old_user_id = member['user']['id']
existing_user = User.find_by(find_user_query(member)) old_user_email = member.dig('user', 'public_email') || member.dig('user', 'email')
existing_user = User.find_by(find_user_query(old_user_email)) if old_user_email
hash[old_user_id] = existing_user.id if existing_user && add_team_member(member, existing_user) hash[old_user_id] = existing_user.id if existing_user && add_team_member(member, existing_user)
else else
add_team_member(member) add_team_member(member)
@ -94,8 +95,8 @@ module Gitlab
relation_class: relation_class) relation_class: relation_class)
end end
def find_user_query(member) def find_user_query(email)
user_arel[:email].eq(member['user']['email']) user_arel[:email].eq(email)
end end
def user_arel def user_arel

View File

@ -115,7 +115,7 @@ tree:
included_attributes: included_attributes:
user: user:
- :id - :id
- :email - :public_email
- :username - :username
author: author:
- :name - :name

View File

@ -11,7 +11,7 @@ module Gitlab
# this if the change to the renderer output is a new feature or a # this if the change to the renderer output is a new feature or a
# minor bug fix. # minor bug fix.
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/330313 # See: https://gitlab.com/gitlab-org/gitlab/-/issues/330313
CACHE_COMMONMARK_VERSION = 28 CACHE_COMMONMARK_VERSION = 29
CACHE_COMMONMARK_VERSION_START = 10 CACHE_COMMONMARK_VERSION_START = 10
BaseError = Class.new(StandardError) BaseError = Class.new(StandardError)

View File

@ -176,7 +176,7 @@ module Gitlab
::Gitlab::Middleware::Multipart::Handler.new(env, message).with_open_files do ::Gitlab::Middleware::Multipart::Handler.new(env, message).with_open_files do
@app.call(env) @app.call(env)
end end
rescue UploadedFile::InvalidPathError => e rescue UploadedFile::InvalidPathError, ApolloUploadServer::GraphQLDataBuilder::OutOfBounds => e
[400, { 'Content-Type' => 'text/plain' }, [e.message]] [400, { 'Content-Type' => 'text/plain' }, [e.message]]
end end
end end

View File

@ -84,13 +84,16 @@ module ObjectStorage
def fog_attributes def fog_attributes
@fog_attributes ||= begin @fog_attributes ||= begin
return {} unless enabled? && aws? return {} unless aws_server_side_encryption_enabled?
return {} unless server_side_encryption.present?
aws_server_side_encryption_headers.compact aws_server_side_encryption_headers.compact
end end
end end
def aws_server_side_encryption_enabled?
aws? && server_side_encryption.present?
end
private private
# This returns a Hash of HTTP encryption headers to send along to S3. # This returns a Hash of HTTP encryption headers to send along to S3.

View File

@ -54,7 +54,10 @@ RSpec.describe JiraConnect::AppDescriptorController do
postInstallPage: { postInstallPage: {
key: 'gitlab-configuration', key: 'gitlab-configuration',
name: { value: 'GitLab Configuration' }, name: { value: 'GitLab Configuration' },
url: '/subscriptions' url: '/subscriptions',
conditions: contain_exactly(
a_hash_including(condition: 'user_is_admin', invert: false)
)
}, },
jiraDevelopmentTool: { jiraDevelopmentTool: {
actions: { actions: {

View File

@ -102,11 +102,17 @@ RSpec.describe JiraConnect::SubscriptionsController do
end end
context 'with valid JWT' do context 'with valid JWT' do
let(:jwt) { Atlassian::Jwt.encode({ iss: installation.client_key }, installation.shared_secret) } let(:claims) { { iss: installation.client_key, sub: 1234 } }
let(:jwt) { Atlassian::Jwt.encode(claims, installation.shared_secret) }
let(:jira_user) { { 'groups' => { 'items' => [{ 'name' => jira_group_name }] } } }
let(:jira_group_name) { 'site-admins' }
context 'signed in to GitLab' do context 'signed in to GitLab' do
before do before do
sign_in(user) sign_in(user)
WebMock
.stub_request(:get, "#{installation.base_url}/rest/api/3/user?accountId=1234&expand=groups")
.to_return(body: jira_user.to_json, status: 200, headers: { 'Content-Type' => 'application/json' })
end end
context 'dev panel integration is available' do context 'dev panel integration is available' do
@ -120,6 +126,16 @@ RSpec.describe JiraConnect::SubscriptionsController do
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
end end
end end
context 'when the Jira user is not a site-admin' do
let(:jira_group_name) { 'some-other-group' }
it 'returns forbidden' do
subject
expect(response).to have_gitlab_http_status(:forbidden)
end
end
end end
context 'not signed in to GitLab' do context 'not signed in to GitLab' do
@ -134,8 +150,14 @@ RSpec.describe JiraConnect::SubscriptionsController do
describe '#destroy' do describe '#destroy' do
let(:subscription) { create(:jira_connect_subscription, installation: installation) } let(:subscription) { create(:jira_connect_subscription, installation: installation) }
let(:jira_user) { { 'groups' => { 'items' => [{ 'name' => jira_group_name }] } } }
let(:jira_group_name) { 'site-admins' }
before do before do
WebMock
.stub_request(:get, "#{installation.base_url}/rest/api/3/user?accountId=1234&expand=groups")
.to_return(body: jira_user.to_json, status: 200, headers: { 'Content-Type' => 'application/json' })
delete :destroy, params: { jwt: jwt, id: subscription.id } delete :destroy, params: { jwt: jwt, id: subscription.id }
end end
@ -148,12 +170,23 @@ RSpec.describe JiraConnect::SubscriptionsController do
end end
context 'with valid JWT' do context 'with valid JWT' do
let(:jwt) { Atlassian::Jwt.encode({ iss: installation.client_key }, installation.shared_secret) } let(:claims) { { iss: installation.client_key, sub: 1234 } }
let(:jwt) { Atlassian::Jwt.encode(claims, installation.shared_secret) }
it 'deletes the subscription' do it 'deletes the subscription' do
expect { subscription.reload }.to raise_error ActiveRecord::RecordNotFound expect { subscription.reload }.to raise_error ActiveRecord::RecordNotFound
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
end end
context 'when the Jira user is not a site admin' do
let(:jira_group_name) { 'some-other-group' }
it 'does not delete the subscription' do
expect(response).to have_gitlab_http_status(:forbidden)
expect { subscription.reload }.not_to raise_error
end
end
end end
end end
end end

View File

@ -19,8 +19,18 @@ RSpec.describe 'Upload a design through graphQL', :js do
let_it_be(:user) { create(:user, :admin) } let_it_be(:user) { create(:user, :admin) }
let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
let_it_be(:design) { create(:design) } let_it_be(:design) { create(:design) }
let_it_be(:operations) { { "operationName": "uploadDesign", "variables": { "files": [], "projectPath": design.project.full_path, "iid": design.issue.iid }, "query": query }.to_json }
let_it_be(:map) { { "1": ["variables.files.0"] }.to_json } let_it_be(:map) { { "1": ["variables.files.0"] }.to_json }
let_it_be(:operations) do
{
"operationName": "uploadDesign",
"variables": {
"files": [nil],
"projectPath": design.project.full_path,
"iid": design.issue.iid
},
"query": query
}.to_json
end
let(:url) { capybara_url("/api/graphql?private_token=#{personal_access_token.token}") } let(:url) { capybara_url("/api/graphql?private_token=#{personal_access_token.token}") }
let(:file) { fixture_file_upload('spec/fixtures/dk.png') } let(:file) { fixture_file_upload('spec/fixtures/dk.png') }

View File

@ -258,20 +258,8 @@ RSpec.describe Ci::PipelinesFinder do
let!(:push_pipeline) { create(:ci_pipeline, project: project, source: 'push') } let!(:push_pipeline) { create(:ci_pipeline, project: project, source: 'push') }
let!(:api_pipeline) { create(:ci_pipeline, project: project, source: 'api') } let!(:api_pipeline) { create(:ci_pipeline, project: project, source: 'api') }
context 'when `pipeline_source_filter` feature flag is disabled' do it 'returns only the matched pipeline' do
before do is_expected.to eq([web_pipeline])
stub_feature_flags(pipeline_source_filter: false)
end
it 'returns all the pipelines' do
is_expected.to contain_exactly(web_pipeline, push_pipeline, api_pipeline)
end
end
context 'when `pipeline_source_filter` feature flag is enabled' do
it 'returns only the matched pipeline' do
is_expected.to eq([web_pipeline])
end
end end
end end

File diff suppressed because one or more lines are too long

View File

@ -2398,7 +2398,7 @@
"requested_at": null, "requested_at": null,
"user": { "user": {
"id": 16, "id": 16,
"email": "bernard_willms@gitlabexample.com", "public_email": "bernard_willms@gitlabexample.com",
"username": "bernard_willms" "username": "bernard_willms"
} }
}, },
@ -2418,7 +2418,7 @@
"requested_at": null, "requested_at": null,
"user": { "user": {
"id": 6, "id": 6,
"email": "saul_will@gitlabexample.com", "public_email": "saul_will@gitlabexample.com",
"username": "saul_will" "username": "saul_will"
} }
}, },
@ -2438,7 +2438,7 @@
"requested_at": null, "requested_at": null,
"user": { "user": {
"id": 15, "id": 15,
"email": "breanna_sanford@wolf.com", "public_email": "breanna_sanford@wolf.com",
"username": "emmet.schamberger" "username": "emmet.schamberger"
} }
}, },
@ -2458,7 +2458,7 @@
"requested_at": null, "requested_at": null,
"user": { "user": {
"id": 26, "id": 26,
"email": "user4@example.com", "public_email": "user4@example.com",
"username": "user4" "username": "user4"
} }
} }

View File

@ -1,4 +1,4 @@
{"id":36,"access_level":40,"source_id":5,"source_type":"Project","user_id":16,"notification_level":3,"created_at":"2016-06-14T15:02:03.834Z","updated_at":"2016-06-14T15:02:03.834Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"user":{"id":16,"email":"bernard_willms@gitlabexample.com","username":"bernard_willms"}} {"id":36,"access_level":40,"source_id":5,"source_type":"Project","user_id":16,"notification_level":3,"created_at":"2016-06-14T15:02:03.834Z","updated_at":"2016-06-14T15:02:03.834Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"user":{"id":16,"public_email":"bernard_willms@gitlabexample.com","username":"bernard_willms"}}
{"id":35,"access_level":10,"source_id":5,"source_type":"Project","user_id":6,"notification_level":3,"created_at":"2016-06-14T15:02:03.811Z","updated_at":"2016-06-14T15:02:03.811Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"user":{"id":6,"email":"saul_will@gitlabexample.com","username":"saul_will"}} {"id":35,"access_level":10,"source_id":5,"source_type":"Project","user_id":6,"notification_level":3,"created_at":"2016-06-14T15:02:03.811Z","updated_at":"2016-06-14T15:02:03.811Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"user":{"id":6,"public_email":"saul_will@gitlabexample.com","username":"saul_will"}}
{"id":34,"access_level":20,"source_id":5,"source_type":"Project","user_id":15,"notification_level":3,"created_at":"2016-06-14T15:02:03.776Z","updated_at":"2016-06-14T15:02:03.776Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"user":{"id":15,"email":"breanna_sanford@wolf.com","username":"emmet.schamberger"}} {"id":34,"access_level":20,"source_id":5,"source_type":"Project","user_id":15,"notification_level":3,"created_at":"2016-06-14T15:02:03.776Z","updated_at":"2016-06-14T15:02:03.776Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"user":{"id":15,"public_email":"breanna_sanford@wolf.com","username":"emmet.schamberger"}}
{"id":33,"access_level":20,"source_id":5,"source_type":"Project","user_id":26,"notification_level":3,"created_at":"2016-06-14T15:02:03.742Z","updated_at":"2016-06-14T15:02:03.742Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"user":{"id":26,"email":"user4@example.com","username":"user4"}} {"id":33,"access_level":20,"source_id":5,"source_type":"Project","user_id":26,"notification_level":3,"created_at":"2016-06-14T15:02:03.742Z","updated_at":"2016-06-14T15:02:03.742Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"user":{"id":26,"public_email":"user4@example.com","username":"user4"}}

View File

@ -425,7 +425,7 @@
"override":false, "override":false,
"user":{ "user":{
"id":1, "id":1,
"email":"admin@example.com", "public_email":"admin@example.com",
"username":"root" "username":"root"
} }
}, },
@ -448,7 +448,7 @@
"override":false, "override":false,
"user":{ "user":{
"id":2, "id":2,
"email":"user_2@gitlabexample.com", "public_email":"user_2@gitlabexample.com",
"username":"user_2" "username":"user_2"
} }
} }

View File

@ -377,7 +377,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 42, "id": 42,
"email": "moriah@collinsmurphy.com", "public_email": "moriah@collinsmurphy.com",
"username": "reported_user_15" "username": "reported_user_15"
} }
}, },
@ -400,7 +400,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 271, "id": 271,
"email": "garret@connellystark.ca", "public_email": "garret@connellystark.ca",
"username": "charlesetta" "username": "charlesetta"
} }
}, },
@ -423,7 +423,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 206, "id": 206,
"email": "gwendolyn_robel@gitlabexample.com", "public_email": "gwendolyn_robel@gitlabexample.com",
"username": "gwendolyn_robel" "username": "gwendolyn_robel"
} }
}, },
@ -446,7 +446,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 39, "id": 39,
"email": "alexis_berge@kerlukeklein.us", "public_email": "alexis_berge@kerlukeklein.us",
"username": "reported_user_12" "username": "reported_user_12"
} }
}, },
@ -469,7 +469,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 1624, "id": 1624,
"email": "adriene.mcclure@gitlabexample.com", "public_email": "adriene.mcclure@gitlabexample.com",
"username": "adriene.mcclure" "username": "adriene.mcclure"
} }
}, },
@ -492,7 +492,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 1, "id": 1,
"email": "admin@example.com", "public_email": "admin@example.com",
"username": "root" "username": "root"
} }
} }
@ -1338,7 +1338,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 1087, "id": 1087,
"email": "paige@blanda.info", "public_email": "paige@blanda.info",
"username": "billi_auer" "username": "billi_auer"
} }
}, },
@ -1361,7 +1361,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 171, "id": 171,
"email": "heidi@bosco.co.uk", "public_email": "heidi@bosco.co.uk",
"username": "gerard.cruickshank" "username": "gerard.cruickshank"
} }
}, },
@ -1384,7 +1384,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 1157, "id": 1157,
"email": "larisa.bruen@carroll.biz", "public_email": "larisa.bruen@carroll.biz",
"username": "milagros.reynolds" "username": "milagros.reynolds"
} }
}, },
@ -1407,7 +1407,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 14, "id": 14,
"email": "madlyn_kovacek@wiza.ca", "public_email": "madlyn_kovacek@wiza.ca",
"username": "monique.gusikowski" "username": "monique.gusikowski"
} }
}, },
@ -1430,7 +1430,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 1167, "id": 1167,
"email": "mirella@koepp.ca", "public_email": "mirella@koepp.ca",
"username": "eileen" "username": "eileen"
} }
}, },
@ -1453,7 +1453,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 1, "id": 1,
"email": "admin@example.com", "public_email": "admin@example.com",
"username": "root" "username": "root"
} }
} }
@ -1909,7 +1909,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 1533, "id": 1533,
"email": "jose@cassin.ca", "public_email": "jose@cassin.ca",
"username": "buster" "username": "buster"
} }
}, },
@ -1932,7 +1932,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 1586, "id": 1586,
"email": "carie@gleichner.us", "public_email": "carie@gleichner.us",
"username": "dominque" "username": "dominque"
} }
}, },
@ -1955,7 +1955,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 190, "id": 190,
"email": "delois@funk.biz", "public_email": "delois@funk.biz",
"username": "kittie" "username": "kittie"
} }
}, },
@ -1978,7 +1978,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 254, "id": 254,
"email": "tyra.lowe@whitemckenzie.co.uk", "public_email": "tyra.lowe@whitemckenzie.co.uk",
"username": "kassie" "username": "kassie"
} }
}, },
@ -2001,7 +2001,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 503, "id": 503,
"email": "tyesha.brakus@bruen.ca", "public_email": "tyesha.brakus@bruen.ca",
"username": "charise" "username": "charise"
} }
}, },
@ -2024,7 +2024,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 1, "id": 1,
"email": "admin@example.com", "public_email": "admin@example.com",
"username": "root" "username": "root"
} }
} }

View File

@ -1,6 +1,6 @@
{"id":13766,"access_level":30,"source_id":4351,"source_type":"Namespace","user_id":42,"notification_level":3,"created_at":"2019-11-20T17:04:36.184Z","updated_at":"2019-11-20T17:04:36.184Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":42,"email":"moriah@collinsmurphy.com","username":"reported_user_15"}} {"id":13766,"access_level":30,"source_id":4351,"source_type":"Namespace","user_id":42,"notification_level":3,"created_at":"2019-11-20T17:04:36.184Z","updated_at":"2019-11-20T17:04:36.184Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":42,"public_email":"moriah@collinsmurphy.com","username":"reported_user_15"}}
{"id":13765,"access_level":40,"source_id":4351,"source_type":"Namespace","user_id":271,"notification_level":3,"created_at":"2019-11-20T17:04:36.044Z","updated_at":"2019-11-20T17:04:36.044Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":271,"email":"garret@connellystark.ca","username":"charlesetta"}} {"id":13765,"access_level":40,"source_id":4351,"source_type":"Namespace","user_id":271,"notification_level":3,"created_at":"2019-11-20T17:04:36.044Z","updated_at":"2019-11-20T17:04:36.044Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":271,"public_email":"garret@connellystark.ca","username":"charlesetta"}}
{"id":13764,"access_level":30,"source_id":4351,"source_type":"Namespace","user_id":206,"notification_level":3,"created_at":"2019-11-20T17:04:35.840Z","updated_at":"2019-11-20T17:04:35.840Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":206,"email":"gwendolyn_robel@gitlabexample.com","username":"gwendolyn_robel"}} {"id":13764,"access_level":30,"source_id":4351,"source_type":"Namespace","user_id":206,"notification_level":3,"created_at":"2019-11-20T17:04:35.840Z","updated_at":"2019-11-20T17:04:35.840Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":206,"public_email":"gwendolyn_robel@gitlabexample.com","username":"gwendolyn_robel"}}
{"id":13763,"access_level":10,"source_id":4351,"source_type":"Namespace","user_id":39,"notification_level":3,"created_at":"2019-11-20T17:04:35.704Z","updated_at":"2019-11-20T17:04:35.704Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":39,"email":"alexis_berge@kerlukeklein.us","username":"reported_user_12"}} {"id":13763,"access_level":10,"source_id":4351,"source_type":"Namespace","user_id":39,"notification_level":3,"created_at":"2019-11-20T17:04:35.704Z","updated_at":"2019-11-20T17:04:35.704Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":39,"public_email":"alexis_berge@kerlukeklein.us","username":"reported_user_12"}}
{"id":13762,"access_level":20,"source_id":4351,"source_type":"Namespace","user_id":1624,"notification_level":3,"created_at":"2019-11-20T17:04:35.566Z","updated_at":"2019-11-20T17:04:35.566Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1624,"email":"adriene.mcclure@gitlabexample.com","username":"adriene.mcclure"}} {"id":13762,"access_level":20,"source_id":4351,"source_type":"Namespace","user_id":1624,"notification_level":3,"created_at":"2019-11-20T17:04:35.566Z","updated_at":"2019-11-20T17:04:35.566Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1624,"public_email":"adriene.mcclure@gitlabexample.com","username":"adriene.mcclure"}}
{"id":12920,"access_level":50,"source_id":4351,"source_type":"Namespace","user_id":1,"notification_level":3,"created_at":"2019-11-20T17:01:53.505Z","updated_at":"2019-11-20T17:01:53.505Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1,"email":"admin@example.com","username":"root"}} {"id":12920,"access_level":50,"source_id":4351,"source_type":"Namespace","user_id":1,"notification_level":3,"created_at":"2019-11-20T17:01:53.505Z","updated_at":"2019-11-20T17:01:53.505Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1,"public_email":"admin@example.com","username":"root"}}

View File

@ -1,6 +1,6 @@
{"id":13771,"access_level":30,"source_id":4352,"source_type":"Namespace","user_id":1087,"notification_level":3,"created_at":"2019-11-20T17:04:36.968Z","updated_at":"2019-11-20T17:04:36.968Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1087,"email":"paige@blanda.info","username":"billi_auer"}} {"id":13771,"access_level":30,"source_id":4352,"source_type":"Namespace","user_id":1087,"notification_level":3,"created_at":"2019-11-20T17:04:36.968Z","updated_at":"2019-11-20T17:04:36.968Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1087,"public_email":"paige@blanda.info","username":"billi_auer"}}
{"id":13770,"access_level":20,"source_id":4352,"source_type":"Namespace","user_id":171,"notification_level":3,"created_at":"2019-11-20T17:04:36.821Z","updated_at":"2019-11-20T17:04:36.821Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":171,"email":"heidi@bosco.co.uk","username":"gerard.cruickshank"}} {"id":13770,"access_level":20,"source_id":4352,"source_type":"Namespace","user_id":171,"notification_level":3,"created_at":"2019-11-20T17:04:36.821Z","updated_at":"2019-11-20T17:04:36.821Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":171,"public_email":"heidi@bosco.co.uk","username":"gerard.cruickshank"}}
{"id":13769,"access_level":30,"source_id":4352,"source_type":"Namespace","user_id":1157,"notification_level":3,"created_at":"2019-11-20T17:04:36.606Z","updated_at":"2019-11-20T17:04:36.606Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1157,"email":"larisa.bruen@carroll.biz","username":"milagros.reynolds"}} {"id":13769,"access_level":30,"source_id":4352,"source_type":"Namespace","user_id":1157,"notification_level":3,"created_at":"2019-11-20T17:04:36.606Z","updated_at":"2019-11-20T17:04:36.606Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1157,"public_email":"larisa.bruen@carroll.biz","username":"milagros.reynolds"}}
{"id":13768,"access_level":40,"source_id":4352,"source_type":"Namespace","user_id":14,"notification_level":3,"created_at":"2019-11-20T17:04:36.465Z","updated_at":"2019-11-20T17:04:36.465Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":14,"email":"madlyn_kovacek@wiza.ca","username":"monique.gusikowski"}} {"id":13768,"access_level":40,"source_id":4352,"source_type":"Namespace","user_id":14,"notification_level":3,"created_at":"2019-11-20T17:04:36.465Z","updated_at":"2019-11-20T17:04:36.465Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":14,"public_email":"madlyn_kovacek@wiza.ca","username":"monique.gusikowski"}}
{"id":13767,"access_level":10,"source_id":4352,"source_type":"Namespace","user_id":1167,"notification_level":3,"created_at":"2019-11-20T17:04:36.324Z","updated_at":"2019-11-20T17:04:36.324Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1167,"email":"mirella@koepp.ca","username":"eileen"}} {"id":13767,"access_level":10,"source_id":4352,"source_type":"Namespace","user_id":1167,"notification_level":3,"created_at":"2019-11-20T17:04:36.324Z","updated_at":"2019-11-20T17:04:36.324Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1167,"public_email":"mirella@koepp.ca","username":"eileen"}}
{"id":12921,"access_level":50,"source_id":4352,"source_type":"Namespace","user_id":1,"notification_level":3,"created_at":"2019-11-20T17:01:53.953Z","updated_at":"2019-11-20T17:01:53.953Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1,"email":"admin@example.com","username":"root"}} {"id":12921,"access_level":50,"source_id":4352,"source_type":"Namespace","user_id":1,"notification_level":3,"created_at":"2019-11-20T17:01:53.953Z","updated_at":"2019-11-20T17:01:53.953Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1,"public_email":"admin@example.com","username":"root"}}

View File

@ -1,6 +1,6 @@
{"id":13786,"access_level":30,"source_id":4355,"source_type":"Namespace","user_id":1533,"notification_level":3,"created_at":"2019-11-20T17:04:39.405Z","updated_at":"2019-11-20T17:04:39.405Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1533,"email":"jose@cassin.ca","username":"buster"}} {"id":13786,"access_level":30,"source_id":4355,"source_type":"Namespace","user_id":1533,"notification_level":3,"created_at":"2019-11-20T17:04:39.405Z","updated_at":"2019-11-20T17:04:39.405Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1533,"public_email":"jose@cassin.ca","username":"buster"}}
{"id":13785,"access_level":10,"source_id":4355,"source_type":"Namespace","user_id":1586,"notification_level":3,"created_at":"2019-11-20T17:04:39.269Z","updated_at":"2019-11-20T17:04:39.269Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1586,"email":"carie@gleichner.us","username":"dominque"}} {"id":13785,"access_level":10,"source_id":4355,"source_type":"Namespace","user_id":1586,"notification_level":3,"created_at":"2019-11-20T17:04:39.269Z","updated_at":"2019-11-20T17:04:39.269Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1586,"public_email":"carie@gleichner.us","username":"dominque"}}
{"id":13784,"access_level":30,"source_id":4355,"source_type":"Namespace","user_id":190,"notification_level":3,"created_at":"2019-11-20T17:04:39.127Z","updated_at":"2019-11-20T17:04:39.127Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":190,"email":"delois@funk.biz","username":"kittie"}} {"id":13784,"access_level":30,"source_id":4355,"source_type":"Namespace","user_id":190,"notification_level":3,"created_at":"2019-11-20T17:04:39.127Z","updated_at":"2019-11-20T17:04:39.127Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":190,"public_email":"delois@funk.biz","username":"kittie"}}
{"id":13783,"access_level":20,"source_id":4355,"source_type":"Namespace","user_id":254,"notification_level":3,"created_at":"2019-11-20T17:04:38.971Z","updated_at":"2019-11-20T17:04:38.971Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":254,"email":"tyra.lowe@whitemckenzie.co.uk","username":"kassie"}} {"id":13783,"access_level":20,"source_id":4355,"source_type":"Namespace","user_id":254,"notification_level":3,"created_at":"2019-11-20T17:04:38.971Z","updated_at":"2019-11-20T17:04:38.971Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":254,"public_email":"tyra.lowe@whitemckenzie.co.uk","username":"kassie"}}
{"id":13782,"access_level":40,"source_id":4355,"source_type":"Namespace","user_id":503,"notification_level":3,"created_at":"2019-11-20T17:04:38.743Z","updated_at":"2019-11-20T17:04:38.743Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":503,"email":"tyesha.brakus@bruen.ca","username":"charise"}} {"id":13782,"access_level":40,"source_id":4355,"source_type":"Namespace","user_id":503,"notification_level":3,"created_at":"2019-11-20T17:04:38.743Z","updated_at":"2019-11-20T17:04:38.743Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":503,"public_email":"tyesha.brakus@bruen.ca","username":"charise"}}
{"id":12924,"access_level":50,"source_id":4355,"source_type":"Namespace","user_id":1,"notification_level":3,"created_at":"2019-11-20T17:01:54.145Z","updated_at":"2019-11-20T17:01:54.145Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1,"email":"admin@example.com","username":"root"}} {"id":12924,"access_level":50,"source_id":4355,"source_type":"Namespace","user_id":1,"notification_level":3,"created_at":"2019-11-20T17:01:54.145Z","updated_at":"2019-11-20T17:01:54.145Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1,"public_email":"admin@example.com","username":"root"}}

View File

@ -299,7 +299,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 42, "id": 42,
"email": "moriah@collinsmurphy.com", "public_email": "moriah@collinsmurphy.com",
"username": "reported_user_15" "username": "reported_user_15"
} }
}, },
@ -322,7 +322,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 271, "id": 271,
"email": "garret@connellystark.ca", "public_email": "garret@connellystark.ca",
"username": "charlesetta" "username": "charlesetta"
} }
}, },
@ -345,7 +345,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 206, "id": 206,
"email": "margaret.bergnaum@reynolds.us", "public_email": "margaret.bergnaum@reynolds.us",
"username": "gwendolyn_robel" "username": "gwendolyn_robel"
} }
}, },
@ -368,7 +368,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 39, "id": 39,
"email": "alexis_berge@kerlukeklein.us", "public_email": "alexis_berge@kerlukeklein.us",
"username": "reported_user_12" "username": "reported_user_12"
} }
}, },
@ -391,7 +391,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 1624, "id": 1624,
"email": "nakesha.herzog@powlowski.com", "public_email": "nakesha.herzog@powlowski.com",
"username": "adriene.mcclure" "username": "adriene.mcclure"
} }
}, },
@ -414,7 +414,7 @@
"override": false, "override": false,
"user": { "user": {
"id": 1, "id": 1,
"email": "admin@example.com", "public_email": "admin@example.com",
"username": "root" "username": "root"
} }
} }

View File

@ -1,6 +1,6 @@
{"id":13766,"access_level":30,"source_id":4351,"source_type":"Namespace","user_id":42,"notification_level":3,"created_at":"2019-11-20T17:04:36.184Z","updated_at":"2019-11-20T17:04:36.184Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":42,"email":"moriah@collinsmurphy.com","username":"reported_user_15"}} {"id":13766,"access_level":30,"source_id":4351,"source_type":"Namespace","user_id":42,"notification_level":3,"created_at":"2019-11-20T17:04:36.184Z","updated_at":"2019-11-20T17:04:36.184Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":42,"public_email":"moriah@collinsmurphy.com","username":"reported_user_15"}}
{"id":13765,"access_level":40,"source_id":4351,"source_type":"Namespace","user_id":271,"notification_level":3,"created_at":"2019-11-20T17:04:36.044Z","updated_at":"2019-11-20T17:04:36.044Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":271,"email":"garret@connellystark.ca","username":"charlesetta"}} {"id":13765,"access_level":40,"source_id":4351,"source_type":"Namespace","user_id":271,"notification_level":3,"created_at":"2019-11-20T17:04:36.044Z","updated_at":"2019-11-20T17:04:36.044Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":271,"public_email":"garret@connellystark.ca","username":"charlesetta"}}
{"id":13764,"access_level":30,"source_id":4351,"source_type":"Namespace","user_id":206,"notification_level":3,"created_at":"2019-11-20T17:04:35.840Z","updated_at":"2019-11-20T17:04:35.840Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":206,"email":"margaret.bergnaum@reynolds.us","username":"gwendolyn_robel"}} {"id":13764,"access_level":30,"source_id":4351,"source_type":"Namespace","user_id":206,"notification_level":3,"created_at":"2019-11-20T17:04:35.840Z","updated_at":"2019-11-20T17:04:35.840Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":206,"public_email":"margaret.bergnaum@reynolds.us","username":"gwendolyn_robel"}}
{"id":13763,"access_level":10,"source_id":4351,"source_type":"Namespace","user_id":39,"notification_level":3,"created_at":"2019-11-20T17:04:35.704Z","updated_at":"2019-11-20T17:04:35.704Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":39,"email":"alexis_berge@kerlukeklein.us","username":"reported_user_12"}} {"id":13763,"access_level":10,"source_id":4351,"source_type":"Namespace","user_id":39,"notification_level":3,"created_at":"2019-11-20T17:04:35.704Z","updated_at":"2019-11-20T17:04:35.704Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":39,"public_email":"alexis_berge@kerlukeklein.us","username":"reported_user_12"}}
{"id":13762,"access_level":20,"source_id":4351,"source_type":"Namespace","user_id":1624,"notification_level":3,"created_at":"2019-11-20T17:04:35.566Z","updated_at":"2019-11-20T17:04:35.566Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1624,"email":"nakesha.herzog@powlowski.com","username":"adriene.mcclure"}} {"id":13762,"access_level":20,"source_id":4351,"source_type":"Namespace","user_id":1624,"notification_level":3,"created_at":"2019-11-20T17:04:35.566Z","updated_at":"2019-11-20T17:04:35.566Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1624,"public_email":"nakesha.herzog@powlowski.com","username":"adriene.mcclure"}}
{"id":12920,"access_level":50,"source_id":4351,"source_type":"Namespace","user_id":1,"notification_level":3,"created_at":"2019-11-20T17:01:53.505Z","updated_at":"2019-11-20T17:01:53.505Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1,"email":"admin@example.com","username":"root"}} {"id":12920,"access_level":50,"source_id":4351,"source_type":"Namespace","user_id":1,"notification_level":3,"created_at":"2019-11-20T17:01:53.505Z","updated_at":"2019-11-20T17:01:53.505Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1,"public_email":"admin@example.com","username":"root"}}

View File

@ -33,8 +33,6 @@ describe('Pipelines filtered search', () => {
}; };
beforeEach(() => { beforeEach(() => {
window.gon = { features: { pipelineSourceFilter: true } };
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
jest.spyOn(Api, 'projectUsers').mockResolvedValue(users); jest.spyOn(Api, 'projectUsers').mockResolvedValue(users);

View File

@ -105,8 +105,6 @@ describe('Pipelines', () => {
}); });
beforeEach(() => { beforeEach(() => {
window.gon = { features: { pipelineSourceFilter: true } };
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
jest.spyOn(window.history, 'pushState'); jest.spyOn(window.history, 'pushState');

View File

@ -98,4 +98,19 @@ RSpec.describe IntegrationsHelper do
end end
end end
end end
describe '#jira_issue_breadcrumb_link' do
let(:issue_reference) { nil }
subject { helper.jira_issue_breadcrumb_link(issue_reference) }
context 'when issue_reference contains HTML' do
let(:issue_reference) { "<script>alert('XSS')</script>" }
it 'escapes issue reference' do
is_expected.not_to include(issue_reference)
is_expected.to include(html_escape(issue_reference))
end
end
end
end end

View File

@ -432,6 +432,77 @@ RSpec.describe Backup::Manager do
end end
end end
context 'with AWS with server side encryption' do
let(:connection) { ::Fog::Storage.new(Gitlab.config.backup.upload.connection.symbolize_keys) }
let(:encryption_key) { nil }
let(:encryption) { nil }
let(:storage_options) { nil }
before do
stub_backup_setting(
upload: {
connection: {
provider: 'AWS',
aws_access_key_id: 'AWS_ACCESS_KEY_ID',
aws_secret_access_key: 'AWS_SECRET_ACCESS_KEY'
},
remote_directory: 'directory',
multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size,
encryption: encryption,
encryption_key: encryption_key,
storage_options: storage_options,
storage_class: nil
}
)
connection.directories.create(key: Gitlab.config.backup.upload.remote_directory)
end
context 'with SSE-S3 without using storage_options' do
let(:encryption) { 'AES256' }
it 'sets encryption attributes' do
result = subject.upload
expect(result.key).to be_present
expect(result.encryption).to eq('AES256')
expect(result.encryption_key).to be_nil
expect(result.kms_key_id).to be_nil
end
end
context 'with SSE-C (customer-provided keys) options' do
let(:encryption) { 'AES256' }
let(:encryption_key) { SecureRandom.hex }
it 'sets encryption attributes' do
result = subject.upload
expect(result.key).to be_present
expect(result.encryption).to eq(encryption)
expect(result.encryption_key).to eq(encryption_key)
expect(result.kms_key_id).to be_nil
end
end
context 'with SSE-KMS options' do
let(:storage_options) do
{
server_side_encryption: 'aws:kms',
server_side_encryption_kms_key_id: 'arn:aws:kms:12345'
}
end
it 'sets encryption attributes' do
result = subject.upload
expect(result.key).to be_present
expect(result.encryption).to eq('aws:kms')
expect(result.kms_key_id).to eq('arn:aws:kms:12345')
end
end
end
context 'with Google provider' do context 'with Google provider' do
before do before do
stub_backup_setting( stub_backup_setting(

View File

@ -90,11 +90,8 @@ RSpec.describe Banzai::Filter::References::DesignReferenceFilter do
[ [
['simple.png'], ['simple.png'],
['SIMPLE.PNG'], ['SIMPLE.PNG'],
['has spaces.png'],
['has-hyphen.jpg'], ['has-hyphen.jpg'],
['snake_case.svg'], ['snake_case.svg']
['has "quotes".svg'],
['has <special> characters [o].svg']
] ]
end end
@ -138,40 +135,25 @@ RSpec.describe Banzai::Filter::References::DesignReferenceFilter do
end end
end end
context 'a design with a quoted filename' do
let(:filename) { %q{A "very" good file.png} }
let(:design) { create(:design, :with_versions, issue: issue, filename: filename) }
it 'links to the design' do
expect(doc.css('a').first.attr('href'))
.to eq url_for_design(design)
end
end
context 'internal reference' do context 'internal reference' do
it_behaves_like 'a reference containing an element node' it_behaves_like 'a reference containing an element node'
context 'the reference is valid' do it_behaves_like 'a good link reference'
it_behaves_like 'a good link reference'
context 'the filename needs to be escaped' do context 'the filename contains invalid characters' do
where(:filename) do where(:filename) do
[ [
['with some spaces.png'], ['with some spaces.png'],
['with <script>console.log("pwded")<%2Fscript>.png'] ['with <script>console.log("pwded")<%2Fscript>.png'],
] ['foo"bar.png'],
end ['A "very" good file.png']
]
end
with_them do with_them do
let(:design) { create(:design, :with_versions, filename: filename, issue: issue) } let(:design) { create(:design, :with_versions, filename: filename, issue: issue) }
let(:link) { doc.css('a').first }
it 'replaces the content with the reference, but keeps the link', :aggregate_failures do it_behaves_like 'a no-op filter'
expect(doc.text).to eq(CGI.unescapeHTML("Added #{design.to_reference}"))
expect(link.attr('title')).to eq(design.filename)
expect(link.attr('href')).to eq(design_url)
end
end
end end
end end

View File

@ -102,7 +102,7 @@ RSpec.describe Gitlab::ImportExport::Base::RelationFactory do
"updated_at" => "2016-11-18T09:29:42.634Z", "updated_at" => "2016-11-18T09:29:42.634Z",
"user" => { "user" => {
"id" => 999, "id" => 999,
"email" => new_user.email, "public_email" => new_user.email,
"username" => new_user.username "username" => new_user.username
} }
} }

View File

@ -83,11 +83,12 @@ RSpec.describe Gitlab::ImportExport::Group::LegacyTreeSaver do
let(:user2) { create(:user, email: 'group@member.com') } let(:user2) { create(:user, email: 'group@member.com') }
let(:member_emails) do let(:member_emails) do
saved_group_json['members'].map do |pm| saved_group_json['members'].map do |pm|
pm['user']['email'] pm['user']['public_email']
end end
end end
before do before do
user2.update(public_email: user2.email)
group.add_developer(user2) group.add_developer(user2)
end end

View File

@ -24,7 +24,7 @@ RSpec.describe Gitlab::ImportExport::MembersMapper do
"user" => "user" =>
{ {
"id" => exported_user_id, "id" => exported_user_id,
"email" => user2.email, "public_email" => user2.email,
"username" => 'test' "username" => 'test'
}, },
"user_id" => 19 "user_id" => 19
@ -122,7 +122,7 @@ RSpec.describe Gitlab::ImportExport::MembersMapper do
"user" => "user" =>
{ {
"id" => exported_user_id, "id" => exported_user_id,
"email" => user2.email, "public_email" => user2.email,
"username" => 'test' "username" => 'test'
}, },
"user_id" => 19 "user_id" => 19
@ -157,6 +157,37 @@ RSpec.describe Gitlab::ImportExport::MembersMapper do
expect(members_mapper.map[exported_user_id]).to eq(user2.id) expect(members_mapper.map[exported_user_id]).to eq(user2.id)
end end
end end
context 'when user has email exported' do
let(:exported_members) do
[
{
"id" => 2,
"access_level" => 40,
"source_id" => 14,
"source_type" => source_type,
"notification_level" => 3,
"created_at" => "2016-03-11T10:21:44.822Z",
"updated_at" => "2016-03-11T10:21:44.822Z",
"created_by_id" => nil,
"invite_email" => nil,
"invite_token" => nil,
"invite_accepted_at" => nil,
"user" =>
{
"id" => exported_user_id,
"email" => user2.email,
"username" => 'test'
},
"user_id" => 19
}
]
end
it 'maps a member' do
expect(members_mapper.map[exported_user_id]).to eq(user2.id)
end
end
end end
context 'when importable is Project' do context 'when importable is Project' do
@ -212,7 +243,7 @@ RSpec.describe Gitlab::ImportExport::MembersMapper do
before do before do
group.add_users([user, user2], GroupMember::DEVELOPER) group.add_users([user, user2], GroupMember::DEVELOPER)
user.update(email: 'invite@test.com') user.update(public_email: 'invite@test.com')
end end
it 'maps the importer' do it 'maps the importer' do

View File

@ -119,7 +119,7 @@ RSpec.describe Gitlab::ImportExport::Project::RelationFactory, :use_clean_rails_
"updated_at" => "2016-11-18T09:29:42.634Z", "updated_at" => "2016-11-18T09:29:42.634Z",
"user" => { "user" => {
"id" => admin.id, "id" => admin.id,
"email" => admin.email, "public_email" => admin.email,
"username" => admin.username "username" => admin.username
} }
} }
@ -187,7 +187,7 @@ RSpec.describe Gitlab::ImportExport::Project::RelationFactory, :use_clean_rails_
"updated_at" => "2016-11-18T09:29:42.634Z", "updated_at" => "2016-11-18T09:29:42.634Z",
"user" => { "user" => {
"id" => admin.id, "id" => admin.id,
"email" => admin.email, "public_email" => admin.email,
"username" => admin.username "username" => admin.username
} }
} }

View File

@ -299,12 +299,13 @@ RSpec.describe Gitlab::ImportExport::Project::TreeSaver do
let(:member_emails) do let(:member_emails) do
emails = subject.map do |pm| emails = subject.map do |pm|
pm['user']['email'] pm['user']['public_email']
end end
emails emails
end end
before do before do
user2.update(public_email: user2.email)
group.add_developer(user2) group.add_developer(user2)
end end

View File

@ -188,6 +188,7 @@ RSpec.describe ObjectStorage::Config do
end end
context 'with SSE-KMS enabled' do context 'with SSE-KMS enabled' do
it { expect(subject.aws_server_side_encryption_enabled?).to be true }
it { expect(subject.server_side_encryption).to eq('AES256') } it { expect(subject.server_side_encryption).to eq('AES256') }
it { expect(subject.server_side_encryption_kms_key_id).to eq('arn:aws:12345') } it { expect(subject.server_side_encryption_kms_key_id).to eq('arn:aws:12345') }
it { expect(subject.fog_attributes.keys).to match_array(%w(x-amz-server-side-encryption x-amz-server-side-encryption-aws-kms-key-id)) } it { expect(subject.fog_attributes.keys).to match_array(%w(x-amz-server-side-encryption x-amz-server-side-encryption-aws-kms-key-id)) }
@ -196,6 +197,7 @@ RSpec.describe ObjectStorage::Config do
context 'with only server side encryption enabled' do context 'with only server side encryption enabled' do
let(:storage_options) { { server_side_encryption: 'AES256' } } let(:storage_options) { { server_side_encryption: 'AES256' } }
it { expect(subject.aws_server_side_encryption_enabled?).to be true }
it { expect(subject.server_side_encryption).to eq('AES256') } it { expect(subject.server_side_encryption).to eq('AES256') }
it { expect(subject.server_side_encryption_kms_key_id).to be_nil } it { expect(subject.server_side_encryption_kms_key_id).to be_nil }
it { expect(subject.fog_attributes).to eq({ 'x-amz-server-side-encryption' => 'AES256' }) } it { expect(subject.fog_attributes).to eq({ 'x-amz-server-side-encryption' => 'AES256' }) }
@ -204,6 +206,7 @@ RSpec.describe ObjectStorage::Config do
context 'without encryption enabled' do context 'without encryption enabled' do
let(:storage_options) { {} } let(:storage_options) { {} }
it { expect(subject.aws_server_side_encryption_enabled?).to be false }
it { expect(subject.server_side_encryption).to be_nil } it { expect(subject.server_side_encryption).to be_nil }
it { expect(subject.server_side_encryption_kms_key_id).to be_nil } it { expect(subject.server_side_encryption_kms_key_id).to be_nil }
it { expect(subject.fog_attributes).to eq({}) } it { expect(subject.fog_attributes).to eq({}) }
@ -215,6 +218,5 @@ RSpec.describe ObjectStorage::Config do
end end
it { expect(subject.enabled?).to be false } it { expect(subject.enabled?).to be false }
it { expect(subject.fog_attributes).to eq({}) }
end end
end end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!('update_external_project_bots')
RSpec.describe UpdateExternalProjectBots, :migration do
def create_user(**extra_options)
defaults = { projects_limit: 0, email: "#{extra_options[:username]}@example.com" }
table(:users).create!(defaults.merge(extra_options))
end
it 'sets bot users as external if were created by external users' do
internal_user = create_user(username: 'foo')
external_user = create_user(username: 'bar', external: true)
internal_project_bot = create_user(username: 'foo2', user_type: 6, created_by_id: internal_user.id, external: false)
external_project_bot = create_user(username: 'bar2', user_type: 6, created_by_id: external_user.id, external: false)
migrate!
expect(table(:users).find(internal_project_bot.id).external).to eq false
expect(table(:users).find(external_project_bot.id).external).to eq true
end
end

View File

@ -572,6 +572,12 @@ RSpec.describe DesignManagement::Design do
expect(described_class.link_reference_pattern).not_to match(url_for_designs(issue)) expect(described_class.link_reference_pattern).not_to match(url_for_designs(issue))
end end
it 'intentionally ignores filenames with any special character' do
design = build(:design, issue: issue, filename: '"invalid')
expect(described_class.link_reference_pattern).not_to match(url_for_design(design))
end
where(:ext) do where(:ext) do
(described_class::SAFE_IMAGE_EXT + described_class::DANGEROUS_IMAGE_EXT).flat_map do |ext| (described_class::SAFE_IMAGE_EXT + described_class::DANGEROUS_IMAGE_EXT).flat_map do |ext|
[[ext], [ext.upcase]] [[ext], [ext.upcase]]
@ -593,14 +599,6 @@ RSpec.describe DesignManagement::Design do
) )
end end
context 'the file needs to be encoded' do
let(:filename) { "my file.#{ext}" }
it 'extracts the encoded filename' do
expect(captures).to include('url_filename' => 'my%20file.' + ext)
end
end
context 'the file is all upper case' do context 'the file is all upper case' do
let(:filename) { "file.#{ext}".upcase } let(:filename) { "file.#{ext}".upcase }

View File

@ -127,18 +127,6 @@ RSpec.describe Integrations::Datadog do
end end
end end
describe '#api_keys_url' do
subject { instance.api_keys_url }
it { is_expected.to eq("https://app.#{dd_site}/account/settings#api") }
context 'with unset datadog_site' do
let(:dd_site) { '' }
it { is_expected.to eq("https://docs.datadoghq.com/account_management/api-app-keys/") }
end
end
describe '#test' do describe '#test' do
subject(:result) { saved_instance.test(pipeline_data) } subject(:result) { saved_instance.test(pipeline_data) }

View File

@ -37,24 +37,10 @@ RSpec.describe API::Ci::Pipelines do
end end
describe 'keys in the response' do describe 'keys in the response' do
context 'when `pipeline_source_filter` feature flag is disabled' do it 'includes pipeline source' do
before do get api("/projects/#{project.id}/pipelines", user)
stub_feature_flags(pipeline_source_filter: false)
end
it 'does not includes pipeline source' do expect(json_response.first.keys).to contain_exactly(*%w[id project_id sha ref status web_url created_at updated_at source])
get api("/projects/#{project.id}/pipelines", user)
expect(json_response.first.keys).to contain_exactly(*%w[id project_id sha ref status web_url created_at updated_at])
end
end
context 'when `pipeline_source_filter` feature flag is disabled' do
it 'includes pipeline source' do
get api("/projects/#{project.id}/pipelines", user)
expect(json_response.first.keys).to contain_exactly(*%w[id project_id sha ref status web_url created_at updated_at source])
end
end end
end end
@ -323,37 +309,20 @@ RSpec.describe API::Ci::Pipelines do
create(:ci_pipeline, project: project, source: :api) create(:ci_pipeline, project: project, source: :api)
end end
context 'when `pipeline_source_filter` feature flag is disabled' do it 'returns matched pipelines' do
before do get api("/projects/#{project.id}/pipelines", user), params: { source: 'web' }
stub_feature_flags(pipeline_source_filter: false)
end
it 'returns all pipelines' do expect(response).to have_gitlab_http_status(:ok)
get api("/projects/#{project.id}/pipelines", user), params: { source: 'web' } expect(response).to include_pagination_headers
expect(json_response).not_to be_empty
expect(response).to have_gitlab_http_status(:ok) json_response.each { |r| expect(r['source']).to eq('web') }
expect(response).to include_pagination_headers
expect(json_response).not_to be_empty
expect(json_response.length).to be >= 3
end
end end
context 'when `pipeline_source_filter` feature flag is enabled' do context 'when source is invalid' do
it 'returns matched pipelines' do it 'returns bad_request' do
get api("/projects/#{project.id}/pipelines", user), params: { source: 'web' } get api("/projects/#{project.id}/pipelines", user), params: { source: 'invalid-source' }
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:bad_request)
expect(response).to include_pagination_headers
expect(json_response).not_to be_empty
json_response.each { |r| expect(r['source']).to eq('web') }
end
context 'when source is invalid' do
it 'returns bad_request' do
get api("/projects/#{project.id}/pipelines", user), params: { source: 'invalid-source' }
expect(response).to have_gitlab_http_status(:bad_request)
end
end end
end end
end end

View File

@ -11,6 +11,7 @@ RSpec.describe "uploading designs" do
let(:project) { issue.project } let(:project) { issue.project }
let(:files) { [fixture_file_upload("spec/fixtures/dk.png")] } let(:files) { [fixture_file_upload("spec/fixtures/dk.png")] }
let(:variables) { {} } let(:variables) { {} }
let(:mutation_response) { graphql_mutation_response(:design_management_upload) }
def mutation def mutation
input = { input = {
@ -21,14 +22,32 @@ RSpec.describe "uploading designs" do
graphql_mutation(:design_management_upload, input) graphql_mutation(:design_management_upload, input)
end end
let(:mutation_response) { graphql_mutation_response(:design_management_upload) }
before do before do
enable_design_management enable_design_management
project.add_developer(current_user) project.add_developer(current_user)
end end
context 'when the input does not include a null value for each mapped file' do
let(:operations) { { query: mutation.query, variables: mutation.variables.merge(files: []) } }
let(:mapping) { { '1' => ['variables.files.0'] } }
let(:params) do
{ '1' => files.first, operations: operations.to_json, map: mapping.to_json }
end
it 'returns an error' do
workhorse_post_with_file(api('/', current_user, version: 'graphql'),
params: params,
file_key: '1'
)
expect(response).to have_attributes(
code: eq('400'),
body: include('out-of-bounds')
)
end
end
it "returns an error if the user is not allowed to upload designs" do it "returns an error if the user is not allowed to upload designs" do
post_graphql_mutation_with_uploads(mutation, current_user: create(:user)) post_graphql_mutation_with_uploads(mutation, current_user: create(:user))

View File

@ -274,7 +274,11 @@ RSpec.describe UsersController do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
end end
it_behaves_like 'renders all public keys' it 'redirects to sign in' do
get "/#{user.username}.keys"
expect(response).to redirect_to(new_user_session_path)
end
end end
end end
end end

View File

@ -152,56 +152,125 @@ RSpec.describe Groups::DestroyService do
end end
context 'for shared groups within different hierarchies' do context 'for shared groups within different hierarchies' do
let(:shared_with_group) { group } let(:group1) { create(:group, :private) }
let!(:shared_group) { create(:group, :private) } let(:group2) { create(:group, :private) }
let!(:shared_group_child) { create(:group, :private, parent: shared_group) }
let!(:shared_group_user) { create(:user) }
let!(:project) { create(:project, group: shared_group) } let(:group1_user) { create(:user) }
let!(:project_child) { create(:project, group: shared_group_child) } let(:group2_user) { create(:user) }
before do before do
shared_group.add_user(shared_group_user, Gitlab::Access::OWNER) group1.add_user(group1_user, Gitlab::Access::OWNER)
group2.add_user(group2_user, Gitlab::Access::OWNER)
create(:group_group_link, shared_group: shared_group, shared_with_group: shared_with_group)
shared_with_group.refresh_members_authorized_projects
end end
context 'the shared group is deleted' do context 'when a project is shared with a group' do
it 'updates project authorization' do let!(:group1_project) { create(:project, :private, group: group1) }
expect(shared_group_user.can?(:read_project, project)).to eq(true)
expect(shared_group_user.can?(:read_project, project_child)).to eq(true)
destroy_group(shared_group, shared_group_user, false) before do
create(:project_group_link, project: group1_project, group: group2)
expect(shared_group_user.can?(:read_project, project)).to eq(false)
expect(shared_group_user.can?(:read_project, project_child)).to eq(false)
end end
it 'does not make use of specific service to update project_authorizations records' do context 'and the shared group is deleted' do
expect(UserProjectAccessChangedService) it 'updates project authorizations so group2 users no longer have access', :aggregate_failures do
.not_to receive(:new).with(shared_group.user_ids_for_project_authorizations).and_call_original expect(group1_user.can?(:read_project, group1_project)).to eq(true)
expect(group2_user.can?(:read_project, group1_project)).to eq(true)
destroy_group(shared_group, shared_group_user, false) destroy_group(group2, group2_user, false)
expect(group1_user.can?(:read_project, group1_project)).to eq(true)
expect(group2_user.can?(:read_project, group1_project)).to eq(false)
end
it 'calls the service to update project authorizations only with necessary user ids' do
expect(UserProjectAccessChangedService)
.to receive(:new).with(array_including(group2_user.id)).and_call_original
destroy_group(group2, group2_user, false)
end
end
context 'and the group is shared with another group' do
let(:group3) { create(:group, :private) }
let(:group3_user) { create(:user) }
before do
group3.add_user(group3_user, Gitlab::Access::OWNER)
create(:group_group_link, shared_group: group2, shared_with_group: group3)
group3.refresh_members_authorized_projects
end
it 'updates project authorizations so group2 and group3 users no longer have access', :aggregate_failures do
expect(group1_user.can?(:read_project, group1_project)).to eq(true)
expect(group2_user.can?(:read_project, group1_project)).to eq(true)
expect(group3_user.can?(:read_project, group1_project)).to eq(true)
destroy_group(group2, group2_user, false)
expect(group1_user.can?(:read_project, group1_project)).to eq(true)
expect(group2_user.can?(:read_project, group1_project)).to eq(false)
expect(group3_user.can?(:read_project, group1_project)).to eq(false)
end
it 'calls the service to update project authorizations only with necessary user ids' do
expect(UserProjectAccessChangedService)
.to receive(:new).with(array_including(group2_user.id, group3_user.id)).and_call_original
destroy_group(group2, group2_user, false)
end
end end
end end
context 'the shared_with group is deleted' do context 'when a group is shared with a group' do
it 'updates project authorization' do let!(:group2_project) { create(:project, :private, group: group2) }
expect(user.can?(:read_project, project)).to eq(true)
expect(user.can?(:read_project, project_child)).to eq(true)
destroy_group(shared_with_group, user, false) before do
create(:group_group_link, shared_group: group2, shared_with_group: group1)
expect(user.can?(:read_project, project)).to eq(false) group1.refresh_members_authorized_projects
expect(user.can?(:read_project, project_child)).to eq(false)
end end
it 'makes use of a specific service to update project_authorizations records' do context 'and the shared group is deleted' do
expect(UserProjectAccessChangedService) it 'updates project authorizations since the project has been deleted with the group', :aggregate_failures do
.to receive(:new).with(shared_with_group.user_ids_for_project_authorizations).and_call_original expect(group1_user.can?(:read_project, group2_project)).to eq(true)
expect(group2_user.can?(:read_project, group2_project)).to eq(true)
destroy_group(shared_with_group, user, false) destroy_group(group2, group2_user, false)
expect(group1_user.can?(:read_project, group2_project)).to eq(false)
expect(group2_user.can?(:read_project, group2_project)).to eq(false)
end
it 'does not call the service to update project authorizations' do
expect(UserProjectAccessChangedService).not_to receive(:new)
destroy_group(group2, group2_user, false)
end
end
context 'the shared_with group is deleted' do
let!(:group2_subgroup) { create(:group, :private, parent: group2)}
let!(:group2_subgroup_project) { create(:project, :private, group: group2_subgroup) }
it 'updates project authorizations so users of both groups lose access', :aggregate_failures do
expect(group1_user.can?(:read_project, group2_project)).to eq(true)
expect(group2_user.can?(:read_project, group2_project)).to eq(true)
expect(group1_user.can?(:read_project, group2_subgroup_project)).to eq(true)
expect(group2_user.can?(:read_project, group2_subgroup_project)).to eq(true)
destroy_group(group1, group1_user, false)
expect(group1_user.can?(:read_project, group2_project)).to eq(false)
expect(group2_user.can?(:read_project, group2_project)).to eq(true)
expect(group1_user.can?(:read_project, group2_subgroup_project)).to eq(false)
expect(group2_user.can?(:read_project, group2_subgroup_project)).to eq(true)
end
it 'calls the service to update project authorizations only with necessary user ids' do
expect(UserProjectAccessChangedService)
.to receive(:new).with([group1_user.id]).and_call_original
destroy_group(group1, group1_user, false)
end
end end
end end
end end

View File

@ -7,8 +7,10 @@ RSpec.describe JiraConnectSubscriptions::CreateService do
let(:current_user) { create(:user) } let(:current_user) { create(:user) }
let(:group) { create(:group) } let(:group) { create(:group) }
let(:path) { group.full_path } let(:path) { group.full_path }
let(:params) { { namespace_path: path, jira_user: jira_user } }
let(:jira_user) { double(:JiraUser, site_admin?: true) }
subject { described_class.new(installation, current_user, namespace_path: path).execute } subject { described_class.new(installation, current_user, params).execute }
before do before do
group.add_maintainer(current_user) group.add_maintainer(current_user)
@ -24,6 +26,30 @@ RSpec.describe JiraConnectSubscriptions::CreateService do
end end
end end
context 'remote user does not have access' do
let(:jira_user) { double(site_admin?: false) }
it 'does not create a subscription' do
expect { subject }.not_to change { installation.subscriptions.count }
end
it 'returns error' do
expect(subject[:status]).to eq(:error)
end
end
context 'remote user cannot be retrieved' do
let(:jira_user) { nil }
it 'does not create a subscription' do
expect { subject }.not_to change { installation.subscriptions.count }
end
it 'returns error' do
expect(subject[:status]).to eq(:error)
end
end
context 'when user does have access' do context 'when user does have access' do
it 'creates a subscription' do it 'creates a subscription' do
expect { subject }.to change { installation.subscriptions.count }.from(0).to(1) expect { subject }.to change { installation.subscriptions.count }.from(0).to(1)

View File

@ -110,6 +110,18 @@ RSpec.describe ResourceAccessTokens::CreateService do
expect(resource.members.developers.map(&:user_id)).to include(bot_user.id) expect(resource.members.developers.map(&:user_id)).to include(bot_user.id)
end end
end end
context 'when user is external' do
let(:user) { create(:user, :external) }
before do
project.add_maintainer(user)
end
it 'creates resource bot user with external status' do
expect(subject.payload[:access_token].user.external).to eq true
end
end
end end
context 'personal access token' do context 'personal access token' do

View File

@ -19,7 +19,7 @@ RSpec.shared_examples 'Notes user references' do
'updated_at' => '2016-11-18T09:29:42.634Z', 'updated_at' => '2016-11-18T09:29:42.634Z',
'user' => { 'user' => {
'id' => 999, 'id' => 999,
'email' => mapped_user.email, 'public_email' => mapped_user.email,
'username' => mapped_user.username 'username' => mapped_user.username
} }
} }