Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-08-17 18:11:29 +00:00
parent 5ff5438a06
commit f1503ea64b
67 changed files with 1874 additions and 580 deletions

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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'

View File

@ -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

View File

@ -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
}

View File

@ -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>

View File

@ -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',
},
};

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
63ec85b4f8b7eb15c232c4a25c1e63027c38c23caf81a89c4d05227a6be00e4b

View File

@ -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);

View File

@ -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.

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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:

View File

@ -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.

View File

@ -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.

View File

@ -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).

View File

@ -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)

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -6,26 +6,24 @@ 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)
)
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)
(new_vars + current_vars).uniq { |var| var[:key] }
end
def transform_to_yaml_variables(vars)
vars.to_h.map do |key, value|
{ key: key.to_s, value: value, public: true }
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 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]] }
end
def inherit_yaml_variables(from:, to:, inheritance:)
merge_variables(apply_inheritance(from, inheritance), to)
end
@ -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

View File

@ -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

View File

@ -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 ""

View File

@ -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",

View File

@ -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))
find(".js-source-branch").click
click_link("fix")
find(".js-target-branch").click
click_link("feature")
click_button("Compare branches")
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
fill_in("Title", with: title)
click_button("Create merge request")
page.within(".merge-request") do
expect(page).to have_content(title)
end
end
context "XSS branch name exists" do
before do
project.repository.create_branch("<img/src='x'/onerror=alert('oops')>", "master")
end
it "doesn't execute the dodgy branch name" do
shared_examples 'creates a merge request' do
specify do
visit(project_new_merge_request_path(project))
find(".js-source-branch").click
click_link("<img/src='x'/onerror=alert('oops')>")
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')
expect { page.driver.browser.switch_to.alert }.to raise_error(Selenium::WebDriver::Error::NoSuchAlertError)
page.within('.merge-request') do
expect(page).to have_content(title)
end
end
end
context "to a forked project" do
let(:forked_project) { fork_project(project, user, namespace: user.namespace, repository: true) }
shared_examples 'renders not found' do
specify do
visit project_new_merge_request_path(project)
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)
click_button("Compare branches and continue")
expect(page).to have_content("You must select source and target branch")
first(".js-source-project").click
first(".dropdown-source-project a", text: forked_project.full_path)
first(".js-target-project").click
first(".dropdown-target-project a", text: project.full_path)
first(".js-source-branch").click
wait_for_requests
source_branch = "fix"
first(".js-source-branch-dropdown .dropdown-content a", text: source_branch).click
click_button("Compare branches and continue")
expect(page).to have_text _('New merge request')
page.within("form#new_merge_request") do
fill_in("Title", with: title)
end
expect(find(".js-assignee-search")["data-project-id"]).to eq(project.id.to_s)
find('.js-assignee-search').click
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")
expect(page).to have_title('Not Found')
expect(page).to have_content('Page Not Found')
end
end
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.add_maintainer(user)
sign_in(user)
end
it_behaves_like 'creates a merge request'
context 'with XSS branch name' do
before do
project.repository.create_branch("<img/src='x'/onerror=alert('oops')>", 'master')
end
it 'does not execute the suspicious branch name' do
visit(project_new_merge_request_path(project))
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)
end
end
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)
click_button('Compare branches and continue')
expect(page).to have_content('You must select source and target branch')
first('.js-source-project').click
first('.dropdown-source-project a', text: forked_project.full_path)
first('.js-target-project').click
first('.dropdown-target-project a', text: project.full_path)
first('.js-source-branch').click
wait_for_requests
source_branch = 'fix'
first('.js-source-branch-dropdown .dropdown-content a', text: source_branch).click
click_button('Compare branches and continue')
expect(page).to have_text _('New merge request')
page.within('form#new_merge_request') do
fill_in('Title', with: title)
end
expect(find('.js-assignee-search')['data-project-id']).to eq(project.id.to_s)
find('.js-assignee-search').click
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
context 'and user is a guest' do
before do
group.add_guest(user)
sign_in(user)
end
it_behaves_like 'renders not found'
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)
sign_in(user)
end
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

View File

@ -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([[[]]]);
});
});
});

View File

@ -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 `;

View File

@ -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' });

View File

@ -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)
end
context 'when user does not have enough permissions to create a merge request' do
before do
project.add_guest(user)
end
it 'raises an error if the resource is not accessible to the user' do
shared_examples 'resource not available' do
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when the user can create a merge request' do
before_all do
project.add_developer(user)
context 'when user is not a project member' do
let_it_be(:project) { create(:project, :public, :repository) }
it_behaves_like 'resource not available'
end
context 'when user is a direct project member' do
let_it_be(:project) { create(:project, :public, :repository) }
context 'and user is a guest' do
before do
project.add_guest(user)
end
it_behaves_like 'resource not available'
end
it 'creates a new merge request' do
expect { mutated_merge_request }.to change(MergeRequest, :count).by(1)
end
context 'and user is a developer' do
before do
project.add_developer(user)
end
it 'returns a new merge request' do
expect(mutated_merge_request.title).to eq(title)
expect(subject[:errors]).to be_empty
end
it 'creates a new merge request' do
expect { mutated_merge_request }.to change(MergeRequest, :count).by(1)
end
context 'when optional description field is set' do
let(:description) { 'content' }
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
context 'when optional description field is set' do
let(:description) { 'content' }
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 optional labels field is set' do
let(:labels) { %w[label-1 label-2] }
context 'when user is an inherited member from the group' do
let_it_be(:group) { create(:group, :public) }
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
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
context 'and user is a guest' do
before do
group.add_guest(user)
end
it_behaves_like 'resource not available'
end
end
context 'when service cannot create a merge request' do
let(:title) { nil }
context 'when project is private' do
let_it_be(:project) { create(:project, :private, :repository, group: group) }
it 'does not create a new merge request' do
expect { mutated_merge_request }.not_to change(MergeRequest, :count)
end
context 'and user is a guest' do
before do
group.add_guest(user)
end
it 'returns errors' do
expect(mutated_merge_request).to be_nil
expect(subject[:errors]).to eq(['Title can\'t be blank'])
it_behaves_like 'resource not available'
end
end
end

View File

@ -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'],

View File

@ -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

View File

@ -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' }
describe '.transform_to_array' do
subject { described_class.transform_to_array(variables) }
context 'when values are strings' do
let(:variables) do
{ 'key1' => 'value1', 'key2' => 'value2' }
end
let(:result) do
[{ key: 'key1', value: 'value1' },
{ key: 'key2', value: 'value2' }]
end
it { is_expected.to match_array(result) }
end
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
describe '.transform_from_yaml_variables' do
let(:variables) do
[{ key: 'key1', value: 'value1', public: true },
{ key: 'key2', value: 'value2', public: true }]
it { is_expected.to match_array([]) }
end
let(:result) do
{ 'key1' => 'value1', 'key2' => 'value2' }
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({}) }
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) }
it { is_expected.to eq(result1) }
context 'when a value data has `key` as a key' do
let(:variables) do
{ 'key1' => { value: 'value1', key: 'new_key1' }, 'key2' => { value: 'value2' } }
end
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

View File

@ -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

View File

@ -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(

View File

@ -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
context 'when user is a direct project member' do
let(:project) { create(:project, :public) }
before do
project.add_guest(guest)
project.add_guest(author)
project.add_developer(developer)
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 author' do
subject { permissions(user, merge_request) }
context 'and the user is a guest' do
let(:user) { guest }
it do
is_expected.to be_allowed(:update_merge_request)
end
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 'when merge requests have been disabled' 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
describe 'the author' do
subject { author }
it_behaves_like 'a denied user'
end
describe 'a guest' do
subject { guest }
it_behaves_like 'a denied user'
end
describe 'a developer' do
subject { developer }
it_behaves_like 'a denied user'
end
end
context 'when merge requests are private' do
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: author) }
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 }
it_behaves_like 'a denied user'
end
describe 'a developer' do
subject { developer }
it_behaves_like 'a user with full access'
end
end
context 'when merge request is unlocked' do
let(:merge_request) { create(:merge_request, :closed, source_project: project, target_project: project, author: author) }
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
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 '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
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) }
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 user is an inherited member from the parent group' do
let_it_be(:group) { create(:group, :public) }
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
context 'when project is public' do
let(:project) { create(:project, :public, group: group) }
describe 'the merge request author' do
subject { permissions(author, merge_request) }
specify do
is_expected.to be_allowed(:approve_merge_request)
end
end
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
context 'when project is private' do
let(:project) { create(:project, :private, group: group) }
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
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 }
context 'and user is anonymous' do
before do
project.add_guest(author)
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
specify do
is_expected.to be_allowed(:approve_merge_request)
end
end
end
context 'when user is not a project member' do
let(:project) { create(:project, :public) }
context 'when merge request is public' do
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
subject { permissions(non_team_member, merge_request) }
specify do
is_expected.not_to be_allowed(:approve_merge_request)
end
end
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 merge requests are private' do
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
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 merge request is locked' do
let(:merge_request) { create(:merge_request, :closed, discussion_locked: true, source_project: project, target_project: project) }
it 'cannot create a note' do
expect(permissions(non_team_member, merge_request)).to be_disallowed(:create_note)
end
end
end
context 'when user is anonymous' do
let(:project) { create(:project, :public) }
context 'when merge request is public' do
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
subject { permissions(nil, merge_request) }
it do
specify do
is_expected.to be_disallowed(:create_todo, :update_subscription)
end
end
context 'and user is author' do
subject { permissions(user, merge_request) }
context 'and the user is a guest' do
let(:user) { guest }
it do
is_expected.to be_allowed(:update_merge_request)
end
it do
is_expected.to be_allowed(:reopen_merge_request)
end
it do
is_expected.to be_allowed(:approve_merge_request)
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
it do
is_expected.to be_allowed(:approve_merge_request)
end
end
context 'and the user is a member of a shared group' do
let(:user) { non_team_member }
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 do
is_expected.to be_allowed(:approve_merge_request)
end
end
context 'and the user is not a project member' do
let(:user) { non_team_member }
it do
is_expected.not_to be_allowed(:approve_merge_request)
end
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) }
before do
project.project_feature.update!(merge_requests_access_level: ProjectFeature::DISABLED)
end
describe 'the author' do
subject { author }
it_behaves_like 'a denied user'
end
describe 'a guest' do
subject { guest }
it_behaves_like 'a denied user'
end
describe 'a developer' do
subject { developer }
it_behaves_like 'a denied user'
end
describe 'any other user' do
subject { non_team_member }
it_behaves_like 'a denied user'
end
end
context 'when merge requests are private' do
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: author) }
before do
project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
end
describe 'a non-team-member' do
subject { non_team_member }
it_behaves_like 'a denied user'
end
describe 'the author' do
subject { author }
it_behaves_like 'a denied user'
end
describe 'a developer' do
subject { developer }
it_behaves_like 'a user with access'
end
end
context 'when merge request is unlocked' do
let(:merge_request) { create(:merge_request, :closed, source_project: project, target_project: project, author: author) }
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
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 '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 not a project member' do
let(:user) { create(:user) }
it 'cannot create a note' do
expect(permissions(user, merge_request_locked)).to be_disallowed(:create_note)
end
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
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) }
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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`.

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
require File.join('omniauth', 'azure_oauth2')

View File

@ -0,0 +1 @@
require File.join('omniauth', 'strategies', 'azure_oauth2')

View File

@ -0,0 +1,5 @@
module OmniAuth
module AzureOauth2
VERSION = "0.0.10"
end
end

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,2 @@
require File.join('bundler', 'setup')
require 'rspec'

View File

@ -4,7 +4,7 @@ workflow:
.rspec:
cache:
key: omniauth-gitlab-ruby
key: omniauth_crowd
paths:
- vendor/gems/omniauth_crowd/vendor/ruby
before_script:

View File

@ -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

View File

@ -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=

View File

@ -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==