Commit b62592ae authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 87d9ec88 4270e89d
...@@ -6,7 +6,3 @@ GraphQL/FieldMethod: ...@@ -6,7 +6,3 @@ GraphQL/FieldMethod:
- app/graphql/types/metrics/dashboards/annotation_type.rb - app/graphql/types/metrics/dashboards/annotation_type.rb
- app/graphql/types/packages/package_details_type.rb - app/graphql/types/packages/package_details_type.rb
- app/graphql/types/project_type.rb - app/graphql/types/project_type.rb
- ee/app/graphql/types/dast/profile_type.rb
- ee/app/graphql/types/dast_site_validation_type.rb
- ee/app/graphql/types/group_release_stats_type.rb
- ee/app/graphql/types/incident_management/oncall_rotation_type.rb
...@@ -149,6 +149,7 @@ gem 'aws-sdk-core', '~> 3' ...@@ -149,6 +149,7 @@ gem 'aws-sdk-core', '~> 3'
gem 'aws-sdk-cloudformation', '~> 1' gem 'aws-sdk-cloudformation', '~> 1'
gem 'aws-sdk-s3', '~> 1' gem 'aws-sdk-s3', '~> 1'
gem 'faraday_middleware-aws-sigv4', '~>0.3.0' gem 'faraday_middleware-aws-sigv4', '~>0.3.0'
gem 'typhoeus', '~> 1.4.0' # Used with Elasticsearch to support http keep-alive connections
# Markdown and HTML processing # Markdown and HTML processing
gem 'html-pipeline', '~> 2.13.2' gem 'html-pipeline', '~> 2.13.2'
......
...@@ -333,6 +333,8 @@ GEM ...@@ -333,6 +333,8 @@ GEM
escape_utils (1.2.1) escape_utils (1.2.1)
et-orbi (1.2.1) et-orbi (1.2.1)
tzinfo tzinfo
ethon (0.15.0)
ffi (>= 1.15.0)
eventmachine (1.2.7) eventmachine (1.2.7)
excon (0.71.1) excon (0.71.1)
execjs (2.8.1) execjs (2.8.1)
...@@ -1311,6 +1313,8 @@ GEM ...@@ -1311,6 +1313,8 @@ GEM
tty-screen (~> 0.8) tty-screen (~> 0.8)
wisper (~> 2.0) wisper (~> 2.0)
tty-screen (0.8.1) tty-screen (0.8.1)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (2.0.4) tzinfo (2.0.4)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
u2f (0.2.1) u2f (0.2.1)
...@@ -1655,6 +1659,7 @@ DEPENDENCIES ...@@ -1655,6 +1659,7 @@ DEPENDENCIES
timecop (~> 0.9.1) timecop (~> 0.9.1)
toml-rb (~> 2.0) toml-rb (~> 2.0)
truncato (~> 0.7.11) truncato (~> 0.7.11)
typhoeus (~> 1.4.0)
u2f (~> 0.2.1) u2f (~> 0.2.1)
undercover (~> 0.4.4) undercover (~> 0.4.4)
unf (~> 0.1.4) unf (~> 0.1.4)
......
...@@ -68,15 +68,7 @@ export default { ...@@ -68,15 +68,7 @@ export default {
GlModal: GlModalDirective, GlModal: GlModalDirective,
}, },
mixins: [Tracking.mixin()], mixins: [Tracking.mixin()],
inject: [ inject: ['packageId', 'svgPath', 'projectListUrl', 'groupListUrl'],
'packageId',
'projectName',
'canDelete',
'svgPath',
'npmPath',
'projectListUrl',
'groupListUrl',
],
trackingActions: { trackingActions: {
DELETE_PACKAGE_TRACKING_ACTION, DELETE_PACKAGE_TRACKING_ACTION,
REQUEST_DELETE_PACKAGE_TRACKING_ACTION, REQUEST_DELETE_PACKAGE_TRACKING_ACTION,
...@@ -99,7 +91,7 @@ export default { ...@@ -99,7 +91,7 @@ export default {
return this.queryVariables; return this.queryVariables;
}, },
update(data) { update(data) {
return data.package; return data.package || {};
}, },
error(error) { error(error) {
createFlash({ createFlash({
...@@ -111,19 +103,22 @@ export default { ...@@ -111,19 +103,22 @@ export default {
}, },
}, },
computed: { computed: {
projectName() {
return this.packageEntity.project?.name;
},
queryVariables() { queryVariables() {
return { return {
id: convertToGraphQLId('Packages::Package', this.packageId), id: convertToGraphQLId('Packages::Package', this.packageId),
}; };
}, },
packageFiles() { packageFiles() {
return this.packageEntity?.packageFiles?.nodes; return this.packageEntity.packageFiles?.nodes;
}, },
isLoading() { isLoading() {
return this.$apollo.queries.packageEntity.loading; return this.$apollo.queries.packageEntity.loading;
}, },
isValidPackage() { isValidPackage() {
return this.isLoading || Boolean(this.packageEntity?.name); return this.isLoading || Boolean(this.packageEntity.name);
}, },
tracking() { tracking() {
return { return {
...@@ -140,7 +135,7 @@ export default { ...@@ -140,7 +135,7 @@ export default {
return this.packageEntity.packageType === PACKAGE_TYPE_NUGET; return this.packageEntity.packageType === PACKAGE_TYPE_NUGET;
}, },
showFiles() { showFiles() {
return this.packageEntity?.packageType !== PACKAGE_TYPE_COMPOSER; return this.packageEntity.packageType !== PACKAGE_TYPE_COMPOSER;
}, },
}, },
methods: { methods: {
...@@ -240,7 +235,7 @@ export default { ...@@ -240,7 +235,7 @@ export default {
<package-title :package-entity="packageEntity"> <package-title :package-entity="packageEntity">
<template #delete-button> <template #delete-button>
<gl-button <gl-button
v-if="canDelete" v-if="packageEntity.canDestroy"
v-gl-modal="'delete-modal'" v-gl-modal="'delete-modal'"
variant="danger" variant="danger"
category="primary" category="primary"
...@@ -264,6 +259,7 @@ export default { ...@@ -264,6 +259,7 @@ export default {
<package-files <package-files
v-if="showFiles" v-if="showFiles"
:can-delete="packageEntity.canDestroy"
:package-files="packageFiles" :package-files="packageFiles"
@download-file="track($options.trackingActions.PULL_PACKAGE)" @download-file="track($options.trackingActions.PULL_PACKAGE)"
@delete-file="handleFileDelete" @delete-file="handleFileDelete"
......
...@@ -18,7 +18,7 @@ export default { ...@@ -18,7 +18,7 @@ export default {
GlLink, GlLink,
GlSprintf, GlSprintf,
}, },
inject: ['composerConfigRepositoryName', 'composerPath', 'groupListUrl'], inject: ['groupListUrl'],
props: { props: {
packageEntity: { packageEntity: {
type: Object, type: Object,
...@@ -28,7 +28,7 @@ export default { ...@@ -28,7 +28,7 @@ export default {
computed: { computed: {
composerRegistryInclude() { composerRegistryInclude() {
// eslint-disable-next-line @gitlab/require-i18n-strings // eslint-disable-next-line @gitlab/require-i18n-strings
return `composer config repositories.${this.composerConfigRepositoryName} '{"type": "composer", "url": "${this.composerPath}"}'`; return `composer config repositories.${this.packageEntity.composerConfigRepositoryUrl} '{"type": "composer", "url": "${this.packageEntity.composerUrl}"}'`;
}, },
composerPackageInclude() { composerPackageInclude() {
// eslint-disable-next-line @gitlab/require-i18n-strings // eslint-disable-next-line @gitlab/require-i18n-strings
......
...@@ -18,7 +18,6 @@ export default { ...@@ -18,7 +18,6 @@ export default {
GlLink, GlLink,
GlSprintf, GlSprintf,
}, },
inject: ['conanPath'],
props: { props: {
packageEntity: { packageEntity: {
type: Object, type: Object,
...@@ -32,7 +31,7 @@ export default { ...@@ -32,7 +31,7 @@ export default {
}, },
conanSetupCommand() { conanSetupCommand() {
// eslint-disable-next-line @gitlab/require-i18n-strings // eslint-disable-next-line @gitlab/require-i18n-strings
return `conan remote add gitlab ${this.conanPath}`; return `conan remote add gitlab ${this.packageEntity.conanUrl}`;
}, },
}, },
i18n: { i18n: {
......
...@@ -24,7 +24,6 @@ export default { ...@@ -24,7 +24,6 @@ export default {
GlLink, GlLink,
GlSprintf, GlSprintf,
}, },
inject: ['mavenPath'],
props: { props: {
packageEntity: { packageEntity: {
type: Object, type: Object,
...@@ -37,6 +36,9 @@ export default { ...@@ -37,6 +36,9 @@ export default {
}; };
}, },
computed: { computed: {
mavenUrl() {
return this.packageEntity.mavenUrl;
},
appGroup() { appGroup() {
return this.packageEntity.metadata.appGroup; return this.packageEntity.metadata.appGroup;
}, },
...@@ -62,19 +64,19 @@ export default { ...@@ -62,19 +64,19 @@ export default {
return `<repositories> return `<repositories>
<repository> <repository>
<id>gitlab-maven</id> <id>gitlab-maven</id>
<url>${this.mavenPath}</url> <url>${this.mavenUrl}</url>
</repository> </repository>
</repositories> </repositories>
<distributionManagement> <distributionManagement>
<repository> <repository>
<id>gitlab-maven</id> <id>gitlab-maven</id>
<url>${this.mavenPath}</url> <url>${this.mavenUrl}</url>
</repository> </repository>
<snapshotRepository> <snapshotRepository>
<id>gitlab-maven</id> <id>gitlab-maven</id>
<url>${this.mavenPath}</url> <url>${this.mavenUrl}</url>
</snapshotRepository> </snapshotRepository>
</distributionManagement>`; </distributionManagement>`;
}, },
...@@ -87,7 +89,7 @@ export default { ...@@ -87,7 +89,7 @@ export default {
gradleGroovyAddSourceCommand() { gradleGroovyAddSourceCommand() {
// eslint-disable-next-line @gitlab/require-i18n-strings // eslint-disable-next-line @gitlab/require-i18n-strings
return `maven { return `maven {
url '${this.mavenPath}' url '${this.mavenUrl}'
}`; }`;
}, },
...@@ -96,7 +98,7 @@ export default { ...@@ -96,7 +98,7 @@ export default {
}, },
gradleKotlinAddSourceCommand() { gradleKotlinAddSourceCommand() {
return `maven("${this.mavenPath}")`; return `maven("${this.mavenUrl}")`;
}, },
showMaven() { showMaven() {
return this.instructionType === 'maven'; return this.instructionType === 'maven';
......
...@@ -26,7 +26,7 @@ export default { ...@@ -26,7 +26,7 @@ export default {
GlSprintf, GlSprintf,
GlFormRadioGroup, GlFormRadioGroup,
}, },
inject: ['npmPath', 'npmProjectPath'], inject: ['npmPath'],
props: { props: {
packageEntity: { packageEntity: {
type: Object, type: Object,
...@@ -66,7 +66,7 @@ export default { ...@@ -66,7 +66,7 @@ export default {
npmSetupCommand(type, endpointType) { npmSetupCommand(type, endpointType) {
const scope = this.packageEntity.name.substring(0, this.packageEntity.name.indexOf('/')); const scope = this.packageEntity.name.substring(0, this.packageEntity.name.indexOf('/'));
const npmPathForEndpoint = const npmPathForEndpoint =
endpointType === INSTANCE_PACKAGE_ENDPOINT_TYPE ? this.npmPath : this.npmProjectPath; endpointType === INSTANCE_PACKAGE_ENDPOINT_TYPE ? this.npmPath : this.packageEntity.npmUrl;
if (type === NPM_PACKAGE_MANAGER) { if (type === NPM_PACKAGE_MANAGER) {
return `echo ${scope}:registry=${npmPathForEndpoint}/ >> .npmrc`; return `echo ${scope}:registry=${npmPathForEndpoint}/ >> .npmrc`;
......
...@@ -18,7 +18,6 @@ export default { ...@@ -18,7 +18,6 @@ export default {
GlLink, GlLink,
GlSprintf, GlSprintf,
}, },
inject: ['nugetPath'],
props: { props: {
packageEntity: { packageEntity: {
type: Object, type: Object,
...@@ -30,7 +29,7 @@ export default { ...@@ -30,7 +29,7 @@ export default {
return `nuget install ${this.packageEntity.name} -Source "GitLab"`; return `nuget install ${this.packageEntity.name} -Source "GitLab"`;
}, },
nugetSetupCommand() { nugetSetupCommand() {
return `nuget source Add -Name "GitLab" -Source "${this.nugetPath}" -UserName <your_username> -Password <your_token>`; return `nuget source Add -Name "GitLab" -Source "${this.packageEntity.nugetUrl}" -UserName <your_username> -Password <your_token>`;
}, },
}, },
tracking: { tracking: {
......
...@@ -22,8 +22,12 @@ export default { ...@@ -22,8 +22,12 @@ export default {
FileSha, FileSha,
}, },
mixins: [Tracking.mixin()], mixins: [Tracking.mixin()],
inject: ['canDelete'],
props: { props: {
canDelete: {
type: Boolean,
required: false,
default: false,
},
packageFiles: { packageFiles: {
type: Array, type: Array,
required: false, required: false,
......
...@@ -19,7 +19,6 @@ export default { ...@@ -19,7 +19,6 @@ export default {
GlLink, GlLink,
GlSprintf, GlSprintf,
}, },
inject: ['pypiPath', 'pypiSetupPath'],
props: { props: {
packageEntity: { packageEntity: {
type: Object, type: Object,
...@@ -29,11 +28,11 @@ export default { ...@@ -29,11 +28,11 @@ export default {
computed: { computed: {
pypiPipCommand() { pypiPipCommand() {
// eslint-disable-next-line @gitlab/require-i18n-strings // eslint-disable-next-line @gitlab/require-i18n-strings
return `pip install ${this.packageEntity.name} --extra-index-url ${this.pypiPath}`; return `pip install ${this.packageEntity.name} --extra-index-url ${this.packageEntity.pypiUrl}`;
}, },
pypiSetupCommand() { pypiSetupCommand() {
return `[gitlab] return `[gitlab]
repository = ${this.pypiSetupPath} repository = ${this.packageEntity.pypiSetupUrl}
username = __token__ username = __token__
password = <your personal access token>`; password = <your personal access token>`;
}, },
......
...@@ -7,9 +7,19 @@ query getPackageDetails($id: ID!) { ...@@ -7,9 +7,19 @@ query getPackageDetails($id: ID!) {
createdAt createdAt
updatedAt updatedAt
status status
canDestroy
npmUrl
mavenUrl
conanUrl
nugetUrl
pypiUrl
pypiSetupUrl
composerUrl
composerConfigRepositoryUrl
project { project {
id id
path path
name
} }
tags(first: 10) { tags(first: 10) {
nodes { nodes {
......
...@@ -124,7 +124,8 @@ module Auth ...@@ -124,7 +124,8 @@ module Auth
type: type, type: type,
name: path.to_s, name: path.to_s,
actions: authorized_actions, actions: authorized_actions,
migration_eligible: self.class.migration_eligible(project: requested_project) migration_eligible: self.class.migration_eligible(project: requested_project),
cdn_redirect: cdn_redirect
}.compact }.compact
end end
...@@ -150,6 +151,13 @@ module Auth ...@@ -150,6 +151,13 @@ module Auth
false false
end end
# This is used to determine whether blob download requests using a given JWT token should be redirected to Google
# Cloud CDN or not. The intent is to enable a percentage of time rollout for this new feature on the Container
# Registry side. See https://gitlab.com/gitlab-org/gitlab/-/issues/349417 for more details.
def cdn_redirect
Feature.enabled?(:container_registry_cdn_redirect) || nil
end
## ##
# Because we do not have two way communication with registry yet, # Because we do not have two way communication with registry yet,
# we create a container repository image resource when push to the # we create a container repository image resource when push to the
......
---
name: container_registry_cdn_redirect
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77705
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/349717
milestone: '14.7'
type: development
group: group::package
default_enabled: false
---
name: dast_api_scanner
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73564
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345837
milestone: '14.7'
type: development
group: group::dynamic analysis
default_enabled: false
---
name: use_typhoeus_elasticsearch_adapter
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76879
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/348607
milestone: '14.7'
type: development
group: group::global search
default_enabled: false
...@@ -102,7 +102,7 @@ a license, upload the license in the **Admin Area** in the web user interface. ...@@ -102,7 +102,7 @@ a license, upload the license in the **Admin Area** in the web user interface.
## What happens when your license expires ## What happens when your license expires
Fifteen days before the license expires, a message with the upcoming expiration One month before the license expires, a message with the upcoming expiration
date displays to GitLab administrators. date displays to GitLab administrators.
When your license expires, GitLab locks features, like Git pushes When your license expires, GitLab locks features, like Git pushes
......
...@@ -26,7 +26,7 @@ module Types ...@@ -26,7 +26,7 @@ module Types
description: 'Associated scanner profile.' description: 'Associated scanner profile.'
field :dast_profile_schedule, ::Types::Dast::ProfileScheduleType, null: true, field :dast_profile_schedule, ::Types::Dast::ProfileScheduleType, null: true,
description: 'Associated profile schedule.' description: 'Associated profile schedule.', method: :dast_profile_schedule
field :branch, Dast::ProfileBranchType, null: true, field :branch, Dast::ProfileBranchType, null: true,
description: 'Associated branch.', description: 'Associated branch.',
...@@ -38,10 +38,6 @@ module Types ...@@ -38,10 +38,6 @@ module Types
def edit_path def edit_path
Gitlab::Routing.url_helpers.edit_project_on_demand_scan_path(object.project, object) Gitlab::Routing.url_helpers.edit_project_on_demand_scan_path(object.project, object)
end end
def dast_profile_schedule
object.dast_profile_schedule
end
end end
end end
end end
...@@ -15,10 +15,7 @@ module Types ...@@ -15,10 +15,7 @@ module Types
method: :state method: :state
field :normalized_target_url, GraphQL::Types::String, null: true, field :normalized_target_url, GraphQL::Types::String, null: true,
description: 'Normalized URL of the target to be validated.' description: 'Normalized URL of the target to be validated.',
method: :url_base
def normalized_target_url
object.url_base
end
end end
end end
...@@ -8,17 +8,10 @@ module Types ...@@ -8,17 +8,10 @@ module Types
authorize :read_group_release_stats authorize :read_group_release_stats
field :releases_count, GraphQL::Types::Int, null: true, field :releases_count, GraphQL::Types::Int, null: true,
description: 'Total number of releases in all descendant projects of the group.' description: 'Total number of releases in all descendant projects of the group.', method: :releases_count
def releases_count
object.releases_count
end
field :releases_percentage, GraphQL::Types::Int, null: true, field :releases_percentage, GraphQL::Types::Int, null: true,
description: "Percentage of the group's descendant projects that have at least one release." description: "Percentage of the group's descendant projects that have at least one release.",
method: :releases_percentage
def releases_percentage
object.releases_percentage
end
end end
end end
...@@ -48,7 +48,8 @@ module Types ...@@ -48,7 +48,8 @@ module Types
field :participants, field :participants,
::Types::IncidentManagement::OncallParticipantType.connection_type, ::Types::IncidentManagement::OncallParticipantType.connection_type,
null: true, null: true,
description: 'Participants of the on-call rotation.' description: 'Participants of the on-call rotation.',
method: :active_participants
field :shifts, field :shifts,
::Types::IncidentManagement::OncallShiftType.connection_type, ::Types::IncidentManagement::OncallShiftType.connection_type,
...@@ -56,10 +57,6 @@ module Types ...@@ -56,10 +57,6 @@ module Types
description: 'Blocks of time for which a participant is on-call within a given time frame. Time frame cannot exceed one month.', description: 'Blocks of time for which a participant is on-call within a given time frame. Time frame cannot exceed one month.',
max_page_size: MAX_SHIFTS_FOR_TIMEFRAME, max_page_size: MAX_SHIFTS_FOR_TIMEFRAME,
resolver: ::Resolvers::IncidentManagement::OncallShiftsResolver resolver: ::Resolvers::IncidentManagement::OncallShiftsResolver
def participants
object.active_participants
end
end end
end end
end end
...@@ -45,6 +45,11 @@ module EE ...@@ -45,6 +45,11 @@ module EE
return if access_level.in?(levels) return if access_level.in?(levels)
errors.add(:access_level, "is not included in the list") errors.add(:access_level, "is not included in the list")
if access_level == ::Gitlab::Access::MINIMAL_ACCESS
errors.add(:access_level, "supported on top level groups only") if group.has_parent?
errors.add(:access_level, "not supported by license") unless group.feature_available?(:minimal_access_role)
end
end end
override :post_create_hook override :post_create_hook
......
...@@ -11,9 +11,6 @@ class License < ApplicationRecord ...@@ -11,9 +11,6 @@ class License < ApplicationRecord
LICENSE_FILE_TYPE = 'license_file' LICENSE_FILE_TYPE = 'license_file'
ALLOWED_PERCENTAGE_OF_USERS_OVERAGE = (10 / 100.0) ALLOWED_PERCENTAGE_OF_USERS_OVERAGE = (10 / 100.0)
NOTIFICATION_DAYS_BEFORE_TRIAL_EXPIRY = 1.week
ADMIN_NOTIFICATION_DAYS_BEFORE_EXPIRY = 15.days
EE_ALL_PLANS = [STARTER_PLAN, PREMIUM_PLAN, ULTIMATE_PLAN].freeze EE_ALL_PLANS = [STARTER_PLAN, PREMIUM_PLAN, ULTIMATE_PLAN].freeze
EES_FEATURES_WITH_USAGE_PING = %i[ EES_FEATURES_WITH_USAGE_PING = %i[
...@@ -626,22 +623,6 @@ class License < ApplicationRecord ...@@ -626,22 +623,6 @@ class License < ApplicationRecord
super || created_at super || created_at
end end
# Overrides method from Gitlab::License which will be removed in a future version
def notify_admins?
return true if expired?
notification_days = trial? ? NOTIFICATION_DAYS_BEFORE_TRIAL_EXPIRY : ADMIN_NOTIFICATION_DAYS_BEFORE_EXPIRY
Date.today >= (expires_at - notification_days)
end
# Overrides method from Gitlab::License which will be removed in a future version
def notify_users?
notification_start_date = trial? ? expires_at - NOTIFICATION_DAYS_BEFORE_TRIAL_EXPIRY : block_changes_at
Date.today >= notification_start_date
end
private private
def restricted_attr(name, default = nil) def restricted_attr(name, default = nil)
......
...@@ -44,13 +44,25 @@ module AppSec ...@@ -44,13 +44,25 @@ module AppSec
def ci_configuration def ci_configuration
{ {
'stages' => [STAGE_NAME], 'stages' => [STAGE_NAME],
'include' => [{ 'template' => 'Security/DAST-On-Demand-Scan.gitlab-ci.yml' }], 'include' => [{ 'template' => dast_template }],
'dast' => { 'dast' => {
'dast_configuration' => { 'site_profile' => dast_site_profile.name, 'scanner_profile' => dast_scanner_profile&.name }.compact 'dast_configuration' => { 'site_profile' => dast_site_profile.name, 'scanner_profile' => dast_scanner_profile&.name }.compact
} }
}.to_yaml }.to_yaml
end end
def dast_template
if should_use_api_scan?
'Security/DAST-On-Demand-API-Scan.gitlab-ci.yml'
else
'Security/DAST-On-Demand-Scan.gitlab-ci.yml'
end
end
def should_use_api_scan?
Feature.enabled?(:dast_api_scanner, dast_site_profile.project, default_enabled: :yaml) && dast_site_profile.target_type == 'api'
end
def dast_profile def dast_profile
strong_memoize(:dast_profile) do strong_memoize(:dast_profile) do
params[:dast_profile] params[:dast_profile]
......
---
key_path: redis_hll_counters.ci_templates.p_ci_templates_security_dast_on_demand_api_scan_monthly
name: "dast_on_demand_api_scan"
description: Count of pipelines using the latest DAST API template
product_section: sec
product_stage: secure
product_group: "group::dynamic analysis"
product_category: DAST
value_type: number
status: active
milestone: "14.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73564
time_frame: 28d
data_source: redis_hll
data_category: optional
instrumentation_class: RedisHLLMetric
options:
events:
- p_ci_templates_security_dast_on_demand_api_scan
performance_indicator_type: []
distribution:
- ee
tier:
#- premium
- ultimate
---
key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_security_dast_on_demand_api_scan_monthly
name: "implicit_security_dast_on_demand_api_scan"
description: Count of pipelines with implicit runs using the latest DAST API template
product_section: sec
product_stage: secure
product_group: "group::dynamic analysis"
product_category: DAST
value_type: number
status: active
milestone: "14.7"
introduced_by_url:
time_frame: 28d
data_source: redis_hll
data_category: optional
instrumentation_class: RedisHLLMetric
options:
events:
- p_ci_templates_implicit_security_dast_on_demand_api_scan
performance_indicator_type: []
distribution:
- ee
tier:
#- premium
- ultimate
---
key_path: redis_hll_counters.ci_templates.p_ci_templates_security_dast_on_demand_api_scan_weekly
name: "dast_on_demand_api_scan"
description: Count of pipelines using the latest DAST API template
product_section: sec
product_stage: secure
product_group: "group::dynamic analysis"
product_category: DAST
value_type: number
status: active
milestone: "14.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73564
time_frame: 7d
data_source: redis_hll
data_category: optional
instrumentation_class: RedisHLLMetric
options:
events:
- p_ci_templates_security_dast_on_demand_api_scan
performance_indicator_type: []
distribution:
- ee
tier:
#- premium
- ultimate
---
key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_security_dast_on_demand_api_scan_weekly
name: "implicit_security_dast_on_demand_api_scan"
description: Count of pipelines with implicit runs using the latest DAST API template
product_section: sec
product_stage: secure
product_group: "group::dynamic analysis"
product_category: DAST
value_type: number
status: active
milestone: "14.7"
introduced_by_url:
time_frame: 7d
data_source: redis_hll
data_category: optional
instrumentation_class: RedisHLLMetric
options:
events:
- p_ci_templates_implicit_security_dast_on_demand_api_scan
performance_indicator_type: []
distribution:
- ee
tier:
#- premium
- ultimate
...@@ -15,16 +15,19 @@ module GemExtensions ...@@ -15,16 +15,19 @@ module GemExtensions
cattr_accessor :cached_client cattr_accessor :cached_client
cattr_accessor :cached_config cattr_accessor :cached_config
cattr_accessor :cached_adapter
def client(_client = nil) def client(_client = nil)
store = ::GemExtensions::Elasticsearch::Model::Client store = ::GemExtensions::Elasticsearch::Model::Client
store::CLIENT_MUTEX.synchronize do store::CLIENT_MUTEX.synchronize do
config = Gitlab::CurrentSettings.elasticsearch_config config = ::Gitlab::CurrentSettings.elasticsearch_config
adapter = ::Gitlab::Elastic::Client.adapter
if store.cached_client.nil? || config != store.cached_config if store.cached_client.nil? || config != store.cached_config || adapter != store.cached_adapter
store.cached_client = ::Gitlab::Elastic::Client.build(config) store.cached_client = ::Gitlab::Elastic::Client.build(config)
store.cached_config = config store.cached_config = config
store.cached_adapter = adapter
end end
end end
......
...@@ -14,6 +14,7 @@ module Gitlab ...@@ -14,6 +14,7 @@ module Gitlab
# and configures itself based on those parameters # and configures itself based on those parameters
def self.build(config) def self.build(config)
base_config = { base_config = {
adapter: self.adapter,
urls: config[:url], urls: config[:url],
transport_options: { transport_options: {
request: { request: {
...@@ -37,6 +38,10 @@ module Gitlab ...@@ -37,6 +38,10 @@ module Gitlab
end end
end end
def self.adapter
::Feature.enabled?(:use_typhoeus_elasticsearch_adapter) ? :typhoeus : :net_http
end
def self.resolve_aws_credentials(config) def self.resolve_aws_credentials(config)
# Resolve credentials in order # Resolve credentials in order
# 1. Static config # 1. Static config
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Expiring Subscription Message', :js, :freeze_time do
context 'for self-managed subscriptions' do
context 'when signed in user is an admin' do
let_it_be(:admin) { create(:admin) }
before do
create_current_license(plan: License::ULTIMATE_PLAN, expires_at: expires_at)
sign_in(admin)
gitlab_enable_admin_mode_sign_in(admin)
end
context 'with an expired license' do
let(:expires_at) { Date.current - 1.day }
it 'notifies the admin of the expired subscription' do
expect(page).to have_content('Your subscription expired!')
end
end
context 'with a license expiring in 15 days' do
let(:expires_at) { Date.current + 15.days }
it 'notifies the admin of a soon expiring subscription' do
expect(page).to have_content('Your subscription will expire in 15 days')
end
end
context 'with a license expiring in more than 15 days' do
let(:expires_at) { Date.current + 16.days }
it 'does not notify the admin of an expiring subscription' do
expect(page).not_to have_content('Your subscription will expire')
end
end
end
context 'when signed in user is not an admin' do
let_it_be(:user) { create(:user) }
before do
create_current_license(plan: License::ULTIMATE_PLAN, expires_at: expires_at, block_changes_at: block_changes_at)
sign_in(user)
visit root_path
end
context 'with an expired license in the grace period' do
let(:expires_at) { Date.current - 1.day }
let(:block_changes_at) { Date.current + 13.days }
it 'notifies the admin of the expired subscription' do
expect(page).not_to have_content('Your subscription expired!')
end
end
context 'with an expired license beyond the grace period' do
let(:expires_at) { Date.current - 15.days }
let(:block_changes_at) { Date.current - 1.day }
it 'notifies the admin of the expired subscription' do
expect(page).to have_content('Your subscription expired!')
end
end
end
end
context 'for namespace subscriptions', :saas do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
before do
enable_namespace_license_check!
create(:gitlab_subscription, namespace: group, end_date: end_date, auto_renew: false)
allow_next_instance_of(GitlabSubscriptions::CheckFutureRenewalService, namespace: group) do |service|
allow(service).to receive(:execute).and_return(false)
end
end
context 'when signed in user is a group owner' do
before do
group.add_owner(user)
sign_in(user)
visit group_path(group)
end
context 'with an expired license' do
let(:end_date) { Date.current - 1.day }
it 'notifies the group owner of the expired subscription' do
expect(page).to have_content('Your subscription expired!')
end
end
context 'with a license expiring in less than 30 days' do
let(:end_date) { Date.current + 29.days }
it 'notifies the group owner of a soon expiring subscription' do
expect(page).to have_content('Your subscription will expire in 29 days')
end
end
context 'with a license expiring in 30 or more days' do
let(:end_date) { Date.current + 30.days }
it 'does not notify the group owner of an expiring subscription' do
expect(page).not_to have_content('Your subscription will expire')
end
end
end
context 'when signed in user is not a group owner' do
before do
group.add_developer(user)
sign_in(user)
visit group_path(group)
end
context 'with an expired license' do
let(:end_date) { Date.current - 1.day }
it 'does not notify the user of the expired subscription' do
expect(page).not_to have_content('Your subscription expired!')
end
end
context 'with a license expiring in less than 30 days' do
let(:end_date) { Date.current + 29.days }
it 'does not notify the user of a soon expiring subscription' do
expect(page).not_to have_content('Your subscription will expire')
end
end
end
end
end
...@@ -23,6 +23,37 @@ RSpec.describe Gitlab::Elastic::Client do ...@@ -23,6 +23,37 @@ RSpec.describe Gitlab::Elastic::Client do
expect(options).to include(open_timeout: described_class::OPEN_TIMEOUT, timeout: nil) expect(options).to include(open_timeout: described_class::OPEN_TIMEOUT, timeout: nil)
end end
context 'with typhoeus adapter for keep-alive connections' do
it 'sets typhoeus as the adapter' do
options = client.transport.options
expect(options).to include(adapter: :typhoeus)
end
context 'when use_typhoeus_elasticsearch_adapter FeatureFlag is disabled' do
before do
stub_feature_flags(use_typhoeus_elasticsearch_adapter: false)
end
it 'uses the net/http adapter' do
options = client.transport.options
expect(options).to include(adapter: :net_http)
end
end
context 'cached client when FeatureFlag changes' do
it 'successfully changes adapter from net/http to typhoeus' do
stub_feature_flags(use_typhoeus_elasticsearch_adapter: false)
adapter = Issue.__elasticsearch__.client.transport.connections.first.connection.builder.adapter
expect(adapter).to eq(::Faraday::Adapter::NetHttp)
stub_feature_flags(use_typhoeus_elasticsearch_adapter: true)
adapter = Issue.__elasticsearch__.client.transport.connections.first.connection.builder.adapter
expect(adapter).to eq(::Faraday::Adapter::Typhoeus)
end
end
end
context 'with client_request_timeout in config' do context 'with client_request_timeout in config' do
let(:params) { { url: 'http://dummy-elastic:9200', client_request_timeout: 30 } } let(:params) { { url: 'http://dummy-elastic:9200', client_request_timeout: 30 } }
......
...@@ -1623,134 +1623,4 @@ RSpec.describe License do ...@@ -1623,134 +1623,4 @@ RSpec.describe License do
it { is_expected.to eq(license.created_at) } it { is_expected.to eq(license.created_at) }
end end
end end
describe '#notify_admins?', :freeze_time do
subject(:notify_admins?) { license.notify_admins? }
context 'when license has expired' do
before do
gl_license.expires_at = Date.yesterday
end
it { is_expected.to eq(true) }
end
context 'when license has not expired' do
context 'when license is a trial' do
before do
gl_license.restrictions = { trial: true }
end
context 'when license expiration is more than a week from today' do
before do
gl_license.expires_at = Date.today + 8.days
end
it { is_expected.to eq(false) }
end
context 'when license expiration is a week from today' do
before do
gl_license.expires_at = Date.today + 7.days
end
it { is_expected.to eq(true) }
end
context 'when license expiration is less than a week from today' do
before do
gl_license.expires_at = Date.today + 6.days
end
it { is_expected.to eq(true) }
end
end
context 'when license is not a trial' do
context 'when license expiration is more than 15 days from today' do
before do
gl_license.expires_at = Date.today + 16.days
end
it { is_expected.to eq(false) }
end
context 'when license expiration is 15 days from today' do
before do
gl_license.expires_at = Date.today + 15.days
end
it { is_expected.to eq(true) }
end
context 'when license expiration is less than 15 days from today' do
before do
gl_license.expires_at = Date.today + 14.days
end
it { is_expected.to eq(true) }
end
end
end
end
describe '#notify_users?', :freeze_time do
subject(:notify_users?) { license.notify_users? }
context 'when license is a trial' do
before do
gl_license.restrictions = { trial: true }
end
context 'when license expiration is more than a week from today' do
before do
gl_license.expires_at = Date.today + 8.days
end
it { is_expected.to eq(false) }
end
context 'when license expiration is a week from today' do
before do
gl_license.expires_at = Date.today + 7.days
end
it { is_expected.to eq(true) }
end
context 'when license expiration is less than a week from today' do
before do
gl_license.expires_at = Date.today + 6.days
end
it { is_expected.to eq(true) }
end
end
context 'when license is not a trial' do
context 'when license block changes date is before today' do
before do
gl_license.block_changes_at = Date.today - 1.day
end
it { is_expected.to eq(true) }
end
context 'when license block changes date is today' do
before do
gl_license.block_changes_at = Date.today
end
it { is_expected.to eq(true) }
end
context 'when license block changes date is after today' do
before do
gl_license.block_changes_at = Date.today + 1.day
end
it { is_expected.to eq(false) }
end
end
end
end end
...@@ -7,11 +7,13 @@ RSpec.describe API::Members do ...@@ -7,11 +7,13 @@ RSpec.describe API::Members do
context 'group members endpoints for group with minimal access feature' do context 'group members endpoints for group with minimal access feature' do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:subgroup) { create(:group, parent: group) }
let_it_be(:minimal_access_member) { create(:group_member, :minimal_access, source: group) } let_it_be(:minimal_access_member) { create(:group_member, :minimal_access, source: group) }
let_it_be(:owner) { create(:user) } let_it_be(:owner) { create(:user) }
before do before do
group.add_owner(owner) group.add_owner(owner)
subgroup.add_owner(owner)
end end
describe "GET /groups/:id/members" do describe "GET /groups/:id/members" do
...@@ -41,21 +43,23 @@ RSpec.describe API::Members do ...@@ -41,21 +43,23 @@ RSpec.describe API::Members do
params: { user_id: stranger.id, access_level: Member::MINIMAL_ACCESS } params: { user_id: stranger.id, access_level: Member::MINIMAL_ACCESS }
end end
context 'when minimal access role is not available' do context 'when minimal access license is not available' do
it 'does not create a member' do it 'does not create a member' do
expect do expect do
subject subject
end.not_to change { group.all_group_members.count } end.not_to change { group.all_group_members.count }
expect(response).to have_gitlab_http_status(:bad_request) expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']).to eq({ 'access_level' => ['is not included in the list'] }) expect(json_response['message']).to eq({ 'access_level' => ['is not included in the list', 'not supported by license'] })
end end
end end
context 'when minimal access role is available' do context 'when minimal access license is available' do
it 'creates a member' do before do
stub_licensed_features(minimal_access_role: true) stub_licensed_features(minimal_access_role: true)
end
it 'creates a member' do
expect do expect do
subject subject
end.to change { group.all_group_members.count }.by(1) end.to change { group.all_group_members.count }.by(1)
...@@ -63,6 +67,16 @@ RSpec.describe API::Members do ...@@ -63,6 +67,16 @@ RSpec.describe API::Members do
expect(response).to have_gitlab_http_status(:created) expect(response).to have_gitlab_http_status(:created)
expect(json_response['id']).to eq(stranger.id) expect(json_response['id']).to eq(stranger.id)
end end
it 'cannot be assigned to subgroup' do
expect do
post api("/groups/#{subgroup.id}/members", owner),
params: { user_id: stranger.id, access_level: Member::MINIMAL_ACCESS }
end.not_to change { subgroup.all_group_members.count }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']).to eq({ 'access_level' => ['is not included in the list', 'supported on top level groups only'] })
end
end end
end end
......
...@@ -4,7 +4,7 @@ require 'spec_helper' ...@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe AppSec::Dast::ScanConfigs::BuildService do RSpec.describe AppSec::Dast::ScanConfigs::BuildService do
let_it_be(:project) { create(:project, :repository) } let_it_be(:project) { create(:project, :repository) }
let_it_be(:dast_site_profile) { create(:dast_site_profile, project: project) } let_it_be_with_reload(:dast_site_profile) { create(:dast_site_profile, project: project, target_type: 'website') }
let_it_be(:dast_scanner_profile) { create(:dast_scanner_profile, project: project, spider_timeout: 5, target_timeout: 20) } let_it_be(:dast_scanner_profile) { create(:dast_scanner_profile, project: project, spider_timeout: 5, target_timeout: 20) }
let_it_be(:dast_profile) { create(:dast_profile, project: project, dast_site_profile: dast_site_profile, dast_scanner_profile: dast_scanner_profile, branch_name: 'master') } let_it_be(:dast_profile) { create(:dast_profile, project: project, dast_site_profile: dast_site_profile, dast_scanner_profile: dast_scanner_profile, branch_name: 'master') }
...@@ -19,6 +19,8 @@ RSpec.describe AppSec::Dast::ScanConfigs::BuildService do ...@@ -19,6 +19,8 @@ RSpec.describe AppSec::Dast::ScanConfigs::BuildService do
let(:dast_full_scan_enabled) { dast_scanner_profile.full_scan_enabled? } let(:dast_full_scan_enabled) { dast_scanner_profile.full_scan_enabled? }
let(:dast_use_ajax_spider) { dast_scanner_profile.use_ajax_spider? } let(:dast_use_ajax_spider) { dast_scanner_profile.use_ajax_spider? }
let(:dast_debug) { dast_scanner_profile.show_debug_messages? } let(:dast_debug) { dast_scanner_profile.show_debug_messages? }
let(:on_demand_scan_template) { 'Security/DAST-On-Demand-Scan.gitlab-ci.yml' }
let(:api_scan_template) { 'Security/DAST-On-Demand-API-Scan.gitlab-ci.yml' }
let(:params) { { dast_site_profile: dast_site_profile, dast_scanner_profile: dast_scanner_profile } } let(:params) { { dast_site_profile: dast_site_profile, dast_scanner_profile: dast_scanner_profile } }
...@@ -28,7 +30,7 @@ RSpec.describe AppSec::Dast::ScanConfigs::BuildService do ...@@ -28,7 +30,7 @@ RSpec.describe AppSec::Dast::ScanConfigs::BuildService do
stages: stages:
- dast - dast
include: include:
- template: Security/DAST-On-Demand-Scan.gitlab-ci.yml - template: #{template}
dast: dast:
dast_configuration: dast_configuration:
site_profile: #{dast_site_profile.name} site_profile: #{dast_site_profile.name}
...@@ -39,6 +41,7 @@ RSpec.describe AppSec::Dast::ScanConfigs::BuildService do ...@@ -39,6 +41,7 @@ RSpec.describe AppSec::Dast::ScanConfigs::BuildService do
subject { described_class.new(container: project, params: params).execute } subject { described_class.new(container: project, params: params).execute }
describe 'execute' do describe 'execute' do
shared_examples 'build service execute tests' do
context 'when a dast_profile is provided' do context 'when a dast_profile is provided' do
let(:params) { { dast_profile: dast_profile } } let(:params) { { dast_profile: dast_profile } }
...@@ -92,7 +95,7 @@ RSpec.describe AppSec::Dast::ScanConfigs::BuildService do ...@@ -92,7 +95,7 @@ RSpec.describe AppSec::Dast::ScanConfigs::BuildService do
stages: stages:
- dast - dast
include: include:
- template: Security/DAST-On-Demand-Scan.gitlab-ci.yml - template: #{template}
dast: dast:
dast_configuration: dast_configuration:
site_profile: #{dast_site_profile.name} site_profile: #{dast_site_profile.name}
...@@ -120,4 +123,33 @@ RSpec.describe AppSec::Dast::ScanConfigs::BuildService do ...@@ -120,4 +123,33 @@ RSpec.describe AppSec::Dast::ScanConfigs::BuildService do
end end
end end
end end
context 'when feature flag dast_api_scanner is disabled' do
let(:template) { on_demand_scan_template }
before do
stub_feature_flags(dast_api_scanner: false)
end
it_behaves_like 'build service execute tests'
end
context 'when feature flag dast_api_scanner is enabled' do
context 'when the target_type is api' do
before do
dast_site_profile.target_type = 'api'
end
let(:template) { api_scan_template }
it_behaves_like 'build service execute tests'
end
context 'when the target_type is NOT api' do
let(:template) { on_demand_scan_template }
it_behaves_like 'build service execute tests'
end
end
end
end end
stages:
- build
- test
- deploy
- dast
variables:
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
DAST_API_VERSION: "1"
DAST_API_IMAGE: $SECURE_ANALYZERS_PREFIX/api-fuzzing:$DAST_API_VERSION
dast:
stage: dast
image: $DAST_API_IMAGE
variables:
GIT_STRATEGY: none
allow_failure: true
script:
- /peach/analyzer-dast-api
artifacts:
when: always
paths:
- gl-assets
- gl-dast-api-report.json
- gl-*.log
reports:
dast: gl-dast-api-report.json
...@@ -103,6 +103,10 @@ ...@@ -103,6 +103,10 @@
category: ci_templates category: ci_templates
redis_slot: ci_templates redis_slot: ci_templates
aggregation: weekly aggregation: weekly
- name: p_ci_templates_security_dast_on_demand_api_scan
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
- name: p_ci_templates_security_coverage_fuzzing - name: p_ci_templates_security_coverage_fuzzing
category: ci_templates category: ci_templates
redis_slot: ci_templates redis_slot: ci_templates
...@@ -539,6 +543,10 @@ ...@@ -539,6 +543,10 @@
category: ci_templates category: ci_templates
redis_slot: ci_templates redis_slot: ci_templates
aggregation: weekly aggregation: weekly
- name: p_ci_templates_implicit_security_dast_on_demand_api_scan
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
- name: p_ci_templates_implicit_security_coverage_fuzzing - name: p_ci_templates_implicit_security_coverage_fuzzing
category: ci_templates category: ci_templates
redis_slot: ci_templates redis_slot: ci_templates
......
...@@ -179,7 +179,7 @@ namespace :gitlab do ...@@ -179,7 +179,7 @@ namespace :gitlab do
task reindex: :environment do task reindex: :environment do
unless Gitlab::Database::Reindexing.enabled? unless Gitlab::Database::Reindexing.enabled?
puts "This feature (database_reindexing) is currently disabled.".color(:yellow) puts "This feature (database_reindexing) is currently disabled.".color(:yellow)
exit next
end end
Gitlab::Database::Reindexing.invoke Gitlab::Database::Reindexing.invoke
...@@ -193,7 +193,7 @@ namespace :gitlab do ...@@ -193,7 +193,7 @@ namespace :gitlab do
task database_name => :environment do task database_name => :environment do
unless Gitlab::Database::Reindexing.enabled? unless Gitlab::Database::Reindexing.enabled?
puts "This feature (database_reindexing) is currently disabled.".color(:yellow) puts "This feature (database_reindexing) is currently disabled.".color(:yellow)
exit next
end end
Gitlab::Database::Reindexing.invoke(database_name) Gitlab::Database::Reindexing.invoke(database_name)
......
...@@ -23,7 +23,7 @@ exports[`ConanInstallation renders all the messages 1`] = ` ...@@ -23,7 +23,7 @@ exports[`ConanInstallation renders all the messages 1`] = `
<code-instruction-stub <code-instruction-stub
copytext="Copy Conan Setup Command" copytext="Copy Conan Setup Command"
instruction="conan remote add gitlab conanPath" instruction="conan remote add gitlab http://gdk.test:3000/api/v4/projects/1/packages/conan"
label="Add Conan Remote" label="Add Conan Remote"
trackingaction="copy_conan_setup_command" trackingaction="copy_conan_setup_command"
trackinglabel="code_instruction" trackinglabel="code_instruction"
......
...@@ -19,7 +19,7 @@ exports[`MavenInstallation groovy renders all the messages 1`] = ` ...@@ -19,7 +19,7 @@ exports[`MavenInstallation groovy renders all the messages 1`] = `
<code-instruction-stub <code-instruction-stub
copytext="Copy add Gradle Groovy DSL repository command" copytext="Copy add Gradle Groovy DSL repository command"
instruction="maven { instruction="maven {
url 'mavenPath' url 'http://gdk.test:3000/api/v4/projects/1/packages/maven'
}" }"
label="Add Gradle Groovy DSL repository command" label="Add Gradle Groovy DSL repository command"
multiline="true" multiline="true"
...@@ -47,7 +47,7 @@ exports[`MavenInstallation kotlin renders all the messages 1`] = ` ...@@ -47,7 +47,7 @@ exports[`MavenInstallation kotlin renders all the messages 1`] = `
<code-instruction-stub <code-instruction-stub
copytext="Copy add Gradle Kotlin DSL repository command" copytext="Copy add Gradle Kotlin DSL repository command"
instruction="maven(\\"mavenPath\\")" instruction="maven(\\"http://gdk.test:3000/api/v4/projects/1/packages/maven\\")"
label="Add Gradle Kotlin DSL repository command" label="Add Gradle Kotlin DSL repository command"
multiline="true" multiline="true"
trackingaction="copy_kotlin_add_to_source_command" trackingaction="copy_kotlin_add_to_source_command"
...@@ -115,19 +115,19 @@ exports[`MavenInstallation maven renders all the messages 1`] = ` ...@@ -115,19 +115,19 @@ exports[`MavenInstallation maven renders all the messages 1`] = `
instruction="<repositories> instruction="<repositories>
<repository> <repository>
<id>gitlab-maven</id> <id>gitlab-maven</id>
<url>mavenPath</url> <url>http://gdk.test:3000/api/v4/projects/1/packages/maven</url>
</repository> </repository>
</repositories> </repositories>
<distributionManagement> <distributionManagement>
<repository> <repository>
<id>gitlab-maven</id> <id>gitlab-maven</id>
<url>mavenPath</url> <url>http://gdk.test:3000/api/v4/projects/1/packages/maven</url>
</repository> </repository>
<snapshotRepository> <snapshotRepository>
<id>gitlab-maven</id> <id>gitlab-maven</id>
<url>mavenPath</url> <url>http://gdk.test:3000/api/v4/projects/1/packages/maven</url>
</snapshotRepository> </snapshotRepository>
</distributionManagement>" </distributionManagement>"
label="" label=""
......
...@@ -23,7 +23,7 @@ exports[`NugetInstallation renders all the messages 1`] = ` ...@@ -23,7 +23,7 @@ exports[`NugetInstallation renders all the messages 1`] = `
<code-instruction-stub <code-instruction-stub
copytext="Copy NuGet Setup Command" copytext="Copy NuGet Setup Command"
instruction="nuget source Add -Name \\"GitLab\\" -Source \\"nugetPath\\" -UserName <your_username> -Password <your_token>" instruction="nuget source Add -Name \\"GitLab\\" -Source \\"http://gdk.test:3000/api/v4/projects/1/packages/nuget/index.json\\" -UserName <your_username> -Password <your_token>"
label="Add NuGet Source" label="Add NuGet Source"
trackingaction="copy_nuget_setup_command" trackingaction="copy_nuget_setup_command"
trackinglabel="code_instruction" trackinglabel="code_instruction"
......
...@@ -10,7 +10,7 @@ exports[`PypiInstallation renders all the messages 1`] = ` ...@@ -10,7 +10,7 @@ exports[`PypiInstallation renders all the messages 1`] = `
<code-instruction-stub <code-instruction-stub
copytext="Copy Pip command" copytext="Copy Pip command"
data-testid="pip-command" data-testid="pip-command"
instruction="pip install @gitlab-org/package-15 --extra-index-url pypiPath" instruction="pip install @gitlab-org/package-15 --extra-index-url http://__token__:<your_personal_token>@gdk.test:3000/api/v4/projects/1/packages/pypi/simple"
label="Pip Command" label="Pip Command"
trackingaction="copy_pip_install_command" trackingaction="copy_pip_install_command"
trackinglabel="code_instruction" trackinglabel="code_instruction"
...@@ -34,7 +34,7 @@ exports[`PypiInstallation renders all the messages 1`] = ` ...@@ -34,7 +34,7 @@ exports[`PypiInstallation renders all the messages 1`] = `
copytext="Copy .pypirc content" copytext="Copy .pypirc content"
data-testid="pypi-setup-content" data-testid="pypi-setup-content"
instruction="[gitlab] instruction="[gitlab]
repository = pypiSetupPath repository = http://gdk.test:3000/api/v4/projects/1/packages/pypi
username = __token__ username = __token__
password = <your personal access token>" password = <your personal access token>"
label="" label=""
......
...@@ -49,9 +49,6 @@ describe('PackagesApp', () => { ...@@ -49,9 +49,6 @@ describe('PackagesApp', () => {
const provide = { const provide = {
packageId: '111', packageId: '111',
titleComponent: 'PackageTitle',
projectName: 'projectName',
canDelete: 'canDelete',
svgPath: 'svgPath', svgPath: 'svgPath',
npmPath: 'npmPath', npmPath: 'npmPath',
npmHelpPath: 'npmHelpPath', npmHelpPath: 'npmHelpPath',
...@@ -149,7 +146,7 @@ describe('PackagesApp', () => { ...@@ -149,7 +146,7 @@ describe('PackagesApp', () => {
expect(findPackageHistory().exists()).toBe(true); expect(findPackageHistory().exists()).toBe(true);
expect(findPackageHistory().props()).toMatchObject({ expect(findPackageHistory().props()).toMatchObject({
packageEntity: expect.objectContaining(packageData()), packageEntity: expect.objectContaining(packageData()),
projectName: provide.projectName, projectName: packageDetailsQuery().data.package.project.name,
}); });
}); });
...@@ -177,7 +174,7 @@ describe('PackagesApp', () => { ...@@ -177,7 +174,7 @@ describe('PackagesApp', () => {
describe('delete package', () => { describe('delete package', () => {
const originalReferrer = document.referrer; const originalReferrer = document.referrer;
const setReferrer = (value = provide.projectName) => { const setReferrer = (value = packageDetailsQuery().data.package.project.name) => {
Object.defineProperty(document, 'referrer', { Object.defineProperty(document, 'referrer', {
value, value,
configurable: true, configurable: true,
...@@ -244,6 +241,7 @@ describe('PackagesApp', () => { ...@@ -244,6 +241,7 @@ describe('PackagesApp', () => {
expect(findPackageFiles().exists()).toBe(true); expect(findPackageFiles().exists()).toBe(true);
expect(findPackageFiles().props('packageFiles')[0]).toMatchObject(expectedFile); expect(findPackageFiles().props('packageFiles')[0]).toMatchObject(expectedFile);
expect(findPackageFiles().props('canDelete')).toBe(packageData().canDestroy);
}); });
it('does not render the package files table when the package is composer', async () => { it('does not render the package files table when the package is composer', async () => {
......
...@@ -25,8 +25,6 @@ describe('ComposerInstallation', () => { ...@@ -25,8 +25,6 @@ describe('ComposerInstallation', () => {
function createComponent(groupListUrl = 'groupListUrl') { function createComponent(groupListUrl = 'groupListUrl') {
wrapper = shallowMountExtended(ComposerInstallation, { wrapper = shallowMountExtended(ComposerInstallation, {
provide: { provide: {
composerConfigRepositoryName: 'composerConfigRepositoryName',
composerPath: 'composerPath',
groupListUrl, groupListUrl,
}, },
propsData: { packageEntity }, propsData: { packageEntity },
...@@ -61,7 +59,7 @@ describe('ComposerInstallation', () => { ...@@ -61,7 +59,7 @@ describe('ComposerInstallation', () => {
const registryIncludeCommand = findRegistryInclude(); const registryIncludeCommand = findRegistryInclude();
expect(registryIncludeCommand.exists()).toBe(true); expect(registryIncludeCommand.exists()).toBe(true);
expect(registryIncludeCommand.props()).toMatchObject({ expect(registryIncludeCommand.props()).toMatchObject({
instruction: `composer config repositories.composerConfigRepositoryName '{"type": "composer", "url": "composerPath"}'`, instruction: `composer config repositories.${packageEntity.composerConfigRepositoryUrl} '{"type": "composer", "url": "${packageEntity.composerUrl}"}'`,
copyText: 'Copy registry include', copyText: 'Copy registry include',
trackingAction: TRACKING_ACTION_COPY_COMPOSER_REGISTRY_INCLUDE_COMMAND, trackingAction: TRACKING_ACTION_COPY_COMPOSER_REGISTRY_INCLUDE_COMMAND,
}); });
......
...@@ -20,9 +20,6 @@ describe('ConanInstallation', () => { ...@@ -20,9 +20,6 @@ describe('ConanInstallation', () => {
function createComponent() { function createComponent() {
wrapper = shallowMountExtended(ConanInstallation, { wrapper = shallowMountExtended(ConanInstallation, {
provide: {
conanPath: 'conanPath',
},
propsData: { propsData: {
packageEntity, packageEntity,
}, },
...@@ -65,7 +62,7 @@ describe('ConanInstallation', () => { ...@@ -65,7 +62,7 @@ describe('ConanInstallation', () => {
describe('setup commands', () => { describe('setup commands', () => {
it('renders the correct command', () => { it('renders the correct command', () => {
expect(findCodeInstructions().at(1).props('instruction')).toBe( expect(findCodeInstructions().at(1).props('instruction')).toBe(
'conan remote add gitlab conanPath', `conan remote add gitlab ${packageEntity.conanUrl}`,
); );
}); });
......
...@@ -30,8 +30,6 @@ describe('MavenInstallation', () => { ...@@ -30,8 +30,6 @@ describe('MavenInstallation', () => {
metadata: mavenMetadata(), metadata: mavenMetadata(),
}; };
const mavenPath = 'mavenPath';
const xmlCodeBlock = `<dependency> const xmlCodeBlock = `<dependency>
<groupId>appGroup</groupId> <groupId>appGroup</groupId>
<artifactId>appName</artifactId> <artifactId>appName</artifactId>
...@@ -41,27 +39,27 @@ describe('MavenInstallation', () => { ...@@ -41,27 +39,27 @@ describe('MavenInstallation', () => {
const mavenSetupXml = `<repositories> const mavenSetupXml = `<repositories>
<repository> <repository>
<id>gitlab-maven</id> <id>gitlab-maven</id>
<url>${mavenPath}</url> <url>${packageEntity.mavenUrl}</url>
</repository> </repository>
</repositories> </repositories>
<distributionManagement> <distributionManagement>
<repository> <repository>
<id>gitlab-maven</id> <id>gitlab-maven</id>
<url>${mavenPath}</url> <url>${packageEntity.mavenUrl}</url>
</repository> </repository>
<snapshotRepository> <snapshotRepository>
<id>gitlab-maven</id> <id>gitlab-maven</id>
<url>${mavenPath}</url> <url>${packageEntity.mavenUrl}</url>
</snapshotRepository> </snapshotRepository>
</distributionManagement>`; </distributionManagement>`;
const gradleGroovyInstallCommandText = `implementation 'appGroup:appName:appVersion'`; const gradleGroovyInstallCommandText = `implementation 'appGroup:appName:appVersion'`;
const gradleGroovyAddSourceCommandText = `maven { const gradleGroovyAddSourceCommandText = `maven {
url '${mavenPath}' url '${packageEntity.mavenUrl}'
}`; }`;
const gradleKotlinInstallCommandText = `implementation("appGroup:appName:appVersion")`; const gradleKotlinInstallCommandText = `implementation("appGroup:appName:appVersion")`;
const gradleKotlinAddSourceCommandText = `maven("${mavenPath}")`; const gradleKotlinAddSourceCommandText = `maven("${packageEntity.mavenUrl}")`;
const findCodeInstructions = () => wrapper.findAllComponents(CodeInstructions); const findCodeInstructions = () => wrapper.findAllComponents(CodeInstructions);
const findInstallationTitle = () => wrapper.findComponent(InstallationTitle); const findInstallationTitle = () => wrapper.findComponent(InstallationTitle);
...@@ -69,9 +67,6 @@ describe('MavenInstallation', () => { ...@@ -69,9 +67,6 @@ describe('MavenInstallation', () => {
function createComponent({ data = {} } = {}) { function createComponent({ data = {} } = {}) {
wrapper = shallowMountExtended(MavenInstallation, { wrapper = shallowMountExtended(MavenInstallation, {
provide: {
mavenPath,
},
propsData: { propsData: {
packageEntity, packageEntity,
}, },
......
...@@ -36,7 +36,6 @@ describe('NpmInstallation', () => { ...@@ -36,7 +36,6 @@ describe('NpmInstallation', () => {
wrapper = shallowMountExtended(NpmInstallation, { wrapper = shallowMountExtended(NpmInstallation, {
provide: { provide: {
npmPath: 'npmPath', npmPath: 'npmPath',
npmProjectPath: 'npmProjectPath',
}, },
propsData: { propsData: {
packageEntity, packageEntity,
...@@ -130,7 +129,7 @@ describe('NpmInstallation', () => { ...@@ -130,7 +129,7 @@ describe('NpmInstallation', () => {
await nextTick(); await nextTick();
expect(findCodeInstructions().at(1).props()).toMatchObject({ expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: `echo @gitlab-org:registry=npmProjectPath/ >> .npmrc`, instruction: `echo @gitlab-org:registry=${packageEntity.npmUrl}/ >> .npmrc`,
multiline: false, multiline: false,
trackingAction: TRACKING_ACTION_COPY_NPM_SETUP_COMMAND, trackingAction: TRACKING_ACTION_COPY_NPM_SETUP_COMMAND,
}); });
...@@ -174,7 +173,7 @@ describe('NpmInstallation', () => { ...@@ -174,7 +173,7 @@ describe('NpmInstallation', () => {
await nextTick(); await nextTick();
expect(findCodeInstructions().at(1).props()).toMatchObject({ expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: `echo \\"@gitlab-org:registry\\" \\"npmProjectPath/\\" >> .yarnrc`, instruction: `echo \\"@gitlab-org:registry\\" \\"${packageEntity.npmUrl}/\\" >> .yarnrc`,
multiline: false, multiline: false,
trackingAction: TRACKING_ACTION_COPY_YARN_SETUP_COMMAND, trackingAction: TRACKING_ACTION_COPY_YARN_SETUP_COMMAND,
}); });
......
...@@ -17,8 +17,7 @@ describe('NugetInstallation', () => { ...@@ -17,8 +17,7 @@ describe('NugetInstallation', () => {
let wrapper; let wrapper;
const nugetInstallationCommandStr = 'nuget install @gitlab-org/package-15 -Source "GitLab"'; const nugetInstallationCommandStr = 'nuget install @gitlab-org/package-15 -Source "GitLab"';
const nugetSetupCommandStr = const nugetSetupCommandStr = `nuget source Add -Name "GitLab" -Source "${packageEntity.nugetUrl}" -UserName <your_username> -Password <your_token>`;
'nuget source Add -Name "GitLab" -Source "nugetPath" -UserName <your_username> -Password <your_token>';
const findCodeInstructions = () => wrapper.findAllComponents(CodeInstructions); const findCodeInstructions = () => wrapper.findAllComponents(CodeInstructions);
const findInstallationTitle = () => wrapper.findComponent(InstallationTitle); const findInstallationTitle = () => wrapper.findComponent(InstallationTitle);
...@@ -26,9 +25,6 @@ describe('NugetInstallation', () => { ...@@ -26,9 +25,6 @@ describe('NugetInstallation', () => {
function createComponent() { function createComponent() {
wrapper = shallowMountExtended(NugetInstallation, { wrapper = shallowMountExtended(NugetInstallation, {
provide: {
nugetPath: 'nugetPath',
},
propsData: { propsData: {
packageEntity, packageEntity,
}, },
......
...@@ -28,8 +28,8 @@ describe('Package Files', () => { ...@@ -28,8 +28,8 @@ describe('Package Files', () => {
const createComponent = ({ packageFiles = [file], canDelete = true } = {}) => { const createComponent = ({ packageFiles = [file], canDelete = true } = {}) => {
wrapper = mountExtended(PackageFiles, { wrapper = mountExtended(PackageFiles, {
provide: { canDelete },
propsData: { propsData: {
canDelete,
packageFiles, packageFiles,
}, },
stubs: { stubs: {
......
...@@ -15,9 +15,9 @@ const packageEntity = { ...packageData(), packageType: PACKAGE_TYPE_PYPI }; ...@@ -15,9 +15,9 @@ const packageEntity = { ...packageData(), packageType: PACKAGE_TYPE_PYPI };
describe('PypiInstallation', () => { describe('PypiInstallation', () => {
let wrapper; let wrapper;
const pipCommandStr = 'pip install @gitlab-org/package-15 --extra-index-url pypiPath'; const pipCommandStr = `pip install @gitlab-org/package-15 --extra-index-url ${packageEntity.pypiUrl}`;
const pypiSetupStr = `[gitlab] const pypiSetupStr = `[gitlab]
repository = pypiSetupPath repository = ${packageEntity.pypiSetupUrl}
username = __token__ username = __token__
password = <your personal access token>`; password = <your personal access token>`;
...@@ -29,10 +29,6 @@ password = <your personal access token>`; ...@@ -29,10 +29,6 @@ password = <your personal access token>`;
function createComponent() { function createComponent() {
wrapper = shallowMountExtended(PypiInstallation, { wrapper = shallowMountExtended(PypiInstallation, {
provide: {
pypiPath: 'pypiPath',
pypiSetupPath: 'pypiSetupPath',
},
propsData: { propsData: {
packageEntity, packageEntity,
}, },
......
...@@ -120,12 +120,22 @@ export const packageVersions = () => [ ...@@ -120,12 +120,22 @@ export const packageVersions = () => [
export const packageData = (extend) => ({ export const packageData = (extend) => ({
id: 'gid://gitlab/Packages::Package/111', id: 'gid://gitlab/Packages::Package/111',
canDestroy: true,
name: '@gitlab-org/package-15', name: '@gitlab-org/package-15',
packageType: 'NPM', packageType: 'NPM',
version: '1.0.0', version: '1.0.0',
createdAt: '2020-08-17T14:23:32Z', createdAt: '2020-08-17T14:23:32Z',
updatedAt: '2020-08-17T14:23:32Z', updatedAt: '2020-08-17T14:23:32Z',
status: 'DEFAULT', status: 'DEFAULT',
mavenUrl: 'http://gdk.test:3000/api/v4/projects/1/packages/maven',
npmUrl: 'http://gdk.test:3000/api/v4/projects/1/packages/npm',
nugetUrl: 'http://gdk.test:3000/api/v4/projects/1/packages/nuget/index.json',
composerConfigRepositoryUrl: 'gdk.test/22',
composerUrl: 'http://gdk.test:3000/api/v4/group/22/-/packages/composer/packages.json',
conanUrl: 'http://gdk.test:3000/api/v4/projects/1/packages/conan',
pypiUrl:
'http://__token__:<your_personal_token>@gdk.test:3000/api/v4/projects/1/packages/pypi/simple',
pypiSetupUrl: 'http://gdk.test:3000/api/v4/projects/1/packages/pypi',
...extend, ...extend,
}); });
...@@ -185,6 +195,7 @@ export const packageDetailsQuery = (extendPackage) => ({ ...@@ -185,6 +195,7 @@ export const packageDetailsQuery = (extendPackage) => ({
project: { project: {
id: '1', id: '1',
path: 'projectPath', path: 'projectPath',
name: 'gitlab-test',
}, },
tags: { tags: {
nodes: packageTags(), nodes: packageTags(),
......
...@@ -145,4 +145,28 @@ RSpec.describe Auth::ContainerRegistryAuthenticationService do ...@@ -145,4 +145,28 @@ RSpec.describe Auth::ContainerRegistryAuthenticationService do
it_behaves_like 'an unmodified token' it_behaves_like 'an unmodified token'
end end
end end
context 'CDN redirection' do
include_context 'container registry auth service context'
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:current_params) { { scopes: ["repository:#{project.full_path}:pull"] } }
before do
project.add_developer(current_user)
end
it_behaves_like 'a valid token'
it { expect(payload['access']).to include(include('cdn_redirect' => true)) }
context 'when the feature flag is disabled' do
before do
stub_feature_flags(container_registry_cdn_redirect: false)
end
it_behaves_like 'a valid token'
it { expect(payload['access']).not_to include(have_key('cdn_redirect')) }
end
end
end end
...@@ -71,6 +71,7 @@ end ...@@ -71,6 +71,7 @@ end
RSpec.shared_examples 'an accessible' do RSpec.shared_examples 'an accessible' do
before do before do
stub_feature_flags(container_registry_migration_phase1: false) stub_feature_flags(container_registry_migration_phase1: false)
stub_feature_flags(container_registry_cdn_redirect: false)
end end
let(:access) do let(:access) do
...@@ -163,6 +164,7 @@ RSpec.shared_examples 'a container registry auth service' do ...@@ -163,6 +164,7 @@ RSpec.shared_examples 'a container registry auth service' do
before do before do
stub_feature_flags(container_registry_migration_phase1: false) stub_feature_flags(container_registry_migration_phase1: false)
stub_feature_flags(container_registry_cdn_redirect: false)
end end
describe '#full_access_token' do describe '#full_access_token' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment