Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3454d4cbe3
commit
9517d0eb2c
|
@ -38,7 +38,7 @@ review-docs-cleanup:
|
|||
script:
|
||||
- ./scripts/trigger-build docs cleanup
|
||||
|
||||
docs lint:
|
||||
docs-lint markdown:
|
||||
extends:
|
||||
- .default-retry
|
||||
- .docs:rules:docs-lint
|
||||
|
@ -47,6 +47,15 @@ docs lint:
|
|||
needs: []
|
||||
script:
|
||||
- scripts/lint-doc.sh
|
||||
|
||||
docs-lint links:
|
||||
extends:
|
||||
- .default-retry
|
||||
- .docs:rules:docs-lint
|
||||
image: "registry.gitlab.com/gitlab-org/gitlab-docs/lint:ruby-2.7.2-alpine-3.12-vale-2.4.3-markdownlint-0.24.0"
|
||||
stage: test
|
||||
needs: []
|
||||
script:
|
||||
# Prepare docs for build
|
||||
# The path must be 'ee/' because we have hardcoded links relying on it
|
||||
# https://gitlab.com/gitlab-org/gitlab-docs/-/blob/887850752fc0e72856da6632db132f005ba77f16/content/index.erb#L44-63
|
||||
|
|
|
@ -1 +1 @@
|
|||
0926b9f8ff2215874eef0e22f750a74da5fe4321
|
||||
194a9f58926793ade53152de90b474d66804e21e
|
||||
|
|
|
@ -1,29 +1,35 @@
|
|||
<script>
|
||||
import { GlTooltipDirective } from '@gitlab/ui';
|
||||
import GlModal from '~/vue_shared/components/gl_modal.vue';
|
||||
import { GlTooltipDirective, GlModal } from '@gitlab/ui';
|
||||
import { s__, sprintf } from '~/locale';
|
||||
import eventHub from '../event_hub';
|
||||
|
||||
export default {
|
||||
id: 'delete-environment-modal',
|
||||
name: 'DeleteEnvironmentModal',
|
||||
|
||||
components: {
|
||||
GlModal,
|
||||
},
|
||||
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
|
||||
props: {
|
||||
environment: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
primaryProps() {
|
||||
return {
|
||||
text: s__('Environments|Delete environment'),
|
||||
attributes: [{ variant: 'danger' }],
|
||||
};
|
||||
},
|
||||
cancelProps() {
|
||||
return {
|
||||
text: s__('Cancel'),
|
||||
};
|
||||
},
|
||||
confirmDeleteMessage() {
|
||||
return sprintf(
|
||||
s__(
|
||||
|
@ -35,8 +41,12 @@ export default {
|
|||
false,
|
||||
);
|
||||
},
|
||||
modalTitle() {
|
||||
return sprintf(s__(`Environments|Delete '%{environmentName}'?`), {
|
||||
environmentName: this.environment.name,
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
onSubmit() {
|
||||
eventHub.$emit('deleteEnvironment', this.environment);
|
||||
|
@ -47,20 +57,12 @@ export default {
|
|||
|
||||
<template>
|
||||
<gl-modal
|
||||
:id="$options.id"
|
||||
:footer-primary-button-text="s__('Environments|Delete environment')"
|
||||
footer-primary-button-variant="danger"
|
||||
@submit="onSubmit"
|
||||
:modal-id="$options.id"
|
||||
:action-primary="primaryProps"
|
||||
:action-cancel="cancelProps"
|
||||
:title="modalTitle"
|
||||
@primary="onSubmit"
|
||||
>
|
||||
<template #header>
|
||||
<h4 class="modal-title d-flex mw-100">
|
||||
{{ __('Delete') }}
|
||||
<span v-gl-tooltip :title="environment.name" class="text-truncate mx-1 flex-fill">
|
||||
{{ environment.name }}?
|
||||
</span>
|
||||
</h4>
|
||||
</template>
|
||||
|
||||
<p>{{ confirmDeleteMessage }}</p>
|
||||
</gl-modal>
|
||||
</template>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* Used in the environments table.
|
||||
*/
|
||||
|
||||
import { GlTooltipDirective, GlButton } from '@gitlab/ui';
|
||||
import { GlTooltipDirective, GlButton, GlModalDirective } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
import eventHub from '../event_hub';
|
||||
|
||||
|
@ -14,6 +14,7 @@ export default {
|
|||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
GlModalDirective,
|
||||
},
|
||||
props: {
|
||||
environment: {
|
||||
|
@ -54,6 +55,7 @@ export default {
|
|||
<template>
|
||||
<gl-button
|
||||
v-gl-tooltip="{ id: $options.deleteEnvironmentTooltipId }"
|
||||
v-gl-modal-directive="'delete-environment-modal'"
|
||||
:loading="isLoading"
|
||||
:title="title"
|
||||
:aria-label="title"
|
||||
|
@ -61,8 +63,6 @@ export default {
|
|||
variant="danger"
|
||||
category="primary"
|
||||
icon="remove"
|
||||
data-toggle="modal"
|
||||
data-target="#delete-environment-modal"
|
||||
@click="onClick"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -3,7 +3,7 @@ import { escape } from 'lodash';
|
|||
import { __, sprintf } from './locale';
|
||||
import axios from './lib/utils/axios_utils';
|
||||
import { deprecatedCreateFlash as flash } from './flash';
|
||||
import { parseBoolean } from './lib/utils/common_utils';
|
||||
import { parseBoolean, spriteIcon } from './lib/utils/common_utils';
|
||||
|
||||
class ImporterStatus {
|
||||
constructor({ jobsUrl, importUrl, ciCdOnly }) {
|
||||
|
@ -108,7 +108,7 @@ class ImporterStatus {
|
|||
switch (job.import_status) {
|
||||
case 'finished':
|
||||
jobItem.removeClass('table-active').addClass('table-success');
|
||||
statusField.html(`<span><i class="fa fa-check"></i> ${__('Done')}</span>`);
|
||||
statusField.html(`<span>${spriteIcon('check', 's16')} ${__('Done')}</span>`);
|
||||
break;
|
||||
case 'scheduled':
|
||||
statusField.html(`${spinner} ${__('Scheduled')}`);
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
const ComponentPerformancePlugin = {
|
||||
install(Vue, options) {
|
||||
Vue.mixin({
|
||||
beforeCreate() {
|
||||
/** Make sure the component you want to measure has `name` option defined
|
||||
* and it matches the one you pass as the plugin option. Example:
|
||||
*
|
||||
* my_component.vue:
|
||||
*
|
||||
* ```
|
||||
* export default {
|
||||
* name: 'MyComponent'
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* index.js (where you initialize your Vue app containing <my-component>):
|
||||
*
|
||||
* ```
|
||||
* Vue.use(PerformancePlugin, {
|
||||
* components: [
|
||||
* 'MyComponent',
|
||||
* ]
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
const componentName = this.$options.name;
|
||||
if (options?.components?.indexOf(componentName) !== -1) {
|
||||
const tagName = `<${componentName}>`;
|
||||
if (!performance.getEntriesByName(`${tagName}-start`).length) {
|
||||
performance.mark(`${tagName}-start`);
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const componentName = this.$options.name;
|
||||
if (options?.components?.indexOf(componentName) !== -1) {
|
||||
this.$nextTick(() => {
|
||||
window.requestAnimationFrame(() => {
|
||||
const tagName = `<${componentName}>`;
|
||||
if (!performance.getEntriesByName(`${tagName}-end`).length) {
|
||||
performance.mark(`${tagName}-end`);
|
||||
performance.measure(`${tagName}`, `${tagName}-start`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default ComponentPerformancePlugin;
|
|
@ -179,7 +179,7 @@ export default {
|
|||
<span class="input-group-prepend">
|
||||
<button
|
||||
ref="toggleEmojiMenuButton"
|
||||
v-gl-tooltip.bottom
|
||||
v-gl-tooltip.bottom.hover
|
||||
:title="s__('SetStatusModal|Add status emoji')"
|
||||
:aria-label="s__('SetStatusModal|Add status emoji')"
|
||||
name="button"
|
||||
|
|
|
@ -92,10 +92,6 @@
|
|||
content: '\f0d7';
|
||||
}
|
||||
|
||||
.fa-check::before {
|
||||
content: '\f00c';
|
||||
}
|
||||
|
||||
.fa-warning::before,
|
||||
.fa-exclamation-triangle::before {
|
||||
content: '\f071';
|
||||
|
|
|
@ -12,6 +12,8 @@ module Packages
|
|||
end
|
||||
|
||||
def execute
|
||||
return Packages::Package.none unless project
|
||||
|
||||
packages
|
||||
end
|
||||
|
||||
|
|
|
@ -118,6 +118,12 @@ module Types
|
|||
field :severity, Types::IssuableSeverityEnum, null: true,
|
||||
description: 'Severity level of the incident'
|
||||
|
||||
field :moved, GraphQL::BOOLEAN_TYPE, method: :moved?, null: true,
|
||||
description: 'Indicates if issue got moved from other project'
|
||||
|
||||
field :moved_to, Types::IssueType, null: true,
|
||||
description: 'Updated Issue after it got moved to another project'
|
||||
|
||||
def user_notes_count
|
||||
BatchLoader::GraphQL.for(object.id).batch(key: :issue_user_notes_count) do |ids, loader, args|
|
||||
counts = Note.count_for_collection(ids, 'Issue').index_by(&:noteable_id)
|
||||
|
@ -150,6 +156,10 @@ module Types
|
|||
Gitlab::Graphql::Loaders::BatchModelLoader.new(Milestone, object.milestone_id).find
|
||||
end
|
||||
|
||||
def moved_to
|
||||
Gitlab::Graphql::Loaders::BatchModelLoader.new(Issue, object.moved_to_id).find
|
||||
end
|
||||
|
||||
def discussion_locked
|
||||
!!object.discussion_locked
|
||||
end
|
||||
|
|
|
@ -311,6 +311,8 @@ class MergeRequest < ApplicationRecord
|
|||
includes(:metrics)
|
||||
end
|
||||
|
||||
scope :with_jira_issue_keys, -> { where('title ~ :regex OR merge_requests.description ~ :regex', regex: Gitlab::Regex.jira_issue_key_regex.source) }
|
||||
|
||||
after_save :keep_around_commit, unless: :importing?
|
||||
|
||||
alias_attribute :project, :target_project
|
||||
|
|
|
@ -602,7 +602,7 @@ class Project < ApplicationRecord
|
|||
# Returns a collection of projects that is either public or visible to the
|
||||
# logged in user.
|
||||
def self.public_or_visible_to_user(user = nil, min_access_level = nil)
|
||||
min_access_level = nil if user&.admin?
|
||||
min_access_level = nil if user&.can_read_all_resources?
|
||||
|
||||
return public_to_user unless user
|
||||
|
||||
|
@ -628,7 +628,7 @@ class Project < ApplicationRecord
|
|||
def self.with_feature_available_for_user(feature, user)
|
||||
visible = [ProjectFeature::ENABLED, ProjectFeature::PUBLIC]
|
||||
|
||||
if user&.admin?
|
||||
if user&.can_read_all_resources?
|
||||
with_feature_enabled(feature)
|
||||
elsif user
|
||||
min_access_level = ProjectFeature.required_minimum_access_level(feature)
|
||||
|
|
|
@ -72,6 +72,10 @@ module PolicyActor
|
|||
def try_obtain_ldap_lease
|
||||
nil
|
||||
end
|
||||
|
||||
def can_read_all_resources?
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
PolicyActor.prepend_if_ee('EE::PolicyActor')
|
||||
|
|
|
@ -3,4 +3,5 @@
|
|||
class MoveToProjectEntity < Grape::Entity
|
||||
expose :id
|
||||
expose :name_with_namespace
|
||||
expose :full_path
|
||||
end
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
module JiraConnectSubscriptions
|
||||
class CreateService < ::JiraConnectSubscriptions::BaseService
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
MERGE_REQUEST_SYNC_BATCH_SIZE = 20
|
||||
MERGE_REQUEST_SYNC_BATCH_delay = 1.minute.freeze
|
||||
|
||||
def execute
|
||||
unless namespace && can?(current_user, :create_jira_connect_subscription, namespace)
|
||||
|
@ -18,6 +20,8 @@ module JiraConnectSubscriptions
|
|||
subscription = JiraConnectSubscription.new(installation: jira_connect_installation, namespace: namespace)
|
||||
|
||||
if subscription.save
|
||||
schedule_sync_project_jobs
|
||||
|
||||
success
|
||||
else
|
||||
error(subscription.errors.full_messages.join(', '), 422)
|
||||
|
@ -29,5 +33,18 @@ module JiraConnectSubscriptions
|
|||
Namespace.find_by_full_path(params[:namespace_path])
|
||||
end
|
||||
end
|
||||
|
||||
def schedule_sync_project_jobs
|
||||
return unless Feature.enabled?(:jira_connect_full_namespace_sync)
|
||||
|
||||
namespace.all_projects.each_batch(of: MERGE_REQUEST_SYNC_BATCH_SIZE) do |projects, index|
|
||||
JiraConnect::SyncProjectWorker.bulk_perform_in_with_contexts(
|
||||
index * MERGE_REQUEST_SYNC_BATCH_delay,
|
||||
projects,
|
||||
arguments_proc: -> (project) { [project.id, Atlassian::JiraConnect::Client.generate_update_sequence_id] },
|
||||
context_proc: -> (project) { { project: project } }
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
- return unless can?(current_user, :remove_project, project)
|
||||
- confirm_phrase = s_('DeleteProject|Delete %{name}') % { name: project.full_name }
|
||||
|
||||
.sub-section
|
||||
%h4.danger-title= _('Delete project')
|
||||
|
@ -7,4 +6,4 @@
|
|||
%strong= _('Deleting the project will delete its repository and all related resources including issues, merge requests etc.')
|
||||
%p
|
||||
%strong= _('Deleted projects cannot be restored!')
|
||||
#js-project-delete-button{ data: { form_path: project_path(project), confirm_phrase: confirm_phrase } }
|
||||
#js-project-delete-button{ data: { form_path: project_path(project), confirm_phrase: project.path } }
|
||||
|
|
|
@ -827,6 +827,14 @@
|
|||
:weight: 1
|
||||
:idempotent:
|
||||
:tags: []
|
||||
- :name: jira_connect:jira_connect_sync_project
|
||||
:feature_category: :integrations
|
||||
:has_external_dependencies: true
|
||||
:urgency: :low
|
||||
:resource_boundary: :unknown
|
||||
:weight: 1
|
||||
:idempotent: true
|
||||
:tags: []
|
||||
- :name: jira_importer:jira_import_advance_stage
|
||||
:feature_category: :importers
|
||||
:has_external_dependencies:
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module JiraConnect
|
||||
class SyncProjectWorker
|
||||
include ApplicationWorker
|
||||
|
||||
queue_namespace :jira_connect
|
||||
feature_category :integrations
|
||||
idempotent!
|
||||
worker_has_external_dependencies!
|
||||
|
||||
MERGE_REQUEST_LIMIT = 400
|
||||
|
||||
def perform(project_id, update_sequence_id)
|
||||
project = Project.find_by_id(project_id)
|
||||
|
||||
return if project.nil?
|
||||
|
||||
JiraConnect::SyncService.new(project).execute(merge_requests: merge_requests_to_sync(project), update_sequence_id: update_sequence_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def merge_requests_to_sync(project)
|
||||
project.merge_requests.with_jira_issue_keys.preload(:author).limit(MERGE_REQUEST_LIMIT).order(id: :desc)
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: NPM project level API
|
||||
merge_request: 46867
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Expose moved and movedTo attributes in Issues query
|
||||
merge_request: 46447
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Make delete repo prompts consistent
|
||||
merge_request: 47117
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Migrate services specs to consider admin mode
|
||||
merge_request: 45988
|
||||
author: Diego Louzán
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix status emoji tooltip trigger
|
||||
merge_request: 47378
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Jira Connect automatically synchronizes up to 400 existing merge requests per project when a namespace is connected.
|
||||
merge_request: 43880
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Replace fa-check icon in importer status
|
||||
merge_request: 47373
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
name: jira_connect_full_namespace_sync
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43880
|
||||
rollout_issue_url:
|
||||
type: development
|
||||
group: group::ecosystem
|
||||
default_enabled: false
|
|
@ -0,0 +1,44 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddMergeRequestJiraReferenceIndexes < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
DESCRIPTION_INDEX_NAME = 'index_merge_requests_on_target_project_id_iid_jira_description'
|
||||
TITLE_INDEX_NAME = 'index_merge_requests_on_target_project_id_and_iid_jira_title'
|
||||
|
||||
JIRA_KEY_REGEX = '[A-Z][A-Z_0-9]+-\d+'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_index(
|
||||
:merge_requests,
|
||||
[:target_project_id, :iid],
|
||||
name: TITLE_INDEX_NAME,
|
||||
using: :btree,
|
||||
where: "(merge_requests.title)::text ~ '#{JIRA_KEY_REGEX}'::text"
|
||||
)
|
||||
|
||||
add_concurrent_index(
|
||||
:merge_requests,
|
||||
[:target_project_id, :iid],
|
||||
name: DESCRIPTION_INDEX_NAME,
|
||||
using: :btree,
|
||||
where: "(merge_requests.description)::text ~ '#{JIRA_KEY_REGEX}'::text"
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name(
|
||||
:merge_requests,
|
||||
TITLE_INDEX_NAME
|
||||
)
|
||||
|
||||
remove_concurrent_index_by_name(
|
||||
:merge_requests,
|
||||
DESCRIPTION_INDEX_NAME
|
||||
)
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
a2dc0d31af6834adf6634f6051d7d451fc48d31492d96efe57547c3e9d61a64d
|
|
@ -21150,8 +21150,12 @@ CREATE UNIQUE INDEX index_merge_requests_on_target_project_id_and_iid ON merge_r
|
|||
|
||||
CREATE INDEX index_merge_requests_on_target_project_id_and_iid_and_state_id ON merge_requests USING btree (target_project_id, iid, state_id);
|
||||
|
||||
CREATE INDEX index_merge_requests_on_target_project_id_and_iid_jira_title ON merge_requests USING btree (target_project_id, iid) WHERE ((title)::text ~ '[A-Z][A-Z_0-9]+-\d+'::text);
|
||||
|
||||
CREATE INDEX index_merge_requests_on_target_project_id_and_target_branch ON merge_requests USING btree (target_project_id, target_branch) WHERE ((state_id = 1) AND (merge_when_pipeline_succeeds = true));
|
||||
|
||||
CREATE INDEX index_merge_requests_on_target_project_id_iid_jira_description ON merge_requests USING btree (target_project_id, iid) WHERE (description ~ '[A-Z][A-Z_0-9]+-\d+'::text);
|
||||
|
||||
CREATE INDEX index_merge_requests_on_title ON merge_requests USING btree (title);
|
||||
|
||||
CREATE INDEX index_merge_requests_on_title_trigram ON merge_requests USING gin (title gin_trgm_ops);
|
||||
|
|
|
@ -260,9 +260,9 @@ The following documentation relates to the DevOps **Verify** stage:
|
|||
|
||||
### Package
|
||||
|
||||
GitLab Packages allows organizations to utilize GitLab as a private repository
|
||||
GitLab Packages allows organizations to use GitLab as a private repository
|
||||
for a variety of common package managers. Users are able to build and publish
|
||||
packages, which can be easily consumed as a dependency in downstream projects.
|
||||
packages, which can be consumed as a dependency in downstream projects.
|
||||
|
||||
The following documentation relates to the DevOps **Package** stage:
|
||||
|
||||
|
|
|
@ -7645,6 +7645,16 @@ type EpicIssue implements CurrentUserTodos & Noteable {
|
|||
"""
|
||||
milestone: Milestone
|
||||
|
||||
"""
|
||||
Indicates if issue got moved from other project
|
||||
"""
|
||||
moved: Boolean
|
||||
|
||||
"""
|
||||
Updated Issue after it got moved to another project
|
||||
"""
|
||||
movedTo: Issue
|
||||
|
||||
"""
|
||||
All notes on this noteable
|
||||
"""
|
||||
|
@ -10169,6 +10179,16 @@ type Issue implements CurrentUserTodos & Noteable {
|
|||
"""
|
||||
milestone: Milestone
|
||||
|
||||
"""
|
||||
Indicates if issue got moved from other project
|
||||
"""
|
||||
moved: Boolean
|
||||
|
||||
"""
|
||||
Updated Issue after it got moved to another project
|
||||
"""
|
||||
movedTo: Issue
|
||||
|
||||
"""
|
||||
All notes on this noteable
|
||||
"""
|
||||
|
|
|
@ -21168,6 +21168,34 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "moved",
|
||||
"description": "Indicates if issue got moved from other project",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "movedTo",
|
||||
"description": "Updated Issue after it got moved to another project",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Issue",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "notes",
|
||||
"description": "All notes on this noteable",
|
||||
|
@ -27821,6 +27849,34 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "moved",
|
||||
"description": "Indicates if issue got moved from other project",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Boolean",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "movedTo",
|
||||
"description": "Updated Issue after it got moved to another project",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Issue",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "notes",
|
||||
"description": "All notes on this noteable",
|
||||
|
|
|
@ -1268,6 +1268,8 @@ Relationship between an epic and an issue.
|
|||
| `iteration` | Iteration | Iteration of the issue |
|
||||
| `labels` | LabelConnection | Labels of the issue |
|
||||
| `milestone` | Milestone | Milestone of the issue |
|
||||
| `moved` | Boolean | Indicates if issue got moved from other project |
|
||||
| `movedTo` | Issue | Updated Issue after it got moved to another project |
|
||||
| `notes` | NoteConnection! | All notes on this noteable |
|
||||
| `participants` | UserConnection | List of participants in the issue |
|
||||
| `reference` | String! | Internal reference of the issue. Returned in shortened format by default |
|
||||
|
@ -1534,6 +1536,8 @@ Represents a recorded measurement (object count) for the Admins.
|
|||
| `iteration` | Iteration | Iteration of the issue |
|
||||
| `labels` | LabelConnection | Labels of the issue |
|
||||
| `milestone` | Milestone | Milestone of the issue |
|
||||
| `moved` | Boolean | Indicates if issue got moved from other project |
|
||||
| `movedTo` | Issue | Updated Issue after it got moved to another project |
|
||||
| `notes` | NoteConnection! | All notes on this noteable |
|
||||
| `participants` | UserConnection | List of participants in the issue |
|
||||
| `reference` | String! | Internal reference of the issue. Returned in shortened format by default |
|
||||
|
|
|
@ -289,8 +289,7 @@ You can find the schemas for these scanners here:
|
|||
|
||||
### Version
|
||||
|
||||
This field specifies the version of the report schema you are using. Please reference individual scanner
|
||||
pages for the specific versions to use.
|
||||
This field specifies the version of the [Security Report Schemas](https://gitlab.com/gitlab-org/security-products/security-report-schemas) you are using. Please refer to the [releases](https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/releases) of the schemas for the specific versions to use.
|
||||
|
||||
### Vulnerabilities
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 123 KiB |
|
@ -32,7 +32,7 @@ Dependencies are displayed with the following information:
|
|||
| --------- | ----------- |
|
||||
| Component | The dependency's name and version |
|
||||
| Packager | The packager used to install the dependency |
|
||||
| Location | A link to the packager-specific lock file in your project that declared the dependency |
|
||||
| Location | A link to the packager-specific lock file in your project that declared the dependency. It also shows the [dependency path](#dependency-paths) to a top-level dependency, if any, and if supported. |
|
||||
| License | Links to dependency's software licenses |
|
||||
|
||||
Dependencies shown are initially sorted by the severity of their known vulnerabilities, if any. They
|
||||
|
@ -44,6 +44,18 @@ If a dependency has known vulnerabilities, you can view them by clicking the arr
|
|||
dependency's name or the badge that indicates how many known vulnerabilities exist. For each
|
||||
vulnerability, its severity and description then appears below it.
|
||||
|
||||
### Dependency Paths
|
||||
|
||||
The dependency list shows the path between a dependency and a top-level dependency it's connected
|
||||
to, if any. There are many possible paths connecting a transient dependency to top-level
|
||||
dependencies, but the UI only shows one of the shortest paths.
|
||||
|
||||
![Dependency Path](img/yarn_dependency_path_v13_6.png)
|
||||
|
||||
Dependency Paths are supported for the following package managers:
|
||||
|
||||
- [NuGet](https://www.nuget.org/)
|
||||
|
||||
## Licenses
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10536) in GitLab Ultimate 12.3.
|
||||
|
|
|
@ -62,7 +62,6 @@ The following languages and package managers are supported.
|
|||
| .NET | [Nuget](https://www.nuget.org/) | The .NET Framework is supported via the [mono project](https://www.mono-project.com/). There are, however, some limitations. The scanner doesn't support Windows-specific dependencies and doesn't report dependencies of your project's listed dependencies. Also, the scanner always marks detected licenses for all dependencies as `unknown`. | [License Finder](https://github.com/pivotal/LicenseFinder) |
|
||||
| Python | [pip](https://pip.pypa.io/en/stable/) | Python is supported through [requirements.txt](https://pip.pypa.io/en/stable/user_guide/#requirements-files) and [Pipfile.lock](https://github.com/pypa/pipfile#pipfilelock). | [License Finder](https://github.com/pivotal/LicenseFinder) |
|
||||
| Ruby | [gem](https://rubygems.org/) | | [License Finder](https://github.com/pivotal/LicenseFinder)|
|
||||
| Objective-C, Swift | [Carthage](https://github.com/Carthage/Carthage) | | [License Finder](https://github.com/pivotal/LicenseFinder) |
|
||||
|
||||
NOTE: **Note:**
|
||||
Java 8 and Gradle 1.x projects are not supported.
|
||||
|
@ -78,6 +77,7 @@ which means that the reported licenses might be incomplete or inaccurate.
|
|||
| JavaScript | [Yarn](https://yarnpkg.com/)|[License Finder](https://github.com/pivotal/LicenseFinder)|
|
||||
| Go | go get, gvt, glide, dep, trash, govendor |[License Finder](https://github.com/pivotal/LicenseFinder)|
|
||||
| Erlang | [Rebar](https://www.rebar3.org/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
|
||||
| Objective-C, Swift | [Carthage](https://github.com/Carthage/Carthage) | | [License Finder](https://github.com/pivotal/LicenseFinder) |
|
||||
| Objective-C, Swift | [CocoaPods](https://cocoapods.org/) v0.39 and below |[License Finder](https://github.com/pivotal/LicenseFinder)|
|
||||
| Elixir | [Mix](https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html) |[License Finder](https://github.com/pivotal/LicenseFinder)|
|
||||
| C++/C | [Conan](https://conan.io/) |[License Finder](https://github.com/pivotal/LicenseFinder)|
|
||||
|
@ -144,7 +144,7 @@ License Compliance can be configured using environment variables.
|
|||
| `ASDF_PYTHON_VERSION` | no | Version of Python to use for the scan. |
|
||||
| `ASDF_RUBY_VERSION` | no | Version of Ruby to use for the scan. |
|
||||
| `GRADLE_CLI_OPTS` | no | Additional arguments for the gradle executable. If not supplied, defaults to `--exclude-task=test`. |
|
||||
| `LICENSE_FINDER_CLI_OPTS` | no | Additional arguments for the `license_finder` executable. For example, if your project has both Golang and Ruby code stored in different directories and you want to only scan the Ruby code, you can update your `.gitlab-ci-yml` template to specify which project directories to scan, like `LICENSE_FINDER_CLI_OPTS: '--debug --aggregate-paths=. ruby'`. |
|
||||
| `LICENSE_FINDER_CLI_OPTS` | no | Additional arguments for the `license_finder` executable. For example, if you have multiple projects in nested directories, you can update your `.gitlab-ci-yml` template to specify a recursive scan, like `LICENSE_FINDER_CLI_OPTS: '--recursive'`. |
|
||||
| `LM_JAVA_VERSION` | no | Version of Java. If set to `11`, Maven and Gradle use Java 11 instead of Java 8. |
|
||||
| `LM_PYTHON_VERSION` | no | Version of Python. If set to `3`, dependencies are installed using Python 3 instead of Python 2.7. |
|
||||
| `MAVEN_CLI_OPTS` | no | Additional arguments for the mvn executable. If not supplied, defaults to `-DskipTests`. |
|
||||
|
|
|
@ -79,7 +79,18 @@ To create a project:
|
|||
the [naming convention](#package-naming-convention) and is scoped to the
|
||||
project or group where the registry exists.
|
||||
|
||||
A `package.json` file is created.
|
||||
A `package.json` file is created.
|
||||
|
||||
## Use the GitLab endpoint for NPM packages
|
||||
|
||||
To use the GitLab endpoint for NPM packages, choose an option:
|
||||
|
||||
- **Project-level**: Use when you have few NPM packages and they are not in
|
||||
the same GitLab group.
|
||||
- **Instance-level**: Use when you have many NPM packages in different
|
||||
GitLab groups or in their own namespace. Be sure to comply with the [package naming convention](#package-naming-convention).
|
||||
|
||||
Some features such as [publishing](#publish-an-npm-package) a package is only available on the project-level endpoint.
|
||||
|
||||
## Authenticate to the Package Registry
|
||||
|
||||
|
@ -94,20 +105,19 @@ To authenticate to the Package Registry, you must use one of the following:
|
|||
|
||||
### Authenticate with a personal access token or deploy token
|
||||
|
||||
To authenticate with a [personal access token](../../profile/personal_access_tokens.md) or [deploy token](../../project/deploy_tokens/index.md),
|
||||
set your NPM configuration:
|
||||
To authenticate with the Package Registry, you will need a [personal access token](../../profile/personal_access_tokens.md) or [deploy token](../../project/deploy_tokens/index.md).
|
||||
|
||||
#### Project-level NPM endpoint
|
||||
|
||||
To use the [project-level](#use-the-gitlab-endpoint-for-npm-packages) NPM endpoint, set your NPM configuration:
|
||||
|
||||
```shell
|
||||
# Set URL for your scoped packages
|
||||
# For example, a package named `@foo/bar` uses this URL for download
|
||||
npm config set @foo:registry https://gitlab.example.com/api/v4/packages/npm/
|
||||
# Set URL for your scoped packages.
|
||||
# For example package with name `@foo/bar` will use this URL for download
|
||||
npm config set @foo:registry https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/
|
||||
|
||||
# Add the token for the scoped packages URL
|
||||
# Use this to download `@foo/` packages from private projects
|
||||
npm config set '//gitlab.example.com/api/v4/packages/npm/:_authToken' "<your_token>"
|
||||
|
||||
# Add token for to publish to the package registry
|
||||
# Replace <your_project_id> with the project you want to publish your package to
|
||||
# Add the token for the scoped packages URL. Replace <your_project_id>
|
||||
# with the project where your package is located.
|
||||
npm config set '//gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken' "<your_token>"
|
||||
```
|
||||
|
||||
|
@ -120,6 +130,28 @@ You should now be able to publish and install NPM packages in your project.
|
|||
If you encounter an error with [Yarn](https://classic.yarnpkg.com/en/), view
|
||||
[troubleshooting steps](#troubleshooting).
|
||||
|
||||
#### Instance-level NPM endpoint
|
||||
|
||||
To use the [instance-level](#use-the-gitlab-endpoint-for-npm-packages) NPM endpoint, set your NPM configuration:
|
||||
|
||||
```shell
|
||||
# Set URL for your scoped packages.
|
||||
# For example package with name `@foo/bar` will use this URL for download
|
||||
npm config set @foo:registry https://gitlab.example.com/api/v4/packages/npm/
|
||||
|
||||
# Add the token for the scoped packages URL. This will allow you to download
|
||||
# `@foo/` packages from private projects.
|
||||
npm config set '//gitlab.example.com/api/v4/packages/npm/:_authToken' "<your_token>"
|
||||
```
|
||||
|
||||
- `<your_token>` is your personal access token or deploy token.
|
||||
- Replace `gitlab.example.com` with your domain name.
|
||||
|
||||
You should now be able to publish and install NPM packages in your project.
|
||||
|
||||
If you encounter an error with [Yarn](https://classic.yarnpkg.com/en/), view
|
||||
[troubleshooting steps](#troubleshooting).
|
||||
|
||||
### Authenticate with a CI job token
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9104) in GitLab Premium 12.5.
|
||||
|
@ -128,12 +160,22 @@ If you encounter an error with [Yarn](https://classic.yarnpkg.com/en/), view
|
|||
If you're using NPM with GitLab CI/CD, a CI job token can be used instead of a personal access token or deploy token.
|
||||
The token inherits the permissions of the user that generates the pipeline.
|
||||
|
||||
Add a corresponding section to your `.npmrc` file:
|
||||
#### Project-level NPM endpoint
|
||||
|
||||
To use the [project-level](#use-the-gitlab-endpoint-for-npm-packages) NPM endpoint, add a corresponding section to your `.npmrc` file:
|
||||
|
||||
```ini
|
||||
@foo:registry=https://gitlab.example.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/
|
||||
//gitlab.example.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}
|
||||
```
|
||||
|
||||
#### Instance-level NPM endpoint
|
||||
|
||||
To use the [instance-level](#use-the-gitlab-endpoint-for-npm-packages) NPM endpoint, add a corresponding section to your `.npmrc` file:
|
||||
|
||||
```ini
|
||||
@foo:registry=https://gitlab.example.com/api/v4/packages/npm/
|
||||
//gitlab.example.com/api/v4/packages/npm/:_authToken=${CI_JOB_TOKEN}
|
||||
//gitlab.example.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}
|
||||
```
|
||||
|
||||
#### Use variables to avoid hard-coding auth token values
|
||||
|
@ -184,37 +226,22 @@ In GitLab, this regex validates all package names from all package managers:
|
|||
This regex allows almost all of the characters that NPM allows, with a few exceptions (for example, `~` is not allowed).
|
||||
|
||||
The regex also allows for capital letters, while NPM does not. Capital letters are needed because the scope must be
|
||||
identical to the root namespace of the project.
|
||||
identical to the root namespace of the project.
|
||||
|
||||
CAUTION: **Caution:**
|
||||
When you update the path of a user or group, or transfer a subgroup or project,
|
||||
you must remove any NPM packages first. You cannot update the root namespace
|
||||
you must remove any NPM packages first. You cannot update the root namespace
|
||||
of a project with NPM packages. Make sure you update your `.npmrc` files to follow
|
||||
the naming convention and run `npm publish` if necessary.
|
||||
|
||||
## Publish an NPM package
|
||||
|
||||
Before you can publish a package, you must specify the registry
|
||||
for NPM. To do this, add the following section to the bottom of `package.json`:
|
||||
Prerequisites:
|
||||
|
||||
```json
|
||||
"publishConfig": {
|
||||
"@foo:registry":"https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/"
|
||||
}
|
||||
```
|
||||
- [Authenticate](#authenticate-to-the-package-registry) to the Package Registry.
|
||||
- Set a [project-level NPM endpoint](#use-the-gitlab-endpoint-for-npm-packages).
|
||||
|
||||
- `<your_project_id>` is your project ID, found on the project's home page.
|
||||
- `@foo` is your scope.
|
||||
- Replace `gitlab.example.com` with your domain name.
|
||||
|
||||
DANGER: **Warning:**
|
||||
The `publishConfig` entry in the `package.json` file is not respected, because of a
|
||||
[bug in NPM](https://github.com/npm/cli/issues/1994) version `7.x` and later. You must
|
||||
use an earlier version of NPM, or temporarily set your `.npmrc` scope to
|
||||
`@foo:registry=https://gitlab.example.com/api/v4/projects/<project_id>/packages/npm`.
|
||||
|
||||
After you have set up [authentication](#authenticate-to-the-package-registry),
|
||||
you can upload an NPM package to your project:
|
||||
To upload an NPM package to your project, run this command:
|
||||
|
||||
```shell
|
||||
npm publish
|
||||
|
@ -227,6 +254,11 @@ a given scope, you get a `403 Forbidden!` error.
|
|||
|
||||
## Publish an NPM package by using CI/CD
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- [Authenticate](#authenticate-to-the-package-registry) to the Package Registry.
|
||||
- Set a [project-level NPM endpoint](#use-the-gitlab-endpoint-for-npm-packages).
|
||||
|
||||
To work with NPM commands within [GitLab CI/CD](../../../ci/README.md), you can use
|
||||
`CI_JOB_TOKEN` in place of the personal access token or deploy token in your commands.
|
||||
|
||||
|
@ -267,7 +299,7 @@ in a JavaScript project.
|
|||
Replace `@foo` with your scope.
|
||||
|
||||
1. Ensure [authentication](#authenticate-to-the-package-registry) is configured.
|
||||
|
||||
|
||||
1. In your project, to install a package, run:
|
||||
|
||||
```shell
|
||||
|
@ -390,9 +422,6 @@ should look like:
|
|||
"name": "@foo/my-package",
|
||||
"version": "1.0.0",
|
||||
"description": "Example package for GitLab NPM registry",
|
||||
"publishConfig": {
|
||||
"@foo:registry":"https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -218,7 +218,8 @@ module API
|
|||
mount ::API::DebianGroupPackages
|
||||
mount ::API::DebianProjectPackages
|
||||
mount ::API::MavenPackages
|
||||
mount ::API::NpmPackages
|
||||
mount ::API::NpmProjectPackages
|
||||
mount ::API::NpmInstancePackages
|
||||
mount ::API::GenericPackages
|
||||
mount ::API::GoProxy
|
||||
mount ::API::Pages
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# NPM Package Manager Client API
|
||||
#
|
||||
# These API endpoints are not consumed directly by users, so there is no documentation for the
|
||||
# individual endpoints. They are called by the NPM package manager client when users run commands
|
||||
# like `npm install` or `npm publish`. The usage of the GitLab NPM registry is documented here:
|
||||
# https://docs.gitlab.com/ee/user/packages/npm_registry/
|
||||
#
|
||||
# Technical debt: https://gitlab.com/gitlab-org/gitlab/issues/35798
|
||||
#
|
||||
# Caution: This Concern has to be included at the end of the API class
|
||||
# The last route of this Concern has a globbing wildcard that will match all urls.
|
||||
# As such, routes declared after the last route of this Concern will not match any url.
|
||||
module API
|
||||
module Concerns
|
||||
module Packages
|
||||
module NpmEndpoints
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
helpers ::API::Helpers::Packages::DependencyProxyHelpers
|
||||
|
||||
before do
|
||||
require_packages_enabled!
|
||||
authenticate_non_get!
|
||||
end
|
||||
|
||||
params do
|
||||
requires :package_name, type: String, desc: 'Package name'
|
||||
end
|
||||
namespace '-/package/*package_name' do
|
||||
desc 'Get all tags for a given an NPM package' do
|
||||
detail 'This feature was introduced in GitLab 12.7'
|
||||
success ::API::Entities::NpmPackageTag
|
||||
end
|
||||
get 'dist-tags', format: false, requirements: ::API::Helpers::Packages::Npm::NPM_ENDPOINT_REQUIREMENTS do
|
||||
package_name = params[:package_name]
|
||||
|
||||
bad_request!('Package Name') if package_name.blank?
|
||||
|
||||
authorize_read_package!(project)
|
||||
|
||||
packages = ::Packages::Npm::PackageFinder.new(project, package_name)
|
||||
.execute
|
||||
|
||||
not_found! if packages.empty?
|
||||
|
||||
present ::Packages::Npm::PackagePresenter.new(package_name, packages),
|
||||
with: ::API::Entities::NpmPackageTag
|
||||
end
|
||||
|
||||
params do
|
||||
requires :tag, type: String, desc: "Package dist-tag"
|
||||
end
|
||||
namespace 'dist-tags/:tag', requirements: ::API::Helpers::Packages::Npm::NPM_ENDPOINT_REQUIREMENTS do
|
||||
desc 'Create or Update the given tag for the given NPM package and version' do
|
||||
detail 'This feature was introduced in GitLab 12.7'
|
||||
end
|
||||
put format: false do
|
||||
package_name = params[:package_name]
|
||||
version = env['api.request.body']
|
||||
tag = params[:tag]
|
||||
|
||||
bad_request!('Package Name') if package_name.blank?
|
||||
bad_request!('Version') if version.blank?
|
||||
bad_request!('Tag') if tag.blank?
|
||||
|
||||
authorize_create_package!(project)
|
||||
|
||||
package = ::Packages::Npm::PackageFinder
|
||||
.new(project, package_name)
|
||||
.find_by_version(version)
|
||||
not_found!('Package') unless package
|
||||
|
||||
::Packages::Npm::CreateTagService.new(package, tag).execute
|
||||
|
||||
no_content!
|
||||
end
|
||||
|
||||
desc 'Deletes the given tag' do
|
||||
detail 'This feature was introduced in GitLab 12.7'
|
||||
end
|
||||
delete format: false do
|
||||
package_name = params[:package_name]
|
||||
tag = params[:tag]
|
||||
|
||||
bad_request!('Package Name') if package_name.blank?
|
||||
bad_request!('Tag') if tag.blank?
|
||||
|
||||
authorize_destroy_package!(project)
|
||||
|
||||
package_tag = ::Packages::TagsFinder
|
||||
.new(project, package_name, package_type: :npm)
|
||||
.find_by_name(tag)
|
||||
|
||||
not_found!('Package tag') unless package_tag
|
||||
|
||||
::Packages::RemoveTagService.new(package_tag).execute
|
||||
|
||||
no_content!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc 'NPM registry metadata endpoint' do
|
||||
detail 'This feature was introduced in GitLab 11.8'
|
||||
end
|
||||
params do
|
||||
requires :package_name, type: String, desc: 'Package name'
|
||||
end
|
||||
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
|
||||
get '*package_name', format: false, requirements: ::API::Helpers::Packages::Npm::NPM_ENDPOINT_REQUIREMENTS do
|
||||
package_name = params[:package_name]
|
||||
|
||||
packages = ::Packages::Npm::PackageFinder.new(project_or_nil, package_name)
|
||||
.execute
|
||||
|
||||
redirect_request = project_or_nil.blank? || packages.empty?
|
||||
|
||||
redirect_registry_request(redirect_request, :npm, package_name: package_name) do
|
||||
authorize_read_package!(project)
|
||||
|
||||
not_found!('Packages') if packages.empty?
|
||||
|
||||
present ::Packages::Npm::PackagePresenter.new(package_name, packages),
|
||||
with: ::API::Entities::NpmPackage
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,62 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Helpers
|
||||
module Packages
|
||||
module Npm
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
include ::API::Helpers::PackagesHelpers
|
||||
|
||||
NPM_ENDPOINT_REQUIREMENTS = {
|
||||
package_name: API::NO_SLASH_URL_PART_REGEX
|
||||
}.freeze
|
||||
|
||||
def endpoint_scope
|
||||
params[:id].present? ? :project : :instance
|
||||
end
|
||||
|
||||
def project
|
||||
strong_memoize(:project) do
|
||||
case endpoint_scope
|
||||
when :project
|
||||
user_project
|
||||
when :instance
|
||||
# Simulate the same behavior as #user_project by re-using #find_project!
|
||||
# but take care if the project_id is nil as #find_project! is not designed
|
||||
# to handle it.
|
||||
project_id = project_id_or_nil
|
||||
|
||||
not_found!('Project') unless project_id
|
||||
|
||||
find_project!(project_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def project_or_nil
|
||||
# mainly used by the metadata endpoint where we need to get a project
|
||||
# and return nil if not found (no errors should be raised)
|
||||
strong_memoize(:project_or_nil) do
|
||||
next unless project_id_or_nil
|
||||
|
||||
find_project(project_id_or_nil)
|
||||
end
|
||||
end
|
||||
|
||||
def project_id_or_nil
|
||||
strong_memoize(:project_id_or_nil) do
|
||||
case endpoint_scope
|
||||
when :project
|
||||
params[:id]
|
||||
when :instance
|
||||
::Packages::Package.npm
|
||||
.with_name(params[:package_name])
|
||||
.first
|
||||
&.project_id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
module API
|
||||
class NpmInstancePackages < ::API::Base
|
||||
helpers ::API::Helpers::Packages::Npm
|
||||
|
||||
feature_category :package_registry
|
||||
|
||||
rescue_from ActiveRecord::RecordInvalid do |e|
|
||||
render_api_error!(e.message, 400)
|
||||
end
|
||||
|
||||
namespace 'packages/npm' do
|
||||
include ::API::Concerns::Packages::NpmEndpoints
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,175 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
module API
|
||||
class NpmPackages < ::API::Base
|
||||
helpers ::API::Helpers::PackagesHelpers
|
||||
helpers ::API::Helpers::Packages::DependencyProxyHelpers
|
||||
|
||||
feature_category :package_registry
|
||||
|
||||
NPM_ENDPOINT_REQUIREMENTS = {
|
||||
package_name: API::NO_SLASH_URL_PART_REGEX
|
||||
}.freeze
|
||||
|
||||
rescue_from ActiveRecord::RecordInvalid do |e|
|
||||
render_api_error!(e.message, 400)
|
||||
end
|
||||
|
||||
before do
|
||||
require_packages_enabled!
|
||||
authenticate_non_get!
|
||||
end
|
||||
|
||||
helpers do
|
||||
def project_by_package_name
|
||||
strong_memoize(:project_by_package_name) do
|
||||
::Packages::Package.npm.with_name(params[:package_name]).first&.project
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Get all tags for a given an NPM package' do
|
||||
detail 'This feature was introduced in GitLab 12.7'
|
||||
success ::API::Entities::NpmPackageTag
|
||||
end
|
||||
params do
|
||||
requires :package_name, type: String, desc: 'Package name'
|
||||
end
|
||||
get 'packages/npm/-/package/*package_name/dist-tags', format: false, requirements: NPM_ENDPOINT_REQUIREMENTS do
|
||||
package_name = params[:package_name]
|
||||
|
||||
bad_request!('Package Name') if package_name.blank?
|
||||
|
||||
authorize_read_package!(project_by_package_name)
|
||||
|
||||
packages = ::Packages::Npm::PackageFinder.new(project_by_package_name, package_name)
|
||||
.execute
|
||||
|
||||
present ::Packages::Npm::PackagePresenter.new(package_name, packages),
|
||||
with: ::API::Entities::NpmPackageTag
|
||||
end
|
||||
|
||||
params do
|
||||
requires :package_name, type: String, desc: 'Package name'
|
||||
requires :tag, type: String, desc: "Package dist-tag"
|
||||
end
|
||||
namespace 'packages/npm/-/package/*package_name/dist-tags/:tag', requirements: NPM_ENDPOINT_REQUIREMENTS do
|
||||
desc 'Create or Update the given tag for the given NPM package and version' do
|
||||
detail 'This feature was introduced in GitLab 12.7'
|
||||
end
|
||||
put format: false do
|
||||
package_name = params[:package_name]
|
||||
version = env['api.request.body']
|
||||
tag = params[:tag]
|
||||
|
||||
bad_request!('Package Name') if package_name.blank?
|
||||
bad_request!('Version') if version.blank?
|
||||
bad_request!('Tag') if tag.blank?
|
||||
|
||||
authorize_create_package!(project_by_package_name)
|
||||
|
||||
package = ::Packages::Npm::PackageFinder
|
||||
.new(project_by_package_name, package_name)
|
||||
.find_by_version(version)
|
||||
not_found!('Package') unless package
|
||||
|
||||
::Packages::Npm::CreateTagService.new(package, tag).execute
|
||||
|
||||
no_content!
|
||||
end
|
||||
|
||||
desc 'Deletes the given tag' do
|
||||
detail 'This feature was introduced in GitLab 12.7'
|
||||
end
|
||||
delete format: false do
|
||||
package_name = params[:package_name]
|
||||
tag = params[:tag]
|
||||
|
||||
bad_request!('Package Name') if package_name.blank?
|
||||
bad_request!('Tag') if tag.blank?
|
||||
|
||||
authorize_destroy_package!(project_by_package_name)
|
||||
|
||||
package_tag = ::Packages::TagsFinder
|
||||
.new(project_by_package_name, package_name, package_type: :npm)
|
||||
.find_by_name(tag)
|
||||
|
||||
not_found!('Package tag') unless package_tag
|
||||
|
||||
::Packages::RemoveTagService.new(package_tag).execute
|
||||
|
||||
no_content!
|
||||
end
|
||||
end
|
||||
|
||||
desc 'NPM registry endpoint at instance level' do
|
||||
detail 'This feature was introduced in GitLab 11.8'
|
||||
end
|
||||
params do
|
||||
requires :package_name, type: String, desc: 'Package name'
|
||||
end
|
||||
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
|
||||
get 'packages/npm/*package_name', format: false, requirements: NPM_ENDPOINT_REQUIREMENTS do
|
||||
package_name = params[:package_name]
|
||||
|
||||
redirect_registry_request(project_by_package_name.blank?, :npm, package_name: package_name) do
|
||||
authorize_read_package!(project_by_package_name)
|
||||
|
||||
packages = ::Packages::Npm::PackageFinder
|
||||
.new(project_by_package_name, package_name).execute
|
||||
|
||||
present ::Packages::Npm::PackagePresenter.new(package_name, packages),
|
||||
with: ::API::Entities::NpmPackage
|
||||
end
|
||||
end
|
||||
|
||||
params do
|
||||
requires :id, type: String, desc: 'The ID of a project'
|
||||
end
|
||||
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
desc 'Download the NPM tarball' do
|
||||
detail 'This feature was introduced in GitLab 11.8'
|
||||
end
|
||||
params do
|
||||
requires :package_name, type: String, desc: 'Package name'
|
||||
requires :file_name, type: String, desc: 'Package file name'
|
||||
end
|
||||
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
|
||||
get ':id/packages/npm/*package_name/-/*file_name', format: false do
|
||||
authorize_read_package!(user_project)
|
||||
|
||||
package = user_project.packages.npm
|
||||
.by_name_and_file_name(params[:package_name], params[:file_name])
|
||||
|
||||
package_file = ::Packages::PackageFileFinder
|
||||
.new(package, params[:file_name]).execute!
|
||||
|
||||
track_package_event('pull_package', package)
|
||||
|
||||
present_carrierwave_file!(package_file.file)
|
||||
end
|
||||
|
||||
desc 'Create NPM package' do
|
||||
detail 'This feature was introduced in GitLab 11.8'
|
||||
end
|
||||
params do
|
||||
requires :package_name, type: String, desc: 'Package name'
|
||||
requires :versions, type: Hash, desc: 'Package version info'
|
||||
end
|
||||
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
|
||||
put ':id/packages/npm/:package_name', requirements: NPM_ENDPOINT_REQUIREMENTS do
|
||||
authorize_create_package!(user_project)
|
||||
|
||||
track_package_event('push_package', :npm)
|
||||
|
||||
created_package = ::Packages::Npm::CreatePackageService
|
||||
.new(user_project, current_user, params.merge(build: current_authenticated_job)).execute
|
||||
|
||||
if created_package[:status] == :error
|
||||
render_api_error!(created_package[:message], created_package[:http_status])
|
||||
else
|
||||
created_package
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,66 @@
|
|||
# frozen_string_literal: true
|
||||
module API
|
||||
class NpmProjectPackages < ::API::Base
|
||||
helpers ::API::Helpers::Packages::Npm
|
||||
|
||||
feature_category :package_registry
|
||||
|
||||
rescue_from ActiveRecord::RecordInvalid do |e|
|
||||
render_api_error!(e.message, 400)
|
||||
end
|
||||
|
||||
params do
|
||||
requires :id, type: String, desc: 'The ID of a project'
|
||||
end
|
||||
namespace 'projects/:id/packages/npm' do
|
||||
desc 'Download the NPM tarball' do
|
||||
detail 'This feature was introduced in GitLab 11.8'
|
||||
end
|
||||
params do
|
||||
requires :package_name, type: String, desc: 'Package name'
|
||||
requires :file_name, type: String, desc: 'Package file name'
|
||||
end
|
||||
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
|
||||
get '*package_name/-/*file_name', format: false do
|
||||
authorize_read_package!(project)
|
||||
|
||||
package = project.packages.npm
|
||||
.by_name_and_file_name(params[:package_name], params[:file_name])
|
||||
|
||||
not_found!('Package') unless package
|
||||
|
||||
package_file = ::Packages::PackageFileFinder
|
||||
.new(package, params[:file_name]).execute!
|
||||
|
||||
track_package_event('pull_package', package, category: 'API::NpmPackages')
|
||||
|
||||
present_carrierwave_file!(package_file.file)
|
||||
end
|
||||
|
||||
desc 'Create NPM package' do
|
||||
detail 'This feature was introduced in GitLab 11.8'
|
||||
end
|
||||
params do
|
||||
requires :package_name, type: String, desc: 'Package name'
|
||||
requires :versions, type: Hash, desc: 'Package version info'
|
||||
end
|
||||
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
|
||||
put ':package_name', requirements: ::API::Helpers::Packages::Npm::NPM_ENDPOINT_REQUIREMENTS do
|
||||
authorize_create_package!(project)
|
||||
|
||||
track_package_event('push_package', :npm, category: 'API::NpmPackages')
|
||||
|
||||
created_package = ::Packages::Npm::CreatePackageService
|
||||
.new(project, current_user, params.merge(build: current_authenticated_job)).execute
|
||||
|
||||
if created_package[:status] == :error
|
||||
render_api_error!(created_package[:message], created_package[:http_status])
|
||||
else
|
||||
created_package
|
||||
end
|
||||
end
|
||||
|
||||
include ::API::Concerns::Packages::NpmEndpoints
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,19 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module BulkImports
|
||||
module Common
|
||||
module Loaders
|
||||
class EntitiesLoader
|
||||
def initialize(*args); end
|
||||
|
||||
def load(context, entities)
|
||||
bulk_import = context.entity.bulk_import
|
||||
|
||||
entities.each do |entity|
|
||||
bulk_import.entities.create!(entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module BulkImports
|
||||
module Common
|
||||
module Loaders
|
||||
class EntityLoader
|
||||
def initialize(*args); end
|
||||
|
||||
def load(context, entity)
|
||||
context.entity.bulk_import.entities.create!(entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -9,12 +9,9 @@ module BulkImports
|
|||
def extract(context)
|
||||
encoded_parent_path = ERB::Util.url_encode(context.entity.source_full_path)
|
||||
|
||||
subgroups = []
|
||||
http_client(context.entity.bulk_import.configuration)
|
||||
.each_page(:get, "groups/#{encoded_parent_path}/subgroups") do |page|
|
||||
subgroups << page
|
||||
end
|
||||
subgroups
|
||||
.each_page(:get, "groups/#{encoded_parent_path}/subgroups")
|
||||
.flat_map(&:itself)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -7,8 +7,8 @@ module BulkImports
|
|||
include Pipeline
|
||||
|
||||
extractor BulkImports::Groups::Extractors::SubgroupsExtractor
|
||||
transformer BulkImports::Groups::Transformers::SubgroupsToEntitiesTransformer
|
||||
loader BulkImports::Common::Loaders::EntitiesLoader
|
||||
transformer BulkImports::Groups::Transformers::SubgroupToEntityTransformer
|
||||
loader BulkImports::Common::Loaders::EntityLoader
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module BulkImports
|
||||
module Groups
|
||||
module Transformers
|
||||
class SubgroupToEntityTransformer
|
||||
def initialize(*args); end
|
||||
|
||||
def transform(context, entry)
|
||||
{
|
||||
source_type: :group_entity,
|
||||
source_full_path: entry['full_path'],
|
||||
destination_name: entry['name'],
|
||||
destination_namespace: context.entity.group.full_path,
|
||||
parent_id: context.entity.id
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,23 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module BulkImports
|
||||
module Groups
|
||||
module Transformers
|
||||
class SubgroupsToEntitiesTransformer
|
||||
def initialize(*args); end
|
||||
|
||||
def transform(context, data)
|
||||
data.map do |entry|
|
||||
{
|
||||
source_type: :group_entity,
|
||||
source_full_path: entry['full_path'],
|
||||
destination_name: entry['name'],
|
||||
destination_namespace: context.entity.group.full_path,
|
||||
parent_id: context.entity.id
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3546,6 +3546,9 @@ msgstr ""
|
|||
msgid "Archived"
|
||||
msgstr ""
|
||||
|
||||
msgid "Archived (%{movedToStart}moved%{movedToEnd})"
|
||||
msgstr ""
|
||||
|
||||
msgid "Archived in this version"
|
||||
msgstr ""
|
||||
|
||||
|
@ -5327,6 +5330,9 @@ msgstr ""
|
|||
msgid "Choose labels"
|
||||
msgstr ""
|
||||
|
||||
msgid "Choose specific groups or storage shards"
|
||||
msgstr ""
|
||||
|
||||
msgid "Choose the top-level group for your repository imports."
|
||||
msgstr ""
|
||||
|
||||
|
@ -7156,7 +7162,7 @@ msgstr ""
|
|||
msgid "Container repositories"
|
||||
msgstr ""
|
||||
|
||||
msgid "Container repositories sync capacity"
|
||||
msgid "Container repositories synchronization concurrency limit"
|
||||
msgstr ""
|
||||
|
||||
msgid "Container repository"
|
||||
|
@ -8810,9 +8816,6 @@ msgstr ""
|
|||
msgid "Delete variable"
|
||||
msgstr ""
|
||||
|
||||
msgid "DeleteProject|Delete %{name}"
|
||||
msgstr ""
|
||||
|
||||
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
|
||||
msgstr ""
|
||||
|
||||
|
@ -10423,6 +10426,9 @@ msgstr ""
|
|||
msgid "Environments|Delete"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|Delete '%{environmentName}'?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Environments|Delete environment"
|
||||
msgstr ""
|
||||
|
||||
|
@ -11754,7 +11760,7 @@ msgstr ""
|
|||
msgid "File renamed with no changes."
|
||||
msgstr ""
|
||||
|
||||
msgid "File sync capacity"
|
||||
msgid "File synchronization concurrency limit"
|
||||
msgstr ""
|
||||
|
||||
msgid "File templates"
|
||||
|
@ -16053,6 +16059,9 @@ msgstr ""
|
|||
msgid "Limit namespaces and projects that can be indexed"
|
||||
msgstr ""
|
||||
|
||||
msgid "Limit the number of concurrent operations this secondary node can run in the background."
|
||||
msgstr ""
|
||||
|
||||
msgid "Limited to showing %d event at most"
|
||||
msgid_plural "Limited to showing %d events at most"
|
||||
msgstr[0] ""
|
||||
|
@ -17685,6 +17694,9 @@ msgstr ""
|
|||
msgid "Move selection up"
|
||||
msgstr ""
|
||||
|
||||
msgid "Move test case"
|
||||
msgstr ""
|
||||
|
||||
msgid "Move this issue to another project."
|
||||
msgstr ""
|
||||
|
||||
|
@ -22856,7 +22868,7 @@ msgstr ""
|
|||
msgid "Repository storage"
|
||||
msgstr ""
|
||||
|
||||
msgid "Repository sync capacity"
|
||||
msgid "Repository synchronization concurrency limit"
|
||||
msgstr ""
|
||||
|
||||
msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
|
||||
|
@ -24559,12 +24571,6 @@ msgstr ""
|
|||
msgid "Set the milestone to %{milestone_reference}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Set the number of concurrent requests this secondary node will make to the primary node while backfilling."
|
||||
msgstr ""
|
||||
|
||||
msgid "Set the synchronization and verification capacity for the secondary node."
|
||||
msgstr ""
|
||||
|
||||
msgid "Set the timeout in seconds to send a secondary node status to the primary and IPs allowed for the secondary nodes."
|
||||
msgstr ""
|
||||
|
||||
|
@ -24607,13 +24613,16 @@ msgstr ""
|
|||
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
|
||||
msgstr ""
|
||||
|
||||
msgid "Set verification limit and frequency."
|
||||
msgstr ""
|
||||
|
||||
msgid "Set weight"
|
||||
msgstr ""
|
||||
|
||||
msgid "Set weight to %{weight}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Set what should be replicated by choosing specific projects or groups by the secondary node."
|
||||
msgid "Set what should be replicated by this secondary node."
|
||||
msgstr ""
|
||||
|
||||
msgid "SetPasswordToCloneLink|set a password"
|
||||
|
@ -26210,6 +26219,9 @@ msgstr ""
|
|||
msgid "Synchronization disabled"
|
||||
msgstr ""
|
||||
|
||||
msgid "Synchronization settings"
|
||||
msgstr ""
|
||||
|
||||
msgid "Syncing…"
|
||||
msgstr ""
|
||||
|
||||
|
@ -26489,6 +26501,12 @@ msgstr[1] ""
|
|||
msgid "Test settings"
|
||||
msgstr ""
|
||||
|
||||
msgid "TestCases|Move test case"
|
||||
msgstr ""
|
||||
|
||||
msgid "TestCases|Moving test case"
|
||||
msgstr ""
|
||||
|
||||
msgid "TestCases|New Test Case"
|
||||
msgstr ""
|
||||
|
||||
|
@ -26516,6 +26534,9 @@ msgstr ""
|
|||
msgid "TestCases|Something went wrong while marking test case todo as done."
|
||||
msgstr ""
|
||||
|
||||
msgid "TestCases|Something went wrong while moving test case."
|
||||
msgstr ""
|
||||
|
||||
msgid "TestCases|Something went wrong while updating the test case labels."
|
||||
msgstr ""
|
||||
|
||||
|
@ -29632,7 +29653,7 @@ msgstr ""
|
|||
msgid "Various settings that affect GitLab performance."
|
||||
msgstr ""
|
||||
|
||||
msgid "Verification capacity"
|
||||
msgid "Verification concurrency limit"
|
||||
msgstr ""
|
||||
|
||||
msgid "Verification information"
|
||||
|
|
|
@ -279,7 +279,7 @@ RSpec.describe 'Project' do
|
|||
end
|
||||
|
||||
it 'deletes a project', :sidekiq_might_not_need_inline do
|
||||
expect { remove_with_confirm('Delete project', "Delete #{project.full_name}", 'Yes, delete project') }.to change { Project.count }.by(-1)
|
||||
expect { remove_with_confirm('Delete project', project.path, 'Yes, delete project') }.to change { Project.count }.by(-1)
|
||||
expect(page).to have_content "Project '#{project.full_name}' is in the process of being deleted."
|
||||
expect(Project.all.count).to be_zero
|
||||
expect(project.issues).to be_empty
|
||||
|
|
|
@ -16,6 +16,12 @@ RSpec.describe ::Packages::Npm::PackageFinder do
|
|||
|
||||
it { is_expected.to be_empty }
|
||||
end
|
||||
|
||||
context 'with nil project' do
|
||||
let(:project) { nil }
|
||||
|
||||
it { is_expected.to be_empty }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#find_by_version' do
|
||||
|
|
|
@ -17,7 +17,7 @@ RSpec.describe GitlabSchema.types['Issue'] do
|
|||
fields = %i[id iid title description state reference author assignees updated_by participants labels milestone due_date
|
||||
confidential discussion_locked upvotes downvotes user_notes_count user_discussions_count web_path web_url relative_position
|
||||
emails_disabled subscribed time_estimate total_time_spent human_time_estimate human_total_time_spent closed_at created_at updated_at task_completion_status
|
||||
designs design_collection alert_management_alert severity current_user_todos]
|
||||
designs design_collection alert_management_alert severity current_user_todos moved moved_to]
|
||||
|
||||
fields.each do |field_name|
|
||||
expect(described_class).to have_graphql_field(field_name)
|
||||
|
|
|
@ -2,20 +2,20 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe BulkImports::Common::Loaders::EntitiesLoader do
|
||||
RSpec.describe BulkImports::Common::Loaders::EntityLoader do
|
||||
describe '#load' do
|
||||
it "creates entities for the given data" do
|
||||
group = create(:group, path: "imported-group")
|
||||
parent_entity = create(:bulk_import_entity, group: group, bulk_import: create(:bulk_import))
|
||||
context = instance_double(BulkImports::Pipeline::Context, entity: parent_entity)
|
||||
|
||||
data = [{
|
||||
data = {
|
||||
source_type: :group_entity,
|
||||
source_full_path: "parent/subgroup",
|
||||
destination_name: "subgroup",
|
||||
destination_namespace: parent_entity.group.full_path,
|
||||
parent_id: parent_entity.id
|
||||
}]
|
||||
}
|
||||
|
||||
expect { subject.load(context, data) }.to change(BulkImports::Entity, :count).by(1)
|
||||
|
|
@ -35,7 +35,7 @@ RSpec.describe BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline do
|
|||
|
||||
before do
|
||||
allow_next_instance_of(BulkImports::Groups::Extractors::SubgroupsExtractor) do |extractor|
|
||||
allow(extractor).to receive(:extract).and_return([subgroup_data])
|
||||
allow(extractor).to receive(:extract).and_return(subgroup_data)
|
||||
end
|
||||
|
||||
parent.add_owner(user)
|
||||
|
@ -67,14 +67,14 @@ RSpec.describe BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline do
|
|||
|
||||
it 'has transformers' do
|
||||
expect(described_class.transformers).to contain_exactly(
|
||||
klass: BulkImports::Groups::Transformers::SubgroupsToEntitiesTransformer,
|
||||
klass: BulkImports::Groups::Transformers::SubgroupToEntityTransformer,
|
||||
options: nil
|
||||
)
|
||||
end
|
||||
|
||||
it 'has loaders' do
|
||||
expect(described_class.loaders).to contain_exactly(
|
||||
klass: BulkImports::Common::Loaders::EntitiesLoader,
|
||||
klass: BulkImports::Common::Loaders::EntityLoader,
|
||||
options: nil
|
||||
)
|
||||
end
|
||||
|
|
|
@ -2,20 +2,18 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe BulkImports::Groups::Transformers::SubgroupsToEntitiesTransformer do
|
||||
RSpec.describe BulkImports::Groups::Transformers::SubgroupToEntityTransformer do
|
||||
describe "#transform" do
|
||||
it "transforms subgroups data in entity params" do
|
||||
parent = create(:group)
|
||||
parent_entity = instance_double(BulkImports::Entity, group: parent, id: 1)
|
||||
context = instance_double(BulkImports::Pipeline::Context, entity: parent_entity)
|
||||
subgroup_data = [
|
||||
{
|
||||
"name" => "subgroup",
|
||||
"full_path" => "parent/subgroup"
|
||||
}
|
||||
]
|
||||
subgroup_data = {
|
||||
"name" => "subgroup",
|
||||
"full_path" => "parent/subgroup"
|
||||
}
|
||||
|
||||
expect(subject.transform(context, subgroup_data)).to contain_exactly(
|
||||
expect(subject.transform(context, subgroup_data)).to eq(
|
||||
source_type: :group_entity,
|
||||
source_full_path: "parent/subgroup",
|
||||
destination_name: "subgroup",
|
|
@ -5,6 +5,7 @@ require 'spec_helper'
|
|||
RSpec.describe Gitlab::GitAccessSnippet do
|
||||
include ProjectHelpers
|
||||
include TermsHelper
|
||||
include AdminModeHelper
|
||||
include_context 'ProjectPolicyTable context'
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
|
@ -207,12 +208,13 @@ RSpec.describe Gitlab::GitAccessSnippet do
|
|||
let(:snippet) { create(:personal_snippet, snippet_level, :repository) }
|
||||
let(:user) { membership == :author ? snippet.author : create_user_from_membership(nil, membership) }
|
||||
|
||||
where(:snippet_level, :membership, :_expected_count) do
|
||||
where(:snippet_level, :membership, :admin_mode, :_expected_count) do
|
||||
permission_table_for_personal_snippet_access
|
||||
end
|
||||
|
||||
with_them do
|
||||
it "respects accessibility" do
|
||||
enable_admin_mode!(user) if admin_mode
|
||||
error_class = described_class::ForbiddenError
|
||||
|
||||
if Ability.allowed?(user, :update_snippet, snippet)
|
||||
|
|
|
@ -79,6 +79,18 @@ RSpec.describe MergeRequest, factory_default: :keep do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.with_jira_issue_keys' do
|
||||
let_it_be(:mr_with_jira_title) { create(:merge_request, :unique_branches, title: 'Fix TEST-123') }
|
||||
let_it_be(:mr_with_jira_description) { create(:merge_request, :unique_branches, description: 'this closes TEST-321') }
|
||||
let_it_be(:mr_without_jira_reference) { create(:merge_request, :unique_branches) }
|
||||
|
||||
subject { described_class.with_jira_issue_keys }
|
||||
|
||||
it { is_expected.to contain_exactly(mr_with_jira_title, mr_with_jira_description) }
|
||||
|
||||
it { is_expected.not_to include(mr_without_jira_reference) }
|
||||
end
|
||||
|
||||
describe '#squash_in_progress?' do
|
||||
let(:repo_path) do
|
||||
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
|
||||
|
|
|
@ -3996,8 +3996,16 @@ RSpec.describe Project, factory_default: :keep do
|
|||
context 'when feature is private' do
|
||||
let(:project) { create(:project, :public, :merge_requests_private) }
|
||||
|
||||
it 'returns projects with the project feature private' do
|
||||
is_expected.to include(project)
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it 'returns projects with the project feature private' do
|
||||
is_expected.to include(project)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when admin mode is disabled' do
|
||||
it 'does not return projects with the project feature private' do
|
||||
is_expected.not_to include(project)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -4020,7 +4028,7 @@ RSpec.describe Project, factory_default: :keep do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.filter_by_feature_visibility', :enable_admin_mode do
|
||||
describe '.filter_by_feature_visibility' do
|
||||
include_context 'ProjectPolicyTable context'
|
||||
include ProjectHelpers
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
@ -4032,12 +4040,13 @@ RSpec.describe Project, factory_default: :keep do
|
|||
context 'reporter level access' do
|
||||
let(:feature) { MergeRequest }
|
||||
|
||||
where(:project_level, :feature_access_level, :membership, :expected_count) do
|
||||
where(:project_level, :feature_access_level, :membership, :admin_mode, :expected_count) do
|
||||
permission_table_for_reporter_feature_access
|
||||
end
|
||||
|
||||
with_them do
|
||||
it "respects visibility" do
|
||||
enable_admin_mode!(user) if admin_mode
|
||||
update_feature_access_level(project, feature_access_level)
|
||||
|
||||
expected_objects = expected_count == 1 ? [project] : []
|
||||
|
@ -4052,12 +4061,13 @@ RSpec.describe Project, factory_default: :keep do
|
|||
context 'issues' do
|
||||
let(:feature) { Issue }
|
||||
|
||||
where(:project_level, :feature_access_level, :membership, :expected_count) do
|
||||
where(:project_level, :feature_access_level, :membership, :admin_mode, :expected_count) do
|
||||
permission_table_for_guest_feature_access
|
||||
end
|
||||
|
||||
with_them do
|
||||
it "respects visibility" do
|
||||
enable_admin_mode!(user) if admin_mode
|
||||
update_feature_access_level(project, feature_access_level)
|
||||
|
||||
expected_objects = expected_count == 1 ? [project] : []
|
||||
|
@ -4072,12 +4082,13 @@ RSpec.describe Project, factory_default: :keep do
|
|||
context 'wiki' do
|
||||
let(:feature) { :wiki }
|
||||
|
||||
where(:project_level, :feature_access_level, :membership, :expected_count) do
|
||||
where(:project_level, :feature_access_level, :membership, :admin_mode, :expected_count) do
|
||||
permission_table_for_guest_feature_access
|
||||
end
|
||||
|
||||
with_them do
|
||||
it "respects visibility" do
|
||||
enable_admin_mode!(user) if admin_mode
|
||||
update_feature_access_level(project, feature_access_level)
|
||||
|
||||
expected_objects = expected_count == 1 ? [project] : []
|
||||
|
@ -4092,12 +4103,13 @@ RSpec.describe Project, factory_default: :keep do
|
|||
context 'code' do
|
||||
let(:feature) { :repository }
|
||||
|
||||
where(:project_level, :feature_access_level, :membership, :expected_count) do
|
||||
where(:project_level, :feature_access_level, :membership, :admin_mode, :expected_count) do
|
||||
permission_table_for_guest_feature_access_and_non_private_project_only
|
||||
end
|
||||
|
||||
with_them do
|
||||
it "respects visibility" do
|
||||
enable_admin_mode!(user) if admin_mode
|
||||
update_feature_access_level(project, feature_access_level)
|
||||
|
||||
expected_objects = expected_count == 1 ? [project] : []
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe BlobPolicy, :enable_admin_mode do
|
||||
RSpec.describe BlobPolicy do
|
||||
include_context 'ProjectPolicyTable context'
|
||||
include ProjectHelpers
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
@ -13,12 +13,13 @@ RSpec.describe BlobPolicy, :enable_admin_mode do
|
|||
|
||||
subject(:policy) { described_class.new(user, blob) }
|
||||
|
||||
where(:project_level, :feature_access_level, :membership, :expected_count) do
|
||||
where(:project_level, :feature_access_level, :membership, :admin_mode, :expected_count) do
|
||||
permission_table_for_guest_feature_access_and_non_private_project_only
|
||||
end
|
||||
|
||||
with_them do
|
||||
it "grants permission" do
|
||||
enable_admin_mode!(user) if admin_mode
|
||||
update_feature_access_level(project, feature_access_level)
|
||||
|
||||
if expected_count == 1
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe WikiPagePolicy, :enable_admin_mode do
|
||||
RSpec.describe WikiPagePolicy do
|
||||
include_context 'ProjectPolicyTable context'
|
||||
include ProjectHelpers
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
@ -13,12 +13,13 @@ RSpec.describe WikiPagePolicy, :enable_admin_mode do
|
|||
|
||||
subject(:policy) { described_class.new(user, wiki_page) }
|
||||
|
||||
where(:project_level, :feature_access_level, :membership, :expected_count) do
|
||||
where(:project_level, :feature_access_level, :membership, :admin_mode, :expected_count) do
|
||||
permission_table_for_guest_feature_access
|
||||
end
|
||||
|
||||
with_them do
|
||||
it "grants permission" do
|
||||
enable_admin_mode!(user) if admin_mode
|
||||
update_feature_access_level(project, feature_access_level)
|
||||
|
||||
if expected_count == 1
|
||||
|
|
|
@ -83,6 +83,25 @@ RSpec.describe 'Query.issue(id)' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when issue got moved' do
|
||||
let_it_be(:issue_fields) { ['moved', 'movedTo { title }'] }
|
||||
let_it_be(:new_issue) { create(:issue) }
|
||||
let_it_be(:issue) { create(:issue, project: project, moved_to: new_issue) }
|
||||
let_it_be(:issue_params) { { 'id' => issue.to_global_id.to_s } }
|
||||
|
||||
before_all do
|
||||
new_issue.project.add_developer(current_user)
|
||||
end
|
||||
|
||||
it 'returns correct attributes' do
|
||||
post_graphql(query, current_user: current_user)
|
||||
|
||||
expect(issue_data.keys).to eq( %w(moved movedTo) )
|
||||
expect(issue_data['moved']).to eq(true)
|
||||
expect(issue_data['movedTo']['title']).to eq(new_issue.title)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when passed a non-Issue gid' do
|
||||
let(:mr) {create(:merge_request)}
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe API::NpmInstancePackages do
|
||||
include_context 'npm api setup'
|
||||
|
||||
describe 'GET /api/v4/packages/npm/*package_name' do
|
||||
it_behaves_like 'handling get metadata requests' do
|
||||
let(:url) { api("/packages/npm/#{package_name}") }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v4/packages/npm/-/package/*package_name/dist-tags' do
|
||||
it_behaves_like 'handling get dist tags requests', scope: :instance do
|
||||
let(:url) { api("/packages/npm/-/package/#{package_name}/dist-tags") }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /api/v4/packages/npm/-/package/*package_name/dist-tags/:tag' do
|
||||
it_behaves_like 'handling create dist tag requests', scope: :instance do
|
||||
let(:url) { api("/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /api/v4/packages/npm/-/package/*package_name/dist-tags/:tag' do
|
||||
it_behaves_like 'handling delete dist tag requests', scope: :instance do
|
||||
let(:url) { api("/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,556 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe API::NpmPackages do
|
||||
include PackagesManagerApiSpecHelpers
|
||||
include HttpBasicAuthHelpers
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project, reload: true) { create(:project, :public, namespace: group) }
|
||||
let_it_be(:package, reload: true) { create(:npm_package, project: project) }
|
||||
let_it_be(:token) { create(:oauth_access_token, scopes: 'api', resource_owner: user) }
|
||||
let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
|
||||
let_it_be(:job, reload: true) { create(:ci_build, user: user, status: :running) }
|
||||
let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) }
|
||||
let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) }
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
shared_examples 'a package that requires auth' do
|
||||
it 'returns the package info with oauth token' do
|
||||
get_package_with_token(package)
|
||||
|
||||
expect_a_valid_package_response
|
||||
end
|
||||
|
||||
it 'returns the package info with running job token' do
|
||||
get_package_with_job_token(package)
|
||||
|
||||
expect_a_valid_package_response
|
||||
end
|
||||
|
||||
it 'denies request without running job token' do
|
||||
job.update!(status: :success)
|
||||
get_package_with_job_token(package)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:unauthorized)
|
||||
end
|
||||
|
||||
it 'denies request without oauth token' do
|
||||
get_package(package)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
|
||||
it 'returns the package info with deploy token' do
|
||||
get_package_with_deploy_token(package)
|
||||
|
||||
expect_a_valid_package_response
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v4/packages/npm/*package_name' do
|
||||
let_it_be(:package_dependency_link1) { create(:packages_dependency_link, package: package, dependency_type: :dependencies) }
|
||||
let_it_be(:package_dependency_link2) { create(:packages_dependency_link, package: package, dependency_type: :devDependencies) }
|
||||
let_it_be(:package_dependency_link3) { create(:packages_dependency_link, package: package, dependency_type: :bundleDependencies) }
|
||||
let_it_be(:package_dependency_link4) { create(:packages_dependency_link, package: package, dependency_type: :peerDependencies) }
|
||||
|
||||
shared_examples 'returning the npm package info' do
|
||||
it 'returns the package info' do
|
||||
get_package(package)
|
||||
|
||||
expect_a_valid_package_response
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'returning forbidden for unknown package' do
|
||||
context 'with an unknown package' do
|
||||
it 'returns forbidden' do
|
||||
get api("/packages/npm/unknown")
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'a public project' do
|
||||
it_behaves_like 'returning the npm package info'
|
||||
|
||||
context 'with application setting enabled' do
|
||||
before do
|
||||
stub_application_setting(npm_package_requests_forwarding: true)
|
||||
end
|
||||
|
||||
it_behaves_like 'returning the npm package info'
|
||||
|
||||
context 'with unknown package' do
|
||||
subject { get api("/packages/npm/unknown") }
|
||||
|
||||
it 'returns a redirect' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
expect(response.headers['Location']).to eq('https://registry.npmjs.org/unknown')
|
||||
end
|
||||
|
||||
it_behaves_like 'a gitlab tracking event', described_class.name, 'npm_request_forward'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with application setting disabled' do
|
||||
before do
|
||||
stub_application_setting(npm_package_requests_forwarding: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'returning the npm package info'
|
||||
|
||||
it_behaves_like 'returning forbidden for unknown package'
|
||||
end
|
||||
|
||||
context 'project path with a dot' do
|
||||
before do
|
||||
project.update!(path: 'foo.bar')
|
||||
end
|
||||
|
||||
it_behaves_like 'returning the npm package info'
|
||||
end
|
||||
end
|
||||
|
||||
context 'internal project' do
|
||||
before do
|
||||
project.team.truncate
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
|
||||
end
|
||||
|
||||
it_behaves_like 'a package that requires auth'
|
||||
end
|
||||
|
||||
context 'private project' do
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
|
||||
end
|
||||
|
||||
it_behaves_like 'a package that requires auth'
|
||||
|
||||
it 'denies request when not enough permissions' do
|
||||
project.add_guest(user)
|
||||
|
||||
get_package_with_token(package)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
def get_package(package, params = {}, headers = {})
|
||||
get api("/packages/npm/#{package.name}"), params: params, headers: headers
|
||||
end
|
||||
|
||||
def get_package_with_token(package, params = {})
|
||||
get_package(package, params.merge(access_token: token.token))
|
||||
end
|
||||
|
||||
def get_package_with_job_token(package, params = {})
|
||||
get_package(package, params.merge(job_token: job.token))
|
||||
end
|
||||
|
||||
def get_package_with_deploy_token(package, params = {})
|
||||
get_package(package, {}, build_token_auth_header(deploy_token.token))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v4/projects/:id/packages/npm/*package_name/-/*file_name' do
|
||||
let_it_be(:package_file) { package.package_files.first }
|
||||
|
||||
shared_examples 'a package file that requires auth' do
|
||||
it 'returns the file with an access token' do
|
||||
get_file_with_token(package_file)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.media_type).to eq('application/octet-stream')
|
||||
end
|
||||
|
||||
it 'returns the file with a job token' do
|
||||
get_file_with_job_token(package_file)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.media_type).to eq('application/octet-stream')
|
||||
end
|
||||
|
||||
it 'denies download with no token' do
|
||||
get_file(package_file)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
context 'a public project' do
|
||||
subject { get_file(package_file) }
|
||||
|
||||
it 'returns the file with no token needed' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.media_type).to eq('application/octet-stream')
|
||||
end
|
||||
|
||||
it_behaves_like 'a package tracking event', described_class.name, 'pull_package'
|
||||
end
|
||||
|
||||
context 'private project' do
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
|
||||
end
|
||||
|
||||
it_behaves_like 'a package file that requires auth'
|
||||
|
||||
it 'denies download when not enough permissions' do
|
||||
project.add_guest(user)
|
||||
|
||||
get_file_with_token(package_file)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
context 'internal project' do
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
|
||||
end
|
||||
|
||||
it_behaves_like 'a package file that requires auth'
|
||||
end
|
||||
|
||||
def get_file(package_file, params = {})
|
||||
get api("/projects/#{project.id}/packages/npm/" \
|
||||
"#{package_file.package.name}/-/#{package_file.file_name}"), params: params
|
||||
end
|
||||
|
||||
def get_file_with_token(package_file, params = {})
|
||||
get_file(package_file, params.merge(access_token: token.token))
|
||||
end
|
||||
|
||||
def get_file_with_job_token(package_file, params = {})
|
||||
get_file(package_file, params.merge(job_token: job.token))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /api/v4/projects/:id/packages/npm/:package_name' do
|
||||
RSpec.shared_examples 'handling invalid record with 400 error' do
|
||||
it 'handles an ActiveRecord::RecordInvalid exception with 400 error' do
|
||||
expect { upload_package_with_token(package_name, params) }
|
||||
.not_to change { project.packages.count }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when params are correct' do
|
||||
context 'invalid package record' do
|
||||
context 'unscoped package' do
|
||||
let(:package_name) { 'my_unscoped_package' }
|
||||
let(:params) { upload_params(package_name: package_name) }
|
||||
|
||||
it_behaves_like 'handling invalid record with 400 error'
|
||||
|
||||
context 'with empty versions' do
|
||||
let(:params) { upload_params(package_name: package_name).merge!(versions: {}) }
|
||||
|
||||
it 'throws a 400 error' do
|
||||
expect { upload_package_with_token(package_name, params) }
|
||||
.not_to change { project.packages.count }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'invalid package name' do
|
||||
let(:package_name) { "@#{group.path}/my_inv@@lid_package_name" }
|
||||
let(:params) { upload_params(package_name: package_name) }
|
||||
|
||||
it_behaves_like 'handling invalid record with 400 error'
|
||||
end
|
||||
|
||||
context 'invalid package version' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let(:package_name) { "@#{group.path}/my_package_name" }
|
||||
|
||||
where(:version) do
|
||||
[
|
||||
'1',
|
||||
'1.2',
|
||||
'1./2.3',
|
||||
'../../../../../1.2.3',
|
||||
'%2e%2e%2f1.2.3'
|
||||
]
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:params) { upload_params(package_name: package_name, package_version: version) }
|
||||
|
||||
it_behaves_like 'handling invalid record with 400 error'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'scoped package' do
|
||||
let(:package_name) { "@#{group.path}/my_package_name" }
|
||||
let(:params) { upload_params(package_name: package_name) }
|
||||
|
||||
context 'with access token' do
|
||||
subject { upload_package_with_token(package_name, params) }
|
||||
|
||||
it_behaves_like 'a package tracking event', described_class.name, 'push_package'
|
||||
|
||||
it 'creates npm package with file' do
|
||||
expect { subject }
|
||||
.to change { project.packages.count }.by(1)
|
||||
.and change { Packages::PackageFile.count }.by(1)
|
||||
.and change { Packages::Tag.count }.by(1)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
end
|
||||
|
||||
it 'creates npm package with file with job token' do
|
||||
expect { upload_package_with_job_token(package_name, params) }
|
||||
.to change { project.packages.count }.by(1)
|
||||
.and change { Packages::PackageFile.count }.by(1)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
|
||||
context 'with an authenticated job token' do
|
||||
let!(:job) { create(:ci_build, user: user) }
|
||||
|
||||
before do
|
||||
Grape::Endpoint.before_each do |endpoint|
|
||||
expect(endpoint).to receive(:current_authenticated_job) { job }
|
||||
end
|
||||
end
|
||||
|
||||
after do
|
||||
Grape::Endpoint.before_each nil
|
||||
end
|
||||
|
||||
it 'creates the package metadata' do
|
||||
upload_package_with_token(package_name, params)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(project.reload.packages.find(json_response['id']).build_info.pipeline).to eq job.pipeline
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'package creation fails' do
|
||||
let(:package_name) { "@#{group.path}/my_package_name" }
|
||||
let(:params) { upload_params(package_name: package_name) }
|
||||
|
||||
it 'returns an error if the package already exists' do
|
||||
create(:npm_package, project: project, version: '1.0.1', name: "@#{group.path}/my_package_name")
|
||||
expect { upload_package_with_token(package_name, params) }
|
||||
.not_to change { project.packages.count }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with dependencies' do
|
||||
let(:package_name) { "@#{group.path}/my_package_name" }
|
||||
let(:params) { upload_params(package_name: package_name, file: 'npm/payload_with_duplicated_packages.json') }
|
||||
|
||||
it 'creates npm package with file and dependencies' do
|
||||
expect { upload_package_with_token(package_name, params) }
|
||||
.to change { project.packages.count }.by(1)
|
||||
.and change { Packages::PackageFile.count }.by(1)
|
||||
.and change { Packages::Dependency.count}.by(4)
|
||||
.and change { Packages::DependencyLink.count}.by(6)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
|
||||
context 'with existing dependencies' do
|
||||
before do
|
||||
name = "@#{group.path}/existing_package"
|
||||
upload_package_with_token(name, upload_params(package_name: name, file: 'npm/payload_with_duplicated_packages.json'))
|
||||
end
|
||||
|
||||
it 'reuses them' do
|
||||
expect { upload_package_with_token(package_name, params) }
|
||||
.to change { project.packages.count }.by(1)
|
||||
.and change { Packages::PackageFile.count }.by(1)
|
||||
.and not_change { Packages::Dependency.count}
|
||||
.and change { Packages::DependencyLink.count}.by(6)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def upload_package(package_name, params = {})
|
||||
put api("/projects/#{project.id}/packages/npm/#{package_name.sub('/', '%2f')}"), params: params
|
||||
end
|
||||
|
||||
def upload_package_with_token(package_name, params = {})
|
||||
upload_package(package_name, params.merge(access_token: token.token))
|
||||
end
|
||||
|
||||
def upload_package_with_job_token(package_name, params = {})
|
||||
upload_package(package_name, params.merge(job_token: job.token))
|
||||
end
|
||||
|
||||
def upload_params(package_name:, package_version: '1.0.1', file: 'npm/payload.json')
|
||||
Gitlab::Json.parse(fixture_file("packages/#{file}")
|
||||
.gsub('@root/npm-test', package_name)
|
||||
.gsub('1.0.1', package_version))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v4/packages/npm/-/package/*package_name/dist-tags' do
|
||||
let_it_be(:package_tag1) { create(:packages_tag, package: package) }
|
||||
let_it_be(:package_tag2) { create(:packages_tag, package: package) }
|
||||
|
||||
let(:package_name) { package.name }
|
||||
let(:url) { "/packages/npm/-/package/#{package_name}/dist-tags" }
|
||||
|
||||
subject { get api(url) }
|
||||
|
||||
context 'with public project' do
|
||||
context 'with authenticated user' do
|
||||
subject { get api(url, personal_access_token: personal_access_token) }
|
||||
|
||||
it_behaves_like 'returns package tags', :maintainer
|
||||
it_behaves_like 'returns package tags', :developer
|
||||
it_behaves_like 'returns package tags', :reporter
|
||||
it_behaves_like 'returns package tags', :guest
|
||||
end
|
||||
|
||||
context 'with unauthenticated user' do
|
||||
it_behaves_like 'returns package tags', :no_type
|
||||
end
|
||||
end
|
||||
|
||||
context 'with private project' do
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
|
||||
end
|
||||
|
||||
context 'with authenticated user' do
|
||||
subject { get api(url, personal_access_token: personal_access_token) }
|
||||
|
||||
it_behaves_like 'returns package tags', :maintainer
|
||||
it_behaves_like 'returns package tags', :developer
|
||||
it_behaves_like 'returns package tags', :reporter
|
||||
it_behaves_like 'rejects package tags access', :guest, :forbidden
|
||||
end
|
||||
|
||||
context 'with unauthenticated user' do
|
||||
it_behaves_like 'rejects package tags access', :no_type, :forbidden
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /api/v4/packages/npm/-/package/*package_name/dist-tags/:tag' do
|
||||
let_it_be(:tag_name) { 'test' }
|
||||
|
||||
let(:package_name) { package.name }
|
||||
let(:version) { package.version }
|
||||
let(:url) { "/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}" }
|
||||
|
||||
subject { put api(url), env: { 'api.request.body': version } }
|
||||
|
||||
context 'with public project' do
|
||||
context 'with authenticated user' do
|
||||
subject { put api(url, personal_access_token: personal_access_token), env: { 'api.request.body': version } }
|
||||
|
||||
it_behaves_like 'create package tag', :maintainer
|
||||
it_behaves_like 'create package tag', :developer
|
||||
it_behaves_like 'rejects package tags access', :reporter, :forbidden
|
||||
it_behaves_like 'rejects package tags access', :guest, :forbidden
|
||||
end
|
||||
|
||||
context 'with unauthenticated user' do
|
||||
it_behaves_like 'rejects package tags access', :no_type, :unauthorized
|
||||
end
|
||||
end
|
||||
|
||||
context 'with private project' do
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
|
||||
end
|
||||
|
||||
context 'with authenticated user' do
|
||||
subject { put api(url, personal_access_token: personal_access_token), env: { 'api.request.body': version } }
|
||||
|
||||
it_behaves_like 'create package tag', :maintainer
|
||||
it_behaves_like 'create package tag', :developer
|
||||
it_behaves_like 'rejects package tags access', :reporter, :forbidden
|
||||
it_behaves_like 'rejects package tags access', :guest, :forbidden
|
||||
end
|
||||
|
||||
context 'with unauthenticated user' do
|
||||
it_behaves_like 'rejects package tags access', :no_type, :unauthorized
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /api/v4/packages/npm/-/package/*package_name/dist-tags/:tag' do
|
||||
let_it_be(:package_tag) { create(:packages_tag, package: package) }
|
||||
|
||||
let(:package_name) { package.name }
|
||||
let(:tag_name) { package_tag.name }
|
||||
let(:url) { "/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}" }
|
||||
|
||||
subject { delete api(url) }
|
||||
|
||||
context 'with public project' do
|
||||
context 'with authenticated user' do
|
||||
subject { delete api(url, personal_access_token: personal_access_token) }
|
||||
|
||||
it_behaves_like 'delete package tag', :maintainer
|
||||
it_behaves_like 'rejects package tags access', :developer, :forbidden
|
||||
it_behaves_like 'rejects package tags access', :reporter, :forbidden
|
||||
it_behaves_like 'rejects package tags access', :guest, :forbidden
|
||||
end
|
||||
|
||||
context 'with unauthenticated user' do
|
||||
it_behaves_like 'rejects package tags access', :no_type, :unauthorized
|
||||
end
|
||||
end
|
||||
|
||||
context 'with private project' do
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
|
||||
end
|
||||
|
||||
context 'with authenticated user' do
|
||||
subject { delete api(url, personal_access_token: personal_access_token) }
|
||||
|
||||
it_behaves_like 'delete package tag', :maintainer
|
||||
it_behaves_like 'rejects package tags access', :developer, :forbidden
|
||||
it_behaves_like 'rejects package tags access', :reporter, :forbidden
|
||||
it_behaves_like 'rejects package tags access', :guest, :forbidden
|
||||
end
|
||||
|
||||
context 'with unauthenticated user' do
|
||||
it_behaves_like 'rejects package tags access', :no_type, :unauthorized
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def expect_a_valid_package_response
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.media_type).to eq('application/json')
|
||||
expect(response).to match_response_schema('public_api/v4/packages/npm_package')
|
||||
expect(json_response['name']).to eq(package.name)
|
||||
expect(json_response['versions'][package.version]).to match_schema('public_api/v4/packages/npm_package_version')
|
||||
::Packages::Npm::PackagePresenter::NPM_VALID_DEPENDENCY_TYPES.each do |dependency_type|
|
||||
expect(json_response.dig('versions', package.version, dependency_type.to_s)).to be_any
|
||||
end
|
||||
expect(json_response['dist-tags']).to match_schema('public_api/v4/packages/npm_package_tags')
|
||||
end
|
||||
end
|
|
@ -0,0 +1,281 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe API::NpmProjectPackages do
|
||||
include_context 'npm api setup'
|
||||
|
||||
describe 'GET /api/v4/projects/:id/packages/npm/*package_name' do
|
||||
it_behaves_like 'handling get metadata requests' do
|
||||
let(:url) { api("/projects/#{project.id}/packages/npm/#{package_name}") }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v4/projects/:id/packages/npm/-/package/*package_name/dist-tags' do
|
||||
it_behaves_like 'handling get dist tags requests' do
|
||||
let(:url) { api("/projects/#{project.id}/packages/npm/-/package/#{package_name}/dist-tags") }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /api/v4/projects/:id/packages/npm/-/package/*package_name/dist-tags/:tag' do
|
||||
it_behaves_like 'handling create dist tag requests' do
|
||||
let(:url) { api("/projects/#{project.id}/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /api/v4/projects/:id/packages/npm/-/package/*package_name/dist-tags/:tag' do
|
||||
it_behaves_like 'handling delete dist tag requests' do
|
||||
let(:url) { api("/projects/#{project.id}/packages/npm/-/package/#{package_name}/dist-tags/#{tag_name}") }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v4/projects/:id/packages/npm/*package_name/-/*file_name' do
|
||||
let_it_be(:package_file) { package.package_files.first }
|
||||
|
||||
let(:params) { {} }
|
||||
let(:url) { api("/projects/#{project.id}/packages/npm/#{package_file.package.name}/-/#{package_file.file_name}") }
|
||||
|
||||
subject { get(url, params: params) }
|
||||
|
||||
shared_examples 'a package file that requires auth' do
|
||||
it 'denies download with no token' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
|
||||
context 'with access token' do
|
||||
let(:params) { { access_token: token.token } }
|
||||
|
||||
it 'returns the file' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.media_type).to eq('application/octet-stream')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with job token' do
|
||||
let(:params) { { job_token: job.token } }
|
||||
|
||||
it 'returns the file' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.media_type).to eq('application/octet-stream')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'a public project' do
|
||||
it 'returns the file with no token needed' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.media_type).to eq('application/octet-stream')
|
||||
end
|
||||
|
||||
it_behaves_like 'a package tracking event', 'API::NpmPackages', 'pull_package'
|
||||
end
|
||||
|
||||
context 'private project' do
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
|
||||
end
|
||||
|
||||
it_behaves_like 'a package file that requires auth'
|
||||
|
||||
context 'with guest' do
|
||||
let(:params) { { access_token: token.token } }
|
||||
|
||||
it 'denies download when not enough permissions' do
|
||||
project.add_guest(user)
|
||||
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'internal project' do
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
|
||||
end
|
||||
|
||||
it_behaves_like 'a package file that requires auth'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /api/v4/projects/:id/packages/npm/:package_name' do
|
||||
RSpec.shared_examples 'handling invalid record with 400 error' do
|
||||
it 'handles an ActiveRecord::RecordInvalid exception with 400 error' do
|
||||
expect { upload_package_with_token(package_name, params) }
|
||||
.not_to change { project.packages.count }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when params are correct' do
|
||||
context 'invalid package record' do
|
||||
context 'unscoped package' do
|
||||
let(:package_name) { 'my_unscoped_package' }
|
||||
let(:params) { upload_params(package_name: package_name) }
|
||||
|
||||
it_behaves_like 'handling invalid record with 400 error'
|
||||
|
||||
context 'with empty versions' do
|
||||
let(:params) { upload_params(package_name: package_name).merge!(versions: {}) }
|
||||
|
||||
it 'throws a 400 error' do
|
||||
expect { upload_package_with_token(package_name, params) }
|
||||
.not_to change { project.packages.count }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'invalid package name' do
|
||||
let(:package_name) { "@#{group.path}/my_inv@@lid_package_name" }
|
||||
let(:params) { upload_params(package_name: package_name) }
|
||||
|
||||
it_behaves_like 'handling invalid record with 400 error'
|
||||
end
|
||||
|
||||
context 'invalid package version' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let(:package_name) { "@#{group.path}/my_package_name" }
|
||||
|
||||
where(:version) do
|
||||
[
|
||||
'1',
|
||||
'1.2',
|
||||
'1./2.3',
|
||||
'../../../../../1.2.3',
|
||||
'%2e%2e%2f1.2.3'
|
||||
]
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:params) { upload_params(package_name: package_name, package_version: version) }
|
||||
|
||||
it_behaves_like 'handling invalid record with 400 error'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'scoped package' do
|
||||
let(:package_name) { "@#{group.path}/my_package_name" }
|
||||
let(:params) { upload_params(package_name: package_name) }
|
||||
|
||||
context 'with access token' do
|
||||
subject { upload_package_with_token(package_name, params) }
|
||||
|
||||
it_behaves_like 'a package tracking event', 'API::NpmPackages', 'push_package'
|
||||
|
||||
it 'creates npm package with file' do
|
||||
expect { subject }
|
||||
.to change { project.packages.count }.by(1)
|
||||
.and change { Packages::PackageFile.count }.by(1)
|
||||
.and change { Packages::Tag.count }.by(1)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
end
|
||||
|
||||
it 'creates npm package with file with job token' do
|
||||
expect { upload_package_with_job_token(package_name, params) }
|
||||
.to change { project.packages.count }.by(1)
|
||||
.and change { Packages::PackageFile.count }.by(1)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
|
||||
context 'with an authenticated job token' do
|
||||
let!(:job) { create(:ci_build, user: user) }
|
||||
|
||||
before do
|
||||
Grape::Endpoint.before_each do |endpoint|
|
||||
expect(endpoint).to receive(:current_authenticated_job) { job }
|
||||
end
|
||||
end
|
||||
|
||||
after do
|
||||
Grape::Endpoint.before_each nil
|
||||
end
|
||||
|
||||
it 'creates the package metadata' do
|
||||
upload_package_with_token(package_name, params)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(project.reload.packages.find(json_response['id']).build_info.pipeline).to eq job.pipeline
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'package creation fails' do
|
||||
let(:package_name) { "@#{group.path}/my_package_name" }
|
||||
let(:params) { upload_params(package_name: package_name) }
|
||||
|
||||
it 'returns an error if the package already exists' do
|
||||
create(:npm_package, project: project, version: '1.0.1', name: "@#{group.path}/my_package_name")
|
||||
expect { upload_package_with_token(package_name, params) }
|
||||
.not_to change { project.packages.count }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with dependencies' do
|
||||
let(:package_name) { "@#{group.path}/my_package_name" }
|
||||
let(:params) { upload_params(package_name: package_name, file: 'npm/payload_with_duplicated_packages.json') }
|
||||
|
||||
it 'creates npm package with file and dependencies' do
|
||||
expect { upload_package_with_token(package_name, params) }
|
||||
.to change { project.packages.count }.by(1)
|
||||
.and change { Packages::PackageFile.count }.by(1)
|
||||
.and change { Packages::Dependency.count}.by(4)
|
||||
.and change { Packages::DependencyLink.count}.by(6)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
|
||||
context 'with existing dependencies' do
|
||||
before do
|
||||
name = "@#{group.path}/existing_package"
|
||||
upload_package_with_token(name, upload_params(package_name: name, file: 'npm/payload_with_duplicated_packages.json'))
|
||||
end
|
||||
|
||||
it 'reuses them' do
|
||||
expect { upload_package_with_token(package_name, params) }
|
||||
.to change { project.packages.count }.by(1)
|
||||
.and change { Packages::PackageFile.count }.by(1)
|
||||
.and not_change { Packages::Dependency.count}
|
||||
.and change { Packages::DependencyLink.count}.by(6)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def upload_package(package_name, params = {})
|
||||
put api("/projects/#{project.id}/packages/npm/#{package_name.sub('/', '%2f')}"), params: params
|
||||
end
|
||||
|
||||
def upload_package_with_token(package_name, params = {})
|
||||
upload_package(package_name, params.merge(access_token: token.token))
|
||||
end
|
||||
|
||||
def upload_package_with_job_token(package_name, params = {})
|
||||
upload_package(package_name, params.merge(job_token: job.token))
|
||||
end
|
||||
|
||||
def upload_params(package_name:, package_version: '1.0.1', file: 'npm/payload.json')
|
||||
Gitlab::Json.parse(fixture_file("packages/#{file}")
|
||||
.gsub('@root/npm-test', package_name)
|
||||
.gsub('1.0.1', package_version))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -12,8 +12,12 @@ RSpec.describe MoveToProjectEntity do
|
|||
expect(subject[:id]).to eq(project.id)
|
||||
end
|
||||
|
||||
it 'includes the full path' do
|
||||
it 'includes the human-readable full path' do
|
||||
expect(subject[:name_with_namespace]).to eq(project.name_with_namespace)
|
||||
end
|
||||
|
||||
it 'includes the full path' do
|
||||
expect(subject[:full_path]).to eq(project.full_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Auth::ContainerRegistryAuthenticationService do
|
||||
include AdminModeHelper
|
||||
|
||||
let(:current_project) { nil }
|
||||
let(:current_user) { nil }
|
||||
let(:current_params) { {} }
|
||||
|
@ -696,6 +698,10 @@ RSpec.describe Auth::ContainerRegistryAuthenticationService do
|
|||
context 'user has access to all projects' do
|
||||
let_it_be(:current_user) { create(:user, :admin) }
|
||||
|
||||
before do
|
||||
enable_admin_mode!(current_user)
|
||||
end
|
||||
|
||||
it_behaves_like 'a browsable' do
|
||||
let(:access) do
|
||||
[
|
||||
|
|
|
@ -4,13 +4,13 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Ci::CreatePipelineService do
|
||||
context 'cache' do
|
||||
let(:user) { create(:admin) }
|
||||
let(:project) { create(:project, :custom_repo, files: files) }
|
||||
let(:user) { project.owner }
|
||||
let(:ref) { 'refs/heads/master' }
|
||||
let(:source) { :push }
|
||||
let(:service) { described_class.new(project, user, { ref: ref }) }
|
||||
let(:pipeline) { service.execute(source) }
|
||||
let(:job) { pipeline.builds.find_by(name: 'job') }
|
||||
let(:project) { create(:project, :custom_repo, files: files) }
|
||||
|
||||
before do
|
||||
stub_ci_pipeline_yaml_file(config)
|
||||
|
|
|
@ -4,8 +4,8 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Ci::CreatePipelineService do
|
||||
describe 'creation errors and warnings' do
|
||||
let_it_be(:user) { create(:admin) }
|
||||
let_it_be(:project) { create(:project, :repository, creator: user) }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:user) { project.owner }
|
||||
|
||||
let(:ref) { 'refs/heads/master' }
|
||||
let(:source) { :push }
|
||||
|
|
|
@ -3,7 +3,7 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Ci::CreatePipelineService do
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:user) { create(:admin) }
|
||||
let_it_be(:user) { project.owner }
|
||||
let(:ref) { 'refs/heads/master' }
|
||||
let(:service) { described_class.new(project, user, { ref: ref }) }
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Ci::CreatePipelineService do
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:user) { create(:admin) }
|
||||
let_it_be(:user) { project.owner }
|
||||
let(:ref) { 'refs/heads/master' }
|
||||
let(:service) { described_class.new(project, user, { ref: ref }) }
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Ci::CreatePipelineService do
|
||||
context 'needs' do
|
||||
let_it_be(:user) { create(:admin) }
|
||||
let_it_be(:project) { create(:project, :repository, creator: user) }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:user) { project.owner }
|
||||
|
||||
let(:ref) { 'refs/heads/master' }
|
||||
let(:source) { :push }
|
||||
|
@ -14,6 +14,7 @@ RSpec.describe Ci::CreatePipelineService do
|
|||
|
||||
before do
|
||||
stub_ci_pipeline_yaml_file(config)
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
context 'with a valid config' do
|
||||
|
|
|
@ -4,7 +4,7 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Ci::CreatePipelineService do
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:user) { create(:admin) }
|
||||
let_it_be(:user) { project.owner }
|
||||
let(:service) { described_class.new(project, user, { ref: 'refs/heads/master' }) }
|
||||
let(:content) do
|
||||
<<~EOY
|
||||
|
|
|
@ -3,8 +3,8 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Ci::CreatePipelineService do
|
||||
describe '.pre/.post stages' do
|
||||
let_it_be(:user) { create(:admin) }
|
||||
let_it_be(:project) { create(:project, :repository, creator: user) }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:user) { project.owner }
|
||||
|
||||
let(:source) { :push }
|
||||
let(:service) { described_class.new(project, user, { ref: ref }) }
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Ci::CreatePipelineService do
|
||||
let(:user) { create(:admin) }
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:user) { project.owner }
|
||||
let(:ref) { 'refs/heads/master' }
|
||||
let(:source) { :push }
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:service) { described_class.new(project, user, { ref: ref }) }
|
||||
let(:pipeline) { service.execute(source) }
|
||||
let(:build_names) { pipeline.builds.pluck(:name) }
|
||||
|
|
|
@ -6,7 +6,7 @@ RSpec.describe Ci::CreatePipelineService do
|
|||
include ProjectForksHelper
|
||||
|
||||
let_it_be(:project, reload: true) { create(:project, :repository) }
|
||||
let(:user) { create(:admin) }
|
||||
let_it_be(:user, reload: true) { project.owner }
|
||||
let(:ref_name) { 'refs/heads/master' }
|
||||
|
||||
before do
|
||||
|
@ -155,6 +155,11 @@ RSpec.describe Ci::CreatePipelineService do
|
|||
context 'when merge request target project is different from source project' do
|
||||
let!(:project) { fork_project(target_project, nil, repository: true) }
|
||||
let!(:target_project) { create(:project, :repository) }
|
||||
let!(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it 'updates head pipeline for merge request', :sidekiq_might_not_need_inline do
|
||||
merge_request = create(:merge_request, source_branch: 'feature',
|
||||
|
@ -1442,6 +1447,11 @@ RSpec.describe Ci::CreatePipelineService do
|
|||
let(:ref_name) { 'refs/heads/feature' }
|
||||
let!(:project) { fork_project(target_project, nil, repository: true) }
|
||||
let!(:target_project) { create(:project, :repository) }
|
||||
let!(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it 'creates a legacy detached merge request pipeline in the forked project', :sidekiq_might_not_need_inline do
|
||||
expect(pipeline).to be_persisted
|
||||
|
|
|
@ -321,21 +321,40 @@ RSpec.describe Issues::MoveService do
|
|||
|
||||
before do
|
||||
authorized_project.add_developer(user)
|
||||
authorized_project.add_developer(admin)
|
||||
authorized_project2.add_developer(user)
|
||||
authorized_project2.add_developer(admin)
|
||||
end
|
||||
|
||||
context 'multiple related issues' do
|
||||
it 'moves all related issues and retains permissions' do
|
||||
new_issue = move_service.execute(old_issue, new_project)
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it 'moves all related issues and retains permissions' do
|
||||
new_issue = move_service.execute(old_issue, new_project)
|
||||
|
||||
expect(new_issue.related_issues(admin))
|
||||
.to match_array([authorized_issue_b, authorized_issue_c, authorized_issue_d, unauthorized_issue])
|
||||
expect(new_issue.related_issues(admin))
|
||||
.to match_array([authorized_issue_b, authorized_issue_c, authorized_issue_d, unauthorized_issue])
|
||||
|
||||
expect(new_issue.related_issues(user))
|
||||
.to match_array([authorized_issue_b, authorized_issue_c, authorized_issue_d])
|
||||
expect(new_issue.related_issues(user))
|
||||
.to match_array([authorized_issue_b, authorized_issue_c, authorized_issue_d])
|
||||
|
||||
expect(authorized_issue_d.related_issues(user))
|
||||
.to match_array([new_issue])
|
||||
expect(authorized_issue_d.related_issues(user))
|
||||
.to match_array([new_issue])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when admin mode is disabled' do
|
||||
it 'moves all related issues and retains permissions' do
|
||||
new_issue = move_service.execute(old_issue, new_project)
|
||||
|
||||
expect(new_issue.related_issues(admin))
|
||||
.to match_array([authorized_issue_b, authorized_issue_c, authorized_issue_d])
|
||||
|
||||
expect(new_issue.related_issues(user))
|
||||
.to match_array([authorized_issue_b, authorized_issue_c, authorized_issue_d])
|
||||
|
||||
expect(authorized_issue_d.related_issues(user))
|
||||
.to match_array([new_issue])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -74,8 +74,16 @@ RSpec.describe Issues::RelatedBranchesService do
|
|||
context 'the user has access to otherwise unreadable pipelines' do
|
||||
let(:user) { create(:admin) }
|
||||
|
||||
it 'returns info a developer could not see' do
|
||||
expect(branch_info.pluck(:pipeline_status)).to include(an_instance_of(Gitlab::Ci::Status::Running))
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it 'returns info a developer could not see' do
|
||||
expect(branch_info.pluck(:pipeline_status)).to include(an_instance_of(Gitlab::Ci::Status::Running))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when admin mode is disabled' do
|
||||
it 'does not return info a developer could not see' do
|
||||
expect(branch_info.pluck(:pipeline_status)).not_to include(an_instance_of(Gitlab::Ci::Status::Running))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -32,6 +32,36 @@ RSpec.describe JiraConnectSubscriptions::CreateService do
|
|||
it 'returns success' do
|
||||
expect(subject[:status]).to eq(:success)
|
||||
end
|
||||
|
||||
context 'namespace has projects' do
|
||||
let!(:project_1) { create(:project, group: group) }
|
||||
let!(:project_2) { create(:project, group: group) }
|
||||
|
||||
before do
|
||||
stub_const("#{described_class}::MERGE_REQUEST_SYNC_BATCH_SIZE", 1)
|
||||
end
|
||||
|
||||
it 'starts workers to sync projects in batches with delay' do
|
||||
allow(Atlassian::JiraConnect::Client).to receive(:generate_update_sequence_id).and_return(123)
|
||||
|
||||
expect(JiraConnect::SyncProjectWorker).to receive(:bulk_perform_in).with(1.minute, [[project_1.id, 123]])
|
||||
expect(JiraConnect::SyncProjectWorker).to receive(:bulk_perform_in).with(2.minutes, [[project_2.id, 123]])
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
context 'when the jira_connect_full_namespace_sync feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(jira_connect_full_namespace_sync: false)
|
||||
end
|
||||
|
||||
specify do
|
||||
expect(JiraConnect::SyncProjectWorker).not_to receive(:bulk_perform_in_with_contexts)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when path is invalid' do
|
||||
|
|
|
@ -4,7 +4,7 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Labels::TransferService do
|
||||
describe '#execute' do
|
||||
let_it_be(:user) { create(:admin) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
let_it_be(:old_group_ancestor) { create(:group) }
|
||||
let_it_be(:old_group) { create(:group, parent: old_group_ancestor) }
|
||||
|
@ -15,6 +15,11 @@ RSpec.describe Labels::TransferService do
|
|||
|
||||
subject(:service) { described_class.new(user, old_group, project) }
|
||||
|
||||
before do
|
||||
old_group_ancestor.add_developer(user)
|
||||
new_group.add_developer(user)
|
||||
end
|
||||
|
||||
it 'recreates missing group labels at project level and assigns them to the issuables' do
|
||||
old_group_label_1 = create(:group_label, group: old_group)
|
||||
old_group_label_2 = create(:group_label, group: old_group)
|
||||
|
|
|
@ -12,10 +12,20 @@ RSpec.describe MergeRequests::AddContextService do
|
|||
subject(:service) { described_class.new(project, admin, merge_request: merge_request, commits: commits) }
|
||||
|
||||
describe "#execute" do
|
||||
it "adds context commit" do
|
||||
service.execute
|
||||
context "when admin mode is enabled", :enable_admin_mode do
|
||||
it "adds context commit" do
|
||||
service.execute
|
||||
|
||||
expect(merge_request.merge_request_context_commit_diff_files.length).to eq(2)
|
||||
expect(merge_request.merge_request_context_commit_diff_files.length).to eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
context "when admin mode is disabled" do
|
||||
it "doesn't add context commit" do
|
||||
subject.execute
|
||||
|
||||
expect(merge_request.merge_request_context_commit_diff_files.length).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context "when user doesn't have permission to update merge request" do
|
||||
|
|
|
@ -3099,12 +3099,26 @@ RSpec.describe NotificationService, :mailer do
|
|||
subject.new_issue(issue, member)
|
||||
end
|
||||
|
||||
it 'still delivers email to admins' do
|
||||
member.update!(admin: true)
|
||||
context 'with admin user' do
|
||||
before do
|
||||
member.update!(admin: true)
|
||||
end
|
||||
|
||||
expect(Notify).to receive(:new_issue_email).at_least(:once).with(member.id, issue.id, nil).and_call_original
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it 'still delivers email to admins' do
|
||||
expect(Notify).to receive(:new_issue_email).at_least(:once).with(member.id, issue.id, nil).and_call_original
|
||||
|
||||
subject.new_issue(issue, member)
|
||||
subject.new_issue(issue, member)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when admin mode is disabled' do
|
||||
it 'does not send an email' do
|
||||
expect(Notify).not_to receive(:new_issue_email)
|
||||
|
||||
subject.new_issue(issue, member)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,7 +38,13 @@ RSpec.describe PersonalAccessTokens::CreateService do
|
|||
context 'when current_user is an administrator' do
|
||||
let(:current_user) { create(:admin) }
|
||||
|
||||
it_behaves_like 'a successfully created token'
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it_behaves_like 'a successfully created token'
|
||||
end
|
||||
|
||||
context 'when admin mode is disabled' do
|
||||
it_behaves_like 'an unsuccessfully created token'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when current_user is not an administrator' do
|
||||
|
|
|
@ -24,10 +24,19 @@ RSpec.describe PersonalAccessTokens::RevokeService do
|
|||
let(:service) { described_class.new(current_user, token: token) }
|
||||
|
||||
context 'when current_user is an administrator' do
|
||||
let_it_be(:current_user) { create(:admin) }
|
||||
let_it_be(:token) { create(:personal_access_token) }
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
let_it_be(:current_user) { create(:admin) }
|
||||
let_it_be(:token) { create(:personal_access_token) }
|
||||
|
||||
it_behaves_like 'a successfully revoked token'
|
||||
it_behaves_like 'a successfully revoked token'
|
||||
end
|
||||
|
||||
context 'when admin mode is disabled' do
|
||||
let_it_be(:current_user) { create(:admin) }
|
||||
let_it_be(:token) { create(:personal_access_token) }
|
||||
|
||||
it_behaves_like 'an unsuccessfully revoked token'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when current_user is not an administrator' do
|
||||
|
|
|
@ -79,14 +79,28 @@ RSpec.describe Projects::AutocompleteService do
|
|||
expect(issues.count).to eq 3
|
||||
end
|
||||
|
||||
it 'lists all project issues for admin' do
|
||||
autocomplete = described_class.new(project, admin)
|
||||
issues = autocomplete.issues.map(&:iid)
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it 'lists all project issues for admin', :enable_admin_mode do
|
||||
autocomplete = described_class.new(project, admin)
|
||||
issues = autocomplete.issues.map(&:iid)
|
||||
|
||||
expect(issues).to include issue.iid
|
||||
expect(issues).to include security_issue_1.iid
|
||||
expect(issues).to include security_issue_2.iid
|
||||
expect(issues.count).to eq 3
|
||||
expect(issues).to include issue.iid
|
||||
expect(issues).to include security_issue_1.iid
|
||||
expect(issues).to include security_issue_2.iid
|
||||
expect(issues.count).to eq 3
|
||||
end
|
||||
end
|
||||
|
||||
context 'when admin mode is disabled' do
|
||||
it 'does not list project confidential issues for admin' do
|
||||
autocomplete = described_class.new(project, admin)
|
||||
issues = autocomplete.issues.map(&:iid)
|
||||
|
||||
expect(issues).to include issue.iid
|
||||
expect(issues).not_to include security_issue_1.iid
|
||||
expect(issues).not_to include security_issue_2.iid
|
||||
expect(issues.count).to eq 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -72,14 +72,25 @@ RSpec.describe Projects::CreateService, '#execute' do
|
|||
end
|
||||
|
||||
context "admin creates project with other user's namespace_id" do
|
||||
it 'sets the correct permissions' do
|
||||
admin = create(:admin)
|
||||
project = create_project(admin, opts)
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it 'sets the correct permissions' do
|
||||
admin = create(:admin)
|
||||
project = create_project(admin, opts)
|
||||
|
||||
expect(project).to be_persisted
|
||||
expect(project.owner).to eq(user)
|
||||
expect(project.team.maintainers).to contain_exactly(user)
|
||||
expect(project.namespace).to eq(user.namespace)
|
||||
expect(project).to be_persisted
|
||||
expect(project.owner).to eq(user)
|
||||
expect(project.team.maintainers).to contain_exactly(user)
|
||||
expect(project.namespace).to eq(user.namespace)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when admin mode is disabled' do
|
||||
it 'is not allowed' do
|
||||
admin = create(:admin)
|
||||
project = create_project(admin, opts)
|
||||
|
||||
expect(project).not_to be_persisted
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -336,7 +347,15 @@ RSpec.describe Projects::CreateService, '#execute' do
|
|||
)
|
||||
end
|
||||
|
||||
it 'allows a restricted visibility level for admins' do
|
||||
it 'does not allow a restricted visibility level for admins when admin mode is disabled' do
|
||||
admin = create(:admin)
|
||||
project = create_project(admin, opts)
|
||||
|
||||
expect(project.errors.any?).to be(true)
|
||||
expect(project.saved?).to be_falsey
|
||||
end
|
||||
|
||||
it 'allows a restricted visibility level for admins when admin mode is enabled', :enable_admin_mode do
|
||||
admin = create(:admin)
|
||||
project = create_project(admin, opts)
|
||||
|
||||
|
|
|
@ -127,11 +127,22 @@ RSpec.describe Projects::UpdateService do
|
|||
end
|
||||
|
||||
context 'when updated by an admin' do
|
||||
it 'updates the project to public' do
|
||||
result = update_project(project, admin, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it 'updates the project to public' do
|
||||
result = update_project(project, admin, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
|
||||
|
||||
expect(result).to eq({ status: :success })
|
||||
expect(project).to be_public
|
||||
expect(result).to eq({ status: :success })
|
||||
expect(project).to be_public
|
||||
end
|
||||
end
|
||||
|
||||
context 'when admin mode is disabled' do
|
||||
it 'does not update the project to public' do
|
||||
result = update_project(project, admin, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
|
||||
|
||||
expect(result).to eq({ status: :error, message: 'New visibility level not allowed!' })
|
||||
expect(project).to be_private
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -144,7 +155,7 @@ RSpec.describe Projects::UpdateService do
|
|||
project.update!(namespace: group, visibility_level: group.visibility_level)
|
||||
end
|
||||
|
||||
it 'does not update project visibility level' do
|
||||
it 'does not update project visibility level even if admin', :enable_admin_mode do
|
||||
result = update_project(project, admin, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
|
||||
|
||||
expect(result).to eq({ status: :error, message: 'Visibility level public is not allowed in a internal group.' })
|
||||
|
@ -181,6 +192,7 @@ RSpec.describe Projects::UpdateService do
|
|||
|
||||
describe 'when updating project that has forks' do
|
||||
let(:project) { create(:project, :internal) }
|
||||
let(:user) { project.owner }
|
||||
let(:forked_project) { fork_project(project) }
|
||||
|
||||
context 'and unlink forks feature flag is off' do
|
||||
|
@ -194,7 +206,7 @@ RSpec.describe Projects::UpdateService do
|
|||
expect(project).to be_internal
|
||||
expect(forked_project).to be_internal
|
||||
|
||||
expect(update_project(project, admin, opts)).to eq({ status: :success })
|
||||
expect(update_project(project, user, opts)).to eq({ status: :success })
|
||||
|
||||
expect(project).to be_private
|
||||
expect(forked_project.reload).to be_private
|
||||
|
@ -206,7 +218,7 @@ RSpec.describe Projects::UpdateService do
|
|||
expect(project).to be_internal
|
||||
expect(forked_project).to be_internal
|
||||
|
||||
expect(update_project(project, admin, opts)).to eq({ status: :success })
|
||||
expect(update_project(project, user, opts)).to eq({ status: :success })
|
||||
|
||||
expect(project).to be_public
|
||||
expect(forked_project.reload).to be_internal
|
||||
|
@ -220,7 +232,7 @@ RSpec.describe Projects::UpdateService do
|
|||
expect(project).to be_internal
|
||||
expect(forked_project).to be_internal
|
||||
|
||||
expect(update_project(project, admin, opts)).to eq({ status: :success })
|
||||
expect(update_project(project, user, opts)).to eq({ status: :success })
|
||||
|
||||
expect(project).to be_private
|
||||
expect(forked_project.reload).to be_internal
|
||||
|
@ -576,15 +588,21 @@ RSpec.describe Projects::UpdateService do
|
|||
context 'authenticated as admin' do
|
||||
let(:user) { create(:admin) }
|
||||
|
||||
it 'schedules the transfer of the repository to the new storage and locks the project' do
|
||||
update_project(project, admin, opts)
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it 'schedules the transfer of the repository to the new storage and locks the project' do
|
||||
update_project(project, admin, opts)
|
||||
|
||||
expect(project).to be_repository_read_only
|
||||
expect(project.repository_storage_moves.last).to have_attributes(
|
||||
state: ::ProjectRepositoryStorageMove.state_machines[:state].states[:scheduled].value,
|
||||
source_storage_name: 'default',
|
||||
destination_storage_name: 'test_second_storage'
|
||||
)
|
||||
expect(project).to be_repository_read_only
|
||||
expect(project.repository_storage_moves.last).to have_attributes(
|
||||
state: ::ProjectRepositoryStorageMove.state_machines[:state].states[:scheduled].value,
|
||||
source_storage_name: 'default',
|
||||
destination_storage_name: 'test_second_storage'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when admin mode is disabled' do
|
||||
it_behaves_like 'the transfer was not scheduled'
|
||||
end
|
||||
|
||||
context 'the repository is read-only' do
|
||||
|
|
|
@ -46,8 +46,18 @@ RSpec.describe ResourceAccessTokens::CreateService do
|
|||
end
|
||||
|
||||
context 'when created by an admin' do
|
||||
it_behaves_like 'creates a user that has their email confirmed' do
|
||||
let(:user) { create(:admin) }
|
||||
let(:user) { create(:admin) }
|
||||
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it_behaves_like 'creates a user that has their email confirmed'
|
||||
end
|
||||
|
||||
context 'when admin mode is disabled' do
|
||||
it 'returns error' do
|
||||
response = subject
|
||||
|
||||
expect(response.error?).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -49,12 +49,24 @@ RSpec.describe Search::SnippetService do
|
|||
expect(results.objects('snippet_titles')).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet]
|
||||
end
|
||||
|
||||
it 'returns all snippets when user is admin' do
|
||||
admin = create(:admin)
|
||||
search = described_class.new(admin, search: 'bar')
|
||||
results = search.execute
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it 'returns all snippets when user is admin' do
|
||||
admin = create(:admin)
|
||||
search = described_class.new(admin, search: 'bar')
|
||||
results = search.execute
|
||||
|
||||
expect(results.objects('snippet_titles')).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet, project_private_snippet]
|
||||
expect(results.objects('snippet_titles')).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet, project_private_snippet]
|
||||
end
|
||||
end
|
||||
|
||||
context 'when admin mode is disabled' do
|
||||
it 'returns only public & internal snippets when user is admin' do
|
||||
admin = create(:admin)
|
||||
search = described_class.new(admin, search: 'bar')
|
||||
results = search.execute
|
||||
|
||||
expect(results.objects('snippet_titles')).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -150,7 +150,7 @@ RSpec.describe TodoService do
|
|||
service.new_issue(issue, author)
|
||||
|
||||
should_create_todo(user: member, target: issue, action: Todo::DIRECTLY_ADDRESSED)
|
||||
should_create_todo(user: admin, target: issue, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: admin, target: issue, action: Todo::MENTIONED)
|
||||
should_create_todo(user: guest, target: issue, action: Todo::MENTIONED)
|
||||
end
|
||||
|
||||
|
@ -160,7 +160,7 @@ RSpec.describe TodoService do
|
|||
should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::ASSIGNED)
|
||||
should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
|
||||
should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
|
||||
should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
|
||||
should_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
|
||||
end
|
||||
|
@ -171,7 +171,7 @@ RSpec.describe TodoService do
|
|||
should_create_todo(user: assignee, target: addressed_confident_issue, author: john_doe, action: Todo::ASSIGNED)
|
||||
should_create_todo(user: author, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
|
||||
should_create_todo(user: member, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
|
||||
should_create_todo(user: admin, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
|
||||
should_not_create_todo(user: admin, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
|
||||
should_not_create_todo(user: guest, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
|
||||
should_create_todo(user: john_doe, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
|
||||
end
|
||||
|
@ -228,7 +228,7 @@ RSpec.describe TodoService do
|
|||
|
||||
should_create_todo(user: member, target: issue, action: Todo::DIRECTLY_ADDRESSED)
|
||||
should_create_todo(user: guest, target: issue, action: Todo::MENTIONED)
|
||||
should_create_todo(user: admin, target: issue, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: admin, target: issue, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: skipped, target: issue)
|
||||
end
|
||||
|
||||
|
@ -273,7 +273,7 @@ RSpec.describe TodoService do
|
|||
should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
|
||||
should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
|
||||
should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
|
||||
should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
|
||||
should_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED)
|
||||
end
|
||||
|
@ -284,7 +284,7 @@ RSpec.describe TodoService do
|
|||
should_create_todo(user: author, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
|
||||
should_create_todo(user: assignee, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
|
||||
should_create_todo(user: member, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
|
||||
should_create_todo(user: admin, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
|
||||
should_not_create_todo(user: admin, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
|
||||
should_not_create_todo(user: guest, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
|
||||
should_create_todo(user: john_doe, target: addressed_confident_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED)
|
||||
end
|
||||
|
@ -432,7 +432,7 @@ RSpec.describe TodoService do
|
|||
service.new_note(note, john_doe)
|
||||
|
||||
should_create_todo(user: member, target: issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: note)
|
||||
should_create_todo(user: admin, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
|
||||
should_not_create_todo(user: admin, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
|
||||
should_create_todo(user: guest, target: issue, author: john_doe, action: Todo::MENTIONED, note: note)
|
||||
end
|
||||
|
||||
|
@ -452,7 +452,7 @@ RSpec.describe TodoService do
|
|||
should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
|
||||
should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
|
||||
should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
|
||||
should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
|
||||
should_not_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
|
||||
should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
|
||||
should_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED, note: note_on_confidential_issue)
|
||||
end
|
||||
|
@ -463,7 +463,7 @@ RSpec.describe TodoService do
|
|||
should_create_todo(user: author, target: confidential_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_confidential_issue)
|
||||
should_create_todo(user: assignee, target: confidential_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_confidential_issue)
|
||||
should_create_todo(user: member, target: confidential_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_confidential_issue)
|
||||
should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_confidential_issue)
|
||||
should_not_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_confidential_issue)
|
||||
should_not_create_todo(user: guest, target: confidential_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_confidential_issue)
|
||||
should_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::DIRECTLY_ADDRESSED, note: addressed_note_on_confidential_issue)
|
||||
end
|
||||
|
@ -699,7 +699,7 @@ RSpec.describe TodoService do
|
|||
service.new_merge_request(mr_assigned, author)
|
||||
|
||||
should_create_todo(user: member, target: mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
|
||||
should_create_todo(user: admin, target: mr_assigned, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: admin, target: mr_assigned, action: Todo::MENTIONED)
|
||||
end
|
||||
|
||||
it 'creates a directly addressed todo for each valid addressed user' do
|
||||
|
@ -731,7 +731,7 @@ RSpec.describe TodoService do
|
|||
service.update_merge_request(mr_assigned, author, skip_users)
|
||||
|
||||
should_create_todo(user: member, target: mr_assigned, action: Todo::DIRECTLY_ADDRESSED)
|
||||
should_create_todo(user: admin, target: mr_assigned, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: admin, target: mr_assigned, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: skipped, target: mr_assigned)
|
||||
end
|
||||
|
||||
|
@ -997,7 +997,7 @@ RSpec.describe TodoService do
|
|||
|
||||
should_create_todo(user: member, target: noteable, action: Todo::DIRECTLY_ADDRESSED)
|
||||
should_create_todo(user: guest, target: noteable, action: Todo::MENTIONED)
|
||||
should_create_todo(user: admin, target: noteable, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: admin, target: noteable, action: Todo::MENTIONED)
|
||||
should_not_create_todo(user: skipped, target: noteable)
|
||||
end
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ RSpec.describe TwoFactor::DestroyService do
|
|||
it_behaves_like 'disables two-factor authentication'
|
||||
end
|
||||
|
||||
context 'admin disables the two-factor authentication of another user' do
|
||||
context 'admin disables the two-factor authentication of another user', :enable_admin_mode do
|
||||
let(:current_user) { create(:admin) }
|
||||
let(:user) { create(:user, :two_factor) }
|
||||
|
||||
|
|
|
@ -19,85 +19,96 @@ RSpec.describe Users::ApproveService do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when user is not in pending approval state' do
|
||||
let(:user) { create(:user, state: 'active') }
|
||||
|
||||
context 'when the executor user is an admin not in admin mode' do
|
||||
it 'returns error result' do
|
||||
expect(subject[:status]).to eq(:error)
|
||||
expect(subject[:message])
|
||||
.to match(/The user you are trying to approve is not pending an approval/)
|
||||
expect(subject[:message]).to match(/You are not allowed to approve a user/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user cannot be activated' do
|
||||
let(:user) do
|
||||
build(:user, state: 'blocked_pending_approval', email: 'invalid email')
|
||||
context 'when the executor user is an admin in admin mode', :enable_admin_mode do
|
||||
context 'when user is not in pending approval state' do
|
||||
let(:user) { create(:user, state: 'active') }
|
||||
|
||||
it 'returns error result' do
|
||||
expect(subject[:status]).to eq(:error)
|
||||
expect(subject[:message])
|
||||
.to match(/The user you are trying to approve is not pending an approval/)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns error result' do
|
||||
expect(subject[:status]).to eq(:error)
|
||||
expect(subject[:message]).to match(/Email is invalid/)
|
||||
end
|
||||
context 'when user cannot be activated' do
|
||||
let(:user) do
|
||||
build(:user, state: 'blocked_pending_approval', email: 'invalid email')
|
||||
end
|
||||
|
||||
it 'does not change the state of the user' do
|
||||
expect { subject }.not_to change { user.state }
|
||||
it 'returns error result' do
|
||||
expect(subject[:status]).to eq(:error)
|
||||
expect(subject[:message]).to match(/Email is invalid/)
|
||||
end
|
||||
|
||||
it 'does not change the state of the user' do
|
||||
expect { subject }.not_to change { user.state }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'success' do
|
||||
it 'activates the user' do
|
||||
expect(subject[:status]).to eq(:success)
|
||||
expect(user.reload).to be_active
|
||||
end
|
||||
context 'when the executor user is an admin in admin mode', :enable_admin_mode do
|
||||
it 'activates the user' do
|
||||
expect(subject[:status]).to eq(:success)
|
||||
expect(user.reload).to be_active
|
||||
end
|
||||
|
||||
context 'email confirmation status' do
|
||||
context 'user is unconfirmed' do
|
||||
let(:user) { create(:user, :blocked_pending_approval, :unconfirmed) }
|
||||
context 'email confirmation status' do
|
||||
context 'user is unconfirmed' do
|
||||
let(:user) { create(:user, :blocked_pending_approval, :unconfirmed) }
|
||||
|
||||
it 'sends confirmation instructions' do
|
||||
expect { subject }
|
||||
.to have_enqueued_mail(DeviseMailer, :confirmation_instructions)
|
||||
it 'sends confirmation instructions' do
|
||||
expect { subject }
|
||||
.to have_enqueued_mail(DeviseMailer, :confirmation_instructions)
|
||||
end
|
||||
end
|
||||
|
||||
context 'user is confirmed' do
|
||||
it 'does not send a confirmation email' do
|
||||
expect { subject }
|
||||
.not_to have_enqueued_mail(DeviseMailer, :confirmation_instructions)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'user is confirmed' do
|
||||
it 'does not send a confirmation email' do
|
||||
expect { subject }
|
||||
.not_to have_enqueued_mail(DeviseMailer, :confirmation_instructions)
|
||||
context 'pending invitations' do
|
||||
let!(:project_member_invite) { create(:project_member, :invited, invite_email: user.email) }
|
||||
let!(:group_member_invite) { create(:group_member, :invited, invite_email: user.email) }
|
||||
|
||||
context 'user is unconfirmed' do
|
||||
let(:user) { create(:user, :blocked_pending_approval, :unconfirmed) }
|
||||
|
||||
it 'does not accept pending invites of the user' do
|
||||
expect(subject[:status]).to eq(:success)
|
||||
|
||||
group_member_invite.reload
|
||||
project_member_invite.reload
|
||||
|
||||
expect(group_member_invite).to be_invite
|
||||
expect(project_member_invite).to be_invite
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'pending invitiations' do
|
||||
let!(:project_member_invite) { create(:project_member, :invited, invite_email: user.email) }
|
||||
let!(:group_member_invite) { create(:group_member, :invited, invite_email: user.email) }
|
||||
context 'user is confirmed' do
|
||||
it 'accepts pending invites of the user' do
|
||||
expect(subject[:status]).to eq(:success)
|
||||
|
||||
context 'user is unconfirmed' do
|
||||
let(:user) { create(:user, :blocked_pending_approval, :unconfirmed) }
|
||||
group_member_invite.reload
|
||||
project_member_invite.reload
|
||||
|
||||
it 'does not accept pending invites of the user' do
|
||||
expect(subject[:status]).to eq(:success)
|
||||
|
||||
group_member_invite.reload
|
||||
project_member_invite.reload
|
||||
|
||||
expect(group_member_invite).to be_invite
|
||||
expect(project_member_invite).to be_invite
|
||||
end
|
||||
end
|
||||
|
||||
context 'user is confirmed' do
|
||||
it 'accepts pending invites of the user' do
|
||||
expect(subject[:status]).to eq(:success)
|
||||
|
||||
group_member_invite.reload
|
||||
project_member_invite.reload
|
||||
|
||||
expect(group_member_invite).not_to be_invite
|
||||
expect(project_member_invite).not_to be_invite
|
||||
expect(group_member_invite.user).to eq(user)
|
||||
expect(project_member_invite.user).to eq(user)
|
||||
expect(group_member_invite).not_to be_invite
|
||||
expect(project_member_invite).not_to be_invite
|
||||
expect(group_member_invite.user).to eq(user)
|
||||
expect(project_member_invite.user).to eq(user)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Users::DestroyService do
|
||||
describe "Deletes a user and all their personal projects" do
|
||||
let!(:user) { create(:user) }
|
||||
let!(:admin) { create(:admin) }
|
||||
let!(:namespace) { user.namespace }
|
||||
let!(:project) { create(:project, namespace: namespace) }
|
||||
let(:service) { described_class.new(admin) }
|
||||
let(:gitlab_shell) { Gitlab::Shell.new }
|
||||
let!(:user) { create(:user) }
|
||||
let!(:admin) { create(:admin) }
|
||||
let!(:namespace) { user.namespace }
|
||||
let!(:project) { create(:project, namespace: namespace) }
|
||||
let(:service) { described_class.new(admin) }
|
||||
let(:gitlab_shell) { Gitlab::Shell.new }
|
||||
|
||||
describe "Deletes a user and all their personal projects", :enable_admin_mode do
|
||||
context 'no options are given' do
|
||||
it 'deletes the user' do
|
||||
user_data = service.execute(user)
|
||||
|
@ -215,35 +215,6 @@ RSpec.describe Users::DestroyService do
|
|||
end
|
||||
end
|
||||
|
||||
context "deletion permission checks" do
|
||||
it 'does not delete the user when user is not an admin' do
|
||||
other_user = create(:user)
|
||||
|
||||
expect { described_class.new(other_user).execute(user) }.to raise_error(Gitlab::Access::AccessDeniedError)
|
||||
expect(User.exists?(user.id)).to be(true)
|
||||
end
|
||||
|
||||
it 'allows admins to delete anyone' do
|
||||
described_class.new(admin).execute(user)
|
||||
|
||||
expect(User.exists?(user.id)).to be(false)
|
||||
end
|
||||
|
||||
it 'allows users to delete their own account' do
|
||||
described_class.new(user).execute(user)
|
||||
|
||||
expect(User.exists?(user.id)).to be(false)
|
||||
end
|
||||
|
||||
it 'allows user to be deleted if skip_authorization: true' do
|
||||
other_user = create(:user)
|
||||
|
||||
described_class.new(user).execute(other_user, skip_authorization: true)
|
||||
|
||||
expect(User.exists?(other_user.id)).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
context "migrating associated records" do
|
||||
let!(:issue) { create(:issue, author: user) }
|
||||
|
||||
|
@ -320,4 +291,43 @@ RSpec.describe Users::DestroyService do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Deletion permission checks" do
|
||||
it 'does not delete the user when user is not an admin' do
|
||||
other_user = create(:user)
|
||||
|
||||
expect { described_class.new(other_user).execute(user) }.to raise_error(Gitlab::Access::AccessDeniedError)
|
||||
expect(User.exists?(user.id)).to be(true)
|
||||
end
|
||||
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it 'allows admins to delete anyone' do
|
||||
described_class.new(admin).execute(user)
|
||||
|
||||
expect(User.exists?(user.id)).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when admin mode is disabled' do
|
||||
it 'disallows admins to delete anyone' do
|
||||
expect { described_class.new(admin).execute(user) }.to raise_error(Gitlab::Access::AccessDeniedError)
|
||||
|
||||
expect(User.exists?(user.id)).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
it 'allows users to delete their own account' do
|
||||
described_class.new(user).execute(user)
|
||||
|
||||
expect(User.exists?(user.id)).to be(false)
|
||||
end
|
||||
|
||||
it 'allows user to be deleted if skip_authorization: true' do
|
||||
other_user = create(:user)
|
||||
|
||||
described_class.new(user).execute(other_user, skip_authorization: true)
|
||||
|
||||
expect(User.exists?(other_user.id)).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -52,7 +52,7 @@ RSpec.describe Users::SetStatusService do
|
|||
{ emoji: 'taurus', message: 'a random status', user: target_user }
|
||||
end
|
||||
|
||||
context 'the current user is admin' do
|
||||
context 'the current user is admin', :enable_admin_mode do
|
||||
let(:current_user) { create(:admin) }
|
||||
|
||||
it 'changes the status when the current user is allowed to do that' do
|
||||
|
|
|
@ -283,12 +283,10 @@ RSpec.configure do |config|
|
|||
./ee/spec/lib
|
||||
./ee/spec/requests/admin
|
||||
./ee/spec/serializers
|
||||
./ee/spec/services
|
||||
./ee/spec/support/protected_tags
|
||||
./ee/spec/support/shared_examples/features
|
||||
./ee/spec/support/shared_examples/finders/geo
|
||||
./ee/spec/support/shared_examples/graphql/geo
|
||||
./ee/spec/support/shared_examples/services
|
||||
./spec/features
|
||||
./spec/finders
|
||||
./spec/frontend
|
||||
|
@ -296,7 +294,6 @@ RSpec.configure do |config|
|
|||
./spec/lib
|
||||
./spec/requests
|
||||
./spec/serializers
|
||||
./spec/services
|
||||
./spec/support/protected_tags
|
||||
./spec/support/shared_examples/features
|
||||
./spec/support/shared_examples/requests
|
||||
|
|
|
@ -13,6 +13,8 @@ module AdminModeHelper
|
|||
def enable_admin_mode!(user)
|
||||
fake_user_mode = instance_double(Gitlab::Auth::CurrentUserMode)
|
||||
|
||||
allow(Gitlab::Auth::CurrentUserMode).to receive(:new).and_call_original
|
||||
|
||||
allow(Gitlab::Auth::CurrentUserMode).to receive(:new).with(user).and_return(fake_user_mode)
|
||||
allow(fake_user_mode).to receive(:admin_mode?).and_return(user&.admin?)
|
||||
end
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
RSpec.shared_context 'ProjectPolicyTable context' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
include AdminModeHelper
|
||||
|
||||
let(:pendings) { {} }
|
||||
let(:pending?) do
|
||||
pendings.include?(
|
||||
|
@ -10,106 +12,117 @@ RSpec.shared_context 'ProjectPolicyTable context' do
|
|||
project_level: project_level,
|
||||
feature_access_level: feature_access_level,
|
||||
membership: membership,
|
||||
admin_mode: admin_mode,
|
||||
expected_count: expected_count
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
# project_level, :feature_access_level, :membership, :expected_count
|
||||
# project_level, :feature_access_level, :membership, :admin_mode, :expected_count
|
||||
def permission_table_for_reporter_feature_access
|
||||
:public | :enabled | :admin | 1
|
||||
:public | :enabled | :reporter | 1
|
||||
:public | :enabled | :guest | 1
|
||||
:public | :enabled | :non_member | 1
|
||||
:public | :enabled | :anonymous | 1
|
||||
:public | :enabled | :admin | true | 1
|
||||
:public | :enabled | :admin | false | 1
|
||||
:public | :enabled | :reporter | nil | 1
|
||||
:public | :enabled | :guest | nil | 1
|
||||
:public | :enabled | :non_member | nil | 1
|
||||
:public | :enabled | :anonymous | nil | 1
|
||||
|
||||
:public | :private | :admin | 1
|
||||
:public | :private | :reporter | 1
|
||||
:public | :private | :guest | 0
|
||||
:public | :private | :non_member | 0
|
||||
:public | :private | :anonymous | 0
|
||||
:public | :private | :admin | true | 1
|
||||
:public | :private | :admin | false | 0
|
||||
:public | :private | :reporter | nil | 1
|
||||
:public | :private | :guest | nil | 0
|
||||
:public | :private | :non_member | nil | 0
|
||||
:public | :private | :anonymous | nil | 0
|
||||
|
||||
:public | :disabled | :reporter | 0
|
||||
:public | :disabled | :guest | 0
|
||||
:public | :disabled | :non_member | 0
|
||||
:public | :disabled | :anonymous | 0
|
||||
:public | :disabled | :reporter | nil | 0
|
||||
:public | :disabled | :guest | nil | 0
|
||||
:public | :disabled | :non_member | nil | 0
|
||||
:public | :disabled | :anonymous | nil | 0
|
||||
|
||||
:internal | :enabled | :admin | 1
|
||||
:internal | :enabled | :reporter | 1
|
||||
:internal | :enabled | :guest | 1
|
||||
:internal | :enabled | :non_member | 1
|
||||
:internal | :enabled | :anonymous | 0
|
||||
:internal | :enabled | :admin | true | 1
|
||||
:internal | :enabled | :admin | false | 1
|
||||
:internal | :enabled | :reporter | nil | 1
|
||||
:internal | :enabled | :guest | nil | 1
|
||||
:internal | :enabled | :non_member | nil | 1
|
||||
:internal | :enabled | :anonymous | nil | 0
|
||||
|
||||
:internal | :private | :admin | 1
|
||||
:internal | :private | :reporter | 1
|
||||
:internal | :private | :guest | 0
|
||||
:internal | :private | :non_member | 0
|
||||
:internal | :private | :anonymous | 0
|
||||
:internal | :private | :admin | true | 1
|
||||
:internal | :private | :admin | false | 0
|
||||
:internal | :private | :reporter | nil | 1
|
||||
:internal | :private | :guest | nil | 0
|
||||
:internal | :private | :non_member | nil | 0
|
||||
:internal | :private | :anonymous | nil | 0
|
||||
|
||||
:internal | :disabled | :reporter | 0
|
||||
:internal | :disabled | :guest | 0
|
||||
:internal | :disabled | :non_member | 0
|
||||
:internal | :disabled | :anonymous | 0
|
||||
:internal | :disabled | :reporter | nil | 0
|
||||
:internal | :disabled | :guest | nil | 0
|
||||
:internal | :disabled | :non_member | nil | 0
|
||||
:internal | :disabled | :anonymous | nil | 0
|
||||
|
||||
:private | :private | :admin | 1
|
||||
:private | :private | :reporter | 1
|
||||
:private | :private | :guest | 0
|
||||
:private | :private | :non_member | 0
|
||||
:private | :private | :anonymous | 0
|
||||
:private | :private | :admin | true | 1
|
||||
:private | :private | :admin | false | 0
|
||||
:private | :private | :reporter | nil | 1
|
||||
:private | :private | :guest | nil | 0
|
||||
:private | :private | :non_member | nil | 0
|
||||
:private | :private | :anonymous | nil | 0
|
||||
|
||||
:private | :disabled | :reporter | 0
|
||||
:private | :disabled | :guest | 0
|
||||
:private | :disabled | :non_member | 0
|
||||
:private | :disabled | :anonymous | 0
|
||||
:private | :disabled | :reporter | nil | 0
|
||||
:private | :disabled | :guest | nil | 0
|
||||
:private | :disabled | :non_member | nil | 0
|
||||
:private | :disabled | :anonymous | nil | 0
|
||||
end
|
||||
|
||||
# project_level, :feature_access_level, :membership, :expected_count
|
||||
# project_level, :feature_access_level, :membership, :admin_mode, :expected_count
|
||||
def permission_table_for_guest_feature_access
|
||||
:public | :enabled | :admin | 1
|
||||
:public | :enabled | :reporter | 1
|
||||
:public | :enabled | :guest | 1
|
||||
:public | :enabled | :non_member | 1
|
||||
:public | :enabled | :anonymous | 1
|
||||
:public | :enabled | :admin | true | 1
|
||||
:public | :enabled | :admin | false | 1
|
||||
:public | :enabled | :reporter | nil | 1
|
||||
:public | :enabled | :guest | nil | 1
|
||||
:public | :enabled | :non_member | nil | 1
|
||||
:public | :enabled | :anonymous | nil | 1
|
||||
|
||||
:public | :private | :admin | 1
|
||||
:public | :private | :reporter | 1
|
||||
:public | :private | :guest | 1
|
||||
:public | :private | :non_member | 0
|
||||
:public | :private | :anonymous | 0
|
||||
:public | :private | :admin | true | 1
|
||||
:public | :private | :admin | false | 0
|
||||
:public | :private | :reporter | nil | 1
|
||||
:public | :private | :guest | nil | 1
|
||||
:public | :private | :non_member | nil | 0
|
||||
:public | :private | :anonymous | nil | 0
|
||||
|
||||
:public | :disabled | :reporter | 0
|
||||
:public | :disabled | :guest | 0
|
||||
:public | :disabled | :non_member | 0
|
||||
:public | :disabled | :anonymous | 0
|
||||
:public | :disabled | :reporter | nil | 0
|
||||
:public | :disabled | :guest | nil | 0
|
||||
:public | :disabled | :non_member | nil | 0
|
||||
:public | :disabled | :anonymous | nil | 0
|
||||
|
||||
:internal | :enabled | :admin | 1
|
||||
:internal | :enabled | :reporter | 1
|
||||
:internal | :enabled | :guest | 1
|
||||
:internal | :enabled | :non_member | 1
|
||||
:internal | :enabled | :anonymous | 0
|
||||
:internal | :enabled | :admin | true | 1
|
||||
:internal | :enabled | :admin | false | 1
|
||||
:internal | :enabled | :reporter | nil | 1
|
||||
:internal | :enabled | :guest | nil | 1
|
||||
:internal | :enabled | :non_member | nil | 1
|
||||
:internal | :enabled | :anonymous | nil | 0
|
||||
|
||||
:internal | :private | :admin | 1
|
||||
:internal | :private | :reporter | 1
|
||||
:internal | :private | :guest | 1
|
||||
:internal | :private | :non_member | 0
|
||||
:internal | :private | :anonymous | 0
|
||||
:internal | :private | :admin | true | 1
|
||||
:internal | :private | :admin | false | 0
|
||||
:internal | :private | :reporter | nil | 1
|
||||
:internal | :private | :guest | nil | 1
|
||||
:internal | :private | :non_member | nil | 0
|
||||
:internal | :private | :anonymous | nil | 0
|
||||
|
||||
:internal | :disabled | :reporter | 0
|
||||
:internal | :disabled | :guest | 0
|
||||
:internal | :disabled | :non_member | 0
|
||||
:internal | :disabled | :anonymous | 0
|
||||
:internal | :disabled | :reporter | nil | 0
|
||||
:internal | :disabled | :guest | nil | 0
|
||||
:internal | :disabled | :non_member | nil | 0
|
||||
:internal | :disabled | :anonymous | nil | 0
|
||||
|
||||
:private | :private | :admin | 1
|
||||
:private | :private | :reporter | 1
|
||||
:private | :private | :guest | 1
|
||||
:private | :private | :non_member | 0
|
||||
:private | :private | :anonymous | 0
|
||||
:private | :private | :admin | true | 1
|
||||
:private | :private | :admin | false | 0
|
||||
:private | :private | :reporter | nil | 1
|
||||
:private | :private | :guest | nil | 1
|
||||
:private | :private | :non_member | nil | 0
|
||||
:private | :private | :anonymous | nil | 0
|
||||
|
||||
:private | :disabled | :reporter | 0
|
||||
:private | :disabled | :guest | 0
|
||||
:private | :disabled | :non_member | 0
|
||||
:private | :disabled | :anonymous | 0
|
||||
:private | :disabled | :reporter | nil | 0
|
||||
:private | :disabled | :guest | nil | 0
|
||||
:private | :disabled | :non_member | nil | 0
|
||||
:private | :disabled | :anonymous | nil | 0
|
||||
end
|
||||
|
||||
# This table is based on permission_table_for_guest_feature_access,
|
||||
|
@ -121,184 +134,208 @@ RSpec.shared_context 'ProjectPolicyTable context' do
|
|||
# e.g. `repository` feature has minimum requirement of GUEST,
|
||||
# but a GUEST are prohibited from reading code if project is private.
|
||||
#
|
||||
# project_level, :feature_access_level, :membership, :expected_count
|
||||
# project_level, :feature_access_level, :membership, :admin_mode, :expected_count
|
||||
def permission_table_for_guest_feature_access_and_non_private_project_only
|
||||
:public | :enabled | :admin | 1
|
||||
:public | :enabled | :reporter | 1
|
||||
:public | :enabled | :guest | 1
|
||||
:public | :enabled | :non_member | 1
|
||||
:public | :enabled | :anonymous | 1
|
||||
:public | :enabled | :admin | true | 1
|
||||
:public | :enabled | :admin | false | 1
|
||||
:public | :enabled | :reporter | nil | 1
|
||||
:public | :enabled | :guest | nil | 1
|
||||
:public | :enabled | :non_member | nil | 1
|
||||
:public | :enabled | :anonymous | nil | 1
|
||||
|
||||
:public | :private | :admin | 1
|
||||
:public | :private | :reporter | 1
|
||||
:public | :private | :guest | 1
|
||||
:public | :private | :non_member | 0
|
||||
:public | :private | :anonymous | 0
|
||||
:public | :private | :admin | true | 1
|
||||
:public | :private | :admin | false | 0
|
||||
:public | :private | :reporter | nil | 1
|
||||
:public | :private | :guest | nil | 1
|
||||
:public | :private | :non_member | nil | 0
|
||||
:public | :private | :anonymous | nil | 0
|
||||
|
||||
:public | :disabled | :reporter | 0
|
||||
:public | :disabled | :guest | 0
|
||||
:public | :disabled | :non_member | 0
|
||||
:public | :disabled | :anonymous | 0
|
||||
:public | :disabled | :reporter | nil | 0
|
||||
:public | :disabled | :guest | nil | 0
|
||||
:public | :disabled | :non_member | nil | 0
|
||||
:public | :disabled | :anonymous | nil | 0
|
||||
|
||||
:internal | :enabled | :admin | 1
|
||||
:internal | :enabled | :reporter | 1
|
||||
:internal | :enabled | :guest | 1
|
||||
:internal | :enabled | :non_member | 1
|
||||
:internal | :enabled | :anonymous | 0
|
||||
:internal | :enabled | :admin | true | 1
|
||||
:internal | :enabled | :admin | false | 1
|
||||
:internal | :enabled | :reporter | nil | 1
|
||||
:internal | :enabled | :guest | nil | 1
|
||||
:internal | :enabled | :non_member | nil | 1
|
||||
:internal | :enabled | :anonymous | nil | 0
|
||||
|
||||
:internal | :private | :admin | 1
|
||||
:internal | :private | :reporter | 1
|
||||
:internal | :private | :guest | 1
|
||||
:internal | :private | :non_member | 0
|
||||
:internal | :private | :anonymous | 0
|
||||
:internal | :private | :admin | true | 1
|
||||
:internal | :private | :admin | false | 0
|
||||
:internal | :private | :reporter | nil | 1
|
||||
:internal | :private | :guest | nil | 1
|
||||
:internal | :private | :non_member | nil | 0
|
||||
:internal | :private | :anonymous | nil | 0
|
||||
|
||||
:internal | :disabled | :reporter | 0
|
||||
:internal | :disabled | :guest | 0
|
||||
:internal | :disabled | :non_member | 0
|
||||
:internal | :disabled | :anonymous | 0
|
||||
:internal | :disabled | :reporter | nil | 0
|
||||
:internal | :disabled | :guest | nil | 0
|
||||
:internal | :disabled | :non_member | nil | 0
|
||||
:internal | :disabled | :anonymous | nil | 0
|
||||
|
||||
:private | :private | :admin | 1
|
||||
:private | :private | :reporter | 1
|
||||
:private | :private | :guest | 0
|
||||
:private | :private | :non_member | 0
|
||||
:private | :private | :anonymous | 0
|
||||
:private | :private | :admin | true | 1
|
||||
:private | :private | :admin | false | 0
|
||||
:private | :private | :reporter | nil | 1
|
||||
:private | :private | :guest | nil | 0
|
||||
:private | :private | :non_member | nil | 0
|
||||
:private | :private | :anonymous | nil | 0
|
||||
|
||||
:private | :disabled | :reporter | 0
|
||||
:private | :disabled | :guest | 0
|
||||
:private | :disabled | :non_member | 0
|
||||
:private | :disabled | :anonymous | 0
|
||||
:private | :disabled | :reporter | nil | 0
|
||||
:private | :disabled | :guest | nil | 0
|
||||
:private | :disabled | :non_member | nil | 0
|
||||
:private | :disabled | :anonymous | nil | 0
|
||||
end
|
||||
|
||||
# :project_level, :issues_access_level, :merge_requests_access_level, :membership, :expected_count
|
||||
# :project_level, :issues_access_level, :merge_requests_access_level, :membership, :admin_mode, :expected_count
|
||||
def permission_table_for_milestone_access
|
||||
:public | :enabled | :enabled | :admin | 1
|
||||
:public | :enabled | :enabled | :reporter | 1
|
||||
:public | :enabled | :enabled | :guest | 1
|
||||
:public | :enabled | :enabled | :non_member | 1
|
||||
:public | :enabled | :enabled | :anonymous | 1
|
||||
:public | :enabled | :enabled | :admin | true | 1
|
||||
:public | :enabled | :enabled | :admin | false | 1
|
||||
:public | :enabled | :enabled | :reporter | nil | 1
|
||||
:public | :enabled | :enabled | :guest | nil | 1
|
||||
:public | :enabled | :enabled | :non_member | nil | 1
|
||||
:public | :enabled | :enabled | :anonymous | nil | 1
|
||||
|
||||
:public | :enabled | :private | :admin | 1
|
||||
:public | :enabled | :private | :reporter | 1
|
||||
:public | :enabled | :private | :guest | 1
|
||||
:public | :enabled | :private | :non_member | 1
|
||||
:public | :enabled | :private | :anonymous | 1
|
||||
:public | :enabled | :private | :admin | true | 1
|
||||
:public | :enabled | :private | :admin | false | 1
|
||||
:public | :enabled | :private | :reporter | nil | 1
|
||||
:public | :enabled | :private | :guest | nil | 1
|
||||
:public | :enabled | :private | :non_member | nil | 1
|
||||
:public | :enabled | :private | :anonymous | nil | 1
|
||||
|
||||
:public | :enabled | :disabled | :admin | 1
|
||||
:public | :enabled | :disabled | :reporter | 1
|
||||
:public | :enabled | :disabled | :guest | 1
|
||||
:public | :enabled | :disabled | :non_member | 1
|
||||
:public | :enabled | :disabled | :anonymous | 1
|
||||
:public | :enabled | :disabled | :admin | true | 1
|
||||
:public | :enabled | :disabled | :admin | false | 1
|
||||
:public | :enabled | :disabled | :reporter | nil | 1
|
||||
:public | :enabled | :disabled | :guest | nil | 1
|
||||
:public | :enabled | :disabled | :non_member | nil | 1
|
||||
:public | :enabled | :disabled | :anonymous | nil | 1
|
||||
|
||||
:public | :private | :enabled | :admin | 1
|
||||
:public | :private | :enabled | :reporter | 1
|
||||
:public | :private | :enabled | :guest | 1
|
||||
:public | :private | :enabled | :non_member | 1
|
||||
:public | :private | :enabled | :anonymous | 1
|
||||
:public | :private | :enabled | :admin | true | 1
|
||||
:public | :private | :enabled | :admin | false | 1
|
||||
:public | :private | :enabled | :reporter | nil | 1
|
||||
:public | :private | :enabled | :guest | nil | 1
|
||||
:public | :private | :enabled | :non_member | nil | 1
|
||||
:public | :private | :enabled | :anonymous | nil | 1
|
||||
|
||||
:public | :private | :private | :admin | 1
|
||||
:public | :private | :private | :reporter | 1
|
||||
:public | :private | :private | :guest | 1
|
||||
:public | :private | :private | :non_member | 0
|
||||
:public | :private | :private | :anonymous | 0
|
||||
:public | :private | :private | :admin | true | 1
|
||||
:public | :private | :private | :admin | false | 0
|
||||
:public | :private | :private | :reporter | nil | 1
|
||||
:public | :private | :private | :guest | nil | 1
|
||||
:public | :private | :private | :non_member | nil | 0
|
||||
:public | :private | :private | :anonymous | nil | 0
|
||||
|
||||
:public | :private | :disabled | :admin | 1
|
||||
:public | :private | :disabled | :reporter | 1
|
||||
:public | :private | :disabled | :guest | 1
|
||||
:public | :private | :disabled | :non_member | 0
|
||||
:public | :private | :disabled | :anonymous | 0
|
||||
:public | :private | :disabled | :admin | true | 1
|
||||
:public | :private | :disabled | :admin | false | 0
|
||||
:public | :private | :disabled | :reporter | nil | 1
|
||||
:public | :private | :disabled | :guest | nil | 1
|
||||
:public | :private | :disabled | :non_member | nil | 0
|
||||
:public | :private | :disabled | :anonymous | nil | 0
|
||||
|
||||
:public | :disabled | :enabled | :admin | 1
|
||||
:public | :disabled | :enabled | :reporter | 1
|
||||
:public | :disabled | :enabled | :guest | 1
|
||||
:public | :disabled | :enabled | :non_member | 1
|
||||
:public | :disabled | :enabled | :anonymous | 1
|
||||
:public | :disabled | :enabled | :admin | true | 1
|
||||
:public | :disabled | :enabled | :admin | false | 1
|
||||
:public | :disabled | :enabled | :reporter | nil | 1
|
||||
:public | :disabled | :enabled | :guest | nil | 1
|
||||
:public | :disabled | :enabled | :non_member | nil | 1
|
||||
:public | :disabled | :enabled | :anonymous | nil | 1
|
||||
|
||||
:public | :disabled | :private | :admin | 1
|
||||
:public | :disabled | :private | :reporter | 1
|
||||
:public | :disabled | :private | :guest | 0
|
||||
:public | :disabled | :private | :non_member | 0
|
||||
:public | :disabled | :private | :anonymous | 0
|
||||
:public | :disabled | :private | :admin | true | 1
|
||||
:public | :disabled | :private | :admin | false | 0
|
||||
:public | :disabled | :private | :reporter | nil | 1
|
||||
:public | :disabled | :private | :guest | nil | 0
|
||||
:public | :disabled | :private | :non_member | nil | 0
|
||||
:public | :disabled | :private | :anonymous | nil | 0
|
||||
|
||||
:public | :disabled | :disabled | :reporter | 0
|
||||
:public | :disabled | :disabled | :guest | 0
|
||||
:public | :disabled | :disabled | :non_member | 0
|
||||
:public | :disabled | :disabled | :anonymous | 0
|
||||
:public | :disabled | :disabled | :reporter | nil | 0
|
||||
:public | :disabled | :disabled | :guest | nil | 0
|
||||
:public | :disabled | :disabled | :non_member | nil | 0
|
||||
:public | :disabled | :disabled | :anonymous | nil | 0
|
||||
|
||||
:internal | :enabled | :enabled | :admin | 1
|
||||
:internal | :enabled | :enabled | :reporter | 1
|
||||
:internal | :enabled | :enabled | :guest | 1
|
||||
:internal | :enabled | :enabled | :non_member | 1
|
||||
:internal | :enabled | :enabled | :anonymous | 0
|
||||
:internal | :enabled | :enabled | :admin | true | 1
|
||||
:internal | :enabled | :enabled | :admin | false | 1
|
||||
:internal | :enabled | :enabled | :reporter | nil | 1
|
||||
:internal | :enabled | :enabled | :guest | nil | 1
|
||||
:internal | :enabled | :enabled | :non_member | nil | 1
|
||||
:internal | :enabled | :enabled | :anonymous | nil | 0
|
||||
|
||||
:internal | :enabled | :private | :admin | 1
|
||||
:internal | :enabled | :private | :reporter | 1
|
||||
:internal | :enabled | :private | :guest | 1
|
||||
:internal | :enabled | :private | :non_member | 1
|
||||
:internal | :enabled | :private | :anonymous | 0
|
||||
:internal | :enabled | :private | :admin | true | 1
|
||||
:internal | :enabled | :private | :admin | false | 1
|
||||
:internal | :enabled | :private | :reporter | nil | 1
|
||||
:internal | :enabled | :private | :guest | nil | 1
|
||||
:internal | :enabled | :private | :non_member | nil | 1
|
||||
:internal | :enabled | :private | :anonymous | nil | 0
|
||||
|
||||
:internal | :enabled | :disabled | :admin | 1
|
||||
:internal | :enabled | :disabled | :reporter | 1
|
||||
:internal | :enabled | :disabled | :guest | 1
|
||||
:internal | :enabled | :disabled | :non_member | 1
|
||||
:internal | :enabled | :disabled | :anonymous | 0
|
||||
:internal | :enabled | :disabled | :admin | true | 1
|
||||
:internal | :enabled | :disabled | :admin | false | 1
|
||||
:internal | :enabled | :disabled | :reporter | nil | 1
|
||||
:internal | :enabled | :disabled | :guest | nil | 1
|
||||
:internal | :enabled | :disabled | :non_member | nil | 1
|
||||
:internal | :enabled | :disabled | :anonymous | nil | 0
|
||||
|
||||
:internal | :private | :enabled | :admin | 1
|
||||
:internal | :private | :enabled | :reporter | 1
|
||||
:internal | :private | :enabled | :guest | 1
|
||||
:internal | :private | :enabled | :non_member | 1
|
||||
:internal | :private | :enabled | :anonymous | 0
|
||||
:internal | :private | :enabled | :admin | true | 1
|
||||
:internal | :private | :enabled | :admin | false | 1
|
||||
:internal | :private | :enabled | :reporter | nil | 1
|
||||
:internal | :private | :enabled | :guest | nil | 1
|
||||
:internal | :private | :enabled | :non_member | nil | 1
|
||||
:internal | :private | :enabled | :anonymous | nil | 0
|
||||
|
||||
:internal | :private | :private | :admin | 1
|
||||
:internal | :private | :private | :reporter | 1
|
||||
:internal | :private | :private | :guest | 1
|
||||
:internal | :private | :private | :non_member | 0
|
||||
:internal | :private | :private | :anonymous | 0
|
||||
:internal | :private | :private | :admin | true | 1
|
||||
:internal | :private | :private | :admin | false | 0
|
||||
:internal | :private | :private | :reporter | nil | 1
|
||||
:internal | :private | :private | :guest | nil | 1
|
||||
:internal | :private | :private | :non_member | nil | 0
|
||||
:internal | :private | :private | :anonymous | nil | 0
|
||||
|
||||
:internal | :private | :disabled | :admin | 1
|
||||
:internal | :private | :disabled | :reporter | 1
|
||||
:internal | :private | :disabled | :guest | 1
|
||||
:internal | :private | :disabled | :non_member | 0
|
||||
:internal | :private | :disabled | :anonymous | 0
|
||||
:internal | :private | :disabled | :admin | true | 1
|
||||
:internal | :private | :disabled | :admin | false | 0
|
||||
:internal | :private | :disabled | :reporter | nil | 1
|
||||
:internal | :private | :disabled | :guest | nil | 1
|
||||
:internal | :private | :disabled | :non_member | nil | 0
|
||||
:internal | :private | :disabled | :anonymous | nil | 0
|
||||
|
||||
:internal | :disabled | :enabled | :admin | 1
|
||||
:internal | :disabled | :enabled | :reporter | 1
|
||||
:internal | :disabled | :enabled | :guest | 1
|
||||
:internal | :disabled | :enabled | :non_member | 1
|
||||
:internal | :disabled | :enabled | :anonymous | 0
|
||||
:internal | :disabled | :enabled | :admin | true | 1
|
||||
:internal | :disabled | :enabled | :admin | false | 1
|
||||
:internal | :disabled | :enabled | :reporter | nil | 1
|
||||
:internal | :disabled | :enabled | :guest | nil | 1
|
||||
:internal | :disabled | :enabled | :non_member | nil | 1
|
||||
:internal | :disabled | :enabled | :anonymous | nil | 0
|
||||
|
||||
:internal | :disabled | :private | :admin | 1
|
||||
:internal | :disabled | :private | :reporter | 1
|
||||
:internal | :disabled | :private | :guest | 0
|
||||
:internal | :disabled | :private | :non_member | 0
|
||||
:internal | :disabled | :private | :anonymous | 0
|
||||
:internal | :disabled | :private | :admin | true | 1
|
||||
:internal | :disabled | :private | :admin | false | 0
|
||||
:internal | :disabled | :private | :reporter | nil | 1
|
||||
:internal | :disabled | :private | :guest | nil | 0
|
||||
:internal | :disabled | :private | :non_member | nil | 0
|
||||
:internal | :disabled | :private | :anonymous | nil | 0
|
||||
|
||||
:internal | :disabled | :disabled | :reporter | 0
|
||||
:internal | :disabled | :disabled | :guest | 0
|
||||
:internal | :disabled | :disabled | :non_member | 0
|
||||
:internal | :disabled | :disabled | :anonymous | 0
|
||||
:internal | :disabled | :disabled | :reporter | nil | 0
|
||||
:internal | :disabled | :disabled | :guest | nil | 0
|
||||
:internal | :disabled | :disabled | :non_member | nil | 0
|
||||
:internal | :disabled | :disabled | :anonymous | nil | 0
|
||||
|
||||
:private | :private | :private | :admin | 1
|
||||
:private | :private | :private | :reporter | 1
|
||||
:private | :private | :private | :guest | 1
|
||||
:private | :private | :private | :non_member | 0
|
||||
:private | :private | :private | :anonymous | 0
|
||||
:private | :private | :private | :admin | true | 1
|
||||
:private | :private | :private | :admin | false | 0
|
||||
:private | :private | :private | :reporter | nil | 1
|
||||
:private | :private | :private | :guest | nil | 1
|
||||
:private | :private | :private | :non_member | nil | 0
|
||||
:private | :private | :private | :anonymous | nil | 0
|
||||
|
||||
:private | :private | :disabled | :admin | 1
|
||||
:private | :private | :disabled | :reporter | 1
|
||||
:private | :private | :disabled | :guest | 1
|
||||
:private | :private | :disabled | :non_member | 0
|
||||
:private | :private | :disabled | :anonymous | 0
|
||||
:private | :private | :disabled | :admin | true | 1
|
||||
:private | :private | :disabled | :admin | false | 0
|
||||
:private | :private | :disabled | :reporter | nil | 1
|
||||
:private | :private | :disabled | :guest | nil | 1
|
||||
:private | :private | :disabled | :non_member | nil | 0
|
||||
:private | :private | :disabled | :anonymous | nil | 0
|
||||
|
||||
:private | :disabled | :private | :admin | 1
|
||||
:private | :disabled | :private | :reporter | 1
|
||||
:private | :disabled | :private | :guest | 0
|
||||
:private | :disabled | :private | :non_member | 0
|
||||
:private | :disabled | :private | :anonymous | 0
|
||||
:private | :disabled | :private | :admin | true | 1
|
||||
:private | :disabled | :private | :admin | false | 0
|
||||
:private | :disabled | :private | :reporter | nil | 1
|
||||
:private | :disabled | :private | :guest | nil | 0
|
||||
:private | :disabled | :private | :non_member | nil | 0
|
||||
:private | :disabled | :private | :anonymous | nil | 0
|
||||
|
||||
:private | :disabled | :disabled | :reporter | 0
|
||||
:private | :disabled | :disabled | :guest | 0
|
||||
:private | :disabled | :disabled | :non_member | 0
|
||||
:private | :disabled | :disabled | :anonymous | 0
|
||||
:private | :disabled | :disabled | :reporter | nil | 0
|
||||
:private | :disabled | :disabled | :guest | nil | 0
|
||||
:private | :disabled | :disabled | :non_member | nil | 0
|
||||
:private | :disabled | :disabled | :anonymous | nil | 0
|
||||
end
|
||||
|
||||
# :project_level, :membership, :expected_count
|
||||
|
@ -321,166 +358,192 @@ RSpec.shared_context 'ProjectPolicyTable context' do
|
|||
|
||||
# :snippet_level, :project_level, :feature_access_level, :membership, :expected_count
|
||||
def permission_table_for_project_snippet_access
|
||||
:public | :public | :enabled | :admin | 1
|
||||
:public | :public | :enabled | :reporter | 1
|
||||
:public | :public | :enabled | :guest | 1
|
||||
:public | :public | :enabled | :non_member | 1
|
||||
:public | :public | :enabled | :anonymous | 1
|
||||
:public | :public | :enabled | :admin | true | 1
|
||||
:public | :public | :enabled | :admin | false | 1
|
||||
:public | :public | :enabled | :reporter | nil | 1
|
||||
:public | :public | :enabled | :guest | nil | 1
|
||||
:public | :public | :enabled | :non_member | nil | 1
|
||||
:public | :public | :enabled | :anonymous | nil | 1
|
||||
|
||||
:public | :public | :private | :admin | 1
|
||||
:public | :public | :private | :reporter | 1
|
||||
:public | :public | :private | :guest | 1
|
||||
:public | :public | :private | :non_member | 0
|
||||
:public | :public | :private | :anonymous | 0
|
||||
:public | :public | :private | :admin | true | 1
|
||||
:public | :public | :private | :admin | false | 0
|
||||
:public | :public | :private | :reporter | nil | 1
|
||||
:public | :public | :private | :guest | nil | 1
|
||||
:public | :public | :private | :non_member | nil | 0
|
||||
:public | :public | :private | :anonymous | nil | 0
|
||||
|
||||
:public | :public | :disabled | :admin | 1
|
||||
:public | :public | :disabled | :reporter | 0
|
||||
:public | :public | :disabled | :guest | 0
|
||||
:public | :public | :disabled | :non_member | 0
|
||||
:public | :public | :disabled | :anonymous | 0
|
||||
:public | :public | :disabled | :admin | true | 1
|
||||
:public | :public | :disabled | :admin | false | 0
|
||||
:public | :public | :disabled | :reporter | nil | 0
|
||||
:public | :public | :disabled | :guest | nil | 0
|
||||
:public | :public | :disabled | :non_member | nil | 0
|
||||
:public | :public | :disabled | :anonymous | nil | 0
|
||||
|
||||
:public | :internal | :enabled | :admin | 1
|
||||
:public | :internal | :enabled | :reporter | 1
|
||||
:public | :internal | :enabled | :guest | 1
|
||||
:public | :internal | :enabled | :non_member | 1
|
||||
:public | :internal | :enabled | :anonymous | 0
|
||||
:public | :internal | :enabled | :admin | true | 1
|
||||
:public | :internal | :enabled | :admin | false | 1
|
||||
:public | :internal | :enabled | :reporter | nil | 1
|
||||
:public | :internal | :enabled | :guest | nil | 1
|
||||
:public | :internal | :enabled | :non_member | nil | 1
|
||||
:public | :internal | :enabled | :anonymous | nil | 0
|
||||
|
||||
:public | :internal | :private | :admin | 1
|
||||
:public | :internal | :private | :reporter | 1
|
||||
:public | :internal | :private | :guest | 1
|
||||
:public | :internal | :private | :non_member | 0
|
||||
:public | :internal | :private | :anonymous | 0
|
||||
:public | :internal | :private | :admin | true | 1
|
||||
:public | :internal | :private | :admin | false | 0
|
||||
:public | :internal | :private | :reporter | nil | 1
|
||||
:public | :internal | :private | :guest | nil | 1
|
||||
:public | :internal | :private | :non_member | nil | 0
|
||||
:public | :internal | :private | :anonymous | nil | 0
|
||||
|
||||
:public | :internal | :disabled | :admin | 1
|
||||
:public | :internal | :disabled | :reporter | 0
|
||||
:public | :internal | :disabled | :guest | 0
|
||||
:public | :internal | :disabled | :non_member | 0
|
||||
:public | :internal | :disabled | :anonymous | 0
|
||||
:public | :internal | :disabled | :admin | true | 1
|
||||
:public | :internal | :disabled | :admin | false | 0
|
||||
:public | :internal | :disabled | :reporter | nil | 0
|
||||
:public | :internal | :disabled | :guest | nil | 0
|
||||
:public | :internal | :disabled | :non_member | nil | 0
|
||||
:public | :internal | :disabled | :anonymous | nil | 0
|
||||
|
||||
:public | :private | :private | :admin | 1
|
||||
:public | :private | :private | :reporter | 1
|
||||
:public | :private | :private | :guest | 1
|
||||
:public | :private | :private | :non_member | 0
|
||||
:public | :private | :private | :anonymous | 0
|
||||
:public | :private | :private | :admin | true | 1
|
||||
:public | :private | :private | :admin | false | 0
|
||||
:public | :private | :private | :reporter | nil | 1
|
||||
:public | :private | :private | :guest | nil | 1
|
||||
:public | :private | :private | :non_member | nil | 0
|
||||
:public | :private | :private | :anonymous | nil | 0
|
||||
|
||||
:public | :private | :disabled | :reporter | 0
|
||||
:public | :private | :disabled | :guest | 0
|
||||
:public | :private | :disabled | :non_member | 0
|
||||
:public | :private | :disabled | :anonymous | 0
|
||||
:public | :private | :disabled | :reporter | nil | 0
|
||||
:public | :private | :disabled | :guest | nil | 0
|
||||
:public | :private | :disabled | :non_member | nil | 0
|
||||
:public | :private | :disabled | :anonymous | nil | 0
|
||||
|
||||
:internal | :public | :enabled | :admin | 1
|
||||
:internal | :public | :enabled | :reporter | 1
|
||||
:internal | :public | :enabled | :guest | 1
|
||||
:internal | :public | :enabled | :non_member | 1
|
||||
:internal | :public | :enabled | :anonymous | 0
|
||||
:internal | :public | :enabled | :admin | true | 1
|
||||
:internal | :public | :enabled | :admin | false | 1
|
||||
:internal | :public | :enabled | :reporter | nil | 1
|
||||
:internal | :public | :enabled | :guest | nil | 1
|
||||
:internal | :public | :enabled | :non_member | nil | 1
|
||||
:internal | :public | :enabled | :anonymous | nil | 0
|
||||
|
||||
:internal | :public | :private | :admin | 1
|
||||
:internal | :public | :private | :reporter | 1
|
||||
:internal | :public | :private | :guest | 1
|
||||
:internal | :public | :private | :non_member | 0
|
||||
:internal | :public | :private | :anonymous | 0
|
||||
:internal | :public | :private | :admin | true | 1
|
||||
:internal | :public | :private | :admin | false | 0
|
||||
:internal | :public | :private | :reporter | nil | 1
|
||||
:internal | :public | :private | :guest | nil | 1
|
||||
:internal | :public | :private | :non_member | nil | 0
|
||||
:internal | :public | :private | :anonymous | nil | 0
|
||||
|
||||
:internal | :public | :disabled | :admin | 1
|
||||
:internal | :public | :disabled | :reporter | 0
|
||||
:internal | :public | :disabled | :guest | 0
|
||||
:internal | :public | :disabled | :non_member | 0
|
||||
:internal | :public | :disabled | :anonymous | 0
|
||||
:internal | :public | :disabled | :admin | true | 1
|
||||
:internal | :public | :disabled | :admin | false | 0
|
||||
:internal | :public | :disabled | :reporter | nil | 0
|
||||
:internal | :public | :disabled | :guest | nil | 0
|
||||
:internal | :public | :disabled | :non_member | nil | 0
|
||||
:internal | :public | :disabled | :anonymous | nil | 0
|
||||
|
||||
:internal | :internal | :enabled | :admin | 1
|
||||
:internal | :internal | :enabled | :reporter | 1
|
||||
:internal | :internal | :enabled | :guest | 1
|
||||
:internal | :internal | :enabled | :non_member | 1
|
||||
:internal | :internal | :enabled | :anonymous | 0
|
||||
:internal | :internal | :enabled | :admin | true | 1
|
||||
:internal | :internal | :enabled | :admin | false | 1
|
||||
:internal | :internal | :enabled | :reporter | nil | 1
|
||||
:internal | :internal | :enabled | :guest | nil | 1
|
||||
:internal | :internal | :enabled | :non_member | nil | 1
|
||||
:internal | :internal | :enabled | :anonymous | nil | 0
|
||||
|
||||
:internal | :internal | :private | :admin | 1
|
||||
:internal | :internal | :private | :reporter | 1
|
||||
:internal | :internal | :private | :guest | 1
|
||||
:internal | :internal | :private | :non_member | 0
|
||||
:internal | :internal | :private | :anonymous | 0
|
||||
:internal | :internal | :private | :admin | true | 1
|
||||
:internal | :internal | :private | :admin | false | 0
|
||||
:internal | :internal | :private | :reporter | nil | 1
|
||||
:internal | :internal | :private | :guest | nil | 1
|
||||
:internal | :internal | :private | :non_member | nil | 0
|
||||
:internal | :internal | :private | :anonymous | nil | 0
|
||||
|
||||
:internal | :internal | :disabled | :admin | 1
|
||||
:internal | :internal | :disabled | :reporter | 0
|
||||
:internal | :internal | :disabled | :guest | 0
|
||||
:internal | :internal | :disabled | :non_member | 0
|
||||
:internal | :internal | :disabled | :anonymous | 0
|
||||
:internal | :internal | :disabled | :admin | true | 1
|
||||
:internal | :internal | :disabled | :admin | false | 0
|
||||
:internal | :internal | :disabled | :reporter | nil | 0
|
||||
:internal | :internal | :disabled | :guest | nil | 0
|
||||
:internal | :internal | :disabled | :non_member | nil | 0
|
||||
:internal | :internal | :disabled | :anonymous | nil | 0
|
||||
|
||||
:internal | :private | :private | :admin | 1
|
||||
:internal | :private | :private | :reporter | 1
|
||||
:internal | :private | :private | :guest | 1
|
||||
:internal | :private | :private | :non_member | 0
|
||||
:internal | :private | :private | :anonymous | 0
|
||||
:internal | :private | :private | :admin | true | 1
|
||||
:internal | :private | :private | :admin | false | 0
|
||||
:internal | :private | :private | :reporter | nil | 1
|
||||
:internal | :private | :private | :guest | nil | 1
|
||||
:internal | :private | :private | :non_member | nil | 0
|
||||
:internal | :private | :private | :anonymous | nil | 0
|
||||
|
||||
:internal | :private | :disabled | :admin | 1
|
||||
:internal | :private | :disabled | :reporter | 0
|
||||
:internal | :private | :disabled | :guest | 0
|
||||
:internal | :private | :disabled | :non_member | 0
|
||||
:internal | :private | :disabled | :anonymous | 0
|
||||
:internal | :private | :disabled | :admin | true | 1
|
||||
:internal | :private | :disabled | :admin | false | 0
|
||||
:internal | :private | :disabled | :reporter | nil | 0
|
||||
:internal | :private | :disabled | :guest | nil | 0
|
||||
:internal | :private | :disabled | :non_member | nil | 0
|
||||
:internal | :private | :disabled | :anonymous | nil | 0
|
||||
|
||||
:private | :public | :enabled | :admin | 1
|
||||
:private | :public | :enabled | :reporter | 1
|
||||
:private | :public | :enabled | :guest | 1
|
||||
:private | :public | :enabled | :non_member | 0
|
||||
:private | :public | :enabled | :anonymous | 0
|
||||
:private | :public | :enabled | :admin | true | 1
|
||||
:private | :public | :enabled | :admin | false | 0
|
||||
:private | :public | :enabled | :reporter | nil | 1
|
||||
:private | :public | :enabled | :guest | nil | 1
|
||||
:private | :public | :enabled | :non_member | nil | 0
|
||||
:private | :public | :enabled | :anonymous | nil | 0
|
||||
|
||||
:private | :public | :private | :admin | 1
|
||||
:private | :public | :private | :reporter | 1
|
||||
:private | :public | :private | :guest | 1
|
||||
:private | :public | :private | :non_member | 0
|
||||
:private | :public | :private | :anonymous | 0
|
||||
:private | :public | :private | :admin | true | 1
|
||||
:private | :public | :private | :admin | false | 0
|
||||
:private | :public | :private | :reporter | nil | 1
|
||||
:private | :public | :private | :guest | nil | 1
|
||||
:private | :public | :private | :non_member | nil | 0
|
||||
:private | :public | :private | :anonymous | nil | 0
|
||||
|
||||
:private | :public | :disabled | :admin | 1
|
||||
:private | :public | :disabled | :reporter | 0
|
||||
:private | :public | :disabled | :guest | 0
|
||||
:private | :public | :disabled | :non_member | 0
|
||||
:private | :public | :disabled | :anonymous | 0
|
||||
:private | :public | :disabled | :admin | true | 1
|
||||
:private | :public | :disabled | :admin | false | 0
|
||||
:private | :public | :disabled | :reporter | nil | 0
|
||||
:private | :public | :disabled | :guest | nil | 0
|
||||
:private | :public | :disabled | :non_member | nil | 0
|
||||
:private | :public | :disabled | :anonymous | nil | 0
|
||||
|
||||
:private | :internal | :enabled | :admin | 1
|
||||
:private | :internal | :enabled | :reporter | 1
|
||||
:private | :internal | :enabled | :guest | 1
|
||||
:private | :internal | :enabled | :non_member | 0
|
||||
:private | :internal | :enabled | :anonymous | 0
|
||||
:private | :internal | :enabled | :admin | true | 1
|
||||
:private | :internal | :enabled | :admin | false | 0
|
||||
:private | :internal | :enabled | :reporter | nil | 1
|
||||
:private | :internal | :enabled | :guest | nil | 1
|
||||
:private | :internal | :enabled | :non_member | nil | 0
|
||||
:private | :internal | :enabled | :anonymous | nil | 0
|
||||
|
||||
:private | :internal | :private | :admin | 1
|
||||
:private | :internal | :private | :reporter | 1
|
||||
:private | :internal | :private | :guest | 1
|
||||
:private | :internal | :private | :non_member | 0
|
||||
:private | :internal | :private | :anonymous | 0
|
||||
:private | :internal | :private | :admin | true | 1
|
||||
:private | :internal | :private | :admin | false | 0
|
||||
:private | :internal | :private | :reporter | nil | 1
|
||||
:private | :internal | :private | :guest | nil | 1
|
||||
:private | :internal | :private | :non_member | nil | 0
|
||||
:private | :internal | :private | :anonymous | nil | 0
|
||||
|
||||
:private | :internal | :disabled | :admin | 1
|
||||
:private | :internal | :disabled | :reporter | 0
|
||||
:private | :internal | :disabled | :guest | 0
|
||||
:private | :internal | :disabled | :non_member | 0
|
||||
:private | :internal | :disabled | :anonymous | 0
|
||||
:private | :internal | :disabled | :admin | true | 1
|
||||
:private | :internal | :disabled | :admin | false | 0
|
||||
:private | :internal | :disabled | :reporter | nil | 0
|
||||
:private | :internal | :disabled | :guest | nil | 0
|
||||
:private | :internal | :disabled | :non_member | nil | 0
|
||||
:private | :internal | :disabled | :anonymous | nil | 0
|
||||
|
||||
:private | :private | :private | :admin | 1
|
||||
:private | :private | :private | :reporter | 1
|
||||
:private | :private | :private | :guest | 1
|
||||
:private | :private | :private | :non_member | 0
|
||||
:private | :private | :private | :anonymous | 0
|
||||
:private | :private | :private | :admin | true | 1
|
||||
:private | :private | :private | :admin | false | 0
|
||||
:private | :private | :private | :reporter | nil | 1
|
||||
:private | :private | :private | :guest | nil | 1
|
||||
:private | :private | :private | :non_member | nil | 0
|
||||
:private | :private | :private | :anonymous | nil | 0
|
||||
|
||||
:private | :private | :disabled | :admin | 1
|
||||
:private | :private | :disabled | :reporter | 0
|
||||
:private | :private | :disabled | :guest | 0
|
||||
:private | :private | :disabled | :non_member | 0
|
||||
:private | :private | :disabled | :anonymous | 0
|
||||
:private | :private | :disabled | :admin | true | 1
|
||||
:private | :private | :disabled | :admin | false | 0
|
||||
:private | :private | :disabled | :reporter | nil | 0
|
||||
:private | :private | :disabled | :guest | nil | 0
|
||||
:private | :private | :disabled | :non_member | nil | 0
|
||||
:private | :private | :disabled | :anonymous | nil | 0
|
||||
end
|
||||
|
||||
# :snippet_level, :membership, :expected_count
|
||||
def permission_table_for_personal_snippet_access
|
||||
:public | :admin | 1
|
||||
:public | :author | 1
|
||||
:public | :non_member | 1
|
||||
:public | :anonymous | 1
|
||||
:public | :admin | true | 1
|
||||
:public | :admin | false | 1
|
||||
:public | :author | nil | 1
|
||||
:public | :non_member | nil | 1
|
||||
:public | :anonymous | nil | 1
|
||||
|
||||
:internal | :admin | 1
|
||||
:internal | :author | 1
|
||||
:internal | :non_member | 1
|
||||
:internal | :anonymous | 0
|
||||
:internal | :admin | true | 1
|
||||
:internal | :admin | false | 1
|
||||
:internal | :author | nil | 1
|
||||
:internal | :non_member | nil | 1
|
||||
:internal | :anonymous | nil | 0
|
||||
|
||||
:private | :admin | 1
|
||||
:private | :author | 1
|
||||
:private | :non_member | 0
|
||||
:private | :anonymous | 0
|
||||
:private | :admin | true | 1
|
||||
:private | :admin | false | 0
|
||||
:private | :author | nil | 1
|
||||
:private | :non_member | nil | 0
|
||||
:private | :anonymous | nil | 0
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
end
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_context 'npm api setup' do
|
||||
include PackagesManagerApiSpecHelpers
|
||||
include HttpBasicAuthHelpers
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project, reload: true) { create(:project, :public, namespace: group) }
|
||||
let_it_be(:package, reload: true) { create(:npm_package, project: project) }
|
||||
let_it_be(:token) { create(:oauth_access_token, scopes: 'api', resource_owner: user) }
|
||||
let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
|
||||
let_it_be(:job, reload: true) { create(:ci_build, user: user, status: :running) }
|
||||
let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) }
|
||||
let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) }
|
||||
|
||||
let(:package_name) { package.name }
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,270 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'handling get metadata requests' do
|
||||
let_it_be(:package_dependency_link1) { create(:packages_dependency_link, package: package, dependency_type: :dependencies) }
|
||||
let_it_be(:package_dependency_link2) { create(:packages_dependency_link, package: package, dependency_type: :devDependencies) }
|
||||
let_it_be(:package_dependency_link3) { create(:packages_dependency_link, package: package, dependency_type: :bundleDependencies) }
|
||||
let_it_be(:package_dependency_link4) { create(:packages_dependency_link, package: package, dependency_type: :peerDependencies) }
|
||||
|
||||
let(:params) { {} }
|
||||
let(:headers) { {} }
|
||||
|
||||
subject { get(url, params: params, headers: headers) }
|
||||
|
||||
shared_examples 'returning the npm package info' do
|
||||
it 'returns the package info' do
|
||||
subject
|
||||
|
||||
expect_a_valid_package_response
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'a package that requires auth' do
|
||||
it 'denies request without oauth token' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
|
||||
context 'with oauth token' do
|
||||
let(:params) { { access_token: token.token } }
|
||||
|
||||
it 'returns the package info with oauth token' do
|
||||
subject
|
||||
|
||||
expect_a_valid_package_response
|
||||
end
|
||||
end
|
||||
|
||||
context 'with job token' do
|
||||
let(:params) { { job_token: job.token } }
|
||||
|
||||
it 'returns the package info with running job token' do
|
||||
subject
|
||||
|
||||
expect_a_valid_package_response
|
||||
end
|
||||
|
||||
it 'denies request without running job token' do
|
||||
job.update!(status: :success)
|
||||
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with deploy token' do
|
||||
let(:headers) { build_token_auth_header(deploy_token.token) }
|
||||
|
||||
it 'returns the package info with deploy token' do
|
||||
subject
|
||||
|
||||
expect_a_valid_package_response
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'a public project' do
|
||||
it_behaves_like 'returning the npm package info'
|
||||
|
||||
context 'project path with a dot' do
|
||||
before do
|
||||
project.update!(path: 'foo.bar')
|
||||
end
|
||||
|
||||
it_behaves_like 'returning the npm package info'
|
||||
end
|
||||
|
||||
context 'with request forward disabled' do
|
||||
before do
|
||||
stub_application_setting(npm_package_requests_forwarding: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'returning the npm package info'
|
||||
|
||||
context 'with unknown package' do
|
||||
let(:package_name) { 'unknown' }
|
||||
|
||||
it 'returns the proper response' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with request forward enabled' do
|
||||
before do
|
||||
stub_application_setting(npm_package_requests_forwarding: true)
|
||||
end
|
||||
|
||||
it_behaves_like 'returning the npm package info'
|
||||
|
||||
context 'with unknown package' do
|
||||
let(:package_name) { 'unknown' }
|
||||
|
||||
it 'returns a redirect' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
expect(response.headers['Location']).to eq('https://registry.npmjs.org/unknown')
|
||||
end
|
||||
|
||||
it_behaves_like 'a gitlab tracking event', described_class.name, 'npm_request_forward'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'internal project' do
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
|
||||
end
|
||||
|
||||
it_behaves_like 'a package that requires auth'
|
||||
end
|
||||
|
||||
context 'private project' do
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
|
||||
end
|
||||
|
||||
it_behaves_like 'a package that requires auth'
|
||||
|
||||
context 'with guest' do
|
||||
let(:params) { { access_token: token.token } }
|
||||
|
||||
it 'denies request when not enough permissions' do
|
||||
project.add_guest(user)
|
||||
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def expect_a_valid_package_response
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.media_type).to eq('application/json')
|
||||
expect(response).to match_response_schema('public_api/v4/packages/npm_package')
|
||||
expect(json_response['name']).to eq(package.name)
|
||||
expect(json_response['versions'][package.version]).to match_schema('public_api/v4/packages/npm_package_version')
|
||||
::Packages::Npm::PackagePresenter::NPM_VALID_DEPENDENCY_TYPES.each do |dependency_type|
|
||||
expect(json_response.dig('versions', package.version, dependency_type.to_s)).to be_any
|
||||
end
|
||||
expect(json_response['dist-tags']).to match_schema('public_api/v4/packages/npm_package_tags')
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'handling get dist tags requests' do
|
||||
let_it_be(:package_tag1) { create(:packages_tag, package: package) }
|
||||
let_it_be(:package_tag2) { create(:packages_tag, package: package) }
|
||||
|
||||
let(:params) { {} }
|
||||
|
||||
subject { get(url, params: params) }
|
||||
|
||||
context 'with public project' do
|
||||
context 'with authenticated user' do
|
||||
let(:params) { { private_token: personal_access_token.token } }
|
||||
|
||||
it_behaves_like 'returns package tags', :maintainer
|
||||
it_behaves_like 'returns package tags', :developer
|
||||
it_behaves_like 'returns package tags', :reporter
|
||||
it_behaves_like 'returns package tags', :guest
|
||||
end
|
||||
|
||||
context 'with unauthenticated user' do
|
||||
it_behaves_like 'returns package tags', :no_type
|
||||
end
|
||||
end
|
||||
|
||||
context 'with private project' do
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
|
||||
end
|
||||
|
||||
context 'with authenticated user' do
|
||||
let(:params) { { private_token: personal_access_token.token } }
|
||||
|
||||
it_behaves_like 'returns package tags', :maintainer
|
||||
it_behaves_like 'returns package tags', :developer
|
||||
it_behaves_like 'returns package tags', :reporter
|
||||
it_behaves_like 'rejects package tags access', :guest, :forbidden
|
||||
end
|
||||
|
||||
context 'with unauthenticated user' do
|
||||
it_behaves_like 'rejects package tags access', :no_type, :not_found
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'handling create dist tag requests' do
|
||||
let_it_be(:tag_name) { 'test' }
|
||||
|
||||
let(:params) { {} }
|
||||
let(:env) { {} }
|
||||
let(:version) { package.version }
|
||||
|
||||
subject { put(url, env: env, params: params) }
|
||||
|
||||
context 'with public project' do
|
||||
context 'with authenticated user' do
|
||||
let(:params) { { private_token: personal_access_token.token } }
|
||||
let(:env) { { 'api.request.body': version } }
|
||||
|
||||
it_behaves_like 'create package tag', :maintainer
|
||||
it_behaves_like 'create package tag', :developer
|
||||
it_behaves_like 'rejects package tags access', :reporter, :forbidden
|
||||
it_behaves_like 'rejects package tags access', :guest, :forbidden
|
||||
end
|
||||
|
||||
context 'with unauthenticated user' do
|
||||
it_behaves_like 'rejects package tags access', :no_type, :unauthorized
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'handling delete dist tag requests' do
|
||||
let_it_be(:package_tag) { create(:packages_tag, package: package) }
|
||||
|
||||
let(:params) { {} }
|
||||
let(:tag_name) { package_tag.name }
|
||||
|
||||
subject { delete(url, params: params) }
|
||||
|
||||
context 'with public project' do
|
||||
context 'with authenticated user' do
|
||||
let(:params) { { private_token: personal_access_token.token } }
|
||||
|
||||
it_behaves_like 'delete package tag', :maintainer
|
||||
it_behaves_like 'rejects package tags access', :developer, :forbidden
|
||||
it_behaves_like 'rejects package tags access', :reporter, :forbidden
|
||||
it_behaves_like 'rejects package tags access', :guest, :forbidden
|
||||
end
|
||||
|
||||
context 'with unauthenticated user' do
|
||||
it_behaves_like 'rejects package tags access', :no_type, :unauthorized
|
||||
end
|
||||
end
|
||||
|
||||
context 'with private project' do
|
||||
before do
|
||||
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
|
||||
end
|
||||
|
||||
context 'with authenticated user' do
|
||||
let(:params) { { private_token: personal_access_token.token } }
|
||||
|
||||
it_behaves_like 'delete package tag', :maintainer
|
||||
it_behaves_like 'rejects package tags access', :developer, :forbidden
|
||||
it_behaves_like 'rejects package tags access', :reporter, :forbidden
|
||||
it_behaves_like 'rejects package tags access', :guest, :forbidden
|
||||
end
|
||||
|
||||
context 'with unauthenticated user' do
|
||||
it_behaves_like 'rejects package tags access', :no_type, :unauthorized
|
||||
end
|
||||
end
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue