From cdfb30f76ae7d821711060740705d73ca404bbfe Mon Sep 17 00:00:00 2001 From: Cindy Halim Date: Fri, 21 Jun 2024 16:49:34 -0400 Subject: [PATCH 01/10] Web IDE OAuth Application callout Create callout component and add unit tests. --- .../components/oauth_application_callout.vue | 120 ++++++++++++++++++ .../ide/oauth_application_callout.js | 21 +++ locale/gitlab.pot | 30 +++++ .../oauth_application_callout_spec.js | 56 ++++++++ 4 files changed, 227 insertions(+) create mode 100644 app/assets/javascripts/ide/components/oauth_application_callout.vue create mode 100644 app/assets/javascripts/ide/oauth_application_callout.js create mode 100644 spec/frontend/ide/components/oauth_application_callout_spec.js diff --git a/app/assets/javascripts/ide/components/oauth_application_callout.vue b/app/assets/javascripts/ide/components/oauth_application_callout.vue new file mode 100644 index 00000000000000..76f7db1bb41531 --- /dev/null +++ b/app/assets/javascripts/ide/components/oauth_application_callout.vue @@ -0,0 +1,120 @@ + + diff --git a/app/assets/javascripts/ide/oauth_application_callout.js b/app/assets/javascripts/ide/oauth_application_callout.js new file mode 100644 index 00000000000000..942f66de190c63 --- /dev/null +++ b/app/assets/javascripts/ide/oauth_application_callout.js @@ -0,0 +1,21 @@ +import Vue from 'vue'; +import WebIdeOAuthApplicationCallout from './components/oauth_application_callout.vue'; + +export const initWebIdeOAuthApplicationCallout = () => { + const el = document.querySelector('#web_ide_oauth_application_callout'); + + if (!el) { + return null; + } + + const { resetApplicationSettings } = el.dataset; + + return new Vue({ + el, + name: 'WebIdeOAuthApplicationCallout', + provide: { resetApplicationSettings }, + render(h) { + return h(WebIdeOAuthApplicationCallout); + }, + }); +}; diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 655ee11de12378..2c9e801d9a3d59 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -26513,15 +26513,33 @@ msgstr "" msgid "IDE" msgstr "" +msgid "IDE|%{boldStart}Confidential%{boldEnd} is unchecked." +msgstr "" + +msgid "IDE|%{boldStart}Trusted%{boldEnd} is checked." +msgstr "" + +msgid "IDE|Are you sure you want to restore this application to its original configuration? All your changes will be lost." +msgstr "" + +msgid "IDE|Cancel" +msgstr "" + msgid "IDE|Cannot open Web IDE" msgstr "" +msgid "IDE|Changes to this application configuration can affect the Web IDE's functionality. Ensure that the configuration satisfies these conditions:" +msgstr "" + msgid "IDE|Commit" msgstr "" msgid "IDE|Commit to %{branchName} branch" msgstr "" +msgid "IDE|Confirm" +msgstr "" + msgid "IDE|Contact your administrator or try to open the Web IDE again with another domain." msgstr "" @@ -26546,6 +26564,12 @@ msgstr "" msgid "IDE|Reopen with other domain" msgstr "" +msgid "IDE|Restore application to default" +msgstr "" + +msgid "IDE|Restore to default" +msgstr "" + msgid "IDE|Review" msgstr "" @@ -26555,9 +26579,15 @@ msgstr "" msgid "IDE|Successful commit" msgstr "" +msgid "IDE|The %{boldStart}api%{boldEnd} scope is checked." +msgstr "" + msgid "IDE|The URL you're using to access the Web IDE and the configured OAuth callback URL do not match. This issue often occurs when you're using a proxy." msgstr "" +msgid "IDE|The redirect URI path is: %{codeBlockStart}%{pathFormat}%{codeBlockEnd}. An example of a valid redirect URI: %{codeBlockStart}%{example}%{codeBlockEnd}." +msgstr "" + msgid "IDE|This option is disabled because you are not allowed to create merge requests in this project." msgstr "" diff --git a/spec/frontend/ide/components/oauth_application_callout_spec.js b/spec/frontend/ide/components/oauth_application_callout_spec.js new file mode 100644 index 00000000000000..902b430fddc8c2 --- /dev/null +++ b/spec/frontend/ide/components/oauth_application_callout_spec.js @@ -0,0 +1,56 @@ +import { GlAlert, GlButton, GlModal } from '@gitlab/ui'; +import { mount } from '@vue/test-utils'; +import { nextTick } from 'vue'; +import { useMockLocationHelper } from 'helpers/mock_window_location_helper'; +import WebIdeOAuthApplicationCallout, { + I18N_WEB_IDE_OAUTH_APPLICATION_CALLOUT, +} from '~/ide/components/oauth_application_callout.vue'; + +describe('WebIdeOAuthApplicationCallout', () => { + const mockResetApplicationSettings = jest.fn(); + useMockLocationHelper(); + + let wrapper; + + const findAlert = () => wrapper.findComponent(GlAlert); + const findButton = () => wrapper.findComponent(GlButton); + const findModal = () => wrapper.findComponent(GlModal); + + const createWrapper = () => { + wrapper = mount(WebIdeOAuthApplicationCallout, { + provide: { + resetApplicationSettings: mockResetApplicationSettings, + }, + }); + }; + + beforeEach(() => { + createWrapper(); + }); + + it('renders alert', () => { + expect(findAlert().exists()).toBe(true); + expect(findButton().text()).toBe(I18N_WEB_IDE_OAUTH_APPLICATION_CALLOUT.alertButtonText); + }); + + describe('on restore button click', () => { + beforeEach(async () => { + findButton().vm.$emit('click'); + await nextTick(); + }); + + it('shows modal if restore button is clicked', () => { + const modal = findModal(); + expect(modal.props('modalId')).toBe('restore-to-default-modal'); + expect(modal.props('visible')).toBe(true); + }); + + it('calls reset application settings on modal primary action click', async () => { + findModal().vm.$emit('primary'); + await nextTick(); + + expect(mockResetApplicationSettings).toHaveBeenCalledTimes(1); + expect(window.location.reload).toHaveBeenCalledTimes(1); + }); + }); +}); -- GitLab From 76277eac574a8dd4602c31035747d1ae4d27f510 Mon Sep 17 00:00:00 2001 From: Cindy Halim Date: Fri, 21 Jun 2024 16:53:29 -0400 Subject: [PATCH 02/10] Restore to default application settings logic --- lib/web_ide/default_oauth_application.rb | 25 +++++++++++++------ .../web_ide/default_oauth_application_spec.rb | 20 +++++++++++++++ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/lib/web_ide/default_oauth_application.rb b/lib/web_ide/default_oauth_application.rb index b9108914c9a96f..d7b5a3a3b494b2 100644 --- a/lib/web_ide/default_oauth_application.rb +++ b/lib/web_ide/default_oauth_application.rb @@ -25,6 +25,12 @@ def oauth_application_callback_urls URI.extract(oauth_application.redirect_uri, %w[http https]).uniq end + def reset_oauth_application_settings + return unless oauth_application + + oauth_application.update!(default_settings) + end + def ensure_oauth_application! return if oauth_application @@ -35,17 +41,12 @@ def ensure_oauth_application! # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132496#note_1587293087 application_settings.lock! - # note: `lock!`` breaks applicaiton_settings cache and will trigger another query. + # note: `lock!`` breaks application_settings cache and will trigger another query. # We need to double check here so that requests previously waiting on the lock can # now just skip. next if oauth_application - application = Doorkeeper::Application.new( - name: 'GitLab Web IDE', - redirect_uri: oauth_callback_url, - scopes: ['api'], - trusted: true, - confidential: false) + application = Doorkeeper::Application.new(default_settings) application.save! application_settings.update!(web_ide_oauth_application: application) should_expire_cache = true @@ -60,6 +61,16 @@ def ensure_oauth_application! def application_settings ::Gitlab::CurrentSettings.current_application_settings end + + def default_settings + { + "name" => 'GitLab Web IDE', + "redirect_uri" => oauth_callback_url, + "scopes" => ['api'], + "trusted" => true, + "confidential" => false + }.freeze + end end end end diff --git a/spec/lib/web_ide/default_oauth_application_spec.rb b/spec/lib/web_ide/default_oauth_application_spec.rb index 4f051ffaafbedc..dc2359e771da8d 100644 --- a/spec/lib/web_ide/default_oauth_application_spec.rb +++ b/spec/lib/web_ide/default_oauth_application_spec.rb @@ -110,6 +110,26 @@ end end + describe '#reset_oauth_application_settings' do + it 'resets oauth application settings to original' do + mock_bad_oauth_application = oauth_application + mock_bad_oauth_application["confidential"] = true + mock_bad_oauth_application["trusted"] = false + + stub_application_setting({ web_ide_oauth_application: mock_bad_oauth_application }) + + described_class.reset_oauth_application_settings + + expect(oauth_application).to have_attributes( + name: 'GitLab Web IDE', + redirect_uri: described_class.oauth_callback_url, + scopes: ['api'], + trusted: true, + confidential: false + ) + end + end + def application_settings ::Gitlab::CurrentSettings.current_application_settings end -- GitLab From ebe5842e51de524c08699feb549438aedf5c027f Mon Sep 17 00:00:00 2001 From: Cindy Halim Date: Fri, 21 Jun 2024 16:54:17 -0400 Subject: [PATCH 03/10] Render callout on edit Web IDE OAuth application page --- app/assets/javascripts/pages/admin/applications/index.js | 2 ++ app/helpers/ide_helper.rb | 4 ++++ .../applications/_web_ide_oauth_application_callout.html.haml | 3 +++ app/views/admin/applications/edit.html.haml | 2 ++ 4 files changed, 11 insertions(+) create mode 100644 app/views/admin/applications/_web_ide_oauth_application_callout.html.haml diff --git a/app/assets/javascripts/pages/admin/applications/index.js b/app/assets/javascripts/pages/admin/applications/index.js index df9e38431b06a9..3d52ea1530a45f 100644 --- a/app/assets/javascripts/pages/admin/applications/index.js +++ b/app/assets/javascripts/pages/admin/applications/index.js @@ -1,5 +1,7 @@ import initApplicationDeleteButtons from '~/admin/applications'; import { initOAuthApplicationSecret } from '~/oauth_application'; +import { initWebIdeOAuthApplicationCallout } from '~/ide/oauth_application_callout'; initApplicationDeleteButtons(); initOAuthApplicationSecret(); +initWebIdeOAuthApplicationCallout(); diff --git a/app/helpers/ide_helper.rb b/app/helpers/ide_helper.rb index e36dc3c75c6657..e49d766f5b41b5 100644 --- a/app/helpers/ide_helper.rb +++ b/app/helpers/ide_helper.rb @@ -33,6 +33,10 @@ def web_ide_oauth_application_id ::WebIde::DefaultOauthApplication.oauth_application_id end + def reset_web_ide_oauth_application_settings + ::Gitlab::WebIde::DefaultOauthApplication.reset_oauth_application_settings + end + def use_new_web_ide? Feature.enabled?(:vscode_web_ide, current_user) end diff --git a/app/views/admin/applications/_web_ide_oauth_application_callout.html.haml b/app/views/admin/applications/_web_ide_oauth_application_callout.html.haml new file mode 100644 index 00000000000000..a652b43b6f7440 --- /dev/null +++ b/app/views/admin/applications/_web_ide_oauth_application_callout.html.haml @@ -0,0 +1,3 @@ +- return unless web_ide_oauth_application_id == application.id + +#web_ide_oauth_application_callout{ :data => {'reset_application_settings' => reset_web_ide_oauth_application_settings.to_s} } diff --git a/app/views/admin/applications/edit.html.haml b/app/views/admin/applications/edit.html.haml index 10a27fb906fd02..d0ecba3f977c82 100644 --- a/app/views/admin/applications/edit.html.haml +++ b/app/views/admin/applications/edit.html.haml @@ -2,6 +2,8 @@ - breadcrumb_title @application.name - page_title _("Edit"), @application.name, _("Applications") += render 'web_ide_oauth_application_callout', application: @application + %h1.page-title.gl-font-size-h-display = _('Edit application') - @url = admin_application_path(@application) -- GitLab From 2f3df31d0e8338d4b3cc3ba7dcf50e13abc949d9 Mon Sep 17 00:00:00 2001 From: Cindy Halim Date: Tue, 25 Jun 2024 13:34:36 +0000 Subject: [PATCH 04/10] Update copy based on suggestions --- .../components/oauth_application_callout.vue | 10 ++++----- locale/gitlab.pot | 22 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/ide/components/oauth_application_callout.vue b/app/assets/javascripts/ide/components/oauth_application_callout.vue index 76f7db1bb41531..6e011d5176fd49 100644 --- a/app/assets/javascripts/ide/components/oauth_application_callout.vue +++ b/app/assets/javascripts/ide/components/oauth_application_callout.vue @@ -4,16 +4,16 @@ import { s__ } from '~/locale'; export const I18N_WEB_IDE_OAUTH_APPLICATION_CALLOUT = { alertTitle: s__( - "IDE|Changes to this application configuration can affect the Web IDE's functionality. Ensure that the configuration satisfies these conditions:", + 'IDE|Editing this application might affect the functionality of the Web IDE. Ensure the configuration meets the following conditions:', ), alertButtonText: s__('IDE|Restore to default'), configurations: [ s__( - 'IDE|The redirect URI path is: %{codeBlockStart}%{pathFormat}%{codeBlockEnd}. An example of a valid redirect URI: %{codeBlockStart}%{example}%{codeBlockEnd}.', + 'IDE|The redirect URI path is %{codeBlockStart}%{pathFormat}%{codeBlockEnd}. An example of a valid redirect URI is %{codeBlockStart}%{example}%{codeBlockEnd}.', ), - s__('IDE|%{boldStart}Trusted%{boldEnd} is checked.'), - s__('IDE|%{boldStart}Confidential%{boldEnd} is unchecked.'), - s__('IDE|The %{boldStart}api%{boldEnd} scope is checked.'), + s__('IDE|The %{boldStart}Trusted%{boldEnd} checkbox is selected.'), + s__('IDE|The %{boldStart}Confidential%{boldEnd} checkbox is cleared.'), + s__('IDE|The %{boldStart}api%{boldEnd} scope is selected.'), ], modalTitle: s__('IDE|Restore application to default'), modalBody: s__( diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 2c9e801d9a3d59..3da525451970f8 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -26513,12 +26513,6 @@ msgstr "" msgid "IDE" msgstr "" -msgid "IDE|%{boldStart}Confidential%{boldEnd} is unchecked." -msgstr "" - -msgid "IDE|%{boldStart}Trusted%{boldEnd} is checked." -msgstr "" - msgid "IDE|Are you sure you want to restore this application to its original configuration? All your changes will be lost." msgstr "" @@ -26528,9 +26522,6 @@ msgstr "" msgid "IDE|Cannot open Web IDE" msgstr "" -msgid "IDE|Changes to this application configuration can affect the Web IDE's functionality. Ensure that the configuration satisfies these conditions:" -msgstr "" - msgid "IDE|Commit" msgstr "" @@ -26546,6 +26537,9 @@ msgstr "" msgid "IDE|Edit" msgstr "" +msgid "IDE|Editing this application might affect the functionality of the Web IDE. Ensure the configuration meets the following conditions:" +msgstr "" + msgid "IDE|Error reloading page" msgstr "" @@ -26579,13 +26573,19 @@ msgstr "" msgid "IDE|Successful commit" msgstr "" -msgid "IDE|The %{boldStart}api%{boldEnd} scope is checked." +msgid "IDE|The %{boldStart}Confidential%{boldEnd} checkbox is cleared." +msgstr "" + +msgid "IDE|The %{boldStart}Trusted%{boldEnd} checkbox is selected." +msgstr "" + +msgid "IDE|The %{boldStart}api%{boldEnd} scope is selected." msgstr "" msgid "IDE|The URL you're using to access the Web IDE and the configured OAuth callback URL do not match. This issue often occurs when you're using a proxy." msgstr "" -msgid "IDE|The redirect URI path is: %{codeBlockStart}%{pathFormat}%{codeBlockEnd}. An example of a valid redirect URI: %{codeBlockStart}%{example}%{codeBlockEnd}." +msgid "IDE|The redirect URI path is %{codeBlockStart}%{pathFormat}%{codeBlockEnd}. An example of a valid redirect URI is %{codeBlockStart}%{example}%{codeBlockEnd}." msgstr "" msgid "IDE|This option is disabled because you are not allowed to create merge requests in this project." -- GitLab From e14e82863e65b72bccdabf830460ebd405085245 Mon Sep 17 00:00:00 2001 From: Cindy Halim Date: Wed, 26 Jun 2024 10:49:15 -0400 Subject: [PATCH 05/10] Add reset_web_ide_oauth_application_settings unit test --- spec/helpers/ide_helper_spec.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spec/helpers/ide_helper_spec.rb b/spec/helpers/ide_helper_spec.rb index 59671f02e86bad..c12d0f6de94b15 100644 --- a/spec/helpers/ide_helper_spec.rb +++ b/spec/helpers/ide_helper_spec.rb @@ -180,4 +180,14 @@ expect(helper.web_ide_oauth_application_id).to eq(oauth_application.id) end end + + describe '#reset_web_ide_oauth_application_settings' do + let_it_be(:oauth_application) { create(:oauth_application, owner: nil) } + + it 'resets Web IDE OAuth application settings' do + stub_application_setting({ web_ide_oauth_application: oauth_application }) + helper.reset_web_ide_oauth_application_settings + expect(oauth_application.name).to eq('GitLab Web IDE') + end + end end -- GitLab From 8ad179223774c6d326c2c4f01237637b5721047b Mon Sep 17 00:00:00 2001 From: Cindy Halim Date: Wed, 3 Jul 2024 09:15:14 -0400 Subject: [PATCH 06/10] Address MR feedback --- .../javascripts/ide/components/oauth_application_callout.vue | 2 +- spec/helpers/ide_helper_spec.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/ide/components/oauth_application_callout.vue b/app/assets/javascripts/ide/components/oauth_application_callout.vue index 6e011d5176fd49..213614c8e444ff 100644 --- a/app/assets/javascripts/ide/components/oauth_application_callout.vue +++ b/app/assets/javascripts/ide/components/oauth_application_callout.vue @@ -73,7 +73,7 @@ export default { } }, }, - redirectUrlPath: '/-/ide/oauth_redirect/', + redirectUrlPath: '/-/ide/oauth_redirect', urlOrigin: window.location.origin, i18n: I18N_WEB_IDE_OAUTH_APPLICATION_CALLOUT, }; diff --git a/spec/helpers/ide_helper_spec.rb b/spec/helpers/ide_helper_spec.rb index c12d0f6de94b15..1d70d4947b3f4b 100644 --- a/spec/helpers/ide_helper_spec.rb +++ b/spec/helpers/ide_helper_spec.rb @@ -186,6 +186,7 @@ it 'resets Web IDE OAuth application settings' do stub_application_setting({ web_ide_oauth_application: oauth_application }) + expect(oauth_application.name).not_to eq('GitLab Web IDE') helper.reset_web_ide_oauth_application_settings expect(oauth_application.name).to eq('GitLab Web IDE') end -- GitLab From d0172d7a3160152c110455edecac701b251971ab Mon Sep 17 00:00:00 2001 From: Cindy Halim Date: Thu, 11 Jul 2024 18:15:47 -0400 Subject: [PATCH 07/10] Create reset web IDE oauth application route --- app/controllers/ide_controller.rb | 10 +++++++ config/routes.rb | 2 ++ spec/requests/ide_controller_spec.rb | 45 ++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb index 1861f0692d81fc..f5372572750fb4 100644 --- a/app/controllers/ide_controller.rb +++ b/app/controllers/ide_controller.rb @@ -36,6 +36,16 @@ def oauth_redirect render layout: 'fullscreen', locals: { minimal: true } end + def reset_oauth_application_settings + return render json: {}, status: :forbidden unless current_user.can_admin_all_resources? + + success = ::Gitlab::WebIde::DefaultOauthApplication.reset_oauth_application_settings + + return render json: {}, status: :bad_request unless success + + render json: {} + end + private def authorize_read_project! diff --git a/config/routes.rb b/config/routes.rb index 868934605a9e28..a6e0efc56fa3bd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -164,6 +164,8 @@ as: :remote, to: 'web_ide/remote_ide#index', constraints: { remote_host: %r{[^/?]+} } + + post '/reset_oauth_application_settings' => 'ide#reset_oauth_application_settings' end draw :operations diff --git a/spec/requests/ide_controller_spec.rb b/spec/requests/ide_controller_spec.rb index 925484810e9c24..dd7e5fb13aa2d0 100644 --- a/spec/requests/ide_controller_spec.rb +++ b/spec/requests/ide_controller_spec.rb @@ -275,6 +275,51 @@ end end + describe "#reset_oauth_application_settings" do + subject(:reset_oauth_application_settings) { post '/-/ide/reset_oauth_application_settings' } + + before do + stub_feature_flags(web_ide_oauth: true) + end + + context "with admin user" do + let(:admin) { create(:admin) } + + before do + allow(admin).to receive(:can_admin_all_resources?).and_return(true) + sign_in(admin) + end + + it 'returns 400 if no oauth application exists' do + stub_application_setting(web_ide_oauth_application: nil) + reset_oauth_application_settings + + expect(response).to have_gitlab_http_status(:bad_request) + end + + it 'returns 200' do + stub_application_setting({ + web_ide_oauth_application: create(:oauth_application, owner_id: nil, owner_type: nil) + }) + + reset_oauth_application_settings + + expect(response).to have_gitlab_http_status(:ok) + end + end + + context "with non-admin user" do + before do + sign_in(user) + end + + it('returns 403 if user is not admin') do + reset_oauth_application_settings + expect(response).to have_gitlab_http_status(:forbidden) + end + end + end + def web_ide_oauth_application ::Gitlab::CurrentSettings.current_application_settings.web_ide_oauth_application end -- GitLab From 9923cb4f01ffb73b4936b05ab67e6bb312b829e7 Mon Sep 17 00:00:00 2001 From: Cindy Halim Date: Thu, 11 Jul 2024 18:18:54 -0400 Subject: [PATCH 08/10] Update restore to default logic in callout Extract restore to default confirmation modal to separate component. Use new reset Web IDE OAuth application settings endpoint to restore to default settings. --- .../components/oauth_application_callout.vue | 65 +++------- .../reset_application_settings_modal.vue | 103 +++++++++++++++ .../ide/oauth_application_callout.js | 3 - app/helpers/ide_helper.rb | 4 - ...eb_ide_oauth_application_callout.html.haml | 2 +- locale/gitlab.pot | 3 + .../oauth_application_callout_spec.js | 49 +++----- .../reset_application_settings_modal_spec.js | 118 ++++++++++++++++++ spec/helpers/ide_helper_spec.rb | 11 -- 9 files changed, 259 insertions(+), 99 deletions(-) create mode 100644 app/assets/javascripts/ide/components/reset_application_settings_modal.vue create mode 100644 spec/frontend/ide/components/reset_application_settings_modal_spec.js diff --git a/app/assets/javascripts/ide/components/oauth_application_callout.vue b/app/assets/javascripts/ide/components/oauth_application_callout.vue index 213614c8e444ff..6ee9882ac592e7 100644 --- a/app/assets/javascripts/ide/components/oauth_application_callout.vue +++ b/app/assets/javascripts/ide/components/oauth_application_callout.vue @@ -1,6 +1,8 @@