summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/groups/settings/ci_cd_controller.rb2
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb2
-rw-r--r--app/models/deploy_token.rb4
-rw-r--r--app/services/auth/container_registry_authentication_service.rb9
-rw-r--r--app/services/projects/update_repository_storage_service.rb7
-rw-r--r--app/views/projects/settings/ci_cd/show.html.haml2
-rw-r--r--app/views/shared/deploy_tokens/_form.html.haml5
-rw-r--r--app/workers/project_update_repository_storage_worker.rb10
-rw-r--r--changelogs/unreleased/22743-deploy-token-write-registry.yml5
-rw-r--r--db/migrate/20200406192059_add_write_registry_to_deploy_tokens.rb17
-rw-r--r--db/structure.sql4
-rw-r--r--doc/administration/availability/index.md8
-rw-r--r--doc/api/deploy_tokens.md4
-rw-r--r--doc/subscriptions/index.md4
-rw-r--r--doc/user/application_security/dast/index.md37
-rw-r--r--doc/user/project/deploy_tokens/img/deploy_tokens.pngbin62979 -> 177352 bytes
-rw-r--r--doc/user/project/deploy_tokens/index.md21
-rw-r--r--lib/api/deploy_tokens.rb5
-rw-r--r--lib/gitlab/auth.rb3
-rw-r--r--locale/gitlab.pot5
-rw-r--r--spec/factories/deploy_tokens.rb1
-rw-r--r--spec/lib/gitlab/auth_spec.rb117
-rw-r--r--spec/models/deploy_token_spec.rb4
-rw-r--r--spec/services/auth/container_registry_authentication_service_spec.rb10
-rw-r--r--spec/services/projects/fork_service_spec.rb2
-rw-r--r--spec/services/projects/update_repository_storage_service_spec.rb15
-rw-r--r--spec/support/services/deploy_token_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb11
-rw-r--r--spec/workers/project_update_repository_storage_worker_spec.rb29
29 files changed, 236 insertions, 109 deletions
diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb
index 989013df8d4..6b842fc9fe1 100644
--- a/app/controllers/groups/settings/ci_cd_controller.rb
+++ b/app/controllers/groups/settings/ci_cd_controller.rb
@@ -114,7 +114,7 @@ module Groups
end
def deploy_token_params
- params.require(:deploy_token).permit(:name, :expires_at, :read_repository, :read_registry, :username)
+ params.require(:deploy_token).permit(:name, :expires_at, :read_repository, :read_registry, :write_registry, :username)
end
end
end
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index 5feb3e019c2..a0f98d8f1d2 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -93,7 +93,7 @@ module Projects
end
def deploy_token_params
- params.require(:deploy_token).permit(:name, :expires_at, :read_repository, :read_registry, :username)
+ params.require(:deploy_token).permit(:name, :expires_at, :read_repository, :read_registry, :write_registry, :username)
end
def run_autodevops_pipeline(service)
diff --git a/app/models/deploy_token.rb b/app/models/deploy_token.rb
index a9844f627b7..69245710f01 100644
--- a/app/models/deploy_token.rb
+++ b/app/models/deploy_token.rb
@@ -7,7 +7,7 @@ class DeployToken < ApplicationRecord
include Gitlab::Utils::StrongMemoize
add_authentication_token_field :token, encrypted: :optional
- AVAILABLE_SCOPES = %i(read_repository read_registry).freeze
+ AVAILABLE_SCOPES = %i(read_repository read_registry write_registry).freeze
GITLAB_DEPLOY_TOKEN_NAME = 'gitlab-deploy-token'
default_value_for(:expires_at) { Forever.date }
@@ -105,7 +105,7 @@ class DeployToken < ApplicationRecord
end
def ensure_at_least_one_scope
- errors.add(:base, _("Scopes can't be blank")) unless read_repository || read_registry
+ errors.add(:base, _("Scopes can't be blank")) unless read_repository || read_registry || write_registry
end
def default_username
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index 629c1cbdc5c..4a699fe3213 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -135,7 +135,7 @@ module Auth
when 'pull'
build_can_pull?(requested_project) || user_can_pull?(requested_project) || deploy_token_can_pull?(requested_project)
when 'push'
- build_can_push?(requested_project) || user_can_push?(requested_project)
+ build_can_push?(requested_project) || user_can_push?(requested_project) || deploy_token_can_push?(requested_project)
when 'delete'
build_can_delete?(requested_project) || user_can_admin?(requested_project)
when '*'
@@ -185,6 +185,13 @@ module Auth
current_user.read_registry?
end
+ def deploy_token_can_push?(requested_project)
+ has_authentication_ability?(:create_container_image) &&
+ current_user.is_a?(DeployToken) &&
+ current_user.has_access_to?(requested_project) &&
+ current_user.write_registry?
+ end
+
##
# We still support legacy pipeline triggers which do not have associated
# actor. New permissions model and new triggers are always associated with
diff --git a/app/services/projects/update_repository_storage_service.rb b/app/services/projects/update_repository_storage_service.rb
index 0602089a3ab..2e5de9411d1 100644
--- a/app/services/projects/update_repository_storage_service.rb
+++ b/app/services/projects/update_repository_storage_service.rb
@@ -5,12 +5,15 @@ module Projects
include Gitlab::ShellAdapter
Error = Class.new(StandardError)
+ SameFilesystemError = Class.new(Error)
def initialize(project)
@project = project
end
def execute(new_repository_storage_key)
+ raise SameFilesystemError if same_filesystem?(project.repository.storage, new_repository_storage_key)
+
mirror_repositories(new_repository_storage_key)
mark_old_paths_for_archive
@@ -33,6 +36,10 @@ module Projects
private
+ def same_filesystem?(old_storage, new_storage)
+ Gitlab::GitalyClient.filesystem_id(old_storage) == Gitlab::GitalyClient.filesystem_id(new_storage)
+ end
+
def mirror_repositories(new_repository_storage_key)
mirror_repository(new_repository_storage_key)
diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml
index ab2f64cdc21..c0f60b5f3b1 100644
--- a/app/views/projects/settings/ci_cd/show.html.haml
+++ b/app/views/projects/settings/ci_cd/show.html.haml
@@ -4,7 +4,7 @@
- expanded = expanded_by_default?
- general_expanded = @project.errors.empty? ? expanded : true
-- deploy_token_description = s_('DeployTokens|Deploy tokens allow read-only access to your repository and registry images.')
+- deploy_token_description = s_('DeployTokens|Deploy tokens allow access to your repository and registry images.')
%section.settings#js-general-pipeline-settings.no-animate{ class: ('expanded' if general_expanded) }
.settings-header
diff --git a/app/views/shared/deploy_tokens/_form.html.haml b/app/views/shared/deploy_tokens/_form.html.haml
index c4e82d8e157..5751ed9cb7a 100644
--- a/app/views/shared/deploy_tokens/_form.html.haml
+++ b/app/views/shared/deploy_tokens/_form.html.haml
@@ -30,5 +30,10 @@
= label_tag ("deploy_token_read_registry"), 'read_registry', class: 'label-bold form-check-label'
.text-secondary= s_('DeployTokens|Allows read-only access to the registry images')
+ %fieldset.form-group.form-check
+ = f.check_box :write_registry, class: 'form-check-input'
+ = label_tag ("deploy_token_write_registry"), 'write_registry', class: 'label-bold form-check-label'
+ .text-secondary= s_('DeployTokens|Allows write access to the registry images')
+
.prepend-top-default
= f.submit s_('DeployTokens|Create deploy token'), class: 'btn btn-success qa-create-deploy-token'
diff --git a/app/workers/project_update_repository_storage_worker.rb b/app/workers/project_update_repository_storage_worker.rb
index bb40107494b..ecee33e6421 100644
--- a/app/workers/project_update_repository_storage_worker.rb
+++ b/app/workers/project_update_repository_storage_worker.rb
@@ -3,21 +3,11 @@
class ProjectUpdateRepositoryStorageWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
- SameFilesystemError = Class.new(StandardError)
-
feature_category :gitaly
def perform(project_id, new_repository_storage_key)
project = Project.find(project_id)
- raise SameFilesystemError if same_filesystem?(project.repository.storage, new_repository_storage_key)
-
::Projects::UpdateRepositoryStorageService.new(project).execute(new_repository_storage_key)
end
-
- private
-
- def same_filesystem?(old_storage, new_storage)
- Gitlab::GitalyClient.filesystem_id(old_storage) == Gitlab::GitalyClient.filesystem_id(new_storage)
- end
end
diff --git a/changelogs/unreleased/22743-deploy-token-write-registry.yml b/changelogs/unreleased/22743-deploy-token-write-registry.yml
new file mode 100644
index 00000000000..842ef95446d
--- /dev/null
+++ b/changelogs/unreleased/22743-deploy-token-write-registry.yml
@@ -0,0 +1,5 @@
+---
+title: Add write_registry scope to deploy tokens for container registry push access
+merge_request: 28958
+author:
+type: added
diff --git a/db/migrate/20200406192059_add_write_registry_to_deploy_tokens.rb b/db/migrate/20200406192059_add_write_registry_to_deploy_tokens.rb
new file mode 100644
index 00000000000..22fdb030edc
--- /dev/null
+++ b/db/migrate/20200406192059_add_write_registry_to_deploy_tokens.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddWriteRegistryToDeployTokens < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default(:deploy_tokens, :write_registry, :boolean, default: false, allow_null: false)
+ end
+
+ def down
+ remove_column(:deploy_tokens, :write_registry)
+ end
+end
diff --git a/db/structure.sql b/db/structure.sql
index 90585a157cf..622234396a2 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -1997,7 +1997,8 @@ CREATE TABLE public.deploy_tokens (
token character varying,
username character varying,
token_encrypted character varying(255),
- deploy_token_type smallint DEFAULT 2 NOT NULL
+ deploy_token_type smallint DEFAULT 2 NOT NULL,
+ write_registry boolean DEFAULT false NOT NULL
);
CREATE SEQUENCE public.deploy_tokens_id_seq
@@ -13087,6 +13088,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200403185127
20200403185422
20200406135648
+20200406192059
20200407094005
20200407094923
20200408110856
diff --git a/doc/administration/availability/index.md b/doc/administration/availability/index.md
index 90113985ad5..a0d4ea7919f 100644
--- a/doc/administration/availability/index.md
+++ b/doc/administration/availability/index.md
@@ -26,6 +26,14 @@ watch [this 1 hour Q&A](https://www.youtube.com/watch?v=uCU8jdYzpac)
with [John Northrup](https://gitlab.com/northrup), and live questions coming
in from some of our customers.
+GitLab offers a number of options to manage availability and resiliency. Below are the options to consider with trade-offs.
+
+| Event | GitLab Feature | Recovery Point Objective (RPO) | Recovery Time Objective (RTO) | Cost |
+| ----- | -------------- | --- | --- | ---- |
+| Availability Zone failure | "GitLab HA" | No loss | No loss | 2x Git storage, multiple nodes balanced across AZ's |
+| Region failure | "GitLab Disaster Recovery" | 5-10 minutes | 30 minutes | 2x primary cost |
+| All failures | Backup/Restore | Last backup | Hours to Days | Cost of storing the backups |
+
## High availability
### Omnibus installation with automatic database failover
diff --git a/doc/api/deploy_tokens.md b/doc/api/deploy_tokens.md
index 4663159f1eb..461957847df 100644
--- a/doc/api/deploy_tokens.md
+++ b/doc/api/deploy_tokens.md
@@ -92,7 +92,7 @@ POST /projects/:id/deploy_tokens
| `name` | string | yes | New deploy token's name |
| `expires_at` | datetime | no | Expiration date for the deploy token. Does not expire if no value is provided. |
| `username` | string | no | Username for deploy token. Default is `gitlab+deploy-token-{n}` |
-| `scopes` | array of strings | yes | Indicates the deploy token scopes. Must be at least one of `read_repository` or `read_registry`. |
+| `scopes` | array of strings | yes | Indicates the deploy token scopes. Must be at least one of `read_repository`, `read_registry`, or `write_registry`. |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --header "Content-Type: application/json" --data '{"name": "My deploy token", "expires_at": "2021-01-01", "username": "custom-user", "scopes": ["read_repository"]}' "https://gitlab.example.com/api/v4/projects/5/deploy_tokens/"
@@ -193,7 +193,7 @@ POST /groups/:id/deploy_tokens
| `name` | string | yes | New deploy token's name |
| `expires_at` | datetime | no | Expiration date for the deploy token. Does not expire if no value is provided. |
| `username` | string | no | Username for deploy token. Default is `gitlab+deploy-token-{n}` |
-| `scopes` | array of strings | yes | Indicates the deploy token scopes. Must be at least one of `read_repository` or `read_registry`. |
+| `scopes` | array of strings | yes | Indicates the deploy token scopes. Must be at least one of `read_repository`, `read_registry`, or `write_registry`. |
Example request:
diff --git a/doc/subscriptions/index.md b/doc/subscriptions/index.md
index e2868d648de..f3488b45bbb 100644
--- a/doc/subscriptions/index.md
+++ b/doc/subscriptions/index.md
@@ -75,6 +75,10 @@ count as active users in the subscription period in which they were originally a
- Members with Guest permissions on an Ultimate subscription.
- GitLab-created service accounts: `Ghost User` and `Support Bot`.
+##### User Statistics
+
+A breakdown of the users within your instance including active, billable and blocked can be found by navigating to **Admin Area > Overview > Dashboard** and selecting `Users Statistics` button within the `Users` widget..
+
NOTE: **Note:**
If you have LDAP integration enabled, anyone in the configured domain can sign up for a GitLab account. This can result in an unexpected bill at time of renewal. Consider [disabling new signups](../user/admin_area/settings/sign_up_restrictions.md) and managing new users manually instead.
diff --git a/doc/user/application_security/dast/index.md b/doc/user/application_security/dast/index.md
index c65d6adcff6..57d2a383768 100644
--- a/doc/user/application_security/dast/index.md
+++ b/doc/user/application_security/dast/index.md
@@ -463,10 +463,41 @@ The DAST job does not require the project's repository to be present when runnin
## Running DAST in an offline environment
-DAST can be executed on an offline GitLab Ultimate installation by using the following process:
+For self-managed GitLab instances in an environment with limited, restricted, or intermittent access
+to external resources through the internet, some adjustments are required for the DAST job to
+successfully run. For more information, see [Offline environments](../offline_deployments/index.md).
+
+### Requirements for offline DAST support
+
+To use DAST in an offline environment, you need:
+
+- GitLab Runner with the [`docker` or `kubernetes` executor](#requirements).
+- Docker Container Registry with a locally available copy of the DAST [container image](https://gitlab.com/gitlab-org/security-products/dast), found in the [DAST container registry](https://gitlab.com/gitlab-org/security-products/dast/container_registry).
+
+NOTE: **Note:**
+GitLab Runner has a [default `pull policy` of `always`](https://docs.gitlab.com/runner/executors/docker.html#using-the-always-pull-policy),
+meaning the runner may try to pull remote images even if a local copy is available. Set GitLab
+Runner's [`pull_policy` to `if-not-present`](https://docs.gitlab.com/runner/executors/docker.html#using-the-if-not-present-pull-policy)
+in an offline environment if you prefer using only locally available Docker images.
+
+### Make GitLab DAST analyzer images available inside your Docker registry
+
+For DAST, import the following default DAST analyzer image from `registry.gitlab.com` to your local "offline"
+registry:
+
+- `registry.gitlab.com/gitlab-org/security-products/dast:latest`
+
+The process for importing Docker images into a local offline Docker registry depends on
+**your network security policy**. Please consult your IT staff to find an accepted and approved
+process by which external resources can be imported or temporarily accessed. Note that these scanners are [updated periodically](../index.md#maintenance-and-update-of-the-vulnerabilities-database)
+with new definitions, so consider if you are able to make periodic updates yourself.
+
+For details on saving and transporting Docker images as a file, see Docker's documentation on
+[`docker save`](https://docs.docker.com/engine/reference/commandline/save/), [`docker load`](https://docs.docker.com/engine/reference/commandline/load/),
+[`docker export`](https://docs.docker.com/engine/reference/commandline/export/), and [`docker import`](https://docs.docker.com/engine/reference/commandline/import/).
+
+### Set DAST CI job variables to use local DAST analyzers
-1. Host the DAST image `registry.gitlab.com/gitlab-org/security-products/dast:latest` in your local
- Docker container registry.
1. Add the following configuration to your `.gitlab-ci.yml` file. You must replace `image` to refer
to the DAST Docker image hosted on your local Docker container registry:
diff --git a/doc/user/project/deploy_tokens/img/deploy_tokens.png b/doc/user/project/deploy_tokens/img/deploy_tokens.png
index 493de8e0fce..afe1dfb922f 100644
--- a/doc/user/project/deploy_tokens/img/deploy_tokens.png
+++ b/doc/user/project/deploy_tokens/img/deploy_tokens.png
Binary files differ
diff --git a/doc/user/project/deploy_tokens/index.md b/doc/user/project/deploy_tokens/index.md
index 0bd511cf837..ebb12a6ed5d 100644
--- a/doc/user/project/deploy_tokens/index.md
+++ b/doc/user/project/deploy_tokens/index.md
@@ -2,8 +2,9 @@
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17894) in GitLab 10.7.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/issues/199370) from **Settings > Repository** in GitLab 12.9.
+> - [Added `write_registry` scope](https://gitlab.com/gitlab-org/gitlab/-/issues/22743) in GitLab 12.10.
-Deploy tokens allow you to download (`git clone`) or read the container registry images of a project without having a user and a password.
+Deploy tokens allow you to download (`git clone`) or push and pull the container registry images of a project without having a user and a password.
Deploy tokens can be managed by [maintainers only](../../permissions.md).
@@ -44,6 +45,7 @@ the following table.
| ----- | ----------- |
| `read_repository` | Allows read-access to the repository through `git clone` |
| `read_registry` | Allows read-access to [container registry](../../packages/container_registry/index.md) images if a project is private and authorization is required. |
+| `write_registry` | Allows write-access (push) to [container registry](../../packages/container_registry/index.md). |
## Deploy token custom username
@@ -83,6 +85,21 @@ docker login -u <username> -p <deploy_token> registry.example.com
Just replace `<username>` and `<deploy_token>` with the proper values. Then you can simply
pull images from your Container Registry.
+### Push Container Registry images
+
+To push the container registry images, you'll need to:
+
+1. Create a Deploy Token with `write_registry` as a scope.
+1. Take note of your `username` and `token`.
+1. Log in to GitLab’s Container Registry using the deploy token:
+
+ ```shell
+ docker login -u <username> -p <deploy_token> registry.example.com
+ ```
+
+Just replace `<username>` and `<deploy_token>` with the proper values. Then you can simply
+push images to your Container Registry.
+
### Group Deploy Token
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/21765) in GitLab 12.9.
@@ -107,7 +124,7 @@ There's a special case when it comes to Deploy Tokens. If a user creates one
named `gitlab-deploy-token`, the username and token of the Deploy Token will be
automatically exposed to the CI/CD jobs as environment variables: `CI_DEPLOY_USER` and
`CI_DEPLOY_PASSWORD`, respectively. With the GitLab Deploy Token, the
-`read_registry` scope is implied.
+`read_registry` and `write_registry` scopes are implied.
After you create the token, you can login to the Container Registry using
those variables:
diff --git a/lib/api/deploy_tokens.rb b/lib/api/deploy_tokens.rb
index d36b75f5bfd..5de36c14d7b 100644
--- a/lib/api/deploy_tokens.rb
+++ b/lib/api/deploy_tokens.rb
@@ -10,6 +10,7 @@ module API
result_hash = {}
result_hash[:read_registry] = scopes.include?('read_registry')
+ result_hash[:write_registry] = scopes.include?('write_registry')
result_hash[:read_repository] = scopes.include?('read_repository')
result_hash
end
@@ -54,7 +55,7 @@ module API
params do
requires :name, type: String, desc: "New deploy token's name"
requires :scopes, type: Array[String], values: ::DeployToken::AVAILABLE_SCOPES.map(&:to_s),
- desc: 'Indicates the deploy token scopes. Must be at least one of "read_repository" or "read_registry".'
+ desc: 'Indicates the deploy token scopes. Must be at least one of "read_repository", "read_registry", or "write_registry".'
optional :expires_at, type: DateTime, desc: 'Expiration date for the deploy token. Does not expire if no value is provided.'
optional :username, type: String, desc: 'Username for deploy token. Default is `gitlab+deploy-token-{n}`'
end
@@ -117,7 +118,7 @@ module API
params do
requires :name, type: String, desc: 'The name of the deploy token'
requires :scopes, type: Array[String], values: ::DeployToken::AVAILABLE_SCOPES.map(&:to_s),
- desc: 'Indicates the deploy token scopes. Must be at least one of "read_repository" or "read_registry".'
+ desc: 'Indicates the deploy token scopes. Must be at least one of "read_repository", "read_registry", or "write_registry".'
optional :expires_at, type: DateTime, desc: 'Expiration date for the deploy token. Does not expire if no value is provided.'
optional :username, type: String, desc: 'Username for deploy token. Default is `gitlab+deploy-token-{n}`'
end
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index c489c835d9d..8e14d21f591 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -12,7 +12,7 @@ module Gitlab
REPOSITORY_SCOPES = [:read_repository, :write_repository].freeze
# Scopes used for GitLab Docker Registry access
- REGISTRY_SCOPES = [:read_registry].freeze
+ REGISTRY_SCOPES = [:read_registry, :write_registry].freeze
# Scopes used for GitLab as admin
ADMIN_SCOPES = [:sudo].freeze
@@ -200,6 +200,7 @@ module Gitlab
api: full_authentication_abilities,
read_api: read_only_authentication_abilities,
read_registry: [:read_container_image],
+ write_registry: [:create_container_image],
read_repository: [:download_code],
write_repository: [:download_code, :push_code]
}
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index fb2f7c60ae2..14fc1487b5e 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -6835,6 +6835,9 @@ msgstr ""
msgid "DeployTokens|Allows read-only access to the repository"
msgstr ""
+msgid "DeployTokens|Allows write access to the registry images"
+msgstr ""
+
msgid "DeployTokens|Copy deploy token"
msgstr ""
@@ -6853,7 +6856,7 @@ msgstr ""
msgid "DeployTokens|Deploy Tokens"
msgstr ""
-msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
+msgid "DeployTokens|Deploy tokens allow access to your repository and registry images."
msgstr ""
msgid "DeployTokens|Expires"
diff --git a/spec/factories/deploy_tokens.rb b/spec/factories/deploy_tokens.rb
index e86d4ab8812..657915f9976 100644
--- a/spec/factories/deploy_tokens.rb
+++ b/spec/factories/deploy_tokens.rb
@@ -7,6 +7,7 @@ FactoryBot.define do
sequence(:name) { |n| "PDT #{n}" }
read_repository { true }
read_registry { true }
+ write_registry { false }
revoked { false }
expires_at { 5.days.from_now }
deploy_token_type { DeployToken.deploy_token_types[:project_type] }
diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb
index ce60a19a7b3..a0a8767637e 100644
--- a/spec/lib/gitlab/auth_spec.rb
+++ b/spec/lib/gitlab/auth_spec.rb
@@ -30,7 +30,7 @@ describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
it 'optional_scopes contains all non-default scopes' do
stub_container_registry_config(enabled: true)
- expect(subject.optional_scopes).to eq %i[read_user read_api read_repository write_repository read_registry sudo openid profile email]
+ expect(subject.optional_scopes).to eq %i[read_user read_api read_repository write_repository read_registry write_registry sudo openid profile email]
end
end
@@ -38,21 +38,21 @@ describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
it 'contains all non-default scopes' do
stub_container_registry_config(enabled: true)
- expect(subject.all_available_scopes).to eq %i[api read_user read_api read_repository write_repository read_registry sudo]
+ expect(subject.all_available_scopes).to eq %i[api read_user read_api read_repository write_repository read_registry write_registry sudo]
end
it 'contains for non-admin user all non-default scopes without ADMIN access' do
stub_container_registry_config(enabled: true)
user = create(:user, admin: false)
- expect(subject.available_scopes_for(user)).to eq %i[api read_user read_api read_repository write_repository read_registry]
+ expect(subject.available_scopes_for(user)).to eq %i[api read_user read_api read_repository write_repository read_registry write_registry]
end
it 'contains for admin user all non-default scopes with ADMIN access' do
stub_container_registry_config(enabled: true)
user = create(:user, admin: true)
- expect(subject.available_scopes_for(user)).to eq %i[api read_user read_api read_repository write_repository read_registry sudo]
+ expect(subject.available_scopes_for(user)).to eq %i[api read_user read_api read_repository write_repository read_registry write_registry sudo]
end
context 'registry_scopes' do
@@ -72,7 +72,7 @@ describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
end
it 'contains all registry related scopes' do
- expect(subject.registry_scopes).to eq %i[read_registry]
+ expect(subject.registry_scopes).to eq %i[read_registry write_registry]
end
end
end
@@ -401,6 +401,49 @@ describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
context 'while using deploy tokens' do
let(:auth_failure) { Gitlab::Auth::Result.new(nil, nil) }
+ shared_examples 'registry token scope' do
+ it 'fails when login is not valid' do
+ expect(gl_auth.find_for_git_client('random_login', deploy_token.token, project: project, ip: 'ip'))
+ .to eq(auth_failure)
+ end
+
+ it 'fails when token is not valid' do
+ expect(gl_auth.find_for_git_client(login, '123123', project: project, ip: 'ip'))
+ .to eq(auth_failure)
+ end
+
+ it 'fails if token is nil' do
+ expect(gl_auth.find_for_git_client(login, nil, project: nil, ip: 'ip'))
+ .to eq(auth_failure)
+ end
+
+ it 'fails if token is not related to project' do
+ expect(gl_auth.find_for_git_client(login, 'abcdef', project: nil, ip: 'ip'))
+ .to eq(auth_failure)
+ end
+
+ it 'fails if token has been revoked' do
+ deploy_token.revoke!
+
+ expect(deploy_token.revoked?).to be_truthy
+ expect(gl_auth.find_for_git_client('deploy-token', deploy_token.token, project: nil, ip: 'ip'))
+ .to eq(auth_failure)
+ end
+ end
+
+ shared_examples 'deploy token with disabled registry' do
+ context 'when registry disabled' do
+ before do
+ stub_container_registry_config(enabled: false)
+ end
+
+ it 'fails when login and token are valid' do
+ expect(gl_auth.find_for_git_client(login, deploy_token.token, project: nil, ip: 'ip'))
+ .to eq(auth_failure)
+ end
+ end
+ end
+
context 'when deploy token and user have the same username' do
let(:username) { 'normal_user' }
let(:user) { create(:user, username: username, password: 'my-secret') }
@@ -425,34 +468,33 @@ describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
context 'and belong to the same project' do
let!(:read_registry) { create(:deploy_token, username: 'deployer', read_repository: false, projects: [project]) }
let!(:read_repository) { create(:deploy_token, username: read_registry.username, read_registry: false, projects: [project]) }
+ let(:auth_success) { Gitlab::Auth::Result.new(read_repository, project, :deploy_token, [:download_code]) }
it 'succeeds for the right token' do
- auth_success = Gitlab::Auth::Result.new(read_repository, project, :deploy_token, [:download_code])
-
expect(gl_auth.find_for_git_client('deployer', read_repository.token, project: project, ip: 'ip'))
.to eq(auth_success)
end
it 'fails for the wrong token' do
expect(gl_auth.find_for_git_client('deployer', read_registry.token, project: project, ip: 'ip'))
- .to eq(auth_failure)
+ .not_to eq(auth_success)
end
end
context 'and belong to different projects' do
+ let_it_be(:other_project) { create(:project) }
let!(:read_registry) { create(:deploy_token, username: 'deployer', read_repository: false, projects: [project]) }
- let!(:read_repository) { create(:deploy_token, username: read_registry.username, read_registry: false, projects: [project]) }
+ let!(:read_repository) { create(:deploy_token, username: read_registry.username, read_registry: false, projects: [other_project]) }
+ let(:auth_success) { Gitlab::Auth::Result.new(read_repository, other_project, :deploy_token, [:download_code]) }
it 'succeeds for the right token' do
- auth_success = Gitlab::Auth::Result.new(read_repository, project, :deploy_token, [:download_code])
-
- expect(gl_auth.find_for_git_client('deployer', read_repository.token, project: project, ip: 'ip'))
+ expect(gl_auth.find_for_git_client('deployer', read_repository.token, project: other_project, ip: 'ip'))
.to eq(auth_success)
end
it 'fails for the wrong token' do
- expect(gl_auth.find_for_git_client('deployer', read_registry.token, project: project, ip: 'ip'))
- .to eq(auth_failure)
+ expect(gl_auth.find_for_git_client('deployer', read_registry.token, project: other_project, ip: 'ip'))
+ .not_to eq(auth_success)
end
end
end
@@ -542,45 +584,32 @@ describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
.to eq(auth_success)
end
- it 'fails when login is not valid' do
- expect(gl_auth.find_for_git_client('random_login', deploy_token.token, project: project, ip: 'ip'))
- .to eq(auth_failure)
- end
+ it_behaves_like 'registry token scope'
+ end
- it 'fails when token is not valid' do
- expect(gl_auth.find_for_git_client(login, '123123', project: project, ip: 'ip'))
- .to eq(auth_failure)
- end
+ it_behaves_like 'deploy token with disabled registry'
+ end
- it 'fails if token is nil' do
- expect(gl_auth.find_for_git_client(login, nil, project: nil, ip: 'ip'))
- .to eq(auth_failure)
- end
+ context 'when the deploy token has write_registry as a scope' do
+ let_it_be(:deploy_token) { create(:deploy_token, write_registry: true, read_repository: false, read_registry: false, projects: [project]) }
+ let_it_be(:login) { deploy_token.username }
- it 'fails if token is not related to project' do
- expect(gl_auth.find_for_git_client(login, 'abcdef', project: nil, ip: 'ip'))
- .to eq(auth_failure)
+ context 'when registry enabled' do
+ before do
+ stub_container_registry_config(enabled: true)
end
- it 'fails if token has been revoked' do
- deploy_token.revoke!
-
- expect(deploy_token.revoked?).to be_truthy
- expect(gl_auth.find_for_git_client('deploy-token', deploy_token.token, project: nil, ip: 'ip'))
- .to eq(auth_failure)
- end
- end
+ it 'succeeds when login and a project token are valid' do
+ auth_success = Gitlab::Auth::Result.new(deploy_token, project, :deploy_token, [:create_container_image])
- context 'when registry disabled' do
- before do
- stub_container_registry_config(enabled: false)
+ expect(gl_auth.find_for_git_client(login, deploy_token.token, project: project, ip: 'ip'))
+ .to eq(auth_success)
end
- it 'fails when login and token are valid' do
- expect(gl_auth.find_for_git_client(login, deploy_token.token, project: nil, ip: 'ip'))
- .to eq(auth_failure)
- end
+ it_behaves_like 'registry token scope'
end
+
+ it_behaves_like 'deploy token with disabled registry'
end
end
end
diff --git a/spec/models/deploy_token_spec.rb b/spec/models/deploy_token_spec.rb
index 568699cf3f6..a2d4c046d46 100644
--- a/spec/models/deploy_token_spec.rb
+++ b/spec/models/deploy_token_spec.rb
@@ -62,7 +62,7 @@ describe DeployToken do
context 'with no scopes' do
it 'is invalid' do
- deploy_token = build(:deploy_token, read_repository: false, read_registry: false)
+ deploy_token = build(:deploy_token, read_repository: false, read_registry: false, write_registry: false)
expect(deploy_token).not_to be_valid
expect(deploy_token.errors[:base].first).to eq("Scopes can't be blank")
@@ -79,7 +79,7 @@ describe DeployToken do
context 'with only one scope' do
it 'returns scopes assigned to DeployToken' do
- deploy_token = create(:deploy_token, read_registry: false)
+ deploy_token = create(:deploy_token, read_registry: false, write_registry: false)
expect(deploy_token.scopes).to eq([:read_repository])
end
end
diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb
index 84f4a7a4e7a..8273269c2fb 100644
--- a/spec/services/auth/container_registry_authentication_service_spec.rb
+++ b/spec/services/auth/container_registry_authentication_service_spec.rb
@@ -766,8 +766,8 @@ describe Auth::ContainerRegistryAuthenticationService do
{ scopes: ["repository:#{project.full_path}:pull"] }
end
- context 'when deploy token has read_registry as a scope' do
- let(:current_user) { create(:deploy_token, projects: [project]) }
+ context 'when deploy token has read and write registry as scopes' do
+ let(:current_user) { create(:deploy_token, write_registry: true, projects: [project]) }
shared_examples 'able to login' do
context 'registry provides read_container_image authentication_abilities' do
@@ -790,7 +790,7 @@ describe Auth::ContainerRegistryAuthenticationService do
{ scopes: ["repository:#{project.full_path}:push"] }
end
- it_behaves_like 'an inaccessible'
+ it_behaves_like 'a pushable'
end
it_behaves_like 'able to login'
@@ -808,7 +808,7 @@ describe Auth::ContainerRegistryAuthenticationService do
{ scopes: ["repository:#{project.full_path}:push"] }
end
- it_behaves_like 'an inaccessible'
+ it_behaves_like 'a pushable'
end
it_behaves_like 'able to login'
@@ -826,7 +826,7 @@ describe Auth::ContainerRegistryAuthenticationService do
{ scopes: ["repository:#{project.full_path}:push"] }
end
- it_behaves_like 'an inaccessible'
+ it_behaves_like 'a pushable'
end
it_behaves_like 'able to login'
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index 443e3dfddf1..c8354f6ba4e 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -311,6 +311,8 @@ describe Projects::ForkService do
fork_before_move = fork_project(project)
# Stub everything required to move a project to a Gitaly shard that does not exist
+ allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original
+ allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('test_second_storage').and_return(SecureRandom.uuid)
stub_storage_settings('test_second_storage' => { 'path' => TestEnv::SECOND_STORAGE_PATH })
allow_any_instance_of(Gitlab::Git::Repository).to receive(:create_repository)
.and_return(true)
diff --git a/spec/services/projects/update_repository_storage_service_spec.rb b/spec/services/projects/update_repository_storage_service_spec.rb
index 23ce6f9165d..05555fa76f7 100644
--- a/spec/services/projects/update_repository_storage_service_spec.rb
+++ b/spec/services/projects/update_repository_storage_service_spec.rb
@@ -20,6 +20,8 @@ describe Projects::UpdateRepositoryStorageService do
let(:project_repository_double) { double(:repository) }
before do
+ allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original
+ allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('test_second_storage').and_return(SecureRandom.uuid)
allow(Gitlab::Git::Repository).to receive(:new).and_call_original
allow(Gitlab::Git::Repository).to receive(:new)
.with('test_second_storage', project.repository.raw.relative_path, project.repository.gl_repository, project.repository.full_path)
@@ -49,17 +51,20 @@ describe Projects::UpdateRepositoryStorageService do
end
end
- context 'when the project is already on the target storage' do
+ context 'when the filesystems are the same' do
it 'bails out and does nothing' do
result = subject.execute(project.repository_storage)
expect(result[:status]).to eq(:error)
- expect(result[:message]).to match(/repository and source have the same storage/)
+ expect(result[:message]).to match(/SameFilesystemError/)
end
end
context 'when the move fails' do
it 'unmarks the repository as read-only without updating the repository storage' do
+ allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original
+ allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('test_second_storage').and_return(SecureRandom.uuid)
+
expect(project_repository_double).to receive(:create_repository)
.and_return(true)
expect(project_repository_double).to receive(:replicate)
@@ -77,6 +82,9 @@ describe Projects::UpdateRepositoryStorageService do
context 'when the checksum does not match' do
it 'unmarks the repository as read-only without updating the repository storage' do
+ allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original
+ allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('test_second_storage').and_return(SecureRandom.uuid)
+
expect(project_repository_double).to receive(:create_repository)
.and_return(true)
expect(project_repository_double).to receive(:replicate)
@@ -97,6 +105,9 @@ describe Projects::UpdateRepositoryStorageService do
let!(:pool) { create(:pool_repository, :ready, source_project: project) }
it 'leaves the pool' do
+ allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original
+ allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('test_second_storage').and_return(SecureRandom.uuid)
+
expect(project_repository_double).to receive(:create_repository)
.and_return(true)
expect(project_repository_double).to receive(:replicate)
diff --git a/spec/support/services/deploy_token_shared_examples.rb b/spec/support/services/deploy_token_shared_examples.rb
index 9d681970739..adc5ea0fcdc 100644
--- a/spec/support/services/deploy_token_shared_examples.rb
+++ b/spec/support/services/deploy_token_shared_examples.rb
@@ -46,7 +46,7 @@ RSpec.shared_examples 'a deploy token creation service' do
end
context 'when the deploy token is invalid' do
- let(:deploy_token_params) { attributes_for(:deploy_token, read_repository: false, read_registry: false) }
+ let(:deploy_token_params) { attributes_for(:deploy_token, read_repository: false, read_registry: false, write_registry: false) }
it 'does not create a new DeployToken' do
expect { subject }.not_to change { DeployToken.count }
diff --git a/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb b/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
index b22379b8b68..d6166ac8188 100644
--- a/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
@@ -22,6 +22,9 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
context 'when the move succeeds', :clean_gitlab_redis_shared_state do
before do
+ allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original
+ allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('test_second_storage').and_return(SecureRandom.uuid)
+
allow(project_repository_double).to receive(:create_repository)
.and_return(true)
allow(project_repository_double).to receive(:replicate)
@@ -83,17 +86,19 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
end
end
- context 'when the project is already on the target storage' do
+ context 'when the filesystems are the same' do
it 'bails out and does nothing' do
result = subject.execute(project.repository_storage)
expect(result[:status]).to eq(:error)
- expect(result[:message]).to match(/repository and source have the same storage/)
+ expect(result[:message]).to match(/SameFilesystemError/)
end
end
context "when the move of the #{repository_type} repository fails" do
it 'unmarks the repository as read-only without updating the repository storage' do
+ allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original
+ allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('test_second_storage').and_return(SecureRandom.uuid)
allow(project_repository_double).to receive(:create_repository)
.and_return(true)
allow(project_repository_double).to receive(:replicate)
@@ -119,6 +124,8 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
context "when the checksum of the #{repository_type} repository does not match" do
it 'unmarks the repository as read-only without updating the repository storage' do
+ allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original
+ allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('test_second_storage').and_return(SecureRandom.uuid)
allow(project_repository_double).to receive(:create_repository)
.and_return(true)
allow(project_repository_double).to receive(:replicate)
diff --git a/spec/workers/project_update_repository_storage_worker_spec.rb b/spec/workers/project_update_repository_storage_worker_spec.rb
index ed99b8135c2..57a4c2128b3 100644
--- a/spec/workers/project_update_repository_storage_worker_spec.rb
+++ b/spec/workers/project_update_repository_storage_worker_spec.rb
@@ -9,33 +9,12 @@ describe ProjectUpdateRepositoryStorageWorker do
subject { described_class.new }
describe "#perform" do
- context 'when source and target repositories are on different filesystems' do
- before do
- allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original
- allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('new_storage').and_return(SecureRandom.uuid)
+ it "calls the update repository storage service" do
+ expect_next_instance_of(Projects::UpdateRepositoryStorageService) do |instance|
+ expect(instance).to receive(:execute).with('new_storage')
end
- it "calls the update repository storage service" do
- expect_next_instance_of(Projects::UpdateRepositoryStorageService) do |instance|
- expect(instance).to receive(:execute).with('new_storage')
- end
-
- subject.perform(project.id, 'new_storage')
- end
- end
-
- context 'when source and target repositories are on the same filesystems' do
- let(:filesystem_id) { SecureRandom.uuid }
-
- before do
- allow(Gitlab::GitalyClient).to receive(:filesystem_id).and_return(filesystem_id)
- end
-
- it 'raises an error' do
- expect_any_instance_of(::Projects::UpdateRepositoryStorageService).not_to receive(:new)
-
- expect { subject.perform(project.id, 'new_storage') }.to raise_error(ProjectUpdateRepositoryStorageWorker::SameFilesystemError)
- end
+ subject.perform(project.id, 'new_storage')
end
end
end