Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-07-04 15:09:33 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-07-04 15:09:33 +0300
commit46b08e61d27f9b3f45b130f9204084ffcf50c304 (patch)
tree96ef84fc43b41e8d1f8539fe085dc01496e29427
parent54cd986c9f16b5f7587b072ee8eb84bbf6642e8c (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/ci/rules.gitlab-ci.yml6
-rw-r--r--.gitlab/ci/vendored-gems.gitlab-ci.yml8
-rw-r--r--.rubocop_todo/gitlab/namespaced_class.yml2
-rw-r--r--.rubocop_todo/layout/line_length.yml2
-rw-r--r--.rubocop_todo/rspec/context_wording.yml2
-rw-r--r--.rubocop_todo/rspec/verified_doubles.yml1
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock7
-rw-r--r--app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue10
-rw-r--r--app/views/layouts/project.html.haml3
-rw-r--r--danger/database/Dangerfile9
-rw-r--r--doc/api/deploy_keys.md42
-rw-r--r--doc/api/dora/metrics.md36
-rw-r--r--doc/api/graphql/reference/index.md2
-rw-r--r--doc/integration/oauth_provider.md4
-rw-r--r--lib/api/entities/deploy_key.rb3
-rw-r--r--lib/gitlab/ci/build/rules/rule/clause/changes.rb16
-rw-r--r--lib/gitlab/ci/config/entry/rules/rule/changes.rb50
-rw-r--r--spec/features/groups_spec.rb16
-rw-r--r--spec/features/merge_request/user_awards_emoji_spec.rb11
-rw-r--r--spec/features/projects_spec.rb93
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/deploy_key.json3
-rw-r--r--spec/lib/api/entities/deploy_key_spec.rb9
-rw-r--r--spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb32
-rw-r--r--spec/lib/gitlab/ci/build/rules/rule_spec.rb20
-rw-r--r--spec/lib/gitlab/ci/config/entry/rules/rule/changes_spec.rb64
-rw-r--r--spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build_spec.rb48
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb47
-rw-r--r--spec/requests/api/npm_project_packages_spec.rb14
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb11
-rw-r--r--tooling/danger/project_helper.rb2
-rw-r--r--vendor/gems/devise-pbkdf2-encryptable/.gitlab-ci.yml26
-rw-r--r--vendor/gems/devise-pbkdf2-encryptable/Gemfile5
-rw-r--r--vendor/gems/devise-pbkdf2-encryptable/Gemfile.lock104
-rw-r--r--vendor/gems/devise-pbkdf2-encryptable/LICENSE201
-rw-r--r--vendor/gems/devise-pbkdf2-encryptable/devise-pbkdf2-encryptable.gemspec25
-rw-r--r--vendor/gems/devise-pbkdf2-encryptable/lib/devise-pbkdf2-encryptable.rb1
-rw-r--r--vendor/gems/devise-pbkdf2-encryptable/lib/devise/pbkdf2_encryptable/encryptable.rb16
-rw-r--r--vendor/gems/devise-pbkdf2-encryptable/lib/devise/pbkdf2_encryptable/encryptors/base.rb52
-rw-r--r--vendor/gems/devise-pbkdf2-encryptable/lib/devise/pbkdf2_encryptable/encryptors/pbkdf2_sha512.rb41
-rw-r--r--vendor/gems/devise-pbkdf2-encryptable/lib/devise/pbkdf2_encryptable/model.rb77
-rw-r--r--vendor/gems/devise-pbkdf2-encryptable/spec/lib/pbkdf2_encryptable/encryptors/pbkdf2_sha512_spec.rb80
-rw-r--r--vendor/gems/devise-pbkdf2-encryptable/spec/lib/pbkdf2_encryptable/model_spec.rb127
-rw-r--r--vendor/gems/devise-pbkdf2-encryptable/spec/spec_helper.rb4
-rw-r--r--workhorse/go.mod4
-rw-r--r--workhorse/go.sum7
47 files changed, 1244 insertions, 113 deletions
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index e188475485b..a2dd646d895 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -1507,6 +1507,12 @@
changes: ["vendor/gems/omniauth-gitlab/**/*"]
- <<: *if-merge-request-labels-run-all-rspec
+.vendor:rules:devise-pbkdf2-encryptable:
+ rules:
+ - <<: *if-merge-request
+ changes: ["vendor/gems/devise-pbkdf2-encryptable/**/*"]
+ - <<: *if-merge-request-labels-run-all-rspec
+
##################
# Releases rules #
##################
diff --git a/.gitlab/ci/vendored-gems.gitlab-ci.yml b/.gitlab/ci/vendored-gems.gitlab-ci.yml
index 6dd6e19a818..1f89186ed94 100644
--- a/.gitlab/ci/vendored-gems.gitlab-ci.yml
+++ b/.gitlab/ci/vendored-gems.gitlab-ci.yml
@@ -21,3 +21,11 @@ vendor omniauth-gitlab:
trigger:
include: vendor/gems/omniauth-gitlab/.gitlab-ci.yml
strategy: depend
+
+vendor devise-pbkdf2-encryptable:
+ extends:
+ - .vendor:rules:devise-pbkdf2-encryptable
+ needs: []
+ trigger:
+ include: vendor/gems/devise-pbkdf2-encryptable/.gitlab-ci.yml
+ strategy: depend
diff --git a/.rubocop_todo/gitlab/namespaced_class.yml b/.rubocop_todo/gitlab/namespaced_class.yml
index ffa0f4d0b2f..d951fee0c37 100644
--- a/.rubocop_todo/gitlab/namespaced_class.yml
+++ b/.rubocop_todo/gitlab/namespaced_class.yml
@@ -863,7 +863,7 @@ Gitlab/NamespacedClass:
- 'ee/app/controllers/survey_responses_controller.rb'
- 'ee/app/controllers/trial_registrations_controller.rb'
- 'ee/app/controllers/trials_controller.rb'
- - 'ee/app/finders/audit_log_finder.rb'
+ - 'ee/app/finders/audit_event_finder.rb'
- 'ee/app/finders/billed_users_finder.rb'
- 'ee/app/finders/custom_project_templates_finder.rb'
- 'ee/app/finders/dast_scanner_profiles_finder.rb'
diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml
index a04a734a148..d430399ae9d 100644
--- a/.rubocop_todo/layout/line_length.yml
+++ b/.rubocop_todo/layout/line_length.yml
@@ -2019,7 +2019,7 @@ Layout/LineLength:
- 'ee/spec/features/users/login_spec.rb'
- 'ee/spec/finders/analytics/devops_adoption/enabled_namespaces_finder_spec.rb'
- 'ee/spec/finders/analytics/devops_adoption/snapshots_finder_spec.rb'
- - 'ee/spec/finders/audit_log_finder_spec.rb'
+ - 'ee/spec/finders/audit_event_finder_spec.rb'
- 'ee/spec/finders/billed_users_finder_spec.rb'
- 'ee/spec/finders/boards/users_finder_spec.rb'
- 'ee/spec/finders/compliance_management/merge_requests/compliance_violations_finder_spec.rb'
diff --git a/.rubocop_todo/rspec/context_wording.yml b/.rubocop_todo/rspec/context_wording.yml
index 49968873ccb..559a15dc5ad 100644
--- a/.rubocop_todo/rspec/context_wording.yml
+++ b/.rubocop_todo/rspec/context_wording.yml
@@ -223,7 +223,7 @@ RSpec/ContextWording:
- 'ee/spec/features/trials/select_namespace_spec.rb'
- 'ee/spec/features/users/login_spec.rb'
- 'ee/spec/features/users/signup_spec.rb'
- - 'ee/spec/finders/audit_log_finder_spec.rb'
+ - 'ee/spec/finders/audit_event_finder_spec.rb'
- 'ee/spec/finders/autocomplete/vulnerabilities_autocomplete_finder_spec.rb'
- 'ee/spec/finders/billed_users_finder_spec.rb'
- 'ee/spec/finders/clusters/environments_finder_spec.rb'
diff --git a/.rubocop_todo/rspec/verified_doubles.yml b/.rubocop_todo/rspec/verified_doubles.yml
index df5e30e94ed..420b51316c3 100644
--- a/.rubocop_todo/rspec/verified_doubles.yml
+++ b/.rubocop_todo/rspec/verified_doubles.yml
@@ -442,7 +442,6 @@ RSpec/VerifiedDoubles:
- spec/lib/gitlab/ci/build/policy/variables_spec.rb
- spec/lib/gitlab/ci/build/policy_spec.rb
- spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb
- - spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb
- spec/lib/gitlab/ci/build/rules/rule_spec.rb
- spec/lib/gitlab/ci/build/rules_spec.rb
- spec/lib/gitlab/ci/build/status/reason_spec.rb
diff --git a/Gemfile b/Gemfile
index 52cd1bd953d..81791a3edab 100644
--- a/Gemfile
+++ b/Gemfile
@@ -30,6 +30,8 @@ gem 'declarative_policy', '~> 1.1.0'
# Authentication libraries
gem 'devise', '~> 4.7.2'
+gem 'devise-pbkdf2-encryptable', '~> 0.0.0', path: 'vendor/gems/devise-pbkdf2-encryptable'
+
gem 'bcrypt', '~> 3.1', '>= 3.1.14'
gem 'doorkeeper', '~> 5.5.0.rc2'
gem 'doorkeeper-openid_connect', '~> 1.7.5'
diff --git a/Gemfile.lock b/Gemfile.lock
index 6f6b98c0876..d0add73fd52 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -5,6 +5,12 @@ PATH
typhoeus (~> 1.0, >= 1.0.1)
PATH
+ remote: vendor/gems/devise-pbkdf2-encryptable
+ specs:
+ devise-pbkdf2-encryptable (0.0.0)
+ devise (~> 4.0)
+
+PATH
remote: vendor/gems/ipynbdiff
specs:
ipynbdiff (0.4.7)
@@ -1514,6 +1520,7 @@ DEPENDENCIES
derailed_benchmarks
device_detector
devise (~> 4.7.2)
+ devise-pbkdf2-encryptable (~> 0.0.0)!
devise-two-factor (~> 4.0.2)
diff_match_patch (~> 0.1.0)
diffy (~> 3.3)
diff --git a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue
index f035795a045..cdc5903b934 100644
--- a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue
+++ b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue
@@ -112,7 +112,7 @@ export default {
<gl-icon v-if="statusIcon" :name="statusIcon" :class="statusIconClass" />
<span class="gl-display-none gl-sm-display-block"><slot name="status-badge"></slot></span>
</gl-badge>
- <div class="issuable-meta gl-display-flex gl-align-items-center d-md-inline-block">
+ <div class="issuable-meta gl-display-flex! gl-align-items-center">
<div v-if="blocked || confidential" class="gl-display-inline-block">
<div v-if="blocked" data-testid="blocked" class="issuable-warning-icon inline">
<gl-icon name="lock" :aria-label="__('Blocked')" />
@@ -139,13 +139,15 @@ export default {
:size="24"
:src="author.avatarUrl"
:label="author.name"
- class="d-none d-sm-inline-flex gl-mx-1"
+ :class="[{ 'gl-display-none': !isAuthorExternal }, 'gl-sm-display-inline-flex gl-mx-1']"
>
<template #meta>
- <gl-icon v-if="isAuthorExternal" name="external-link" />
+ <gl-icon v-if="isAuthorExternal" name="external-link" class="gl-ml-1" />
</template>
</gl-avatar-labeled>
- <strong class="author d-sm-none d-inline">@{{ author.username }}</strong>
+ <strong v-if="author.username" class="author gl-display-inline gl-sm-display-none!"
+ >@{{ author.username }}</strong
+ >
</gl-avatar-link>
<span
v-if="taskCompletionStatus && hasTasks"
diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml
index a54e0351d2f..7e69cf1d685 100644
--- a/app/views/layouts/project.html.haml
+++ b/app/views/layouts/project.html.haml
@@ -8,6 +8,9 @@
- @left_sidebar = true
- @content_class = [@content_class, project_classes(@project)].compact.join(" ")
+- content_for :flash_message do
+ = render "layouts/header/storage_enforcement_banner", namespace: @project.namespace
+
- content_for :project_javascripts do
- project = @target_project || @project
- if current_user
diff --git a/danger/database/Dangerfile b/danger/database/Dangerfile
index f94184263ad..7b3a32358fe 100644
--- a/danger/database/Dangerfile
+++ b/danger/database/Dangerfile
@@ -30,6 +30,12 @@ If you no longer require a database review, you can remove this suggestion
by removing the ~database label and re-running the [`danger-review` job](#{ENV['CI_JOB_URL']}).
MSG
+DB_MIGRATION_TESTING_REQUIRED_MESSAGE = <<~MSG
+1. If this is not a ~"Community contribution" or from a Fork, kick off the
+ `db:gitlabcom-database-testing` manual job.
+
+MSG
+
DATABASE_APPROVED_LABEL = 'database::approved'
non_geo_db_schema_updated = !git.modified_files.grep(%r{\Adb/structure\.sql}).empty?
@@ -58,9 +64,10 @@ if helper.mr_labels.include?('database') || db_paths_to_review.any?
'review from the [Database team](https://gitlab.com/groups/gl-database/-/group_members).'
markdown(DB_MESSAGE)
+ markdown(DB_MIGRATION_TESTING_REQUIRED_MESSAGE) if non_geo_migration_created
if db_paths_to_review.any?
- markdown(DB_FILES_MESSAGE + helper.markdown_list(db_paths_to_review))
+ markdown(DB_FILES_MESSAGE + helper.markdown_list(db_paths_to_review.to_set))
else
markdown(DB_REMOVE_MESSAGE)
end
diff --git a/doc/api/deploy_keys.md b/doc/api/deploy_keys.md
index adeda014af0..40641c6e2f7 100644
--- a/doc/api/deploy_keys.md
+++ b/doc/api/deploy_keys.md
@@ -6,6 +6,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Deploy keys API **(FREE)**
+The deploy keys API can return in responses fingerprints of the public key in the following fields:
+
+- `fingerprint` (MD5 hash). Not available on FIPS-enabled systems.
+- `fingerprint_sha256` (SHA256 hash). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91302) in GitLab 15.2.
+
## List all deploy keys **(FREE SELF)**
Get a list of all deploy keys across all projects of the GitLab instance. This
@@ -34,8 +39,9 @@ Example response:
{
"id": 1,
"title": "Public key",
- "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
- "fingerprint": "7f:72:08:7d:0e:47:48:ec:37:79:b2:76:68:b5:87:65",
+ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDNJAkI3Wdf0r13c8a5pEExB2YowPWCSVzfZV22pNBc1CuEbyYLHpUyaD0GwpGvFdx2aP7lMEk35k6Rz3ccBF6jRaVJyhsn5VNnW92PMpBJ/P1UebhXwsFHdQf5rTt082cSxWuk61kGWRQtk4ozt/J2DF/dIUVaLvc+z4HomT41fQ==",
+ "fingerprint": "4a:9d:64:15:ed:3a:e6:07:6e:89:36:b3:3b:03:05:d9",
+ "fingerprint_sha256": "SHA256:Jrs3LD1Ji30xNLtTVf9NDCj7kkBgPBb2pjvTZ3HfIgU",
"created_at": "2013-10-02T10:12:29Z",
"projects_with_write_access": [
{
@@ -61,8 +67,9 @@ Example response:
{
"id": 3,
"title": "Another Public key",
- "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
- "fingerprint": "64:d3:73:d4:83:70:ab:41:96:68:d5:3d:a5:b0:34:ea",
+ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDIJFwIL6YNcCgVBLTHgM6hzmoL5vf0ThDKQMWT3HrwCjUCGPwR63vBwn6+/Gx+kx+VTo9FuojzR0O4XfwD3LrYA+oT3ETbn9U4e/VS4AH/G4SDMzgSLwu0YuPe517FfGWhWGQhjiXphkaQ+6bXPmcASWb0RCO5+pYlGIfxv4eFGQ=="
+ "fingerprint": "0b:cf:58:40:b9:23:96:c7:ba:44:df:0e:9e:87:5e:75",
+ "fingerprint_sha256": "SHA256:lGI/Ys/Wx7PfMhUO1iuBH92JQKYN+3mhJZvWO4Q5ims",
"created_at": "2013-10-02T11:12:29Z",
"projects_with_write_access": []
}
@@ -92,14 +99,18 @@ Example response:
{
"id": 1,
"title": "Public key",
- "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
+ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDNJAkI3Wdf0r13c8a5pEExB2YowPWCSVzfZV22pNBc1CuEbyYLHpUyaD0GwpGvFdx2aP7lMEk35k6Rz3ccBF6jRaVJyhsn5VNnW92PMpBJ/P1UebhXwsFHdQf5rTt082cSxWuk61kGWRQtk4ozt/J2DF/dIUVaLvc+z4HomT41fQ==",
+ "fingerprint": "4a:9d:64:15:ed:3a:e6:07:6e:89:36:b3:3b:03:05:d9",
+ "fingerprint_sha256": "SHA256:Jrs3LD1Ji30xNLtTVf9NDCj7kkBgPBb2pjvTZ3HfIgU",
"created_at": "2013-10-02T10:12:29Z",
"can_push": false
},
{
"id": 3,
"title": "Another Public key",
- "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
+ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDIJFwIL6YNcCgVBLTHgM6hzmoL5vf0ThDKQMWT3HrwCjUCGPwR63vBwn6+/Gx+kx+VTo9FuojzR0O4XfwD3LrYA+oT3ETbn9U4e/VS4AH/G4SDMzgSLwu0YuPe517FfGWhWGQhjiXphkaQ+6bXPmcASWb0RCO5+pYlGIfxv4eFGQ=="
+ "fingerprint": "0b:cf:58:40:b9:23:96:c7:ba:44:df:0e:9e:87:5e:75",
+ "fingerprint_sha256": "SHA256:lGI/Ys/Wx7PfMhUO1iuBH92JQKYN+3mhJZvWO4Q5ims",
"created_at": "2013-10-02T11:12:29Z",
"can_push": false
}
@@ -129,16 +140,18 @@ Parameters:
"title": "Key A",
"created_at": "2022-05-30T12:28:27.855Z",
"expires_at": null,
- "key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTEVaAtU5wiVducsOa01InRFf7QSTxoAm6Xy0PGv/k48M6xCALa9nY+BzlOv47jUT57 Key",
- "fingerprint": "60:8e:10:f0:6a:82:c8:29:5f:bf:c0:38:72:00:6f:8f"
+ "key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILkYXU2fVeO4/0rDCSsswP5iIX2+B6tv15YT3KObgyDl Key",
+ "fingerprint": "40:8e:fa:df:70:f7:a7:06:1e:0d:6f:ae:f2:27:92:01",
+ "fingerprint_sha256": "SHA256:Ojq2LZW43BFK/AMP81jBkDGn9YpPWYRNcViKBB44LPU"
},
{
"id": 2,
"title": "Key B",
"created_at": "2022-05-30T13:34:56.219Z",
"expires_at": null,
- "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
- "fingerprint": "75:33:44:7e:55:84:dd:70:29:a3:8e:a3:c0:b9:8b:65"
+ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDNJAkI3Wdf0r13c8a5pEExB2YowPWCSVzfZV22pNBc1CuEbyYLHpUyaD0GwpGvFdx2aP7lMEk35k6Rz3ccBF6jRaVJyhsn5VNnW92PMpBJ/P1UebhXwsFHdQf5rTt082cSxWuk61kGWRQtk4ozt/J2DF/dIUVaLvc+z4HomT41fQ==",
+ "fingerprint": "4a:9d:64:15:ed:3a:e6:07:6e:89:36:b3:3b:03:05:d9",
+ "fingerprint_sha256": "SHA256:Jrs3LD1Ji30xNLtTVf9NDCj7kkBgPBb2pjvTZ3HfIgU",
}
]
```
@@ -156,8 +169,9 @@ Example response:
"title": "Key A",
"created_at": "2022-05-30T12:28:27.855Z",
"expires_at": "2022-10-30T12:28:27.855Z",
- "key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTEVaAtU5wiVducsOa01InRFf7QSTxoAm6Xy0PGv/k48M6xCALa9nY+BzlOv47jUT57 Key",
- "fingerprint": "60:8e:10:f0:6a:82:c8:29:5f:bf:c0:38:72:00:6f:8f"
+ "key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILkYXU2fVeO4/0rDCSsswP5iIX2+B6tv15YT3KObgyDl Key",
+ "fingerprint": "40:8e:fa:df:70:f7:a7:06:1e:0d:6f:ae:f2:27:92:01",
+ "fingerprint_sha256": "SHA256:Ojq2LZW43BFK/AMP81jBkDGn9YpPWYRNcViKBB44LPU"
}
]
```
@@ -187,7 +201,9 @@ Example response:
{
"id": 1,
"title": "Public key",
- "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
+ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDNJAkI3Wdf0r13c8a5pEExB2YowPWCSVzfZV22pNBc1CuEbyYLHpUyaD0GwpGvFdx2aP7lMEk35k6Rz3ccBF6jRaVJyhsn5VNnW92PMpBJ/P1UebhXwsFHdQf5rTt082cSxWuk61kGWRQtk4ozt/J2DF/dIUVaLvc+z4HomT41fQ==",
+ "fingerprint": "4a:9d:64:15:ed:3a:e6:07:6e:89:36:b3:3b:03:05:d9",
+ "fingerprint_sha256": "SHA256:Jrs3LD1Ji30xNLtTVf9NDCj7kkBgPBb2pjvTZ3HfIgU",
"created_at": "2013-10-02T10:12:29Z",
"can_push": false
}
diff --git a/doc/api/dora/metrics.md b/doc/api/dora/metrics.md
index 27d005c0f9c..dfad6210d8d 100644
--- a/doc/api/dora/metrics.md
+++ b/doc/api/dora/metrics.md
@@ -21,15 +21,15 @@ Get project-level DORA metrics.
GET /projects/:id/dora/metrics
```
-| Attribute | Type | Required | Description |
-|----------------------|------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../index.md#namespaced-path-encoding) can be accessed by the authenticated user. |
-| `metric` | string | yes | The metric name: `deployment_frequency`, `lead_time_for_changes` or `time_to_restore_service`. |
-| `start_date` | string | no | Date range to start from. ISO 8601 Date format, for example `2021-03-01`. Default is 3 months ago. |
-| `end_date` | string | no | Date range to end at. ISO 8601 Date format, for example `2021-03-01`. Default is the current date. |
-| `interval` | string | no | The bucketing interval. One of `all`, `monthly` or `daily`. Default is `daily`. |
-| `environment_tier` | string | no | The [tier of the environment](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. Planned for deprecation, please use `environment_tiers`. |
-| `environment_tiers` | array of strings | no | The [tiers of the environments](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. |
+| Attribute | Type | Required | Description |
+|----------------------|------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../index.md#namespaced-path-encoding) can be accessed by the authenticated user. |
+| `metric` | string | yes | The metric name: `deployment_frequency`, `lead_time_for_changes` or `time_to_restore_service`. |
+| `start_date` | string | no | Date range to start from. ISO 8601 Date format, for example `2021-03-01`. Default is 3 months ago. |
+| `end_date` | string | no | Date range to end at. ISO 8601 Date format, for example `2021-03-01`. Default is the current date. |
+| `interval` | string | no | The bucketing interval. One of `all`, `monthly` or `daily`. Default is `daily`. |
+| `environment_tier` | string | no | The [tier of the environment](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. Deprecated, please use `environment_tiers`. |
+| `environment_tiers` | array of strings | no | The [tiers of the environments](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. |
Example request:
@@ -62,15 +62,15 @@ Get group-level DORA metrics.
GET /groups/:id/dora/metrics
```
-| Attribute | Type | Required | Description |
-|---------------------|------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../index.md#namespaced-path-encoding) can be accessed by the authenticated user. |
-| `metric` | string | yes | One of `deployment_frequency`, `lead_time_for_changes`, `time_to_restore_service` or `change_failure_rate`. |
-| `start_date` | string | no | Date range to start from. ISO 8601 Date format, for example `2021-03-01`. Default is 3 months ago. |
-| `end_date` | string | no | Date range to end at. ISO 8601 Date format, for example `2021-03-01`. Default is the current date. |
-| `interval` | string | no | The bucketing interval. One of `all`, `monthly` or `daily`. Default is `daily`. |
-| `environment_tier` | string | no | The [tier of the environment](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. Planned for deprecation, please use `environment_tiers`. |
-| `environment_tiers` | array of strings | no | The [tiers of the environments](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. |
+| Attribute | Type | Required | Description |
+|---------------------|------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../index.md#namespaced-path-encoding) can be accessed by the authenticated user. |
+| `metric` | string | yes | One of `deployment_frequency`, `lead_time_for_changes`, `time_to_restore_service` or `change_failure_rate`. |
+| `start_date` | string | no | Date range to start from. ISO 8601 Date format, for example `2021-03-01`. Default is 3 months ago. |
+| `end_date` | string | no | Date range to end at. ISO 8601 Date format, for example `2021-03-01`. Default is the current date. |
+| `interval` | string | no | The bucketing interval. One of `all`, `monthly` or `daily`. Default is `daily`. |
+| `environment_tier` | string | no | The [tier of the environment](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. Deprecated, please use `environment_tiers`. |
+| `environment_tiers` | array of strings | no | The [tiers of the environments](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. |
Example request:
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 5b38f524ede..9b13c8303b0 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -11074,7 +11074,7 @@ Returns [`[DoraMetric!]`](#dorametric).
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="dorametricsenddate"></a>`endDate` | [`Date`](#date) | Date range to end at. Default is the current date. |
-| <a id="dorametricsenvironmenttier"></a>`environmentTier` | [`DeploymentTier`](#deploymenttier) | Deployment tier of the environments to return. Planned for deprecation: please update to `environment_tiers` param. |
+| <a id="dorametricsenvironmenttier"></a>`environmentTier` | [`DeploymentTier`](#deploymenttier) | Deployment tier of the environments to return. Deprecated, please update to `environment_tiers` param. |
| <a id="dorametricsenvironmenttiers"></a>`environmentTiers` | [`[DeploymentTier!]`](#deploymenttier) | Deployment tiers of the environments to return. Defaults to [`PRODUCTION`]. |
| <a id="dorametricsinterval"></a>`interval` | [`DoraMetricBucketingInterval`](#dorametricbucketinginterval) | How the metric should be aggregrated. Defaults to `DAILY`. In the case of `ALL`, the `date` field in the response will be `null`. |
| <a id="dorametricsmetric"></a>`metric` | [`DoraMetricType!`](#dorametrictype) | Type of metric to return. |
diff --git a/doc/integration/oauth_provider.md b/doc/integration/oauth_provider.md
index 4031f3a52e7..962f5c4e5fb 100644
--- a/doc/integration/oauth_provider.md
+++ b/doc/integration/oauth_provider.md
@@ -96,8 +96,8 @@ The ability to opt-out of expiring access tokens was [deprecated](https://gitlab
in GitLab 14.3 and [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/340848) in 15.0. All
existing integrations must be updated to support access token refresh.
-Access tokens expire in two hours which means that integrations that use them must support generating new access
-tokens at least every two hours.
+Access tokens expire after two hours. Integrations that use access tokens must generate new ones at least every
+two hours.
When applications are deleted, all grants and tokens associated with the application are also deleted.
diff --git a/lib/api/entities/deploy_key.rb b/lib/api/entities/deploy_key.rb
index e8537c4c677..2c9c33549a1 100644
--- a/lib/api/entities/deploy_key.rb
+++ b/lib/api/entities/deploy_key.rb
@@ -4,7 +4,8 @@ module API
module Entities
class DeployKey < Entities::SSHKey
expose :key
- expose :fingerprint
+ expose :fingerprint, if: ->(key, _) { key.fingerprint.present? }
+ expose :fingerprint_sha256
expose :projects_with_write_access, using: Entities::ProjectIdentity, if: -> (_, options) { options[:include_projects_with_write_access] }
end
diff --git a/lib/gitlab/ci/build/rules/rule/clause/changes.rb b/lib/gitlab/ci/build/rules/rule/clause/changes.rb
index 4c5f02b4f7b..6eba3160249 100644
--- a/lib/gitlab/ci/build/rules/rule/clause/changes.rb
+++ b/lib/gitlab/ci/build/rules/rule/clause/changes.rb
@@ -4,8 +4,10 @@ module Gitlab
module Ci
module Build
class Rules::Rule::Clause::Changes < Rules::Rule::Clause
+ include Gitlab::Utils::StrongMemoize
+
def initialize(globs)
- @globs = Array(globs)
+ @globs = globs
end
def satisfied_by?(pipeline, context)
@@ -19,13 +21,21 @@ module Gitlab
end
end
+ private
+
def expand_globs(context)
- return @globs unless context
+ return paths unless context
- @globs.map do |glob|
+ paths.map do |glob|
ExpandVariables.expand_existing(glob, -> { context.variables_hash })
end
end
+
+ def paths
+ strong_memoize(:paths) do
+ Array(@globs[:paths])
+ end
+ end
end
end
end
diff --git a/lib/gitlab/ci/config/entry/rules/rule/changes.rb b/lib/gitlab/ci/config/entry/rules/rule/changes.rb
index be57e089f34..a56b928450a 100644
--- a/lib/gitlab/ci/config/entry/rules/rule/changes.rb
+++ b/lib/gitlab/ci/config/entry/rules/rule/changes.rb
@@ -6,13 +6,51 @@ module Gitlab
module Entry
class Rules
class Rule
- class Changes < ::Gitlab::Config::Entry::Node
- include ::Gitlab::Config::Entry::Validatable
+ class Changes < ::Gitlab::Config::Entry::Simplifiable
+ strategy :SimpleChanges, if: -> (config) { config.is_a?(Array) }
+ strategy :ComplexChanges, if: -> (config) { config.is_a?(Hash) }
- validations do
- validates :config,
- array_of_strings: true,
- length: { maximum: 50, too_long: "has too many entries (maximum %{count})" }
+ class SimpleChanges < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Validatable
+
+ validations do
+ validates :config,
+ array_of_strings: true,
+ length: { maximum: 50, too_long: "has too many entries (maximum %{count})" }
+ end
+
+ def value
+ {
+ paths: config
+ }.compact
+ end
+ end
+
+ class ComplexChanges < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Validatable
+ include ::Gitlab::Config::Entry::Attributable
+
+ ALLOWED_KEYS = %i[paths].freeze
+ REQUIRED_KEYS = %i[paths].freeze
+
+ attributes ALLOWED_KEYS
+
+ validations do
+ validates :config, allowed_keys: ALLOWED_KEYS
+ validates :config, required_keys: REQUIRED_KEYS
+
+ with_options allow_nil: false do
+ validates :paths,
+ array_of_strings: true,
+ length: { maximum: 50, too_long: "has too many entries (maximum %{count})" }
+ end
+ end
+ end
+
+ class UnknownStrategy < ::Gitlab::Config::Entry::Node
+ def errors
+ ["#{location} should be an array or a hash"]
+ end
end
end
end
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index 31390b110e7..58443b66858 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -499,8 +499,6 @@ RSpec.describe 'Group' do
let_it_be_with_refind(:user) { create(:user) }
before do
- stub_feature_flags(namespace_storage_limit_bypass_date_check: false)
-
group.add_owner(user)
sign_in(user)
end
@@ -509,8 +507,8 @@ RSpec.describe 'Group' do
let_it_be(:storage_enforcement_date) { Date.today + 30 }
before do
- allow_next_found_instance_of(Group) do |g|
- allow(g).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
+ allow_next_found_instance_of(Group) do |grp|
+ allow(grp).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
end
end
@@ -520,8 +518,8 @@ RSpec.describe 'Group' do
end
it 'does not display the banner in a paid group page' do
- allow_next_found_instance_of(Group) do |g|
- allow(g).to receive(:paid?).and_return(true)
+ allow_next_found_instance_of(Group) do |grp|
+ allow(grp).to receive(:paid?).and_return(true)
end
visit group_path(group)
expect_page_not_to_have_storage_enforcement_banner
@@ -531,12 +529,13 @@ RSpec.describe 'Group' do
visit group_path(group)
expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
find('.js-storage-enforcement-banner [data-testid="close-icon"]').click
+ wait_for_requests
page.refresh
expect_page_not_to_have_storage_enforcement_banner
storage_enforcement_date = Date.today + 13
- allow_next_found_instance_of(Group) do |g|
- allow(g).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
+ allow_next_found_instance_of(Group) do |grp|
+ allow(grp).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
end
page.refresh
expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
@@ -547,6 +546,7 @@ RSpec.describe 'Group' do
# This test should break and be rewritten after the implementation of the storage_enforcement_date
# TBD: https://gitlab.com/gitlab-org/gitlab/-/issues/350632
it 'does not display the banner in the group page' do
+ stub_feature_flags(namespace_storage_limit_bypass_date_check: false)
visit group_path(group)
expect_page_not_to_have_storage_enforcement_banner
end
diff --git a/spec/features/merge_request/user_awards_emoji_spec.rb b/spec/features/merge_request/user_awards_emoji_spec.rb
index 81a88cad458..6fdc1a29174 100644
--- a/spec/features/merge_request/user_awards_emoji_spec.rb
+++ b/spec/features/merge_request/user_awards_emoji_spec.rb
@@ -11,27 +11,35 @@ RSpec.describe 'Merge request > User awards emoji', :js do
describe 'logged in' do
before do
sign_in(user)
- visit project_merge_request_path(project, merge_request)
+ visit project_merge_request_path(project, merge_request)
wait_for_requests
end
it 'adds award to merge request' do
first('[data-testid="award-button"]').click
+ wait_for_requests
expect(page).to have_selector('[data-testid="award-button"].selected')
expect(first('[data-testid="award-button"]')).to have_content '1'
visit project_merge_request_path(project, merge_request)
+ wait_for_requests
+
expect(first('[data-testid="award-button"]')).to have_content '1'
end
it 'removes award from merge request' do
first('[data-testid="award-button"]').click
+ wait_for_requests
expect(first('[data-testid="award-button"]')).to have_content '1'
+
find('[data-testid="award-button"].selected').click
+ wait_for_requests
expect(first('[data-testid="award-button"]')).to have_content '0'
visit project_merge_request_path(project, merge_request)
+ wait_for_requests
+
expect(first('[data-testid="award-button"]')).to have_content '0'
end
@@ -62,6 +70,7 @@ RSpec.describe 'Merge request > User awards emoji', :js do
describe 'logged out' do
before do
visit project_merge_request_path(project, merge_request)
+ wait_for_requests
end
it 'does not see award menu button' do
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index db64f84aa76..f6f9c7f0d3c 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -440,6 +440,99 @@ RSpec.describe 'Project' do
end
end
+ describe 'storage_enforcement_banner', :js do
+ let_it_be(:group) { create(:group) }
+ let_it_be_with_refind(:user) { create(:user) }
+ let_it_be(:project) { create(:project, group: group) }
+
+ before do
+ group.add_maintainer(user)
+ sign_in(user)
+ end
+
+ context 'with storage_enforcement_date set' do
+ let_it_be(:storage_enforcement_date) { Date.today + 30 }
+
+ before do
+ allow_next_found_instance_of(Group) do |grp|
+ allow(grp).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
+ end
+ end
+
+ it 'displays the banner in the project page' do
+ visit project_path(project)
+ expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
+ end
+
+ context 'when in a subgroup project page' do
+ let_it_be(:subgroup) { create(:group, parent: group) }
+ let_it_be(:project) { create(:project, namespace: subgroup) }
+
+ it 'displays the banner' do
+ visit project_path(project)
+ expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
+ end
+ end
+
+ context 'when in a user namespace project page' do
+ let_it_be(:project) { create(:project, namespace: user.namespace) }
+
+ before do
+ allow_next_found_instance_of(Namespaces::UserNamespace) do |namspace|
+ allow(namspace).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
+ end
+ end
+
+ it 'displays the banner' do
+ visit project_path(project)
+ expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
+ end
+ end
+
+ it 'does not display the banner in a paid group project page' do
+ allow_next_found_instance_of(Group) do |grp|
+ allow(grp).to receive(:paid?).and_return(true)
+ end
+ visit project_path(project)
+ expect_page_not_to_have_storage_enforcement_banner
+ end
+
+ it 'does not display the banner if user has previously closed unless threshold has changed' do
+ visit project_path(project)
+ expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
+ find('.js-storage-enforcement-banner [data-testid="close-icon"]').click
+ wait_for_requests
+ page.refresh
+ expect_page_not_to_have_storage_enforcement_banner
+
+ storage_enforcement_date = Date.today + 13
+ allow_next_found_instance_of(Group) do |grp|
+ allow(grp).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
+ end
+ page.refresh
+ expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
+ end
+ end
+
+ context 'with storage_enforcement_date not set' do
+ # This test should break and be rewritten after the implementation of the storage_enforcement_date
+ # TBD: https://gitlab.com/gitlab-org/gitlab/-/issues/350632
+ it 'does not display the banner in the group page' do
+ stub_feature_flags(namespace_storage_limit_bypass_date_check: false)
+ visit project_path(project)
+ expect_page_not_to_have_storage_enforcement_banner
+ end
+ end
+ end
+
+ def expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
+ expect(page).to have_text "From #{storage_enforcement_date} storage limits will apply to this namespace"
+ end
+
+ def expect_page_not_to_have_storage_enforcement_banner
+ expect(page).not_to have_text "storage limits will apply to this namespace"
+ end
+
def remove_with_confirm(button_text, confirm_with, confirm_button_text = 'Confirm')
click_button button_text
fill_in 'confirm_name_input', with: confirm_with
diff --git a/spec/fixtures/api/schemas/public_api/v4/deploy_key.json b/spec/fixtures/api/schemas/public_api/v4/deploy_key.json
index 3dbdfcc95a1..99e57a4c218 100644
--- a/spec/fixtures/api/schemas/public_api/v4/deploy_key.json
+++ b/spec/fixtures/api/schemas/public_api/v4/deploy_key.json
@@ -6,7 +6,7 @@
"created_at",
"expires_at",
"key",
- "fingerprint",
+ "fingerprint_sha256",
"projects_with_write_access"
],
"properties": {
@@ -16,6 +16,7 @@
"expires_at": { "type": ["string", "null"], "format": "date-time" },
"key": { "type": "string" },
"fingerprint": { "type": "string" },
+ "fingerprint_sha256": { "type": "string" },
"projects_with_write_access": {
"type": "array",
"items": { "$ref": "project/identity.json" }
diff --git a/spec/lib/api/entities/deploy_key_spec.rb b/spec/lib/api/entities/deploy_key_spec.rb
index 6427d6eac8f..50a27418488 100644
--- a/spec/lib/api/entities/deploy_key_spec.rb
+++ b/spec/lib/api/entities/deploy_key_spec.rb
@@ -15,8 +15,15 @@ RSpec.describe API::Entities::DeployKey do
title: deploy_key.title,
created_at: deploy_key.created_at,
expires_at: deploy_key.expires_at,
- key: deploy_key.key
+ key: deploy_key.key,
+ fingerprint_sha256: deploy_key.fingerprint_sha256
)
+
+ is_expected.to include(fingerprint: deploy_key.fingerprint) unless Gitlab::FIPS.enabled?
+ end
+
+ context 'when in FIPS mode', :fips_mode do
+ it { is_expected.not_to have_key(:fingerprint) }
end
end
end
diff --git a/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb b/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb
index 4ac8bf61738..da9d3fab869 100644
--- a/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb
+++ b/spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb
@@ -6,19 +6,41 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do
describe '#satisfied_by?' do
subject { described_class.new(globs).satisfied_by?(pipeline, context) }
- it_behaves_like 'a glob matching rule' do
+ context 'a glob matching rule' do
+ using RSpec::Parameterized::TableSyntax
+
let(:pipeline) { build(:ci_pipeline) }
let(:context) {}
before do
allow(pipeline).to receive(:modified_paths).and_return(files.keys)
end
+
+ # rubocop:disable Layout/LineLength
+ where(:case_name, :globs, :files, :satisfied) do
+ 'exact top-level match' | { paths: ['Dockerfile'] } | { 'Dockerfile' => '', 'Gemfile' => '' } | true
+ 'exact top-level no match' | { paths: ['Dockerfile'] } | { 'Gemfile' => '' } | false
+ 'pattern top-level match' | { paths: ['Docker*'] } | { 'Dockerfile' => '', 'Gemfile' => '' } | true
+ 'pattern top-level no match' | { paths: ['Docker*'] } | { 'Gemfile' => '' } | false
+ 'exact nested match' | { paths: ['project/build.properties'] } | { 'project/build.properties' => '' } | true
+ 'exact nested no match' | { paths: ['project/build.properties'] } | { 'project/README.md' => '' } | false
+ 'pattern nested match' | { paths: ['src/**/*.go'] } | { 'src/gitlab.com/goproject/goproject.go' => '' } | true
+ 'pattern nested no match' | { paths: ['src/**/*.go'] } | { 'src/gitlab.com/goproject/README.md' => '' } | false
+ 'ext top-level match' | { paths: ['*.go'] } | { 'main.go' => '', 'cmd/goproject/main.go' => '' } | true
+ 'ext nested no match' | { paths: ['*.go'] } | { 'cmd/goproject/main.go' => '' } | false
+ 'ext slash no match' | { paths: ['/*.go'] } | { 'main.go' => '', 'cmd/goproject/main.go' => '' } | false
+ end
+ # rubocop:enable Layout/LineLength
+
+ with_them do
+ it { is_expected.to eq(satisfied) }
+ end
end
context 'when pipeline is nil' do
let(:pipeline) {}
let(:context) {}
- let(:globs) { [] }
+ let(:globs) { { paths: [] } }
it { is_expected.to be_truthy }
end
@@ -26,8 +48,8 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do
context 'when using variable expansion' do
let(:pipeline) { build(:ci_pipeline) }
let(:modified_paths) { ['helm/test.txt'] }
- let(:globs) { ['$HELM_DIR/**/*'] }
- let(:context) { double('context') }
+ let(:globs) { { paths: ['$HELM_DIR/**/*'] } }
+ let(:context) { instance_double(Gitlab::Ci::Build::Context::Base) }
before do
allow(pipeline).to receive(:modified_paths).and_return(modified_paths)
@@ -58,7 +80,7 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Changes do
end
context 'when variable expansion does not match' do
- let(:globs) { ['path/with/$in/it/*'] }
+ let(:globs) { { paths: ['path/with/$in/it/*'] } }
let(:modified_paths) { ['path/with/$in/it/file.txt'] }
before do
diff --git a/spec/lib/gitlab/ci/build/rules/rule_spec.rb b/spec/lib/gitlab/ci/build/rules/rule_spec.rb
index f905e229415..ac73b665f3a 100644
--- a/spec/lib/gitlab/ci/build/rules/rule_spec.rb
+++ b/spec/lib/gitlab/ci/build/rules/rule_spec.rb
@@ -14,10 +14,14 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule do
let(:ci_build) { build(:ci_build, pipeline: pipeline) }
let(:rule) { described_class.new(rule_hash) }
+ before do
+ allow(pipeline).to receive(:modified_paths).and_return(['file.rb'])
+ end
+
describe '#matches?' do
subject { rule.matches?(pipeline, seed) }
- context 'with one matching clause' do
+ context 'with one matching clause if' do
let(:rule_hash) do
{ if: '$VAR == null', when: 'always' }
end
@@ -25,9 +29,17 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule do
it { is_expected.to eq(true) }
end
+ context 'with one matching clause changes' do
+ let(:rule_hash) do
+ { changes: { paths: ['**/*'] }, when: 'always' }
+ end
+
+ it { is_expected.to eq(true) }
+ end
+
context 'with two matching clauses' do
let(:rule_hash) do
- { if: '$VAR == null', changes: '**/*', when: 'always' }
+ { if: '$VAR == null', changes: { paths: ['**/*'] }, when: 'always' }
end
it { is_expected.to eq(true) }
@@ -35,7 +47,7 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule do
context 'with a matching and non-matching clause' do
let(:rule_hash) do
- { if: '$VAR != null', changes: '$VAR == null', when: 'always' }
+ { if: '$VAR != null', changes: { paths: ['invalid.xyz'] }, when: 'always' }
end
it { is_expected.to eq(false) }
@@ -43,7 +55,7 @@ RSpec.describe Gitlab::Ci::Build::Rules::Rule do
context 'with two non-matching clauses' do
let(:rule_hash) do
- { if: '$VAR != null', changes: 'README', when: 'always' }
+ { if: '$VAR != null', changes: { paths: ['README'] }, when: 'always' }
end
it { is_expected.to eq(false) }
diff --git a/spec/lib/gitlab/ci/config/entry/rules/rule/changes_spec.rb b/spec/lib/gitlab/ci/config/entry/rules/rule/changes_spec.rb
index 3ed4a9f263f..295561b3c4d 100644
--- a/spec/lib/gitlab/ci/config/entry/rules/rule/changes_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/rules/rule/changes_spec.rb
@@ -37,7 +37,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule::Changes do
it { is_expected.not_to be_valid }
it 'reports an error about invalid policy' do
- expect(entry.errors).to include(/should be an array of strings/)
+ expect(entry.errors).to include(/should be an array or a hash/)
end
end
@@ -64,7 +64,59 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule::Changes do
it 'returns information about errors' do
expect(entry.errors)
- .to include(/should be an array of strings/)
+ .to include(/should be an array or a hash/)
+ end
+ end
+
+ context 'with paths' do
+ context 'when paths is an array of strings' do
+ let(:config) { { paths: %w[app/ lib/] } }
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'when paths is not an array' do
+ let(:config) { { paths: 'string' } }
+
+ it { is_expected.not_to be_valid }
+
+ it 'returns information about errors' do
+ expect(entry.errors)
+ .to include(/should be an array of strings/)
+ end
+ end
+
+ context 'when paths is an array of integers' do
+ let(:config) { { paths: [1, 2] } }
+
+ it { is_expected.not_to be_valid }
+
+ it 'returns information about errors' do
+ expect(entry.errors)
+ .to include(/should be an array of strings/)
+ end
+ end
+
+ context 'when paths is an array of long strings' do
+ let(:config) { { paths: ['a'] * 51 } }
+
+ it { is_expected.not_to be_valid }
+
+ it 'returns information about errors' do
+ expect(entry.errors)
+ .to include(/has too many entries \(maximum 50\)/)
+ end
+ end
+
+ context 'when paths is nil' do
+ let(:config) { { paths: nil } }
+
+ it { is_expected.not_to be_valid }
+
+ it 'returns information about errors' do
+ expect(entry.errors)
+ .to include(/should be an array of strings/)
+ end
end
end
end
@@ -75,6 +127,14 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule::Changes do
context 'when using a string array' do
let(:config) { %w[app/ lib/ spec/ other/* paths/**/*.rb] }
+ it { is_expected.to eq(paths: config) }
+ end
+
+ context 'with paths' do
+ let(:config) do
+ { paths: ['app/', 'lib/'] }
+ end
+
it { is_expected.to eq(config) }
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
index 89d349efe8f..93f4a66bfb6 100644
--- a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
@@ -115,7 +115,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule do
it { is_expected.not_to be_valid }
it 'reports an error about invalid policy' do
- expect(subject.errors).to include(/should be an array of strings/)
+ expect(subject.errors).to include(/should be an array or a hash/)
end
end
@@ -411,7 +411,13 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule do
context 'when using a changes: clause' do
let(:config) { { changes: %w[app/ lib/ spec/ other/* paths/**/*.rb] } }
- it { is_expected.to eq(config) }
+ it { is_expected.to eq(changes: { paths: %w[app/ lib/ spec/ other/* paths/**/*.rb] }) }
+
+ context 'when using changes with paths' do
+ let(:config) { { changes: { paths: %w[app/ lib/ spec/ other/* paths/**/*.rb] } } }
+
+ it { is_expected.to eq(config) }
+ end
end
context 'when default value has been provided' do
@@ -426,7 +432,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule do
end
it 'does not add to provided configuration' do
- expect(entry.value).to eq(config)
+ expect(entry.value).to eq(changes: { paths: %w[app/**/*.rb] })
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
index 49505d397c2..040f3ab5830 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
@@ -858,14 +858,14 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
context 'with an explicit `when: never`' do
where(:rule_set) do
[
- [[{ changes: %w[*/**/*.rb], when: 'never' }, { changes: %w[*/**/*.rb], when: 'always' }]],
- [[{ changes: %w[app/models/ci/pipeline.rb], when: 'never' }, { changes: %w[app/models/ci/pipeline.rb], when: 'always' }]],
- [[{ changes: %w[spec/**/*.rb], when: 'never' }, { changes: %w[spec/**/*.rb], when: 'always' }]],
- [[{ changes: %w[*.yml], when: 'never' }, { changes: %w[*.yml], when: 'always' }]],
- [[{ changes: %w[.*.yml], when: 'never' }, { changes: %w[.*.yml], when: 'always' }]],
- [[{ changes: %w[**/*], when: 'never' }, { changes: %w[**/*], when: 'always' }]],
- [[{ changes: %w[*/**/*.rb *.yml], when: 'never' }, { changes: %w[*/**/*.rb *.yml], when: 'always' }]],
- [[{ changes: %w[.*.yml **/*], when: 'never' }, { changes: %w[.*.yml **/*], when: 'always' }]]
+ [[{ changes: { paths: %w[*/**/*.rb] }, when: 'never' }, { changes: { paths: %w[*/**/*.rb] }, when: 'always' }]],
+ [[{ changes: { paths: %w[app/models/ci/pipeline.rb] }, when: 'never' }, { changes: { paths: %w[app/models/ci/pipeline.rb] }, when: 'always' }]],
+ [[{ changes: { paths: %w[spec/**/*.rb] }, when: 'never' }, { changes: { paths: %w[spec/**/*.rb] }, when: 'always' }]],
+ [[{ changes: { paths: %w[*.yml] }, when: 'never' }, { changes: { paths: %w[*.yml] }, when: 'always' }]],
+ [[{ changes: { paths: %w[.*.yml] }, when: 'never' }, { changes: { paths: %w[.*.yml] }, when: 'always' }]],
+ [[{ changes: { paths: %w[**/*] }, when: 'never' }, { changes: { paths: %w[**/*] }, when: 'always' }]],
+ [[{ changes: { paths: %w[*/**/*.rb *.yml] }, when: 'never' }, { changes: { paths: %w[*/**/*.rb *.yml] }, when: 'always' }]],
+ [[{ changes: { paths: %w[.*.yml **/*] }, when: 'never' }, { changes: { paths: %w[.*.yml **/*] }, when: 'always' }]]
]
end
@@ -881,14 +881,14 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
context 'with an explicit `when: always`' do
where(:rule_set) do
[
- [[{ changes: %w[*/**/*.rb], when: 'always' }, { changes: %w[*/**/*.rb], when: 'never' }]],
- [[{ changes: %w[app/models/ci/pipeline.rb], when: 'always' }, { changes: %w[app/models/ci/pipeline.rb], when: 'never' }]],
- [[{ changes: %w[spec/**/*.rb], when: 'always' }, { changes: %w[spec/**/*.rb], when: 'never' }]],
- [[{ changes: %w[*.yml], when: 'always' }, { changes: %w[*.yml], when: 'never' }]],
- [[{ changes: %w[.*.yml], when: 'always' }, { changes: %w[.*.yml], when: 'never' }]],
- [[{ changes: %w[**/*], when: 'always' }, { changes: %w[**/*], when: 'never' }]],
- [[{ changes: %w[*/**/*.rb *.yml], when: 'always' }, { changes: %w[*/**/*.rb *.yml], when: 'never' }]],
- [[{ changes: %w[.*.yml **/*], when: 'always' }, { changes: %w[.*.yml **/*], when: 'never' }]]
+ [[{ changes: { paths: %w[*/**/*.rb] }, when: 'always' }, { changes: { paths: %w[*/**/*.rb] }, when: 'never' }]],
+ [[{ changes: { paths: %w[app/models/ci/pipeline.rb] }, when: 'always' }, { changes: { paths: %w[app/models/ci/pipeline.rb] }, when: 'never' }]],
+ [[{ changes: { paths: %w[spec/**/*.rb] }, when: 'always' }, { changes: { paths: %w[spec/**/*.rb] }, when: 'never' }]],
+ [[{ changes: { paths: %w[*.yml] }, when: 'always' }, { changes: { paths: %w[*.yml] }, when: 'never' }]],
+ [[{ changes: { paths: %w[.*.yml] }, when: 'always' }, { changes: { paths: %w[.*.yml] }, when: 'never' }]],
+ [[{ changes: { paths: %w[**/*] }, when: 'always' }, { changes: { paths: %w[**/*] }, when: 'never' }]],
+ [[{ changes: { paths: %w[*/**/*.rb *.yml] }, when: 'always' }, { changes: { paths: %w[*/**/*.rb *.yml] }, when: 'never' }]],
+ [[{ changes: { paths: %w[.*.yml **/*] }, when: 'always' }, { changes: { paths: %w[.*.yml **/*] }, when: 'never' }]]
]
end
@@ -904,14 +904,14 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
context 'without an explicit when: value' do
where(:rule_set) do
[
- [[{ changes: %w[*/**/*.rb] }]],
- [[{ changes: %w[app/models/ci/pipeline.rb] }]],
- [[{ changes: %w[spec/**/*.rb] }]],
- [[{ changes: %w[*.yml] }]],
- [[{ changes: %w[.*.yml] }]],
- [[{ changes: %w[**/*] }]],
- [[{ changes: %w[*/**/*.rb *.yml] }]],
- [[{ changes: %w[.*.yml **/*] }]]
+ [[{ changes: { paths: %w[*/**/*.rb] } }]],
+ [[{ changes: { paths: %w[app/models/ci/pipeline.rb] } }]],
+ [[{ changes: { paths: %w[spec/**/*.rb] } }]],
+ [[{ changes: { paths: %w[*.yml] } }]],
+ [[{ changes: { paths: %w[.*.yml] } }]],
+ [[{ changes: { paths: %w[**/*] } }]],
+ [[{ changes: { paths: %w[*/**/*.rb *.yml] } }]],
+ [[{ changes: { paths: %w[.*.yml **/*] } }]]
]
end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 15567b36673..22bc6b0db59 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -70,7 +70,7 @@ module Gitlab
options: { script: ['rspec'] },
rules: [
{ if: '$CI_COMMIT_REF_NAME == "master"' },
- { changes: %w[README.md] }
+ { changes: { paths: %w[README.md] } }
],
allow_failure: false,
when: 'on_success',
@@ -2893,6 +2893,51 @@ module Gitlab
end
end
+ describe 'Rules' do
+ context 'changes' do
+ let(:config) do
+ <<~YAML
+ rspec:
+ script: exit 0
+ rules:
+ - changes: [README.md]
+ YAML
+ end
+
+ it 'returns builds with correct rules' do
+ expect(processor.builds.size).to eq(1)
+ expect(processor.builds[0]).to match(
+ hash_including(
+ name: "rspec",
+ rules: [{ changes: { paths: ["README.md"] } }]
+ )
+ )
+ end
+
+ context 'with paths' do
+ let(:config) do
+ <<~YAML
+ rspec:
+ script: exit 0
+ rules:
+ - changes:
+ paths: [README.md]
+ YAML
+ end
+
+ it 'returns builds with correct rules' do
+ expect(processor.builds.size).to eq(1)
+ expect(processor.builds[0]).to match(
+ hash_including(
+ name: "rspec",
+ rules: [{ changes: { paths: ["README.md"] } }]
+ )
+ )
+ end
+ end
+ end
+ end
+
describe '#execute' do
subject { Gitlab::Ci::YamlProcessor.new(content).execute }
diff --git a/spec/requests/api/npm_project_packages_spec.rb b/spec/requests/api/npm_project_packages_spec.rb
index 7c3f1890095..62809b432af 100644
--- a/spec/requests/api/npm_project_packages_spec.rb
+++ b/spec/requests/api/npm_project_packages_spec.rb
@@ -30,6 +30,7 @@ RSpec.describe API::NpmProjectPackages do
end
describe 'GET /api/v4/projects/:id/packages/npm/*package_name/-/*file_name' do
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
let(:package_file) { package.package_files.first }
let(:headers) { {} }
@@ -61,18 +62,18 @@ RSpec.describe API::NpmProjectPackages do
let(:headers) { build_token_auth_header(token.token) }
it_behaves_like 'successfully downloads the file'
+ it_behaves_like 'a package tracking event', 'API::NpmPackages', 'pull_package'
end
context 'with job token' do
let(:headers) { build_token_auth_header(job.token) }
it_behaves_like 'successfully downloads the file'
+ it_behaves_like 'a package tracking event', 'API::NpmPackages', 'pull_package'
end
end
context 'a public project' do
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace } }
-
it_behaves_like 'successfully downloads the file'
it_behaves_like 'a package tracking event', 'API::NpmPackages', 'pull_package'
@@ -112,6 +113,15 @@ RSpec.describe API::NpmProjectPackages do
end
it_behaves_like 'a package file that requires auth'
+
+ context 'with a job token for a different user' do
+ let_it_be(:other_user) { create(:user) }
+ let_it_be_with_reload(:other_job) { create(:ci_build, :running, user: other_user) }
+
+ let(:headers) { build_token_auth_header(other_job.token) }
+
+ it_behaves_like 'successfully downloads the file'
+ end
end
end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index aac059f2104..9cef7f7dadb 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -2087,6 +2087,12 @@ RSpec.describe Ci::CreatePipelineService do
rules:
- changes:
- $CI_JOB_NAME*
+
+ changes-paths:
+ script: "I am using a new syntax!"
+ rules:
+ - changes:
+ paths: [README.md]
EOY
end
@@ -2098,8 +2104,9 @@ RSpec.describe Ci::CreatePipelineService do
it 'creates five jobs' do
expect(pipeline).to be_persisted
- expect(build_names)
- .to contain_exactly('regular-job', 'rules-job', 'delayed-job', 'negligible-job', 'README')
+ expect(build_names).to contain_exactly(
+ 'regular-job', 'rules-job', 'delayed-job', 'negligible-job', 'README', 'changes-paths'
+ )
end
it 'sets when: for all jobs' do
diff --git a/tooling/danger/project_helper.rb b/tooling/danger/project_helper.rb
index 64837c45862..264c9f34818 100644
--- a/tooling/danger/project_helper.rb
+++ b/tooling/danger/project_helper.rb
@@ -26,7 +26,7 @@ module Tooling
%r{\Adoc/.*(\.(md|png|gif|jpg|yml))\z} => :docs,
%r{\A(CONTRIBUTING|LICENSE|MAINTENANCE|PHILOSOPHY|PROCESS|README)(\.md)?\z} => :docs,
%r{\Adata/whats_new/} => :docs,
- %r{\Adb/docs/.yml\z} => :docs,
+ %r{\Adb/docs/.+\.yml\z} => :docs,
%r{\Adata/deprecations/} => :none,
%r{\Adata/removals/} => :none,
diff --git a/vendor/gems/devise-pbkdf2-encryptable/.gitlab-ci.yml b/vendor/gems/devise-pbkdf2-encryptable/.gitlab-ci.yml
new file mode 100644
index 00000000000..a2517953178
--- /dev/null
+++ b/vendor/gems/devise-pbkdf2-encryptable/.gitlab-ci.yml
@@ -0,0 +1,26 @@
+workflow:
+ rules:
+ - if: $CI_MERGE_REQUEST_ID
+
+.rspec:
+ cache:
+ key: devise-pbkdf2-encryptable
+ paths:
+ - vendor/gems/devise-pbkdf2-encryptable/vendor/ruby
+ before_script:
+ - cd vendor/gems/devise-pbkdf2-encryptable
+ - ruby -v # Print out ruby version for debugging
+ - gem install bundler --no-document # Bundler is not installed with the image
+ - bundle config set --local path 'vendor' # Install dependencies into ./vendor/ruby
+ - bundle config set with 'development'
+ - bundle install -j $(nproc)
+ script:
+ - bundle exec rspec
+
+rspec-2.7:
+ image: "ruby:2.7"
+ extends: .rspec
+
+rspec-3.0:
+ image: "ruby:3.0"
+ extends: .rspec
diff --git a/vendor/gems/devise-pbkdf2-encryptable/Gemfile b/vendor/gems/devise-pbkdf2-encryptable/Gemfile
new file mode 100644
index 00000000000..be173b205f7
--- /dev/null
+++ b/vendor/gems/devise-pbkdf2-encryptable/Gemfile
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+
+gemspec
diff --git a/vendor/gems/devise-pbkdf2-encryptable/Gemfile.lock b/vendor/gems/devise-pbkdf2-encryptable/Gemfile.lock
new file mode 100644
index 00000000000..3be824244ad
--- /dev/null
+++ b/vendor/gems/devise-pbkdf2-encryptable/Gemfile.lock
@@ -0,0 +1,104 @@
+PATH
+ remote: .
+ specs:
+ devise-pbkdf2-encryptable (0.0.0)
+ devise (~> 4.0)
+
+GEM
+ remote: https://rubygems.org/
+ specs:
+ actionpack (6.1.6)
+ actionview (= 6.1.6)
+ activesupport (= 6.1.6)
+ rack (~> 2.0, >= 2.0.9)
+ rack-test (>= 0.6.3)
+ rails-dom-testing (~> 2.0)
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
+ actionview (6.1.6)
+ activesupport (= 6.1.6)
+ builder (~> 3.1)
+ erubi (~> 1.4)
+ rails-dom-testing (~> 2.0)
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
+ activemodel (6.1.6)
+ activesupport (= 6.1.6)
+ activesupport (6.1.6)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (>= 1.6, < 2)
+ minitest (>= 5.1)
+ tzinfo (~> 2.0)
+ zeitwerk (~> 2.3)
+ bcrypt (3.1.18)
+ builder (3.2.4)
+ concurrent-ruby (1.1.10)
+ crass (1.0.6)
+ devise (4.8.1)
+ bcrypt (~> 3.0)
+ orm_adapter (~> 0.1)
+ railties (>= 4.1.0)
+ responders
+ warden (~> 1.2.3)
+ diff-lcs (1.5.0)
+ erubi (1.10.0)
+ i18n (1.10.0)
+ concurrent-ruby (~> 1.0)
+ loofah (2.18.0)
+ crass (~> 1.0.2)
+ nokogiri (>= 1.5.9)
+ method_source (1.0.0)
+ minitest (5.16.0)
+ nokogiri (1.13.6-arm64-darwin)
+ racc (~> 1.4)
+ nokogiri (1.13.6-x86_64-linux)
+ racc (~> 1.4)
+ orm_adapter (0.5.0)
+ racc (1.6.0)
+ rack (2.2.3.1)
+ rack-test (1.1.0)
+ rack (>= 1.0, < 3)
+ rails-dom-testing (2.0.3)
+ activesupport (>= 4.2.0)
+ nokogiri (>= 1.6)
+ rails-html-sanitizer (1.4.3)
+ loofah (~> 2.3)
+ railties (6.1.6)
+ actionpack (= 6.1.6)
+ activesupport (= 6.1.6)
+ method_source
+ rake (>= 12.2)
+ thor (~> 1.0)
+ rake (13.0.6)
+ responders (3.0.1)
+ actionpack (>= 5.0)
+ railties (>= 5.0)
+ rspec (3.10.0)
+ rspec-core (~> 3.10.0)
+ rspec-expectations (~> 3.10.0)
+ rspec-mocks (~> 3.10.0)
+ rspec-core (3.10.2)
+ rspec-support (~> 3.10.0)
+ rspec-expectations (3.10.2)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.10.0)
+ rspec-mocks (3.10.3)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.10.0)
+ rspec-support (3.10.3)
+ thor (1.2.1)
+ tzinfo (2.0.4)
+ concurrent-ruby (~> 1.0)
+ warden (1.2.9)
+ rack (>= 2.0.9)
+ zeitwerk (2.6.0)
+
+PLATFORMS
+ arm64-darwin-21
+ x86_64-linux
+
+DEPENDENCIES
+ activemodel (~> 6.1, < 8)
+ devise-pbkdf2-encryptable!
+ rspec (~> 3.10.0)
+
+BUNDLED WITH
+ 2.3.16
diff --git a/vendor/gems/devise-pbkdf2-encryptable/LICENSE b/vendor/gems/devise-pbkdf2-encryptable/LICENSE
new file mode 100644
index 00000000000..d5a25c59027
--- /dev/null
+++ b/vendor/gems/devise-pbkdf2-encryptable/LICENSE
@@ -0,0 +1,201 @@
+Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright 2012-2015 Plataformatec (opensource@plataformatec.com.br)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/vendor/gems/devise-pbkdf2-encryptable/devise-pbkdf2-encryptable.gemspec b/vendor/gems/devise-pbkdf2-encryptable/devise-pbkdf2-encryptable.gemspec
new file mode 100644
index 00000000000..e507633c0bf
--- /dev/null
+++ b/vendor/gems/devise-pbkdf2-encryptable/devise-pbkdf2-encryptable.gemspec
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+lib = File.expand_path('lib', __dir__)
+$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
+
+Gem::Specification.new do |spec|
+ spec.name = 'devise-pbkdf2-encryptable'
+ spec.authors = ['Drew Blessing']
+ spec.email = ['drew@gitlab.com']
+
+ spec.summary = 'Extension that allows Devise to use PBKDF2 password hashing'
+ spec.homepage = 'https://gitlab.com/gitlab-org/gitlab/-/tree/master/vendor/gems/devise-pbkdf2-encryptable'
+ spec.metadata = { 'source_code_uri' => 'https://gitlab.com/gitlab-org/gitlab/-/tree/master/vendor/gems/devise-pbkdf2-encryptable' }
+ spec.license = 'Apache-2.0'
+
+ spec.files = Dir['lib/**/*.rb']
+ spec.require_paths = ['lib']
+
+ spec.version = '0.0.0'
+
+ spec.add_runtime_dependency 'devise', '~> 4.0'
+
+ spec.add_development_dependency 'activemodel', '~> 6.1', '< 8'
+ spec.add_development_dependency 'rspec', '~> 3.10.0'
+end
diff --git a/vendor/gems/devise-pbkdf2-encryptable/lib/devise-pbkdf2-encryptable.rb b/vendor/gems/devise-pbkdf2-encryptable/lib/devise-pbkdf2-encryptable.rb
new file mode 100644
index 00000000000..4db9b82052f
--- /dev/null
+++ b/vendor/gems/devise-pbkdf2-encryptable/lib/devise-pbkdf2-encryptable.rb
@@ -0,0 +1 @@
+require "devise/pbkdf2_encryptable/encryptable"
diff --git a/vendor/gems/devise-pbkdf2-encryptable/lib/devise/pbkdf2_encryptable/encryptable.rb b/vendor/gems/devise-pbkdf2-encryptable/lib/devise/pbkdf2_encryptable/encryptable.rb
new file mode 100644
index 00000000000..027032f46c1
--- /dev/null
+++ b/vendor/gems/devise-pbkdf2-encryptable/lib/devise/pbkdf2_encryptable/encryptable.rb
@@ -0,0 +1,16 @@
+module Devise
+ # Used to define the password encryption algorithm.
+ mattr_accessor :encryptor
+ @@encryptor = nil
+
+ module Pbkdf2Encryptable
+ module Encryptors
+ InvalidHash = Class.new(StandardError)
+
+ autoload :Base, 'devise/pbkdf2_encryptable/encryptors/base'
+ autoload :Pbkdf2Sha512, 'devise/pbkdf2_encryptable/encryptors/pbkdf2_sha512'
+ end
+ end
+end
+
+Devise.add_module(:pbkdf2_encryptable, :model => 'devise/pbkdf2_encryptable/model')
diff --git a/vendor/gems/devise-pbkdf2-encryptable/lib/devise/pbkdf2_encryptable/encryptors/base.rb b/vendor/gems/devise-pbkdf2-encryptable/lib/devise/pbkdf2_encryptable/encryptors/base.rb
new file mode 100644
index 00000000000..970be7f51a6
--- /dev/null
+++ b/vendor/gems/devise-pbkdf2-encryptable/lib/devise/pbkdf2_encryptable/encryptors/base.rb
@@ -0,0 +1,52 @@
+module Devise
+ module Pbkdf2Encryptable
+ module Encryptors
+ class Base
+ def self.split_digest(hash)
+ split_digest = hash.split('$')
+ _, strategy, stretches, salt, checksum = split_digest
+
+ unless split_digest.length == 5 && strategy.start_with?('pbkdf2-')
+ raise InvalidHash.new('invalid PBKDF2 hash')
+ end
+
+ { strategy: strategy, stretches: stretches.to_i,
+ salt: passlib_decode64(salt), checksum: passlib_decode64(checksum) }
+ end
+
+ # Passlib-style Base64 encoding:
+ # - Replaces '+' with '.'
+ # - Strips trailing newline and '=='
+ private_class_method def self.passlib_encode64(value)
+ Base64.strict_encode64([value].pack('H*')).tr('+', '.').delete('=')
+ end
+
+ private_class_method def self.passlib_decode64(value)
+ enc = value.tr('.', '+')
+ Base64.decode64(enc).unpack1('H*')
+ end
+
+ # Passlib-style hash: $pbkdf2-sha512$rounds$salt$checksum
+ # where salt and checksum are "adapted" Base64 encoded
+ private_class_method def self.format_hash(strategy, stretches, salt, checksum)
+ encoded_salt = passlib_encode64(salt)
+ encoded_checksum = passlib_encode64(checksum)
+
+ "$#{strategy}$#{stretches}$#{encoded_salt}$#{encoded_checksum}"
+ end
+
+ private_class_method def self.pbkdf2_checksum(hash, password, stretches, salt)
+ raise 'Stretches must be greater than zero' unless stretches.to_i > 0
+
+ OpenSSL::KDF.pbkdf2_hmac(
+ password.to_s,
+ salt: [salt].pack("H*"),
+ iterations: stretches,
+ hash: hash,
+ length: hash.digest_length
+ ).unpack1('H*')
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/gems/devise-pbkdf2-encryptable/lib/devise/pbkdf2_encryptable/encryptors/pbkdf2_sha512.rb b/vendor/gems/devise-pbkdf2-encryptable/lib/devise/pbkdf2_encryptable/encryptors/pbkdf2_sha512.rb
new file mode 100644
index 00000000000..3fedc724c06
--- /dev/null
+++ b/vendor/gems/devise-pbkdf2-encryptable/lib/devise/pbkdf2_encryptable/encryptors/pbkdf2_sha512.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Devise
+ module Pbkdf2Encryptable
+ module Encryptors
+ class Pbkdf2Sha512 < Base
+ STRATEGY = 'pbkdf2-sha512'
+ STRETCHES = 20_000
+
+ def self.compare(encrypted_password, password)
+ split_digest = self.split_digest(encrypted_password)
+ value_to_test = self.sha512_checksum(password, split_digest[:stretches], split_digest[:salt])
+
+ Devise.secure_compare(split_digest[:checksum], value_to_test)
+ end
+
+ def self.digest(password, stretches, salt)
+ checksum = sha512_checksum(password, stretches, salt)
+
+ format_hash(STRATEGY, stretches, salt, checksum)
+ end
+
+ def self.split_digest(hash)
+ split_digest = super
+
+ unless split_digest[:strategy] == STRATEGY
+ raise InvalidHash.new('invalid PBKDF2 SHA512 hash')
+ end
+
+ split_digest
+ end
+
+ private_class_method def self.sha512_checksum(password, stretches, salt)
+ hash = OpenSSL::Digest.new('SHA512')
+
+ pbkdf2_checksum(hash, password, stretches, salt)
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/gems/devise-pbkdf2-encryptable/lib/devise/pbkdf2_encryptable/model.rb b/vendor/gems/devise-pbkdf2-encryptable/lib/devise/pbkdf2_encryptable/model.rb
new file mode 100644
index 00000000000..06430b26db9
--- /dev/null
+++ b/vendor/gems/devise-pbkdf2-encryptable/lib/devise/pbkdf2_encryptable/model.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require 'devise/strategies/database_authenticatable'
+
+# Based on `devise-encryptable` Encryptable model
+# https://github.com/heartcombo/devise-encryptable/blob/main/lib/devise/encryptable/model.rb
+module Devise
+ module Models
+ module Pbkdf2Encryptable
+ extend ActiveSupport::Concern
+
+ def valid_password?(password)
+ encryptor_class.compare(encrypted_password, password)
+ end
+
+ def password_strategy
+ split_encrypted_password[:strategy]&.tr('-', '_')&.to_sym
+ end
+
+ def password_salt
+ split_encrypted_password[:salt]
+ end
+
+ # Used by warden and other modules where there is a
+ # need for a random token based on the user password.
+ alias_method :authenticatable_salt, :password_salt
+
+ def password_stretches
+ split_encrypted_password[:stretches]
+ end
+
+ def password_checksum
+ split_encrypted_password[:checksum]
+ end
+
+ protected
+
+ # Used by Devise DatabaseAuthenticatable when setting a password
+ def password_digest(password)
+ remove_instance_variable('@split_encrypted_password') if defined?(@split_encrypted_password)
+
+ encryptor_class.digest(password, encryptor_class::STRETCHES, Devise.friendly_token[0, 16])
+ end
+
+ def encryptor_class
+ self.class.encryptor_class
+ end
+
+ private
+
+ def split_encrypted_password
+ return {} unless encrypted_password.present?
+ return @split_encrypted_password if defined?(@split_encrypted_password)
+
+ @split_encrypted_password = encryptor_class.split_digest(encrypted_password)
+ end
+
+ module ClassMethods
+ Devise::Models.config(self, :encryptor)
+
+ # Returns the class for the configured encryptor.
+ def encryptor_class
+ @encryptor_class ||= case encryptor
+ when :bcrypt
+ raise "In order to use bcrypt as encryptor, simply remove :pbkdf2_encryptable from your devise model"
+ when nil
+ raise "You need to specify an :encryptor in Devise configuration in order to use :pbkdf2_encryptable"
+ else
+ Devise::Pbkdf2Encryptable::Encryptors.const_get(encryptor.to_s.classify)
+ end
+ rescue NameError
+ raise "Configured encryptor '#{encryptor.to_sym}' could not be found for pbkdf2_encryptable"
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/gems/devise-pbkdf2-encryptable/spec/lib/pbkdf2_encryptable/encryptors/pbkdf2_sha512_spec.rb b/vendor/gems/devise-pbkdf2-encryptable/spec/lib/pbkdf2_encryptable/encryptors/pbkdf2_sha512_spec.rb
new file mode 100644
index 00000000000..3a9b692db6e
--- /dev/null
+++ b/vendor/gems/devise-pbkdf2-encryptable/spec/lib/pbkdf2_encryptable/encryptors/pbkdf2_sha512_spec.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512 do
+ let(:bcrypt_hash) { '$2a$12$ftnD4XSrhVdlEaEgCn/lxO0Pt3QplwgblmhCug3nSeRhh5a9UDBWK' }
+ let(:pbkdf2_sha512_hash) { '$pbkdf2-sha512$20000$boHGAw0hEyI$DBA67J7zNZebyzLtLk2X9wRDbmj1LNKVGnZLYyz6PGrIDGIl45fl/BPH0y1TPZnV90A20i.fD9C3G9Bp8jzzOA' }
+
+ describe '.compare' do
+ subject(:compare) { described_class.compare(encrypted_password, password) }
+
+ context 'with a PBKDF2+SHA512 encrypted password' do
+ let(:encrypted_password) { pbkdf2_sha512_hash }
+
+ context 'with a matching password' do
+ let(:password) { 'password' }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'with an incorrect password' do
+ let(:password) { 'other_password' }
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ context 'with a non PBKDF2+SHA512 encrypted password' do
+ let(:encrypted_password) { bcrypt_hash }
+ let(:password) { 'password' }
+
+ it 'raises an invalid hash error ' do
+ expect { compare }.to raise_error Devise::Pbkdf2Encryptable::Encryptors::InvalidHash, 'invalid PBKDF2 hash'
+ end
+ end
+ end
+
+ describe '.digest' do
+ it 'returns a properly formatted and correct hash' do
+ expect(described_class.digest('password', 20000, '6e81c6030d211322')).to eq(pbkdf2_sha512_hash)
+ end
+
+ it 'raises an error if stretches is not greater than 0' do
+ expect { described_class.digest('password', 0, '6e81c6030d211322') }
+ .to raise_error('Stretches must be greater than zero')
+ end
+ end
+
+ describe '.split_digest' do
+ subject(:split_digest) { described_class.split_digest(digest) }
+
+ context 'with a PBKDF2+SHA512 digest' do
+ let(:digest) { pbkdf2_sha512_hash }
+
+ it { is_expected.to eq({
+ strategy: 'pbkdf2-sha512',
+ stretches: 20000,
+ salt: '6e81c6030d211322',
+ checksum: '0c103aec9ef335979bcb32ed2e4d97f704436e68f52cd2951a764b632cfa3c6ac80c6225e397e5fc13c7d32d533d99d5f74036d22f9f0fd0b71bd069f23cf338'
+ })
+ }
+ end
+
+ context 'with a PBKDF2+SHA256 digest' do
+ let(:digest) { '$pbkdf2-sha256$6400$.6UI/S.nXIk8jcbdHx3Fhg$98jZicV16ODfEsEZeYPGHU3kbrUrvUEXOPimVSQDD44' }
+
+ it 'raises invalid hash error' do
+ expect { split_digest }.to raise_error Devise::Pbkdf2Encryptable::Encryptors::InvalidHash, 'invalid PBKDF2 SHA512 hash'
+ end
+ end
+
+ context 'with a BCrypt digest' do
+ let(:digest) { bcrypt_hash }
+
+ it 'raises invalid hash error' do
+ expect { split_digest }.to raise_error Devise::Pbkdf2Encryptable::Encryptors::InvalidHash, 'invalid PBKDF2 hash'
+ end
+ end
+ end
+end
diff --git a/vendor/gems/devise-pbkdf2-encryptable/spec/lib/pbkdf2_encryptable/model_spec.rb b/vendor/gems/devise-pbkdf2-encryptable/spec/lib/pbkdf2_encryptable/model_spec.rb
new file mode 100644
index 00000000000..ebb94c1d68c
--- /dev/null
+++ b/vendor/gems/devise-pbkdf2-encryptable/spec/lib/pbkdf2_encryptable/model_spec.rb
@@ -0,0 +1,127 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'active_model'
+
+RSpec.describe Devise::Models::Pbkdf2Encryptable do
+ let(:unconfigured_model) do
+ Class.new do
+ include ActiveModel::Model
+ extend ActiveModel::Callbacks
+ include ActiveModel::Validations::Callbacks
+ extend Devise::Models
+
+ define_model_callbacks :update, only: :after
+
+ devise :database_authenticatable, :pbkdf2_encryptable
+
+ attr_accessor :encrypted_password
+
+ def initialize(encrypted_password: '')
+ self.encrypted_password = encrypted_password
+ end
+ end
+ end
+
+ let(:bcrypt_configured_model) do
+ Class.new(unconfigured_model) do
+ devise :database_authenticatable, :pbkdf2_encryptable, encryptor: :bcrypt
+ end
+ end
+
+ let(:unknown_configured_model) do
+ Class.new(unconfigured_model) do
+ devise :database_authenticatable, :pbkdf2_encryptable, encryptor: :sha512
+ end
+ end
+
+ let(:configured_model) do
+ Class.new(unconfigured_model) do
+ devise :database_authenticatable, :pbkdf2_encryptable, encryptor: :pbkdf2_sha512
+ end
+ end
+
+ let(:user) { configured_model.new }
+ let(:pbkdf2_sha512_hash) { '$pbkdf2-sha512$20000$boHGAw0hEyI$DBA67J7zNZebyzLtLk2X9wRDbmj1LNKVGnZLYyz6PGrIDGIl45fl/BPH0y1TPZnV90A20i.fD9C3G9Bp8jzzOA' }
+
+ describe '#valid_password?' do
+ let(:user) { configured_model.new(encrypted_password: pbkdf2_sha512_hash) }
+
+ it 'validates a correct password' do
+ expect(user.valid_password?('password')).to eq(true)
+ end
+
+ it 'does not validate an incorrect password' do
+ expect(user.valid_password?('other_password')).to eq(false)
+ end
+ end
+
+ describe '#password=' do
+ it 'sets the correct encrypted_password value', :aggregate_failures do
+ expect(user.encrypted_password).to be_empty
+
+ user.password = 'password'
+
+ expect(user.encrypted_password).to start_with('$pbkdf2-sha512$')
+ end
+
+ it 'clears split digest memoization' do
+ user.encrypted_password = '$pbkdf2-sha512$1000$boHGAw0hEyI$DBA67J7zNZebyzLtLk2X9wRDbmj1LNKVGnZLYyz6PGrIDGIl45fl/BPH0y1TPZnV90A20i.fD9C3G9Bp8jzzOA'
+
+ expect(user.password_stretches).to eq(1_000)
+
+ user.password = 'other_password'
+
+ expect(user.password_stretches).to eq(20_000)
+ end
+ end
+
+ describe 'password_* methods' do
+ let(:user) { configured_model.new(encrypted_password: encrypted_password) }
+
+ context 'with a PBKDF2+SHA512 encrypted password' do
+ let(:encrypted_password) { pbkdf2_sha512_hash }
+
+ it 'extracts the correct split hash values', :aggregate_failures do
+ expect(Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512).to receive(:split_digest).once.and_call_original
+
+ expect(user.password_strategy).to eq(:pbkdf2_sha512)
+ expect(user.password_salt).to eq('6e81c6030d211322')
+ expect(user.password_stretches).to eq(20_000)
+ expect(user.password_checksum).to eq('0c103aec9ef335979bcb32ed2e4d97f704436e68f52cd2951a764b632cfa3c6ac80c6225e397e5fc13c7d32d533d99d5f74036d22f9f0fd0b71bd069f23cf338')
+ end
+ end
+
+ context 'with a BCrypt encrypted password' do
+ let(:encrypted_password) { '$2a$10$xLTxCKOa75IU4RQGqqOrTuZOgZdJEzfSzjG6ZSEi/C31TB/yLZYpi' }
+
+ it 'raises errors', :aggregate_failures do
+ expect { user.password_strategy }.to raise_error Devise::Pbkdf2Encryptable::Encryptors::InvalidHash
+ expect { user.password_salt }.to raise_error Devise::Pbkdf2Encryptable::Encryptors::InvalidHash
+ expect { user.password_stretches }.to raise_error Devise::Pbkdf2Encryptable::Encryptors::InvalidHash
+ expect { user.password_checksum }.to raise_error Devise::Pbkdf2Encryptable::Encryptors::InvalidHash
+ end
+ end
+ end
+
+ describe '.encryptor_class' do
+ it 'returns a class when configured' do
+ expect(configured_model.encryptor_class).to eq(Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512)
+ end
+
+ it 'raises an error when unconfigured' do
+ expect { unconfigured_model.encryptor_class }
+ .to raise_error('You need to specify an :encryptor in Devise configuration in order to use :pbkdf2_encryptable')
+ end
+
+ it 'raises an error when BCrypt is configured' do
+ expect { bcrypt_configured_model.encryptor_class }
+ .to raise_error('In order to use bcrypt as encryptor, simply remove :pbkdf2_encryptable from your devise model')
+ end
+
+ it 'raises an error when a class cannot be found' do
+ expect { unknown_configured_model.encryptor_class }
+ .to raise_error("Configured encryptor 'sha512' could not be found for pbkdf2_encryptable")
+ end
+ end
+end
diff --git a/vendor/gems/devise-pbkdf2-encryptable/spec/spec_helper.rb b/vendor/gems/devise-pbkdf2-encryptable/spec/spec_helper.rb
new file mode 100644
index 00000000000..ced0cf6fdab
--- /dev/null
+++ b/vendor/gems/devise-pbkdf2-encryptable/spec/spec_helper.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+require 'devise'
+require 'devise/pbkdf2_encryptable/encryptable'
diff --git a/workhorse/go.mod b/workhorse/go.mod
index 5e81c835218..15279b50529 100644
--- a/workhorse/go.mod
+++ b/workhorse/go.mod
@@ -5,7 +5,7 @@ go 1.17
require (
github.com/Azure/azure-storage-blob-go v0.14.0
github.com/BurntSushi/toml v0.3.1
- github.com/FZambia/sentinel v1.0.0
+ github.com/FZambia/sentinel v1.1.0
github.com/alecthomas/chroma v0.10.0
github.com/aws/aws-sdk-go v1.43.31
github.com/disintegration/imaging v1.6.2
@@ -14,7 +14,7 @@ require (
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f
github.com/golang/protobuf v1.5.2
github.com/gomodule/redigo v2.0.0+incompatible
- github.com/gorilla/websocket v1.4.1
+ github.com/gorilla/websocket v1.5.0
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/johannesboyne/gofakes3 v0.0.0-20220627085814-c3ac35da23b2
diff --git a/workhorse/go.sum b/workhorse/go.sum
index 9559572d1c7..51c7a51c002 100644
--- a/workhorse/go.sum
+++ b/workhorse/go.sum
@@ -155,8 +155,8 @@ github.com/DataDog/datadog-go v4.4.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3
github.com/DataDog/gostackparse v0.5.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM=
github.com/DataDog/sketches-go v1.0.0 h1:chm5KSXO7kO+ywGWJ0Zs6tdmWU8PBXSbywFVciL6BG4=
github.com/DataDog/sketches-go v1.0.0/go.mod h1:O+XkJHWk9w4hDwY2ZUDU31ZC9sNYlYo8DiFsxjYeo1k=
-github.com/FZambia/sentinel v1.0.0 h1:KJ0ryjKTZk5WMp0dXvSdNqp3lFaW1fNFuEYfrkLOYIc=
-github.com/FZambia/sentinel v1.0.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI=
+github.com/FZambia/sentinel v1.1.0 h1:qrCBfxc8SvJihYNjBWgwUI93ZCvFe/PJIPTHKmlp8a8=
+github.com/FZambia/sentinel v1.1.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI=
github.com/GoogleCloudPlatform/cloudsql-proxy v1.22.0/go.mod h1:mAm5O/zik2RFmcpigNjg6nMotDL8ZXJaxKzgGVcSMFA=
github.com/GoogleCloudPlatform/cloudsql-proxy v1.29.0/go.mod h1:spvB9eLJH9dutlbPSRmHvSXXHOwGRyeXh1jVdquA2G8=
github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
@@ -589,8 +589,9 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
+github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20170920190843-316c5e0ff04e/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=