Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/ci/rules.gitlab-ci.yml6
-rw-r--r--.gitlab/ci/vendored-gems.gitlab-ci.yml8
-rw-r--r--.rubocop_todo/layout/hash_alignment.yml21
-rw-r--r--Gemfile2
-rw-r--r--app/assets/javascripts/jobs/components/job_log_controllers.vue33
-rw-r--r--app/assets/javascripts/lib/utils/text_markdown.js10
-rw-r--r--app/assets/javascripts/pipeline_editor/components/validate/ci_validate.vue17
-rw-r--r--app/assets/javascripts/pipeline_editor/constants.js2
-rw-r--r--app/controllers/projects/alerting/notifications_controller.rb2
-rw-r--r--app/controllers/projects/jobs_controller.rb5
-rw-r--r--app/views/projects/labels/index.html.haml6
-rw-r--r--app/views/shared/_label_row.html.haml4
-rw-r--r--config/feature_flags/development/job_log_search.yml8
-rw-r--r--db/post_migrate/20220616171355_update_vulnerabilities_project_id_id_active_cis_index.rb24
-rw-r--r--db/schema_migrations/202206161713551
-rw-r--r--db/structure.sql4
-rw-r--r--doc/administration/audit_events.md1
-rw-r--r--doc/api/packages.md2
-rw-r--r--doc/ci/jobs/ci_job_token.md1
-rw-r--r--doc/ci/jobs/job_control.md3
-rw-r--r--doc/ci/pipelines/multi_project_pipelines.md4
-rw-r--r--doc/ci/yaml/index.md11
-rw-r--r--doc/user/admin_area/settings/visibility_and_access_controls.md1
-rw-r--r--doc/user/project/settings/index.md5
-rw-r--r--lib/api/project_packages.rb3
-rw-r--r--lib/api/releases.rb6
-rw-r--r--lib/gitlab/ci/config/entry/processable.rb2
-rw-r--r--lib/gitlab/ci/config/entry/variables.rb16
-rw-r--r--lib/gitlab/ci/variables/helpers.rb28
-rw-r--r--lib/gitlab/ci/yaml_processor/result.rb10
-rw-r--r--locale/gitlab.pot8
-rw-r--r--package.json1
-rw-r--r--spec/features/merge_request/user_creates_merge_request_spec.rb183
-rw-r--r--spec/frontend/jobs/components/job_log_controllers_spec.js42
-rw-r--r--spec/frontend/lib/utils/text_markdown_spec.js27
-rw-r--r--spec/frontend/pipeline_editor/components/validate/ci_validate_spec.js35
-rw-r--r--spec/graphql/mutations/merge_requests/create_spec.rb119
-rw-r--r--spec/lib/gitlab/ci/config/entry/root_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build_spec.rb68
-rw-r--r--spec/lib/gitlab/ci/variables/helpers_spec.rb123
-rw-r--r--spec/lib/gitlab/ci/yaml_processor/result_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb18
-rw-r--r--spec/policies/merge_request_policy_spec.rb374
-rw-r--r--spec/requests/api/merge_requests_spec.rb91
-rw-r--r--spec/requests/api/project_packages_spec.rb76
-rw-r--r--spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb2
-rw-r--r--spec/services/ci/list_config_variables_service_spec.rb4
-rw-r--r--spec/support/shared_examples/services/packages_shared_examples.rb20
-rw-r--r--vendor/gems/omniauth-azure-oauth2/.gitlab-ci.yml28
-rw-r--r--vendor/gems/omniauth-azure-oauth2/CHANGELOG.md31
-rw-r--r--vendor/gems/omniauth-azure-oauth2/Gemfile8
-rw-r--r--vendor/gems/omniauth-azure-oauth2/Gemfile.lock73
-rw-r--r--vendor/gems/omniauth-azure-oauth2/LICENSE22
-rw-r--r--vendor/gems/omniauth-azure-oauth2/README.md161
-rw-r--r--vendor/gems/omniauth-azure-oauth2/Rakefile6
-rw-r--r--vendor/gems/omniauth-azure-oauth2/examples/sinatra.rb31
-rw-r--r--vendor/gems/omniauth-azure-oauth2/lib/omniauth-azure-oauth2.rb1
-rw-r--r--vendor/gems/omniauth-azure-oauth2/lib/omniauth/azure_oauth2.rb1
-rw-r--r--vendor/gems/omniauth-azure-oauth2/lib/omniauth/azure_oauth2/version.rb5
-rw-r--r--vendor/gems/omniauth-azure-oauth2/lib/omniauth/strategies/azure_oauth2.rb73
-rw-r--r--vendor/gems/omniauth-azure-oauth2/omniauth-azure-oauth2.gemspec25
-rw-r--r--vendor/gems/omniauth-azure-oauth2/spec/omniauth/strategies/azure_oauth2_spec.rb332
-rw-r--r--vendor/gems/omniauth-azure-oauth2/spec/spec_helper.rb2
-rw-r--r--vendor/gems/omniauth_crowd/.gitlab-ci.yml2
-rw-r--r--workhorse/go.mod1
-rw-r--r--workhorse/go.sum1
-rw-r--r--yarn.lock2
67 files changed, 1772 insertions, 478 deletions
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index dd47acd0b7d..fcb853a7bd2 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -1467,6 +1467,12 @@
changes: ["vendor/gems/ipynbdiff/**/*"]
- <<: *if-merge-request-labels-run-all-rspec
+.vendor:rules:omniauth-azure-oauth2:
+ rules:
+ - <<: *if-merge-request
+ changes: ["vendor/gems/omniauth-azure-oauth2/**/*"]
+ - <<: *if-merge-request-labels-run-all-rspec
+
.vendor:rules:omniauth-cas3:
rules:
- <<: *if-merge-request
diff --git a/.gitlab/ci/vendored-gems.gitlab-ci.yml b/.gitlab/ci/vendored-gems.gitlab-ci.yml
index 275faa389e2..4408a6e4624 100644
--- a/.gitlab/ci/vendored-gems.gitlab-ci.yml
+++ b/.gitlab/ci/vendored-gems.gitlab-ci.yml
@@ -14,6 +14,14 @@ vendor ipynbdiff:
include: vendor/gems/ipynbdiff/.gitlab-ci.yml
strategy: depend
+vendor omniauth-azure-oauth2:
+ extends:
+ - .vendor:rules:omniauth-azure-oauth2
+ needs: []
+ trigger:
+ include: vendor/gems/omniauth-azure-oauth2/.gitlab-ci.yml
+ strategy: depend
+
vendor omniauth-cas3:
extends:
- .vendor:rules:omniauth-cas3
diff --git a/.rubocop_todo/layout/hash_alignment.yml b/.rubocop_todo/layout/hash_alignment.yml
index 0f440235339..4a98bcea706 100644
--- a/.rubocop_todo/layout/hash_alignment.yml
+++ b/.rubocop_todo/layout/hash_alignment.yml
@@ -2,27 +2,6 @@
# Cop supports --auto-correct.
Layout/HashAlignment:
Exclude:
- - 'ee/app/graphql/types/vulnerable_dependency_type.rb'
- - 'ee/app/graphql/types/vulnerable_kubernetes_resource_type.rb'
- - 'ee/app/graphql/types/vulnerable_projects_by_grade_type.rb'
- - 'ee/app/graphql/types/work_items/widgets/verification_status_type.rb'
- - 'ee/app/graphql/types/work_items/widgets/weight_type.rb'
- - 'ee/app/helpers/ee/geo_helper.rb'
- - 'ee/app/serializers/ee/evidences/release_entity.rb'
- - 'ee/app/services/ci/external_pull_requests/process_github_event_service.rb'
- - 'ee/app/services/ci_cd/setup_project.rb'
- - 'ee/app/services/ee/issues/base_service.rb'
- - 'ee/app/services/elastic/cluster_reindexing_service.rb'
- - 'ee/app/services/elastic/process_bookkeeping_service.rb'
- - 'ee/app/services/epics/epic_links/list_service.rb'
- - 'ee/app/services/epics/issue_promote_service.rb'
- - 'ee/app/services/groups/memberships/export_service.rb'
- - 'ee/app/services/projects/setup_ci_cd.rb'
- - 'ee/app/services/security/security_orchestration_policies/on_demand_scan_pipeline_configuration_service.rb'
- - 'ee/config/routes/project.rb'
- - 'ee/config/routes/uploads.rb'
- - 'ee/elastic/migrate/20220118150500_delete_orphaned_commits.rb'
- - 'ee/elastic/migrate/20220119120500_populate_commit_permissions_in_main_index.rb'
- 'ee/lib/api/iterations.rb'
- 'ee/lib/api/merge_trains.rb'
- 'ee/lib/api/related_epic_links.rb'
diff --git a/Gemfile b/Gemfile
index 5cf417a67ae..e85488a34fb 100644
--- a/Gemfile
+++ b/Gemfile
@@ -39,7 +39,7 @@ gem 'ruby-saml', '~> 1.13.0'
gem 'omniauth', '~> 1.8'
gem 'omniauth-auth0', '~> 2.0.0'
gem 'omniauth-azure-activedirectory-v2', '~> 1.0'
-gem 'omniauth-azure-oauth2', '~> 0.0.9' # Deprecated v1 version
+gem 'omniauth-azure-oauth2', '~> 0.0.9' # See vendor/gems/omniauth-azure-oauth2/README.md
gem 'omniauth-cas3', '~> 1.1.4', path: 'vendor/gems/omniauth-cas3' # See vendor/gems/omniauth-cas3/README.md
gem 'omniauth-dingtalk-oauth2', '~> 1.0'
gem 'omniauth-alicloud', '~> 1.0.1'
diff --git a/app/assets/javascripts/jobs/components/job_log_controllers.vue b/app/assets/javascripts/jobs/components/job_log_controllers.vue
index 9f5dbe70331..e9809ac661b 100644
--- a/app/assets/javascripts/jobs/components/job_log_controllers.vue
+++ b/app/assets/javascripts/jobs/components/job_log_controllers.vue
@@ -79,9 +79,6 @@ export default {
size: numberToHumanSize(this.size),
});
},
- showJobLogSearch() {
- return this.glFeatures.jobLogSearch;
- },
showJumpToFailures() {
return this.glFeatures.jobLogJumpToFailures;
},
@@ -198,24 +195,22 @@ export default {
<!-- eo truncate information -->
<div class="controllers">
- <template v-if="showJobLogSearch">
- <gl-search-box-by-click
- v-model="searchTerm"
- class="gl-mr-3"
- :placeholder="$options.i18n.searchPlaceholder"
- data-testid="job-log-search-box"
- @clear="$emit('searchResults', [])"
- @submit="searchJobLog"
- />
+ <gl-search-box-by-click
+ v-model="searchTerm"
+ class="gl-mr-3"
+ :placeholder="$options.i18n.searchPlaceholder"
+ data-testid="job-log-search-box"
+ @clear="$emit('searchResults', [])"
+ @submit="searchJobLog"
+ />
- <help-popover class="gl-mr-3">
- <template #title>{{ $options.i18n.searchPopoverTitle }}</template>
+ <help-popover class="gl-mr-3">
+ <template #title>{{ $options.i18n.searchPopoverTitle }}</template>
- <p class="gl-mb-0">
- {{ $options.i18n.searchPopoverDescription }}
- </p>
- </help-popover>
- </template>
+ <p class="gl-mb-0">
+ {{ $options.i18n.searchPopoverDescription }}
+ </p>
+ </help-popover>
<!-- links -->
<gl-button
diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js
index 9f4e12a3010..48be8af3ff6 100644
--- a/app/assets/javascripts/lib/utils/text_markdown.js
+++ b/app/assets/javascripts/lib/utils/text_markdown.js
@@ -263,10 +263,12 @@ export function insertMarkdownText({
if (tag === LINK_TAG_PATTERN) {
if (URL) {
try {
- new URL(selected); // eslint-disable-line no-new
- // valid url
- tag = '[text]({text})';
- select = 'text';
+ const url = new URL(selected);
+
+ if (url.origin !== 'null' || url.origin === null) {
+ tag = '[text]({text})';
+ select = 'text';
+ }
} catch (e) {
// ignore - no valid url
}
diff --git a/app/assets/javascripts/pipeline_editor/components/validate/ci_validate.vue b/app/assets/javascripts/pipeline_editor/components/validate/ci_validate.vue
index a4ef0befb91..83fcab4b343 100644
--- a/app/assets/javascripts/pipeline_editor/components/validate/ci_validate.vue
+++ b/app/assets/javascripts/pipeline_editor/components/validate/ci_validate.vue
@@ -11,6 +11,8 @@ import {
GlSprintf,
} from '@gitlab/ui';
import { s__, __ } from '~/locale';
+import Tracking from '~/tracking';
+import { pipelineEditorTrackingOptions } from '../../constants';
import ValidatePipelinePopover from '../popovers/validate_pipeline_popover.vue';
import CiLintResults from '../lint/ci_lint_results.vue';
import getBlobContent from '../../graphql/queries/blob_content.query.graphql';
@@ -70,6 +72,7 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
+ mixins: [Tracking.mixin()],
inject: ['ciConfigPath', 'ciLintPath', 'projectFullPath', 'validateTabIllustrationPath'],
props: {
ciFileContent: {
@@ -110,6 +113,9 @@ export default {
};
},
computed: {
+ canResimulatePipeline() {
+ return this.hasSimulationResults && this.hasCiContentChanged;
+ },
isInitialCiContentLoading() {
return this.$apollo.queries.initialBlobContent.loading;
},
@@ -128,6 +134,10 @@ export default {
variant: this.isValid ? 'success' : 'danger',
};
},
+ trackingAction() {
+ const { actions } = pipelineEditorTrackingOptions;
+ return this.canResimulatePipeline ? actions.resimulatePipeline : actions.simulatePipeline;
+ },
},
watch: {
ciFileContent(value) {
@@ -139,7 +149,12 @@ export default {
cancelSimulation() {
this.state = VALIDATE_TAB_INIT;
},
+ trackSimulation() {
+ const { label } = pipelineEditorTrackingOptions;
+ this.track(this.trackingAction, { label });
+ },
async validateYaml() {
+ this.trackSimulation();
this.state = VALIDATE_TAB_LOADING;
try {
@@ -198,7 +213,7 @@ export default {
:aria-label="$options.i18n.help"
/>
</div>
- <div v-if="hasSimulationResults && hasCiContentChanged">
+ <div v-if="canResimulatePipeline">
<span class="gl-text-gray-400" data-testid="content-status">
{{ $options.i18n.contentChange }}
</span>
diff --git a/app/assets/javascripts/pipeline_editor/constants.js b/app/assets/javascripts/pipeline_editor/constants.js
index 4b07045e638..dd25c4d433b 100644
--- a/app/assets/javascripts/pipeline_editor/constants.js
+++ b/app/assets/javascripts/pipeline_editor/constants.js
@@ -75,6 +75,8 @@ export const pipelineEditorTrackingOptions = {
[CI_YAML_LINK]: 'visit_help_drawer_link_yaml',
},
openHelpDrawer: 'open_help_drawer',
+ resimulatePipeline: 'resimulate_pipeline',
+ simulatePipeline: 'simulate_pipeline',
},
};
diff --git a/app/controllers/projects/alerting/notifications_controller.rb b/app/controllers/projects/alerting/notifications_controller.rb
index 82fff287c4a..f3283c88740 100644
--- a/app/controllers/projects/alerting/notifications_controller.rb
+++ b/app/controllers/projects/alerting/notifications_controller.rb
@@ -13,8 +13,6 @@ module Projects
prepend_before_action :repository, :project_without_auth
feature_category :incident_management
- # Goal is to increase the urgency to medium.
- # See https://gitlab.com/gitlab-org/gitlab/-/issues/361310.
urgency :low, [:create]
def create
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index 8570bbf1dbd..7878ace5015 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -18,7 +18,6 @@ class Projects::JobsController < Projects::ApplicationController
before_action :verify_api_request!, only: :terminal_websocket_authorize
before_action :authorize_create_proxy_build!, only: :proxy_websocket_authorize
before_action :verify_proxy_request!, only: :proxy_websocket_authorize
- before_action :push_job_log_search, only: [:show]
before_action :push_job_log_jump_to_failures, only: [:show]
before_action :reject_if_build_artifacts_size_refreshing!, only: [:erase]
@@ -250,10 +249,6 @@ class Projects::JobsController < Projects::ApplicationController
::Gitlab::Workhorse.channel_websocket(service)
end
- def push_job_log_search
- push_frontend_feature_flag(:job_log_search, @project)
- end
-
def push_job_log_jump_to_failures
push_frontend_feature_flag(:job_log_jump_to_failures, @project)
end
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index 647464b31f8..f7a02c521f5 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -11,14 +11,14 @@
.labels-container.gl-mt-5
- if can_admin_label && search.blank?
%p.text-muted
- = _('Labels can be applied to issues and merge requests.')
- %br
- = _('Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging.')
+ = _('Labels can be applied to issues and merge requests. Star a label to make it a priority label.')
-# Only show it in the first page
- hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1')
.prioritized-labels.gl-mb-7{ class: [('hide' if hide), ('is-not-draggable' unless can_admin_label)] }
%h4.gl-mt-3= _('Prioritized Labels')
+ %p.text-muted
+ = _('Drag to reorder prioritized labels and change their relative priority.')
.manage-labels-list.js-prioritized-labels{ data: { url: set_priorities_project_labels_path(@project), sortable: can_admin_label } }
#js-priority-labels-empty-state.priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty? && search.blank?}" }
= render 'shared/empty_states/priority_labels'
diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml
index 252f9c26f06..c351ea29c7c 100644
--- a/app/views/shared/_label_row.html.haml
+++ b/app/views/shared/_label_row.html.haml
@@ -3,11 +3,11 @@
- show_label_issues_link = subject_or_group_defined && show_label_issuables_link?(label, :issues)
- show_label_merge_requests_link = subject_or_group_defined && show_label_issuables_link?(label, :merge_requests)
-.label-name.gl-flex-shrink-0.gl-mt-2.gl-mr-3
+.label-name.gl-flex-shrink-0.gl-mt-2.gl-mr-5
= render_label(label, tooltip: false)
.label-description.gl-overflow-hidden.gl-w-full
.gl-display-flex.gl-align-items-stretch.gl-flex-wrap.gl-mt-2
- .gl-flex-basis-half.gl-flex-grow-1.gl-overflow-hidden.gl-mr-2
+ .gl-flex-basis-half.gl-flex-grow-1.gl-overflow-hidden.gl-mr-5
- if label.description.present?
= markdown_field(label, :description)
- elsif show_labels_full_path?(@project, @group)
diff --git a/config/feature_flags/development/job_log_search.yml b/config/feature_flags/development/job_log_search.yml
deleted file mode 100644
index b6f1cec26f6..00000000000
--- a/config/feature_flags/development/job_log_search.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: job_log_search
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91293
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366455
-milestone: '15.2'
-type: development
-group: group::pipeline execution
-default_enabled: false
diff --git a/db/post_migrate/20220616171355_update_vulnerabilities_project_id_id_active_cis_index.rb b/db/post_migrate/20220616171355_update_vulnerabilities_project_id_id_active_cis_index.rb
new file mode 100644
index 00000000000..047ae0d1132
--- /dev/null
+++ b/db/post_migrate/20220616171355_update_vulnerabilities_project_id_id_active_cis_index.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class UpdateVulnerabilitiesProjectIdIdActiveCisIndex < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ NEW_INDEX_NAME = 'idx_vulnerabilities_on_project_id_and_id_active_cis_dft_branch'
+ OLD_INDEX_NAME = 'index_vulnerabilities_on_project_id_and_id_active_cis'
+ OLD_INDEX_FILTER_CONDITION = 'report_type = 7 AND state = ANY(ARRAY[1, 4])'
+ NEW_INDEX_FILTER_CONDITION = 'report_type = 7 AND state = ANY(ARRAY[1, 4]) AND present_on_default_branch IS TRUE'
+
+ def up
+ add_concurrent_index :vulnerabilities, [:project_id, :id],
+ where: NEW_INDEX_FILTER_CONDITION,
+ name: NEW_INDEX_NAME
+
+ remove_concurrent_index_by_name(:vulnerabilities, OLD_INDEX_NAME)
+ end
+
+ def down
+ add_concurrent_index :vulnerabilities, [:project_id, :id], where: OLD_INDEX_FILTER_CONDITION, name: OLD_INDEX_NAME
+
+ remove_concurrent_index_by_name(:vulnerabilities, NEW_INDEX_NAME)
+ end
+end
diff --git a/db/schema_migrations/20220616171355 b/db/schema_migrations/20220616171355
new file mode 100644
index 00000000000..cd212025f70
--- /dev/null
+++ b/db/schema_migrations/20220616171355
@@ -0,0 +1 @@
+63ec85b4f8b7eb15c232c4a25c1e63027c38c23caf81a89c4d05227a6be00e4b \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index b055d831ce6..aff91f735e7 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -27472,6 +27472,8 @@ CREATE UNIQUE INDEX idx_vuln_signatures_on_occurrences_id_and_signature_sha ON v
CREATE UNIQUE INDEX idx_vuln_signatures_uniqueness_signature_sha ON vulnerability_finding_signatures USING btree (finding_id, algorithm_type, signature_sha);
+CREATE INDEX idx_vulnerabilities_on_project_id_and_id_active_cis_dft_branch ON vulnerabilities USING btree (project_id, id) WHERE ((report_type = 7) AND (state = ANY (ARRAY[1, 4])) AND (present_on_default_branch IS TRUE));
+
CREATE INDEX idx_vulnerabilities_partial_devops_adoption_and_default_branch ON vulnerabilities USING btree (project_id, created_at, present_on_default_branch) WHERE (state <> 1);
CREATE UNIQUE INDEX idx_vulnerability_ext_issue_links_on_vulne_id_and_ext_issue ON vulnerability_external_issue_links USING btree (vulnerability_id, external_type, external_project_key, external_issue_key);
@@ -30364,8 +30366,6 @@ CREATE INDEX index_vulnerabilities_on_last_edited_by_id ON vulnerabilities USING
CREATE INDEX index_vulnerabilities_on_milestone_id ON vulnerabilities USING btree (milestone_id);
-CREATE INDEX index_vulnerabilities_on_project_id_and_id_active_cis ON vulnerabilities USING btree (project_id, id) WHERE ((report_type = 7) AND (state = ANY (ARRAY[1, 4])));
-
CREATE INDEX index_vulnerabilities_on_project_id_and_state_and_severity ON vulnerabilities USING btree (project_id, state, severity);
CREATE INDEX index_vulnerabilities_on_resolved_by_id ON vulnerabilities USING btree (resolved_by_id);
diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md
index 0aa766aa0d4..92504c226fb 100644
--- a/doc/administration/audit_events.md
+++ b/doc/administration/audit_events.md
@@ -152,6 +152,7 @@ From there, you can see the following actions:
- Added, removed, or updated protected branches
- Release was added to a project
- Release was updated
+- Release was deleted ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94793/) in GitLab 13.5)
- Release milestone associations changed
- Permission to approve merge requests by committers was updated ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7531) in GitLab 12.9)
- Permission to approve merge requests by committers was updated.
diff --git a/doc/api/packages.md b/doc/api/packages.md
index 9d9c21546cd..3cd93bd09c1 100644
--- a/doc/api/packages.md
+++ b/doc/api/packages.md
@@ -6,6 +6,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Packages API **(FREE)**
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/349418) support for [GitLab CI/CD job token](../ci/jobs/ci_job_token.md) authentication for the project-level API in GitLab 15.3.
+
This is the API documentation of [GitLab Packages](../administration/packages/index.md).
## List packages
diff --git a/doc/ci/jobs/ci_job_token.md b/doc/ci/jobs/ci_job_token.md
index 9567bc9cd65..93f22da648a 100644
--- a/doc/ci/jobs/ci_job_token.md
+++ b/doc/ci/jobs/ci_job_token.md
@@ -13,6 +13,7 @@ You can use a GitLab CI/CD job token to authenticate with specific API endpoints
- Packages:
- [Package Registry](../../user/packages/package_registry/index.md#use-gitlab-cicd-to-build-packages).
+ - [Packages API](../../api/packages.md) (project-level).
- [Container Registry](../../user/packages/container_registry/index.md#build-and-push-by-using-gitlab-cicd)
(the `$CI_REGISTRY_PASSWORD` is `$CI_JOB_TOKEN`).
- [Container Registry API](../../api/container_registry.md)
diff --git a/doc/ci/jobs/job_control.md b/doc/ci/jobs/job_control.md
index e2a1751f279..0d5357e63ad 100644
--- a/doc/ci/jobs/job_control.md
+++ b/doc/ci/jobs/job_control.md
@@ -567,6 +567,9 @@ In blocking manual jobs:
enabled can't be merged with a blocked pipeline.
- The pipeline shows a status of **blocked**.
+When using manual jobs in triggered pipelines with [`strategy: depend`](../yaml/index.md#triggerstrategy),
+the type of manual job can affect the trigger job's status while the pipeline runs.
+
### Run a manual job
To run a manual job, you must have permission to merge to the assigned branch:
diff --git a/doc/ci/pipelines/multi_project_pipelines.md b/doc/ci/pipelines/multi_project_pipelines.md
index 81c0639817f..a71af78f410 100644
--- a/doc/ci/pipelines/multi_project_pipelines.md
+++ b/doc/ci/pipelines/multi_project_pipelines.md
@@ -341,8 +341,8 @@ If you use [`only/except`](../yaml/index.md#only--except) to control job behavio
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11238) in GitLab Premium 12.3.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/199224) to GitLab Free in 12.8.
-You can mirror the pipeline status from the triggered pipeline to the source
-trigger job by using `strategy: depend`. For example:
+You can mirror the pipeline status from the triggered pipeline to the source trigger job
+by using [`strategy: depend`](../yaml/index.md#triggerstrategy). For example:
```yaml
trigger_job:
diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md
index 466cfc5e6ba..0b434e7013c 100644
--- a/doc/ci/yaml/index.md
+++ b/doc/ci/yaml/index.md
@@ -3966,6 +3966,17 @@ trigger_job:
In this example, jobs from subsequent stages wait for the triggered pipeline to
successfully complete before starting.
+**Additional details**:
+
+- [Optional manual jobs](../jobs/job_control.md#types-of-manual-jobs) in the downstream pipeline
+ do not affect the status of the downstream pipeline or the upstream trigger job.
+ The downstream pipeline can complete successfully without running any optional manual jobs.
+- [Blocking manual jobs](../jobs/job_control.md#types-of-manual-jobs) in the downstream pipeline
+ must run before the trigger job is marked as successful or failed. The trigger job
+ shows **pending** (**{status_pending}**) if the downstream pipeline status is
+ **waiting for manual action** (**{status_manual}**) due to manual jobs. By default,
+ jobs in later stages do not start until the trigger job completes.
+
#### `trigger:forward`
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213729) in GitLab 14.9 [with a flag](../../administration/feature_flags.md) named `ci_trigger_forward_variables`. Disabled by default.
diff --git a/doc/user/admin_area/settings/visibility_and_access_controls.md b/doc/user/admin_area/settings/visibility_and_access_controls.md
index 361a14be75c..118d375da01 100644
--- a/doc/user/admin_area/settings/visibility_and_access_controls.md
+++ b/doc/user/admin_area/settings/visibility_and_access_controls.md
@@ -53,6 +53,7 @@ By default both administrators and anyone with the **Owner** role can delete a p
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/255449) in GitLab 14.2 for groups created after August 12, 2021.
> - [Renamed](https://gitlab.com/gitlab-org/gitlab/-/issues/352960) from default delayed project deletion in GitLab 15.1.
> - [Enabled for projects in personal namespaces](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89466) in GitLab 15.1.
+> - [Disabled for projects in personal namespaces](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95495) in GitLab 15.3.
Instance-level protection against accidental deletion of groups and projects.
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index 5a3a7e13205..b973a0f56d1 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -447,9 +447,10 @@ in GitLab 12.6, and then to [immediate deletion](https://gitlab.com/gitlab-org/g
### Delayed project deletion **(PREMIUM)**
-> [Enabled for projects in personal namespaces](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89466) in GitLab 15.1.
+> - [Enabled for projects in personal namespaces](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89466) in GitLab 15.1.
+> - [Disabled for projects in personal namespaces](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95495) in GitLab 15.3.
-Projects can be deleted after a delay period. Multiple settings can affect whether
+Projects in a group (not a personal namespace) can be deleted after a delay period. Multiple settings can affect whether
delayed project deletion is enabled for a particular project:
- Self-managed instance [settings](../../admin_area/settings/visibility_and_access_controls.md#delayed-project-deletion).
diff --git a/lib/api/project_packages.rb b/lib/api/project_packages.rb
index 21b2b17d234..800966408fc 100644
--- a/lib/api/project_packages.rb
+++ b/lib/api/project_packages.rb
@@ -36,6 +36,7 @@ module API
optional :status, type: String, values: Packages::Package.statuses.keys,
desc: 'Return packages with specified status'
end
+ route_setting :authentication, job_token_allowed: true
get ':id/packages' do
packages = ::Packages::PackagesFinder.new(
user_project,
@@ -52,6 +53,7 @@ module API
params do
requires :package_id, type: Integer, desc: 'The ID of a package'
end
+ route_setting :authentication, job_token_allowed: true
get ':id/packages/:package_id' do
package = ::Packages::PackageFinder
.new(user_project, params[:package_id]).execute
@@ -65,6 +67,7 @@ module API
params do
requires :package_id, type: Integer, desc: 'The ID of a package'
end
+ route_setting :authentication, job_token_allowed: true
delete ':id/packages/:package_id' do
authorize_destroy_package!(user_project)
diff --git a/lib/api/releases.rb b/lib/api/releases.rb
index d7e497bddf3..10e879ec70b 100644
--- a/lib/api/releases.rb
+++ b/lib/api/releases.rb
@@ -186,6 +186,8 @@ module API
.execute
if result[:status] == :success
+ log_release_deleted_audit_event
+
present result[:release], with: Entities::Release, current_user: current_user
else
render_api_error!(result[:message], result[:http_status])
@@ -238,6 +240,10 @@ module API
# extended in EE
end
+ def log_release_deleted_audit_event
+ # extended in EE
+ end
+
def log_release_milestones_updated_audit_event
# extended in EE
end
diff --git a/lib/gitlab/ci/config/entry/processable.rb b/lib/gitlab/ci/config/entry/processable.rb
index 78794f524f4..975da8662e1 100644
--- a/lib/gitlab/ci/config/entry/processable.rb
+++ b/lib/gitlab/ci/config/entry/processable.rb
@@ -120,7 +120,7 @@ module Gitlab
stage: stage_value,
extends: extends,
rules: rules_value,
- job_variables: variables_value.to_h,
+ job_variables: variables_entry.value_with_data,
root_variables_inheritance: root_variables_inheritance,
only: only_value,
except: except_value,
diff --git a/lib/gitlab/ci/config/entry/variables.rb b/lib/gitlab/ci/config/entry/variables.rb
index efb469ee32a..3130aec0446 100644
--- a/lib/gitlab/ci/config/entry/variables.rb
+++ b/lib/gitlab/ci/config/entry/variables.rb
@@ -18,7 +18,9 @@ module Gitlab
end
def value
- @config.to_h { |key, value| [key.to_s, expand_value(value)[:value]] }
+ @config.to_h do |key, data|
+ [key.to_s, expand_data(data)[:value]]
+ end
end
def self.default(**)
@@ -26,7 +28,9 @@ module Gitlab
end
def value_with_data
- @config.to_h { |key, value| [key.to_s, expand_value(value)] }
+ @config.to_h do |key, data|
+ [key.to_s, expand_data(data)]
+ end
end
def use_value_data?
@@ -35,11 +39,11 @@ module Gitlab
private
- def expand_value(value)
- if value.is_a?(Hash)
- { value: value[:value].to_s, description: value[:description] }
+ def expand_data(data)
+ if data.is_a?(Hash)
+ { value: data[:value].to_s, description: data[:description] }.compact
else
- { value: value.to_s, description: nil }
+ { value: data.to_s }
end
end
end
diff --git a/lib/gitlab/ci/variables/helpers.rb b/lib/gitlab/ci/variables/helpers.rb
index 7cc727bb3ea..300b2708e6d 100644
--- a/lib/gitlab/ci/variables/helpers.rb
+++ b/lib/gitlab/ci/variables/helpers.rb
@@ -6,24 +6,22 @@ module Gitlab
module Helpers
class << self
def merge_variables(current_vars, new_vars)
- current_vars = transform_from_yaml_variables(current_vars)
- new_vars = transform_from_yaml_variables(new_vars)
+ return current_vars if new_vars.blank?
- transform_to_yaml_variables(
- current_vars.merge(new_vars)
- )
- end
+ current_vars = transform_to_array(current_vars) if current_vars.is_a?(Hash)
+ new_vars = transform_to_array(new_vars) if new_vars.is_a?(Hash)
- def transform_to_yaml_variables(vars)
- vars.to_h.map do |key, value|
- { key: key.to_s, value: value, public: true }
- end
+ (new_vars + current_vars).uniq { |var| var[:key] }
end
- def transform_from_yaml_variables(vars)
- return vars.stringify_keys.transform_values(&:to_s) if vars.is_a?(Hash)
-
- vars.to_a.to_h { |var| [var[:key].to_s, var[:value]] }
+ def transform_to_array(vars)
+ vars.to_h.map do |key, data|
+ if data.is_a?(Hash)
+ { key: key.to_s, **data.except(:key) }
+ else
+ { key: key.to_s, value: data }
+ end
+ end
end
def inherit_yaml_variables(from:, to:, inheritance:)
@@ -35,7 +33,7 @@ module Gitlab
def apply_inheritance(variables, inheritance)
case inheritance
when true then variables
- when false then {}
+ when false then []
when Array then variables.select { |var| inheritance.include?(var[:key]) }
end
end
diff --git a/lib/gitlab/ci/yaml_processor/result.rb b/lib/gitlab/ci/yaml_processor/result.rb
index 4bd1ac3b67f..f203f88442d 100644
--- a/lib/gitlab/ci/yaml_processor/result.rb
+++ b/lib/gitlab/ci/yaml_processor/result.rb
@@ -43,7 +43,7 @@ module Gitlab
end
def root_variables
- @root_variables ||= transform_to_yaml_variables(variables)
+ @root_variables ||= transform_to_array(variables)
end
def jobs
@@ -70,7 +70,7 @@ module Gitlab
environment: job[:environment_name],
coverage_regex: job[:coverage],
# yaml_variables is calculated with using job_variables in Seed::Build
- job_variables: transform_to_yaml_variables(job[:job_variables]),
+ job_variables: transform_to_array(job[:job_variables]),
root_variables_inheritance: job[:root_variables_inheritance],
needs_attributes: job.dig(:needs, :job),
interruptible: job[:interruptible],
@@ -114,7 +114,7 @@ module Gitlab
Gitlab::Ci::Variables::Helpers.inherit_yaml_variables(
from: root_variables,
- to: transform_to_yaml_variables(job[:job_variables]),
+ to: job[:job_variables],
inheritance: job.fetch(:root_variables_inheritance, true)
)
end
@@ -137,8 +137,8 @@ module Gitlab
job[:release]
end
- def transform_to_yaml_variables(variables)
- ::Gitlab::Ci::Variables::Helpers.transform_to_yaml_variables(variables)
+ def transform_to_array(variables)
+ ::Gitlab::Ci::Variables::Helpers.transform_to_array(variables)
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 8e7d31fd88e..b86d02d5627 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -13929,6 +13929,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -22949,7 +22952,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -37306,9 +37309,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
diff --git a/package.json b/package.json
index a601c5804fc..ecd30dd2560 100644
--- a/package.json
+++ b/package.json
@@ -205,7 +205,6 @@
"@types/jest": "^27.5.1",
"@vue/test-utils": "1.3.0",
"@vue/vue2-jest": "^27.0.0",
- "acorn": "^6.3.0",
"ajv": "^8.10.0",
"ajv-formats": "^2.1.1",
"axios-mock-adapter": "^1.15.0",
diff --git a/spec/features/merge_request/user_creates_merge_request_spec.rb b/spec/features/merge_request/user_creates_merge_request_spec.rb
index c8b22bb3125..0ae4ef18649 100644
--- a/spec/features/merge_request/user_creates_merge_request_spec.rb
+++ b/spec/features/merge_request/user_creates_merge_request_spec.rb
@@ -1,111 +1,164 @@
# frozen_string_literal: true
-require "spec_helper"
+require 'spec_helper'
-RSpec.describe "User creates a merge request", :js do
+RSpec.describe 'User creates a merge request', :js do
include ProjectForksHelper
- let_it_be(:project) { create(:project, :repository) }
- let_it_be(:user) { create(:user) }
-
- let(:title) { "Some feature" }
-
- before do
- project.add_maintainer(user)
- sign_in(user)
- end
-
- it "creates a merge request" do
- visit(project_new_merge_request_path(project))
+ shared_examples 'creates a merge request' do
+ specify do
+ visit(project_new_merge_request_path(project))
- find(".js-source-branch").click
- click_link("fix")
+ compare_source_and_target('fix', 'feature')
- find(".js-target-branch").click
- click_link("feature")
+ page.within('.merge-request-form') do
+ expect(page.find('#merge_request_description')['placeholder']).to eq 'Describe the goal of the changes and what reviewers should be aware of.'
+ end
- click_button("Compare branches")
+ fill_in('Title', with: title)
+ click_button('Create merge request')
- page.within('.merge-request-form') do
- expect(page.find('#merge_request_description')['placeholder']).to eq 'Describe the goal of the changes and what reviewers should be aware of.'
+ page.within('.merge-request') do
+ expect(page).to have_content(title)
+ end
end
+ end
- fill_in("Title", with: title)
- click_button("Create merge request")
+ shared_examples 'renders not found' do
+ specify do
+ visit project_new_merge_request_path(project)
- page.within(".merge-request") do
- expect(page).to have_content(title)
+ expect(page).to have_title('Not Found')
+ expect(page).to have_content('Page Not Found')
end
end
- context "XSS branch name exists" do
+ context 'when user is a direct project member' do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
+
+ let(:title) { 'Some feature' }
+
before do
- project.repository.create_branch("<img/src='x'/onerror=alert('oops')>", "master")
+ project.add_maintainer(user)
+ sign_in(user)
end
- it "doesn't execute the dodgy branch name" do
- visit(project_new_merge_request_path(project))
+ it_behaves_like 'creates a merge request'
- find(".js-source-branch").click
- click_link("<img/src='x'/onerror=alert('oops')>")
+ context 'with XSS branch name' do
+ before do
+ project.repository.create_branch("<img/src='x'/onerror=alert('oops')>", 'master')
+ end
- find(".js-target-branch").click
- click_link("feature")
+ it 'does not execute the suspicious branch name' do
+ visit(project_new_merge_request_path(project))
- click_button("Compare branches")
+ compare_source_and_target("<img/src='x'/onerror=alert('oops')>", 'feature')
- expect { page.driver.browser.switch_to.alert }.to raise_error(Selenium::WebDriver::Error::NoSuchAlertError)
+ expect { page.driver.browser.switch_to.alert }.to raise_error(Selenium::WebDriver::Error::NoSuchAlertError)
+ end
end
- end
- context "to a forked project" do
- let(:forked_project) { fork_project(project, user, namespace: user.namespace, repository: true) }
+ context 'to a forked project' do
+ let(:forked_project) { fork_project(project, user, namespace: user.namespace, repository: true) }
+
+ it 'creates a merge request', :sidekiq_might_not_need_inline do
+ visit(project_new_merge_request_path(forked_project))
+
+ expect(page).to have_content('Source branch').and have_content('Target branch')
+ expect(find('#merge_request_target_project_id', visible: false).value).to eq(project.id.to_s)
- it "creates a merge request", :sidekiq_might_not_need_inline do
- visit(project_new_merge_request_path(forked_project))
+ click_button('Compare branches and continue')
- expect(page).to have_content("Source branch").and have_content("Target branch")
- expect(find("#merge_request_target_project_id", visible: false).value).to eq(project.id.to_s)
+ expect(page).to have_content('You must select source and target branch')
- click_button("Compare branches and continue")
+ first('.js-source-project').click
+ first('.dropdown-source-project a', text: forked_project.full_path)
- expect(page).to have_content("You must select source and target branch")
+ first('.js-target-project').click
+ first('.dropdown-target-project a', text: project.full_path)
- first(".js-source-project").click
- first(".dropdown-source-project a", text: forked_project.full_path)
+ first('.js-source-branch').click
- first(".js-target-project").click
- first(".dropdown-target-project a", text: project.full_path)
+ wait_for_requests
- first(".js-source-branch").click
+ source_branch = 'fix'
- wait_for_requests
+ first('.js-source-branch-dropdown .dropdown-content a', text: source_branch).click
- source_branch = "fix"
+ click_button('Compare branches and continue')
- first(".js-source-branch-dropdown .dropdown-content a", text: source_branch).click
+ expect(page).to have_text _('New merge request')
- click_button("Compare branches and continue")
+ page.within('form#new_merge_request') do
+ fill_in('Title', with: title)
+ end
- expect(page).to have_text _('New merge request')
+ expect(find('.js-assignee-search')['data-project-id']).to eq(project.id.to_s)
+ find('.js-assignee-search').click
- page.within("form#new_merge_request") do
- fill_in("Title", with: title)
+ page.within('.dropdown-menu-user') do
+ expect(page).to have_content('Unassigned')
+ .and have_content(user.name)
+ .and have_content(project.users.first.name)
+ end
+ find('.js-assignee-search').click
+
+ click_button('Create merge request')
+
+ expect(page).to have_content(title).and have_content("requested to merge #{forked_project.full_path}:#{source_branch} into master")
+ end
+ end
+ end
+
+ context 'when user is an inherited member from the group' do
+ let_it_be(:group) { create(:group, :public) }
+
+ let(:user) { create(:user) }
+
+ context 'when project is public and merge requests are private' do
+ let_it_be(:project) do
+ create(:project,
+ :public,
+ :repository,
+ group: group,
+ merge_requests_access_level: ProjectFeature::DISABLED)
end
- expect(find(".js-assignee-search")["data-project-id"]).to eq(project.id.to_s)
- find('.js-assignee-search').click
+ context 'and user is a guest' do
+ before do
+ group.add_guest(user)
+ sign_in(user)
+ end
- page.within(".dropdown-menu-user") do
- expect(page).to have_content("Unassigned")
- .and have_content(user.name)
- .and have_content(project.users.first.name)
+ it_behaves_like 'renders not found'
end
- find('.js-assignee-search').click
+ end
+
+ context 'when project is private' do
+ let_it_be(:project) { create(:project, :private, :repository, group: group) }
- click_button("Create merge request")
+ context 'and user is a guest' do
+ before do
+ group.add_guest(user)
+ sign_in(user)
+ end
- expect(page).to have_content(title).and have_content("requested to merge #{forked_project.full_path}:#{source_branch} into master")
+ it_behaves_like 'renders not found'
+ end
end
end
+
+ private
+
+ def compare_source_and_target(source_branch, target_branch)
+ find('.js-source-branch').click
+ click_link(source_branch)
+
+ find('.js-target-branch').click
+ click_link(target_branch)
+
+ click_button('Compare branches')
+ end
end
diff --git a/spec/frontend/jobs/components/job_log_controllers_spec.js b/spec/frontend/jobs/components/job_log_controllers_spec.js
index 4075abd24d5..aa85253a177 100644
--- a/spec/frontend/jobs/components/job_log_controllers_spec.js
+++ b/spec/frontend/jobs/components/job_log_controllers_spec.js
@@ -34,7 +34,7 @@ describe('Job log controllers', () => {
jobLog: mockJobLog,
};
- const createWrapper = (props, { jobLogSearch = false, jobLogJumpToFailures = false } = {}) => {
+ const createWrapper = (props, { jobLogJumpToFailures = false } = {}) => {
wrapper = mount(JobLogControllers, {
propsData: {
...defaultProps,
@@ -42,7 +42,6 @@ describe('Job log controllers', () => {
},
provide: {
glFeatures: {
- jobLogSearch,
jobLogJumpToFailures,
},
},
@@ -290,38 +289,27 @@ describe('Job log controllers', () => {
});
describe('Job log search', () => {
- describe('with feature flag off', () => {
- it('does not display job log search', () => {
- createWrapper();
-
- expect(findJobLogSearch().exists()).toBe(false);
- expect(findSearchHelp().exists()).toBe(false);
- });
+ beforeEach(() => {
+ createWrapper();
});
- describe('with feature flag on', () => {
- beforeEach(() => {
- createWrapper({}, { jobLogSearch: true });
- });
-
- it('displays job log search', () => {
- expect(findJobLogSearch().exists()).toBe(true);
- expect(findSearchHelp().exists()).toBe(true);
- });
+ it('displays job log search', () => {
+ expect(findJobLogSearch().exists()).toBe(true);
+ expect(findSearchHelp().exists()).toBe(true);
+ });
- it('emits search results', () => {
- const expectedSearchResults = [[[mockJobLog[6].lines[1], mockJobLog[6].lines[2]]]];
+ it('emits search results', () => {
+ const expectedSearchResults = [[[mockJobLog[6].lines[1], mockJobLog[6].lines[2]]]];
- findJobLogSearch().vm.$emit('submit');
+ findJobLogSearch().vm.$emit('submit');
- expect(wrapper.emitted('searchResults')).toEqual(expectedSearchResults);
- });
+ expect(wrapper.emitted('searchResults')).toEqual(expectedSearchResults);
+ });
- it('clears search results', () => {
- findJobLogSearch().vm.$emit('clear');
+ it('clears search results', () => {
+ findJobLogSearch().vm.$emit('clear');
- expect(wrapper.emitted('searchResults')).toEqual([[[]]]);
- });
+ expect(wrapper.emitted('searchResults')).toEqual([[[]]]);
});
});
});
diff --git a/spec/frontend/lib/utils/text_markdown_spec.js b/spec/frontend/lib/utils/text_markdown_spec.js
index 733d89fe08c..8d179baa505 100644
--- a/spec/frontend/lib/utils/text_markdown_spec.js
+++ b/spec/frontend/lib/utils/text_markdown_spec.js
@@ -586,6 +586,33 @@ describe('init markdown', () => {
);
});
+ it('only converts valid URLs', () => {
+ const notValidUrl = 'group::label';
+ const expectedUrlValue = 'url';
+ const expectedText = `other [${notValidUrl}](${expectedUrlValue}) text`;
+ const initialValue = `other ${notValidUrl} text`;
+
+ textArea.value = initialValue;
+ selectedIndex = initialValue.indexOf(notValidUrl);
+ textArea.setSelectionRange(selectedIndex, selectedIndex + notValidUrl.length);
+
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag,
+ blockTag: null,
+ selected: notValidUrl,
+ wrap: false,
+ select,
+ });
+
+ expect(textArea.value).toEqual(expectedText);
+ expect(textArea.selectionStart).toEqual(expectedText.indexOf(expectedUrlValue, 1));
+ expect(textArea.selectionEnd).toEqual(
+ expectedText.indexOf(expectedUrlValue, 1) + expectedUrlValue.length,
+ );
+ });
+
it('adds block tags on line above and below selection', () => {
selected = 'this text\nis multiple\nlines';
text = `before \n${selected}\nafter `;
diff --git a/spec/frontend/pipeline_editor/components/validate/ci_validate_spec.js b/spec/frontend/pipeline_editor/components/validate/ci_validate_spec.js
index b8fbc2b2460..09d4f9736ad 100644
--- a/spec/frontend/pipeline_editor/components/validate/ci_validate_spec.js
+++ b/spec/frontend/pipeline_editor/components/validate/ci_validate_spec.js
@@ -2,6 +2,7 @@ import { GlAlert, GlDropdown, GlIcon, GlLoadingIcon, GlPopover } from '@gitlab/u
import { nextTick } from 'vue';
import { createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import CiLintResults from '~/pipeline_editor/components/lint/ci_lint_results.vue';
@@ -9,6 +10,7 @@ import CiValidate, { i18n } from '~/pipeline_editor/components/validate/ci_valid
import ValidatePipelinePopover from '~/pipeline_editor/components/popovers/validate_pipeline_popover.vue';
import getBlobContent from '~/pipeline_editor/graphql/queries/blob_content.query.graphql';
import lintCIMutation from '~/pipeline_editor/graphql/mutations/client/lint_ci.mutation.graphql';
+import { pipelineEditorTrackingOptions } from '~/pipeline_editor/constants';
import {
mockBlobContentQueryResponse,
mockCiLintPath,
@@ -24,6 +26,7 @@ describe('Pipeline Editor Validate Tab', () => {
let wrapper;
let mockApollo;
let mockBlobContentData;
+ let trackingSpy;
const createComponent = ({
props,
@@ -140,9 +143,24 @@ describe('Pipeline Editor Validate Tab', () => {
mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
await createComponentWithApollo();
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockLintDataValid);
});
+ afterEach(() => {
+ unmockTracking();
+ });
+
+ it('tracks the simulation event', () => {
+ const {
+ label,
+ actions: { simulatePipeline },
+ } = pipelineEditorTrackingOptions;
+ findCta().vm.$emit('click');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, simulatePipeline, { label });
+ });
+
it('renders loading state while simulation is ongoing', async () => {
findCta().vm.$emit('click');
await nextTick();
@@ -224,10 +242,27 @@ describe('Pipeline Editor Validate Tab', () => {
mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
await createComponentWithApollo();
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockLintDataValid);
await findCta().vm.$emit('click');
});
+ afterEach(() => {
+ unmockTracking();
+ });
+
+ it('tracks the second simulation event', async () => {
+ const {
+ label,
+ actions: { resimulatePipeline },
+ } = pipelineEditorTrackingOptions;
+
+ await wrapper.setProps({ ciFileContent: 'new yaml content' });
+ findResultsCta().vm.$emit('click');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, resimulatePipeline, { label });
+ });
+
it('renders content change status', async () => {
await wrapper.setProps({ ciFileContent: 'new yaml content' });
diff --git a/spec/graphql/mutations/merge_requests/create_spec.rb b/spec/graphql/mutations/merge_requests/create_spec.rb
index e1edb60e4ff..6e593a5f4be 100644
--- a/spec/graphql/mutations/merge_requests/create_spec.rb
+++ b/spec/graphql/mutations/merge_requests/create_spec.rb
@@ -7,8 +7,7 @@ RSpec.describe Mutations::MergeRequests::Create do
subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
- let_it_be(:project) { create(:project, :public, :repository) }
- let_it_be(:user) { create(:user) }
+ let(:user) { create(:user) }
let(:context) do
GraphQL::Query::Context.new(
@@ -38,62 +37,106 @@ RSpec.describe Mutations::MergeRequests::Create do
let(:mutated_merge_request) { subject[:merge_request] }
- it 'raises an error if the resource is not accessible to the user' do
- expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ shared_examples 'resource not available' do
+ it 'raises an error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
end
- context 'when user does not have enough permissions to create a merge request' do
- before do
- project.add_guest(user)
- end
+ context 'when user is not a project member' do
+ let_it_be(:project) { create(:project, :public, :repository) }
- it 'raises an error if the resource is not accessible to the user' do
- expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
- end
+ it_behaves_like 'resource not available'
end
- context 'when the user can create a merge request' do
- before_all do
- project.add_developer(user)
- end
+ context 'when user is a direct project member' do
+ let_it_be(:project) { create(:project, :public, :repository) }
- it 'creates a new merge request' do
- expect { mutated_merge_request }.to change(MergeRequest, :count).by(1)
- end
+ context 'and user is a guest' do
+ before do
+ project.add_guest(user)
+ end
- it 'returns a new merge request' do
- expect(mutated_merge_request.title).to eq(title)
- expect(subject[:errors]).to be_empty
+ it_behaves_like 'resource not available'
end
- context 'when optional description field is set' do
- let(:description) { 'content' }
+ context 'and user is a developer' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'creates a new merge request' do
+ expect { mutated_merge_request }.to change(MergeRequest, :count).by(1)
+ end
- it 'returns a new merge request with a description' do
- expect(mutated_merge_request.description).to eq(description)
+ it 'returns a new merge request' do
+ expect(mutated_merge_request.title).to eq(title)
expect(subject[:errors]).to be_empty
end
- end
- context 'when optional labels field is set' do
- let(:labels) { %w[label-1 label-2] }
+ context 'when optional description field is set' do
+ let(:description) { 'content' }
- it 'returns a new merge request with labels' do
- expect(mutated_merge_request.labels.map(&:title)).to eq(labels)
- expect(subject[:errors]).to be_empty
+ it 'returns a new merge request with a description' do
+ expect(mutated_merge_request.description).to eq(description)
+ expect(subject[:errors]).to be_empty
+ end
+ end
+
+ context 'when optional labels field is set' do
+ let(:labels) { %w[label-1 label-2] }
+
+ it 'returns a new merge request with labels' do
+ expect(mutated_merge_request.labels.map(&:title)).to eq(labels)
+ expect(subject[:errors]).to be_empty
+ end
+ end
+
+ context 'when service cannot create a merge request' do
+ let(:title) { nil }
+
+ it 'does not create a new merge request' do
+ expect { mutated_merge_request }.not_to change(MergeRequest, :count)
+ end
+
+ it 'returns errors' do
+ expect(mutated_merge_request).to be_nil
+ expect(subject[:errors]).to match_array(['Title can\'t be blank'])
+ end
end
end
+ end
- context 'when service cannot create a merge request' do
- let(:title) { nil }
+ context 'when user is an inherited member from the group' do
+ let_it_be(:group) { create(:group, :public) }
- it 'does not create a new merge request' do
- expect { mutated_merge_request }.not_to change(MergeRequest, :count)
+ context 'when project is public with private merge requests' do
+ let_it_be(:project) do
+ create(:project,
+ :public,
+ :repository,
+ group: group,
+ merge_requests_access_level: ProjectFeature::DISABLED)
end
- it 'returns errors' do
- expect(mutated_merge_request).to be_nil
- expect(subject[:errors]).to eq(['Title can\'t be blank'])
+ context 'and user is a guest' do
+ before do
+ group.add_guest(user)
+ end
+
+ it_behaves_like 'resource not available'
+ end
+ end
+
+ context 'when project is private' do
+ let_it_be(:project) { create(:project, :private, :repository, group: group) }
+
+ context 'and user is a guest' do
+ before do
+ group.add_guest(user)
+ end
+
+ it_behaves_like 'resource not available'
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb
index 55ad119ea21..1f8543227c9 100644
--- a/spec/lib/gitlab/ci/config/entry/root_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb
@@ -155,7 +155,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
services: [{ name: "postgres:9.1" }, { name: "mysql:5.5" }],
cache: [{ key: "k", untracked: true, paths: ["public/"], policy: "pull-push", when: 'on_success' }],
only: { refs: %w(branches tags) },
- job_variables: { 'VAR' => 'job' },
+ job_variables: { 'VAR' => { value: 'job' } },
root_variables_inheritance: true,
after_script: [],
ignore: false,
@@ -215,7 +215,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
stage: 'test',
cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
- job_variables: { 'VAR' => 'job' },
+ job_variables: { 'VAR' => { value: 'job' } },
root_variables_inheritance: true,
ignore: false,
after_script: ['make clean'],
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
index 890ba51157a..75f6a773c2d 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
@@ -97,15 +97,15 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
let(:attributes) do
{ name: 'rspec',
ref: 'master',
- job_variables: [{ key: 'VAR1', value: 'var 1', public: true },
- { key: 'VAR2', value: 'var 2', public: true }],
+ job_variables: [{ key: 'VAR1', value: 'var 1' },
+ { key: 'VAR2', value: 'var 2' }],
rules: [{ if: '$VAR == null', variables: { VAR1: 'new var 1', VAR3: 'var 3' } }] }
end
it do
- is_expected.to include(yaml_variables: [{ key: 'VAR1', value: 'new var 1', public: true },
- { key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }])
+ is_expected.to include(yaml_variables: [{ key: 'VAR1', value: 'new var 1' },
+ { key: 'VAR3', value: 'var 3' },
+ { key: 'VAR2', value: 'var 2' }])
end
end
@@ -114,13 +114,13 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
{
name: 'rspec',
ref: 'master',
- job_variables: [{ key: 'VARIABLE', value: 'value', public: true }],
+ job_variables: [{ key: 'VARIABLE', value: 'value' }],
tag_list: ['static-tag', '$VARIABLE', '$NO_VARIABLE']
}
end
it { is_expected.to include(tag_list: ['static-tag', 'value', '$NO_VARIABLE']) }
- it { is_expected.to include(yaml_variables: [{ key: 'VARIABLE', value: 'value', public: true }]) }
+ it { is_expected.to include(yaml_variables: [{ key: 'VARIABLE', value: 'value' }]) }
end
context 'with cache:key' do
@@ -257,19 +257,19 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
let(:attributes) do
{ name: 'rspec',
ref: 'master',
- yaml_variables: [{ key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }],
- job_variables: [{ key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }],
+ yaml_variables: [{ key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }],
+ job_variables: [{ key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }],
root_variables_inheritance: root_variables_inheritance }
end
context 'when the pipeline has variables' do
let(:root_variables) do
- [{ key: 'VAR1', value: 'var overridden pipeline 1', public: true },
- { key: 'VAR2', value: 'var pipeline 2', public: true },
- { key: 'VAR3', value: 'var pipeline 3', public: true },
- { key: 'VAR4', value: 'new var pipeline 4', public: true }]
+ [{ key: 'VAR1', value: 'var overridden pipeline 1' },
+ { key: 'VAR2', value: 'var pipeline 2' },
+ { key: 'VAR3', value: 'var pipeline 3' },
+ { key: 'VAR4', value: 'new var pipeline 4' }]
end
context 'when root_variables_inheritance is true' do
@@ -277,10 +277,10 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
it 'returns calculated yaml variables' do
expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR1', value: 'var overridden pipeline 1', public: true },
- { key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true },
- { key: 'VAR4', value: 'new var pipeline 4', public: true }]
+ [{ key: 'VAR1', value: 'var overridden pipeline 1' },
+ { key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' },
+ { key: 'VAR4', value: 'new var pipeline 4' }]
)
end
end
@@ -290,8 +290,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
it 'returns job variables' do
expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }]
+ [{ key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }]
)
end
end
@@ -301,9 +301,9 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
it 'returns calculated yaml variables' do
expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR1', value: 'var overridden pipeline 1', public: true },
- { key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }]
+ [{ key: 'VAR1', value: 'var overridden pipeline 1' },
+ { key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }]
)
end
end
@@ -314,8 +314,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
it 'returns seed yaml variables' do
expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }])
+ [{ key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }])
end
end
end
@@ -324,8 +324,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
let(:attributes) do
{ name: 'rspec',
ref: 'master',
- yaml_variables: [{ key: 'VAR1', value: 'var 1', public: true }],
- job_variables: [{ key: 'VAR1', value: 'var 1', public: true }],
+ yaml_variables: [{ key: 'VAR1', value: 'var 1' }],
+ job_variables: [{ key: 'VAR1', value: 'var 1' }],
root_variables_inheritance: root_variables_inheritance,
rules: rules }
end
@@ -338,14 +338,14 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
end
it 'recalculates the variables' do
- expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1', public: true },
- { key: 'VAR2', value: 'new var 2', public: true })
+ expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1' },
+ { key: 'VAR2', value: 'new var 2' })
end
end
context 'when the rules use root variables' do
let(:root_variables) do
- [{ key: 'VAR2', value: 'var pipeline 2', public: true }]
+ [{ key: 'VAR2', value: 'var pipeline 2' }]
end
let(:rules) do
@@ -353,15 +353,15 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
end
it 'recalculates the variables' do
- expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1', public: true },
- { key: 'VAR2', value: 'overridden var 2', public: true })
+ expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1' },
+ { key: 'VAR2', value: 'overridden var 2' })
end
context 'when the root_variables_inheritance is false' do
let(:root_variables_inheritance) { false }
it 'does not recalculate the variables' do
- expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'var 1', public: true })
+ expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'var 1' })
end
end
end
diff --git a/spec/lib/gitlab/ci/variables/helpers_spec.rb b/spec/lib/gitlab/ci/variables/helpers_spec.rb
index fc1055751bd..2a1cdaeb3a7 100644
--- a/spec/lib/gitlab/ci/variables/helpers_spec.rb
+++ b/spec/lib/gitlab/ci/variables/helpers_spec.rb
@@ -15,21 +15,27 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do
end
let(:result) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value22', public: true },
- { key: 'key3', value: 'value3', public: true }]
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
end
subject { described_class.merge_variables(current_variables, new_variables) }
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
context 'when new variables is a hash' do
let(:new_variables) do
{ 'key2' => 'value22', 'key3' => 'value3' }
end
- it { is_expected.to eq(result) }
+ let(:result) do
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
+ end
+
+ it { is_expected.to match_array(result) }
end
context 'when new variables is a hash with symbol keys' do
@@ -37,79 +43,72 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do
{ key2: 'value22', key3: 'value3' }
end
- it { is_expected.to eq(result) }
+ let(:result) do
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
+ end
+
+ it { is_expected.to match_array(result) }
end
context 'when new variables is nil' do
let(:new_variables) {}
let(:result) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value2', public: true }]
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value2' }]
end
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
end
end
- describe '.transform_to_yaml_variables' do
- let(:variables) do
- { 'key1' => 'value1', 'key2' => 'value2' }
- end
+ describe '.transform_to_array' do
+ subject { described_class.transform_to_array(variables) }
- let(:result) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value2', public: true }]
- end
-
- subject { described_class.transform_to_yaml_variables(variables) }
-
- it { is_expected.to eq(result) }
-
- context 'when variables is nil' do
- let(:variables) {}
-
- it { is_expected.to eq([]) }
- end
- end
+ context 'when values are strings' do
+ let(:variables) do
+ { 'key1' => 'value1', 'key2' => 'value2' }
+ end
- describe '.transform_from_yaml_variables' do
- let(:variables) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value2', public: true }]
- end
+ let(:result) do
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value2' }]
+ end
- let(:result) do
- { 'key1' => 'value1', 'key2' => 'value2' }
+ it { is_expected.to match_array(result) }
end
- subject { described_class.transform_from_yaml_variables(variables) }
-
- it { is_expected.to eq(result) }
-
context 'when variables is nil' do
let(:variables) {}
- it { is_expected.to eq({}) }
+ it { is_expected.to match_array([]) }
end
- context 'when variables is a hash' do
+ context 'when values are hashes' do
let(:variables) do
- { key1: 'value1', 'key2' => 'value2' }
+ { 'key1' => { value: 'value1', description: 'var 1' }, 'key2' => { value: 'value2' } }
end
- it { is_expected.to eq(result) }
- end
-
- context 'when variables contain integers and symbols' do
- let(:variables) do
- { key1: 1, key2: :value2 }
+ let(:result) do
+ [{ key: 'key1', value: 'value1', description: 'var 1' },
+ { key: 'key2', value: 'value2' }]
end
- let(:result1) do
- { 'key1' => '1', 'key2' => 'value2' }
- end
+ it { is_expected.to match_array(result) }
+
+ context 'when a value data has `key` as a key' do
+ let(:variables) do
+ { 'key1' => { value: 'value1', key: 'new_key1' }, 'key2' => { value: 'value2' } }
+ end
- it { is_expected.to eq(result1) }
+ let(:result) do
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value2' }]
+ end
+
+ it { is_expected.to match_array(result) }
+ end
end
end
@@ -127,35 +126,35 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do
let(:inheritance) { true }
let(:result) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value22', public: true },
- { key: 'key3', value: 'value3', public: true }]
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
end
subject { described_class.inherit_yaml_variables(from: from, to: to, inheritance: inheritance) }
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
context 'when inheritance is false' do
let(:inheritance) { false }
let(:result) do
- [{ key: 'key2', value: 'value22', public: true },
- { key: 'key3', value: 'value3', public: true }]
+ [{ key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
end
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
end
context 'when inheritance is array' do
let(:inheritance) { ['key2'] }
let(:result) do
- [{ key: 'key2', value: 'value22', public: true },
- { key: 'key3', value: 'value3', public: true }]
+ [{ key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
end
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
end
end
end
diff --git a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
index 8416501e949..f7a0905d9da 100644
--- a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
@@ -72,8 +72,8 @@ module Gitlab
it 'returns calculated variables with root and job variables' do
is_expected.to match_array([
- { key: 'VAR1', value: 'value 11', public: true },
- { key: 'VAR2', value: 'value 2', public: true }
+ { key: 'VAR1', value: 'value 11' },
+ { key: 'VAR2', value: 'value 2' }
])
end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 35af9ae6201..3477fe837b4 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -448,7 +448,7 @@ module Gitlab
it 'parses the root:variables as #root_variables' do
expect(subject.root_variables)
- .to contain_exactly({ key: 'SUPPORTED', value: 'parsed', public: true })
+ .to contain_exactly({ key: 'SUPPORTED', value: 'parsed' })
end
end
@@ -490,7 +490,7 @@ module Gitlab
it 'parses the root:variables as #root_variables' do
expect(subject.root_variables)
- .to contain_exactly({ key: 'SUPPORTED', value: 'parsed', public: true })
+ .to contain_exactly({ key: 'SUPPORTED', value: 'parsed' })
end
end
@@ -1098,8 +1098,8 @@ module Gitlab
it 'returns job variables' do
expect(job_variables).to contain_exactly(
- { key: 'VAR1', value: 'value1', public: true },
- { key: 'VAR2', value: 'value2', public: true }
+ { key: 'VAR1', value: 'value1' },
+ { key: 'VAR2', value: 'value2' }
)
expect(root_variables_inheritance).to eq(true)
end
@@ -1203,21 +1203,21 @@ module Gitlab
expect(config_processor.builds[0]).to include(
name: 'test1',
options: { script: ['test'] },
- job_variables: [{ key: 'VAR1', value: 'test1 var 1', public: true },
- { key: 'VAR2', value: 'test2 var 2', public: true }]
+ job_variables: [{ key: 'VAR1', value: 'test1 var 1' },
+ { key: 'VAR2', value: 'test2 var 2' }]
)
expect(config_processor.builds[1]).to include(
name: 'test2',
options: { script: ['test'] },
- job_variables: [{ key: 'VAR1', value: 'base var 1', public: true },
- { key: 'VAR2', value: 'test2 var 2', public: true }]
+ job_variables: [{ key: 'VAR1', value: 'base var 1' },
+ { key: 'VAR2', value: 'test2 var 2' }]
)
expect(config_processor.builds[2]).to include(
name: 'test3',
options: { script: ['test'] },
- job_variables: [{ key: 'VAR1', value: 'base var 1', public: true }]
+ job_variables: [{ key: 'VAR1', value: 'base var 1' }]
)
expect(config_processor.builds[3]).to include(
diff --git a/spec/policies/merge_request_policy_spec.rb b/spec/policies/merge_request_policy_spec.rb
index dd42e1b9313..7e1af132b1d 100644
--- a/spec/policies/merge_request_policy_spec.rb
+++ b/spec/policies/merge_request_policy_spec.rb
@@ -7,24 +7,18 @@ RSpec.describe MergeRequestPolicy do
let_it_be(:guest) { create(:user) }
let_it_be(:author) { create(:user) }
+ let_it_be(:reporter) { create(:user) }
let_it_be(:developer) { create(:user) }
let_it_be(:non_team_member) { create(:user) }
- let(:project) { create(:project, :public) }
-
def permissions(user, merge_request)
described_class.new(user, merge_request)
end
- before do
- project.add_guest(guest)
- project.add_guest(author)
- project.add_developer(developer)
- end
-
mr_perms = %i[create_merge_request_in
create_merge_request_from
read_merge_request
+ update_merge_request
create_todo
approve_merge_request
create_note
@@ -40,7 +34,28 @@ RSpec.describe MergeRequestPolicy do
end
end
- shared_examples_for 'a user with access' do
+ shared_examples_for 'a user with reporter access' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:policy, :is_allowed) do
+ :create_merge_request_in | true
+ :read_merge_request | true
+ :create_todo | true
+ :create_note | true
+ :update_subscription | true
+ :create_merge_request_from | false
+ :approve_merge_request | false
+ :update_merge_request | false
+ end
+
+ with_them do
+ specify do
+ is_allowed ? (is_expected.to be_allowed(policy)) : (is_expected.to be_disallowed(policy))
+ end
+ end
+ end
+
+ shared_examples_for 'a user with full access' do
let(:perms) { permissions(subject, merge_request) }
mr_perms.each do |thing|
@@ -50,199 +65,304 @@ RSpec.describe MergeRequestPolicy do
end
end
- context 'when merge request is public' do
- let(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) }
- let(:user) { author }
-
- context 'and user is anonymous' do
- subject { permissions(nil, merge_request) }
+ context 'when user is a direct project member' do
+ let(:project) { create(:project, :public) }
- it do
- is_expected.to be_disallowed(:create_todo, :update_subscription)
- end
+ before do
+ project.add_guest(guest)
+ project.add_guest(author)
+ project.add_developer(developer)
end
- context 'and user is author' do
- subject { permissions(user, merge_request) }
+ context 'when merge request is public' do
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) }
+ let(:user) { author }
- context 'and the user is a guest' do
- let(:user) { guest }
+ context 'and user is author' do
+ subject { permissions(user, merge_request) }
- it do
- is_expected.to be_allowed(:update_merge_request)
- end
+ context 'and the user is a guest' do
+ let(:user) { guest }
- it do
- is_expected.to be_allowed(:reopen_merge_request)
- end
+ it do
+ is_expected.to be_allowed(:update_merge_request)
+ end
- it do
- is_expected.to be_allowed(:approve_merge_request)
+ it do
+ is_expected.to be_allowed(:reopen_merge_request)
+ end
+
+ it do
+ is_expected.to be_allowed(:approve_merge_request)
+ end
end
end
+ end
- context 'and the user is a group member' do
- let(:project) { create(:project, :public, group: group) }
- let(:group) { create(:group) }
- let(:user) { non_team_member }
-
- before do
- group.add_guest(non_team_member)
- end
+ context 'when merge requests have been disabled' do
+ let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: author) }
- it do
- is_expected.to be_allowed(:approve_merge_request)
- end
+ before do
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::DISABLED)
end
- context 'and the user is a member of a shared group' do
- let(:user) { non_team_member }
+ describe 'the author' do
+ subject { author }
- before do
- group = create(:group)
- project.project_group_links.create!(
- group: group,
- group_access: Gitlab::Access::DEVELOPER)
+ it_behaves_like 'a denied user'
+ end
- group.add_guest(non_team_member)
- end
+ describe 'a guest' do
+ subject { guest }
- it do
- is_expected.to be_allowed(:approve_merge_request)
- end
+ it_behaves_like 'a denied user'
end
- context 'and the user is not a project member' do
- let(:user) { non_team_member }
+ describe 'a developer' do
+ subject { developer }
- it do
- is_expected.not_to be_allowed(:approve_merge_request)
- end
+ it_behaves_like 'a denied user'
end
end
- end
- context 'when merge requests have been disabled' do
- let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: author) }
+ context 'when merge requests are private' do
+ let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: author) }
- before do
- project.project_feature.update!(merge_requests_access_level: ProjectFeature::DISABLED)
- end
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
+ end
- describe 'the author' do
- subject { author }
+ describe 'the author' do
+ subject { author }
- it_behaves_like 'a denied user'
+ it_behaves_like 'a denied user'
+ end
+
+ describe 'a developer' do
+ subject { developer }
+
+ it_behaves_like 'a user with full access'
+ end
end
- describe 'a guest' do
- subject { guest }
+ context 'when merge request is unlocked' do
+ let(:merge_request) { create(:merge_request, :closed, source_project: project, target_project: project, author: author) }
- it_behaves_like 'a denied user'
+ it 'allows author to reopen merge request' do
+ expect(permissions(author, merge_request)).to be_allowed(:reopen_merge_request)
+ end
+
+ it 'allows developer to reopen merge request' do
+ expect(permissions(developer, merge_request)).to be_allowed(:reopen_merge_request)
+ end
+
+ it 'prevents guest from reopening merge request' do
+ expect(permissions(guest, merge_request)).to be_disallowed(:reopen_merge_request)
+ end
end
- describe 'a developer' do
- subject { developer }
+ context 'when merge request is locked' do
+ let(:merge_request_locked) { create(:merge_request, :closed, discussion_locked: true, source_project: project, target_project: project, author: author) }
- it_behaves_like 'a denied user'
+ it 'prevents author from reopening merge request' do
+ expect(permissions(author, merge_request_locked)).to be_disallowed(:reopen_merge_request)
+ end
+
+ it 'prevents developer from reopening merge request' do
+ expect(permissions(developer, merge_request_locked)).to be_disallowed(:reopen_merge_request)
+ end
+
+ it 'prevents guests from reopening merge request' do
+ expect(permissions(guest, merge_request_locked)).to be_disallowed(:reopen_merge_request)
+ end
+
+ context 'when the user is project member, with at least guest access' do
+ let(:user) { guest }
+
+ it 'can create a note' do
+ expect(permissions(user, merge_request_locked)).to be_allowed(:create_note)
+ end
+ end
end
- describe 'any other user' do
- subject { non_team_member }
+ context 'with external authorization enabled' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:policies) { described_class.new(user, merge_request) }
- it_behaves_like 'a denied user'
+ before do
+ enable_external_authorization_service_check
+ end
+
+ it 'can read the issue iid without accessing the external service' do
+ expect(::Gitlab::ExternalAuthorization).not_to receive(:access_allowed?)
+
+ expect(policies).to be_allowed(:read_merge_request_iid)
+ end
end
end
- context 'when merge requests are private' do
- let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: author) }
+ context 'when user is an inherited member from the parent group' do
+ let_it_be(:group) { create(:group, :public) }
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
- project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: author) }
+
+ before_all do
+ group.add_guest(guest)
+ group.add_guest(author)
+ group.add_reporter(reporter)
+ group.add_developer(developer)
end
- describe 'a non-team-member' do
- subject { non_team_member }
+ context 'when project is public' do
+ let(:project) { create(:project, :public, group: group) }
- it_behaves_like 'a denied user'
- end
+ describe 'the merge request author' do
+ subject { permissions(author, merge_request) }
- describe 'the author' do
- subject { author }
+ specify do
+ is_expected.to be_allowed(:approve_merge_request)
+ end
+ end
- it_behaves_like 'a denied user'
+ context 'and merge requests are private' do
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
+ end
+
+ describe 'a guest' do
+ subject { guest }
+
+ it_behaves_like 'a denied user'
+ end
+
+ describe 'a reporter' do
+ subject { permissions(reporter, merge_request) }
+
+ it_behaves_like 'a user with reporter access'
+ end
+
+ describe 'a developer' do
+ subject { developer }
+
+ it_behaves_like 'a user with full access'
+ end
+ end
end
- describe 'a developer' do
- subject { developer }
+ context 'when project is private' do
+ let(:project) { create(:project, :private, group: group) }
+
+ describe 'a guest' do
+ subject { guest }
- it_behaves_like 'a user with access'
+ it_behaves_like 'a denied user'
+ end
+
+ describe 'a reporter' do
+ subject { permissions(reporter, merge_request) }
+
+ it_behaves_like 'a user with reporter access'
+ end
+
+ describe 'a developer' do
+ subject { developer }
+
+ it_behaves_like 'a user with full access'
+ end
end
end
- context 'when merge request is unlocked' do
- let(:merge_request) { create(:merge_request, :closed, source_project: project, target_project: project, author: author) }
+ context 'when user is an inherited member from a shared group' do
+ let(:project) { create(:project, :public) }
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) }
+ let(:user) { author }
- it 'allows author to reopen merge request' do
- expect(permissions(author, merge_request)).to be_allowed(:reopen_merge_request)
+ before do
+ project.add_guest(author)
end
- it 'allows developer to reopen merge request' do
- expect(permissions(developer, merge_request)).to be_allowed(:reopen_merge_request)
- end
+ context 'and group is given developer access' do
+ let(:user) { non_team_member }
+
+ subject { permissions(user, merge_request) }
+
+ before do
+ group = create(:group)
+ project.project_group_links.create!(
+ group: group,
+ group_access: Gitlab::Access::DEVELOPER)
+
+ group.add_guest(non_team_member)
+ end
- it 'prevents guest from reopening merge request' do
- expect(permissions(guest, merge_request)).to be_disallowed(:reopen_merge_request)
+ specify do
+ is_expected.to be_allowed(:approve_merge_request)
+ end
end
end
- context 'when merge request is locked' do
- let(:merge_request_locked) { create(:merge_request, :closed, discussion_locked: true, source_project: project, target_project: project, author: author) }
+ context 'when user is not a project member' do
+ let(:project) { create(:project, :public) }
- it 'prevents author from reopening merge request' do
- expect(permissions(author, merge_request_locked)).to be_disallowed(:reopen_merge_request)
- end
+ context 'when merge request is public' do
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
- it 'prevents developer from reopening merge request' do
- expect(permissions(developer, merge_request_locked)).to be_disallowed(:reopen_merge_request)
+ subject { permissions(non_team_member, merge_request) }
+
+ specify do
+ is_expected.not_to be_allowed(:approve_merge_request)
+ end
end
- it 'prevents guests from reopening merge request' do
- expect(permissions(guest, merge_request_locked)).to be_disallowed(:reopen_merge_request)
+ context 'when merge requests are disabled' do
+ let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
+
+ before do
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::DISABLED)
+ end
+
+ subject { non_team_member }
+
+ it_behaves_like 'a denied user'
end
- context 'when the user is not a project member' do
- let(:user) { create(:user) }
+ context 'when merge requests are private' do
+ let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
- it 'cannot create a note' do
- expect(permissions(user, merge_request_locked)).to be_disallowed(:create_note)
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
end
+
+ subject { non_team_member }
+
+ it_behaves_like 'a denied user'
end
- context 'when the user is project member, with at least guest access' do
- let(:user) { guest }
+ context 'when merge request is locked' do
+ let(:merge_request) { create(:merge_request, :closed, discussion_locked: true, source_project: project, target_project: project) }
- it 'can create a note' do
- expect(permissions(user, merge_request_locked)).to be_allowed(:create_note)
+ it 'cannot create a note' do
+ expect(permissions(non_team_member, merge_request)).to be_disallowed(:create_note)
end
end
end
- context 'with external authorization enabled' do
- let(:user) { create(:user) }
+ context 'when user is anonymous' do
let(:project) { create(:project, :public) }
- let(:merge_request) { create(:merge_request, source_project: project) }
- let(:policies) { described_class.new(user, merge_request) }
- before do
- enable_external_authorization_service_check
- end
+ context 'when merge request is public' do
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
- it 'can read the issue iid without accessing the external service' do
- expect(::Gitlab::ExternalAuthorization).not_to receive(:access_allowed?)
+ subject { permissions(nil, merge_request) }
- expect(policies).to be_allowed(:read_merge_request_iid)
+ specify do
+ is_expected.to be_disallowed(:create_todo, :update_subscription)
+ end
end
end
end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 2a03ae89389..320e9e0cd66 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -1120,6 +1120,44 @@ RSpec.describe API::MergeRequests do
end.not_to exceed_query_limit(control)
end
end
+
+ context 'when user is an inherited member from the group' do
+ let_it_be(:group) { create(:group) }
+
+ shared_examples 'user cannot view merge requests' do
+ it 'returns 403 forbidden' do
+ get api("/projects/#{group_project.id}/merge_requests", inherited_user)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'and user is a guest' do
+ let_it_be(:inherited_user) { create(:user) }
+
+ before_all do
+ group.add_guest(inherited_user)
+ end
+
+ context 'when project is public with private merge requests' do
+ let(:group_project) do
+ create(:project,
+ :public,
+ :repository,
+ group: group,
+ merge_requests_access_level: ProjectFeature::DISABLED)
+ end
+
+ it_behaves_like 'user cannot view merge requests'
+ end
+
+ context 'when project is private' do
+ let(:group_project) { create(:project, :private, :repository, group: group) }
+
+ it_behaves_like 'user cannot view merge requests'
+ end
+ end
+ end
end
describe "GET /groups/:id/merge_requests" do
@@ -2219,6 +2257,59 @@ RSpec.describe API::MergeRequests do
expect(response).to have_gitlab_http_status(:created)
end
end
+
+ context 'when user is an inherited member from the group' do
+ let_it_be(:group) { create(:group) }
+
+ shared_examples 'user cannot create merge requests' do
+ it 'returns 403 forbidden' do
+ post api("/projects/#{group_project.id}/merge_requests", inherited_user), params: params
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'and user is a guest' do
+ let_it_be(:inherited_user) { create(:user) }
+ let_it_be(:params) do
+ {
+ title: 'Test merge request',
+ source_branch: 'feature_conflict',
+ target_branch: 'master',
+ author_id: inherited_user.id
+ }
+ end
+
+ before_all do
+ group.add_guest(inherited_user)
+ end
+
+ context 'when project is public with private merge requests' do
+ let(:group_project) do
+ create(:project,
+ :public,
+ :repository,
+ group: group,
+ merge_requests_access_level: ProjectFeature::DISABLED,
+ only_allow_merge_if_pipeline_succeeds: false)
+ end
+
+ it_behaves_like 'user cannot create merge requests'
+ end
+
+ context 'when project is private' do
+ let(:group_project) do
+ create(:project,
+ :private,
+ :repository,
+ group: group,
+ only_allow_merge_if_pipeline_succeeds: false)
+ end
+
+ it_behaves_like 'user cannot create merge requests'
+ end
+ end
+ end
end
describe 'PUT /projects/:id/merge_requests/:merge_request_iid' do
diff --git a/spec/requests/api/project_packages_spec.rb b/spec/requests/api/project_packages_spec.rb
index 5f4b8899a33..7a05da8e13f 100644
--- a/spec/requests/api/project_packages_spec.rb
+++ b/spec/requests/api/project_packages_spec.rb
@@ -86,6 +86,18 @@ RSpec.describe API::ProjectPackages do
expect(json_response).to include(a_hash_including('_links' => a_hash_including('web_path' => include(nested_project.namespace.full_path))))
end
end
+
+ context 'with JOB-TOKEN auth' do
+ let(:job) { create(:ci_build, :running, user: user) }
+
+ subject { get api(url, job_token: job.token) }
+
+ it_behaves_like 'returns packages', :project, :maintainer
+ it_behaves_like 'returns packages', :project, :developer
+ it_behaves_like 'returns packages', :project, :reporter
+ it_behaves_like 'returns packages', :project, :no_type
+ it_behaves_like 'returns packages', :project, :guest
+ end
end
context 'project is private' do
@@ -116,6 +128,19 @@ RSpec.describe API::ProjectPackages do
end
end
end
+
+ context 'with JOB-TOKEN auth' do
+ let(:job) { create(:ci_build, :running, user: user) }
+
+ subject { get api(url, job_token: job.token) }
+
+ it_behaves_like 'returns packages', :project, :maintainer
+ it_behaves_like 'returns packages', :project, :developer
+ it_behaves_like 'returns packages', :project, :reporter
+ it_behaves_like 'rejects packages access', :project, :no_type, :not_found
+ # TODO uncomment when https://gitlab.com/gitlab-org/gitlab/-/issues/370998 is resolved
+ # it_behaves_like 'rejects packages access', :project, :guest, :not_found
+ end
end
context 'with pagination params' do
@@ -177,6 +202,8 @@ RSpec.describe API::ProjectPackages do
end
describe 'GET /projects/:id/packages/:package_id' do
+ let(:single_package_schema) { 'public_api/v4/packages/package' }
+
subject { get api(package_url, user) }
shared_examples 'no destroy url' do
@@ -217,7 +244,7 @@ RSpec.describe API::ProjectPackages do
subject
expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('public_api/v4/packages/package')
+ expect(response).to match_response_schema(single_package_schema)
end
it 'returns 404 when the package does not exist' do
@@ -233,6 +260,18 @@ RSpec.describe API::ProjectPackages do
end
it_behaves_like 'no destroy url'
+
+ context 'with JOB-TOKEN auth' do
+ let(:job) { create(:ci_build, :running, user: user) }
+
+ subject { get api(package_url, job_token: job.token) }
+
+ it_behaves_like 'returns package', :project, :maintainer
+ it_behaves_like 'returns package', :project, :developer
+ it_behaves_like 'returns package', :project, :reporter
+ it_behaves_like 'returns package', :project, :no_type
+ it_behaves_like 'returns package', :project, :guest
+ end
end
context 'project is private' do
@@ -259,7 +298,7 @@ RSpec.describe API::ProjectPackages do
subject
expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('public_api/v4/packages/package')
+ expect(response).to match_response_schema(single_package_schema)
end
it_behaves_like 'no destroy url'
@@ -273,6 +312,19 @@ RSpec.describe API::ProjectPackages do
it_behaves_like 'destroy url'
end
+ context 'with JOB-TOKEN auth' do
+ let(:job) { create(:ci_build, :running, user: user) }
+
+ subject { get api(package_url, job_token: job.token) }
+
+ it_behaves_like 'returns package', :project, :maintainer
+ it_behaves_like 'returns package', :project, :developer
+ it_behaves_like 'returns package', :project, :reporter
+ # TODO uncomment when https://gitlab.com/gitlab-org/gitlab/-/issues/370998 is resolved
+ # it_behaves_like 'rejects packages access', :project, :guest, :not_found
+ it_behaves_like 'rejects packages access', :project, :no_type, :not_found
+ end
+
context 'with pipeline' do
let!(:package1) { create(:npm_package, :with_build, project: project) }
@@ -355,6 +407,26 @@ RSpec.describe API::ProjectPackages do
expect(response).to have_gitlab_http_status(:no_content)
end
+
+ context 'with JOB-TOKEN auth' do
+ let(:job) { create(:ci_build, :running, user: user) }
+
+ it 'returns 403 for a user without enough permissions' do
+ project.add_developer(user)
+
+ expect { delete api(package_url, job_token: job.token) }.not_to change { ::Packages::Package.pending_destruction.count }
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ it 'returns 204' do
+ project.add_maintainer(user)
+
+ expect { delete api(package_url, job_token: job.token) }.to change { ::Packages::Package.pending_destruction.count }.by(1)
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ end
+ end
end
context 'with a maven package' do
diff --git a/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb b/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb
index 4326fa5533f..cc808b7e61c 100644
--- a/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb
+++ b/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do
expect(pipeline.statuses).to match_array [test, bridge]
expect(bridge.options).to eq(expected_bridge_options)
expect(bridge.yaml_variables)
- .to include(key: 'CROSS', value: 'downstream', public: true)
+ .to include(key: 'CROSS', value: 'downstream')
end
end
diff --git a/spec/services/ci/list_config_variables_service_spec.rb b/spec/services/ci/list_config_variables_service_spec.rb
index 1735f4cfc97..4953b18bfcc 100644
--- a/spec/services/ci/list_config_variables_service_spec.rb
+++ b/spec/services/ci/list_config_variables_service_spec.rb
@@ -40,8 +40,8 @@ RSpec.describe Ci::ListConfigVariablesService, :use_clean_rails_memory_store_cac
it 'returns variable list' do
expect(subject['KEY1']).to eq({ value: 'val 1', description: 'description 1' })
expect(subject['KEY2']).to eq({ value: 'val 2', description: '' })
- expect(subject['KEY3']).to eq({ value: 'val 3', description: nil })
- expect(subject['KEY4']).to eq({ value: 'val 4', description: nil })
+ expect(subject['KEY3']).to eq({ value: 'val 3' })
+ expect(subject['KEY4']).to eq({ value: 'val 4' })
end
end
diff --git a/spec/support/shared_examples/services/packages_shared_examples.rb b/spec/support/shared_examples/services/packages_shared_examples.rb
index 6bc4f171d9c..704a4bbe0b8 100644
--- a/spec/support/shared_examples/services/packages_shared_examples.rb
+++ b/spec/support/shared_examples/services/packages_shared_examples.rb
@@ -81,6 +81,26 @@ RSpec.shared_examples 'returns packages' do |container_type, user_type|
end
end
+RSpec.shared_examples 'returns package' do |container_type, user_type|
+ context "for #{user_type}" do
+ before do
+ send(container_type)&.send("add_#{user_type}", user) unless user_type == :no_type
+ end
+
+ it 'returns success response' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:success)
+ end
+
+ it 'returns a valid response schema' do
+ subject
+
+ expect(response).to match_response_schema(single_package_schema)
+ end
+ end
+end
+
RSpec.shared_examples 'returns packages with subgroups' do |container_type, user_type|
context "with subgroups for #{user_type}" do
before do
diff --git a/vendor/gems/omniauth-azure-oauth2/.gitlab-ci.yml b/vendor/gems/omniauth-azure-oauth2/.gitlab-ci.yml
new file mode 100644
index 00000000000..575fec39767
--- /dev/null
+++ b/vendor/gems/omniauth-azure-oauth2/.gitlab-ci.yml
@@ -0,0 +1,28 @@
+workflow:
+ rules:
+ - if: $CI_MERGE_REQUEST_ID
+
+.rspec:
+ cache:
+ key: omniauth-azure-oauth2
+ paths:
+ - vendor/gems/omniauth-azure-oauth2/vendor/ruby
+ before_script:
+ - cd vendor/gems/omniauth-azure-oauth2
+ - ruby -v # Print out ruby version for debugging
+ - gem install bundler --no-document # Bundler is not installed with the image
+ - bundle config set --local path 'vendor' # Install dependencies into ./vendor/ruby
+ - bundle config set with 'development'
+ - bundle config set --local frozen 'true' # Disallow Gemfile.lock changes on CI
+ - bundle config # Show bundler configuration
+ - bundle install -j $(nproc)
+ script:
+ - bundle exec rspec
+
+rspec-2.7:
+ image: "ruby:2.7"
+ extends: .rspec
+
+rspec-3.0:
+ image: "ruby:3.0"
+ extends: .rspec
diff --git a/vendor/gems/omniauth-azure-oauth2/CHANGELOG.md b/vendor/gems/omniauth-azure-oauth2/CHANGELOG.md
new file mode 100644
index 00000000000..ca274303b18
--- /dev/null
+++ b/vendor/gems/omniauth-azure-oauth2/CHANGELOG.md
@@ -0,0 +1,31 @@
+# Version 0.0.9
+* Expand JWT dep. Thanks @ronaldsalas
+
+# Version 0.0.9
+* Added support for dynamic tenant urls. Thanks @marcus-fellinger-esc
+
+# Version 0.0.8
+* Upgrade to omniauth-oauth2 1.4.0 and fix callback url issue
+* Allow prompt parameter, thanks @hilu
+* Add tenant id to info
+* Updated base url
+
+# Version 0.0.6
+* Use 'name' from Azure for name, and 'unique_name' for nickname per Auth Hash spec. Thanks @jayme-github
+
+# Version 0.0.5
+* loosen jwt requirement
+
+# Version 0.0.5
+* loosen jwt requirement
+
+# VERSION 0.0.4
+* fix for JWT scoping, thanks @tobsher
+
+# VERSION 0.0.3
+* added common endpoint and removed mandatory requirement for tenant-id
+* upgraded jwt
+
+# VERSION 0.0.1
+
+* Initial build \ No newline at end of file
diff --git a/vendor/gems/omniauth-azure-oauth2/Gemfile b/vendor/gems/omniauth-azure-oauth2/Gemfile
new file mode 100644
index 00000000000..ef2f8b4147f
--- /dev/null
+++ b/vendor/gems/omniauth-azure-oauth2/Gemfile
@@ -0,0 +1,8 @@
+source 'https://rubygems.org'
+
+# Specify your gem's dependencies in omniauth-azure-oauth2.gemspec
+gemspec
+
+group :example do
+ gem 'sinatra'
+end \ No newline at end of file
diff --git a/vendor/gems/omniauth-azure-oauth2/Gemfile.lock b/vendor/gems/omniauth-azure-oauth2/Gemfile.lock
new file mode 100644
index 00000000000..0bd5d401175
--- /dev/null
+++ b/vendor/gems/omniauth-azure-oauth2/Gemfile.lock
@@ -0,0 +1,73 @@
+PATH
+ remote: .
+ specs:
+ omniauth-azure-oauth2 (0.0.10)
+ jwt (>= 1.0, < 3.0)
+ omniauth (~> 1.0, < 3)
+ omniauth-oauth2 (~> 1.4)
+
+GEM
+ remote: https://rubygems.org/
+ specs:
+ diff-lcs (1.5.0)
+ faraday (2.5.2)
+ faraday-net_http (>= 2.0, < 3.1)
+ ruby2_keywords (>= 0.0.4)
+ faraday-net_http (3.0.0)
+ hashie (5.0.0)
+ jwt (2.4.1)
+ multi_xml (0.6.0)
+ mustermann (2.0.2)
+ ruby2_keywords (~> 0.0.1)
+ oauth2 (2.0.6)
+ faraday (>= 0.17.3, < 3.0)
+ jwt (>= 1.0, < 3.0)
+ multi_xml (~> 0.5)
+ rack (>= 1.2, < 3)
+ rash_alt (>= 0.4, < 1)
+ version_gem (~> 1.1)
+ omniauth (1.9.1)
+ hashie (>= 3.4.6)
+ rack (>= 1.6.2, < 3)
+ omniauth-oauth2 (1.7.3)
+ oauth2 (>= 1.4, < 3)
+ omniauth (>= 1.9, < 3)
+ rack (2.2.4)
+ rack-protection (2.2.2)
+ rack
+ rake (13.0.6)
+ rash_alt (0.4.12)
+ hashie (>= 3.4)
+ rspec (3.11.0)
+ rspec-core (~> 3.11.0)
+ rspec-expectations (~> 3.11.0)
+ rspec-mocks (~> 3.11.0)
+ rspec-core (3.11.0)
+ rspec-support (~> 3.11.0)
+ rspec-expectations (3.11.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.11.0)
+ rspec-mocks (3.11.1)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.11.0)
+ rspec-support (3.11.0)
+ ruby2_keywords (0.0.5)
+ sinatra (2.2.2)
+ mustermann (~> 2.0)
+ rack (~> 2.2)
+ rack-protection (= 2.2.2)
+ tilt (~> 2.0)
+ tilt (2.0.11)
+ version_gem (1.1.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ omniauth-azure-oauth2!
+ rake
+ rspec (>= 2.14.0)
+ sinatra
+
+BUNDLED WITH
+ 2.3.20
diff --git a/vendor/gems/omniauth-azure-oauth2/LICENSE b/vendor/gems/omniauth-azure-oauth2/LICENSE
new file mode 100644
index 00000000000..57ecd3eabb9
--- /dev/null
+++ b/vendor/gems/omniauth-azure-oauth2/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2014 Deltek
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
diff --git a/vendor/gems/omniauth-azure-oauth2/README.md b/vendor/gems/omniauth-azure-oauth2/README.md
new file mode 100644
index 00000000000..a28e9ffdfd2
--- /dev/null
+++ b/vendor/gems/omniauth-azure-oauth2/README.md
@@ -0,0 +1,161 @@
+# OmniAuth Windows Azure Active Directory Strategy
+
+This is fork of [omniauth-azure-oauth2](https://github.com/marknadig/omniauth-azure-oauth2) to support:
+
+1. OmniAuth v1 and v2. OmniAuth v2 disables GET requests by default
+ and defaults to POST. GitLab already has patched v1 to use POST,
+ but other dependencies need to be updated:
+ https://gitlab.com/gitlab-org/gitlab/-/issues/30073.
+2. We may deprecate this library entirely in the future:
+ https://gitlab.com/gitlab-org/gitlab/-/issues/366212
+
+[![Build Status](https://travis-ci.org/KonaTeam/omniauth-azure-oauth2.svg?branch=master)](https://travis-ci.org/KonaTeam/omniauth-azure-oauth2)
+
+This gem provides a simple way to authenticate to Windows Azure Active Directory (WAAD) over OAuth2 using OmniAuth.
+
+One of the unique challenges of WAAD OAuth is that WAAD is multi tenant. Any given tenant can have multiple active
+directories. The CLIENT-ID, REPLY-URL and keys will be unique to the tenant/AD/application combination. This gem simply
+provides hooks for determining those unique values for each call.
+
+## Installation
+
+Add this line to your application's Gemfile:
+
+```ruby
+gem 'omniauth-azure-oauth2'
+```
+
+## Usage
+
+First, you will need to add your site as an application in WAAD.:
+[Adding, Updating, and Removing an Application](http://msdn.microsoft.com/en-us/library/azure/dn132599.aspx)
+
+Summary:
+Select your Active Directory in https://manage.windowsazure.com/<tenantid> of type 'Web Application'. Name, sign-on url,
+logo are not important. You will need the CLIENT-ID from the application configuration and you will need to generate
+an expiring key (aka 'client secret'). REPLY URL is the oauth redirect uri which will be the omniauth callback path
+https://example.com/users/auth/azure_oauth2/callback. The APP ID UI just needs to be unique to that tenant and identify
+your site and isn't needed to configure the gem.
+Permissions need Delegated Permissions to at least have "Enable sign-on and read user's profiles".
+
+Note: Seems like the terminology is still fluid, so follow the MS guidance (buwahaha) to set this up.
+
+The TenantInfo information can be a hash or class. It must provide client_id and client_secret.
+Optionally a domain_hint and tenant_id. For a simple single-tenant app, this could be:
+
+```ruby
+use OmniAuth::Builder do
+ provider :azure_oauth2,
+ {
+ client_id: ENV['AZURE_CLIENT_ID'],
+ client_secret: ENV['AZURE_CLIENT_SECRET'],
+ tenant_id: ENV['AZURE_TENANT_ID']
+ }
+end
+```
+
+Or the alternative format for use with [devise](https://github.com/plataformatec/devise):
+
+```ruby
+config.omniauth :azure_oauth2, client_id: ENV['AZURE_CLIENT_ID'],
+ client_secret: ENV['AZURE_CLIENT_SECRET'], tenant_id: ENV['AZURE_TENANT_ID']
+```
+
+For multi-tenant apps where you don't know the tenant_id in advance, simply leave out the tenant_id to use the
+[common endpoint](http://msdn.microsoft.com/en-us/library/azure/dn645542.aspx).
+
+```ruby
+use OmniAuth::Builder do
+ provider :azure_oauth2,
+ {
+ client_id: ENV['AZURE_CLIENT_ID'],
+ client_secret: ENV['AZURE_CLIENT_SECRET']
+ }
+end
+```
+
+For dynamic tenant assignment, pass a class that supports those same attributes and accepts the strategy as a parameter
+
+```ruby
+class YouTenantProvider
+ def initialize(strategy)
+ @strategy = strategy
+ end
+
+ def client_id
+ tenant.azure_client_id
+ end
+
+ def client_secret
+ tenant.azure_client_secret
+ end
+
+ def tenant_id
+ tenant.azure_tanant_id
+ end
+
+ def domain_hint
+ tenant.azure_domain_hint
+ end
+
+ private
+
+ def tenant
+ # whatever strategy you want to figure out the right tenant from params/session
+ @tenant ||= Customer.find(@strategy.session[:customer_id])
+ end
+end
+
+use OmniAuth::Builder do
+ provider :azure_oauth2, YourTenantProvider
+end
+```
+
+The base_azure_url can be overridden in the provider configuration for different locales; e.g. `base_azure_url: "https://login.microsoftonline.de"`
+
+
+## Auth Hash Schema
+
+The following information is provided back to you for this provider:
+
+```ruby
+{
+ uid: '12345',
+ info: {
+ name: 'some one',
+ first_name: 'some',
+ last_name: 'one',
+ email: 'someone@example.com'
+ },
+ credentials: {
+ token: 'thetoken',
+ refresh_token: 'refresh'
+ },
+ extra: { raw_info: raw_api_response }
+}
+```
+## notes
+
+When you make a request to WAAD you must specify a resource. The gem currently assumes this is the AD identified as '00000002-0000-0000-c000-000000000000'.
+This can be passed in as part of the config. It currently isn't designed to be dynamic.
+
+```ruby
+use OmniAuth::Builder do
+ provider :azure_oauth2, TenantInfo, resource: 'myresource'
+end
+```
+
+## Contributing
+
+1. Fork it
+2. Create your feature branch (`git checkout -b my-new-feature`)
+3. Make your changes, add tests, run tests (`rake`)
+4. Commit your changes and tests (`git commit -am 'Added some feature'`)
+5. Push to the branch (`git push origin my-new-feature`)
+6. Create new Pull Request
+
+
+## Misc
+Run tests `bundle exec rake`
+Push to rubygems `bundle exec rake release`.
+
diff --git a/vendor/gems/omniauth-azure-oauth2/Rakefile b/vendor/gems/omniauth-azure-oauth2/Rakefile
new file mode 100644
index 00000000000..965431eb7c9
--- /dev/null
+++ b/vendor/gems/omniauth-azure-oauth2/Rakefile
@@ -0,0 +1,6 @@
+require File.join('bundler', 'gem_tasks')
+require File.join('rspec', 'core', 'rake_task')
+
+RSpec::Core::RakeTask.new(:spec)
+
+task :default => :spec \ No newline at end of file
diff --git a/vendor/gems/omniauth-azure-oauth2/examples/sinatra.rb b/vendor/gems/omniauth-azure-oauth2/examples/sinatra.rb
new file mode 100644
index 00000000000..3db9e5fe435
--- /dev/null
+++ b/vendor/gems/omniauth-azure-oauth2/examples/sinatra.rb
@@ -0,0 +1,31 @@
+$:.push File.dirname(__FILE__) + '/../lib'
+
+require 'omniauth-azure-oauth2'
+require 'sinatra'
+
+class MyAzureProvider
+ def self.client_id
+ ENV['AZURE_CLIENT_ID']
+ end
+
+ def self.client_secret
+ ENV['AZURE_CLIENT_SECRET']
+ end
+
+ def self.tenant_id
+ ENV['AZURE_TENANT_ID']
+ end
+
+end
+
+use Rack::Session::Cookie
+use OmniAuth::Strategies::Azure, MyAzureProvider
+
+get '/' do
+ "<a href='/auth/azure_oauth2'>Log in with Azure</a>"
+end
+
+get '/auth/azure_oauth2/callback' do
+ content_type 'text/plain'
+ request.env['omniauth.auth'].inspect
+end \ No newline at end of file
diff --git a/vendor/gems/omniauth-azure-oauth2/lib/omniauth-azure-oauth2.rb b/vendor/gems/omniauth-azure-oauth2/lib/omniauth-azure-oauth2.rb
new file mode 100644
index 00000000000..121c26842aa
--- /dev/null
+++ b/vendor/gems/omniauth-azure-oauth2/lib/omniauth-azure-oauth2.rb
@@ -0,0 +1 @@
+require File.join('omniauth', 'azure_oauth2') \ No newline at end of file
diff --git a/vendor/gems/omniauth-azure-oauth2/lib/omniauth/azure_oauth2.rb b/vendor/gems/omniauth-azure-oauth2/lib/omniauth/azure_oauth2.rb
new file mode 100644
index 00000000000..69651ede9e7
--- /dev/null
+++ b/vendor/gems/omniauth-azure-oauth2/lib/omniauth/azure_oauth2.rb
@@ -0,0 +1 @@
+require File.join('omniauth', 'strategies', 'azure_oauth2') \ No newline at end of file
diff --git a/vendor/gems/omniauth-azure-oauth2/lib/omniauth/azure_oauth2/version.rb b/vendor/gems/omniauth-azure-oauth2/lib/omniauth/azure_oauth2/version.rb
new file mode 100644
index 00000000000..cfaa9ddd458
--- /dev/null
+++ b/vendor/gems/omniauth-azure-oauth2/lib/omniauth/azure_oauth2/version.rb
@@ -0,0 +1,5 @@
+module OmniAuth
+ module AzureOauth2
+ VERSION = "0.0.10"
+ end
+end
diff --git a/vendor/gems/omniauth-azure-oauth2/lib/omniauth/strategies/azure_oauth2.rb b/vendor/gems/omniauth-azure-oauth2/lib/omniauth/strategies/azure_oauth2.rb
new file mode 100644
index 00000000000..f18babc0619
--- /dev/null
+++ b/vendor/gems/omniauth-azure-oauth2/lib/omniauth/strategies/azure_oauth2.rb
@@ -0,0 +1,73 @@
+require 'omniauth/strategies/oauth2'
+require 'jwt'
+
+module OmniAuth
+ module Strategies
+ class AzureOauth2 < OmniAuth::Strategies::OAuth2
+ BASE_AZURE_URL = 'https://login.microsoftonline.com'
+
+ option :name, 'azure_oauth2'
+
+ option :tenant_provider, nil
+
+ # AD resource identifier
+ option :resource, '00000002-0000-0000-c000-000000000000'
+
+ # tenant_provider must return client_id, client_secret and optionally tenant_id and base_azure_url
+ args [:tenant_provider]
+
+ def client
+ if options.tenant_provider
+ provider = options.tenant_provider.new(self)
+ else
+ provider = options # if pass has to config, get mapped right on to options
+ end
+
+ options.client_id = provider.client_id
+ options.client_secret = provider.client_secret
+ options.tenant_id =
+ provider.respond_to?(:tenant_id) ? provider.tenant_id : 'common'
+ options.base_azure_url =
+ provider.respond_to?(:base_azure_url) ? provider.base_azure_url : BASE_AZURE_URL
+
+ options.authorize_params = provider.authorize_params if provider.respond_to?(:authorize_params)
+ options.authorize_params.domain_hint = provider.domain_hint if provider.respond_to?(:domain_hint) && provider.domain_hint
+ options.authorize_params.prompt = request.params['prompt'] if request.params['prompt']
+ options.client_options.authorize_url = "#{options.base_azure_url}/#{options.tenant_id}/oauth2/authorize"
+ options.client_options.token_url = "#{options.base_azure_url}/#{options.tenant_id}/oauth2/token"
+ super
+ end
+
+ uid {
+ raw_info['sub']
+ }
+
+ info do
+ {
+ name: raw_info['name'],
+ nickname: raw_info['unique_name'],
+ first_name: raw_info['given_name'],
+ last_name: raw_info['family_name'],
+ email: raw_info['email'] || raw_info['upn'],
+ oid: raw_info['oid'],
+ tid: raw_info['tid']
+ }
+ end
+
+ def token_params
+ azure_resource = request.env['omniauth.params'] && request.env['omniauth.params']['azure_resource']
+ super.merge(resource: azure_resource || options.resource)
+ end
+
+ def callback_url
+ full_host + script_name + callback_path
+ end
+
+ def raw_info
+ # it's all here in JWT http://msdn.microsoft.com/en-us/library/azure/dn195587.aspx
+ @raw_info ||= ::JWT.decode(access_token.token, nil, false).first
+ end
+
+ end
+ end
+end
diff --git a/vendor/gems/omniauth-azure-oauth2/omniauth-azure-oauth2.gemspec b/vendor/gems/omniauth-azure-oauth2/omniauth-azure-oauth2.gemspec
new file mode 100644
index 00000000000..6e1bc583881
--- /dev/null
+++ b/vendor/gems/omniauth-azure-oauth2/omniauth-azure-oauth2.gemspec
@@ -0,0 +1,25 @@
+# -*- encoding: utf-8 -*-
+require File.expand_path(File.join('..', 'lib', 'omniauth', 'azure_oauth2', 'version'), __FILE__)
+
+Gem::Specification.new do |gem|
+ gem.authors = ["Mark Nadig"]
+ gem.email = ["mark@nadigs.net"]
+ gem.description = %q{An Windows Azure Active Directory OAuth2 strategy for OmniAuth}
+ gem.summary = %q{An Windows Azure Active Directory OAuth2 strategy for OmniAuth}
+ gem.homepage = "https://github.com/KonaTeam/omniauth-azure-oauth2"
+
+ gem.files = Dir.glob("lib/**/*.*")
+ gem.test_files = Dir.glob("spec/**/**/*.*")
+ gem.name = "omniauth-azure-oauth2"
+ gem.require_paths = ["lib"]
+ gem.version = OmniAuth::AzureOauth2::VERSION
+ gem.license = "MIT"
+
+ gem.add_runtime_dependency 'omniauth', '~> 1.0', '< 3'
+ gem.add_dependency 'jwt', ['>= 1.0', '< 3.0']
+
+ gem.add_runtime_dependency 'omniauth-oauth2', '~> 1.4'
+
+ gem.add_development_dependency 'rspec', '>= 2.14.0'
+ gem.add_development_dependency 'rake'
+end
diff --git a/vendor/gems/omniauth-azure-oauth2/spec/omniauth/strategies/azure_oauth2_spec.rb b/vendor/gems/omniauth-azure-oauth2/spec/omniauth/strategies/azure_oauth2_spec.rb
new file mode 100644
index 00000000000..d171d88ac6c
--- /dev/null
+++ b/vendor/gems/omniauth-azure-oauth2/spec/omniauth/strategies/azure_oauth2_spec.rb
@@ -0,0 +1,332 @@
+require 'spec_helper'
+require 'omniauth-azure-oauth2'
+
+module OmniAuth
+ module Strategies
+ module JWT; end
+ end
+end
+
+describe OmniAuth::Strategies::AzureOauth2 do
+ let(:request) { double('Request', :params => {}, :cookies => {}, :env => {}) }
+ let(:app) {
+ lambda do
+ [200, {}, ["Hello."]]
+ end
+ }
+
+ before do
+ OmniAuth.config.test_mode = true
+ end
+
+ after do
+ OmniAuth.config.test_mode = false
+ end
+
+ describe 'static configuration' do
+ let(:options) { @options || {} }
+ subject do
+ OmniAuth::Strategies::AzureOauth2.new(app, {client_id: 'id', client_secret: 'secret', tenant_id: 'tenant'}.merge(options))
+ end
+
+ describe '#client' do
+ it 'has correct authorize url' do
+ allow(subject).to receive(:request) { request }
+ expect(subject.client.options[:authorize_url]).to eql('https://login.microsoftonline.com/tenant/oauth2/authorize')
+ end
+
+ it 'has correct authorize params' do
+ allow(subject).to receive(:request) { request }
+ subject.client
+ expect(subject.authorize_params[:domain_hint]).to be_nil
+ end
+
+ it 'has correct token url' do
+ allow(subject).to receive(:request) { request }
+ expect(subject.client.options[:token_url]).to eql('https://login.microsoftonline.com/tenant/oauth2/token')
+ end
+
+ describe "overrides" do
+ it 'should override domain_hint' do
+ @options = {domain_hint: 'hint'}
+ allow(subject).to receive(:request) { request }
+ subject.client
+ expect(subject.authorize_params[:domain_hint]).to eql('hint')
+ end
+ end
+ end
+
+ end
+
+ describe 'static configuration - german' do
+ let(:options) { @options || {} }
+ subject do
+ OmniAuth::Strategies::AzureOauth2.new(app, {client_id: 'id', client_secret: 'secret', tenant_id: 'tenant', base_azure_url: 'https://login.microsoftonline.de'}.merge(options))
+ end
+
+ describe '#client' do
+ it 'has correct authorize url' do
+ allow(subject).to receive(:request) { request }
+ expect(subject.client.options[:authorize_url]).to eql('https://login.microsoftonline.de/tenant/oauth2/authorize')
+ end
+
+ it 'has correct authorize params' do
+ allow(subject).to receive(:request) { request }
+ subject.client
+ expect(subject.authorize_params[:domain_hint]).to be_nil
+ end
+
+ it 'has correct token url' do
+ allow(subject).to receive(:request) { request }
+ expect(subject.client.options[:token_url]).to eql('https://login.microsoftonline.de/tenant/oauth2/token')
+ end
+
+ it 'has correct token params' do
+ allow(subject).to receive(:request) { request }
+ subject.client
+ expect(subject.token_params[:resource]).to eql('00000002-0000-0000-c000-000000000000')
+ end
+
+ describe "overrides" do
+ it 'should override domain_hint' do
+ @options = {domain_hint: 'hint'}
+ allow(subject).to receive(:request) { request }
+ subject.client
+ expect(subject.authorize_params[:domain_hint]).to eql('hint')
+ end
+ end
+ end
+ end
+
+ describe 'static common configuration' do
+ let(:options) { @options || {} }
+ subject do
+ OmniAuth::Strategies::AzureOauth2.new(app, {client_id: 'id', client_secret: 'secret'}.merge(options))
+ end
+
+ before do
+ allow(subject).to receive(:request) { request }
+ end
+
+ describe '#client' do
+ it 'has correct authorize url' do
+ expect(subject.client.options[:authorize_url]).to eql('https://login.microsoftonline.com/common/oauth2/authorize')
+ end
+
+ it 'has correct token url' do
+ expect(subject.client.options[:token_url]).to eql('https://login.microsoftonline.com/common/oauth2/token')
+ end
+ end
+ end
+
+ describe 'dynamic configuration' do
+ let(:provider_klass) {
+ Class.new {
+ def initialize(strategy)
+ end
+
+ def client_id
+ 'id'
+ end
+
+ def client_secret
+ 'secret'
+ end
+
+ def tenant_id
+ 'tenant'
+ end
+
+ def authorize_params
+ { custom_option: 'value' }
+ end
+ }
+ }
+
+ subject do
+ OmniAuth::Strategies::AzureOauth2.new(app, provider_klass)
+ end
+
+ before do
+ allow(subject).to receive(:request) { request }
+ end
+
+ describe '#client' do
+ it 'has correct authorize url' do
+ expect(subject.client.options[:authorize_url]).to eql('https://login.microsoftonline.com/tenant/oauth2/authorize')
+ end
+
+ it 'has correct authorize params' do
+ subject.client
+ expect(subject.authorize_params[:domain_hint]).to be_nil
+ expect(subject.authorize_params[:custom_option]).to eql('value')
+ end
+
+ it 'has correct token url' do
+ expect(subject.client.options[:token_url]).to eql('https://login.microsoftonline.com/tenant/oauth2/token')
+ end
+
+ it 'has correct token params' do
+ subject.client
+ expect(subject.token_params[:resource]).to eql('00000002-0000-0000-c000-000000000000')
+ end
+
+ # todo: how to get this working?
+ # describe "overrides" do
+ # it 'should override domain_hint' do
+ # provider_klass.domain_hint = 'hint'
+ # subject.client
+ # expect(subject.authorize_params[:domain_hint]).to eql('hint')
+ # end
+ # end
+ end
+
+ end
+
+ describe 'dynamic configuration - german' do
+ let(:provider_klass) {
+ Class.new {
+ def initialize(strategy)
+ end
+
+ def client_id
+ 'id'
+ end
+
+ def client_secret
+ 'secret'
+ end
+
+ def tenant_id
+ 'tenant'
+ end
+
+ def base_azure_url
+ 'https://login.microsoftonline.de'
+ end
+ }
+ }
+
+ subject do
+ OmniAuth::Strategies::AzureOauth2.new(app, provider_klass)
+ end
+
+ before do
+ allow(subject).to receive(:request) { request }
+ end
+
+ describe '#client' do
+ it 'has correct authorize url' do
+ expect(subject.client.options[:authorize_url]).to eql('https://login.microsoftonline.de/tenant/oauth2/authorize')
+ end
+
+ it 'has correct authorize params' do
+ subject.client
+ expect(subject.authorize_params[:domain_hint]).to be_nil
+ end
+
+ it 'has correct token url' do
+ expect(subject.client.options[:token_url]).to eql('https://login.microsoftonline.de/tenant/oauth2/token')
+ end
+
+ it 'has correct token params' do
+ subject.client
+ expect(subject.token_params[:resource]).to eql('00000002-0000-0000-c000-000000000000')
+ end
+
+ # todo: how to get this working?
+ # describe "overrides" do
+ # it 'should override domain_hint' do
+ # provider_klass.domain_hint = 'hint'
+ # subject.client
+ # expect(subject.authorize_params[:domain_hint]).to eql('hint')
+ # end
+ # end
+ end
+
+ end
+
+ describe 'dynamic common configuration' do
+ let(:provider_klass) {
+ Class.new {
+ def initialize(strategy)
+ end
+
+ def client_id
+ 'id'
+ end
+
+ def client_secret
+ 'secret'
+ end
+ }
+ }
+
+ subject do
+ OmniAuth::Strategies::AzureOauth2.new(app, provider_klass)
+ end
+
+ before do
+ allow(subject).to receive(:request) { request }
+ end
+
+ describe '#client' do
+ it 'has correct authorize url' do
+ expect(subject.client.options[:authorize_url]).to eql('https://login.microsoftonline.com/common/oauth2/authorize')
+ end
+
+ it 'has correct token url' do
+ expect(subject.client.options[:token_url]).to eql('https://login.microsoftonline.com/common/oauth2/token')
+ end
+ end
+ end
+
+ describe "raw_info" do
+ subject do
+ OmniAuth::Strategies::AzureOauth2.new(app, {client_id: 'id', client_secret: 'secret'})
+ end
+
+ let(:token) do
+ JWT.encode({"some" => "payload"}, "secret")
+ end
+
+ let(:access_token) do
+ double(:token => token)
+ end
+
+ before do
+ allow(subject).to receive(:access_token) { access_token }
+ allow(subject).to receive(:request) { request }
+ end
+
+ it "does not clash if JWT strategy is used" do
+ expect do
+ subject.info
+ end.to_not raise_error
+ end
+ end
+
+ describe 'token_params' do
+ let(:strategy) { OmniAuth::Strategies::AzureOauth2.new(app, client_id: 'id', client_secret: 'secret') }
+ let(:request) { double('Request', env: env) }
+ let(:env) { {} }
+
+ subject { strategy.token_params }
+
+ before { allow(strategy).to receive(:request).and_return request }
+
+ it { is_expected.to be_a OmniAuth::Strategy::Options }
+ it 'has default resource' do
+ expect(subject.resource).to eq '00000002-0000-0000-c000-000000000000'
+ end
+
+ context 'when custom crm url' do
+ let(:crm_url) { 'https://mydomain.crm.dynamics.com/' }
+ let(:env) { { 'omniauth.params' => { 'azure_resource' => crm_url } } }
+
+ it 'has resource from url params' do
+ expect(subject.resource).to eq crm_url
+ end
+ end
+ end
+end
diff --git a/vendor/gems/omniauth-azure-oauth2/spec/spec_helper.rb b/vendor/gems/omniauth-azure-oauth2/spec/spec_helper.rb
new file mode 100644
index 00000000000..9d0890421a2
--- /dev/null
+++ b/vendor/gems/omniauth-azure-oauth2/spec/spec_helper.rb
@@ -0,0 +1,2 @@
+require File.join('bundler', 'setup')
+require 'rspec' \ No newline at end of file
diff --git a/vendor/gems/omniauth_crowd/.gitlab-ci.yml b/vendor/gems/omniauth_crowd/.gitlab-ci.yml
index 1ad51409803..08a5da1a3d1 100644
--- a/vendor/gems/omniauth_crowd/.gitlab-ci.yml
+++ b/vendor/gems/omniauth_crowd/.gitlab-ci.yml
@@ -4,7 +4,7 @@ workflow:
.rspec:
cache:
- key: omniauth-gitlab-ruby
+ key: omniauth_crowd
paths:
- vendor/gems/omniauth_crowd/vendor/ruby
before_script:
diff --git a/workhorse/go.mod b/workhorse/go.mod
index 18dba215038..e2191c1a749 100644
--- a/workhorse/go.mod
+++ b/workhorse/go.mod
@@ -129,6 +129,7 @@ exclude (
// CVE-2021-42576
github.com/microcosm-cc/bluemonday v1.0.2
+ github.com/nats-io/jwt v0.3.0
// CVE-2020-26892
github.com/nats-io/nats-server/v2 v2.1.2
diff --git a/workhorse/go.sum b/workhorse/go.sum
index e98e18e96e5..d143f62f812 100644
--- a/workhorse/go.sum
+++ b/workhorse/go.sum
@@ -873,7 +873,6 @@ github.com/montanaflynn/stats v0.0.0-20151014174947-eeaced052adb/go.mod h1:wL8QJ
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
diff --git a/yarn.lock b/yarn.lock
index 137b5fdc073..c143f007aec 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2488,7 +2488,7 @@ acorn-walk@^8.0.0:
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.0.2.tgz#d4632bfc63fd93d0f15fd05ea0e984ffd3f5a8c3"
integrity sha512-+bpA9MJsHdZ4bgfDcpk0ozQyhhVct7rzOmO0s1IIr0AGGgKBljss8n2zp11rRP2wid5VGeh04CgeKzgat5/25A==
-acorn@^6.3.0, acorn@^6.4.1:
+acorn@^6.4.1:
version "6.4.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==