diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-04 15:09:33 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-04 15:09:33 +0300 |
commit | 46b08e61d27f9b3f45b130f9204084ffcf50c304 (patch) | |
tree | 96ef84fc43b41e8d1f8539fe085dc01496e29427 | |
parent | 54cd986c9f16b5f7587b072ee8eb84bbf6642e8c (diff) |
Add latest changes from gitlab-org/gitlab@master
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 @@ -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= |