diff --git a/.rubocop_todo/gitlab/delegate_predicate_methods.yml b/.rubocop_todo/gitlab/delegate_predicate_methods.yml
index 854cd3e0a83..543b0059fe1 100644
--- a/.rubocop_todo/gitlab/delegate_predicate_methods.yml
+++ b/.rubocop_todo/gitlab/delegate_predicate_methods.yml
@@ -2,5 +2,4 @@
Gitlab/DelegatePredicateMethods:
Exclude:
- app/models/clusters/cluster.rb
- - app/models/project.rb
- ee/app/models/concerns/ee/ci/metadatable.rb
diff --git a/app/assets/javascripts/init_confirm_danger.js b/app/assets/javascripts/init_confirm_danger.js
index 98bfa48740c..6c6cadedf00 100644
--- a/app/assets/javascripts/init_confirm_danger.js
+++ b/app/assets/javascripts/init_confirm_danger.js
@@ -12,11 +12,11 @@ export default () => {
phrase,
buttonText,
buttonClass = '',
- buttonTestid = null,
- buttonVariant = null,
+ buttonTestid,
+ buttonVariant,
confirmDangerMessage,
confirmButtonText = null,
- disabled = false,
+ disabled,
additionalInformation,
htmlConfirmationMessage,
} = el.dataset;
diff --git a/app/assets/javascripts/pages/projects/project_members/index.js b/app/assets/javascripts/pages/projects/project_members/index.js
index bf4fb5f3b7e..595a285032c 100644
--- a/app/assets/javascripts/pages/projects/project_members/index.js
+++ b/app/assets/javascripts/pages/projects/project_members/index.js
@@ -38,7 +38,7 @@ initMembersApp(document.querySelector('.js-project-members-list-app'), {
},
},
[MEMBER_TYPES.group]: {
- tableFields: SHARED_FIELDS.concat('granted'),
+ tableFields: SHARED_FIELDS.concat(['source', 'granted']),
tableAttrs: {
table: { 'data-qa-selector': 'groups_list' },
tr: { 'data-qa-selector': 'group_row' },
@@ -46,7 +46,7 @@ initMembersApp(document.querySelector('.js-project-members-list-app'), {
requestFormatter: groupLinkRequestFormatter,
filteredSearchBar: {
show: true,
- tokens: [],
+ tokens: ['groups_with_inherited_permissions'],
searchParam: 'search_groups',
placeholder: s__('Members|Search groups'),
recentSearchesStorageKey: 'project_group_links',
diff --git a/app/assets/javascripts/projects/compare/components/app.vue b/app/assets/javascripts/projects/compare/components/app.vue
index 3945bed9649..bda58091b97 100644
--- a/app/assets/javascripts/projects/compare/components/app.vue
+++ b/app/assets/javascripts/projects/compare/components/app.vue
@@ -121,27 +121,21 @@ export default {
@selectRevision="onSelectRevision"
/>
-
+
{{ s__('CompareRevisions|Compare') }}
-
+
{{ s__('CompareRevisions|Swap revisions') }}
{{ s__('CompareRevisions|View open merge request') }}
-
+
{{ s__('CompareRevisions|Create merge request') }}
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index 2c2f957a75d..186946a83ad 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -276,28 +276,33 @@ const bindEvents = () => {
);
let isProjectImportUrlDirty = false;
- $projectImportUrl.addEventListener('blur', () => {
- isProjectImportUrlDirty = true;
- debouncedUpdateUrlPathWarningVisibility();
- });
- $projectImportUrl.addEventListener('keyup', () => {
- deriveProjectPathFromUrl($projectImportUrl);
- });
+
+ if ($projectImportUrl) {
+ $projectImportUrl.addEventListener('blur', () => {
+ isProjectImportUrlDirty = true;
+ debouncedUpdateUrlPathWarningVisibility();
+ });
+ $projectImportUrl.addEventListener('keyup', () => {
+ deriveProjectPathFromUrl($projectImportUrl);
+ });
+ }
[$projectImportUrl, $projectImportUrlUser, $projectImportUrlPassword].forEach(($f) => {
- if ($f?.on) {
- $f.on('input', () => {
- if (isProjectImportUrlDirty) {
- debouncedUpdateUrlPathWarningVisibility();
- }
- });
- } else {
- $f.addEventListener('input', () => {
+ if (!$f) return false;
+
+ if ($f.on) {
+ return $f.on('input', () => {
if (isProjectImportUrlDirty) {
debouncedUpdateUrlPathWarningVisibility();
}
});
}
+
+ return $f.addEventListener('input', () => {
+ if (isProjectImportUrlDirty) {
+ debouncedUpdateUrlPathWarningVisibility();
+ }
+ });
});
$projectImportForm.on('submit', async (e) => {
diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb
index 168e703c87d..cd9c6efb106 100644
--- a/app/controllers/projects/project_members_controller.rb
+++ b/app/controllers/projects/project_members_controller.rb
@@ -13,9 +13,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
def index
@sort = params[:sort].presence || sort_value_name
-
- @group_links = @project.project_group_links
- @group_links = @group_links.search(params[:search_groups]) if params[:search_groups].present?
+ @include_relations ||= requested_relations(:groups_with_inherited_permissions)
if can?(current_user, :admin_project_member, @project)
@invited_members = present_members(invited_members)
diff --git a/app/helpers/projects/project_members_helper.rb b/app/helpers/projects/project_members_helper.rb
index d5cc2b72ae9..51a7d3e35d0 100644
--- a/app/helpers/projects/project_members_helper.rb
+++ b/app/helpers/projects/project_members_helper.rb
@@ -1,10 +1,10 @@
# frozen_string_literal: true
module Projects::ProjectMembersHelper
- def project_members_app_data_json(project, members:, group_links:, invited:, access_requests:)
+ def project_members_app_data_json(project, members:, invited:, access_requests:, include_relations:, search:)
{
user: project_members_list_data(project, members, { param_name: :page, params: { search_groups: nil } }),
- group: project_group_links_list_data(project, group_links),
+ group: project_group_links_list_data(project, include_relations, search),
invite: project_members_list_data(project, invited.nil? ? [] : invited),
access_request: project_members_list_data(project, access_requests.nil? ? [] : access_requests),
source_id: project.id,
@@ -57,10 +57,29 @@ module Projects::ProjectMembersHelper
}
end
- def project_group_links_list_data(project, group_links)
+ def project_group_links_list_data(project, include_relations, search)
+ members = []
+
+ if include_relations.include?(:direct)
+ project_group_links = project.project_group_links
+ project_group_links = project_group_links.search(search) if search
+ members += project_group_links_serialized(project, project_group_links)
+ end
+
+ if include_relations.include?(:inherited)
+ group_group_links = project.group_group_links.distinct_on_shared_with_group_id_with_group_access
+ group_group_links = group_group_links.search(search) if search
+ members += group_group_links_serialized(project, group_group_links)
+ end
+
+ if project_group_links.present? && group_group_links.present?
+ members = members.sort_by { |m| -m.dig(:access_level, :integer_value).to_i }
+ .uniq { |m| m.dig(:shared_with_group, :id) }
+ end
+
{
- members: project_group_links_serialized(project, group_links),
- pagination: members_pagination_data(group_links),
+ members: members,
+ pagination: members_pagination_data(members),
member_path: project_group_link_path(project, ':id')
}
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 110f400d3ec..9ca969e0c53 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -441,33 +441,29 @@ class Project < ApplicationRecord
accepts_nested_attributes_for :prometheus_integration, update_only: true
accepts_nested_attributes_for :alerting_setting, update_only: true
- delegate :feature_available?, :builds_enabled?, :wiki_enabled?,
- :merge_requests_enabled?, :forking_enabled?, :issues_enabled?,
- :pages_enabled?, :analytics_enabled?, :snippets_enabled?, :public_pages?, :private_pages?,
- :merge_requests_access_level, :forking_access_level, :issues_access_level,
- :wiki_access_level, :snippets_access_level, :builds_access_level,
- :repository_access_level, :package_registry_access_level, :pages_access_level, :metrics_dashboard_access_level, :analytics_access_level,
- :operations_enabled?, :operations_access_level, :security_and_compliance_access_level,
- :container_registry_access_level, :container_registry_enabled?,
- to: :project_feature, allow_nil: true
- alias_method :container_registry_enabled, :container_registry_enabled?
- delegate :show_default_award_emojis, :show_default_award_emojis=, :show_default_award_emojis?,
- :enforce_auth_checks_on_uploads, :enforce_auth_checks_on_uploads=, :enforce_auth_checks_on_uploads?,
- :warn_about_potentially_unwanted_characters, :warn_about_potentially_unwanted_characters=, :warn_about_potentially_unwanted_characters?,
- to: :project_setting, allow_nil: true
- delegate :scheduled?, :started?, :in_progress?, :failed?, :finished?,
- prefix: :import, to: :import_state, allow_nil: true
+ delegate :merge_requests_access_level, :forking_access_level, :issues_access_level,
+ :wiki_access_level, :snippets_access_level, :builds_access_level,
+ :repository_access_level, :package_registry_access_level, :pages_access_level,
+ :metrics_dashboard_access_level, :analytics_access_level,
+ :operations_access_level, :security_and_compliance_access_level,
+ :container_registry_access_level,
+ to: :project_feature, allow_nil: true
+
+ delegate :show_default_award_emojis, :show_default_award_emojis=,
+ :enforce_auth_checks_on_uploads, :enforce_auth_checks_on_uploads=,
+ :warn_about_potentially_unwanted_characters, :warn_about_potentially_unwanted_characters=,
+ to: :project_setting, allow_nil: true
+
delegate :squash_always?, :squash_never?, :squash_enabled_by_default?, :squash_readonly?, to: :project_setting
delegate :squash_option, :squash_option=, to: :project_setting
delegate :mr_default_target_self, :mr_default_target_self=, to: :project_setting
delegate :previous_default_branch, :previous_default_branch=, to: :project_setting
- delegate :no_import?, to: :import_state, allow_nil: true
delegate :name, to: :owner, allow_nil: true, prefix: true
delegate :members, to: :team, prefix: true
delegate :add_user, :add_users, to: :team
delegate :add_guest, :add_reporter, :add_developer, :add_maintainer, :add_owner, :add_role, to: :team
delegate :group_runners_enabled, :group_runners_enabled=, to: :ci_cd_settings, allow_nil: true
- delegate :root_ancestor, :certificate_based_clusters_enabled?, to: :namespace, allow_nil: true
+ delegate :root_ancestor, to: :namespace, allow_nil: true
delegate :last_pipeline, to: :commit, allow_nil: true
delegate :external_dashboard_url, to: :metrics_setting, allow_nil: true, prefix: true
delegate :dashboard_timezone, to: :metrics_setting, allow_nil: true, prefix: true
@@ -483,7 +479,6 @@ class Project < ApplicationRecord
delegate :allow_merge_on_skipped_pipeline, :allow_merge_on_skipped_pipeline?,
:allow_merge_on_skipped_pipeline=, :has_confluence?, :has_shimo?,
to: :project_setting
- delegate :active?, to: :prometheus_integration, allow_nil: true, prefix: true
delegate :merge_commit_template, :merge_commit_template=, to: :project_setting, allow_nil: true
delegate :squash_commit_template, :squash_commit_template=, to: :project_setting, allow_nil: true
@@ -916,6 +911,14 @@ class Project < ApplicationRecord
association(:namespace).loaded?
end
+ def certificate_based_clusters_enabled?
+ !!namespace&.certificate_based_clusters_enabled?
+ end
+
+ def prometheus_integration_active?
+ !!prometheus_integration&.active?
+ end
+
def personal_namespace_holder?(user)
return false unless personal?
return false unless user
@@ -932,6 +935,42 @@ class Project < ApplicationRecord
super.presence || build_project_setting
end
+ def show_default_award_emojis?
+ !!project_setting&.show_default_award_emojis?
+ end
+
+ def enforce_auth_checks_on_uploads?
+ !!project_setting&.enforce_auth_checks_on_uploads?
+ end
+
+ def warn_about_potentially_unwanted_characters?
+ !!project_setting&.warn_about_potentially_unwanted_characters?
+ end
+
+ def no_import?
+ !!import_state&.no_import?
+ end
+
+ def import_scheduled?
+ !!import_state&.scheduled?
+ end
+
+ def import_started?
+ !!import_state&.started?
+ end
+
+ def import_in_progress?
+ !!import_state&.in_progress?
+ end
+
+ def import_failed?
+ !!import_state&.failed?
+ end
+
+ def import_finished?
+ !!import_state&.finished?
+ end
+
def all_pipelines
if builds_enabled?
super
@@ -1839,6 +1878,59 @@ class Project < ApplicationRecord
end
end
+ def feature_available?(feature, user = nil)
+ !!project_feature&.feature_available?(feature, user)
+ end
+
+ def builds_enabled?
+ !!project_feature&.builds_enabled?
+ end
+
+ def wiki_enabled?
+ !!project_feature&.wiki_enabled?
+ end
+
+ def merge_requests_enabled?
+ !!project_feature&.merge_requests_enabled?
+ end
+
+ def forking_enabled?
+ !!project_feature&.forking_enabled?
+ end
+
+ def issues_enabled?
+ !!project_feature&.issues_enabled?
+ end
+
+ def pages_enabled?
+ !!project_feature&.pages_enabled?
+ end
+
+ def analytics_enabled?
+ !!project_feature&.analytics_enabled?
+ end
+
+ def snippets_enabled?
+ !!project_feature&.snippets_enabled?
+ end
+
+ def public_pages?
+ !!project_feature&.public_pages?
+ end
+
+ def private_pages?
+ !!project_feature&.private_pages?
+ end
+
+ def operations_enabled?
+ !!project_feature&.operations_enabled?
+ end
+
+ def container_registry_enabled?
+ !!project_feature&.container_registry_enabled?
+ end
+ alias_method :container_registry_enabled, :container_registry_enabled?
+
def enable_ci
project_feature.update_attribute(:builds_access_level, ProjectFeature::ENABLED)
end
@@ -2902,6 +2994,10 @@ class Project < ApplicationRecord
build_artifacts_size_refresh&.started?
end
+ def group_group_links
+ group&.shared_with_group_links&.of_ancestors_and_self || GroupGroupLink.none
+ end
+
def security_training_available?
licensed_feature_available?(:security_training)
end
diff --git a/app/services/packages/pypi/create_package_service.rb b/app/services/packages/pypi/create_package_service.rb
index 5d7e967ceb0..b464ce4504a 100644
--- a/app/services/packages/pypi/create_package_service.rb
+++ b/app/services/packages/pypi/create_package_service.rb
@@ -16,6 +16,8 @@ module Packages
raise ActiveRecord::RecordInvalid, meta
end
+ params.delete(:md5_digest) if Gitlab::FIPS.enabled?
+
Packages::Pypi::Metadatum.upsert(meta.attributes)
::Packages::CreatePackageFileService.new(created_package, file_params).execute
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 1a5a7ae94d3..fd91786e9f9 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -39,7 +39,8 @@
.js-project-members-list-app{ data: { members_data: project_members_app_data_json(@project,
members: @project_members,
- group_links: @group_links,
invited: @invited_members,
- access_requests: @requesters) } }
+ access_requests: @requesters,
+ include_relations: @include_relations,
+ search: params[:search_groups]) } }
= gl_loading_icon(css_class: 'gl-my-5', size: 'md')
diff --git a/config/application.rb b/config/application.rb
index ad76a6d8e7e..5e18d5fdd96 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -20,6 +20,8 @@ module Gitlab
config.view_component.preview_route = "/-/view_component/previews"
+ config.active_support.hash_digest_class = ::OpenSSL::Digest::SHA256
+
# This section contains configuration from Rails upgrades to override the new defaults so that we
# keep existing behavior.
#
@@ -38,7 +40,6 @@ module Gitlab
# Rails 5.2
config.action_dispatch.use_authenticated_cookie_encryption = false
config.active_support.use_authenticated_message_encryption = false
- config.active_support.hash_digest_class = ::Digest::MD5 # New default is ::Digest::SHA1
config.action_controller.default_protect_from_forgery = false
config.action_view.form_with_generates_ids = false
diff --git a/config/feature_flags/development/active_support_hash_digest_sha256.yml b/config/feature_flags/development/active_support_hash_digest_sha256.yml
deleted file mode 100644
index 147b84bf112..00000000000
--- a/config/feature_flags/development/active_support_hash_digest_sha256.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: active_support_hash_digest_sha256
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90098
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365314
-milestone: '15.1'
-type: development
-group: group::sharding
-default_enabled: false
diff --git a/config/initializers/rack_timeout.rb b/config/initializers/rack_timeout.rb
index c2f2f3e093c..c9dc9fa209a 100644
--- a/config/initializers/rack_timeout.rb
+++ b/config/initializers/rack_timeout.rb
@@ -8,7 +8,8 @@
# and it's used only as the last resort. In such case this termination is
# logged and we should fix the potential timeout issue in the code itself.
-if Gitlab::Runtime.puma? && !Rails.env.test?
+if Gitlab::Runtime.puma? && !Rails.env.test? &&
+ Gitlab::Utils.to_boolean(ENV['GITLAB_RAILS_RACK_TIMEOUT_ENABLE'], default: true)
Rack::Timeout::Logger.level = Logger::ERROR
Gitlab::Application.configure do |config|
diff --git a/config/initializers/set_active_support_hash_digest_class.rb b/config/initializers/set_active_support_hash_digest_class.rb
deleted file mode 100644
index 743b45eed34..00000000000
--- a/config/initializers/set_active_support_hash_digest_class.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-Rails.application.configure do
- # We set ActiveSupport::Digest.hash_digest_class directly copying
- # See https://github.com/rails/rails/blob/6-1-stable/activesupport/lib/active_support/railtie.rb#L96-L98
- #
- # Note that is the only usage of config.active_support.hash_digest_class
- config.after_initialize do
- ActiveSupport::Digest.hash_digest_class = Gitlab::HashDigest::Facade
- end
-end
diff --git a/doc/administration/audit_event_streaming.md b/doc/administration/audit_event_streaming.md
index 5fc92c5fb6c..4e44a3a3e79 100644
--- a/doc/administration/audit_event_streaming.md
+++ b/doc/administration/audit_event_streaming.md
@@ -169,6 +169,20 @@ mutation {
The header is created if the returned `errors` object is empty.
+### Update with the API
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/361964) in GitLab 15.2.
+
+Group owners can update a HTTP header using the GraphQL `auditEventsStreamingHeadersCreate` mutation.
+
+```graphql
+mutation {
+ auditEventsStreamingHeadersUpdate(input: { headerId: "gid://gitlab/AuditEvents::Streaming::Header/24255", key: "new-foo", value: "new-bar" }) {
+ errors
+ }
+}
+```
+
### Delete with the API
Group owners can remove a HTTP header using the GraphQL `auditEventsStreamingHeadersDestroy` mutation. You can retrieve the header ID
diff --git a/doc/administration/auth/ldap/ldap_synchronization.md b/doc/administration/auth/ldap/ldap_synchronization.md
index 0f0d301bfa9..b0ada1c11dd 100644
--- a/doc/administration/auth/ldap/ldap_synchronization.md
+++ b/doc/administration/auth/ldap/ldap_synchronization.md
@@ -42,11 +42,6 @@ The process also updates the following user information:
- SSH public keys (if `sync_ssh_keys` is set)
- Kerberos identity (if Kerberos is enabled)
-The LDAP sync process:
-
-- Updates existing users.
-- Creates new users on first sign in.
-
### Adjust LDAP user sync schedule
By default, GitLab runs a worker once per day at 01:30 a.m. server time to
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index b3cdaeea85c..5c179e18a4b 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -778,6 +778,27 @@ Input type: `AuditEventsStreamingHeadersDestroyInput`
| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+### `Mutation.auditEventsStreamingHeadersUpdate`
+
+Input type: `AuditEventsStreamingHeadersUpdateInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `headerId` | [`AuditEventsStreamingHeaderID!`](#auditeventsstreamingheaderid) | Header to update. |
+| `key` | [`String!`](#string) | Header key. |
+| `value` | [`String!`](#string) | Header value. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+| `header` | [`AuditEventStreamingHeader`](#auditeventstreamingheader) | Updates header. |
+
### `Mutation.awardEmojiAdd`
Input type: `AwardEmojiAddInput`
diff --git a/doc/api/jobs.md b/doc/api/jobs.md
index 85cdf7d892a..b23c33ddc0d 100644
--- a/doc/api/jobs.md
+++ b/doc/api/jobs.md
@@ -692,7 +692,7 @@ Example of response
"finished_at": null,
"duration": 8,
"queued_duration": 0.010,
- "id": 42,
+ "id": 1,
"name": "rubocop",
"ref": "main",
"artifacts": [],
@@ -742,7 +742,7 @@ Example of response
"finished_at": null,
"duration": null,
"queued_duration": 0.010,
- "id": 42,
+ "id": 1,
"name": "rubocop",
"ref": "main",
"artifacts": [],
@@ -792,7 +792,7 @@ Example of response
"coverage": null,
"allow_failure": false,
"download_url": null,
- "id": 42,
+ "id": 1,
"name": "rubocop",
"ref": "main",
"artifacts": [],
@@ -873,7 +873,7 @@ Example response:
"finished_at": null,
"duration": null,
"queued_duration": 0.010,
- "id": 42,
+ "id": 1,
"name": "rubocop",
"ref": "main",
"artifacts": [],
diff --git a/doc/api/packages/pypi.md b/doc/api/packages/pypi.md
index 3e23ded61f4..e6204d87e1f 100644
--- a/doc/api/packages/pypi.md
+++ b/doc/api/packages/pypi.md
@@ -20,6 +20,10 @@ These endpoints do not adhere to the standard API authentication methods.
See the [PyPI package registry documentation](../../user/packages/pypi_repository/index.md)
for details on which headers and token types are supported.
+NOTE:
+[Twine 3.4.2](https://twine.readthedocs.io/en/stable/changelog.html?highlight=FIPS#id28) or greater
+is recommended when [FIPS mode](../../development/fips_compliance.md) is enabled.
+
## Download a package file from a group
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/225545) in GitLab 13.12.
diff --git a/doc/development/cicd/templates.md b/doc/development/cicd/templates.md
index 8d88e7155a2..eafb1817a0a 100644
--- a/doc/development/cicd/templates.md
+++ b/doc/development/cicd/templates.md
@@ -393,7 +393,7 @@ is updated in a major version GitLab release.
### Add metrics
-Every CI/CD template must also have metrics defined to track their use.
+Every CI/CD template must also have metrics defined to track their use. The CI/CD template monthly usage report can be found in [Sisense (GitLab team members only)](https://app.periscopedata.com/app/gitlab/785953/Pipeline-Authoring-Dashboard?widget=14910475&udv=0).
To add a metric definition for a new template:
diff --git a/lib/api/pypi_packages.rb b/lib/api/pypi_packages.rb
index 5bf3c3b8aac..ae53f08fb1d 100644
--- a/lib/api/pypi_packages.rb
+++ b/lib/api/pypi_packages.rb
@@ -217,6 +217,8 @@ module API
track_package_event('push_package', :pypi, project: authorized_user_project, user: current_user, namespace: authorized_user_project.namespace)
+ unprocessable_entity! if Gitlab::FIPS.enabled? && declared_params[:md5_digest].present?
+
::Packages::Pypi::CreatePackageService
.new(authorized_user_project, current_user, declared_params.merge(build: current_authenticated_job))
.execute
diff --git a/lib/gitlab/content_security_policy/config_loader.rb b/lib/gitlab/content_security_policy/config_loader.rb
index 574a7dceaa4..f57050e5a1f 100644
--- a/lib/gitlab/content_security_policy/config_loader.rb
+++ b/lib/gitlab/content_security_policy/config_loader.rb
@@ -7,6 +7,8 @@ module Gitlab
form_action frame_ancestors frame_src img_src manifest_src
media_src object_src report_uri script_src style_src worker_src).freeze
+ DEFAULT_FALLBACK_VALUE = '
'
+
def self.default_enabled
Rails.env.development? || Rails.env.test?
end
@@ -62,8 +64,10 @@ module Gitlab
end
def initialize(csp_directives)
+ # Using falls back to the default values.
+ directives = csp_directives.reject { |_, value| value == DEFAULT_FALLBACK_VALUE }
@merged_csp_directives =
- HashWithIndifferentAccess.new(csp_directives)
+ HashWithIndifferentAccess.new(directives)
.reverse_merge(::Gitlab::ContentSecurityPolicy::ConfigLoader.default_directives)
end
diff --git a/lib/gitlab/hash_digest/facade.rb b/lib/gitlab/hash_digest/facade.rb
deleted file mode 100644
index d8efef02893..00000000000
--- a/lib/gitlab/hash_digest/facade.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module HashDigest
- # Used for rolling out to use OpenSSL::Digest::SHA256
- # for ActiveSupport::Digest
- class Facade
- class << self
- def hexdigest(...)
- hash_digest_class.hexdigest(...)
- end
-
- def hash_digest_class
- if use_sha256?
- ::OpenSSL::Digest::SHA256
- else
- ::Digest::MD5 # rubocop:disable Fips/MD5
- end
- end
-
- def use_sha256?
- return false unless Feature.feature_flags_available?
-
- Feature.enabled?(:active_support_hash_digest_sha256)
- end
- end
- end
- end
-end
diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb
index fe468de60cd..b331e1724db 100644
--- a/qa/qa/page/project/issue/show.rb
+++ b/qa/qa/page/project/issue/show.rb
@@ -79,7 +79,10 @@ module QA
def delete_issue
click_element(:issue_actions_ellipsis_dropdown)
- click_element(:delete_issue_button, Page::Modal::DeleteIssue)
+
+ click_element(:delete_issue_button,
+ Page::Modal::DeleteIssue,
+ wait: Support::Repeater::DEFAULT_MAX_WAIT_TIME)
Page::Modal::DeleteIssue.perform(&:confirm_delete_issue)
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/personal_project_permissions_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/personal_project_permissions_spec.rb
deleted file mode 100644
index fb486ab1532..00000000000
--- a/qa/qa/specs/features/browser_ui/1_manage/project/personal_project_permissions_spec.rb
+++ /dev/null
@@ -1,94 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- RSpec.describe 'Manage' do
- describe 'Personal project permissions', :reliable do
- let!(:owner) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) }
-
- let!(:owner_api_client) { Runtime::API::Client.new(:gitlab, user: owner) }
-
- let!(:project) do
- Resource::Project.fabricate_via_api! do |project|
- project.api_client = owner_api_client
- project.name = 'qa-owner-personal-project'
- project.personal_namespace = owner.username
- end
- end
-
- after do
- project&.remove_via_api!
- end
-
- context 'when user is added as Owner' do
- let(:issue) do
- Resource::Issue.fabricate_via_api! do |issue|
- issue.api_client = owner_api_client
- issue.project = project
- issue.title = 'Test Owner deletes issue'
- end
- end
-
- before do
- Flow::Login.sign_in(as: owner)
- end
-
- it "has Owner role with Owner permissions", testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352542' do
- Page::Dashboard::Projects.perform do |projects|
- projects.filter_by_name(project.name)
-
- expect(projects).to have_project_with_access_role(project.name, 'Owner')
- end
-
- expect_owner_permissions_allow_delete_issue
- end
- end
-
- context 'when user is added as Maintainer' do
- let(:maintainer) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_2, Runtime::Env.gitlab_qa_password_2) }
-
- let(:issue) do
- Resource::Issue.fabricate_via_api! do |issue|
- issue.api_client = owner_api_client
- issue.project = project
- issue.title = 'Test Maintainer deletes issue'
- end
- end
-
- before do
- project.add_member(maintainer, Resource::Members::AccessLevel::MAINTAINER)
- Flow::Login.sign_in(as: maintainer)
- end
-
- it "has Maintainer role without Owner permissions", testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352607' do
- Page::Dashboard::Projects.perform do |projects|
- projects.filter_by_name(project.name)
-
- expect(projects).to have_project_with_access_role(project.name, 'Maintainer')
- end
-
- expect_maintainer_permissions_do_not_allow_delete_issue
- end
- end
-
- private
-
- def expect_owner_permissions_allow_delete_issue
- issue.visit!
-
- Page::Project::Issue::Show.perform(&:delete_issue)
-
- Page::Project::Issue::Index.perform do |index|
- expect(index).not_to have_issue(issue)
- end
- end
-
- def expect_maintainer_permissions_do_not_allow_delete_issue
- issue.visit!
-
- Page::Project::Issue::Show.perform do |issue|
- expect(issue).not_to have_delete_issue_button
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/project_owner_permissions_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/project_owner_permissions_spec.rb
new file mode 100644
index 00000000000..2f148c4051c
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/project_owner_permissions_spec.rb
@@ -0,0 +1,104 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Manage' do
+ describe 'Project owner permissions' do
+ let!(:owner) do
+ Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1)
+ end
+
+ let!(:owner_api_client) { Runtime::API::Client.new(:gitlab, user: owner) }
+
+ let!(:maintainer) do
+ Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_2, Runtime::Env.gitlab_qa_password_2)
+ end
+
+ shared_examples 'when user is added as owner' do |project_type, testcase|
+ let!(:issue) do
+ Resource::Issue.fabricate_via_api! do |issue|
+ issue.api_client = owner_api_client
+ issue.project = project
+ issue.title = 'Test Owner Deletes Issue'
+ end
+ end
+
+ before do
+ project.add_member(owner, Resource::Members::AccessLevel::OWNER) if project_type == :group_project
+ Flow::Login.sign_in(as: owner)
+ end
+
+ it "has owner role with owner permissions", testcase: testcase do
+ Page::Dashboard::Projects.perform do |projects|
+ projects.filter_by_name(project.name)
+
+ expect(projects).to have_project_with_access_role(project.name, 'Owner')
+ end
+
+ issue.visit!
+
+ Page::Project::Issue::Show.perform(&:delete_issue)
+
+ Page::Project::Issue::Index.perform do |index|
+ expect(index).not_to have_issue(issue)
+ end
+ end
+ end
+
+ shared_examples 'when user is added as maintainer' do |testcase|
+ let!(:issue) do
+ Resource::Issue.fabricate_via_api! do |issue|
+ issue.api_client = owner_api_client
+ issue.project = project
+ issue.title = 'Test Maintainer Deletes Issue'
+ end
+ end
+
+ before do
+ project.add_member(maintainer, Resource::Members::AccessLevel::MAINTAINER)
+ Flow::Login.sign_in(as: maintainer)
+ end
+
+ it "has maintainer role without owner permissions", testcase: testcase do
+ Page::Dashboard::Projects.perform do |projects|
+ projects.filter_by_name(project.name)
+
+ expect(projects).to have_project_with_access_role(project.name, 'Maintainer')
+ end
+
+ issue.visit!
+
+ Page::Project::Issue::Show.perform do |issue|
+ expect(issue).not_to have_delete_issue_button
+ end
+ end
+ end
+
+ context 'for personal projects' do
+ let!(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.api_client = owner_api_client
+ project.name = 'qa-owner-personal-project'
+ project.personal_namespace = owner.username
+ end
+ end
+
+ it_behaves_like 'when user is added as owner', :personal_project, 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352542'
+ it_behaves_like 'when user is added as maintainer', 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352607'
+ end
+
+ context 'for group projects' do
+ let!(:group) { Resource::Group.fabricate_via_api! }
+
+ let!(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.group = group
+ project.name = 'qa-owner-group-project'
+ end
+ end
+
+ it_behaves_like 'when user is added as owner', :group_project, 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/366436'
+ it_behaves_like 'when user is added as maintainer', 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/366435'
+ end
+ end
+ end
+end
diff --git a/scripts/lib/glfm/parse_examples.rb b/scripts/lib/glfm/parse_examples.rb
index 051cbdc7941..14634bcfb3e 100644
--- a/scripts/lib/glfm/parse_examples.rb
+++ b/scripts/lib/glfm/parse_examples.rb
@@ -41,7 +41,6 @@ module Glfm
h1_regex = /\A# / # new logic compared to original Python code
h2_regex = /\A## / # new logic compared to original Python code
- h3_regex = /\A### / # new logic compared to original Python code
header_regex = /\A#+ / # Added beginning of line anchor to original Python code
spec_txt_lines.each do |line|
@@ -103,19 +102,12 @@ module Glfm
# reset the headers array if we found a new H1
headers = [] if line =~ h1_regex
- if headers.length == 2 && line =~ h2_regex
- # pop the last entry from the headers array if we are in an H2 and found a new H2
- headers.pop
- elsif headers.length == 3 && line =~ h3_regex
- # pop the last entry from the headers array if we are in an H3 and found a new H3
- headers.pop
- elsif headers.length == 3 && line =~ h2_regex
- # pop the last two entries from the headers array if we are in an H3 and found a new H2
- headers.pop(2)
- end
+ # headers should be size 2 or less [, ]
+ # pop the last entry from the headers array if we are in an H2 and found a new H2
+ headers.pop if headers.length == 2 && line =~ h2_regex
# push the new header text to the headers array
- headers << headertext # New logic compared to original Python code
+ headers << headertext if line =~ h1_regex || line =~ h2_regex
else
# Else if we are in regular text...
diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb
index 9bb34a38005..46eb340cbba 100644
--- a/spec/controllers/projects/project_members_controller_spec.rb
+++ b/spec/controllers/projects/project_members_controller_spec.rb
@@ -68,27 +68,6 @@ RSpec.describe Projects::ProjectMembersController do
end
end
- context 'group links' do
- let_it_be(:project_group_link) { create(:project_group_link, project: project, group: group) }
-
- it 'lists group links' do
- get :index, params: { namespace_id: project.namespace, project_id: project }
-
- expect(assigns(:group_links).map(&:id)).to contain_exactly(project_group_link.id)
- end
-
- context 'when `search_groups` param is present' do
- let(:group_2) { create(:group, :public, name: 'group_2') }
- let!(:project_group_link_2) { create(:project_group_link, project: project, group: group_2) }
-
- it 'lists group links that match search' do
- get :index, params: { namespace_id: project.namespace, project_id: project, search_groups: 'group_2' }
-
- expect(assigns(:group_links).map(&:id)).to contain_exactly(project_group_link_2.id)
- end
- end
- end
-
context 'invited members' do
let_it_be(:invited_member) { create(:project_member, :invited, project: project) }
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index c323e60bb71..a1e92a79516 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -298,6 +298,20 @@ RSpec.describe 'New project', :js do
end
end
+ context 'Import project options without any sources', :js do
+ before do
+ stub_application_setting(import_sources: [])
+
+ visit new_project_path
+ click_link 'Import project'
+ end
+
+ it 'displays the no import options message' do
+ expect(page).to have_text s_('ProjectsNew|No import options available')
+ expect(page).to have_text s_('ProjectsNew|Contact an administrator to enable options for importing your project.')
+ end
+ end
+
context 'Import project options', :js do
before do
visit new_project_path
diff --git a/spec/helpers/projects/project_members_helper_spec.rb b/spec/helpers/projects/project_members_helper_spec.rb
index 2414a1782c5..844c33de635 100644
--- a/spec/helpers/projects/project_members_helper_spec.rb
+++ b/spec/helpers/projects/project_members_helper_spec.rb
@@ -14,7 +14,6 @@ RSpec.describe Projects::ProjectMembersHelper do
describe 'project members' do
let_it_be(:members) { create_list(:project_member, 2, project: project) }
- let_it_be(:group_links) { create_list(:project_group_link, 1, project: project) }
let_it_be(:invited) { create_list(:project_member, 2, :invited, project: project) }
let_it_be(:access_requests) { create_list(:project_member, 2, :access_request, project: project) }
@@ -26,9 +25,10 @@ RSpec.describe Projects::ProjectMembersHelper do
helper.project_members_app_data_json(
project,
members: present_members(members_collection),
- group_links: group_links,
invited: present_members(invited),
- access_requests: present_members(access_requests)
+ access_requests: present_members(access_requests),
+ include_relations: [:inherited, :direct],
+ search: nil
)
)
end
@@ -84,6 +84,70 @@ RSpec.describe Projects::ProjectMembersHelper do
expect(subject['user']['pagination']).to match(expected)
end
end
+
+ context 'group links' do
+ let_it_be(:shared_with_group) { create(:group) }
+ let_it_be(:group_link) { create(:project_group_link, project: project, group: shared_with_group) }
+
+ before do
+ allow(helper).to receive(:project_group_link_path).with(project, ':id').and_return('/foo-group/foo-project/-/group_links/:id')
+ end
+
+ it 'sets `group.members` property that matches json schema' do
+ expect(subject['group']['members'].to_json).to match_schema('group_link/project_group_links')
+ end
+
+ it 'sets `member_path` property' do
+ expect(subject['group']['member_path']).to eq('/foo-group/foo-project/-/group_links/:id')
+ end
+
+ context 'inherited' do
+ let_it_be(:shared_with_group_1) { create(:group) }
+ let_it_be(:shared_with_group_2) { create(:group) }
+ let_it_be(:shared_with_group_3) { create(:group) }
+ let_it_be(:shared_with_group_4) { create(:group) }
+ let_it_be(:shared_with_group_5) { create(:group) }
+ let_it_be(:top_group) { create(:group) }
+ let_it_be(:sub_group) { create(:group, parent: top_group) }
+ let_it_be(:project) { create(:project, group: sub_group) }
+ let_it_be(:group_link_1) { create(:group_group_link, shared_group: top_group, shared_with_group: shared_with_group_1, group_access: Gitlab::Access::GUEST) }
+ let_it_be(:group_link_2) { create(:group_group_link, shared_group: top_group, shared_with_group: shared_with_group_4, group_access: Gitlab::Access::GUEST) }
+ let_it_be(:group_link_3) { create(:group_group_link, shared_group: top_group, shared_with_group: shared_with_group_5, group_access: Gitlab::Access::DEVELOPER) }
+ let_it_be(:group_link_4) { create(:group_group_link, shared_group: sub_group, shared_with_group: shared_with_group_2, group_access: Gitlab::Access::DEVELOPER) }
+ let_it_be(:group_link_5) { create(:group_group_link, shared_group: sub_group, shared_with_group: shared_with_group_4, group_access: Gitlab::Access::DEVELOPER) }
+ let_it_be(:group_link_6) { create(:group_group_link, shared_group: sub_group, shared_with_group: shared_with_group_5, group_access: Gitlab::Access::GUEST) }
+ let_it_be(:group_link_7) { create(:project_group_link, project: project, group: shared_with_group_1, group_access: Gitlab::Access::DEVELOPER) }
+ let_it_be(:group_link_8) { create(:project_group_link, project: project, group: shared_with_group_2, group_access: Gitlab::Access::GUEST) }
+ let_it_be(:group_link_9) { create(:project_group_link, project: project, group: shared_with_group_3, group_access: Gitlab::Access::REPORTER) }
+
+ subject do
+ Gitlab::Json.parse(
+ helper.project_members_app_data_json(
+ project,
+ members: present_members(members_collection),
+ invited: present_members(invited),
+ access_requests: present_members(access_requests),
+ include_relations: include_relations,
+ search: nil
+ )
+ )
+ end
+
+ using RSpec::Parameterized::TableSyntax
+
+ where(:include_relations, :result) do
+ [:inherited, :direct] | lazy { [group_link_7, group_link_4, group_link_9, group_link_5, group_link_3].map(&:id) }
+ [:inherited] | lazy { [group_link_1, group_link_4, group_link_5, group_link_3].map(&:id) }
+ [:direct] | lazy { [group_link_7, group_link_8, group_link_9].map(&:id) }
+ end
+
+ with_them do
+ it 'returns correct group links' do
+ expect(subject['group']['members'].map { |link| link['id'] }).to match_array(result)
+ end
+ end
+ end
+ end
end
end
diff --git a/spec/initializers/set_active_support_hash_digest_class_spec.rb b/spec/initializers/set_active_support_hash_digest_class_spec.rb
deleted file mode 100644
index 256e8a1f218..00000000000
--- a/spec/initializers/set_active_support_hash_digest_class_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'setting ActiveSupport::Digest.hash_digest_class' do
- it 'sets overrides config.active_support.hash_digest_class' do
- expect(ActiveSupport::Digest.hash_digest_class).to eq(Gitlab::HashDigest::Facade)
- end
-end
diff --git a/spec/lib/gitlab/ci/tags/bulk_insert_spec.rb b/spec/lib/gitlab/ci/tags/bulk_insert_spec.rb
index 72574d50176..5ab859241c6 100644
--- a/spec/lib/gitlab/ci/tags/bulk_insert_spec.rb
+++ b/spec/lib/gitlab/ci/tags/bulk_insert_spec.rb
@@ -71,6 +71,13 @@ RSpec.describe Gitlab::Ci::Tags::BulkInsert do
expect(Ci::Build.tagged_with('tag3')).to include(other_job)
end
+ it 'strips tags' do
+ job.tag_list = [' taga', 'tagb ', ' tagc ']
+
+ service.insert!
+ expect(job.tags.map(&:name)).to match_array(%w[taga tagb tagc])
+ end
+
context 'when batching inserts for tags' do
before do
stub_const("#{described_class}::TAGS_BATCH_SIZE", 2)
diff --git a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
index 109e83be294..7ab60af31e5 100644
--- a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
+++ b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
@@ -220,10 +220,11 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
expect(policy.directives['base-uri']).to be_nil
end
- it 'returns default values for directives not defined by the user' do
+ it 'returns default values for directives not defined by the user or with and disables directives set to false' do
# Explicitly disabling script_src and setting report_uri
csp_config[:directives] = {
script_src: false,
+ style_src: '',
report_uri: 'https://example.org'
}
diff --git a/spec/lib/gitlab/hash_digest/facade_spec.rb b/spec/lib/gitlab/hash_digest/facade_spec.rb
deleted file mode 100644
index b352744513e..00000000000
--- a/spec/lib/gitlab/hash_digest/facade_spec.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::HashDigest::Facade do
- describe '.hexdigest' do
- let(:plaintext) { 'something that is plaintext' }
-
- let(:sha256_hash) { OpenSSL::Digest::SHA256.hexdigest(plaintext) }
- let(:md5_hash) { Digest::MD5.hexdigest(plaintext) } # rubocop:disable Fips/MD5
-
- it 'uses SHA256' do
- expect(described_class.hexdigest(plaintext)).to eq(sha256_hash)
- end
-
- context 'when feature flags is not available' do
- before do
- allow(Feature).to receive(:feature_flags_available?).and_return(false)
- end
-
- it 'uses MD5' do
- expect(described_class.hexdigest(plaintext)).to eq(md5_hash)
- end
- end
-
- context 'when active_support_hash_digest_sha256 FF is disabled' do
- before do
- stub_feature_flags(active_support_hash_digest_sha256: false)
- end
-
- it 'uses MD5' do
- expect(described_class.hexdigest(plaintext)).to eq(md5_hash)
- end
- end
- end
-end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 587c7332ad3..dea1c5c57e1 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -2073,6 +2073,13 @@ RSpec.describe Ci::Build do
expect(build.tags.first.name).to eq('tag')
end
+ it 'strips tags' do
+ build.tag_list = [' taga', 'tagb ', ' tagc ']
+
+ build.save!
+ expect(build.tags.map(&:name)).to match_array(%w[taga tagb tagc])
+ end
+
context 'with BulkInsertableTags.with_bulk_insert_tags' do
it 'does not save_tags' do
Ci::BulkInsertableTags.with_bulk_insert_tags do
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 06baad38768..b1f5cca647b 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -1190,6 +1190,13 @@ RSpec.describe Ci::Runner do
expect(runner.tags.first.name).to eq('tag')
end
+ it 'strips tags' do
+ runner.tag_list = [' taga', 'tagb ', ' tagc ']
+
+ runner.save!
+ expect(runner.tags.map(&:name)).to match_array(%w[taga tagb tagc])
+ end
+
context 'with BulkInsertableTags.with_bulk_insert_tags' do
it 'does not save_tags' do
Ci::BulkInsertableTags.with_bulk_insert_tags do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 421e9ec47c3..b7ed7b20833 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -8390,6 +8390,27 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ describe '#group_group_links' do
+ context 'with group project' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+
+ it 'returns group links of group' do
+ expect(group).to receive_message_chain(:shared_with_group_links, :of_ancestors_and_self)
+
+ project.group_group_links
+ end
+ end
+
+ context 'with personal project' do
+ let_it_be(:project) { create(:project) }
+
+ it 'returns none' do
+ expect(project.group_group_links).to eq(GroupGroupLink.none)
+ end
+ end
+ end
+
describe '#security_training_available?' do
subject { build(:project) }
diff --git a/spec/requests/api/pypi_packages_spec.rb b/spec/requests/api/pypi_packages_spec.rb
index a24b852cdac..9e0d3780fd8 100644
--- a/spec/requests/api/pypi_packages_spec.rb
+++ b/spec/requests/api/pypi_packages_spec.rb
@@ -197,7 +197,7 @@ RSpec.describe API::PypiPackages do
let(:url) { "/projects/#{project.id}/packages/pypi" }
let(:headers) { {} }
let(:requires_python) { '>=3.7' }
- let(:base_params) { { requires_python: requires_python, version: '1.0.0', name: 'sample-project', sha256_digest: '1' * 64 } }
+ let(:base_params) { { requires_python: requires_python, version: '1.0.0', name: 'sample-project', sha256_digest: '1' * 64, md5_digest: '1' * 32 } }
let(:params) { base_params.merge(content: temp_file(file_name)) }
let(:send_rewritten_field) { true }
let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } }
@@ -254,6 +254,19 @@ RSpec.describe API::PypiPackages do
it_behaves_like 'PyPI package creation', :developer, :created, true
end
+
+ context 'without md5_digest' do
+ let(:token) { personal_access_token.token }
+ let(:user_headers) { basic_auth_header(user.username, token) }
+ let(:headers) { user_headers.merge(workhorse_headers) }
+ let(:params) { base_params.merge(content: temp_file(file_name)) }
+
+ before do
+ params.delete(:md5_digest)
+ end
+
+ it_behaves_like 'PyPI package creation', :developer, :created, true, false
+ end
end
context 'with required_python too big' do
diff --git a/spec/services/packages/pypi/create_package_service_spec.rb b/spec/services/packages/pypi/create_package_service_spec.rb
index 354ac92b99a..6794ab4d9d6 100644
--- a/spec/services/packages/pypi/create_package_service_spec.rb
+++ b/spec/services/packages/pypi/create_package_service_spec.rb
@@ -42,6 +42,21 @@ RSpec.describe Packages::Pypi::CreatePackageService, :aggregate_failures do
end
end
+ context 'with FIPS mode', :fips_mode do
+ it 'does not generate file_md5' do
+ expect { subject }.to change { Packages::Package.pypi.count }.by(1)
+
+ expect(created_package.name).to eq 'foo'
+ expect(created_package.version).to eq '1.0'
+
+ expect(created_package.pypi_metadatum.required_python).to eq '>=2.7'
+ expect(created_package.package_files.size).to eq 1
+ expect(created_package.package_files.first.file_name).to eq 'foo.tgz'
+ expect(created_package.package_files.first.file_sha256).to eq sha256
+ expect(created_package.package_files.first.file_md5).to be_nil
+ end
+ end
+
context 'without required_python' do
before do
params.delete(:requires_python)
diff --git a/spec/services/projects/create_from_template_service_spec.rb b/spec/services/projects/create_from_template_service_spec.rb
index 7e23daabcd3..fba6225b87a 100644
--- a/spec/services/projects/create_from_template_service_spec.rb
+++ b/spec/services/projects/create_from_template_service_spec.rb
@@ -49,7 +49,7 @@ RSpec.describe Projects::CreateFromTemplateService do
end
it 'is not scheduled' do
- expect(project.import_scheduled?).to be_nil
+ expect(project.import_scheduled?).to be(false)
end
it 'repository is empty' do
diff --git a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
index 795545e4ad1..1a248bb04e7 100644
--- a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples 'PyPI package creation' do |user_type, status, add_member = true|
+RSpec.shared_examples 'PyPI package creation' do |user_type, status, add_member = true, md5_digest = true|
RSpec.shared_examples 'creating pypi package files' do
it 'creates package files' do
expect { subject }
@@ -14,6 +14,17 @@ RSpec.shared_examples 'PyPI package creation' do |user_type, status, add_member
expect(package.name).to eq params[:name]
expect(package.version).to eq params[:version]
expect(package.pypi_metadatum.required_python).to eq params[:requires_python]
+
+ if md5_digest
+ expect(package.package_files.first.file_md5).not_to be_nil
+ else
+ expect(package.package_files.first.file_md5).to be_nil
+ end
+ end
+
+ context 'with FIPS mode', :fips_mode do
+ it_behaves_like 'returning response status', :unprocessable_entity if md5_digest
+ it_behaves_like 'returning response status', status unless md5_digest
end
end