Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
e4476c4a18
commit
6bbf310347
|
@ -15,6 +15,7 @@ const PERSISTENT_USER_CALLOUTS = [
|
|||
'.js-user-over-limit-free-plan-alert',
|
||||
'.js-minute-limit-banner',
|
||||
'.js-submit-license-usage-data-banner',
|
||||
'.js-project-usage-limitations-callout',
|
||||
];
|
||||
|
||||
const initCallouts = () => {
|
||||
|
|
|
@ -19,6 +19,9 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
|
|||
session.delete(:user_return_to)
|
||||
render "doorkeeper/authorizations/redirect", locals: { redirect_uri: parsed_redirect_uri }, layout: false
|
||||
else
|
||||
redirect_uri = URI(authorization.authorize.redirect_uri)
|
||||
allow_redirect_uri_form_action(redirect_uri.scheme)
|
||||
|
||||
render "doorkeeper/authorizations/new"
|
||||
end
|
||||
else
|
||||
|
@ -28,6 +31,20 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
|
|||
|
||||
private
|
||||
|
||||
# Chrome blocks redirections if the form-action CSP directive is present
|
||||
# and the redirect location's scheme isn't allow-listed
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/form-action
|
||||
# https://github.com/w3c/webappsec-csp/issues/8
|
||||
def allow_redirect_uri_form_action(redirect_uri_scheme)
|
||||
return unless content_security_policy?
|
||||
|
||||
form_action = request.content_security_policy.form_action
|
||||
return unless form_action
|
||||
|
||||
form_action.push("#{redirect_uri_scheme}:")
|
||||
request.content_security_policy.form_action(*form_action)
|
||||
end
|
||||
|
||||
def pre_auth_params
|
||||
# Cannot be achieved with a before_action hook, due to the execution order.
|
||||
downgrade_scopes! if action_name == 'new'
|
||||
|
|
|
@ -16,7 +16,7 @@ module Nav
|
|||
menu_sections.push(general_menu_section)
|
||||
|
||||
{
|
||||
title: _("New..."),
|
||||
title: _("Create new"),
|
||||
menu_sections: menu_sections.select { |x| x.fetch(:menu_items).any? }
|
||||
}
|
||||
end
|
||||
|
|
|
@ -137,8 +137,8 @@ module Ci
|
|||
where('NOT EXISTS (?)', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').trace)
|
||||
end
|
||||
|
||||
scope :with_reports, ->(reports_scope) do
|
||||
with_existing_job_artifacts(reports_scope)
|
||||
scope :with_artifacts, ->(artifact_scope) do
|
||||
with_existing_job_artifacts(artifact_scope)
|
||||
.eager_load_job_artifacts
|
||||
end
|
||||
|
||||
|
@ -1047,7 +1047,7 @@ module Ci
|
|||
end
|
||||
|
||||
def report_artifacts
|
||||
job_artifacts.with_reports
|
||||
job_artifacts.all_reports
|
||||
end
|
||||
|
||||
# Virtual deployment status depending on the environment status.
|
||||
|
|
|
@ -152,7 +152,7 @@ module Ci
|
|||
where(file_type: types)
|
||||
end
|
||||
|
||||
scope :with_reports, -> do
|
||||
scope :all_reports, -> do
|
||||
with_file_types(REPORT_TYPES.keys.map(&:to_s))
|
||||
end
|
||||
|
||||
|
|
|
@ -342,7 +342,7 @@ module Ci
|
|||
end
|
||||
|
||||
scope :with_reports, -> (reports_scope) do
|
||||
where('EXISTS (?)', ::Ci::Build.latest.with_reports(reports_scope).where('ci_pipelines.id=ci_builds.commit_id').select(1))
|
||||
where('EXISTS (?)', ::Ci::Build.latest.with_artifacts(reports_scope).where('ci_pipelines.id=ci_builds.commit_id').select(1))
|
||||
end
|
||||
|
||||
scope :with_only_interruptible_builds, -> do
|
||||
|
@ -696,7 +696,7 @@ module Ci
|
|||
def latest_report_artifacts
|
||||
::Gitlab::SafeRequestStore.fetch("pipeline:#{self.id}:latest_report_artifacts") do
|
||||
::Ci::JobArtifact.where(
|
||||
id: job_artifacts.with_reports
|
||||
id: job_artifacts.all_reports
|
||||
.select('max(ci_job_artifacts.id) as id')
|
||||
.group(:file_type)
|
||||
)
|
||||
|
@ -1057,16 +1057,16 @@ module Ci
|
|||
@latest_builds_with_artifacts ||= builds.latest.with_artifacts_not_expired.to_a
|
||||
end
|
||||
|
||||
def latest_report_builds(reports_scope = ::Ci::JobArtifact.with_reports)
|
||||
builds.latest.with_reports(reports_scope)
|
||||
def latest_report_builds(reports_scope = ::Ci::JobArtifact.all_reports)
|
||||
builds.latest.with_artifacts(reports_scope)
|
||||
end
|
||||
|
||||
def latest_test_report_builds
|
||||
latest_report_builds(Ci::JobArtifact.test_reports).preload(:project, :metadata)
|
||||
end
|
||||
|
||||
def latest_report_builds_in_self_and_descendants(reports_scope = ::Ci::JobArtifact.with_reports)
|
||||
builds_in_self_and_descendants.with_reports(reports_scope)
|
||||
def latest_report_builds_in_self_and_descendants(reports_scope = ::Ci::JobArtifact.all_reports)
|
||||
builds_in_self_and_descendants.with_artifacts(reports_scope)
|
||||
end
|
||||
|
||||
def builds_with_coverage
|
||||
|
|
|
@ -52,7 +52,8 @@ module Users
|
|||
minute_limit_banner: 49,
|
||||
preview_user_over_limit_free_plan_alert: 50, # EE-only
|
||||
user_reached_limit_free_plan_alert: 51, # EE-only
|
||||
submit_license_usage_data_banner: 52 # EE-only
|
||||
submit_license_usage_data_banner: 52, # EE-only
|
||||
personal_project_limitations_banner: 53 # EE-only
|
||||
}
|
||||
|
||||
validates :feature_name,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
- verified_at = mirror.ssh_known_hosts_verified_at
|
||||
|
||||
.form-group.js-ssh-host-keys-section{ class: ('collapse' unless mirror.ssh_mirror_url?) }
|
||||
%button.btn.gl-button.btn-inverted.btn-secondary.inline.js-detect-host-keys.gl-mr-3{ type: 'button', data: { qa_selector: 'detect_host_keys' } }
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-detect-host-keys gl-mr-3', data: { qa_selector: 'detect_host_keys' } }) do
|
||||
= gl_loading_icon(inline: true, css_class: 'js-spinner gl-display-none gl-mr-2')
|
||||
= _('Detect host keys')
|
||||
.fingerprint-ssh-info.js-fingerprint-ssh-info.gl-mt-3.gl-mb-3{ class: ('collapse' unless mirror.ssh_mirror_url?) }
|
||||
|
@ -23,7 +23,7 @@
|
|||
#{time_ago_in_words(verified_at)} ago
|
||||
|
||||
.js-ssh-hosts-advanced.inline
|
||||
%button.btn.gl-button.btn-default.btn-show-advanced.show-advanced{ type: 'button' }
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'btn-show-advanced show-advanced' }) do
|
||||
%span.label-show
|
||||
= _('Input host keys manually')
|
||||
%span.label-hide
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
- page_title s_("UsageQuota|Usage")
|
||||
|
||||
- presenter_class = Namespaces::FreeUserCap::Projects::UsageQuotaLimitationsBannerPresenter
|
||||
- usage_quota_limits_banner_presenter = presenter_class.new(@project, current_user: current_user)
|
||||
|
||||
- if usage_quota_limits_banner_presenter.visible?
|
||||
= render Pajamas::AlertComponent.new(**usage_quota_limits_banner_presenter.alert_component_attributes) do |c|
|
||||
- c.body do
|
||||
= usage_quota_limits_banner_presenter.body_text
|
||||
|
||||
= render Pajamas::AlertComponent.new(title: _('Repository usage recalculation started'),
|
||||
variant: :info,
|
||||
alert_options: { class: 'js-recalculation-started-alert gl-mt-4 gl-mb-5 gl-display-none' }) do |c|
|
||||
|
|
|
@ -320,7 +320,7 @@
|
|||
:tags: []
|
||||
- :name: cronjob:database_ci_namespace_mirrors_consistency_check
|
||||
:worker_name: Database::CiNamespaceMirrorsConsistencyCheckWorker
|
||||
:feature_category: :sharding
|
||||
:feature_category: :pods
|
||||
:has_external_dependencies: false
|
||||
:urgency: :low
|
||||
:resource_boundary: :unknown
|
||||
|
@ -329,7 +329,7 @@
|
|||
:tags: []
|
||||
- :name: cronjob:database_ci_project_mirrors_consistency_check
|
||||
:worker_name: Database::CiProjectMirrorsConsistencyCheckWorker
|
||||
:feature_category: :sharding
|
||||
:feature_category: :pods
|
||||
:has_external_dependencies: false
|
||||
:urgency: :low
|
||||
:resource_boundary: :unknown
|
||||
|
@ -455,7 +455,7 @@
|
|||
:tags: []
|
||||
- :name: cronjob:loose_foreign_keys_cleanup
|
||||
:worker_name: LooseForeignKeys::CleanupWorker
|
||||
:feature_category: :sharding
|
||||
:feature_category: :pods
|
||||
:has_external_dependencies: false
|
||||
:urgency: :low
|
||||
:resource_boundary: :unknown
|
||||
|
@ -2625,7 +2625,7 @@
|
|||
:tags: []
|
||||
- :name: namespaces_process_sync_events
|
||||
:worker_name: Namespaces::ProcessSyncEventsWorker
|
||||
:feature_category: :sharding
|
||||
:feature_category: :pods
|
||||
:has_external_dependencies: false
|
||||
:urgency: :high
|
||||
:resource_boundary: :unknown
|
||||
|
@ -2814,7 +2814,7 @@
|
|||
:tags: []
|
||||
- :name: projects_process_sync_events
|
||||
:worker_name: Projects::ProcessSyncEventsWorker
|
||||
:feature_category: :sharding
|
||||
:feature_category: :pods
|
||||
:has_external_dependencies: false
|
||||
:urgency: :high
|
||||
:resource_boundary: :unknown
|
||||
|
|
|
@ -6,7 +6,7 @@ module Database
|
|||
include CronjobQueue # rubocop: disable Scalability/CronWorkerContext
|
||||
|
||||
sidekiq_options retry: false
|
||||
feature_category :sharding
|
||||
feature_category :pods
|
||||
data_consistency :sticky
|
||||
idempotent!
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ module Database
|
|||
include CronjobQueue # rubocop: disable Scalability/CronWorkerContext
|
||||
|
||||
sidekiq_options retry: false
|
||||
feature_category :sharding
|
||||
feature_category :pods
|
||||
data_consistency :sticky
|
||||
idempotent!
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ module LooseForeignKeys
|
|||
include CronjobQueue # rubocop: disable Scalability/CronWorkerContext
|
||||
|
||||
sidekiq_options retry: false
|
||||
feature_category :sharding
|
||||
feature_category :pods
|
||||
data_consistency :always
|
||||
idempotent!
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ module Namespaces
|
|||
|
||||
data_consistency :always
|
||||
|
||||
feature_category :sharding
|
||||
feature_category :pods
|
||||
urgency :high
|
||||
|
||||
idempotent!
|
||||
|
|
|
@ -9,7 +9,7 @@ module Projects
|
|||
|
||||
data_consistency :always
|
||||
|
||||
feature_category :sharding
|
||||
feature_category :pods
|
||||
urgency :high
|
||||
|
||||
idempotent!
|
||||
|
|
|
@ -90,6 +90,7 @@
|
|||
- permissions
|
||||
- pipeline_authoring
|
||||
- planning_analytics
|
||||
- pods
|
||||
- portfolio_management
|
||||
- privacy_control_center
|
||||
- product_analytics
|
||||
|
@ -114,10 +115,10 @@
|
|||
- security_orchestration
|
||||
- service_desk
|
||||
- service_ping
|
||||
- sharding
|
||||
- snippets
|
||||
- source_code_management
|
||||
- static_application_security_testing
|
||||
- static_site_editor
|
||||
- subgroups
|
||||
- system_access
|
||||
- team_planning
|
||||
|
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339181
|
|||
milestone: '14.3'
|
||||
type: development
|
||||
group: group::code review
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: scan_execution_rule_mode
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90099
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/359883
|
||||
milestone: '15.2'
|
||||
type: development
|
||||
group: group::container security
|
||||
default_enabled: false
|
|
@ -11,26 +11,7 @@ class ScheduleMigratePagesToZipStorage < ActiveRecord::Migration[6.0]
|
|||
|
||||
disable_ddl_transaction!
|
||||
|
||||
class ProjectPagesMetadatum < ActiveRecord::Base
|
||||
extend SuppressCompositePrimaryKeyWarning
|
||||
|
||||
include EachBatch
|
||||
|
||||
self.primary_key = :project_id
|
||||
self.table_name = 'project_pages_metadata'
|
||||
self.inheritance_column = :_type_disabled
|
||||
|
||||
scope :deployed, -> { where(deployed: true) }
|
||||
scope :only_on_legacy_storage, -> { deployed.where(pages_deployment_id: nil) }
|
||||
end
|
||||
|
||||
def up
|
||||
queue_background_migration_jobs_by_range_at_intervals(
|
||||
ProjectPagesMetadatum.only_on_legacy_storage,
|
||||
MIGRATION,
|
||||
BATCH_TIME,
|
||||
batch_size: BATCH_SIZE,
|
||||
primary_column_name: :project_id
|
||||
)
|
||||
# no-op
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19849,6 +19849,7 @@ Name of the feature that the callout is for.
|
|||
| <a id="usercalloutfeaturenameenumminute_limit_banner"></a>`MINUTE_LIMIT_BANNER` | Callout feature name for minute_limit_banner. |
|
||||
| <a id="usercalloutfeaturenameenumnew_user_signups_cap_reached"></a>`NEW_USER_SIGNUPS_CAP_REACHED` | Callout feature name for new_user_signups_cap_reached. |
|
||||
| <a id="usercalloutfeaturenameenumpersonal_access_token_expiry"></a>`PERSONAL_ACCESS_TOKEN_EXPIRY` | Callout feature name for personal_access_token_expiry. |
|
||||
| <a id="usercalloutfeaturenameenumpersonal_project_limitations_banner"></a>`PERSONAL_PROJECT_LIMITATIONS_BANNER` | Callout feature name for personal_project_limitations_banner. |
|
||||
| <a id="usercalloutfeaturenameenumpipeline_needs_banner"></a>`PIPELINE_NEEDS_BANNER` | Callout feature name for pipeline_needs_banner. |
|
||||
| <a id="usercalloutfeaturenameenumpipeline_needs_hover_tip"></a>`PIPELINE_NEEDS_HOVER_TIP` | Callout feature name for pipeline_needs_hover_tip. |
|
||||
| <a id="usercalloutfeaturenameenumpreview_user_over_limit_free_plan_alert"></a>`PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT` | Callout feature name for preview_user_over_limit_free_plan_alert. |
|
||||
|
|
|
@ -83,7 +83,8 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
|
|||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77236) in GitLab 14.7.
|
||||
|
||||
Create a [group access token](../user/group/settings/group_access_tokens.md).
|
||||
Create a [group access token](../user/group/settings/group_access_tokens.md). You must have the Owner role for the
|
||||
group to create group access tokens.
|
||||
|
||||
```plaintext
|
||||
POST groups/:id/access_tokens
|
||||
|
@ -94,7 +95,7 @@ POST groups/:id/access_tokens
|
|||
| `id` | integer or string | yes | ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) |
|
||||
| `name` | String | yes | Name of the group access token |
|
||||
| `scopes` | `Array[String]` | yes | [List of scopes](../user/group/settings/group_access_tokens.md#scopes-for-a-group-access-token) |
|
||||
| `access_level` | Integer | no | A valid access level. Default value is 40 (Maintainer). Other allowed values are 10 (Guest), 20 (Reporter), and 30 (Developer). |
|
||||
| `access_level` | Integer | no | Access level. Valid values are `10` (Guest), `20` (Reporter), `30` (Developer), `40` (Maintainer), and `50` (Owner). |
|
||||
| `expires_at` | Date | no | Token expires at midnight UTC on that date |
|
||||
|
||||
```shell
|
||||
|
|
|
@ -15,6 +15,16 @@ To configure GitLab for this, see
|
|||
|
||||
This functionality is based on the [doorkeeper Ruby gem](https://github.com/doorkeeper-gem/doorkeeper).
|
||||
|
||||
## CORS preflight requests
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/364680) in GitLab 15.1.
|
||||
|
||||
The following endpoints support [CORS preflight requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS):
|
||||
|
||||
- `/oauth/revoke`
|
||||
- `/oauth/token`
|
||||
- `/oauth/userinfo`
|
||||
|
||||
## Supported OAuth 2.0 flows
|
||||
|
||||
GitLab supports the following authorization flows:
|
||||
|
|
|
@ -87,19 +87,25 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
|
|||
|
||||
Create a [project access token](../user/project/settings/project_access_tokens.md).
|
||||
|
||||
**NOTE:** Project Maintainers cannot create project access tokens with Owner (50) access level.
|
||||
When you create a project access token, the maximum role (access level) you set depends on if you have the Owner or Maintainer role for the group. For example, the maximum
|
||||
role that can be set is:
|
||||
|
||||
- Owner (`50`), if you have the Owner role for the project.
|
||||
- Maintainer (`40`), if you have the Maintainer role on the project.
|
||||
|
||||
In GitLab 14.8 and earlier, project access tokens have a maximum role of Maintainer.
|
||||
|
||||
```plaintext
|
||||
POST projects/:id/access_tokens
|
||||
```
|
||||
|
||||
| Attribute | Type | required | Description |
|
||||
|-----------|---------|----------|---------------------|
|
||||
| `id` | integer or string | yes | ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) |
|
||||
| `name` | String | yes | Name of the project access token |
|
||||
| `scopes` | `Array[String]` | yes | [List of scopes](../user/project/settings/project_access_tokens.md#scopes-for-a-project-access-token) |
|
||||
| `access_level` | Integer | no | A valid access level. Default value is 40 (Maintainer). Other allowed values are 10 (Guest), 20 (Reporter), and 30 (Developer). |
|
||||
| `expires_at` | Date | no | Token expires at midnight UTC on that date |
|
||||
| Attribute | Type | required | Description |
|
||||
|-----------|---------|----------|---------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer or string | yes | ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) |
|
||||
| `name` | String | yes | Name of the project access token |
|
||||
| `scopes` | `Array[String]` | yes | [List of scopes](../user/project/settings/project_access_tokens.md#scopes-for-a-project-access-token) |
|
||||
| `access_level` | Integer | no | Access level. Valid values are `10` (Guest), `20` (Reporter), `30` (Developer), `40` (Maintainer), and `50` (Owner). Defaults to `40`. |
|
||||
| `expires_at` | Date | no | Token expires at midnight UTC on that date |
|
||||
|
||||
```shell
|
||||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
|
|
|
@ -3484,7 +3484,7 @@ to the image specified in the [`image`](#image) keyword.
|
|||
- `<image-name>:<tag>`
|
||||
- `<image-name>@<digest>`
|
||||
|
||||
CI/CD variables [are supported](../variables/where_variables_can_be_used.md#gitlab-ciyml-file).
|
||||
CI/CD variables [are supported](../variables/where_variables_can_be_used.md#gitlab-ciyml-file), but [not for `alias`](https://gitlab.com/gitlab-org/gitlab/-/issues/19561).
|
||||
|
||||
**Example of `services`**:
|
||||
|
||||
|
|
|
@ -147,9 +147,12 @@ Even when creation is disabled, you can still use and revoke existing group acce
|
|||
|
||||
## Bot users for groups
|
||||
|
||||
Each time you create a group access token, a bot user is created and added to the group.
|
||||
These bot users are similar to [bot users for projects](../../project/settings/project_access_tokens.md#bot-users-for-projects),
|
||||
except they are added to groups instead of projects.
|
||||
These bot users do not count as licensed seats.
|
||||
Each time you create a group access token, a bot user is created and added to the group. These bot users are similar to
|
||||
[bot users for projects](../../project/settings/project_access_tokens.md#bot-users-for-projects), except they are added
|
||||
to groups instead of projects. Bot users for groups:
|
||||
|
||||
- Do not count as licensed seats.
|
||||
- Can have a maximum role of Owner for a group. For more information, see
|
||||
[Create a group access token](../../../api/group_access_tokens.md#create-a-group-access-token).
|
||||
|
||||
For more information, see [Bot users for projects](../../project/settings/project_access_tokens.md#bot-users-for-projects).
|
||||
|
|
|
@ -43,6 +43,8 @@ configured for personal access tokens.
|
|||
|
||||
## Create a project access token
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89114) in GitLab 15.1, Owners can select Owner role for project access tokens.
|
||||
|
||||
To create a project access token:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
|
@ -102,8 +104,6 @@ These bot users do not count as licensed seats.
|
|||
The bot users for projects have [permissions](../../permissions.md#project-members-permissions) that correspond with the
|
||||
selected role and [scope](#scopes-for-a-project-access-token) of the project access token.
|
||||
|
||||
**Note** Project maintainers cannot select Owner role for bot users.
|
||||
|
||||
- The name is set to the name of the token.
|
||||
- The username is set to `project_{project_id}_bot` for the first access token. For example, `project_123_bot`.
|
||||
- The email is set to `project{project_id}_bot@noreply.{Gitlab.config.gitlab.host}`. For example, `project123_bot@noreply.example.com`.
|
||||
|
@ -118,6 +118,8 @@ Bot users for projects:
|
|||
|
||||
- Are included in a project's member list but cannot be modified.
|
||||
- Cannot be added to any other project.
|
||||
- Can have a maximum role of Owner for a project. For more information, see
|
||||
[Create a project access token](../../../api/project_access_tokens.md#create-a-project-access-token).
|
||||
|
||||
When the project access token is [revoked](#revoke-a-project-access-token):
|
||||
|
||||
|
|
|
@ -8,11 +8,12 @@ module ContainerRegistry
|
|||
class BaseClient
|
||||
DOCKER_DISTRIBUTION_MANIFEST_V2_TYPE = 'application/vnd.docker.distribution.manifest.v2+json'
|
||||
DOCKER_DISTRIBUTION_MANIFEST_LIST_V2_TYPE = 'application/vnd.docker.distribution.manifest.list.v2+json'
|
||||
OCI_DISTRIBUTION_INDEX_TYPE = 'application/vnd.oci.image.index.v1+json'
|
||||
OCI_MANIFEST_V1_TYPE = 'application/vnd.oci.image.manifest.v1+json'
|
||||
CONTAINER_IMAGE_V1_TYPE = 'application/vnd.docker.container.image.v1+json'
|
||||
|
||||
ACCEPTED_TYPES = [DOCKER_DISTRIBUTION_MANIFEST_V2_TYPE, OCI_MANIFEST_V1_TYPE].freeze
|
||||
ACCEPTED_TYPES_RAW = [DOCKER_DISTRIBUTION_MANIFEST_V2_TYPE, OCI_MANIFEST_V1_TYPE, DOCKER_DISTRIBUTION_MANIFEST_LIST_V2_TYPE].freeze
|
||||
ACCEPTED_TYPES_RAW = [DOCKER_DISTRIBUTION_MANIFEST_V2_TYPE, OCI_MANIFEST_V1_TYPE, DOCKER_DISTRIBUTION_MANIFEST_LIST_V2_TYPE, OCI_DISTRIBUTION_INDEX_TYPE].freeze
|
||||
|
||||
RETRY_EXCEPTIONS = [Faraday::Request::Retry::DEFAULT_EXCEPTIONS, Faraday::ConnectionFailed].flatten.freeze
|
||||
RETRY_OPTIONS = {
|
||||
|
@ -107,6 +108,7 @@ module ContainerRegistry
|
|||
conn.response :json, content_type: 'application/vnd.docker.distribution.manifest.v1+json'
|
||||
conn.response :json, content_type: DOCKER_DISTRIBUTION_MANIFEST_V2_TYPE
|
||||
conn.response :json, content_type: OCI_MANIFEST_V1_TYPE
|
||||
conn.response :json, content_type: OCI_DISTRIBUTION_INDEX_TYPE
|
||||
end
|
||||
|
||||
def delete_if_exists(path)
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Analytics
|
||||
class UniqueVisits
|
||||
# Returns number of unique visitors for given targets in given time frame
|
||||
#
|
||||
# @param [String, Array[<String>]] targets ids of targets to count visits on. Special case for :any
|
||||
# @param [ActiveSupport::TimeWithZone] start_date start of time frame
|
||||
# @param [ActiveSupport::TimeWithZone] end_date end of time frame
|
||||
# @return [Integer] number of unique visitors
|
||||
def unique_visits_for(targets:, start_date: 7.days.ago, end_date: start_date + 1.week)
|
||||
events = if targets == :compliance
|
||||
self.class.compliance_events
|
||||
else
|
||||
Array(targets)
|
||||
end
|
||||
|
||||
Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: events, start_date: start_date, end_date: end_date)
|
||||
end
|
||||
|
||||
class << self
|
||||
def compliance_events
|
||||
Gitlab::UsageDataCounters::HLLRedisCounter.events_for_category('compliance')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -9,10 +9,7 @@ module Gitlab
|
|||
# see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54578 for discussion
|
||||
class MigratePagesToZipStorage
|
||||
def perform(start_id, stop_id)
|
||||
::Pages::MigrateFromLegacyStorageService.new(Gitlab::AppLogger,
|
||||
ignore_invalid_entries: false,
|
||||
mark_projects_as_not_deployed: false)
|
||||
.execute_for_batch(start_id..stop_id)
|
||||
# no-op
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -27,7 +27,6 @@ module Gitlab
|
|||
project_maximum_id
|
||||
user_minimum_id
|
||||
user_maximum_id
|
||||
unique_visit_service
|
||||
deployment_minimum_id
|
||||
deployment_maximum_id
|
||||
auth_providers
|
||||
|
@ -67,13 +66,17 @@ module Gitlab
|
|||
# rubocop: disable Metrics/AbcSize
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def system_usage_data
|
||||
issues_created_manually_from_alerts = count(Issue.with_alert_management_alerts.not_authored_by(::User.alert_bot), start: minimum_id(Issue), finish: maximum_id(Issue))
|
||||
issues_created_manually_from_alerts = if Gitlab.com?
|
||||
FALLBACK
|
||||
else
|
||||
count(Issue.with_alert_management_alerts.not_authored_by(::User.alert_bot), start: minimum_id(Issue), finish: maximum_id(Issue))
|
||||
end
|
||||
|
||||
{
|
||||
counts: {
|
||||
assignee_lists: count(List.assignee),
|
||||
ci_builds: count(::Ci::Build),
|
||||
ci_internal_pipelines: count(::Ci::Pipeline.internal),
|
||||
ci_internal_pipelines: Gitlab.com? ? FALLBACK : count(::Ci::Pipeline.internal),
|
||||
ci_external_pipelines: count(::Ci::Pipeline.external),
|
||||
ci_pipeline_config_auto_devops: count(::Ci::Pipeline.auto_devops_source),
|
||||
ci_pipeline_config_repository: count(::Ci::Pipeline.repository_source),
|
||||
|
@ -643,16 +646,6 @@ module Gitlab
|
|||
}
|
||||
end
|
||||
|
||||
def compliance_unique_visits_data
|
||||
results = ::Gitlab::Analytics::UniqueVisits.compliance_events.each_with_object({}) do |target, hash|
|
||||
hash[target] = redis_usage_data { unique_visit_service.unique_visits_for(targets: target) }
|
||||
end
|
||||
results['compliance_unique_visits_for_any_target'] = redis_usage_data { unique_visit_service.unique_visits_for(targets: :compliance) }
|
||||
results['compliance_unique_visits_for_any_target_monthly'] = redis_usage_data { unique_visit_service.unique_visits_for(targets: :compliance, **monthly_time_range) }
|
||||
|
||||
{ compliance_unique_visits: results }
|
||||
end
|
||||
|
||||
def action_monthly_active_users(time_period)
|
||||
date_range = { date_from: time_period[:created_at].first, date_to: time_period[:created_at].last }
|
||||
|
||||
|
@ -700,7 +693,6 @@ module Gitlab
|
|||
.merge(topology_usage_data)
|
||||
.merge(usage_activity_by_stage)
|
||||
.merge(usage_activity_by_stage(:usage_activity_by_stage_monthly, monthly_time_range_db_params))
|
||||
.merge(compliance_unique_visits_data)
|
||||
.merge(redis_hll_counters)
|
||||
.deep_merge(aggregated_metrics_data)
|
||||
end
|
||||
|
@ -803,12 +795,6 @@ module Gitlab
|
|||
clicked_emails.is_a?(Hash) ? clicked_emails.fetch([track, series], 0) : clicked_emails
|
||||
end
|
||||
|
||||
def unique_visit_service
|
||||
strong_memoize(:unique_visit_service) do
|
||||
::Gitlab::Analytics::UniqueVisits.new
|
||||
end
|
||||
end
|
||||
|
||||
def total_alert_issues
|
||||
# Remove prometheus table queries once they are deprecated
|
||||
# To be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/217407.
|
||||
|
|
|
@ -1008,6 +1008,9 @@ msgstr[1] ""
|
|||
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
|
||||
msgstr ""
|
||||
|
||||
msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
|
||||
msgstr ""
|
||||
|
||||
msgid "%{strong_start}%{release_count}%{strong_end} Release"
|
||||
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
|
||||
msgstr[0] ""
|
||||
|
@ -11364,15 +11367,15 @@ msgstr ""
|
|||
msgid "DastConfig|Customize DAST settings to suit your requirements. Configuration changes made here override those provided by GitLab and are excluded from updates. For details of more advanced configuration options, see the %{docsLinkStart}GitLab DAST documentation%{docsLinkEnd}."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastConfig|DAST Settings"
|
||||
msgid "DastConfig|DAST CI/CD configuration"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastConfig|Enable DAST to automatically test for vulnerabilities in your project's running application, website, or API, in the CI/CD pipeline. Configuration changes must be applied to your .gitlab-ci.yml file to take effect. For details of all configuration options, see the %{linkStart}GitLab DAST documentation%{linkEnd}."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastConfig|Generate code snippet"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastConfig|Scan Configuration"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
|
||||
msgstr ""
|
||||
|
||||
|
@ -25527,9 +25530,6 @@ msgstr ""
|
|||
msgid "New! Suggest changes directly"
|
||||
msgstr ""
|
||||
|
||||
msgid "New..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Newest first"
|
||||
msgstr ""
|
||||
|
||||
|
@ -44325,6 +44325,9 @@ msgstr ""
|
|||
msgid "Your profile"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your project has limited quotas and features"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -2,11 +2,7 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
describe 'Merge request creation from fork', quarantine: {
|
||||
only: :production,
|
||||
issue: "https://gitlab.com/gitlab-org/gitlab/-/issues/343801",
|
||||
type: :investigating
|
||||
} do
|
||||
describe 'Merge request creation from fork' do
|
||||
let(:merge_request) do
|
||||
Resource::MergeRequestFromFork.fabricate_via_browser_ui! do |merge_request|
|
||||
merge_request.fork_branch = 'feature-branch'
|
||||
|
|
|
@ -41,7 +41,7 @@ RSpec.describe 'top nav responsive', :js do
|
|||
end
|
||||
|
||||
it 'has new dropdown', :aggregate_failures do
|
||||
click_button('New...')
|
||||
click_button('Create new')
|
||||
|
||||
expect(page).to have_link('New project', href: new_project_path)
|
||||
expect(page).to have_link('New group', href: new_group_path)
|
||||
|
|
|
@ -15,10 +15,10 @@ RSpec.describe 'top nav tooltips', :js do
|
|||
|
||||
page.find(btn).hover
|
||||
|
||||
expect(page).to have_content('New...')
|
||||
expect(page).to have_content('Create new')
|
||||
|
||||
page.find(btn).click
|
||||
|
||||
expect(page).not_to have_content('New...')
|
||||
expect(page).not_to have_content('Create new')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -55,7 +55,7 @@ RSpec.describe Nav::NewDropdownHelper do
|
|||
end
|
||||
|
||||
it 'has title' do
|
||||
expect(subject[:title]).to eq('New...')
|
||||
expect(subject[:title]).to eq('Create new')
|
||||
end
|
||||
|
||||
context 'when current_user is nil (anonymous)' do
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::BackgroundMigration::MigratePagesToZipStorage do
|
||||
let(:namespace) { create(:group) } # rubocop: disable RSpec/FactoriesInMigrationSpecs
|
||||
let(:migration) { described_class.new }
|
||||
|
||||
describe '#perform' do
|
||||
context 'when there is project to migrate' do
|
||||
let!(:project) { create_project('project') }
|
||||
|
||||
after do
|
||||
FileUtils.rm_rf(project.pages_path)
|
||||
end
|
||||
|
||||
it 'migrates project to zip storage' do
|
||||
expect_next_instance_of(::Pages::MigrateFromLegacyStorageService,
|
||||
anything,
|
||||
ignore_invalid_entries: false,
|
||||
mark_projects_as_not_deployed: false) do |service|
|
||||
expect(service).to receive(:execute_for_batch).with(project.id..project.id).and_call_original
|
||||
end
|
||||
|
||||
migration.perform(project.id, project.id)
|
||||
|
||||
expect(project.reload.pages_metadatum.pages_deployment.file.filename).to eq("_migrated.zip")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_project(path)
|
||||
project = create(:project) # rubocop: disable RSpec/FactoriesInMigrationSpecs
|
||||
project.mark_pages_as_deployed
|
||||
|
||||
FileUtils.mkdir_p File.join(project.pages_path, "public")
|
||||
File.open(File.join(project.pages_path, "public/index.html"), "w") do |f|
|
||||
f.write("Hello!")
|
||||
end
|
||||
|
||||
project
|
||||
end
|
||||
end
|
|
@ -84,7 +84,6 @@ RSpec.describe Gitlab::Usage::Metrics::NameSuggestion do
|
|||
|
||||
context 'for redis metrics' do
|
||||
it_behaves_like 'name suggestion' do
|
||||
# corresponding metric is collected with redis_usage_data { unique_visit_service.unique_visits_for(targets: :analytics) }
|
||||
let(:operation) { :redis }
|
||||
let(:column) { nil }
|
||||
let(:relation) { nil }
|
||||
|
|
|
@ -77,8 +77,7 @@ RSpec.describe Gitlab::Usage::Metrics::NamesSuggestions::Generator do
|
|||
|
||||
context 'for redis metrics' do
|
||||
it_behaves_like 'name suggestion' do
|
||||
# corresponding metric is collected with redis_usage_data { unique_visit_service.unique_visits_for(targets: :compliance) }
|
||||
let(:key_path) { 'compliance_unique_visits.compliance_unique_visits_for_any_target' }
|
||||
let(:key_path) { 'usage_activity_by_stage_monthly.create.merge_requests_users' }
|
||||
let(:name_suggestion) { /<please fill metric name, suggested format is: {subject}_{verb}{ing|ed}_{object} eg: users_creating_epics or merge_requests_viewed_in_single_file_mode>/ }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1204,36 +1204,6 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.compliance_unique_visits_data' do
|
||||
subject { described_class.compliance_unique_visits_data }
|
||||
|
||||
before do
|
||||
allow_next_instance_of(::Gitlab::Analytics::UniqueVisits) do |instance|
|
||||
::Gitlab::Analytics::UniqueVisits.compliance_events.each do |target|
|
||||
allow(instance).to receive(:unique_visits_for).with(targets: target).and_return(123)
|
||||
end
|
||||
|
||||
allow(instance).to receive(:unique_visits_for).with(targets: :compliance).and_return(543)
|
||||
|
||||
allow(instance).to receive(:unique_visits_for).with(targets: :compliance, start_date: 4.weeks.ago.to_date, end_date: Date.current).and_return(987)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns the number of unique visits to pages with compliance features' do
|
||||
expect(subject).to eq({
|
||||
compliance_unique_visits: {
|
||||
'g_compliance_dashboard' => 123,
|
||||
'g_compliance_audit_events' => 123,
|
||||
'i_compliance_credential_inventory' => 123,
|
||||
'i_compliance_audit_events' => 123,
|
||||
'a_compliance_audit_events_api' => 123,
|
||||
'compliance_unique_visits_for_any_target' => 543,
|
||||
'compliance_unique_visits_for_any_target_monthly' => 987
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
describe 'redis_hll_counters' do
|
||||
subject { described_class.redis_hll_counters }
|
||||
|
||||
|
@ -1423,4 +1393,20 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'on Gitlab.com' do
|
||||
before do
|
||||
allow(Gitlab).to receive(:com?).and_return(true)
|
||||
end
|
||||
|
||||
describe '.system_usage_data' do
|
||||
subject { described_class.system_usage_data }
|
||||
|
||||
it 'returns fallback value for disabled metrics' do
|
||||
expect(subject[:counts][:ci_internal_pipelines]).to eq(Gitlab::Utils::UsageData::FALLBACK)
|
||||
expect(subject[:counts][:issues_created_gitlab_alerts]).to eq(Gitlab::Utils::UsageData::FALLBACK)
|
||||
expect(subject[:counts][:issues_created_manually_from_alerts]).to eq(Gitlab::Utils::UsageData::FALLBACK)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe ScheduleMigratePagesToZipStorage, :sidekiq_might_not_need_inline, schema: 20210301200959 do
|
||||
let(:migration_class) { described_class::MIGRATION }
|
||||
let(:migration_name) { migration_class.to_s.demodulize }
|
||||
|
||||
let(:namespaces_table) { table(:namespaces) }
|
||||
let(:projects_table) { table(:projects) }
|
||||
let(:metadata_table) { table(:project_pages_metadata) }
|
||||
let(:deployments_table) { table(:pages_deployments) }
|
||||
|
||||
let(:namespace) { namespaces_table.create!(path: "group", name: "group") }
|
||||
|
||||
def create_project_metadata(path, deployed, with_deployment)
|
||||
project = projects_table.create!(path: path, namespace_id: namespace.id)
|
||||
|
||||
deployment_id = nil
|
||||
|
||||
if with_deployment
|
||||
deployment_id = deployments_table.create!(project_id: project.id, file_store: 1, file: '1', file_count: 1, file_sha256: '123', size: 1).id
|
||||
end
|
||||
|
||||
metadata_table.create!(project_id: project.id, deployed: deployed, pages_deployment_id: deployment_id)
|
||||
end
|
||||
|
||||
it 'correctly schedules background migrations' do
|
||||
Sidekiq::Testing.fake! do
|
||||
freeze_time do
|
||||
create_project_metadata("not-deployed-project", false, false)
|
||||
|
||||
first_id = create_project_metadata("project1", true, false).id
|
||||
last_id = create_project_metadata("project2", true, false).id
|
||||
|
||||
create_project_metadata("project-with-deployment", true, true)
|
||||
|
||||
migrate!
|
||||
|
||||
expect(migration_name).to be_scheduled_delayed_migration(5.minutes, first_id, last_id)
|
||||
expect(BackgroundMigrationWorker.jobs.size).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -294,31 +294,28 @@ RSpec.describe Ci::Build do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.with_reports' do
|
||||
subject { described_class.with_reports(Ci::JobArtifact.test_reports) }
|
||||
describe '.with_artifacts' do
|
||||
subject(:builds) { described_class.with_artifacts(artifact_scope) }
|
||||
|
||||
context 'when build has a test report' do
|
||||
let!(:build) { create(:ci_build, :success, :test_reports) }
|
||||
let(:artifact_scope) { Ci::JobArtifact.where(file_type: 'archive') }
|
||||
|
||||
it 'selects the build' do
|
||||
is_expected.to eq([build])
|
||||
end
|
||||
let!(:build_1) { create(:ci_build, :artifacts) }
|
||||
let!(:build_2) { create(:ci_build, :codequality_reports) }
|
||||
let!(:build_3) { create(:ci_build, :test_reports) }
|
||||
let!(:build_4) { create(:ci_build, :artifacts) }
|
||||
|
||||
it 'returns artifacts matching the given scope' do
|
||||
expect(builds).to contain_exactly(build_1, build_4)
|
||||
end
|
||||
|
||||
context 'when build does not have test reports' do
|
||||
let!(:build) { create(:ci_build, :success, :trace_artifact) }
|
||||
|
||||
it 'does not select the build' do
|
||||
is_expected.to be_empty
|
||||
context 'when there are multiple builds containing artifacts' do
|
||||
before do
|
||||
create_list(:ci_build, 5, :success, :test_reports)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are multiple builds with test reports' do
|
||||
let!(:builds) { create_list(:ci_build, 5, :success, :test_reports) }
|
||||
|
||||
it 'does not execute a query for selecting job artifact one by one' do
|
||||
recorded = ActiveRecord::QueryRecorder.new do
|
||||
subject.each do |build|
|
||||
builds.each do |build|
|
||||
build.job_artifacts.map { |a| a.file.exists? }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -33,10 +33,10 @@ RSpec.describe Ci::JobArtifact do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.with_reports' do
|
||||
describe '.all_reports' do
|
||||
let!(:artifact) { create(:ci_job_artifact, :archive) }
|
||||
|
||||
subject { described_class.with_reports }
|
||||
subject { described_class.all_reports }
|
||||
|
||||
it { is_expected.to be_empty }
|
||||
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Oauth::AuthorizationsController do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:application) { create(:oauth_application, redirect_uri: 'custom://test') }
|
||||
let_it_be(:oauth_authorization_path) do
|
||||
Gitlab::Routing.url_helpers.oauth_authorization_url(
|
||||
client_id: application.uid,
|
||||
response_type: 'code',
|
||||
scope: application.scopes,
|
||||
redirect_uri: application.redirect_uri,
|
||||
state: SecureRandom.hex
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
describe 'GET #new' do
|
||||
context 'when application redirect URI has a custom scheme' do
|
||||
context 'when CSP is disabled' do
|
||||
before do
|
||||
allow_next_instance_of(ActionDispatch::Request) do |instance|
|
||||
allow(instance).to receive(:content_security_policy).and_return(nil)
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not add a CSP' do
|
||||
get oauth_authorization_path
|
||||
|
||||
expect(response.headers['Content-Security-Policy']).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when CSP contains form-action' do
|
||||
before do
|
||||
csp = ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.form_action "'self'"
|
||||
end
|
||||
|
||||
allow_next_instance_of(ActionDispatch::Request) do |instance|
|
||||
allow(instance).to receive(:content_security_policy).and_return(csp)
|
||||
end
|
||||
end
|
||||
|
||||
it 'adds custom scheme to CSP form-action' do
|
||||
get oauth_authorization_path
|
||||
|
||||
expect(response.headers['Content-Security-Policy']).to include("form-action 'self' custom:")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when CSP does not contain form-action' do
|
||||
before do
|
||||
csp = ActionDispatch::ContentSecurityPolicy.new do |p|
|
||||
p.script_src :self, 'https://some-cdn.test'
|
||||
p.style_src :self, 'https://some-cdn.test'
|
||||
end
|
||||
|
||||
allow_next_instance_of(ActionDispatch::Request) do |instance|
|
||||
allow(instance).to receive(:content_security_policy).and_return(csp)
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not add form-action to the CSP' do
|
||||
get oauth_authorization_path
|
||||
|
||||
expect(response.headers['Content-Security-Policy']).not_to include('form-action')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue