Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
34d6e7c91b
commit
d9115c7779
41
CHANGELOG.md
41
CHANGELOG.md
|
@ -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)
|
||||||
|
|
2
Gemfile
2
Gemfile
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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) => ({
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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:
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'apollo_upload_server'
|
||||||
|
|
||||||
|
ApolloUploadServer::Middleware.strict_mode = true
|
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
f6f5e081672fb42adde980fa12f696f5d8fd11921ee52c1472b3d745bb11a5ff
|
|
@ -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",
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -259,7 +259,7 @@ Only include the following attributes for the models specified:
|
||||||
included_attributes:
|
included_attributes:
|
||||||
user:
|
user:
|
||||||
- :id
|
- :id
|
||||||
- :email
|
- :public_email
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -20,7 +20,7 @@ tree:
|
||||||
included_attributes:
|
included_attributes:
|
||||||
user:
|
user:
|
||||||
- :id
|
- :id
|
||||||
- :email
|
- :public_email
|
||||||
- :username
|
- :username
|
||||||
author:
|
author:
|
||||||
- :name
|
- :name
|
||||||
|
|
|
@ -20,7 +20,7 @@ tree:
|
||||||
included_attributes:
|
included_attributes:
|
||||||
user:
|
user:
|
||||||
- :id
|
- :id
|
||||||
- :email
|
- :public_email
|
||||||
- :username
|
- :username
|
||||||
author:
|
author:
|
||||||
- :name
|
- :name
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -115,7 +115,7 @@ tree:
|
||||||
included_attributes:
|
included_attributes:
|
||||||
user:
|
user:
|
||||||
- :id
|
- :id
|
||||||
- :email
|
- :public_email
|
||||||
- :username
|
- :username
|
||||||
author:
|
author:
|
||||||
- :name
|
- :name
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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') }
|
||||||
|
|
|
@ -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
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"}}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"}}
|
||||||
|
|
|
@ -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"}}
|
||||||
|
|
|
@ -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"}}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"}}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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 }
|
||||||
|
|
||||||
|
|
|
@ -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) }
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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))
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue