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:
- app/graphql/types/metrics/dashboards/annotation_type.rb
- app/graphql/types/packages/package_details_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'
gem 'aws-sdk-cloudformation', '~> 1'
gem 'aws-sdk-s3', '~> 1'
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
gem 'html-pipeline', '~> 2.13.2'
......
......@@ -333,6 +333,8 @@ GEM
escape_utils (1.2.1)
et-orbi (1.2.1)
tzinfo
ethon (0.15.0)
ffi (>= 1.15.0)
eventmachine (1.2.7)
excon (0.71.1)
execjs (2.8.1)
......@@ -1311,6 +1313,8 @@ GEM
tty-screen (~> 0.8)
wisper (~> 2.0)
tty-screen (0.8.1)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (2.0.4)
concurrent-ruby (~> 1.0)
u2f (0.2.1)
......@@ -1655,6 +1659,7 @@ DEPENDENCIES
timecop (~> 0.9.1)
toml-rb (~> 2.0)
truncato (~> 0.7.11)
typhoeus (~> 1.4.0)
u2f (~> 0.2.1)
undercover (~> 0.4.4)
unf (~> 0.1.4)
......
......@@ -68,15 +68,7 @@ export default {
GlModal: GlModalDirective,
},
mixins: [Tracking.mixin()],
inject: [
'packageId',
'projectName',
'canDelete',
'svgPath',
'npmPath',
'projectListUrl',
'groupListUrl',
],
inject: ['packageId', 'svgPath', 'projectListUrl', 'groupListUrl'],
trackingActions: {
DELETE_PACKAGE_TRACKING_ACTION,
REQUEST_DELETE_PACKAGE_TRACKING_ACTION,
......@@ -99,7 +91,7 @@ export default {
return this.queryVariables;
},
update(data) {
return data.package;
return data.package || {};
},
error(error) {
createFlash({
......@@ -111,19 +103,22 @@ export default {
},
},
computed: {
projectName() {
return this.packageEntity.project?.name;
},
queryVariables() {
return {
id: convertToGraphQLId('Packages::Package', this.packageId),
};
},
packageFiles() {
return this.packageEntity?.packageFiles?.nodes;
return this.packageEntity.packageFiles?.nodes;
},
isLoading() {
return this.$apollo.queries.packageEntity.loading;
},
isValidPackage() {
return this.isLoading || Boolean(this.packageEntity?.name);
return this.isLoading || Boolean(this.packageEntity.name);
},
tracking() {
return {
......@@ -140,7 +135,7 @@ export default {
return this.packageEntity.packageType === PACKAGE_TYPE_NUGET;
},
showFiles() {
return this.packageEntity?.packageType !== PACKAGE_TYPE_COMPOSER;
return this.packageEntity.packageType !== PACKAGE_TYPE_COMPOSER;
},
},
methods: {
......@@ -240,7 +235,7 @@ export default {
<package-title :package-entity="packageEntity">
<template #delete-button>
<gl-button
v-if="canDelete"
v-if="packageEntity.canDestroy"
v-gl-modal="'delete-modal'"
variant="danger"
category="primary"
......@@ -264,6 +259,7 @@ export default {
<package-files
v-if="showFiles"
:can-delete="packageEntity.canDestroy"
:package-files="packageFiles"
@download-file="track($options.trackingActions.PULL_PACKAGE)"
@delete-file="handleFileDelete"
......
......@@ -18,7 +18,7 @@ export default {
GlLink,
GlSprintf,
},
inject: ['composerConfigRepositoryName', 'composerPath', 'groupListUrl'],
inject: ['groupListUrl'],
props: {
packageEntity: {
type: Object,
......@@ -28,7 +28,7 @@ export default {
computed: {
composerRegistryInclude() {
// 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() {
// eslint-disable-next-line @gitlab/require-i18n-strings
......
......@@ -18,7 +18,6 @@ export default {
GlLink,
GlSprintf,
},
inject: ['conanPath'],
props: {
packageEntity: {
type: Object,
......@@ -32,7 +31,7 @@ export default {
},
conanSetupCommand() {
// eslint-disable-next-line @gitlab/require-i18n-strings
return `conan remote add gitlab ${this.conanPath}`;
return `conan remote add gitlab ${this.packageEntity.conanUrl}`;
},
},
i18n: {
......
......@@ -24,7 +24,6 @@ export default {
GlLink,
GlSprintf,
},
inject: ['mavenPath'],
props: {
packageEntity: {
type: Object,
......@@ -37,6 +36,9 @@ export default {
};
},
computed: {
mavenUrl() {
return this.packageEntity.mavenUrl;
},
appGroup() {
return this.packageEntity.metadata.appGroup;
},
......@@ -62,19 +64,19 @@ export default {
return `<repositories>
<repository>
<id>gitlab-maven</id>
<url>${this.mavenPath}</url>
<url>${this.mavenUrl}</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>gitlab-maven</id>
<url>${this.mavenPath}</url>
<url>${this.mavenUrl}</url>
</repository>
<snapshotRepository>
<id>gitlab-maven</id>
<url>${this.mavenPath}</url>
<url>${this.mavenUrl}</url>
</snapshotRepository>
</distributionManagement>`;
},
......@@ -87,7 +89,7 @@ export default {
gradleGroovyAddSourceCommand() {
// eslint-disable-next-line @gitlab/require-i18n-strings
return `maven {
url '${this.mavenPath}'
url '${this.mavenUrl}'
}`;
},
......@@ -96,7 +98,7 @@ export default {
},
gradleKotlinAddSourceCommand() {
return `maven("${this.mavenPath}")`;
return `maven("${this.mavenUrl}")`;
},
showMaven() {
return this.instructionType === 'maven';
......
......@@ -26,7 +26,7 @@ export default {
GlSprintf,
GlFormRadioGroup,
},
inject: ['npmPath', 'npmProjectPath'],
inject: ['npmPath'],
props: {
packageEntity: {
type: Object,
......@@ -66,7 +66,7 @@ export default {
npmSetupCommand(type, endpointType) {
const scope = this.packageEntity.name.substring(0, this.packageEntity.name.indexOf('/'));
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) {
return `echo ${scope}:registry=${npmPathForEndpoint}/ >> .npmrc`;
......
......@@ -18,7 +18,6 @@ export default {
GlLink,
GlSprintf,
},
inject: ['nugetPath'],
props: {
packageEntity: {
type: Object,
......@@ -30,7 +29,7 @@ export default {
return `nuget install ${this.packageEntity.name} -Source "GitLab"`;
},
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: {
......
......@@ -22,8 +22,12 @@ export default {
FileSha,
},
mixins: [Tracking.mixin()],
inject: ['canDelete'],
props: {
canDelete: {
type: Boolean,
required: false,
default: false,
},
packageFiles: {
type: Array,
required: false,
......
......@@ -19,7 +19,6 @@ export default {
GlLink,
GlSprintf,
},
inject: ['pypiPath', 'pypiSetupPath'],
props: {
packageEntity: {
type: Object,
......@@ -29,11 +28,11 @@ export default {
computed: {
pypiPipCommand() {
// 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() {
return `[gitlab]
repository = ${this.pypiSetupPath}
repository = ${this.packageEntity.pypiSetupUrl}
username = __token__
password = <your personal access token>`;
},
......
......@@ -7,9 +7,19 @@ query getPackageDetails($id: ID!) {
createdAt
updatedAt
status
canDestroy
npmUrl
mavenUrl
conanUrl
nugetUrl
pypiUrl
pypiSetupUrl
composerUrl
composerConfigRepositoryUrl
project {
id
path
name
}
tags(first: 10) {
nodes {
......
......@@ -124,7 +124,8 @@ module Auth
type: type,
name: path.to_s,
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
end
......@@ -150,6 +151,13 @@ module Auth
false
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,
# 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.
## 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.
When your license expires, GitLab locks features, like Git pushes
......
......@@ -26,7 +26,7 @@ module Types
description: 'Associated scanner profile.'
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,
description: 'Associated branch.',
......@@ -38,10 +38,6 @@ module Types
def edit_path
Gitlab::Routing.url_helpers.edit_project_on_demand_scan_path(object.project, object)
end
def dast_profile_schedule
object.dast_profile_schedule
end
end
end
end
......@@ -15,10 +15,7 @@ module Types
method: :state
field :normalized_target_url, GraphQL::Types::String, null: true,
description: 'Normalized URL of the target to be validated.'
def normalized_target_url
object.url_base
end
description: 'Normalized URL of the target to be validated.',
method: :url_base
end
end
......@@ -8,17 +8,10 @@ module Types
authorize :read_group_release_stats
field :releases_count, GraphQL::Types::Int, null: true,
description: 'Total number of releases in all descendant projects of the group.'
def releases_count
object.releases_count
end
description: 'Total number of releases in all descendant projects of the group.', method: :releases_count
field :releases_percentage, GraphQL::Types::Int, null: true,
description: "Percentage of the group's descendant projects that have at least one release."
def releases_percentage
object.releases_percentage
end
description: "Percentage of the group's descendant projects that have at least one release.",
method: :releases_percentage
end
end
......@@ -48,7 +48,8 @@ module Types
field :participants,
::Types::IncidentManagement::OncallParticipantType.connection_type,
null: true,
description: 'Participants of the on-call rotation.'
description: 'Participants of the on-call rotation.',
method: :active_participants
field :shifts,
::Types::IncidentManagement::OncallShiftType.connection_type,
......@@ -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.',
max_page_size: MAX_SHIFTS_FOR_TIMEFRAME,
resolver: ::Resolvers::IncidentManagement::OncallShiftsResolver
def participants
object.active_participants
end
end
end
end
......@@ -45,6 +45,11 @@ module EE
return if access_level.in?(levels)
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
override :post_create_hook
......
......@@ -11,9 +11,6 @@ class License < ApplicationRecord
LICENSE_FILE_TYPE = 'license_file'
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
EES_FEATURES_WITH_USAGE_PING = %i[
......@@ -626,22 +623,6 @@ class License < ApplicationRecord
super || created_at
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
def restricted_attr(name, default = nil)
......
......@@ -44,13 +44,25 @@ module AppSec
def ci_configuration
{
'stages' => [STAGE_NAME],
'include' => [{ 'template' => 'Security/DAST-On-Demand-Scan.gitlab-ci.yml' }],
'include' => [{ 'template' => dast_template }],
'dast' => {
'dast_configuration' => { 'site_profile' => dast_site_profile.name, 'scanner_profile' => dast_scanner_profile&.name }.compact
}
}.to_yaml
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
strong_memoize(:dast_profile) do
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
cattr_accessor :cached_client
cattr_accessor :cached_config
cattr_accessor :cached_adapter
def client(_client = nil)
store = ::GemExtensions::Elasticsearch::Model::Client
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_config = config
store.cached_adapter = adapter
end
end
......
......@@ -14,6 +14,7 @@ module Gitlab
# and configures itself based on those parameters
def self.build(config)
base_config = {
adapter: self.adapter,
urls: config[:url],
transport_options: {
request: {
......@@ -37,6 +38,10 @@ module Gitlab
end
end
def self.adapter
::Feature.enabled?(:use_typhoeus_elasticsearch_adapter) ? :typhoeus : :net_http
end
def self.resolve_aws_credentials(config)
# Resolve credentials in order
# 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
expect(options).to include(open_timeout: described_class::OPEN_TIMEOUT, timeout: nil)
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
let(:params) { { url: 'http://dummy-elastic:9200', client_request_timeout: 30 } }
......
......@@ -1623,134 +1623,4 @@ RSpec.describe License do
it { is_expected.to eq(license.created_at) }
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
......@@ -7,11 +7,13 @@ RSpec.describe API::Members do
context 'group members endpoints for group with minimal access feature' do
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(:owner) { create(:user) }
before do
group.add_owner(owner)
subgroup.add_owner(owner)
end
describe "GET /groups/:id/members" do
......@@ -41,21 +43,23 @@ RSpec.describe API::Members do
params: { user_id: stranger.id, access_level: Member::MINIMAL_ACCESS }
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
expect do
subject
end.not_to change { group.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'] })
expect(json_response['message']).to eq({ 'access_level' => ['is not included in the list', 'not supported by license'] })
end
end
context 'when minimal access role is available' do
it 'creates a member' do
context 'when minimal access license is available' do
before do
stub_licensed_features(minimal_access_role: true)
end
it 'creates a member' do
expect do
subject
end.to change { group.all_group_members.count }.by(1)
......@@ -63,6 +67,16 @@ RSpec.describe API::Members do
expect(response).to have_gitlab_http_status(:created)
expect(json_response['id']).to eq(stranger.id)
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
......
......@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe AppSec::Dast::ScanConfigs::BuildService do
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_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
let(:dast_full_scan_enabled) { dast_scanner_profile.full_scan_enabled? }
let(:dast_use_ajax_spider) { dast_scanner_profile.use_ajax_spider? }
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 } }
......@@ -28,7 +30,7 @@ RSpec.describe AppSec::Dast::ScanConfigs::BuildService do
stages:
- dast
include:
- template: Security/DAST-On-Demand-Scan.gitlab-ci.yml
- template: #{template}
dast:
dast_configuration:
site_profile: #{dast_site_profile.name}
......@@ -39,6 +41,7 @@ RSpec.describe AppSec::Dast::ScanConfigs::BuildService do
subject { described_class.new(container: project, params: params).execute }
describe 'execute' do
shared_examples 'build service execute tests' do
context 'when a dast_profile is provided' do
let(:params) { { dast_profile: dast_profile } }
......@@ -92,7 +95,7 @@ RSpec.describe AppSec::Dast::ScanConfigs::BuildService do
stages:
- dast
include:
- template: Security/DAST-On-Demand-Scan.gitlab-ci.yml
- template: #{template}
dast:
dast_configuration:
site_profile: #{dast_site_profile.name}
......@@ -120,4 +123,33 @@ RSpec.describe AppSec::Dast::ScanConfigs::BuildService do
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
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 @@
category: ci_templates
redis_slot: ci_templates
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
category: ci_templates
redis_slot: ci_templates
......@@ -539,6 +543,10 @@
category: ci_templates
redis_slot: ci_templates
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
category: ci_templates
redis_slot: ci_templates
......
......@@ -179,7 +179,7 @@ namespace :gitlab do
task reindex: :environment do
unless Gitlab::Database::Reindexing.enabled?
puts "This feature (database_reindexing) is currently disabled.".color(:yellow)
exit
next
end
Gitlab::Database::Reindexing.invoke
......@@ -193,7 +193,7 @@ namespace :gitlab do
task database_name => :environment do
unless Gitlab::Database::Reindexing.enabled?
puts "This feature (database_reindexing) is currently disabled.".color(:yellow)
exit
next
end
Gitlab::Database::Reindexing.invoke(database_name)
......
......@@ -23,7 +23,7 @@ exports[`ConanInstallation renders all the messages 1`] = `
<code-instruction-stub
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"
trackingaction="copy_conan_setup_command"
trackinglabel="code_instruction"
......
......@@ -19,7 +19,7 @@ exports[`MavenInstallation groovy renders all the messages 1`] = `
<code-instruction-stub
copytext="Copy add Gradle Groovy DSL repository command"
instruction="maven {
url 'mavenPath'
url 'http://gdk.test:3000/api/v4/projects/1/packages/maven'
}"
label="Add Gradle Groovy DSL repository command"
multiline="true"
......@@ -47,7 +47,7 @@ exports[`MavenInstallation kotlin renders all the messages 1`] = `
<code-instruction-stub
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"
multiline="true"
trackingaction="copy_kotlin_add_to_source_command"
......@@ -115,19 +115,19 @@ exports[`MavenInstallation maven renders all the messages 1`] = `
instruction="<repositories>
<repository>
<id>gitlab-maven</id>
<url>mavenPath</url>
<url>http://gdk.test:3000/api/v4/projects/1/packages/maven</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>gitlab-maven</id>
<url>mavenPath</url>
<url>http://gdk.test:3000/api/v4/projects/1/packages/maven</url>
</repository>
<snapshotRepository>
<id>gitlab-maven</id>
<url>mavenPath</url>
<url>http://gdk.test:3000/api/v4/projects/1/packages/maven</url>
</snapshotRepository>
</distributionManagement>"
label=""
......
......@@ -23,7 +23,7 @@ exports[`NugetInstallation renders all the messages 1`] = `
<code-instruction-stub
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"
trackingaction="copy_nuget_setup_command"
trackinglabel="code_instruction"
......
......@@ -10,7 +10,7 @@ exports[`PypiInstallation renders all the messages 1`] = `
<code-instruction-stub
copytext="Copy 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"
trackingaction="copy_pip_install_command"
trackinglabel="code_instruction"
......@@ -34,7 +34,7 @@ exports[`PypiInstallation renders all the messages 1`] = `
copytext="Copy .pypirc content"
data-testid="pypi-setup-content"
instruction="[gitlab]
repository = pypiSetupPath
repository = http://gdk.test:3000/api/v4/projects/1/packages/pypi
username = __token__
password = <your personal access token>"
label=""
......
......@@ -49,9 +49,6 @@ describe('PackagesApp', () => {
const provide = {
packageId: '111',
titleComponent: 'PackageTitle',
projectName: 'projectName',
canDelete: 'canDelete',
svgPath: 'svgPath',
npmPath: 'npmPath',
npmHelpPath: 'npmHelpPath',
......@@ -149,7 +146,7 @@ describe('PackagesApp', () => {
expect(findPackageHistory().exists()).toBe(true);
expect(findPackageHistory().props()).toMatchObject({
packageEntity: expect.objectContaining(packageData()),
projectName: provide.projectName,
projectName: packageDetailsQuery().data.package.project.name,
});
});
......@@ -177,7 +174,7 @@ describe('PackagesApp', () => {
describe('delete package', () => {
const originalReferrer = document.referrer;
const setReferrer = (value = provide.projectName) => {
const setReferrer = (value = packageDetailsQuery().data.package.project.name) => {
Object.defineProperty(document, 'referrer', {
value,
configurable: true,
......@@ -244,6 +241,7 @@ describe('PackagesApp', () => {
expect(findPackageFiles().exists()).toBe(true);
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 () => {
......
......@@ -25,8 +25,6 @@ describe('ComposerInstallation', () => {
function createComponent(groupListUrl = 'groupListUrl') {
wrapper = shallowMountExtended(ComposerInstallation, {
provide: {
composerConfigRepositoryName: 'composerConfigRepositoryName',
composerPath: 'composerPath',
groupListUrl,
},
propsData: { packageEntity },
......@@ -61,7 +59,7 @@ describe('ComposerInstallation', () => {
const registryIncludeCommand = findRegistryInclude();
expect(registryIncludeCommand.exists()).toBe(true);
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',
trackingAction: TRACKING_ACTION_COPY_COMPOSER_REGISTRY_INCLUDE_COMMAND,
});
......
......@@ -20,9 +20,6 @@ describe('ConanInstallation', () => {
function createComponent() {
wrapper = shallowMountExtended(ConanInstallation, {
provide: {
conanPath: 'conanPath',
},
propsData: {
packageEntity,
},
......@@ -65,7 +62,7 @@ describe('ConanInstallation', () => {
describe('setup commands', () => {
it('renders the correct command', () => {
expect(findCodeInstructions().at(1).props('instruction')).toBe(
'conan remote add gitlab conanPath',
`conan remote add gitlab ${packageEntity.conanUrl}`,
);
});
......
......@@ -30,8 +30,6 @@ describe('MavenInstallation', () => {
metadata: mavenMetadata(),
};
const mavenPath = 'mavenPath';
const xmlCodeBlock = `<dependency>
<groupId>appGroup</groupId>
<artifactId>appName</artifactId>
......@@ -41,27 +39,27 @@ describe('MavenInstallation', () => {
const mavenSetupXml = `<repositories>
<repository>
<id>gitlab-maven</id>
<url>${mavenPath}</url>
<url>${packageEntity.mavenUrl}</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>gitlab-maven</id>
<url>${mavenPath}</url>
<url>${packageEntity.mavenUrl}</url>
</repository>
<snapshotRepository>
<id>gitlab-maven</id>
<url>${mavenPath}</url>
<url>${packageEntity.mavenUrl}</url>
</snapshotRepository>
</distributionManagement>`;
const gradleGroovyInstallCommandText = `implementation 'appGroup:appName:appVersion'`;
const gradleGroovyAddSourceCommandText = `maven {
url '${mavenPath}'
url '${packageEntity.mavenUrl}'
}`;
const gradleKotlinInstallCommandText = `implementation("appGroup:appName:appVersion")`;
const gradleKotlinAddSourceCommandText = `maven("${mavenPath}")`;
const gradleKotlinAddSourceCommandText = `maven("${packageEntity.mavenUrl}")`;
const findCodeInstructions = () => wrapper.findAllComponents(CodeInstructions);
const findInstallationTitle = () => wrapper.findComponent(InstallationTitle);
......@@ -69,9 +67,6 @@ describe('MavenInstallation', () => {
function createComponent({ data = {} } = {}) {
wrapper = shallowMountExtended(MavenInstallation, {
provide: {
mavenPath,
},
propsData: {
packageEntity,
},
......
......@@ -36,7 +36,6 @@ describe('NpmInstallation', () => {
wrapper = shallowMountExtended(NpmInstallation, {
provide: {
npmPath: 'npmPath',
npmProjectPath: 'npmProjectPath',
},
propsData: {
packageEntity,
......@@ -130,7 +129,7 @@ describe('NpmInstallation', () => {
await nextTick();
expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: `echo @gitlab-org:registry=npmProjectPath/ >> .npmrc`,
instruction: `echo @gitlab-org:registry=${packageEntity.npmUrl}/ >> .npmrc`,
multiline: false,
trackingAction: TRACKING_ACTION_COPY_NPM_SETUP_COMMAND,
});
......@@ -174,7 +173,7 @@ describe('NpmInstallation', () => {
await nextTick();
expect(findCodeInstructions().at(1).props()).toMatchObject({
instruction: `echo \\"@gitlab-org:registry\\" \\"npmProjectPath/\\" >> .yarnrc`,
instruction: `echo \\"@gitlab-org:registry\\" \\"${packageEntity.npmUrl}/\\" >> .yarnrc`,
multiline: false,
trackingAction: TRACKING_ACTION_COPY_YARN_SETUP_COMMAND,
});
......
......@@ -17,8 +17,7 @@ describe('NugetInstallation', () => {
let wrapper;
const nugetInstallationCommandStr = 'nuget install @gitlab-org/package-15 -Source "GitLab"';
const nugetSetupCommandStr =
'nuget source Add -Name "GitLab" -Source "nugetPath" -UserName <your_username> -Password <your_token>';
const nugetSetupCommandStr = `nuget source Add -Name "GitLab" -Source "${packageEntity.nugetUrl}" -UserName <your_username> -Password <your_token>`;
const findCodeInstructions = () => wrapper.findAllComponents(CodeInstructions);
const findInstallationTitle = () => wrapper.findComponent(InstallationTitle);
......@@ -26,9 +25,6 @@ describe('NugetInstallation', () => {
function createComponent() {
wrapper = shallowMountExtended(NugetInstallation, {
provide: {
nugetPath: 'nugetPath',
},
propsData: {
packageEntity,
},
......
......@@ -28,8 +28,8 @@ describe('Package Files', () => {
const createComponent = ({ packageFiles = [file], canDelete = true } = {}) => {
wrapper = mountExtended(PackageFiles, {
provide: { canDelete },
propsData: {
canDelete,
packageFiles,
},
stubs: {
......
......@@ -15,9 +15,9 @@ const packageEntity = { ...packageData(), packageType: PACKAGE_TYPE_PYPI };
describe('PypiInstallation', () => {
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]
repository = pypiSetupPath
repository = ${packageEntity.pypiSetupUrl}
username = __token__
password = <your personal access token>`;
......@@ -29,10 +29,6 @@ password = <your personal access token>`;
function createComponent() {
wrapper = shallowMountExtended(PypiInstallation, {
provide: {
pypiPath: 'pypiPath',
pypiSetupPath: 'pypiSetupPath',
},
propsData: {
packageEntity,
},
......
......@@ -120,12 +120,22 @@ export const packageVersions = () => [
export const packageData = (extend) => ({
id: 'gid://gitlab/Packages::Package/111',
canDestroy: true,
name: '@gitlab-org/package-15',
packageType: 'NPM',
version: '1.0.0',
createdAt: '2020-08-17T14:23:32Z',
updatedAt: '2020-08-17T14:23:32Z',
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,
});
......@@ -185,6 +195,7 @@ export const packageDetailsQuery = (extendPackage) => ({
project: {
id: '1',
path: 'projectPath',
name: 'gitlab-test',
},
tags: {
nodes: packageTags(),
......
......@@ -145,4 +145,28 @@ RSpec.describe Auth::ContainerRegistryAuthenticationService do
it_behaves_like 'an unmodified token'
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
......@@ -71,6 +71,7 @@ end
RSpec.shared_examples 'an accessible' do
before do
stub_feature_flags(container_registry_migration_phase1: false)
stub_feature_flags(container_registry_cdn_redirect: false)
end
let(:access) do
......@@ -163,6 +164,7 @@ RSpec.shared_examples 'a container registry auth service' do
before do
stub_feature_flags(container_registry_migration_phase1: false)
stub_feature_flags(container_registry_cdn_redirect: false)
end
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