Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-12-23 00:10:18 +00:00
parent 6c68583a42
commit 2994a84f01
48 changed files with 665 additions and 232 deletions

View File

@ -4564,7 +4564,6 @@ Layout/LineLength:
- 'spec/services/packages/nuget/search_service_spec.rb'
- 'spec/services/packages/nuget/update_package_from_metadata_service_spec.rb'
- 'spec/services/packages/rubygems/process_gem_service_spec.rb'
- 'spec/services/packages/terraform_module/create_package_service_spec.rb'
- 'spec/services/personal_access_tokens/create_service_spec.rb'
- 'spec/services/post_receive_service_spec.rb'
- 'spec/services/projects/apple_target_platform_detector_service_spec.rb'

View File

@ -3690,15 +3690,11 @@ RSpec/FeatureCategory:
- 'spec/lib/gitlab/lazy_spec.rb'
- 'spec/lib/gitlab/legacy_github_import/branch_formatter_spec.rb'
- 'spec/lib/gitlab/legacy_github_import/client_spec.rb'
- 'spec/lib/gitlab/legacy_github_import/comment_formatter_spec.rb'
- 'spec/lib/gitlab/legacy_github_import/issuable_formatter_spec.rb'
- 'spec/lib/gitlab/legacy_github_import/issue_formatter_spec.rb'
- 'spec/lib/gitlab/legacy_github_import/label_formatter_spec.rb'
- 'spec/lib/gitlab/legacy_github_import/milestone_formatter_spec.rb'
- 'spec/lib/gitlab/legacy_github_import/project_creator_spec.rb'
- 'spec/lib/gitlab/legacy_github_import/pull_request_formatter_spec.rb'
- 'spec/lib/gitlab/legacy_github_import/release_formatter_spec.rb'
- 'spec/lib/gitlab/legacy_github_import/user_formatter_spec.rb'
- 'spec/lib/gitlab/legacy_github_import/wiki_formatter_spec.rb'
- 'spec/lib/gitlab/lets_encrypt/challenge_spec.rb'
- 'spec/lib/gitlab/lets_encrypt/client_spec.rb'

View File

@ -46,6 +46,7 @@ export default () => {
gl.mrWidgetData.can_create_pipeline_in_target_project,
),
commitPathTemplate: gl.mrWidgetData.commit_path_template,
canAdminVulnerability: gl.mrWidgetData.can_admin_vulnerability,
dismissalDescriptions,
},
...MrWidgetOptions,

View File

@ -43,6 +43,7 @@ export default {
:key="opt.value"
:disabled="!!opt.disabled"
:selected="value === opt.value"
v-bind="opt.props"
@click="$emit('input', opt.value)"
>
<slot name="button-content" v-bind="opt">{{ opt.text }}</slot>

View File

@ -51,6 +51,16 @@ module Mutations
required: false,
description: copy_field_description(Types::Namespace::PackageSettingsType, :nuget_duplicate_exception_regex)
argument :terraform_module_duplicates_allowed,
GraphQL::Types::Boolean,
required: false,
description: copy_field_description(Types::Namespace::PackageSettingsType, :terraform_module_duplicates_allowed)
argument :terraform_module_duplicate_exception_regex,
Types::UntrustedRegexp,
required: false,
description: copy_field_description(Types::Namespace::PackageSettingsType, :terraform_module_duplicate_exception_regex)
argument :maven_package_requests_forwarding,
GraphQL::Types::Boolean,
required: false,

View File

@ -35,6 +35,12 @@ module Types
field :pypi_package_requests_forwarding, GraphQL::Types::Boolean,
null: true,
description: 'Indicates whether PyPI package forwarding is allowed for this namespace.'
field :terraform_module_duplicate_exception_regex, Types::UntrustedRegexp,
null: true,
description: 'When terraform_module_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect.'
field :terraform_module_duplicates_allowed, GraphQL::Types::Boolean,
null: false,
description: 'Indicates whether duplicate Terraform packages are allowed for this namespace.'
field :lock_maven_package_requests_forwarding, GraphQL::Types::Boolean,
null: false,

View File

@ -1,14 +1,6 @@
# frozen_string_literal: true
module EnvironmentHelper
# rubocop: disable CodeReuse/ActiveRecord
def environment_for_build(project, build)
return unless build.environment
project.environments.find_by(name: build.expanded_environment_name)
end
# rubocop: enable CodeReuse/ActiveRecord
def deployment_path(deployment)
[deployment.project, deployment.deployable]
end

View File

@ -12,7 +12,7 @@ class Namespace::PackageSetting < ApplicationRecord
PackageSettingNotImplemented = Class.new(StandardError)
PACKAGES_WITH_SETTINGS = %w[maven generic nuget].freeze
PACKAGES_WITH_SETTINGS = %w[maven generic nuget terraform_module].freeze
belongs_to :namespace, inverse_of: :package_setting_relation
@ -24,6 +24,14 @@ class Namespace::PackageSetting < ApplicationRecord
validates :nuget_duplicates_allowed, inclusion: { in: [true, false] }
validates :nuget_duplicate_exception_regex, untrusted_regexp: true, length: { maximum: 255 }
validates :nuget_symbol_server_enabled, inclusion: { in: [true, false] }
validates :terraform_module_duplicates_allowed, inclusion: { in: [true, false] }
validates :terraform_module_duplicate_exception_regex, untrusted_regexp: true, length: { maximum: 255 }
scope :namespace_id_in, ->(namespace_ids) { where(namespace_id: namespace_ids) }
scope :with_terraform_module_duplicates_allowed_or_exception_regex, -> do
where(terraform_module_duplicates_allowed: true)
.or(where.not(terraform_module_duplicate_exception_regex: ''))
end
class << self
def duplicates_allowed?(package)

View File

@ -13,12 +13,14 @@ module Organizations
rule { admin }.policy do
enable :admin_organization
enable :create_group
enable :read_organization
enable :read_organization_user
end
rule { organization_user }.policy do
enable :admin_organization
enable :create_group
enable :read_organization
enable :read_organization_user
end

View File

@ -92,9 +92,32 @@ module Groups
end
end
unless organization_setting_valid?
# We are unsetting this here to match behavior of invalid parent_id above and protect against possible
# committing to the database of a value that isn't allowed.
@group.organization = nil
message = s_("CreateGroup|You don't have permission to create a group in the provided organization.")
@group.errors.add(:organization_id, message)
return false
end
true
end
def organization_setting_valid?
# we check for the params presence explicitly since:
# 1. We have a default organization_id at db level set and organization exists and may not have the entry
# in organization_users table to allow authorization. This shouldn't be the case longterm as we
# plan on populating organization_users correctly.
# 2. We shouldn't need to check if this is allowed if the user didn't try to set it themselves. i.e.
# provided in the params
return true if params[:organization_id].blank?
return true if @group.organization.blank?
can?(current_user, :create_group, @group.organization)
end
def can_use_visibility_level?
unless Gitlab::VisibilityLevel.allowed_for?(current_user, visibility_level)
deny_visibility_level(@group)

View File

@ -12,6 +12,8 @@ module Namespaces
maven_package_requests_forwarding
nuget_duplicates_allowed
nuget_duplicate_exception_regex
terraform_module_duplicates_allowed
terraform_module_duplicate_exception_regex
npm_package_requests_forwarding
pypi_package_requests_forwarding
lock_maven_package_requests_forwarding

View File

@ -6,10 +6,20 @@ module Packages
include Gitlab::Utils::StrongMemoize
def execute
return error('Version is empty.', 400) if params[:module_version].blank?
return error('Access Denied', 403) if current_package_exists_elsewhere?
return error('Package version already exists.', 403) if current_package_version_exists?
return error('File is too large.', 400) if file_size_exceeded?
if params[:module_version].blank?
return ServiceResponse.error(message: 'Version is empty.', reason: :bad_request)
end
if duplicates_not_allowed? && current_package_exists_elsewhere?
return ServiceResponse.error(
message: 'A package with the same name already exists in the namespace',
reason: :forbidden
)
end
if current_package_version_exists?
return ServiceResponse.error(message: 'Package version already exists.', reason: :forbidden)
end
ApplicationRecord.transaction { create_terraform_module_package! }
end
@ -24,6 +34,15 @@ module Packages
package
end
def duplicates_not_allowed?
return true if package_settings_with_duplicates_allowed.blank?
package_settings_with_duplicates_allowed.none? do |setting|
setting.terraform_module_duplicates_allowed ||
::Gitlab::UntrustedRegexp.new("\\A#{setting.terraform_module_duplicate_exception_regex}\\z").match?(name)
end
end
def current_package_exists_elsewhere?
::Packages::Package
.for_projects(project.root_namespace.all_projects.id_not_in(project.id))
@ -62,9 +81,13 @@ module Packages
}
end
def file_size_exceeded?
project.actual_limits.exceeded?(:generic_packages_max_file_size, params[:file].size)
end
def package_settings_with_duplicates_allowed
::Namespace::PackageSetting
.select(:terraform_module_duplicates_allowed, :terraform_module_duplicate_exception_regex)
.namespace_id_in(project.namespace.self_and_ancestor_ids)
.with_terraform_module_duplicates_allowed_or_exception_regex
end
strong_memoize_attr :package_settings_with_duplicates_allowed
end
end
end

View File

@ -0,0 +1,35 @@
# frozen_string_literal: true
class AddTerraformModuleDuplicatesAllowedToNamespacePackageSettings < Gitlab::Database::Migration[2.2]
milestone '16.8'
disable_ddl_transaction!
def up
with_lock_retries do
add_column(:namespace_package_settings,
:terraform_module_duplicates_allowed,
:boolean,
null: false,
default: false,
if_not_exists: true
)
add_column(:namespace_package_settings,
:terraform_module_duplicate_exception_regex,
:text,
null: false,
default: '',
if_not_exists: true
)
end
add_text_limit(:namespace_package_settings, :terraform_module_duplicate_exception_regex, 255)
end
def down
with_lock_retries do
remove_column(:namespace_package_settings, :terraform_module_duplicates_allowed, if_exists: true)
remove_column(:namespace_package_settings, :terraform_module_duplicate_exception_regex, if_exists: true)
end
end
end

View File

@ -3,24 +3,10 @@
class ChangeICodeReviewCreateMrKeysFromRedisHllToRedis < Gitlab::Database::Migration[2.2]
milestone '16.8'
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_main
REDIS_HLL_PREFIX = '{hll_counters}_i_code_review_create_mr'
REDIS_PREFIX = '{event_counters}_i_code_review_user_create_mr'
def up
# For each old (redis_hll) counter we find the corresponding target (redis) counter and add
# old value to migrate a metric. If the Redis counter does not exist, it will get created.
# Since the RedisHLL keys expire after 6 weeks, we will migrate 6 keys at the most.
Gitlab::Redis::SharedState.with do |redis|
redis.scan_each(match: "#{REDIS_HLL_PREFIX}-*") do |key|
redis_key = key.sub(REDIS_HLL_PREFIX, REDIS_PREFIX)
redis_hll_value = redis.pfcount(key)
redis.incrby(redis_key, redis_hll_value)
end
end
# no-op
#
# Removed due to https://gitlab.com/gitlab-com/gl-infra/production/-/issues/17321
end
def down

View File

@ -0,0 +1 @@
a18e718e99c23ae6db929929a905af0db72e3a3734d3c33e12ec2cdb44467f6d

View File

@ -19505,9 +19505,12 @@ CREATE TABLE namespace_package_settings (
nuget_duplicates_allowed boolean DEFAULT true NOT NULL,
nuget_duplicate_exception_regex text DEFAULT ''::text NOT NULL,
nuget_symbol_server_enabled boolean DEFAULT false NOT NULL,
terraform_module_duplicates_allowed boolean DEFAULT false NOT NULL,
terraform_module_duplicate_exception_regex text DEFAULT ''::text NOT NULL,
CONSTRAINT check_31340211b1 CHECK ((char_length(generic_duplicate_exception_regex) <= 255)),
CONSTRAINT check_d63274b2b6 CHECK ((char_length(maven_duplicate_exception_regex) <= 255)),
CONSTRAINT check_eedcf85c48 CHECK ((char_length(nuget_duplicate_exception_regex) <= 255))
CONSTRAINT check_eedcf85c48 CHECK ((char_length(nuget_duplicate_exception_regex) <= 255)),
CONSTRAINT check_f10503f1ad CHECK ((char_length(terraform_module_duplicate_exception_regex) <= 255))
);
CREATE TABLE namespace_root_storage_statistics (

View File

@ -7788,6 +7788,8 @@ Input type: `UpdateNamespacePackageSettingsInput`
| <a id="mutationupdatenamespacepackagesettingsnugetduplicatesallowed"></a>`nugetDuplicatesAllowed` | [`Boolean`](#boolean) | Indicates whether duplicate NuGet packages are allowed for this namespace. |
| <a id="mutationupdatenamespacepackagesettingsnugetsymbolserverenabled"></a>`nugetSymbolServerEnabled` | [`Boolean`](#boolean) | Indicates wheather the NuGet symbol server is enabled for this namespace. |
| <a id="mutationupdatenamespacepackagesettingspypipackagerequestsforwarding"></a>`pypiPackageRequestsForwarding` | [`Boolean`](#boolean) | Indicates whether PyPI package forwarding is allowed for this namespace. |
| <a id="mutationupdatenamespacepackagesettingsterraformmoduleduplicateexceptionregex"></a>`terraformModuleDuplicateExceptionRegex` | [`UntrustedRegexp`](#untrustedregexp) | When terraform_module_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect. |
| <a id="mutationupdatenamespacepackagesettingsterraformmoduleduplicatesallowed"></a>`terraformModuleDuplicatesAllowed` | [`Boolean`](#boolean) | Indicates whether duplicate Terraform packages are allowed for this namespace. |
#### Fields
@ -23730,6 +23732,8 @@ Namespace-level Package Registry settings.
| <a id="packagesettingsnugetsymbolserverenabled"></a>`nugetSymbolServerEnabled` | [`Boolean!`](#boolean) | Indicates wheather the NuGet symbol server is enabled for this namespace. |
| <a id="packagesettingspypipackagerequestsforwarding"></a>`pypiPackageRequestsForwarding` | [`Boolean`](#boolean) | Indicates whether PyPI package forwarding is allowed for this namespace. |
| <a id="packagesettingspypipackagerequestsforwardinglocked"></a>`pypiPackageRequestsForwardingLocked` | [`Boolean!`](#boolean) | Indicates whether PyPI package forwarding settings are locked by a parent namespace. |
| <a id="packagesettingsterraformmoduleduplicateexceptionregex"></a>`terraformModuleDuplicateExceptionRegex` | [`UntrustedRegexp`](#untrustedregexp) | When terraform_module_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect. |
| <a id="packagesettingsterraformmoduleduplicatesallowed"></a>`terraformModuleDuplicatesAllowed` | [`Boolean!`](#boolean) | Indicates whether duplicate Terraform packages are allowed for this namespace. |
### `PackageTag`

View File

@ -817,7 +817,7 @@ POST /groups
Parameters:
| Attribute | Type | Required | Description |
| ------------------------------------------------------- | ------- | -------- | ----------- |
| ------------------------------------------------------- | ------- | -------- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name` | string | yes | The name of the group. |
| `path` | string | yes | The path of the group. |
| `auto_devops_enabled` | boolean | no | Default to Auto DevOps pipeline for all projects within this group. |
@ -829,6 +829,7 @@ Parameters:
| `emails_enabled` | boolean | no | Enable email notifications. |
| `lfs_enabled` | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. |
| `mentions_disabled` | boolean | no | Disable the capability of a group from getting mentioned. |
| `organization_id` | integer | no | The organization ID for the group. |
| `parent_id` | integer | no | The parent group ID for creating nested group. |
| `project_creation_level` | string | no | Determine if developers can create projects in the group. Can be `noone` (No one), `maintainer` (users with the Maintainer role), or `developer` (users with the Developer or Maintainer role). |
| `request_access_enabled` | boolean | no | Allow users to request member access. |

View File

@ -70,6 +70,8 @@ Setting | Table | Description
`nuget_duplicates_allowed` | `namespace_package_settings` | Allow or prevent duplicate NuGet packages.
`nuget_duplicate_exception_regex` | `namespace_package_settings` | Regex defining NuGet packages that are allowed to be duplicate when duplicates are not allowed.
`nuget_symbol_server_enabled` | `namespace_package_settings` | Enable or disable the NuGet symbol server.
`terraform_module_duplicates_allowed` | `namespace_package_settings` | Allow or prevent duplicate Terraform module packages.
`terraform_module_duplicate_exception_regex` | `namespace_package_settings` | Regex defining Terraform module packages that are allowed to be duplicate when duplicates are not allowed.
Dependency Proxy Cleanup Policies - `ttl` | `dependency_proxy_image_ttl_group_policies` | Number of days to retain an unused Dependency Proxy file before it is removed.
Dependency Proxy - `enabled` | `dependency_proxy_image_ttl_group_policies` | Enable or disable the Dependency Proxy cleanup policy.

View File

@ -48,10 +48,10 @@ You can publish Terraform modules by using the [Terraform Module Registry API](.
Prerequisites:
- The package name and version [must be unique in the top-level namespace](#how-module-resolution-works).
- Unless [duplicates are allowed](#allow-duplicate-terraform-modules), the package name and version [must be unique in the top-level namespace](#how-module-resolution-works).
- Your project and group names must not include a dot (`.`). For example, `source = "gitlab.example.com/my.group/project.name"`.
- You must [authenticate with the API](../../../api/rest/index.md#authentication). If authenticating with a deploy token, it must be configured with the `write_package_registry` scope.
- The name of a module [must be unique in the scope of its group](#how-module-resolution-works), otherwise an
- Unless [duplicates are allowed](#allow-duplicate-terraform-modules), the name of a module [must be unique in the scope of its group](#how-module-resolution-works), otherwise an
[error occurs](#troubleshooting).
```plaintext
@ -157,6 +157,22 @@ upload:
To trigger this upload job, add a Git tag to your commit. Ensure the tag follows the [Semantic versioning specification](https://semver.org/) that Terraform requires. The `rules:if: $CI_COMMIT_TAG` ensures that only tagged commits to your repository trigger the module upload job.
For other ways to control jobs in your CI/CD pipeline, refer to the [`.gitlab-ci.yml`](../../../ci/yaml/index.md) keyword reference.
### Allow duplicate Terraform modules
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368040) in GitLab 16.8.
By default, the Terraform Module Registry enforces uniqueness for module names in the same namespace. To allow publishing duplicate module names:
- Enable `terraform_module_duplicates_allowed` for the namespace with the [GraphQl API](../../../api/graphql/reference/index.md#packagesettings).
To allow duplicates with specific names:
1. Ensure `terraform_module_duplicates_allowed` is disabled.
1. Use `terraform_module_duplicate_exception_regex` to define a regex pattern for the module names you want to allow duplicates for.
The top-level namespace setting takes precedence over the child namespace settings.
For example, if you enable `terraform_module_duplicates_allowed` for a group, and disable it for a subgroup, duplicates are allowed for all projects in the group and its subgroups.
## Reference a Terraform module
Prerequisites:
@ -209,6 +225,8 @@ module "<module>" {
If you need to reference the latest version of a module, you can omit the `<module-version>` from the source URL. To prevent future issues, you should reference a specific version if possible.
If there are [duplicate module names](#allow-duplicate-terraform-modules) in the same namespace, referencing the module from the namespace level installs the recently published module. To reference a specific version of a duplicate module, use the [project-level](#from-a-project) source type.
## Download a Terraform module
To download a Terraform module:
@ -275,4 +293,4 @@ For examples of the Terraform Module Registry, check the projects below:
## Troubleshooting
- Publishing a module with a duplicate name results in a `{"message":"Access Denied"}` error. There's an ongoing discussion about allowing duplicate module names [in this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/368040).
- Publishing a module with a duplicate name results in a `{"message":"A package with the same name already exists in the namespace"}` error.

View File

@ -395,6 +395,6 @@ third-party Git clients.
### Branch names are case-sensitive
Branch names in `git` are case-sensitive. When configuring your protected branch
or [target branch rule](repository/branches/index.md#configure-rules-for-target-branches),
Branch names in `git` are case-sensitive. When configuring your protected branch,
or your [target branch workflow](repository/branches/index.md#configure-workflows-for-target-branches),
`dev` is not the same `DEV` or `Dev`.

View File

@ -287,58 +287,58 @@ To do this:
1. Select **Delete merged branches**.
1. In the dialog, enter the word `delete` to confirm, then select **Delete merged branches**.
## Configure rules for target branches **(PREMIUM ALL)**
## Configure workflows for target branches **(PREMIUM ALL)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127115) in GitLab 16.4 [with a flag](../../../../administration/feature_flags.md) named `target_branch_rules_flag`. Enabled by default.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136431) in GitLab 16.7.
Some projects use multiple long-term branches for development, like `develop` and `qa`.
In these projects, you might want to keep `main` as the default branch, but expect
merge requests to target `develop` or `qa` instead. Target branch rules help ensure
merge requests to target `develop` or `qa` instead. Target branch workflows help ensure
merge requests target the appropriate development branch for your project.
When you create a merge request, the rule checks the name of the branch. If the
branch name matches the rule, the merge request targets the branch you specify
in the rule. If the branch name does not match, the merge request targets the
When you create a merge request, the workflow checks the name of the branch. If the
branch name matches the workflow, the merge request targets the branch you specify. If the branch name does not match, the merge request targets the
default branch of the project.
Prerequisites:
- You must have at least the Maintainer role.
To create a target branch rule:
To create a target branch workflow:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings > Merge requests**.
1. Select **Add target branch rule**.
1. For **Rule name**, provide a string or wild card to compare against branch names.
1. Select the **Target branch** to use when the branch name matches the **Rule name**.
1. Scroll down to **Merge request branch workflow**
1. Select **Add branch target**.
1. For **Branch name pattern**, provide a string or wild card to compare against branch names.
1. Select the **Target branch** to use when the branch name matches the **Branch name pattern**.
1. Select **Save**.
### Example
You could configure your project to have the following target branch rules:
You could configure your project to have the following target branch workflows:
| Rule name | Target branch |
| Branch name pattern | Target branch |
|-------------|---------------|
| `feature/*` | `develop` |
| `bug/*` | `develop` |
| `release/*` | `main` |
These rules simplify the process of creating merge requests for a project that:
These target branches simplify the process of creating merge requests for a project that:
- Uses `main` to represent the deployed state of your application.
- Tracks current, unreleased development work in another long-running branch, like `develop`.
If your workflow initially places new features in `develop` instead of `main`, these rules
If your workflow initially places new features in `develop` instead of `main`, these target branches
ensure all branches matching either `feature/*` or `bug/*` do not target `main` by mistake.
When you're ready to release to `main`, create a branch named `release/*`, and the rules
When you're ready to release to `main`, create a branch named `release/*`, and
ensure this branch targets `main`.
## Delete a target branch rule
## Delete a target branch workflow
When you remove a target branch rule, existing merge requests remain unchanged.
When you remove a target branch workflow, existing merge requests remain unchanged.
Prerequisites:
@ -348,7 +348,7 @@ To do this:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings > Merge requests**.
1. Select **Delete** on the rule you want to delete.
1. Select **Delete** on the branch target you want to delete.
## Related topics

View File

@ -23,6 +23,7 @@ module API
expose :full_name, :full_path
expose :created_at
expose :parent_id
expose :organization_id
expose :shared_runners_setting
expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes

View File

@ -213,11 +213,15 @@ module API
requires :name, type: String, desc: 'The name of the group'
requires :path, type: String, desc: 'The path of the group'
optional :parent_id, type: Integer, desc: 'The parent group id for creating nested group'
optional :organization_id, type: Integer, desc: 'The organization id for the group'
use :optional_params
end
post feature_category: :groups_and_projects, urgency: :low do
parent_group = find_group!(params[:parent_id]) if params[:parent_id].present?
organization = find_organization!(params[:organization_id]) if params[:organization_id].present?
authorize! :create_group, organization if organization
parent_group = find_group!(params[:parent_id], organization: organization) if params[:parent_id].present?
if parent_group
authorize! :create_subgroup, parent_group
else

View File

@ -211,18 +211,25 @@ module API
not_found!('Pipeline')
end
def find_organization!(id)
organization = Organizations::Organization.find_by_id(id)
check_organization_access(organization)
end
# rubocop: disable CodeReuse/ActiveRecord
def find_group(id)
def find_group(id, organization: nil)
collection = organization.present? ? Group.in_organization(organization) : Group.all
if id.to_s =~ INTEGER_ID_REGEX
Group.find_by(id: id)
collection.find_by(id: id)
else
Group.find_by_full_path(id)
collection.find_by_full_path(id)
end
end
# rubocop: enable CodeReuse/ActiveRecord
def find_group!(id)
group = find_group(id)
def find_group!(id, organization: nil)
group = find_group(id, organization: organization)
check_group_access(group)
end
@ -835,6 +842,12 @@ module API
@sudo_identifier ||= params[SUDO_PARAM] || env[SUDO_HEADER]
end
def check_organization_access(organization)
return organization if can?(current_user, :read_organization, organization)
not_found!('Organization')
end
def secret_token
Gitlab::Shell.secret_token
end

View File

@ -171,7 +171,7 @@ module API
.new(authorized_user_project, current_user, create_package_file_params)
.execute
render_api_error!(result[:message], result[:http_status]) if result[:status] == :error
render_api_error!(result.message, result.reason) if result.error?
track_package_event('push_package', :terraform_module, project: authorized_user_project,
namespace: authorized_user_project.namespace)

View File

@ -23,7 +23,7 @@ module Gitlab
def gitlab_id
return @gitlab_id if defined?(@gitlab_id)
@gitlab_id = find_by_external_uid || find_by_email
@gitlab_id = find_by_email
end
private
@ -45,14 +45,6 @@ module Gitlab
User.find_by_any_email(email)
.try(:id)
end
# rubocop: disable CodeReuse/ActiveRecord
def find_by_external_uid
return unless id
User.by_provider_and_extern_uid(:github, id).select(:id).first&.id
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end

View File

@ -14578,6 +14578,9 @@ msgstr ""
msgid "CreateGitTag|Set tag message"
msgstr ""
msgid "CreateGroup|You don't have permission to create a group in the provided organization."
msgstr ""
msgid "CreateGroup|You dont have permission to create a subgroup in this group."
msgstr ""

View File

@ -15,6 +15,9 @@ FactoryBot.define do
nuget_symbol_server_enabled { false }
terraform_module_duplicates_allowed { false }
terraform_module_duplicate_exception_regex { 'foo' }
trait :group do
namespace { association(:group) }
end

View File

@ -122,6 +122,7 @@ describe('~/vue_shared/components/segmented_control_button_group.vue', () => {
[[{ value: '1' }]],
[[{ value: 1, disabled: true }]],
[[{ value: true, disabled: false }]],
[[{ value: true, props: { 'data-testid': 'test' } }]],
])('with options=%j, passes validation', (options) => {
createComponent({ options });

View File

@ -39,7 +39,9 @@ RSpec.describe Mutations::Namespace::PackageSettings::Update, feature_category:
lock_npm_package_requests_forwarding: false,
pypi_package_requests_forwarding: nil,
lock_pypi_package_requests_forwarding: false,
nuget_symbol_server_enabled: false
nuget_symbol_server_enabled: false,
terraform_module_duplicates_allowed: false,
terraform_module_duplicate_exception_regex: 'foo'
}, to: {
maven_duplicates_allowed: false,
maven_duplicate_exception_regex: 'RELEASE',
@ -53,7 +55,9 @@ RSpec.describe Mutations::Namespace::PackageSettings::Update, feature_category:
lock_npm_package_requests_forwarding: true,
pypi_package_requests_forwarding: true,
lock_pypi_package_requests_forwarding: true,
nuget_symbol_server_enabled: true
nuget_symbol_server_enabled: true,
terraform_module_duplicates_allowed: true,
terraform_module_duplicate_exception_regex: 'bar'
}
it_behaves_like 'returning a success'
@ -109,7 +113,9 @@ RSpec.describe Mutations::Namespace::PackageSettings::Update, feature_category:
lock_npm_package_requests_forwarding: true,
pypi_package_requests_forwarding: true,
lock_pypi_package_requests_forwarding: true,
nuget_symbol_server_enabled: true
nuget_symbol_server_enabled: true,
terraform_module_duplicates_allowed: true,
terraform_module_duplicate_exception_regex: 'bar'
}
end

View File

@ -33,6 +33,8 @@ RSpec.describe GitlabSchema.types['PackageSettings'], feature_category: :package
npm_package_requests_forwarding_locked
pypi_package_requests_forwarding_locked
nuget_symbol_server_enabled
terraform_module_duplicates_allowed
terraform_module_duplicate_exception_regex
]
expect(described_class).to include_graphql_fields(*expected_fields)

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::Entities::Group, feature_category: :groups_and_projects do
let_it_be(:group) do
base_group = create(:group) { |g| create(:project_statistics, namespace_id: g.id) }
Group.with_statistics.find(base_group.id)
end
subject(:json) { described_class.new(group, { with_custom_attributes: true, statistics: true }).as_json }
it 'returns expected data' do
expect(json.keys).to(
include(
:organization_id, :path, :description, :visibility, :share_with_group_lock, :require_two_factor_authentication,
:two_factor_grace_period, :project_creation_level, :auto_devops_enabled,
:subgroup_creation_level, :emails_disabled, :emails_enabled, :lfs_enabled, :default_branch_protection,
:default_branch_protection_defaults, :avatar_url, :request_access_enabled, :full_name, :full_path, :created_at,
:parent_id, :organization_id, :shared_runners_setting, :custom_attributes, :statistics
)
)
end
end

View File

@ -406,6 +406,37 @@ RSpec.describe API::Helpers, feature_category: :shared do
end
end
describe '#find_organization!' do
let_it_be(:organization) { create(:organization) }
let_it_be(:user) { create(:user) }
before do
allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:initial_current_user).and_return(user)
end
context 'when user is authenticated' do
it 'returns requested organization' do
expect(helper.find_organization!(organization.id)).to eq(organization)
end
end
context 'when user is not authenticated' do
let(:user) { nil }
it 'returns requested organization' do
expect(helper.find_organization!(organization.id)).to eq(organization)
end
end
context 'when organization does not exist' do
it 'returns nil' do
expect(helper).to receive(:render_api_error!).with('404 Organization Not Found', 404)
expect(helper.find_organization!(non_existing_record_id)).to be_nil
end
end
end
describe '#find_group!' do
let_it_be(:group) { create(:group, :public) }
let_it_be(:user) { create(:user) }
@ -457,7 +488,7 @@ RSpec.describe API::Helpers, feature_category: :shared do
end
end
context 'support for IDs and paths as arguments' do
context 'with support for IDs and paths as arguments' do
let_it_be(:group) { create(:group) }
let(:user) { group.first_owner }
@ -505,6 +536,34 @@ RSpec.describe API::Helpers, feature_category: :shared do
end
end
context 'with support for organization as an argument' do
let_it_be(:group) { create(:group) }
let_it_be(:organization) { create(:organization) }
before do
allow(helper).to receive(:current_user).and_return(group.first_owner)
allow(helper).to receive(:job_token_authentication?).and_return(false)
allow(helper).to receive(:authenticate_non_public?).and_return(false)
end
subject { helper.find_group!(group.id, organization: organization) }
context 'when group exists in the organization' do
before do
group.update!(organization: organization)
end
it { is_expected.to eq(group) }
end
context 'when group does not exist in the organization' do
it 'returns nil' do
expect(helper).to receive(:render_api_error!).with('404 Group Not Found', 404)
is_expected.to be_nil
end
end
end
describe '#find_group_by_full_path!' do
let_it_be(:group) { create(:group, :public) }
let_it_be(:user) { create(:user) }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Gitlab::LegacyGithubImport::CommentFormatter do
RSpec.describe Gitlab::LegacyGithubImport::CommentFormatter, feature_category: :importers do
let_it_be(:project) { create(:project) }
let(:client) { double }
let(:octocat) { { id: 123456, login: 'octocat', email: 'octocat@example.com' } }
@ -76,12 +76,6 @@ RSpec.describe Gitlab::LegacyGithubImport::CommentFormatter do
context 'when author is a GitLab user' do
let(:raw) { base.merge(user: octocat) }
it 'returns GitLab user id associated with GitHub id as author_id' do
gl_user = create(:omniauth_user, extern_uid: octocat[:id], provider: 'github')
expect(comment.attributes.fetch(:author_id)).to eq gl_user.id
end
it 'returns GitLab user id associated with GitHub email as author_id' do
gl_user = create(:user, email: octocat[:email])
@ -89,7 +83,7 @@ RSpec.describe Gitlab::LegacyGithubImport::CommentFormatter do
end
it 'returns note without created at tag line' do
create(:omniauth_user, extern_uid: octocat[:id], provider: 'github')
create(:user, email: octocat[:email])
expect(comment.attributes.fetch(:note)).to eq("I'm having a problem with this.")
end

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Gitlab::LegacyGithubImport::IssueFormatter do
RSpec.describe Gitlab::LegacyGithubImport::IssueFormatter, feature_category: :importers do
let_it_be(:project) { create(:project, namespace: create(:namespace, path: 'octocat')) }
let(:client) { double }
let(:octocat) { { id: 123456, login: 'octocat', email: 'octocat@example.com' } }
@ -82,12 +82,6 @@ RSpec.describe Gitlab::LegacyGithubImport::IssueFormatter do
expect(issue.attributes.fetch(:assignee_ids)).to be_empty
end
it 'returns GitLab user id associated with GitHub id as assignee_id' do
gl_user = create(:omniauth_user, extern_uid: octocat[:id], provider: 'github')
expect(issue.attributes.fetch(:assignee_ids)).to eq [gl_user.id]
end
it 'returns GitLab user id associated with GitHub email as assignee_id' do
gl_user = create(:user, email: octocat[:email])
@ -117,12 +111,6 @@ RSpec.describe Gitlab::LegacyGithubImport::IssueFormatter do
expect(issue.attributes.fetch(:author_id)).to eq project.creator_id
end
it 'returns GitLab user id associated with GitHub id as author_id' do
gl_user = create(:omniauth_user, extern_uid: octocat[:id], provider: 'github')
expect(issue.attributes.fetch(:author_id)).to eq gl_user.id
end
it 'returns GitLab user id associated with GitHub email as author_id' do
gl_user = create(:user, email: octocat[:email])
@ -130,7 +118,7 @@ RSpec.describe Gitlab::LegacyGithubImport::IssueFormatter do
end
it 'returns description without created at tag line' do
create(:omniauth_user, extern_uid: octocat[:id], provider: 'github')
create(:user, email: octocat[:email])
expect(issue.attributes.fetch(:description)).to eq("I'm having a problem with this.")
end

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Gitlab::LegacyGithubImport::PullRequestFormatter do
RSpec.describe Gitlab::LegacyGithubImport::PullRequestFormatter, feature_category: :importers do
let_it_be(:project) { create(:project, :repository) }
let(:client) { double }
let(:source_sha) { create(:commit, project: project).id }
@ -136,12 +136,6 @@ RSpec.describe Gitlab::LegacyGithubImport::PullRequestFormatter do
expect(pull_request.attributes.fetch(:assignee_id)).to be_nil
end
it 'returns GitLab user id associated with GitHub id as assignee_id' do
gl_user = create(:omniauth_user, extern_uid: octocat[:id], provider: 'github')
expect(pull_request.attributes.fetch(:assignee_id)).to eq gl_user.id
end
it 'returns GitLab user id associated with GitHub email as assignee_id' do
gl_user = create(:user, email: octocat[:email])
@ -156,12 +150,6 @@ RSpec.describe Gitlab::LegacyGithubImport::PullRequestFormatter do
expect(pull_request.attributes.fetch(:author_id)).to eq project.creator_id
end
it 'returns GitLab user id associated with GitHub id as author_id' do
gl_user = create(:omniauth_user, extern_uid: octocat[:id], provider: 'github')
expect(pull_request.attributes.fetch(:author_id)).to eq gl_user.id
end
it 'returns GitLab user id associated with GitHub email as author_id' do
gl_user = create(:user, email: octocat[:email])
@ -169,7 +157,7 @@ RSpec.describe Gitlab::LegacyGithubImport::PullRequestFormatter do
end
it 'returns description without created at tag line' do
create(:omniauth_user, extern_uid: octocat[:id], provider: 'github')
create(:user, email: octocat[:email])
expect(pull_request.attributes.fetch(:description)).to eq('Please pull these awesome changes')
end

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Gitlab::LegacyGithubImport::UserFormatter do
RSpec.describe Gitlab::LegacyGithubImport::UserFormatter, feature_category: :importers do
let(:client) { double }
let(:octocat) { { id: 123456, login: 'octocat', email: 'octocat@example.com' } }
let(:gitea_ghost) { { id: -1, login: 'Ghost', email: '' } }
@ -15,12 +15,6 @@ RSpec.describe Gitlab::LegacyGithubImport::UserFormatter do
end
context 'when GitHub user is a GitLab user' do
it 'return GitLab user id when user associated their account with GitHub' do
gl_user = create(:omniauth_user, extern_uid: octocat[:id], provider: 'github')
expect(user.gitlab_id).to eq gl_user.id
end
it 'returns GitLab user id when user confirmed primary email matches GitHub email' do
gl_user = create(:user, email: octocat[:email])

View File

@ -1,41 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe ChangeICodeReviewCreateMrKeysFromRedisHllToRedis, :migration, :clean_gitlab_redis_cache, feature_category: :service_ping do
def set_redis_hll(key, value)
Gitlab::Redis::HLL.add(key: key, value: value, expiry: 6.weeks)
end
def get_int_from_redis(key)
Gitlab::Redis::SharedState.with { |redis| redis.get(key)&.to_i }
end
describe "#up" do
before do
set_redis_hll('{hll_counters}_i_code_review_create_mr-2023-16', 1)
set_redis_hll('{hll_counters}_i_code_review_create_mr-2023-16', 2)
set_redis_hll('{hll_counters}_i_code_review_create_mr-2023-47', 3)
set_redis_hll('{hll_counters}_i_code_review_create_mr-2023-48', 1)
set_redis_hll('{hll_counters}_i_code_review_create_mr-2023-49', 2)
set_redis_hll('{hll_counters}_i_code_review_create_mr-2023-49', 4)
set_redis_hll('{hll_counters}_some_other_event-2023-49', 7)
end
it 'migrates all RedisHLL keys for i_code_review_create_mr', :aggregate_failures do
migrate!
expect(get_int_from_redis('{event_counters}_i_code_review_user_create_mr-2023-16')).to eq(2)
expect(get_int_from_redis('{event_counters}_i_code_review_user_create_mr-2023-47')).to eq(1)
expect(get_int_from_redis('{event_counters}_i_code_review_user_create_mr-2023-48')).to eq(1)
expect(get_int_from_redis('{event_counters}_i_code_review_user_create_mr-2023-49')).to eq(2)
end
it 'does not not migrate other RedisHLL keys' do
migrate!
expect(get_int_from_redis('{event_counters}_some_other_event-2023-16')).to be_nil
end
end
end

View File

@ -12,13 +12,21 @@ RSpec.describe Namespace::PackageSetting, feature_category: :package_registry do
describe '#maven_duplicates_allowed' do
it { is_expected.to validate_inclusion_of(:maven_duplicates_allowed).in_array([true, false]) }
it { is_expected.to validate_inclusion_of(:generic_duplicates_allowed).in_array([true, false]) }
it { is_expected.to validate_inclusion_of(:nuget_duplicates_allowed).in_array([true, false]) }
it { is_expected.to validate_length_of(:maven_duplicate_exception_regex).is_at_most(255) }
end
it { is_expected.to allow_value(true, false).for(:nuget_symbol_server_enabled) }
it { is_expected.not_to allow_value(nil).for(:nuget_symbol_server_enabled) }
it { is_expected.to validate_inclusion_of(:generic_duplicates_allowed).in_array([true, false]) }
it { is_expected.to validate_length_of(:generic_duplicate_exception_regex).is_at_most(255) }
it { is_expected.to validate_inclusion_of(:nuget_duplicates_allowed).in_array([true, false]) }
it { is_expected.to validate_length_of(:nuget_duplicate_exception_regex).is_at_most(255) }
it { is_expected.to allow_value(true, false).for(:terraform_module_duplicates_allowed) }
it { is_expected.not_to allow_value(nil).for(:terraform_module_duplicates_allowed) }
it { is_expected.to validate_length_of(:terraform_module_duplicate_exception_regex).is_at_most(255) }
describe 'regex values' do
let_it_be(:package_settings) { create(:namespace_package_setting) }
@ -39,6 +47,50 @@ RSpec.describe Namespace::PackageSetting, feature_category: :package_registry do
end
end
describe 'scopes' do
describe '.namespace_id_in' do
let_it_be(:package_settings) { create(:namespace_package_setting) }
let_it_be(:other_package_settings) { create(:namespace_package_setting) }
subject { described_class.namespace_id_in([package_settings.namespace_id]) }
it { is_expected.to eq([package_settings]) }
end
describe '.with_terraform_module_duplicates_allowed_or_exception_regex' do
let_it_be(:package_settings) { create(:namespace_package_setting) }
subject { described_class.with_terraform_module_duplicates_allowed_or_exception_regex }
context 'when terraform_module_duplicates_allowed is true' do
before do
package_settings.update_column(:terraform_module_duplicates_allowed, true)
end
it { is_expected.to eq([package_settings]) }
end
context 'when terraform_module_duplicate_exception_regex is present' do
before do
package_settings.update_column(:terraform_module_duplicate_exception_regex, 'foo')
end
it { is_expected.to eq([package_settings]) }
end
context 'when terraform_module_duplicates_allowed is false and terraform_module_duplicate_exception_regex is empty' do
before do
package_settings.update_columns(
terraform_module_duplicates_allowed: false,
terraform_module_duplicate_exception_regex: ''
)
end
it { is_expected.to be_empty }
end
end
end
describe '#duplicates_allowed?' do
using RSpec::Parameterized::TableSyntax
@ -46,9 +98,14 @@ RSpec.describe Namespace::PackageSetting, feature_category: :package_registry do
context 'package types with package_settings' do
# As more package types gain settings they will be added to this list
%i[maven_package generic_package nuget_package].each do |format|
context "with package_type:#{format}" do
let_it_be(:package) { create(format, name: 'foo', version: '1.0.0-beta') }
[
{ format: :maven_package, package_name: 'foo' },
{ format: :generic_package, package_name: 'foo' },
{ format: :nuget_package, package_name: 'foo' },
{ format: :terraform_module_package, package_name: 'foo/bar' }
].each do |type|
context "with package_type: #{type[:format]}" do
let_it_be(:package) { create(type[:format], name: type[:package_name], version: '1.0.0-beta') }
let_it_be(:package_type) { package.package_type }
let_it_be(:package_setting) { package.project.namespace.package_settings }
@ -61,7 +118,7 @@ RSpec.describe Namespace::PackageSetting, feature_category: :package_registry do
end
with_them do
context "for #{format}" do
context "for #{type[:format]}" do
before do
package_setting.update!(
"#{package_type}_duplicates_allowed" => duplicates_allowed,

View File

@ -20,6 +20,7 @@ RSpec.describe Organizations::OrganizationPolicy, feature_category: :cell do
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:admin_organization) }
it { is_expected.to be_allowed(:create_group) }
it { is_expected.to be_allowed(:read_organization) }
it { is_expected.to be_allowed(:read_organization_user) }
end
@ -36,12 +37,14 @@ RSpec.describe Organizations::OrganizationPolicy, feature_category: :cell do
end
it { is_expected.to be_allowed(:admin_organization) }
it { is_expected.to be_allowed(:create_group) }
it { is_expected.to be_allowed(:read_organization) }
it { is_expected.to be_allowed(:read_organization_user) }
end
context 'when the user is not part of the organization' do
it { is_expected.to be_disallowed(:admin_organization) }
it { is_expected.to be_disallowed(:create_group) }
it { is_expected.to be_disallowed(:read_organization_user) }
# All organizations are currently public, and hence they are allowed to be read
# even if the user is not a part of the organization.

View File

@ -23,7 +23,9 @@ RSpec.describe 'Updating the package settings', feature_category: :package_regis
lock_npm_package_requests_forwarding: true,
pypi_package_requests_forwarding: true,
lock_pypi_package_requests_forwarding: true,
nuget_symbol_server_enabled: true
nuget_symbol_server_enabled: true,
terraform_module_duplicates_allowed: true,
terraform_module_duplicate_exception_regex: 'foo-.*'
}
end
@ -44,6 +46,8 @@ RSpec.describe 'Updating the package settings', feature_category: :package_regis
pypiPackageRequestsForwarding
lockPypiPackageRequestsForwarding
nugetSymbolServerEnabled
terraformModuleDuplicatesAllowed
terraformModuleDuplicateExceptionRegex
}
errors
QL
@ -73,6 +77,8 @@ RSpec.describe 'Updating the package settings', feature_category: :package_regis
expect(package_settings_response['npmPackageRequestsForwarding']).to eq(params[:npm_package_requests_forwarding])
expect(package_settings_response['lockNpmPackageRequestsForwarding']).to eq(params[:lock_npm_package_requests_forwarding])
expect(package_settings_response['nugetSymbolServerEnabled']).to eq(params[:nuget_symbol_server_enabled])
expect(package_settings_response['terraformModuleDuplicatesAllowed']).to eq(params[:terraform_module_duplicates_allowed])
expect(package_settings_response['terraformModuleDuplicateExceptionRegex']).to eq(params[:terraform_module_duplicate_exception_regex])
end
end
@ -115,7 +121,9 @@ RSpec.describe 'Updating the package settings', feature_category: :package_regis
lock_npm_package_requests_forwarding: false,
pypi_package_requests_forwarding: nil,
lock_pypi_package_requests_forwarding: false,
nuget_symbol_server_enabled: false
nuget_symbol_server_enabled: false,
terraform_module_duplicates_allowed: false,
terraform_module_duplicate_exception_regex: 'foo'
}, to: {
maven_duplicates_allowed: false,
maven_duplicate_exception_regex: 'foo-.*',
@ -129,7 +137,9 @@ RSpec.describe 'Updating the package settings', feature_category: :package_regis
lock_npm_package_requests_forwarding: true,
pypi_package_requests_forwarding: true,
lock_pypi_package_requests_forwarding: true,
nuget_symbol_server_enabled: true
nuget_symbol_server_enabled: true,
terraform_module_duplicates_allowed: true,
terraform_module_duplicate_exception_regex: 'foo-.*'
}
it_behaves_like 'returning a success'

View File

@ -1937,6 +1937,59 @@ RSpec.describe API::Groups, feature_category: :groups_and_projects do
end
end
context 'when group is within a provided organization' do
let_it_be(:organization) { create(:organization) }
context 'when user is an organization user' do
before_all do
create(:organization_user, user: user3, organization: organization)
end
it 'creates group within organization' do
post api('/groups', user3), params: attributes_for_group_api(organization_id: organization.id)
expect(response).to have_gitlab_http_status(:created)
expect(json_response['organization_id']).to eq(organization.id)
end
context 'when parent_group is not part of the organization' do
it 'does not create the group with not_found' do
post(
api('/groups', user3),
params: attributes_for_group_api(parent_id: group2.id, organization_id: organization.id)
)
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
context 'when organization does not exist' do
it 'does not create the group with not_found' do
post api('/groups', user3), params: attributes_for_group_api(organization_id: non_existing_record_id)
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when user is not an organization user' do
it 'does not create the group' do
post api('/groups', user3), params: attributes_for_group_api(organization_id: organization.id)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when user is an admin' do
it 'creates group within organization' do
post api('/groups', admin, admin_mode: true), params: attributes_for_group_api(organization_id: organization.id)
expect(response).to have_gitlab_http_status(:created)
expect(json_response['organization_id']).to eq(organization.id)
end
end
end
context "when authenticated as user with group permissions" do
it "creates group", :aggregate_failures do
group = attributes_for_group_api request_access_enabled: false

View File

@ -103,7 +103,28 @@ RSpec.describe API::Terraform::Modules::V1::ProjectPackages, feature_category: :
)
end
shared_examples 'creating a package' do
it 'creates a package' do
expect { api_request }
.to change { project.packages.count }.by(1)
.and change { Packages::PackageFile.count }.by(1)
expect(response).to have_gitlab_http_status(:created)
end
end
shared_examples 'not creating a package' do |expected_status|
it 'does not create a package' do
expect { api_request }
.to change { project.packages.count }.by(0)
.and change { Packages::PackageFile.count }.by(0)
expect(response).to have_gitlab_http_status(expected_status)
end
end
context 'with valid project' do
let(:user_headers) { { 'PRIVATE-TOKEN' => personal_access_token.token } }
let(:headers) { user_headers.merge(workhorse_headers) }
where(:visibility, :user_role, :member, :token_header, :token_type, :shared_examples_name, :expected_status) do
:public | :developer | true | 'PRIVATE-TOKEN' | :personal_access_token | 'process terraform module upload' | :created
:public | :guest | true | 'PRIVATE-TOKEN' | :personal_access_token | 'rejects terraform module packages access' | :forbidden
@ -147,7 +168,6 @@ RSpec.describe API::Terraform::Modules::V1::ProjectPackages, feature_category: :
with_them do
let(:user_headers) { user_role == :anonymous ? {} : { token_header => token } }
let(:headers) { user_headers.merge(workhorse_headers) }
let(:snowplow_gitlab_standard_context) do
{ project: project, namespace: project.namespace, user: snowplow_user,
property: 'i_package_terraform_module_user' }
@ -172,43 +192,73 @@ RSpec.describe API::Terraform::Modules::V1::ProjectPackages, feature_category: :
end
context 'when failed package file save' do
let(:user_headers) { { 'PRIVATE-TOKEN' => personal_access_token.token } }
let(:headers) { user_headers.merge(workhorse_headers) }
before do
project.add_developer(user)
allow(Packages::CreatePackageFileService).to receive(:new).and_raise(StandardError)
end
it_behaves_like 'not creating a package', :error
end
context 'with an existing package in the same project' do
let_it_be_with_reload(:existing_package) do
create(:terraform_module_package, name: 'mymodule/mysystem', version: '1.0.0', project: project)
end
before do
project.add_developer(user)
end
it 'does not create package record', :aggregate_failures do
allow(Packages::CreatePackageFileService).to receive(:new).and_raise(StandardError)
expect { api_request }
.to change { project.packages.count }.by(0)
.and change { Packages::PackageFile.count }.by(0)
expect(response).to have_gitlab_http_status(:error)
end
context 'with an existing package' do
let_it_be_with_reload(:existing_package) do
create(:terraform_module_package, name: 'mymodule/mysystem', version: '1.0.0', project: project)
end
it 'does not create a new package' do
expect { api_request }
.to change { project.packages.count }.by(0)
.and change { Packages::PackageFile.count }.by(0)
expect(response).to have_gitlab_http_status(:forbidden)
end
it_behaves_like 'not creating a package', :forbidden
context 'when marked as pending_destruction' do
it 'does create a new package' do
before do
existing_package.pending_destruction!
end
expect { api_request }
.to change { project.packages.count }.by(1)
.and change { Packages::PackageFile.count }.by(1)
expect(response).to have_gitlab_http_status(:created)
end
it_behaves_like 'creating a package'
end
end
context 'with existing package in another project' do
let_it_be(:package_settings) { create(:namespace_package_setting, namespace: group) }
let_it_be(:project2) { create(:project, namespace: group) }
let!(:existing_package) { create(:terraform_module_package, name: 'mymodule/mysystem', project: project2) }
before do
project.add_developer(user)
end
context 'when duplicates not allowed' do
it_behaves_like 'not creating a package', :forbidden
end
context 'when duplicates allowed' do
before do
package_settings.update_column(:terraform_module_duplicates_allowed, true)
end
it_behaves_like 'creating a package'
end
context 'with duplicate regex exception' do
before do
package_settings.update_columns(
terraform_module_duplicates_allowed: false,
terraform_module_duplicate_exception_regex: regex
)
end
context 'when regex matches' do
let(:regex) { ".*#{existing_package.name.last(3)}.*" }
it_behaves_like 'creating a package'
end
context 'when regex does not match' do
let(:regex) { '.*non-matching-regex.*' }
it_behaves_like 'not creating a package', :forbidden
end
end
end

View File

@ -6,7 +6,7 @@ RSpec.describe Groups::CreateService, '#execute', feature_category: :groups_and_
let!(:user) { create(:user) }
let!(:group_params) { { path: "group_path", visibility_level: Gitlab::VisibilityLevel::PUBLIC } }
subject { service.execute }
subject(:execute) { service.execute }
shared_examples 'has sync-ed traversal_ids' do
specify { expect(subject.reload.traversal_ids).to eq([subject.parent&.traversal_ids, subject.id].flatten.compact) }
@ -119,6 +119,49 @@ RSpec.describe Groups::CreateService, '#execute', feature_category: :groups_and_
end
end
describe 'creating a group within an organization' do
let(:current_user) { user }
let(:service) { described_class.new(current_user, params) }
context 'when organization is provided' do
let_it_be(:organization) { create(:organization) }
let(:params) { group_params.merge(organization_id: organization.id) }
context 'when user can create the group' do
before do
create(:organization_user, user: user, organization: organization)
end
it { is_expected.to be_persisted }
end
context 'when user is an admin', :enable_admin_mode do
let(:current_user) { create(:admin) }
it { is_expected.to be_persisted }
end
context 'when user can not create the group' do
it 'does not save group and returns an error' do
expect(execute).not_to be_persisted
expect(execute.errors[:organization_id].first)
.to eq(s_("CreateGroup|You don't have permission to create a group in the provided organization."))
expect(execute.organization_id).to be_nil
end
end
end
context 'when organization is the default organization and not set by params' do
let(:params) { group_params }
before do
create(:organization, :default)
end
it { is_expected.to be_persisted }
end
end
describe 'creating subgroup' do
let!(:group) { create(:group) }
let!(:service) { described_class.new(user, group_params.merge(parent_id: group.id)) }

View File

@ -46,7 +46,9 @@ RSpec.describe ::Namespaces::PackageSettings::UpdateService, feature_category: :
lock_npm_package_requests_forwarding: false,
pypi_package_requests_forwarding: nil,
lock_pypi_package_requests_forwarding: false,
nuget_symbol_server_enabled: false
nuget_symbol_server_enabled: false,
terraform_module_duplicates_allowed: false,
terraform_module_duplicate_exception_regex: 'foo'
}, to: {
maven_duplicates_allowed: false,
maven_duplicate_exception_regex: 'RELEASE',
@ -60,7 +62,9 @@ RSpec.describe ::Namespaces::PackageSettings::UpdateService, feature_category: :
lock_npm_package_requests_forwarding: true,
pypi_package_requests_forwarding: true,
lock_pypi_package_requests_forwarding: true,
nuget_symbol_server_enabled: true
nuget_symbol_server_enabled: true,
terraform_module_duplicates_allowed: true,
terraform_module_duplicate_exception_regex: 'bar'
}
it_behaves_like 'returning a success'
@ -112,7 +116,9 @@ RSpec.describe ::Namespaces::PackageSettings::UpdateService, feature_category: :
lock_npm_package_requests_forwarding: true,
pypi_package_requests_forwarding: true,
lock_pypi_package_requests_forwarding: true,
nuget_symbol_server_enabled: true
nuget_symbol_server_enabled: true,
terraform_module_duplicates_allowed: true,
terraform_module_duplicate_exception_regex: 'bar'
}
end

View File

@ -2,10 +2,11 @@
require 'spec_helper'
RSpec.describe Packages::TerraformModule::CreatePackageService, feature_category: :package_registry do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:namespace) { create(:group) }
let_it_be(:project) { create(:project, namespace: namespace) }
let_it_be(:user) { create(:user) }
let_it_be(:sha256) { '440e5e148a25331bbd7991575f7d54933c0ebf6cc735a18ee5066ac1381bb590' }
let_it_be(:package_settings) { create(:namespace_package_setting, namespace: namespace) }
let(:overrides) { {} }
@ -36,10 +37,72 @@ RSpec.describe Packages::TerraformModule::CreatePackageService, feature_category
context 'package already exists elsewhere' do
let(:project2) { create(:project, namespace: namespace) }
let!(:existing_package) { create(:terraform_module_package, project: project2, name: 'foo/bar', version: '1.0.0') }
let!(:existing_package) do
create(:terraform_module_package, project: project2, name: 'foo/bar', version: '1.0.0')
end
it { expect(subject[:http_status]).to eq 403 }
it { expect(subject[:message]).to be 'Access Denied' }
context 'when duplicates not allowed' do
it { expect(subject.reason).to eq :forbidden }
it { expect(subject.message).to be 'A package with the same name already exists in the namespace' }
end
context 'when duplicates allowed' do
before do
package_settings.update_column(:terraform_module_duplicates_allowed, true)
end
it_behaves_like 'creating a package'
end
context 'with duplicate regex exception' do
before do
package_settings.update_columns(
terraform_module_duplicates_allowed: false,
terraform_module_duplicate_exception_regex: regex
)
end
context 'when regex matches' do
let(:regex) { ".*#{existing_package.name.last(3)}.*" }
it_behaves_like 'creating a package'
end
context 'when regex does not match' do
let(:regex) { '.*not-a-match.*' }
it { expect(subject.reason).to eq :forbidden }
it { expect(subject.message).to be 'A package with the same name already exists in the namespace' }
end
end
context 'for ancestor namespace' do
let_it_be(:package_settings) { create(:namespace_package_setting, :group) }
let_it_be(:parent_namespace) { package_settings.namespace }
before do
namespace.update!(parent: parent_namespace)
end
context 'when duplicates allowed in an ancestor' do
before do
package_settings.update_column(:terraform_module_duplicates_allowed, true)
end
it_behaves_like 'creating a package'
end
context 'when duplicates allowed in an ancestor with exception' do
before do
package_settings.update_columns(
terraform_module_duplicates_allowed: false,
terraform_module_duplicate_exception_regex: ".*#{existing_package.name.last(3)}.*"
)
end
it_behaves_like 'creating a package'
end
end
context 'marked as pending_destruction' do
before do
@ -53,7 +116,7 @@ RSpec.describe Packages::TerraformModule::CreatePackageService, feature_category
context 'version already exists' do
let!(:existing_version) { create(:terraform_module_package, project: project, name: 'foo/bar', version: '1.0.1') }
it { expect(subject[:http_status]).to eq 403 }
it { expect(subject[:reason]).to eq :forbidden }
it { expect(subject[:message]).to be 'Package version already exists.' }
context 'marked as pending_destruction' do
@ -68,7 +131,7 @@ RSpec.describe Packages::TerraformModule::CreatePackageService, feature_category
context 'with empty version' do
let(:overrides) { { module_version: '' } }
it { expect(subject[:http_status]).to eq 400 }
it { expect(subject[:reason]).to eq :bad_request }
it { expect(subject[:message]).to eq 'Version is empty.' }
end
end

View File

@ -12,6 +12,8 @@ RSpec.shared_examples 'updating the namespace package setting attributes' do |to
.and change { namespace.package_settings.reload.nuget_duplicates_allowed }.from(from[:nuget_duplicates_allowed]).to(to[:nuget_duplicates_allowed])
.and change { namespace.package_settings.reload.nuget_duplicate_exception_regex }.from(from[:nuget_duplicate_exception_regex]).to(to[:nuget_duplicate_exception_regex])
.and change { namespace.package_settings.reload.nuget_symbol_server_enabled }.from(from[:nuget_symbol_server_enabled]).to(to[:nuget_symbol_server_enabled])
.and change { namespace.package_settings.reload.terraform_module_duplicates_allowed }.from(from[:terraform_module_duplicates_allowed]).to(to[:terraform_module_duplicates_allowed])
.and change { namespace.package_settings.reload.terraform_module_duplicate_exception_regex }.from(from[:terraform_module_duplicate_exception_regex]).to(to[:terraform_module_duplicate_exception_regex])
end
end
@ -36,6 +38,8 @@ RSpec.shared_examples 'creating the namespace package setting' do
expect(namespace.package_setting_relation.nuget_duplicates_allowed).to eq(package_settings[:nuget_duplicates_allowed])
expect(namespace.package_setting_relation.nuget_duplicate_exception_regex).to eq(package_settings[:nuget_duplicate_exception_regex])
expect(namespace.package_setting_relation.nuget_symbol_server_enabled).to eq(package_settings[:nuget_symbol_server_enabled])
expect(namespace.package_setting_relation.terraform_module_duplicates_allowed).to eq(package_settings[:terraform_module_duplicates_allowed])
expect(namespace.package_setting_relation.terraform_module_duplicate_exception_regex).to eq(package_settings[:terraform_module_duplicate_exception_regex])
end
it_behaves_like 'returning a success'