From 4a2df25dfebc602c63baea38af68c3e75e2cb5ec Mon Sep 17 00:00:00 2001 From: Steve Abrams <sabrams@gitlab.com> Date: Fri, 18 Sep 2020 07:49:58 +0000 Subject: [PATCH] Add form for package limits to admin UI Form for package max file size plan_limits is added to the package registry application settings admin UI page. --- .../admin/application_settings_controller.rb | 1 + .../admin/plan_limits_controller.rb | 39 +++++++++++ .../_package_registry.html.haml | 50 ++++++++++++++ .../240951-package-size-limits-ui.yml | 5 ++ config/routes/admin.rb | 2 + .../settings/continuous_integration.md | 16 ++++- .../_ee_package_registry.haml | 14 ++++ .../_package_registry.haml | 23 ------- locale/gitlab.pot | 30 +++++++++ .../admin/plan_limits_controller_spec.rb | 45 +++++++++++++ spec/factories/plan_limits.rb | 9 +++ spec/routing/admin_routing_spec.rb | 6 ++ .../_package_registry.html.haml_spec.rb | 65 +++++++++++++++++++ 13 files changed, 281 insertions(+), 24 deletions(-) create mode 100644 app/controllers/admin/plan_limits_controller.rb create mode 100644 app/views/admin/application_settings/_package_registry.html.haml create mode 100644 changelogs/unreleased/240951-package-size-limits-ui.yml create mode 100644 ee/app/views/admin/application_settings/_ee_package_registry.haml delete mode 100644 ee/app/views/admin/application_settings/_package_registry.haml create mode 100644 spec/controllers/admin/plan_limits_controller_spec.rb create mode 100644 spec/views/admin/application_settings/_package_registry.html.haml_spec.rb diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index fc3d0053859e9..73f71f7ad5592 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -170,6 +170,7 @@ def self_monitoring_data def set_application_setting @application_setting = ApplicationSetting.current_without_cache + @plans = Plan.all end def whitelist_query_limiting diff --git a/app/controllers/admin/plan_limits_controller.rb b/app/controllers/admin/plan_limits_controller.rb new file mode 100644 index 0000000000000..2620db8aec5ef --- /dev/null +++ b/app/controllers/admin/plan_limits_controller.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +class Admin::PlanLimitsController < Admin::ApplicationController + include InternalRedirect + + before_action :set_plan_limits + + def create + redirect_path = referer_path(request) || general_admin_application_settings_path + + respond_to do |format| + if @plan_limits.update(plan_limits_params) + format.json { head :ok } + format.html { redirect_to redirect_path, notice: _('Application limits saved successfully') } + else + format.json { head :bad_request } + format.html { render_update_error } + end + end + end + + private + + def set_plan_limits + @plan_limits = Plan.find(plan_limits_params[:plan_id]).actual_limits + end + + def plan_limits_params + params.require(:plan_limits).permit(%i[ + plan_id + conan_max_file_size + maven_max_file_size + npm_max_file_size + nuget_max_file_size + pypi_max_file_size + generic_packages_max_file_size + ]) + end +end diff --git a/app/views/admin/application_settings/_package_registry.html.haml b/app/views/admin/application_settings/_package_registry.html.haml new file mode 100644 index 0000000000000..257a90252ccdf --- /dev/null +++ b/app/views/admin/application_settings/_package_registry.html.haml @@ -0,0 +1,50 @@ +- if Gitlab.config.packages.enabled + %section.settings.as-package.no-animate#js-package-settings{ class: ('expanded' if expanded_by_default?) } + .settings-header + %h4 + = _('Package Registry') + %button.btn.btn-default.js-settings-toggle{ type: 'button' } + = expanded_by_default? ? _('Collapse') : _('Expand') + %p + = _("Settings related to the use and experience of using GitLab's Package Registry.") + + = render_if_exists 'admin/application_settings/ee_package_registry' + + .settings-content + %h4 + = _('Package file size limits') + %p + = _('Set limit to 0 to allow any file size.') + .scrolling-tabs-container.inner-page-scroll-tabs + - if @plans.size > 1 + %ul.nav-links.scrolling-tabs.mobile-separator.nav.nav-tabs.mb-3 + - @plans.each_with_index do |plan, index| + %li + = link_to admin_plan_limits_path(anchor: 'js-package-settings'), data: { target: "div#plan#{index}", action: "plan#{index}", toggle: 'tab'}, class: index == 0 ? 'active': '' do + = plan.name.capitalize + .tab-content + - @plans.each_with_index do |plan, index| + .tab-pane{ :id => "plan#{index}", class: index == 0 ? 'active': '' } + = form_for plan.actual_limits, url: admin_plan_limits_path(anchor: 'js-package-settings'), html: { class: 'fieldset-form' }, method: :post do |f| + = form_errors(plan) + %fieldset + = f.hidden_field(:plan_id, value: plan.id) + .form-group + = f.label :conan_max_file_size, _('Maximum Conan package file size in bytes'), class: 'label-bold' + = f.number_field :conan_max_file_size, class: 'form-control' + .form-group + = f.label :maven_max_file_size, _('Maximum Maven package file size in bytes'), class: 'label-bold' + = f.number_field :maven_max_file_size, class: 'form-control' + .form-group + = f.label :npm_max_file_size, _('Maximum NPM package file size in bytes'), class: 'label-bold' + = f.number_field :npm_max_file_size, class: 'form-control' + .form-group + = f.label :nuget_max_file_size, _('Maximum NuGet package file size in bytes'), class: 'label-bold' + = f.number_field :nuget_max_file_size, class: 'form-control' + .form-group + = f.label :pypi_max_file_size, _('Maximum PyPI package file size in bytes'), class: 'label-bold' + = f.number_field :pypi_max_file_size, class: 'form-control' + .form-group + = f.label :generic_packages_max_file_size, _('Generic package file size in bytes'), class: 'label-bold' + = f.number_field :generic_packages_max_file_size, class: 'form-control' + = f.submit _('Save %{name} size limits').html_safe % { name: plan.name.capitalize }, class: 'btn gl-button btn-success' diff --git a/changelogs/unreleased/240951-package-size-limits-ui.yml b/changelogs/unreleased/240951-package-size-limits-ui.yml new file mode 100644 index 0000000000000..5f98146bb889a --- /dev/null +++ b/changelogs/unreleased/240951-package-size-limits-ui.yml @@ -0,0 +1,5 @@ +--- +title: Add admin UI for adjusting package file size limits +merge_request: 40423 +author: +type: added diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 1dd1149a9d2d2..bac8247de2e8a 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -141,6 +141,8 @@ get :status_delete_self_monitoring_project end + resources :plan_limits, only: :create + resources :labels resources :runners, only: [:index, :show, :update, :destroy] do diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md index 607678bfe0eec..b4867d33644cf 100644 --- a/doc/user/admin_area/settings/continuous_integration.md +++ b/doc/user/admin_area/settings/continuous_integration.md @@ -196,7 +196,9 @@ To set required pipeline configuration:  -## Package Registry configuration **(PREMIUM ONLY)** +## Package Registry configuration + +### NPM Forwarding **(PREMIUM ONLY)** GitLab administrators can disable the forwarding of NPM requests to [npmjs.com](https://www.npmjs.com/). @@ -208,3 +210,15 @@ To disable it: 1. Click **Save changes**.  + +### Package file size limits + +GitLab administrators can adjust the maximum allowed file size for each package type. + +To set the maximum file size: + +1. Go to **Admin Area > Settings > CI/CD**. +1. Expand the **Package Registry** section. +1. Find the package type you would like to adjust. +1. Enter the maximum file size, in bytes. +1. Click **Save size limits**. diff --git a/ee/app/views/admin/application_settings/_ee_package_registry.haml b/ee/app/views/admin/application_settings/_ee_package_registry.haml new file mode 100644 index 0000000000000..ec2c96ef9b769 --- /dev/null +++ b/ee/app/views/admin/application_settings/_ee_package_registry.haml @@ -0,0 +1,14 @@ +.settings-content + = form_for @application_setting, url: ci_cd_admin_application_settings_path(anchor: 'js-package-settings'), html: { class: 'fieldset-form' } do |f| + = form_errors(@application_setting) + + %fieldset + .form-group + .form-check + = f.check_box :npm_package_requests_forwarding, class: 'form-check-input' + = f.label :npm_package_requests_forwarding, class: 'form-check-label' do + Enable forwarding of NPM package requests to npmjs.org + .form-text.text-muted + = _("When enabled, if an NPM package isn't found in the GitLab Registry, we will attempt to pull from the global NPM registry.") + + = f.submit _('Save changes'), class: 'btn gl-button btn-success' diff --git a/ee/app/views/admin/application_settings/_package_registry.haml b/ee/app/views/admin/application_settings/_package_registry.haml deleted file mode 100644 index 1768a6626b82a..0000000000000 --- a/ee/app/views/admin/application_settings/_package_registry.haml +++ /dev/null @@ -1,23 +0,0 @@ -- if Gitlab.config.packages.enabled - %section.settings.as-package.no-animate#js-package-settings{ class: ('expanded' if expanded_by_default?) } - .settings-header - %h4 - = _('Package Registry') - %button.btn.btn-default.js-settings-toggle{ type: 'button' } - = expanded_by_default? ? _('Collapse') : _('Expand') - %p - = _("Settings related to the use and experience of using GitLab's Package Registry.") - .settings-content - = form_for @application_setting, url: ci_cd_admin_application_settings_path(anchor: 'js-package-settings'), html: { class: 'fieldset-form' } do |f| - = form_errors(@application_setting) - - %fieldset - .form-group - .form-check - = f.check_box :npm_package_requests_forwarding, class: 'form-check-input' - = f.label :npm_package_requests_forwarding, class: 'form-check-label' do - Enable forwarding of NPM package requests to npmjs.org - .form-text.text-muted - = _("When enabled, if an NPM package isn't found in the GitLab Registry, we will attempt to pull from the global NPM registry.") - - = f.submit _('Save changes'), class: "btn btn-success" diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 3f4b80959970e..249b2a9e67177 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3101,6 +3101,9 @@ msgstr "" msgid "Application ID" msgstr "" +msgid "Application limits saved successfully" +msgstr "" + msgid "Application settings saved successfully" msgstr "" @@ -11378,6 +11381,9 @@ msgstr "" msgid "Generate new token" msgstr "" +msgid "Generic package file size in bytes" +msgstr "" + msgid "Geo" msgstr "" @@ -15403,6 +15409,21 @@ msgstr "" msgid "Max size 15 MB" msgstr "" +msgid "Maximum Conan package file size in bytes" +msgstr "" + +msgid "Maximum Maven package file size in bytes" +msgstr "" + +msgid "Maximum NPM package file size in bytes" +msgstr "" + +msgid "Maximum NuGet package file size in bytes" +msgstr "" + +msgid "Maximum PyPI package file size in bytes" +msgstr "" + msgid "Maximum Users:" msgstr "" @@ -17862,6 +17883,9 @@ msgstr "" msgid "Package deleted successfully" msgstr "" +msgid "Package file size limits" +msgstr "" + msgid "Package recipe already exists" msgstr "" @@ -21987,6 +22011,9 @@ msgstr "" msgid "Save" msgstr "" +msgid "Save %{name} size limits" +msgstr "" + msgid "Save Changes" msgstr "" @@ -23048,6 +23075,9 @@ msgstr "" msgid "Set iteration" msgstr "" +msgid "Set limit to 0 to allow any file size." +msgstr "" + msgid "Set max session time for web terminal." msgstr "" diff --git a/spec/controllers/admin/plan_limits_controller_spec.rb b/spec/controllers/admin/plan_limits_controller_spec.rb new file mode 100644 index 0000000000000..2666925c2b7d4 --- /dev/null +++ b/spec/controllers/admin/plan_limits_controller_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Admin::PlanLimitsController do + let_it_be(:plan) { create(:plan) } + let_it_be(:plan_limits) { create(:plan_limits, plan: plan) } + + describe 'POST create' do + let(:params) do + { + plan_limits: { + plan_id: plan.id, + conan_max_file_size: file_size, id: plan_limits.id + } + } + end + + context 'with an authenticated admin user' do + let(:file_size) { 10.megabytes } + + it 'updates the plan limits', :aggregate_failures do + sign_in(create(:admin)) + + post :create, params: params + + expect(response).to redirect_to(general_admin_application_settings_path) + expect(plan_limits.reload.conan_max_file_size).to eq(file_size) + end + end + + context 'without admin access' do + let(:file_size) { 1.megabytes } + + it 'returns `not_found`' do + sign_in(create(:user)) + + post :create, params: params + + expect(response).to have_gitlab_http_status(:not_found) + expect(plan_limits.conan_max_file_size).not_to eq(file_size) + end + end + end +end diff --git a/spec/factories/plan_limits.rb b/spec/factories/plan_limits.rb index 4aea09618d0c3..ae892307193eb 100644 --- a/spec/factories/plan_limits.rb +++ b/spec/factories/plan_limits.rb @@ -7,5 +7,14 @@ trait :default_plan do plan factory: :default_plan end + + trait :with_package_file_sizes do + conan_max_file_size { 100 } + maven_max_file_size { 100 } + npm_max_file_size { 100 } + nuget_max_file_size { 100 } + pypi_max_file_size { 100 } + generic_packages_max_file_size { 100 } + end end end diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb index 396b01edbfa47..fedafff0d1b14 100644 --- a/spec/routing/admin_routing_spec.rb +++ b/spec/routing/admin_routing_spec.rb @@ -178,3 +178,9 @@ expect(post("/admin/session/destroy")).to route_to('admin/sessions#destroy') end end + +RSpec.describe Admin::PlanLimitsController, "routing" do + it "to #create" do + expect(post("/admin/plan_limits")).to route_to('admin/plan_limits#create') + end +end diff --git a/spec/views/admin/application_settings/_package_registry.html.haml_spec.rb b/spec/views/admin/application_settings/_package_registry.html.haml_spec.rb new file mode 100644 index 0000000000000..ef40829c29b19 --- /dev/null +++ b/spec/views/admin/application_settings/_package_registry.html.haml_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'admin/application_settings/_package_registry' do + let_it_be(:admin) { create(:admin) } + let_it_be(:default_plan_limits) { create(:plan_limits, :default_plan, :with_package_file_sizes) } + let_it_be(:application_setting) { build(:application_setting) } + let(:page) { Capybara::Node::Simple.new(rendered) } + + before do + assign(:application_setting, application_setting) + allow(view).to receive(:current_user) { admin } + allow(view).to receive(:expanded) { true } + end + + subject { render partial: 'admin/application_settings/package_registry' } + + context 'package file size limits' do + before do + assign(:plans, [default_plan_limits.plan]) + end + + it 'has fields for max package file sizes' do + subject + + expect(rendered).to have_field('Maximum Conan package file size in bytes', type: 'number') + expect(page.find_field('Maximum Conan package file size in bytes').value).to eq(default_plan_limits.conan_max_file_size.to_s) + + expect(rendered).to have_field('Maximum Maven package file size in bytes', type: 'number') + expect(page.find_field('Maximum Maven package file size in bytes').value).to eq(default_plan_limits.maven_max_file_size.to_s) + + expect(rendered).to have_field('Maximum NPM package file size in bytes', type: 'number') + expect(page.find_field('Maximum NPM package file size in bytes').value).to eq(default_plan_limits.npm_max_file_size.to_s) + + expect(rendered).to have_field('Maximum NuGet package file size in bytes', type: 'number') + expect(page.find_field('Maximum NuGet package file size in bytes').value).to eq(default_plan_limits.nuget_max_file_size.to_s) + + expect(rendered).to have_field('Maximum PyPI package file size in bytes', type: 'number') + expect(page.find_field('Maximum PyPI package file size in bytes').value).to eq(default_plan_limits.pypi_max_file_size.to_s) + end + + it 'does not display the plan name when there is only one plan' do + subject + + expect(page).not_to have_content('Default') + end + end + + context 'with multiple plans' do + let_it_be(:plan) { create(:plan, name: 'Gold') } + let_it_be(:gold_plan_limits) { create(:plan_limits, :with_package_file_sizes, plan: plan) } + + before do + assign(:plans, [default_plan_limits.plan, gold_plan_limits.plan]) + end + + it 'displays the plan name when there is more than one plan' do + subject + + expect(page).to have_content('Default') + expect(page).to have_content('Gold') + end + end +end -- GitLab