diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index e93f34498d6..1d24563a6a6 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -7,6 +7,7 @@ class ProjectsController < Projects::ApplicationController before_action :repository, except: [:index, :new, :create] before_action :assign_ref_vars, only: [:show], if: :repo_exists? before_action :tree, only: [:show], if: [:repo_exists?, :project_view_files?] + before_action :project_export_enabled, only: [:export, :download_export, :remove_export, :generate_new_export] # Authorize before_action :authorize_admin_project!, only: [:edit, :update, :housekeeping, :download_export, :export, :remove_export, :generate_new_export] @@ -390,4 +391,8 @@ class ProjectsController < Projects::ApplicationController url_for(params) end + + def project_export_enabled + render_404 unless current_application_settings.project_export_enabled? + end end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 6825adcb39f..150188f0b65 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -146,6 +146,7 @@ module ApplicationSettingsHelper :plantuml_enabled, :plantuml_url, :polling_interval_multiplier, + :project_export_enabled, :prometheus_metrics_enabled, :recaptcha_enabled, :recaptcha_private_key, diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index bd7c4cd45ea..8e446ff6dd8 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -241,6 +241,7 @@ class ApplicationSetting < ActiveRecord::Base performance_bar_allowed_group_id: nil, plantuml_enabled: false, plantuml_url: nil, + project_export_enabled: true, recaptcha_enabled: false, repository_checks_enabled: true, repository_storages: ['default'], diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index a4f49d3f6d7..8bf6556079b 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -48,6 +48,12 @@ = select(:application_setting, :enabled_git_access_protocol, [['Both SSH and HTTP(S)', nil], ['Only SSH', 'ssh'], ['Only HTTP(S)', 'http']], {}, class: 'form-control') %span.help-block#clone-protocol-help Allow only the selected protocols to be used for Git access. + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :project_export_enabled do + = f.check_box :project_export_enabled + Project export enabled %fieldset %legend Account and Limit Settings diff --git a/app/views/projects/_export.html.haml b/app/views/projects/_export.html.haml new file mode 100644 index 00000000000..623d3bc91c6 --- /dev/null +++ b/app/views/projects/_export.html.haml @@ -0,0 +1,41 @@ +- return unless current_application_settings.project_export_enabled? + +- project = local_assigns.fetch(:project) +- expanded = Rails.env.test? + +%section.settings + .settings-header + %h4 + Export project + %button.btn.js-settings-toggle + = expanded ? 'Collapse' : 'Expand' + %p + Export this project with all its related data in order to move your project to a new GitLab instance. Once the export is finished, you can import the file from the "New Project" page. + .settings-content.no-animate{ class: ('expanded' if expanded) } + .bs-callout.bs-callout-info + %p.append-bottom-0 + %p + The following items will be exported: + %ul + %li Project and wiki repositories + %li Project uploads + %li Project configuration including web hooks and services + %li Issues with comments, merge requests with diffs and comments, labels, milestones, snippets, and other project entities + %p + The following items will NOT be exported: + %ul + %li Job traces and artifacts + %li LFS objects + %li Container registry images + %li CI variables + %li Any encrypted tokens + %p + Once the exported file is ready, you will receive a notification email with a download link. + - if project.export_project_path + = link_to 'Download export', download_export_project_path(project), + rel: 'nofollow', download: '', method: :get, class: "btn btn-default" + = link_to 'Generate new export', generate_new_export_project_path(project), + method: :post, class: "btn btn-default" + - else + = link_to 'Export project', export_project_path(project), + method: :post, class: "btn btn-default" diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index c2794f8aaa8..6178abe9160 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -161,42 +161,7 @@ = render 'merge_request_settings', form: f = f.submit 'Save changes', class: "btn btn-save" - %section.settings - .settings-header - %h4 - Export project - %button.btn.js-settings-toggle - = expanded ? 'Collapse' : 'Expand' - %p - Export this project with all its related data in order to move your project to a new GitLab instance. Once the export is finished, you can import the file from the "New Project" page. - .settings-content.no-animate{ class: ('expanded' if expanded) } - .bs-callout.bs-callout-info - %p.append-bottom-0 - %p - The following items will be exported: - %ul - %li Project and wiki repositories - %li Project uploads - %li Project configuration including web hooks and services - %li Issues with comments, merge requests with diffs and comments, labels, milestones, snippets, and other project entities - %p - The following items will NOT be exported: - %ul - %li Job traces and artifacts - %li LFS objects - %li Container registry images - %li CI variables - %li Any encrypted tokens - %p - Once the exported file is ready, you will receive a notification email with a download link. - - if @project.export_project_path - = link_to 'Download export', download_export_project_path(@project), - rel: 'nofollow', download: '', method: :get, class: "btn btn-default" - = link_to 'Generate new export', generate_new_export_project_path(@project), - method: :post, class: "btn btn-default" - - else - = link_to 'Export project', export_project_path(@project), - method: :post, class: "btn btn-default" + = render 'export', project: @project %section.settings.advanced-settings .settings-header diff --git a/changelogs/unreleased/disable-project-export.yml b/changelogs/unreleased/disable-project-export.yml new file mode 100644 index 00000000000..d7ca9f46193 --- /dev/null +++ b/changelogs/unreleased/disable-project-export.yml @@ -0,0 +1,4 @@ +--- +title: Add option to disable project export on instance +merge_request: 13211 +author: Robin Bobbitt diff --git a/db/migrate/20170809161910_add_project_export_enabled_to_application_settings.rb b/db/migrate/20170809161910_add_project_export_enabled_to_application_settings.rb new file mode 100644 index 00000000000..4baba1ade6d --- /dev/null +++ b/db/migrate/20170809161910_add_project_export_enabled_to_application_settings.rb @@ -0,0 +1,14 @@ +class AddProjectExportEnabledToApplicationSettings < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + disable_ddl_transaction! + + DOWNTIME = false + + def up + add_column_with_default(:application_settings, :project_export_enabled, :boolean, default: true) + end + + def down + remove_column(:application_settings, :project_export_enabled) + end +end diff --git a/db/schema.rb b/db/schema.rb index 9d3b3ad1826..3206e106552 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170809142252) do +ActiveRecord::Schema.define(version: 20170809161910) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -127,6 +127,7 @@ ActiveRecord::Schema.define(version: 20170809142252) do t.string "help_page_support_url" t.integer "performance_bar_allowed_group_id" t.boolean "password_authentication_enabled" + t.boolean "project_export_enabled", default: true, null: false end create_table "audit_events", force: :cascade do |t| diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md index 35960ade3d4..97cca3007b1 100644 --- a/doc/user/project/settings/import_export.md +++ b/doc/user/project/settings/import_export.md @@ -9,6 +9,9 @@ > application settings (`/admin/application_settings`) under 'Import sources'. > Ask your administrator if you don't see the **GitLab export** button when > creating a new project. +> - Starting with GitLab 10.0, administrators can disable the project export option +> on the GitLab instance in application settings (`/admin/application_settings`) +> under 'Visibility and Access Controls'. > - You can find some useful raketasks if you are an administrator in the > [import_export](../../../administration/raketasks/project_import_export.md) > raketask. diff --git a/lib/api/settings.rb b/lib/api/settings.rb index d55a61fa638..667ba468ce6 100644 --- a/lib/api/settings.rb +++ b/lib/api/settings.rb @@ -29,6 +29,7 @@ module API desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com' optional :disabled_oauth_sign_in_sources, type: Array[String], desc: 'Disable certain OAuth sign-in sources' optional :enabled_git_access_protocol, type: String, values: %w[ssh http nil], desc: 'Allow only the selected protocols to be used for Git access.' + optional :project_export_enabled, type: Boolean, desc: 'Enable project export' optional :gravatar_enabled, type: Boolean, desc: 'Flag indicating if the Gravatar service is enabled' optional :default_projects_limit, type: Integer, desc: 'The maximum number of personal projects' optional :max_attachment_size, type: Integer, desc: 'Maximum attachment size in MB' diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 8ecd8b6ca71..c0e48046937 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -578,6 +578,118 @@ describe ProjectsController do end end + describe '#export' do + before do + sign_in(user) + + project.add_master(user) + end + + context 'when project export is enabled' do + it 'returns 302' do + get :export, namespace_id: project.namespace, id: project + + expect(response).to have_http_status(302) + end + end + + context 'when project export is disabled' do + before do + stub_application_setting(project_export_enabled?: false) + end + + it 'returns 404' do + get :export, namespace_id: project.namespace, id: project + + expect(response).to have_http_status(404) + end + end + end + + describe '#download_export' do + before do + sign_in(user) + + project.add_master(user) + end + + context 'when project export is enabled' do + it 'returns 302' do + get :download_export, namespace_id: project.namespace, id: project + + expect(response).to have_http_status(302) + end + end + + context 'when project export is disabled' do + before do + stub_application_setting(project_export_enabled?: false) + end + + it 'returns 404' do + get :download_export, namespace_id: project.namespace, id: project + + expect(response).to have_http_status(404) + end + end + end + + describe '#remove_export' do + before do + sign_in(user) + + project.add_master(user) + end + + context 'when project export is enabled' do + it 'returns 302' do + post :remove_export, namespace_id: project.namespace, id: project + + expect(response).to have_http_status(302) + end + end + + context 'when project export is disabled' do + before do + stub_application_setting(project_export_enabled?: false) + end + + it 'returns 404' do + post :remove_export, namespace_id: project.namespace, id: project + + expect(response).to have_http_status(404) + end + end + end + + describe '#generate_new_export' do + before do + sign_in(user) + + project.add_master(user) + end + + context 'when project export is enabled' do + it 'returns 302' do + post :generate_new_export, namespace_id: project.namespace, id: project + + expect(response).to have_http_status(302) + end + end + + context 'when project export is disabled' do + before do + stub_application_setting(project_export_enabled?: false) + end + + it 'returns 404' do + post :generate_new_export, namespace_id: project.namespace, id: project + + expect(response).to have_http_status(404) + end + end + end + def project_moved_message(redirect_route, project) "Project '#{redirect_route.path}' was moved to '#{project.full_path}'. Please update any links and bookmarks that may still have the old path." end diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index c9591a7d854..5db42175c15 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -35,6 +35,7 @@ feature 'Admin updates settings' do fill_in 'Help page text', with: 'Example text' check 'Hide marketing-related entries from help' fill_in 'Support page URL', with: 'http://example.com/help' + uncheck 'Project export enabled' click_button 'Save' expect(current_application_settings.gravatar_enabled).to be_falsey @@ -42,6 +43,7 @@ feature 'Admin updates settings' do expect(current_application_settings.help_page_text).to eq "Example text" expect(current_application_settings.help_page_hide_commercial_content).to be_truthy expect(current_application_settings.help_page_support_url).to eq "http://example.com/help" + expect(current_application_settings.project_export_enabled).to be_falsey expect(page).to have_content "Application settings saved successfully" end diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index c3ed5cd8ece..97275b80d03 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -43,7 +43,9 @@ describe API::Settings, 'Settings' do default_artifacts_expire_in: '2 days', help_page_text: 'custom help text', help_page_hide_commercial_content: true, - help_page_support_url: 'http://example.com/help' + help_page_support_url: 'http://example.com/help', + project_export_enabled: false + expect(response).to have_http_status(200) expect(json_response['default_projects_limit']).to eq(3) expect(json_response['password_authentication_enabled']).to be_falsey @@ -58,6 +60,7 @@ describe API::Settings, 'Settings' do expect(json_response['help_page_text']).to eq('custom help text') expect(json_response['help_page_hide_commercial_content']).to be_truthy expect(json_response['help_page_support_url']).to eq('http://example.com/help') + expect(json_response['project_export_enabled']).to be_falsey end end diff --git a/spec/views/projects/edit.html.haml_spec.rb b/spec/views/projects/edit.html.haml_spec.rb index 94899e26292..1af422941d7 100644 --- a/spec/views/projects/edit.html.haml_spec.rb +++ b/spec/views/projects/edit.html.haml_spec.rb @@ -11,14 +11,26 @@ describe 'projects/edit' do allow(controller).to receive(:current_user).and_return(user) allow(view).to receive_messages(current_user: user, can?: true) - allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) end context 'LFS enabled setting' do it 'displays the correct elements' do + allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) + render + expect(rendered).to have_select('project_lfs_enabled') expect(rendered).to have_content('Git Large File Storage') end end + + context 'project export disabled' do + it 'does not display the project export option' do + stub_application_setting(project_export_enabled?: false) + + render + + expect(rendered).not_to have_content('Export project') + end + end end