Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
ab421e159d
commit
3e1f93c033
20 changed files with 308 additions and 104 deletions
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlSprintf, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
|
||||
import { GlSprintf, GlSafeHtmlDirective as SafeHtml, GlAvatarLink, GlAvatar } from '@gitlab/ui';
|
||||
import $ from 'jquery';
|
||||
import { escape, isEmpty } from 'lodash';
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
|
@ -11,7 +11,6 @@ import { ignoreWhilePending } from '~/lib/utils/ignore_while_pending';
|
|||
import { truncateSha } from '~/lib/utils/text_utility';
|
||||
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
|
||||
import { __, s__, sprintf } from '~/locale';
|
||||
import userAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
|
||||
import eventHub from '../event_hub';
|
||||
import noteable from '../mixins/noteable';
|
||||
import resolvable from '../mixins/resolvable';
|
||||
|
@ -31,11 +30,12 @@ export default {
|
|||
name: 'NoteableNote',
|
||||
components: {
|
||||
GlSprintf,
|
||||
userAvatarLink,
|
||||
noteHeader,
|
||||
noteActions,
|
||||
NoteBody,
|
||||
TimelineEntryItem,
|
||||
GlAvatarLink,
|
||||
GlAvatar,
|
||||
},
|
||||
directives: {
|
||||
SafeHtml,
|
||||
|
@ -196,13 +196,11 @@ export default {
|
|||
|
||||
return fileResolvedFromAvailableSource || null;
|
||||
},
|
||||
avatarSize() {
|
||||
// Use a different size if shown on a Merge Request Diff
|
||||
if (this.line && !this.isOverviewTab) {
|
||||
return 24;
|
||||
}
|
||||
|
||||
return 40;
|
||||
isMRDiffView() {
|
||||
return this.line && !this.isOverviewTab;
|
||||
},
|
||||
authorAvatarAdaptiveSize() {
|
||||
return { default: 24, md: 32 };
|
||||
},
|
||||
},
|
||||
created() {
|
||||
|
@ -428,19 +426,33 @@ export default {
|
|||
</template>
|
||||
</gl-sprintf>
|
||||
</div>
|
||||
<div class="timeline-icon">
|
||||
<user-avatar-link
|
||||
:link-href="author.path"
|
||||
:img-src="author.avatar_url"
|
||||
:img-alt="author.name"
|
||||
:img-size="avatarSize"
|
||||
lazy
|
||||
>
|
||||
<template #avatar-badge>
|
||||
<slot name="avatar-badge"></slot>
|
||||
</template>
|
||||
</user-avatar-link>
|
||||
|
||||
<div v-if="isMRDiffView" class="gl-float-left gl-mt-n1 gl-mr-3">
|
||||
<gl-avatar-link :href="author.path">
|
||||
<gl-avatar
|
||||
:src="author.avatar_url"
|
||||
:entity-name="author.username"
|
||||
:alt="author.name"
|
||||
:size="24"
|
||||
/>
|
||||
|
||||
<slot name="avatar-badge"></slot>
|
||||
</gl-avatar-link>
|
||||
</div>
|
||||
|
||||
<div v-else class="gl-float-left gl-pl-3 gl-mr-3 gl-md-pl-2 gl-md-pr-2">
|
||||
<gl-avatar-link :href="author.path">
|
||||
<gl-avatar
|
||||
:src="author.avatar_url"
|
||||
:entity-name="author.username"
|
||||
:alt="author.name"
|
||||
:size="authorAvatarAdaptiveSize"
|
||||
/>
|
||||
|
||||
<slot name="avatar-badge"></slot>
|
||||
</gl-avatar-link>
|
||||
</div>
|
||||
|
||||
<div class="timeline-content">
|
||||
<div class="note-header">
|
||||
<note-header
|
||||
|
|
|
@ -185,7 +185,7 @@ class Deployment < ApplicationRecord
|
|||
def self.last_deployment_group_for_environment(env)
|
||||
return self.none unless env.last_deployment_pipeline&.latest_successful_builds&.present?
|
||||
|
||||
BatchLoader.for(env).batch do |environments, loader|
|
||||
BatchLoader.for(env).batch(default_value: self.none) do |environments, loader|
|
||||
latest_successful_build_ids = []
|
||||
environments_hash = {}
|
||||
|
||||
|
|
|
@ -59,7 +59,13 @@ class ProjectPolicy < BasePolicy
|
|||
|
||||
desc "Container registry is disabled"
|
||||
condition(:container_registry_disabled, scope: :subject) do
|
||||
!access_allowed_to?(:container_registry)
|
||||
if user.is_a?(DeployToken)
|
||||
(!user.read_registry? && !user.write_registry?) ||
|
||||
user.revoked? ||
|
||||
!project.container_registry_enabled?
|
||||
else
|
||||
!access_allowed_to?(:container_registry)
|
||||
end
|
||||
end
|
||||
|
||||
desc "Container registry is enabled for everyone with access to the project"
|
||||
|
@ -88,6 +94,16 @@ class ProjectPolicy < BasePolicy
|
|||
user.is_a?(DeployKey) && user.can_push_to?(project)
|
||||
end
|
||||
|
||||
desc "Deploy token with read_container_image scope"
|
||||
condition(:read_container_image_deploy_token) do
|
||||
user.is_a?(DeployToken) && user.has_access_to?(project) && user.read_registry?
|
||||
end
|
||||
|
||||
desc "Deploy token with create_container_image scope"
|
||||
condition(:create_container_image_deploy_token) do
|
||||
user.is_a?(DeployToken) && user.has_access_to?(project) && user.write_registry?
|
||||
end
|
||||
|
||||
desc "Deploy token with read_package_registry scope"
|
||||
condition(:read_package_registry_deploy_token) do
|
||||
user.is_a?(DeployToken) && user.has_access_to?(project) && user.read_package_registry
|
||||
|
@ -698,6 +714,14 @@ class ProjectPolicy < BasePolicy
|
|||
enable :push_code
|
||||
end
|
||||
|
||||
rule { read_container_image_deploy_token }.policy do
|
||||
enable :read_container_image
|
||||
end
|
||||
|
||||
rule { create_container_image_deploy_token }.policy do
|
||||
enable :create_container_image
|
||||
end
|
||||
|
||||
rule { read_package_registry_deploy_token }.policy do
|
||||
enable :read_package
|
||||
enable :read_project
|
||||
|
|
|
@ -215,15 +215,13 @@ module Auth
|
|||
def deploy_token_can_pull?(requested_project)
|
||||
has_authentication_ability?(:read_container_image) &&
|
||||
deploy_token.present? &&
|
||||
deploy_token.has_access_to?(requested_project) &&
|
||||
deploy_token.read_registry?
|
||||
can?(deploy_token, :read_container_image, requested_project)
|
||||
end
|
||||
|
||||
def deploy_token_can_push?(requested_project)
|
||||
has_authentication_ability?(:create_container_image) &&
|
||||
deploy_token.present? &&
|
||||
deploy_token.has_access_to?(requested_project) &&
|
||||
deploy_token.write_registry?
|
||||
can?(deploy_token, :create_container_image, requested_project)
|
||||
end
|
||||
|
||||
##
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
- if Gitlab.config.packages.enabled
|
||||
%section.settings.as-package.no-animate#js-package-settings{ class: ('expanded' if expanded_by_default?) }
|
||||
.settings-header
|
||||
%h4
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _('Package Registry')
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded_by_default? ? _('Collapse') : _('Expand')
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- expanded = local_assigns.fetch(:expanded)
|
||||
|
||||
%h4
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _('Variables')
|
||||
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
%section.settings.as-ci-cd.no-animate#js-ci-cd-settings{ class: ('expanded' if expanded_by_default?) }
|
||||
.settings-header
|
||||
%h4
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _('Continuous Integration and Deployment')
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded_by_default? ? _('Collapse') : _('Expand')
|
||||
|
@ -29,7 +29,7 @@
|
|||
- if Gitlab.config.registry.enabled
|
||||
%section.settings.as-registry.no-animate#js-registry-settings{ class: ('expanded' if expanded_by_default?) }
|
||||
.settings-header
|
||||
%h4
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _('Container Registry')
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded_by_default? ? _('Collapse') : _('Expand')
|
||||
|
@ -41,7 +41,7 @@
|
|||
- if Feature.enabled?(:runner_registration_control)
|
||||
%section.settings.as-runner.no-animate#js-runner-settings{ class: ('expanded' if expanded_by_default?) }
|
||||
.settings-header
|
||||
%h4
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= s_('Runners|Runner registration')
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded_by_default? ? 'Collapse' : 'Expand'
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: container_registry_legacy_authentication_for_deploy_tokens
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests/2470
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365968
|
||||
milestone: '15.1'
|
||||
type: development
|
||||
group: group::package
|
||||
default_enabled: false
|
|
@ -9,11 +9,16 @@ class AddIndexesIssuesOnProjectIdAndClosedAt < Gitlab::Database::Migration[2.0]
|
|||
|
||||
def up
|
||||
# Index to improve performance when sorting issues by closed_at desc
|
||||
add_concurrent_index :issues, 'project_id, closed_at DESC NULLS LAST, state_id, id', name: NEW_INDEX_NAME_1
|
||||
unless index_exists_by_name?(:issues, NEW_INDEX_NAME_1)
|
||||
add_concurrent_index :issues, 'project_id, closed_at DESC NULLS LAST, state_id, id', name: NEW_INDEX_NAME_1
|
||||
end
|
||||
|
||||
# Index to improve performance when sorting issues by closed_at asc
|
||||
# This replaces the old index which didn't account for state_id and id
|
||||
add_concurrent_index :issues, [:project_id, :closed_at, :state_id, :id], name: NEW_INDEX_NAME_2
|
||||
unless index_exists_by_name?(:issues, NEW_INDEX_NAME_2)
|
||||
add_concurrent_index :issues, [:project_id, :closed_at, :state_id, :id], name: NEW_INDEX_NAME_2
|
||||
end
|
||||
|
||||
remove_concurrent_index_by_name :issues, OLD_INDEX_NAME
|
||||
end
|
||||
|
||||
|
|
|
@ -938,7 +938,7 @@ Prerequisites:
|
|||
[cloud native chart](https://docs.gitlab.com/charts/charts/registry/#garbage-collection).
|
||||
- You must set the Registry to [read-only mode](#performing-garbage-collection-without-downtime).
|
||||
Running garbage collection causes downtime for the Container Registry. When you run this command
|
||||
on an instance in an environment where another instances is still writing to the Registry storage,
|
||||
on an instance in an environment where another instance is still writing to the Registry storage,
|
||||
referenced manifests are removed.
|
||||
|
||||
### Understanding the content-addressable layers
|
||||
|
|
|
@ -88,9 +88,8 @@ This rule enforces the defined actions and schedules a scan on the provided date
|
|||
|------------|------|-----------------|-------------|
|
||||
| `type` | `string` | `schedule` | The rule's type. |
|
||||
| `branches` | `array` of `string` | `*` or the branch's name | The branch the given policy applies to (supports wildcard). |
|
||||
| `cadence` | `string` | CRON expression (for example, `0 0 * * *`) | A whitespace-separated string containing five fields that represents the scheduled time. |
|
||||
| `agents` | `object` | | The name of the [GitLab agents](../../clusters/agent/index.md) where [cluster image scanning](../../clusters/agent/vulnerabilities.md) will run. The key of the object is the name of the Kubernetes cluster configured for your project in GitLab. In the optionally provided value of the object, you can precisely select Kubernetes resources that are scanned. <!--- start_remove The following content will be removed on remove_date: '2022-08-22' --> |
|
||||
| `clusters` (removed) | `object` | | This field was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/356465) in 15.0. Use the `agents` field instead. The cluster where the given policy enforces running selected scans (only for `container_scanning`/`cluster_image_scanning` scans). The key of the object is the name of the Kubernetes cluster configured for your project in GitLab. In the optionally provided value of the object, you can precisely select Kubernetes resources that are scanned. <!--- end_remove --> |
|
||||
| `cadence` | `string` | CRON expression (for example, `0 0 * * *`) | A whitespace-separated string containing five fields that represents the scheduled time. <!--- start_remove The following content will be removed on remove_date: '2022-08-22' --> |
|
||||
| `clusters` (removed) | `object` | | This field was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/356465) in 15.0. The cluster where the given policy enforces running selected scans (only for `container_scanning`/`cluster_image_scanning` scans). The key of the object is the name of the Kubernetes cluster configured for your project in GitLab. In the optionally provided value of the object, you can precisely select Kubernetes resources that are scanned. <!--- end_remove --> |
|
||||
|
||||
GitLab supports the following types of CRON syntax for the `cadence` field:
|
||||
|
||||
|
@ -99,20 +98,11 @@ GitLab supports the following types of CRON syntax for the `cadence` field:
|
|||
|
||||
It is possible that other elements of the CRON syntax will work in the cadence field, however, GitLab does not officially test or support them.
|
||||
|
||||
### `agent` schema
|
||||
|
||||
Use this schema to define `agents` objects in the [`schedule` rule type](#schedule-rule-type).
|
||||
|
||||
| Field | Type | Possible values | Description |
|
||||
|--------------|---------------------|--------------------------|-------------|
|
||||
| `namespaces` | `array` of `string` | | The namespace that is scanned. If empty, all namespaces will be scanned. |
|
||||
|
||||
<!--- start_remove The following content will be removed on remove_date: '2022-08-22' -->
|
||||
|
||||
### `cluster` schema (removed)
|
||||
|
||||
This schema was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/356465) in 15.0.
|
||||
Use the [`agent` schema](#agent-schema) instead.
|
||||
|
||||
Use this schema to define `clusters` objects in the [`schedule` rule type](#schedule-rule-type).
|
||||
|
||||
|
@ -203,18 +193,6 @@ scan_execution_policy:
|
|||
variables:
|
||||
SAST_EXCLUDED_ANALYZERS: brakeman
|
||||
- scan: container_scanning
|
||||
- name: Enforce Cluster Image Scanning on production-cluster every 24h
|
||||
description: This policy enforces Cluster Image Scanning scan to run every 24 hours
|
||||
enabled: true
|
||||
rules:
|
||||
- type: schedule
|
||||
cadence: "15 3 * * *"
|
||||
agents:
|
||||
production-agent:
|
||||
namespaces:
|
||||
- production-namespace
|
||||
actions:
|
||||
- scan: cluster_image_scanning
|
||||
```
|
||||
|
||||
In this example:
|
||||
|
|
|
@ -35,6 +35,8 @@ module API
|
|||
name, _, format = file_name.rpartition('.')
|
||||
|
||||
if %w(md5 sha1).include?(format)
|
||||
unprocessable_entity! if Gitlab::FIPS.enabled? && format == 'md5'
|
||||
|
||||
[name, format]
|
||||
else
|
||||
[file_name, format]
|
||||
|
@ -109,6 +111,7 @@ module API
|
|||
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
|
||||
get 'packages/maven/*path/:file_name', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
|
||||
# return a similar failure to authorize_read_package!(project)
|
||||
|
||||
forbidden! unless path_exists?(params[:path])
|
||||
|
||||
file_name, format = extract_format(params[:file_name])
|
||||
|
@ -241,6 +244,7 @@ module API
|
|||
end
|
||||
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
|
||||
put ':id/packages/maven/*path/:file_name', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
|
||||
unprocessable_entity! if Gitlab::FIPS.enabled? && params['file.md5']
|
||||
authorize_upload!
|
||||
bad_request!('File is too large') if user_project.actual_limits.exceeded?(:maven_max_file_size, params[:file].size)
|
||||
|
||||
|
|
|
@ -54,4 +54,8 @@ COPY ./INSTALLATION_TYPE ./VERSION /home/gitlab/
|
|||
|
||||
COPY ./qa /home/gitlab/qa
|
||||
|
||||
# Add JH files when JH dir exist.
|
||||
COPY ./j[h]/qa /home/gitlab/jh/qa
|
||||
COPY ./j[h]/lib /home/gitlab/jh/lib
|
||||
|
||||
ENTRYPOINT ["bin/test"]
|
||||
|
|
|
@ -1,20 +1,15 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
|
||||
import { GlAvatar } from '@gitlab/ui';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
|
||||
import DiffsModule from '~/diffs/store/modules';
|
||||
|
||||
import NoteActions from '~/notes/components/note_actions.vue';
|
||||
import NoteBody from '~/notes/components/note_body.vue';
|
||||
import NoteHeader from '~/notes/components/note_header.vue';
|
||||
import issueNote from '~/notes/components/noteable_note.vue';
|
||||
import NotesModule from '~/notes/stores/modules';
|
||||
import { NOTEABLE_TYPE_MAPPING } from '~/notes/constants';
|
||||
|
||||
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
|
||||
|
||||
import { noteableDataMock, notesDataMock, note } from '../mock_data';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
@ -205,19 +200,21 @@ describe('issue_note', () => {
|
|||
|
||||
await nextTick();
|
||||
|
||||
expect(wrapper.findComponent(UserAvatarLink).props('imgSize')).toBe(24);
|
||||
const avatar = wrapper.findComponent(GlAvatar);
|
||||
const avatarProps = avatar.props();
|
||||
expect(avatarProps.size).toBe(24);
|
||||
});
|
||||
});
|
||||
|
||||
it('should render user information', () => {
|
||||
it('should render user avatar', () => {
|
||||
const { author } = note;
|
||||
const avatar = wrapper.findComponent(UserAvatarLink);
|
||||
const avatar = wrapper.findComponent(GlAvatar);
|
||||
const avatarProps = avatar.props();
|
||||
|
||||
expect(avatarProps.linkHref).toBe(author.path);
|
||||
expect(avatarProps.imgSrc).toBe(author.avatar_url);
|
||||
expect(avatarProps.imgAlt).toBe(author.name);
|
||||
expect(avatarProps.imgSize).toBe(40);
|
||||
expect(avatarProps.src).toBe(author.avatar_url);
|
||||
expect(avatarProps.entityName).toBe(author.username);
|
||||
expect(avatarProps.alt).toBe(author.name);
|
||||
expect(avatarProps.size).toEqual({ default: 24, md: 32 });
|
||||
});
|
||||
|
||||
it('should render note header content', () => {
|
||||
|
|
|
@ -692,6 +692,37 @@ RSpec.describe Deployment do
|
|||
.to contain_exactly(stop_env_b)
|
||||
end
|
||||
end
|
||||
|
||||
context 'When last deployment for environment is a retried build' do
|
||||
let(:pipeline) { create(:ci_pipeline, project: project) }
|
||||
let(:environment_b) { create(:environment, project: project) }
|
||||
|
||||
let(:build_a) do
|
||||
create(:ci_build, :success, project: project, pipeline: pipeline, environment: environment.name)
|
||||
end
|
||||
|
||||
let(:build_b) do
|
||||
create(:ci_build, :success, project: project, pipeline: pipeline, environment: environment_b.name)
|
||||
end
|
||||
|
||||
let!(:deployment_a) do
|
||||
create(:deployment, :success, project: project, environment: environment, deployable: build_a)
|
||||
end
|
||||
|
||||
let!(:deployment_b) do
|
||||
create(:deployment, :success, project: project, environment: environment_b, deployable: build_b)
|
||||
end
|
||||
|
||||
before do
|
||||
# Retry build_b
|
||||
build_b.update!(retried: true)
|
||||
|
||||
# New successful build after retry.
|
||||
create(:ci_build, :success, project: project, pipeline: pipeline, environment: environment_b.name)
|
||||
end
|
||||
|
||||
it { expect(subject_method(environment_b)).not_to be_nil }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1098,25 +1098,117 @@ RSpec.describe ProjectPolicy do
|
|||
|
||||
subject { described_class.new(deploy_token, project) }
|
||||
|
||||
context 'a deploy token with read_package_registry scope' do
|
||||
let(:deploy_token) { create(:deploy_token, read_package_registry: true) }
|
||||
context 'private project' do
|
||||
let(:project) { private_project }
|
||||
|
||||
it { is_expected.to be_allowed(:read_package) }
|
||||
it { is_expected.to be_allowed(:read_project) }
|
||||
it { is_expected.to be_disallowed(:create_package) }
|
||||
context 'a deploy token with read_registry scope' do
|
||||
let(:deploy_token) { create(:deploy_token, read_registry: true, write_registry: false) }
|
||||
|
||||
it_behaves_like 'package access with repository disabled'
|
||||
it { is_expected.to be_allowed(:read_container_image) }
|
||||
it { is_expected.to be_disallowed(:create_container_image) }
|
||||
|
||||
context 'with registry disabled' do
|
||||
include_context 'registry disabled via project features'
|
||||
|
||||
it { is_expected.to be_disallowed(:read_container_image) }
|
||||
it { is_expected.to be_disallowed(:create_container_image) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'a deploy token with write_registry scope' do
|
||||
let(:deploy_token) { create(:deploy_token, read_registry: false, write_registry: true) }
|
||||
|
||||
it { is_expected.to be_disallowed(:read_container_image) }
|
||||
it { is_expected.to be_allowed(:create_container_image) }
|
||||
|
||||
context 'with registry disabled' do
|
||||
include_context 'registry disabled via project features'
|
||||
|
||||
it { is_expected.to be_disallowed(:read_container_image) }
|
||||
it { is_expected.to be_disallowed(:create_container_image) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'a deploy token with no registry scope' do
|
||||
let(:deploy_token) { create(:deploy_token, read_registry: false, write_registry: false) }
|
||||
|
||||
it { is_expected.to be_disallowed(:read_container_image) }
|
||||
it { is_expected.to be_disallowed(:create_container_image) }
|
||||
end
|
||||
|
||||
context 'a deploy token with read_package_registry scope' do
|
||||
let(:deploy_token) { create(:deploy_token, read_repository: false, read_registry: false, read_package_registry: true) }
|
||||
|
||||
it { is_expected.to be_allowed(:read_project) }
|
||||
it { is_expected.to be_allowed(:read_package) }
|
||||
it { is_expected.to be_disallowed(:create_package) }
|
||||
|
||||
it_behaves_like 'package access with repository disabled'
|
||||
end
|
||||
|
||||
context 'a deploy token with write_package_registry scope' do
|
||||
let(:deploy_token) { create(:deploy_token, read_repository: false, read_registry: false, write_package_registry: true) }
|
||||
|
||||
it { is_expected.to be_allowed(:create_package) }
|
||||
it { is_expected.to be_allowed(:read_package) }
|
||||
it { is_expected.to be_allowed(:read_project) }
|
||||
it { is_expected.to be_disallowed(:destroy_package) }
|
||||
|
||||
it_behaves_like 'package access with repository disabled'
|
||||
end
|
||||
end
|
||||
|
||||
context 'a deploy token with write_package_registry scope' do
|
||||
let(:deploy_token) { create(:deploy_token, write_package_registry: true) }
|
||||
context 'public project' do
|
||||
let(:project) { public_project }
|
||||
|
||||
it { is_expected.to be_allowed(:create_package) }
|
||||
it { is_expected.to be_allowed(:read_package) }
|
||||
it { is_expected.to be_allowed(:read_project) }
|
||||
it { is_expected.to be_disallowed(:destroy_package) }
|
||||
context 'a deploy token with read_registry scope' do
|
||||
let(:deploy_token) { create(:deploy_token, read_registry: true, write_registry: false) }
|
||||
|
||||
it_behaves_like 'package access with repository disabled'
|
||||
it { is_expected.to be_allowed(:read_container_image) }
|
||||
it { is_expected.to be_disallowed(:create_container_image) }
|
||||
|
||||
context 'with registry disabled' do
|
||||
include_context 'registry disabled via project features'
|
||||
|
||||
it { is_expected.to be_disallowed(:read_container_image) }
|
||||
it { is_expected.to be_disallowed(:create_container_image) }
|
||||
end
|
||||
|
||||
context 'with registry private' do
|
||||
include_context 'registry set to private via project features'
|
||||
|
||||
it { is_expected.to be_allowed(:read_container_image) }
|
||||
it { is_expected.to be_disallowed(:create_container_image) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'a deploy token with write_registry scope' do
|
||||
let(:deploy_token) { create(:deploy_token, read_registry: false, write_registry: true) }
|
||||
|
||||
it { is_expected.to be_allowed(:read_container_image) }
|
||||
it { is_expected.to be_allowed(:create_container_image) }
|
||||
|
||||
context 'with registry disabled' do
|
||||
include_context 'registry disabled via project features'
|
||||
|
||||
it { is_expected.to be_disallowed(:read_container_image) }
|
||||
it { is_expected.to be_disallowed(:create_container_image) }
|
||||
end
|
||||
|
||||
context 'with registry private' do
|
||||
include_context 'registry set to private via project features'
|
||||
|
||||
it { is_expected.to be_allowed(:read_container_image) }
|
||||
it { is_expected.to be_allowed(:create_container_image) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'a deploy token with no registry scope' do
|
||||
let(:deploy_token) { create(:deploy_token, read_registry: false, write_registry: false) }
|
||||
|
||||
it { is_expected.to be_disallowed(:read_container_image) }
|
||||
it { is_expected.to be_disallowed(:create_container_image) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -226,14 +226,26 @@ RSpec.describe API::MavenPackages do
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples 'file download in FIPS mode' do
|
||||
context 'in FIPS mode', :fips_mode do
|
||||
it_behaves_like 'successfully returning the file'
|
||||
|
||||
it 'rejects the request for an md5 file' do
|
||||
download_file(file_name: package_file.file_name + '.md5')
|
||||
|
||||
expect(response).to have_gitlab_http_status(:unprocessable_entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v4/packages/maven/*path/:file_name' do
|
||||
context 'a public project' do
|
||||
subject { download_file(file_name: package_file.file_name) }
|
||||
|
||||
shared_examples 'getting a file' do
|
||||
it_behaves_like 'tracking the file download event'
|
||||
|
||||
it_behaves_like 'successfully returning the file'
|
||||
it_behaves_like 'file download in FIPS mode'
|
||||
|
||||
it 'returns sha1 of the file' do
|
||||
download_file(file_name: package_file.file_name + '.sha1')
|
||||
|
@ -402,8 +414,8 @@ RSpec.describe API::MavenPackages do
|
|||
|
||||
shared_examples 'getting a file for a group' do
|
||||
it_behaves_like 'tracking the file download event'
|
||||
|
||||
it_behaves_like 'successfully returning the file'
|
||||
it_behaves_like 'file download in FIPS mode'
|
||||
|
||||
it 'returns sha1 of the file' do
|
||||
download_file(file_name: package_file.file_name + '.sha1')
|
||||
|
@ -625,8 +637,8 @@ RSpec.describe API::MavenPackages do
|
|||
subject { download_file(file_name: package_file.file_name) }
|
||||
|
||||
it_behaves_like 'tracking the file download event'
|
||||
|
||||
it_behaves_like 'successfully returning the file'
|
||||
it_behaves_like 'file download in FIPS mode'
|
||||
|
||||
it 'returns sha1 of the file' do
|
||||
download_file(file_name: package_file.file_name + '.sha1')
|
||||
|
@ -833,6 +845,16 @@ RSpec.describe API::MavenPackages do
|
|||
|
||||
subject { upload_file_with_token(params: params) }
|
||||
|
||||
context 'FIPS mode', :fips_mode do
|
||||
it_behaves_like 'package workhorse uploads'
|
||||
|
||||
it 'rejects the request for md5 file' do
|
||||
upload_file_with_token(params: params, file_extension: 'jar.md5')
|
||||
|
||||
expect(response).to have_gitlab_http_status(:unprocessable_entity)
|
||||
end
|
||||
end
|
||||
|
||||
context 'file size is too large' do
|
||||
it 'rejects the request' do
|
||||
allow_next_instance_of(UploadedFile) do |uploaded_file|
|
||||
|
@ -995,12 +1017,22 @@ RSpec.describe API::MavenPackages do
|
|||
end
|
||||
|
||||
context 'for md5 file' do
|
||||
subject { upload_file_with_token(params: params, file_extension: 'jar.md5') }
|
||||
|
||||
it 'returns an empty body' do
|
||||
upload_file_with_token(params: params, file_extension: 'jar.md5')
|
||||
subject
|
||||
|
||||
expect(response.body).to eq('')
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
|
||||
context 'with FIPS mode enabled', :fips_mode do
|
||||
it 'rejects the request' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:unprocessable_entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_context 'repository disabled via project features' do
|
||||
before do
|
||||
project.project_feature.update_columns(
|
||||
# Disable merge_requests and builds as well, since merge_requests and
|
||||
# builds cannot have higher visibility than repository.
|
||||
merge_requests_access_level: ProjectFeature::DISABLED,
|
||||
builds_access_level: ProjectFeature::DISABLED,
|
||||
repository_access_level: ProjectFeature::DISABLED)
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_context 'registry disabled via project features' do
|
||||
before do
|
||||
project.project_feature.update_columns(
|
||||
container_registry_access_level: ProjectFeature::DISABLED
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_context 'registry set to private via project features' do
|
||||
before do
|
||||
project.project_feature.update_columns(
|
||||
container_registry_access_level: ProjectFeature::PRIVATE
|
||||
)
|
||||
end
|
||||
end
|
|
@ -345,16 +345,7 @@ RSpec.shared_examples 'project policies as admin without admin mode' do
|
|||
end
|
||||
|
||||
RSpec.shared_examples 'package access with repository disabled' do
|
||||
context 'when repository is disabled' do
|
||||
before do
|
||||
project.project_feature.update!(
|
||||
# Disable merge_requests and builds as well, since merge_requests and
|
||||
# builds cannot have higher visibility than repository.
|
||||
merge_requests_access_level: ProjectFeature::DISABLED,
|
||||
builds_access_level: ProjectFeature::DISABLED,
|
||||
repository_access_level: ProjectFeature::DISABLED)
|
||||
end
|
||||
include_context 'repository disabled via project features'
|
||||
|
||||
it { is_expected.to be_allowed(:read_package) }
|
||||
end
|
||||
it { is_expected.to be_allowed(:read_package) }
|
||||
end
|
||||
|
|
|
@ -142,9 +142,9 @@ RSpec.shared_examples 'logs an auth warning' do |requested_actions|
|
|||
requested_project_path: project.full_path,
|
||||
requested_actions: requested_actions,
|
||||
authorized_actions: [],
|
||||
user_id: current_user.id,
|
||||
username: current_user.username
|
||||
}
|
||||
user_id: current_user&.id,
|
||||
username: current_user&.username
|
||||
}.compact
|
||||
end
|
||||
|
||||
it do
|
||||
|
|
Loading…
Reference in a new issue