From 054378fd4a238b3e1f921afda4e9a650854935d9 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 12 Aug 2022 21:11:43 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../vue_shared/components/web_ide_link.vue | 30 +++++++----- app/models/oauth_access_token.rb | 10 ++++ .../development/hash_oauth_tokens.yml | 8 ++++ config/initializers/doorkeeper.rb | 2 + doc/development/documentation/testing.md | 30 ++++++++++++ .../pbkdf2_sha512.rb | 28 +++++++++++ locale/gitlab.pot | 9 ++-- .../blob_header_default_actions_spec.js | 2 +- spec/frontend/blob/sketch/index_spec.js | 2 +- .../components/delete_button_spec.js | 4 +- .../design_notes/design_discussion_spec.js | 12 ++--- .../design_notes/design_note_spec.js | 2 +- .../design_notes/design_reply_form_spec.js | 6 +-- .../toggle_replies_widget_spec.js | 8 ++-- .../components/design_scaler_spec.js | 2 +- .../components/design_sidebar_spec.js | 8 ++-- .../components/design_todo_button_spec.js | 2 +- .../components/image_spec.js | 2 +- .../components/list/item_spec.js | 10 ++-- .../components/toolbar/index_spec.js | 10 ++-- .../components/upload/button_spec.js | 2 +- .../upload/design_version_dropdown_spec.js | 12 ++--- .../pages/design/index_spec.js | 12 ++--- .../design_management/pages/index_spec.js | 8 ++-- .../frontend/design_management/router_spec.js | 4 +- .../diffs/components/diff_file_header_spec.js | 2 +- .../shared/commit_message_field_spec.js | 2 +- spec/frontend/ide/stores/actions/file_spec.js | 8 ++-- .../commits/components/author_select_spec.js | 4 +- .../components/states/mr_widget_wip_spec.js | 14 +++--- .../components/confirm_modal_spec.js | 2 +- .../runner_instructions_modal_spec.js | 4 +- .../components/web_ide_link_spec.js | 6 +-- spec/lib/gitlab/auth/auth_finders_spec.rb | 6 +-- .../pbkdf2_sha512_spec.rb | 36 ++++++++++++++ spec/models/oauth_access_token_spec.rb | 47 +++++++++++++++++++ spec/requests/api/doorkeeper_access_spec.rb | 6 +-- spec/requests/api/go_proxy_spec.rb | 6 +-- spec/requests/api/helpers_spec.rb | 2 +- .../requests/api/npm_project_packages_spec.rb | 6 +-- spec/requests/rack_attack_global_spec.rb | 12 ++--- spec/support/helpers/api_helpers.rb | 6 ++- .../helpers/rack_attack_spec_helpers.rb | 6 ++- .../api/npm_packages_shared_examples.rb | 8 ++-- 44 files changed, 292 insertions(+), 116 deletions(-) create mode 100644 config/feature_flags/development/hash_oauth_tokens.yml create mode 100644 lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512.rb create mode 100644 spec/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512_spec.rb diff --git a/app/assets/javascripts/vue_shared/components/web_ide_link.vue b/app/assets/javascripts/vue_shared/components/web_ide_link.vue index cac0d5a45c9..6d179b3dc92 100644 --- a/app/assets/javascripts/vue_shared/components/web_ide_link.vue +++ b/app/assets/javascripts/vue_shared/components/web_ide_link.vue @@ -10,6 +10,21 @@ const KEY_WEB_IDE = 'webide'; const KEY_GITPOD = 'gitpod'; const KEY_PIPELINE_EDITOR = 'pipeline_editor'; +export const i18n = { + modal: { + title: __('Enable Gitpod?'), + content: s__( + 'Gitpod|To use Gitpod you must first enable the feature in the integrations section of your %{linkStart}user preferences%{linkEnd}.', + ), + actionCancelText: __('Cancel'), + actionPrimaryText: __('Enable Gitpod'), + }, + webIdeText: s__('WebIDE|Quickly and easily edit multiple files in your project.'), + webIdeTooltip: s__( + 'WebIDE|Quickly and easily edit multiple files in your project. Press . to open', + ), +}; + export default { components: { ActionsButton, @@ -19,16 +34,7 @@ export default { GlLink, ConfirmForkModal, }, - i18n: { - modal: { - title: __('Enable Gitpod?'), - content: s__( - 'Gitpod|To use Gitpod you must first enable the feature in the integrations section of your %{linkStart}user preferences%{linkEnd}.', - ), - actionCancelText: __('Cancel'), - actionPrimaryText: __('Enable Gitpod'), - }, - }, + i18n, props: { isFork: { type: Boolean, @@ -207,8 +213,8 @@ export default { return { key: KEY_WEB_IDE, text: this.webIdeActionText, - secondaryText: __('Quickly and easily edit multiple files in your project.'), - tooltip: '', + secondaryText: this.$options.i18n.webIdeText, + tooltip: this.$options.i18n.webIdeTooltip, attrs: { 'data-qa-selector': 'web_ide_button', 'data-track-action': 'click_consolidated_edit_ide', diff --git a/app/models/oauth_access_token.rb b/app/models/oauth_access_token.rb index 87e82c04783..7d71e15d3c5 100644 --- a/app/models/oauth_access_token.rb +++ b/app/models/oauth_access_token.rb @@ -16,4 +16,14 @@ class OauthAccessToken < Doorkeeper::AccessToken super end end + + # this method overrides a shortcoming upstream, more context: + # https://gitlab.com/gitlab-org/gitlab/-/issues/367888 + def self.find_by_fallback_token(attr, plain_secret) + return unless fallback_secret_strategy && fallback_secret_strategy == Doorkeeper::SecretStoring::Plain + # token is hashed, don't allow plaintext comparison + return if plain_secret.starts_with?("$") + + super + end end diff --git a/config/feature_flags/development/hash_oauth_tokens.yml b/config/feature_flags/development/hash_oauth_tokens.yml new file mode 100644 index 00000000000..43cd5672fc4 --- /dev/null +++ b/config/feature_flags/development/hash_oauth_tokens.yml @@ -0,0 +1,8 @@ +--- +name: hash_oauth_tokens +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91501 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367570 +milestone: '15.3' +type: development +group: group::authentication and authorization +default_enabled: false diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index 761904009bb..867f3fd47cc 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -90,6 +90,8 @@ Doorkeeper.configure do # Check out the wiki for more information on customization access_token_methods :from_access_token_param, :from_bearer_authorization, :from_bearer_param + hash_token_secrets using: '::Gitlab::DoorkeeperSecretStoring::Pbkdf2Sha512', fallback: :plain + # Specify what grant flows are enabled in array of Strings. The valid # strings and the flows they enable are: # diff --git a/doc/development/documentation/testing.md b/doc/development/documentation/testing.md index 1a678873509..85219ec0364 100644 --- a/doc/development/documentation/testing.md +++ b/doc/development/documentation/testing.md @@ -81,6 +81,36 @@ This requires you to either: ### Documentation link tests +Merge requests containing changes to Markdown (`.md`) files run a `docs-lint links` +job, which runs two types of link checks. In both cases, links with destinations +that begin with `http` or `https` are considered external links, and skipped: + +- `bundle exec nanoc check internal_links`: Tests links to internal pages. +- `bundle exec nanoc check internal_anchors`: Tests links to subheadings (anchors) on internal pages. + +Failures from these tests are displayed at the end of the test results in the **Issues found!** area. +For example, failures in the `internal_anchors` test follow this format: + +```plaintext +[ ERROR ] internal_anchors - Broken anchor detected! + - source file `/tmp/gitlab-docs/public/ee/user/application_security/api_fuzzing/index.html` + - destination `/tmp/gitlab-docs/public/ee/development/code_review.html` + - link `../../../development/code_review.html#review-response-slo` + - anchor `#review-response-slo` +``` + +- **Source file**: The full path to the file containing the error. To find the + file in the `gitlab` repository, replace `/tmp/gitlab-docs/public/ee` with `doc`, and `.html` with `.md`. +- **Destination**: The full path to the file not found by the test. To find the + file in the `gitlab` repository, replace `/tmp/gitlab-docs/public/ee` with `doc`, and `.html` with `.md`. +- **Link**: The actual link the script attempted to find. +- **Anchor**: If present, the subheading (anchor) the script attempted to find. + +Check for multiple instances of the same broken link on each page reporting an error. +Even if a specific broken link appears multiple times on a page, the test reports it only once. + +#### Run document link tests locally + To execute documentation link tests locally: 1. Navigate to the [`gitlab-docs`](https://gitlab.com/gitlab-org/gitlab-docs) directory. diff --git a/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512.rb b/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512.rb new file mode 100644 index 00000000000..4bfb5f9e64c --- /dev/null +++ b/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Gitlab + module DoorkeeperSecretStoring + class Pbkdf2Sha512 < ::Doorkeeper::SecretStoring::Base + STRETCHES = 20_000 + # An empty salt is used because we need to look tokens up solely by + # their hashed value. Additionally, tokens are always cryptographically + # pseudo-random and unique, therefore salting provides no + # additional security. + SALT = '' + + def self.transform_secret(plain_secret) + return plain_secret unless Feature.enabled?(:hash_oauth_tokens) + + Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512.digest(plain_secret, STRETCHES, SALT) + end + + ## + # Determines whether this strategy supports restoring + # secrets from the database. This allows detecting users + # trying to use a non-restorable strategy with +reuse_access_tokens+. + def self.allows_restoring_secrets? + false + end + end + end +end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index c0b5cabf983..9f9d7c7665e 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -32068,9 +32068,6 @@ msgstr "" msgid "Quick range" msgstr "" -msgid "Quickly and easily edit multiple files in your project." -msgstr "" - msgid "Quota of CI/CD minutes" msgstr "" @@ -43720,6 +43717,12 @@ msgstr "" msgid "WebIDE|Merge request" msgstr "" +msgid "WebIDE|Quickly and easily edit multiple files in your project." +msgstr "" + +msgid "WebIDE|Quickly and easily edit multiple files in your project. Press . to open" +msgstr "" + msgid "WebIDE|This project does not accept unsigned commits." msgstr "" diff --git a/spec/frontend/blob/components/blob_header_default_actions_spec.js b/spec/frontend/blob/components/blob_header_default_actions_spec.js index 2382ac15f40..0f015715dc2 100644 --- a/spec/frontend/blob/components/blob_header_default_actions_spec.js +++ b/spec/frontend/blob/components/blob_header_default_actions_spec.js @@ -71,7 +71,7 @@ describe('Blob Header Default Actions', () => { }); buttons = wrapper.findAllComponents(GlButton); - expect(buttons.at(0).attributes('disabled')).toBeTruthy(); + expect(buttons.at(0).attributes('disabled')).toBe('true'); }); it('does not render the copy button if a rendering error is set', () => { diff --git a/spec/frontend/blob/sketch/index_spec.js b/spec/frontend/blob/sketch/index_spec.js index 5e1922a24f4..e8d1f724c4b 100644 --- a/spec/frontend/blob/sketch/index_spec.js +++ b/spec/frontend/blob/sketch/index_spec.js @@ -69,7 +69,7 @@ describe('Sketch viewer', () => { const img = document.querySelector('#js-sketch-viewer img'); expect(img).not.toBeNull(); - expect(img.classList.contains('img-fluid')).toBeTruthy(); + expect(img.classList.contains('img-fluid')).toBe(true); }); it('renders link to image', () => { diff --git a/spec/frontend/design_management/components/delete_button_spec.js b/spec/frontend/design_management/components/delete_button_spec.js index e3907fdbe15..cee1eec792d 100644 --- a/spec/frontend/design_management/components/delete_button_spec.js +++ b/spec/frontend/design_management/components/delete_button_spec.js @@ -6,8 +6,8 @@ import BatchDeleteButton from '~/design_management/components/delete_button.vue' describe('Batch delete button component', () => { let wrapper; - const findButton = () => wrapper.find(GlButton); - const findModal = () => wrapper.find(GlModal); + const findButton = () => wrapper.findComponent(GlButton); + const findModal = () => wrapper.findComponent(GlModal); function createComponent({ isDeleting = false } = {}, { slots = {} } = {}) { wrapper = shallowMount(BatchDeleteButton, { diff --git a/spec/frontend/design_management/components/design_notes/design_discussion_spec.js b/spec/frontend/design_management/components/design_notes/design_discussion_spec.js index ae2dc23a94d..2091e1e08dd 100644 --- a/spec/frontend/design_management/components/design_notes/design_discussion_spec.js +++ b/spec/frontend/design_management/components/design_notes/design_discussion_spec.js @@ -26,13 +26,13 @@ describe('Design discussions component', () => { const originalGon = window.gon; let wrapper; - const findDesignNotes = () => wrapper.findAll(DesignNote); - const findReplyPlaceholder = () => wrapper.find(ReplyPlaceholder); - const findReplyForm = () => wrapper.find(DesignReplyForm); - const findRepliesWidget = () => wrapper.find(ToggleRepliesWidget); + const findDesignNotes = () => wrapper.findAllComponents(DesignNote); + const findReplyPlaceholder = () => wrapper.findComponent(ReplyPlaceholder); + const findReplyForm = () => wrapper.findComponent(DesignReplyForm); + const findRepliesWidget = () => wrapper.findComponent(ToggleRepliesWidget); const findResolveButton = () => wrapper.find('[data-testid="resolve-button"]'); const findResolvedMessage = () => wrapper.find('[data-testid="resolved-message"]'); - const findResolveLoadingIcon = () => wrapper.find(GlLoadingIcon); + const findResolveLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); const findResolveCheckbox = () => wrapper.find('[data-testid="resolve-checkbox"]'); const findApolloMutation = () => wrapper.findComponent(ApolloMutation); @@ -307,7 +307,7 @@ describe('Design discussions component', () => { expect( wrapper - .findAll(DesignNote) + .findAllComponents(DesignNote) .wrappers.every((designNote) => designNote.classes('gl-bg-blue-50')), ).toBe(true); }, diff --git a/spec/frontend/design_management/components/design_notes/design_note_spec.js b/spec/frontend/design_management/components/design_notes/design_note_spec.js index 1f84fde9f7f..28833b4af5c 100644 --- a/spec/frontend/design_management/components/design_notes/design_note_spec.js +++ b/spec/frontend/design_management/components/design_notes/design_note_spec.js @@ -100,7 +100,7 @@ describe('Design note component', () => { note, }); - expect(wrapper.find(TimeAgoTooltip).exists()).toBe(true); + expect(wrapper.findComponent(TimeAgoTooltip).exists()).toBe(true); }); it('should not render edit icon when user does not have a permission', () => { diff --git a/spec/frontend/design_management/components/design_notes/design_reply_form_spec.js b/spec/frontend/design_management/components/design_notes/design_reply_form_spec.js index 38dd2df01a3..f7ce742b933 100644 --- a/spec/frontend/design_management/components/design_notes/design_reply_form_spec.js +++ b/spec/frontend/design_management/components/design_notes/design_reply_form_spec.js @@ -15,9 +15,9 @@ describe('Design reply form component', () => { let wrapper; const findTextarea = () => wrapper.find('textarea'); - const findSubmitButton = () => wrapper.find({ ref: 'submitButton' }); - const findCancelButton = () => wrapper.find({ ref: 'cancelButton' }); - const findModal = () => wrapper.find({ ref: 'cancelCommentModal' }); + const findSubmitButton = () => wrapper.findComponent({ ref: 'submitButton' }); + const findCancelButton = () => wrapper.findComponent({ ref: 'cancelButton' }); + const findModal = () => wrapper.findComponent({ ref: 'cancelCommentModal' }); function createComponent(props = {}, mountOptions = {}) { wrapper = mount(DesignReplyForm, { diff --git a/spec/frontend/design_management/components/design_notes/toggle_replies_widget_spec.js b/spec/frontend/design_management/components/design_notes/toggle_replies_widget_spec.js index f87228663b6..41129e2b58d 100644 --- a/spec/frontend/design_management/components/design_notes/toggle_replies_widget_spec.js +++ b/spec/frontend/design_management/components/design_notes/toggle_replies_widget_spec.js @@ -8,10 +8,10 @@ describe('Toggle replies widget component', () => { let wrapper; const findToggleWrapper = () => wrapper.find('[data-testid="toggle-comments-wrapper"]'); - const findIcon = () => wrapper.find(GlIcon); - const findButton = () => wrapper.find(GlButton); - const findAuthorLink = () => wrapper.find(GlLink); - const findTimeAgo = () => wrapper.find(TimeAgoTooltip); + const findIcon = () => wrapper.findComponent(GlIcon); + const findButton = () => wrapper.findComponent(GlButton); + const findAuthorLink = () => wrapper.findComponent(GlLink); + const findTimeAgo = () => wrapper.findComponent(TimeAgoTooltip); function createComponent(props = {}) { wrapper = shallowMount(ToggleRepliesWidget, { diff --git a/spec/frontend/design_management/components/design_scaler_spec.js b/spec/frontend/design_management/components/design_scaler_spec.js index a04e2ebda5b..e1a66cea329 100644 --- a/spec/frontend/design_management/components/design_scaler_spec.js +++ b/spec/frontend/design_management/components/design_scaler_spec.js @@ -6,7 +6,7 @@ import DesignScaler from '~/design_management/components/design_scaler.vue'; describe('Design management design scaler component', () => { let wrapper; - const getButtons = () => wrapper.findAll(GlButton); + const getButtons = () => wrapper.findAllComponents(GlButton); const getDecreaseScaleButton = () => getButtons().at(0); const getResetScaleButton = () => getButtons().at(1); const getIncreaseScaleButton = () => getButtons().at(2); diff --git a/spec/frontend/design_management/components/design_sidebar_spec.js b/spec/frontend/design_management/components/design_sidebar_spec.js index f13796138bd..af995f75ddc 100644 --- a/spec/frontend/design_management/components/design_sidebar_spec.js +++ b/spec/frontend/design_management/components/design_sidebar_spec.js @@ -32,12 +32,12 @@ describe('Design management design sidebar component', () => { const originalGon = window.gon; let wrapper; - const findDiscussions = () => wrapper.findAll(DesignDiscussion); + const findDiscussions = () => wrapper.findAllComponents(DesignDiscussion); const findFirstDiscussion = () => findDiscussions().at(0); const findUnresolvedDiscussions = () => wrapper.findAll('[data-testid="unresolved-discussion"]'); const findResolvedDiscussions = () => wrapper.findAll('[data-testid="resolved-discussion"]'); - const findParticipants = () => wrapper.find(Participants); - const findResolvedCommentsToggle = () => wrapper.find(GlAccordionItem); + const findParticipants = () => wrapper.findComponent(Participants); + const findResolvedCommentsToggle = () => wrapper.findComponent(GlAccordionItem); const findNewDiscussionDisclaimer = () => wrapper.find('[data-testid="new-discussion-disclaimer"]'); @@ -87,7 +87,7 @@ describe('Design management design sidebar component', () => { it('renders To-Do button', () => { createComponent(); - expect(wrapper.find(DesignTodoButton).exists()).toBe(true); + expect(wrapper.findComponent(DesignTodoButton).exists()).toBe(true); }); describe('when has no discussions', () => { diff --git a/spec/frontend/design_management/components/design_todo_button_spec.js b/spec/frontend/design_management/components/design_todo_button_spec.js index 73661c9fcb0..b3afcefe1ed 100644 --- a/spec/frontend/design_management/components/design_todo_button_spec.js +++ b/spec/frontend/design_management/components/design_todo_button_spec.js @@ -57,7 +57,7 @@ describe('Design management design todo button', () => { }); it('renders TodoButton component', () => { - expect(wrapper.find(TodoButton).exists()).toBe(true); + expect(wrapper.findComponent(TodoButton).exists()).toBe(true); }); describe('when design has a pending todo', () => { diff --git a/spec/frontend/design_management/components/image_spec.js b/spec/frontend/design_management/components/image_spec.js index 65ee0ae6238..8163cb0d87a 100644 --- a/spec/frontend/design_management/components/image_spec.js +++ b/spec/frontend/design_management/components/image_spec.js @@ -71,7 +71,7 @@ describe('Design management large image component', () => { image.trigger('error'); await nextTick(); expect(image.isVisible()).toBe(false); - expect(wrapper.find(GlIcon).element).toMatchSnapshot(); + expect(wrapper.findComponent(GlIcon).element).toMatchSnapshot(); }); describe('zoom', () => { diff --git a/spec/frontend/design_management/components/list/item_spec.js b/spec/frontend/design_management/components/list/item_spec.js index e00dda2015e..66d3f883960 100644 --- a/spec/frontend/design_management/components/list/item_spec.js +++ b/spec/frontend/design_management/components/list/item_spec.js @@ -23,8 +23,8 @@ describe('Design management list item component', () => { const findDesignEvent = () => wrapper.findByTestId('design-event'); const findImgFilename = (id = imgId) => wrapper.findByTestId(`design-img-filename-${id}`); - const findEventIcon = () => findDesignEvent().find(GlIcon); - const findLoadingIcon = () => wrapper.find(GlLoadingIcon); + const findEventIcon = () => findDesignEvent().findComponent(GlIcon); + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); function createComponent({ notesCount = 0, @@ -74,7 +74,7 @@ describe('Design management list item component', () => { beforeEach(async () => { createComponent(); image = wrapper.find('img'); - glIntersectionObserver = wrapper.find(GlIntersectionObserver); + glIntersectionObserver = wrapper.findComponent(GlIntersectionObserver); glIntersectionObserver.vm.$emit('appear'); await nextTick(); @@ -86,7 +86,7 @@ describe('Design management list item component', () => { describe('before image is loaded', () => { it('renders loading spinner', () => { - expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); + expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true); }); }); @@ -105,7 +105,7 @@ describe('Design management list item component', () => { image.trigger('error'); await nextTick(); expect(image.isVisible()).toBe(false); - expect(wrapper.find(GlIcon).element).toMatchSnapshot(); + expect(wrapper.findComponent(GlIcon).element).toMatchSnapshot(); }); describe('when imageV432x230 and image provided', () => { diff --git a/spec/frontend/design_management/components/toolbar/index_spec.js b/spec/frontend/design_management/components/toolbar/index_spec.js index 412f3de911e..b6137ba2eee 100644 --- a/spec/frontend/design_management/components/toolbar/index_spec.js +++ b/spec/frontend/design_management/components/toolbar/index_spec.js @@ -85,35 +85,35 @@ describe('Design management toolbar component', () => { createComponent(); await nextTick(); - expect(wrapper.find(DeleteButton).exists()).toBe(true); + expect(wrapper.findComponent(DeleteButton).exists()).toBe(true); }); it('does not render delete button on non-latest version', async () => { createComponent(false, true, { isLatestVersion: false }); await nextTick(); - expect(wrapper.find(DeleteButton).exists()).toBe(false); + expect(wrapper.findComponent(DeleteButton).exists()).toBe(false); }); it('does not render delete button when user is not logged in', async () => { createComponent(false, false); await nextTick(); - expect(wrapper.find(DeleteButton).exists()).toBe(false); + expect(wrapper.findComponent(DeleteButton).exists()).toBe(false); }); it('emits `delete` event on deleteButton `delete-selected-designs` event', async () => { createComponent(); await nextTick(); - wrapper.find(DeleteButton).vm.$emit('delete-selected-designs'); + wrapper.findComponent(DeleteButton).vm.$emit('delete-selected-designs'); expect(wrapper.emitted().delete).toBeTruthy(); }); it('renders download button with correct link', () => { createComponent(); - expect(wrapper.find(GlButton).attributes('href')).toBe( + expect(wrapper.findComponent(GlButton).attributes('href')).toBe( '/-/designs/306/7f747adcd4693afadbe968d7ba7d983349b9012d', ); }); diff --git a/spec/frontend/design_management/components/upload/button_spec.js b/spec/frontend/design_management/components/upload/button_spec.js index d123db43ce6..59821218ab8 100644 --- a/spec/frontend/design_management/components/upload/button_spec.js +++ b/spec/frontend/design_management/components/upload/button_spec.js @@ -34,7 +34,7 @@ describe('Design management upload button component', () => { it('Button `loading` prop is `true`', () => { createComponent({ isSaving: true }); - const button = wrapper.find(GlButton); + const button = wrapper.findComponent(GlButton); expect(button.exists()).toBe(true); expect(button.props('loading')).toBe(true); }); diff --git a/spec/frontend/design_management/components/upload/design_version_dropdown_spec.js b/spec/frontend/design_management/components/upload/design_version_dropdown_spec.js index ec5db04bb80..7c26ab9739b 100644 --- a/spec/frontend/design_management/components/upload/design_version_dropdown_spec.js +++ b/spec/frontend/design_management/components/upload/design_version_dropdown_spec.js @@ -46,7 +46,7 @@ describe('Design management design version dropdown component', () => { wrapper.destroy(); }); - const findVersionLink = (index) => wrapper.findAll(GlDropdownItem).at(index); + const findVersionLink = (index) => wrapper.findAllComponents(GlDropdownItem).at(index); it('renders design version dropdown button', async () => { createComponent(); @@ -76,35 +76,35 @@ describe('Design management design version dropdown component', () => { createComponent(); await nextTick(); - expect(wrapper.find(GlDropdown).attributes('text')).toBe('Showing latest version'); + expect(wrapper.findComponent(GlDropdown).attributes('text')).toBe('Showing latest version'); }); it('displays latest version text when only 1 version is present', async () => { createComponent({ maxVersions: 1 }); await nextTick(); - expect(wrapper.find(GlDropdown).attributes('text')).toBe('Showing latest version'); + expect(wrapper.findComponent(GlDropdown).attributes('text')).toBe('Showing latest version'); }); it('displays version text when the current version is not the latest', async () => { createComponent({ $route: designRouteFactory(PREVIOUS_VERSION_ID) }); await nextTick(); - expect(wrapper.find(GlDropdown).attributes('text')).toBe(`Showing version #1`); + expect(wrapper.findComponent(GlDropdown).attributes('text')).toBe(`Showing version #1`); }); it('displays latest version text when the current version is the latest', async () => { createComponent({ $route: designRouteFactory(LATEST_VERSION_ID) }); await nextTick(); - expect(wrapper.find(GlDropdown).attributes('text')).toBe('Showing latest version'); + expect(wrapper.findComponent(GlDropdown).attributes('text')).toBe('Showing latest version'); }); it('should have the same length as apollo query', async () => { createComponent(); await nextTick(); - expect(wrapper.findAll(GlDropdownItem)).toHaveLength(wrapper.vm.allVersions.length); + expect(wrapper.findAllComponents(GlDropdownItem)).toHaveLength(wrapper.vm.allVersions.length); }); it('should render TimeAgo', async () => { diff --git a/spec/frontend/design_management/pages/design/index_spec.js b/spec/frontend/design_management/pages/design/index_spec.js index 17a299c5de1..774e37a8b21 100644 --- a/spec/frontend/design_management/pages/design/index_spec.js +++ b/spec/frontend/design_management/pages/design/index_spec.js @@ -85,9 +85,9 @@ describe('Design management design index page', () => { let wrapper; let router; - const findDiscussionForm = () => wrapper.find(DesignReplyForm); - const findSidebar = () => wrapper.find(DesignSidebar); - const findDesignPresentation = () => wrapper.find(DesignPresentation); + const findDiscussionForm = () => wrapper.findComponent(DesignReplyForm); + const findSidebar = () => wrapper.findComponent(DesignSidebar); + const findDesignPresentation = () => wrapper.findComponent(DesignPresentation); function createComponent( { loading = false } = {}, @@ -181,15 +181,15 @@ describe('Design management design index page', () => { it('sets loading state', () => { createComponent({ loading: true }); - expect(wrapper.find(DesignPresentation).props('isLoading')).toBe(true); - expect(wrapper.find(DesignSidebar).props('isLoading')).toBe(true); + expect(wrapper.findComponent(DesignPresentation).props('isLoading')).toBe(true); + expect(wrapper.findComponent(DesignSidebar).props('isLoading')).toBe(true); }); it('renders design index', () => { createComponent({ loading: false }, { data: { design } }); expect(wrapper.element).toMatchSnapshot(); - expect(wrapper.find(GlAlert).exists()).toBe(false); + expect(wrapper.findComponent(GlAlert).exists()).toBe(false); }); it('passes correct props to sidebar component', () => { diff --git a/spec/frontend/design_management/pages/index_spec.js b/spec/frontend/design_management/pages/index_spec.js index 321d55cbaef..f90feaadfb0 100644 --- a/spec/frontend/design_management/pages/index_spec.js +++ b/spec/frontend/design_management/pages/index_spec.js @@ -111,8 +111,8 @@ describe('Design management index page', () => { const findDropzoneWrapper = () => wrapper.findByTestId('design-dropzone-wrapper'); const findFirstDropzoneWithDesign = () => wrapper.findAllComponents(DesignDropzone).at(1); const findDesignsWrapper = () => wrapper.findByTestId('designs-root'); - const findDesigns = () => wrapper.findAll(Design); - const draggableAttributes = () => wrapper.find(VueDraggable).vm.$attrs; + const findDesigns = () => wrapper.findAllComponents(Design); + const draggableAttributes = () => wrapper.findComponent(VueDraggable).vm.$attrs; const findDesignUploadButton = () => wrapper.findByTestId('design-upload-button'); const findDesignToolbarWrapper = () => wrapper.findByTestId('design-toolbar-wrapper'); const findDesignUpdateAlert = () => wrapper.findByTestId('design-update-alert'); @@ -120,8 +120,8 @@ describe('Design management index page', () => { async function moveDesigns(localWrapper) { await waitForPromises(); - localWrapper.find(VueDraggable).vm.$emit('input', reorderedDesigns); - localWrapper.find(VueDraggable).vm.$emit('change', { + localWrapper.findComponent(VueDraggable).vm.$emit('input', reorderedDesigns); + localWrapper.findComponent(VueDraggable).vm.$emit('change', { moved: { newIndex: 0, element: designToMove, diff --git a/spec/frontend/design_management/router_spec.js b/spec/frontend/design_management/router_spec.js index b9c62334223..b9edde559c8 100644 --- a/spec/frontend/design_management/router_spec.js +++ b/spec/frontend/design_management/router_spec.js @@ -44,7 +44,7 @@ describe('Design management router', () => { it('pushes home component', () => { const wrapper = factory(routeArg); - expect(wrapper.find(Designs).exists()).toBe(true); + expect(wrapper.findComponent(Designs).exists()).toBe(true); }); }); @@ -55,7 +55,7 @@ describe('Design management router', () => { const wrapper = factory(routeArg); return nextTick().then(() => { - const detail = wrapper.find(DesignDetail); + const detail = wrapper.findComponent(DesignDetail); expect(detail.exists()).toBe(true); expect(detail.props('id')).toEqual('1'); }); diff --git a/spec/frontend/diffs/components/diff_file_header_spec.js b/spec/frontend/diffs/components/diff_file_header_spec.js index d90afeb6b82..92b8b2d4aa3 100644 --- a/spec/frontend/diffs/components/diff_file_header_spec.js +++ b/spec/frontend/diffs/components/diff_file_header_spec.js @@ -263,7 +263,7 @@ describe('DiffFileHeader component', () => { }, }, }); - expect(findModeChangedLine().exists()).toBeFalsy(); + expect(findModeChangedLine().exists()).toBe(false); }, ); diff --git a/spec/frontend/ide/components/shared/commit_message_field_spec.js b/spec/frontend/ide/components/shared/commit_message_field_spec.js index f4f9b95b233..94da06f4cb2 100644 --- a/spec/frontend/ide/components/shared/commit_message_field_spec.js +++ b/spec/frontend/ide/components/shared/commit_message_field_spec.js @@ -79,7 +79,7 @@ describe('CommitMessageField', () => { await fillText(text); expect(findHighlightsText().text()).toEqual(text); - expect(findHighlightsMark().text()).toBeFalsy(); + expect(findHighlightsMark().text()).toBe(''); }); it('highlights characters over 50 length', async () => { diff --git a/spec/frontend/ide/stores/actions/file_spec.js b/spec/frontend/ide/stores/actions/file_spec.js index 6c1dee1e5ca..d1c31cd412b 100644 --- a/spec/frontend/ide/stores/actions/file_spec.js +++ b/spec/frontend/ide/stores/actions/file_spec.js @@ -60,8 +60,8 @@ describe('IDE store file actions', () => { it('closes open files', () => { return store.dispatch('closeFile', localFile).then(() => { - expect(localFile.opened).toBeFalsy(); - expect(localFile.active).toBeFalsy(); + expect(localFile.opened).toBe(false); + expect(localFile.active).toBe(false); expect(store.state.openFiles.length).toBe(0); }); }); @@ -269,7 +269,7 @@ describe('IDE store file actions', () => { it('sets the file as active', () => { return store.dispatch('getFileData', { path: localFile.path }).then(() => { - expect(localFile.active).toBeTruthy(); + expect(localFile.active).toBe(true); }); }); @@ -277,7 +277,7 @@ describe('IDE store file actions', () => { return store .dispatch('getFileData', { path: localFile.path, makeFileActive: false }) .then(() => { - expect(localFile.active).toBeFalsy(); + expect(localFile.active).toBe(false); }); }); diff --git a/spec/frontend/projects/commits/components/author_select_spec.js b/spec/frontend/projects/commits/components/author_select_spec.js index 436262ff461..57e5ef0ed1d 100644 --- a/spec/frontend/projects/commits/components/author_select_spec.js +++ b/spec/frontend/projects/commits/components/author_select_spec.js @@ -71,7 +71,7 @@ describe('Author Select', () => { wrapper.setData({ hasSearchParam: true }); await nextTick(); - expect(findDropdownContainer().attributes('disabled')).toBe(undefined); + expect(findDropdownContainer().attributes('disabled')).toBeUndefined(); }); it('has correct tooltip message', async () => { @@ -91,7 +91,7 @@ describe('Author Select', () => { wrapper.setData({ hasSearchParam: false }); await nextTick(); - expect(findDropdown().attributes('disabled')).toBe(undefined); + expect(findDropdown().attributes('disabled')).toBeUndefined(); }); it('hasSearchParam if user types a truthy string', () => { diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_wip_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_wip_spec.js index e4ffa37a84d..af52901f508 100644 --- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_wip_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_wip_spec.js @@ -26,11 +26,11 @@ describe('Wip', () => { it('should have props', () => { const { mr, service } = WorkInProgress.props; - expect(mr.type instanceof Object).toBeTruthy(); - expect(mr.required).toBeTruthy(); + expect(mr.type instanceof Object).toBe(true); + expect(mr.required).toBe(true); - expect(service.type instanceof Object).toBeTruthy(); - expect(service.required).toBeTruthy(); + expect(service.type instanceof Object).toBe(true); + expect(service.required).toBe(true); }); }); @@ -64,7 +64,7 @@ describe('Wip', () => { await waitForPromises(); - expect(vm.isMakingRequest).toBeTruthy(); + expect(vm.isMakingRequest).toBe(true); expect(eventHub.$emit).toHaveBeenCalledWith('UpdateWidgetData', mrObj); expect(toast).toHaveBeenCalledWith('Marked as ready. Merging is now allowed.'); }); @@ -81,7 +81,7 @@ describe('Wip', () => { }); it('should have correct elements', () => { - expect(el.classList.contains('mr-widget-body')).toBeTruthy(); + expect(el.classList.contains('mr-widget-body')).toBe(true); expect(el.innerText).toContain( "Merge blocked: merge request must be marked as ready. It's still marked as draft.", ); @@ -95,7 +95,7 @@ describe('Wip', () => { await nextTick(); - expect(el.querySelector('.js-remove-draft')).toEqual(null); + expect(el.querySelector('.js-remove-draft')).toBeNull(); }); }); }); diff --git a/spec/frontend/vue_shared/components/confirm_modal_spec.js b/spec/frontend/vue_shared/components/confirm_modal_spec.js index 0af789c78df..c1e682a1aae 100644 --- a/spec/frontend/vue_shared/components/confirm_modal_spec.js +++ b/spec/frontend/vue_shared/components/confirm_modal_spec.js @@ -104,7 +104,7 @@ describe('vue_shared/components/confirm_modal', () => { }); it('renders GlModal with data', () => { - expect(findModal().exists()).toBeTruthy(); + expect(findModal().exists()).toBe(true); expect(findModal().attributes()).toEqual( expect.objectContaining({ oktitle: MOCK_MODAL_DATA.modalAttributes.okTitle, diff --git a/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js b/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js index 112a0e67a0f..7c5fc63856a 100644 --- a/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js +++ b/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js @@ -234,14 +234,14 @@ describe('RunnerInstructionsModal component', () => { MockResizeObserver.mockResize('xs'); await nextTick(); - expect(findPlatformButtonGroup().attributes('vertical')).toBeTruthy(); + expect(findPlatformButtonGroup().attributes('vertical')).toEqual('true'); }); it('to a non-xs viewport', async () => { MockResizeObserver.mockResize('sm'); await nextTick(); - expect(findPlatformButtonGroup().props('vertical')).toBeFalsy(); + expect(findPlatformButtonGroup().props('vertical')).toBeUndefined(); }); }); }); diff --git a/spec/frontend/vue_shared/components/web_ide_link_spec.js b/spec/frontend/vue_shared/components/web_ide_link_spec.js index 41163aef320..a0b868d1d52 100644 --- a/spec/frontend/vue_shared/components/web_ide_link_spec.js +++ b/spec/frontend/vue_shared/components/web_ide_link_spec.js @@ -3,7 +3,7 @@ import { nextTick } from 'vue'; import ActionsButton from '~/vue_shared/components/actions_button.vue'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; -import WebIdeLink from '~/vue_shared/components/web_ide_link.vue'; +import WebIdeLink, { i18n } from '~/vue_shared/components/web_ide_link.vue'; import ConfirmForkModal from '~/vue_shared/components/confirm_fork_modal.vue'; import { stubComponent } from 'helpers/stub_component'; @@ -37,8 +37,8 @@ const ACTION_EDIT_CONFIRM_FORK = { const ACTION_WEB_IDE = { href: TEST_WEB_IDE_URL, key: 'webide', - secondaryText: 'Quickly and easily edit multiple files in your project.', - tooltip: '', + secondaryText: i18n.webIdeText, + tooltip: i18n.webIdeTooltip, text: 'Web IDE', attrs: { 'data-qa-selector': 'web_ide_button', diff --git a/spec/lib/gitlab/auth/auth_finders_spec.rb b/spec/lib/gitlab/auth/auth_finders_spec.rb index e985f66bfe9..d0b44135a2f 100644 --- a/spec/lib/gitlab/auth/auth_finders_spec.rb +++ b/spec/lib/gitlab/auth/auth_finders_spec.rb @@ -127,7 +127,7 @@ RSpec.describe Gitlab::Auth::AuthFinders do let(:doorkeeper_access_token) { Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: 'api') } before do - set_bearer_token(doorkeeper_access_token.token) + set_bearer_token(doorkeeper_access_token.plaintext_token) end it { is_expected.to eq user } @@ -577,7 +577,7 @@ RSpec.describe Gitlab::Auth::AuthFinders do context 'passed as header' do before do - set_bearer_token(doorkeeper_access_token.token) + set_bearer_token(doorkeeper_access_token.plaintext_token) end it 'returns token if valid oauth_access_token' do @@ -587,7 +587,7 @@ RSpec.describe Gitlab::Auth::AuthFinders do context 'passed as param' do it 'returns user if valid oauth_access_token' do - set_param(:access_token, doorkeeper_access_token.token) + set_param(:access_token, doorkeeper_access_token.plaintext_token) expect(find_oauth_access_token.token).to eq doorkeeper_access_token.token end diff --git a/spec/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512_spec.rb b/spec/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512_spec.rb new file mode 100644 index 00000000000..e953733c997 --- /dev/null +++ b/spec/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::DoorkeeperSecretStoring::Pbkdf2Sha512 do + describe '.transform_secret' do + let(:plaintext_token) { 'CzOBzBfU9F-HvsqfTaTXF4ivuuxYZuv3BoAK4pnvmyw' } + + it 'generates a PBKDF2+SHA512 hashed value in the correct format' do + expect(described_class.transform_secret(plaintext_token)) + .to eq("$pbkdf2-sha512$20000$$.c0G5XJVEew1TyeJk5TrkvB0VyOaTmDzPrsdNRED9vVeZlSyuG3G90F0ow23zUCiWKAVwmNnR/ceh.nJG3MdpQ") # rubocop:disable Layout/LineLength + end + + context 'when hash_oauth_tokens is disabled' do + before do + stub_feature_flags(hash_oauth_tokens: false) + end + + it 'returns a plaintext token' do + expect(described_class.transform_secret(plaintext_token)).to eq(plaintext_token) + end + end + end + + describe 'STRETCHES' do + it 'is 20_000' do + expect(described_class::STRETCHES).to eq(20_000) + end + end + + describe 'SALT' do + it 'is empty' do + expect(described_class::SALT).to be_empty + end + end +end diff --git a/spec/models/oauth_access_token_spec.rb b/spec/models/oauth_access_token_spec.rb index 2d617e0c7b3..544f6643712 100644 --- a/spec/models/oauth_access_token_spec.rb +++ b/spec/models/oauth_access_token_spec.rb @@ -22,4 +22,51 @@ RSpec.describe OauthAccessToken do end end end + + describe 'Doorkeeper secret storing' do + it 'stores the token in hashed format' do + expect(token.token).not_to eq(token.plaintext_token) + end + + it 'does not allow falling back to plaintext token comparison' do + expect(described_class.by_token(token.token)).to be_nil + end + + it 'finds a token by plaintext token' do + expect(described_class.by_token(token.plaintext_token)).to be_a(OauthAccessToken) + end + + context 'when the token is stored in plaintext' do + let(:plaintext_token) { Devise.friendly_token(20) } + + before do + token.update_column(:token, plaintext_token) + end + + it 'falls back to plaintext token comparison' do + expect(described_class.by_token(plaintext_token)).to be_a(OauthAccessToken) + end + end + + context 'when hash_oauth_secrets is disabled' do + let(:hashed_token) { create(:oauth_access_token, application_id: app_one.id) } + + before do + hashed_token + stub_feature_flags(hash_oauth_tokens: false) + end + + it 'stores the token in plaintext' do + expect(token.token).to eq(token.plaintext_token) + end + + it 'finds a token by plaintext token' do + expect(described_class.by_token(token.plaintext_token)).to be_a(OauthAccessToken) + end + + it 'does not find a token that was previously stored as hashed' do + expect(described_class.by_token(hashed_token.plaintext_token)).to be_nil + end + end + end end diff --git a/spec/requests/api/doorkeeper_access_spec.rb b/spec/requests/api/doorkeeper_access_spec.rb index 77f1dadff46..14da9a600cd 100644 --- a/spec/requests/api/doorkeeper_access_spec.rb +++ b/spec/requests/api/doorkeeper_access_spec.rb @@ -9,13 +9,13 @@ RSpec.describe 'doorkeeper access' do describe "unauthenticated" do it "returns authentication success" do - get api("/user"), params: { access_token: token.token } + get api("/user"), params: { access_token: token.plaintext_token } expect(response).to have_gitlab_http_status(:ok) end include_examples 'user login request with unique ip limit' do def request - get api('/user'), params: { access_token: token.token } + get api('/user'), params: { access_token: token.plaintext_token } end end end @@ -42,7 +42,7 @@ RSpec.describe 'doorkeeper access' do shared_examples 'forbidden request' do it 'returns 403 response' do - get api("/user"), params: { access_token: token.token } + get api("/user"), params: { access_token: token.plaintext_token } expect(response).to have_gitlab_http_status(:forbidden) end diff --git a/spec/requests/api/go_proxy_spec.rb b/spec/requests/api/go_proxy_spec.rb index 2b1250320ce..7c44fddc303 100644 --- a/spec/requests/api/go_proxy_spec.rb +++ b/spec/requests/api/go_proxy_spec.rb @@ -376,7 +376,7 @@ RSpec.describe API::GoProxy do end it 'returns ok with a job token' do - get_resource(oauth_access_token: job) + get_resource(access_token: job) expect(response).to have_gitlab_http_status(:ok) end @@ -395,7 +395,7 @@ RSpec.describe API::GoProxy do it 'returns unauthorized with a failed job token' do job.update!(status: :failed) - get_resource(oauth_access_token: job) + get_resource(access_token: job) expect(response).to have_gitlab_http_status(:unauthorized) end @@ -445,7 +445,7 @@ RSpec.describe API::GoProxy do end it 'returns not found with a job token' do - get_resource(oauth_access_token: job) + get_resource(access_token: job) expect(response).to have_gitlab_http_status(:not_found) end diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb index c961fd9cfc8..e29e5c31a34 100644 --- a/spec/requests/api/helpers_spec.rb +++ b/spec/requests/api/helpers_spec.rb @@ -539,7 +539,7 @@ RSpec.describe API::Helpers do let(:token) { create(:oauth_access_token) } before do - env['HTTP_AUTHORIZATION'] = "Bearer #{token.token}" + env['HTTP_AUTHORIZATION'] = "Bearer #{token.plaintext_token}" end it_behaves_like 'sudo' diff --git a/spec/requests/api/npm_project_packages_spec.rb b/spec/requests/api/npm_project_packages_spec.rb index 6061b3ba965..3bcffac2760 100644 --- a/spec/requests/api/npm_project_packages_spec.rb +++ b/spec/requests/api/npm_project_packages_spec.rb @@ -59,7 +59,7 @@ RSpec.describe API::NpmProjectPackages do end context 'with access token' do - let(:headers) { build_token_auth_header(token.token) } + let(:headers) { build_token_auth_header(token.plaintext_token) } it_behaves_like 'successfully downloads the file' it_behaves_like 'a package tracking event', 'API::NpmPackages', 'pull_package' @@ -95,7 +95,7 @@ RSpec.describe API::NpmProjectPackages do it_behaves_like 'a package file that requires auth' context 'with guest' do - let(:headers) { build_token_auth_header(token.token) } + let(:headers) { build_token_auth_header(token.plaintext_token) } it 'denies download when not enough permissions' do project.add_guest(user) @@ -356,7 +356,7 @@ RSpec.describe API::NpmProjectPackages do end def upload_with_token(package_name, params = {}) - upload_package(package_name, params.merge(access_token: token.token)) + upload_package(package_name, params.merge(access_token: token.plaintext_token)) end def upload_with_job_token(package_name, params = {}) diff --git a/spec/requests/rack_attack_global_spec.rb b/spec/requests/rack_attack_global_spec.rb index 115f78a5600..f6b9bc527ac 100644 --- a/spec/requests/rack_attack_global_spec.rb +++ b/spec/requests/rack_attack_global_spec.rb @@ -104,8 +104,8 @@ RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_cac end context 'with the token in the OAuth headers' do - let(:request_args) { api_get_args_with_token_headers(api_partial_url, oauth_token_headers(token)) } - let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, oauth_token_headers(other_user_token)) } + let(:request_args) { api_get_args_with_token_headers(api_partial_url, bearer_headers(token)) } + let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, bearer_headers(other_user_token)) } it_behaves_like 'rate-limited user based token-authenticated requests' end @@ -131,8 +131,8 @@ RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_cac end context 'with the token in the OAuth headers' do - let(:request_args) { api_get_args_with_token_headers(api_partial_url, oauth_token_headers(token)) } - let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, oauth_token_headers(other_user_token)) } + let(:request_args) { api_get_args_with_token_headers(api_partial_url, bearer_headers(token)) } + let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, bearer_headers(other_user_token)) } it_behaves_like 'rate-limited user based token-authenticated requests' end @@ -1189,7 +1189,7 @@ RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_cac it 'request is authenticated by token in the OAuth headers' do expect_authenticated_request - get url, headers: oauth_token_headers(personal_access_token) + get url, headers: bearer_headers(personal_access_token) end it 'request is authenticated by token in basic auth' do @@ -1206,7 +1206,7 @@ RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_cac it 'request is authenticated by token in query string' do expect_authenticated_request - get url, params: { access_token: oauth_token.token } + get url, params: { access_token: oauth_token.plaintext_token } end it 'request is authenticated by token in the headers' do diff --git a/spec/support/helpers/api_helpers.rb b/spec/support/helpers/api_helpers.rb index b084508be9a..62bb9576695 100644 --- a/spec/support/helpers/api_helpers.rb +++ b/spec/support/helpers/api_helpers.rb @@ -19,15 +19,17 @@ module ApiHelpers # => "/api/v2/issues?foo=bar&private_token=..." # # Returns the relative path to the requested API resource - def api(path, user = nil, version: API::API.version, personal_access_token: nil, oauth_access_token: nil, job_token: nil) + def api(path, user = nil, version: API::API.version, personal_access_token: nil, oauth_access_token: nil, job_token: nil, access_token: nil) full_path = "/api/#{version}#{path}" if oauth_access_token - query_string = "access_token=#{oauth_access_token.token}" + query_string = "access_token=#{oauth_access_token.plaintext_token}" elsif personal_access_token query_string = "private_token=#{personal_access_token.token}" elsif job_token query_string = "job_token=#{job_token}" + elsif access_token + query_string = "access_token=#{access_token.token}" elsif user personal_access_token = create(:personal_access_token, user: user) query_string = "private_token=#{personal_access_token.token}" diff --git a/spec/support/helpers/rack_attack_spec_helpers.rb b/spec/support/helpers/rack_attack_spec_helpers.rb index 6c06781df03..2502889e17c 100644 --- a/spec/support/helpers/rack_attack_spec_helpers.rb +++ b/spec/support/helpers/rack_attack_spec_helpers.rb @@ -17,8 +17,12 @@ module RackAttackSpecHelpers { Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER => personal_access_token.token } end + def bearer_headers(token) + { 'AUTHORIZATION' => "Bearer #{token.token}" } + end + def oauth_token_headers(oauth_access_token) - { 'AUTHORIZATION' => "Bearer #{oauth_access_token.token}" } + { 'AUTHORIZATION' => "Bearer #{oauth_access_token.plaintext_token}" } end def basic_auth_headers(user, personal_access_token) diff --git a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb index 8d6d85732be..b651ffc8996 100644 --- a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb @@ -244,7 +244,7 @@ RSpec.shared_examples 'handling get metadata requests' do |scope: :project| let(:headers) do case auth when :oauth - build_token_auth_header(token.token) + build_token_auth_header(token.plaintext_token) when :personal_access_token build_token_auth_header(personal_access_token.token) when :job_token @@ -404,7 +404,7 @@ RSpec.shared_examples 'handling get dist tags requests' do |scope: :project| shared_examples 'handling all conditions' do context 'with oauth token' do - let(:headers) { build_token_auth_header(token.token) } + let(:headers) { build_token_auth_header(token.plaintext_token) } it_behaves_like 'handling different package names, visibilities and user roles' end @@ -514,7 +514,7 @@ RSpec.shared_examples 'handling create dist tag requests' do |scope: :project| shared_examples 'handling all conditions' do context 'with oauth token' do - let(:headers) { build_token_auth_header(token.token) } + let(:headers) { build_token_auth_header(token.plaintext_token) } it_behaves_like 'handling different package names, visibilities and user roles' end @@ -622,7 +622,7 @@ RSpec.shared_examples 'handling delete dist tag requests' do |scope: :project| shared_examples 'handling all conditions' do context 'with oauth token' do - let(:headers) { build_token_auth_header(token.token) } + let(:headers) { build_token_auth_header(token.plaintext_token) } it_behaves_like 'handling different package names, visibilities and user roles' end