diff --git a/GITLAB_KAS_VERSION b/GITLAB_KAS_VERSION index ee597f5a950..7648b785c4b 100644 --- a/GITLAB_KAS_VERSION +++ b/GITLAB_KAS_VERSION @@ -1 +1 @@ -e23e51064c5e5a11e328173d7ef2bb724737307c +6c90dbb27ce3609304d71ffcd2b8113a08590f5c diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb index 693fb8924a7..763fb1dc1ff 100644 --- a/app/controllers/import/gitlab_projects_controller.rb +++ b/app/controllers/import/gitlab_projects_controller.rb @@ -23,7 +23,11 @@ class Import::GitlabProjectsController < Import::BaseController ) end - @project = ::Projects::GitlabProjectsImportService.new(current_user, project_params).execute + @project = ::Projects::GitlabProjectsImportService.new( + current_user, + project_params, + import_type: 'gitlab_project' + ).execute if @project.saved? redirect_to( diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb index bd4d13ac93a..665335f49d0 100644 --- a/app/controllers/projects/imports_controller.rb +++ b/app/controllers/projects/imports_controller.rb @@ -50,9 +50,15 @@ class Projects::ImportsController < Projects::ApplicationController redirect_to project_path(@project) if @project.repository_exists? end + # Project creation by template uses a different permission model to regular imports + # https://gitlab.com/gitlab-org/gitlab/-/issues/414046#note_1945586449. def require_namespace_project_creation_permission - unless can?(current_user, :admin_project, @project) || can?(current_user, :import_projects, @project.namespace) - render_404 + if Gitlab::ImportSources.template?(@project.import_type) + render_404 unless can?(current_user, :create_projects, project.namespace) + else + unless can?(current_user, :admin_project, @project) || can?(current_user, :import_projects, @project.namespace) + render_404 + end end end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 72b421b60c7..caad29badfc 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -453,7 +453,8 @@ class ApplicationSetting < ApplicationRecord value&.each do |source| # Temporary allow "gitlab_custom_project_template" to avoid validation errors # "gitlab_custom_project_template" can be excluded after incorrect values are removed from the database - allowed_import_sources = Gitlab::ImportSources.values + # Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/550410 + allowed_import_sources = Gitlab::ImportSources.values - ['gitlab_built_in_project_template'] unless allowed_import_sources.include?(source) record.errors.add(attr, format(_("'%{source}' is not a import source"), source: source)) diff --git a/app/models/project.rb b/app/models/project.rb index 8b3608a2e50..8d4d1f9ac20 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1711,7 +1711,7 @@ class Project < ApplicationRecord end def import? - external_import? || forked? || gitlab_project_import? || jira_import? || gitlab_project_migration? + external_import? || forked? || gitlab_project_import? || jira_import? || gitlab_project_migration? || Gitlab::ImportSources.template?(import_type) end def external_import? diff --git a/app/services/import/gitlab_projects/create_project_service.rb b/app/services/import/gitlab_projects/create_project_service.rb index dc0f24df0cb..f8bc9b5cc5e 100644 --- a/app/services/import/gitlab_projects/create_project_service.rb +++ b/app/services/import/gitlab_projects/create_project_service.rb @@ -63,7 +63,8 @@ module Import @project ||= ::Projects::GitlabProjectsImportService.new( current_user, project_params, - params[:override] + params[:override], + import_type: 'gitlab_project' ).execute end @@ -72,8 +73,7 @@ module Import name: params[:name], path: params[:path], namespace_id: params[:namespace].id, - overwrite: params[:overwrite], - import_type: 'gitlab_project' + overwrite: params[:overwrite] }.merge(strategy.project_params) end end diff --git a/app/services/projects/create_from_template_service.rb b/app/services/projects/create_from_template_service.rb index 48dda09da71..19d27eccaa8 100644 --- a/app/services/projects/create_from_template_service.rb +++ b/app/services/projects/create_from_template_service.rb @@ -26,7 +26,7 @@ module Projects params[:sample_data] = true end - GitlabProjectsImportService.new(current_user, params, override_params).execute + GitlabProjectsImportService.new(current_user, params, override_params, import_type: 'gitlab_built_in_project_template').execute ensure file&.close end diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 39b5956ffa1..6aa2f774ff5 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -113,7 +113,6 @@ module Projects def validate_import_permissions return unless @project.import? - return if @project.gitlab_project_import? # Skip for project template importers, as their permission model is different from other importers # See: https://gitlab.com/gitlab-org/gitlab/-/issues/414046#note_1945586449. @@ -335,9 +334,6 @@ module Projects # controlled by the `import_sources` application setting. return if Gitlab::ImportSources.template?(import_type) - # Skip validation when creating project from a built in template - return if @import_export_upload.present? && import_type == 'gitlab_project' - unless ::Gitlab::CurrentSettings.import_sources&.include?(import_type) raise ImportSourceDisabledError, "#{import_type} import source is disabled" end diff --git a/app/services/projects/gitlab_projects_import_service.rb b/app/services/projects/gitlab_projects_import_service.rb index ad5882b36ad..3fb72779bb8 100644 --- a/app/services/projects/gitlab_projects_import_service.rb +++ b/app/services/projects/gitlab_projects_import_service.rb @@ -10,10 +10,13 @@ module Projects attr_reader :current_user, :params - def initialize(user, import_params, override_params = nil) + def initialize(user, import_params, override_params = nil, import_type:) @current_user = user @params = import_params.dup @override_params = override_params + @import_type = import_type.to_s + + raise ArgumentError, 'Invalid import_type provided' unless valid_import_type? end def execute @@ -26,6 +29,12 @@ module Projects private + attr_reader :import_type + + def valid_import_type? + import_type == 'gitlab_project' || Gitlab::ImportSources.template?(import_type) + end + def overwrite_project? overwrite? && project_with_same_full_path? end @@ -67,13 +76,10 @@ module Projects params[:path] += "-#{tmp_filename}" end - if template_file - data[:sample_data] = params.delete(:sample_data) if params.key?(:sample_data) - params[:import_type] = 'gitlab_project' - end + data[:sample_data] = params.delete(:sample_data) if template_file && params.key?(:sample_data) + params[:import_type] = import_type params[:organization_id] = current_namespace.organization_id - params[:import_data] = { data: data } if data.present? end end diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index fe3645fe24b..b64a7347b07 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -70,10 +70,6 @@ class RepositoryImportWorker # rubocop:disable Scalability/IdempotentWorker def fail_import(message) project.import_state.mark_as_failed(message) end - - def template_import? - project.gitlab_project_import? - end end RepositoryImportWorker.prepend_mod_with('RepositoryImportWorker') diff --git a/lib/gitlab/import_export/project/import_task.rb b/lib/gitlab/import_export/project/import_task.rb index 3568e08af4c..c2138595d26 100644 --- a/lib/gitlab/import_export/project/import_task.rb +++ b/lib/gitlab/import_export/project/import_task.rb @@ -56,7 +56,8 @@ module Gitlab disable_upload_object_storage do service = Projects::GitlabProjectsImportService.new( current_user, - import_params + import_params, + import_type: 'gitlab_project' ) service.execute diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb index a340a4deae2..4b7f431c8ef 100644 --- a/lib/gitlab/import_sources.rb +++ b/lib/gitlab/import_sources.rb @@ -16,9 +16,14 @@ module Gitlab ImportSource.new('git', 'Repository by URL', nil), ImportSource.new('gitlab_project', 'GitLab export', Gitlab::ImportExport::Importer), ImportSource.new('gitea', 'Gitea', Gitlab::LegacyGithubImport::Importer), - ImportSource.new('manifest', 'Manifest file', nil) + ImportSource.new('manifest', 'Manifest file', nil), + ImportSource.new( + 'gitlab_built_in_project_template', 'GitLab built-in project template', Gitlab::ImportExport::Importer + ) ].freeze + PROJECT_TEMPLATE_IMPORTERS = ['gitlab_built_in_project_template'].freeze + class << self prepend_mod_with('Gitlab::ImportSources') # rubocop: disable Cop/InjectEnterpriseEditionModule @@ -51,7 +56,7 @@ module Gitlab end def project_template_importers - [] + PROJECT_TEMPLATE_IMPORTERS end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index ab714063ced..0a538d1f572 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2394,12 +2394,24 @@ msgstr "" msgid "AICatalog|Agents" msgstr "" +msgid "AICatalog|Create agent" +msgstr "" + +msgid "AICatalog|Description can't be blank." +msgstr "" + msgid "AICatalog|Edit agent" msgstr "" msgid "AICatalog|Failed to run agent." msgstr "" +msgid "AICatalog|Input cannot exceed %{value} characters. Please shorten your input." +msgstr "" + +msgid "AICatalog|Modify the agent settings and configuration." +msgstr "" + msgid "AICatalog|Prompt" msgstr "" @@ -2412,6 +2424,15 @@ msgstr "" msgid "AICatalog|Run agent" msgstr "" +msgid "AICatalog|Save changes" +msgstr "" + +msgid "AICatalog|System Prompt" +msgstr "" + +msgid "AICatalog|User Prompt" +msgstr "" + msgid "AISummary|Generates a summary of this issue" msgstr "" diff --git a/spec/controllers/projects/imports_controller_spec.rb b/spec/controllers/projects/imports_controller_spec.rb index 4502f3d7bd9..b911e6f5739 100644 --- a/spec/controllers/projects/imports_controller_spec.rb +++ b/spec/controllers/projects/imports_controller_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Projects::ImportsController, feature_category: :importers do - let(:user) { create(:user) } + let_it_be(:user) { create(:user) } let(:project) { create(:project) } before do @@ -22,6 +22,33 @@ RSpec.describe Projects::ImportsController, feature_category: :importers do end end + context 'when the developer user has project creation rights' do + let_it_be(:group) { create(:group, project_creation_level: Gitlab::Access::DEVELOPER_PROJECT_ACCESS) } + let_it_be(:project) { create(:project_empty_repo, :import_started, group: group) } + + before_all do + group.add_developer(user) + end + + it 'returns 404 response' do + get :show, params: { namespace_id: project.namespace.to_param, project_id: project } + + expect(response).to have_gitlab_http_status(:not_found) + end + + context 'when import type is gitlab_built_in_project_template' do + before do + project.update!(import_type: 'gitlab_built_in_project_template') + end + + it 'returns 200 response' do + get :show, params: { namespace_id: project.namespace.to_param, project_id: project } + + expect(response).to have_gitlab_http_status(:success) + end + end + end + context 'when the user has maintainer rights' do before do project.add_maintainer(user) @@ -137,29 +164,6 @@ RSpec.describe Projects::ImportsController, feature_category: :importers do end end end - - context 'when project is in group' do - let(:project) { create(:project_empty_repo, import_url: 'https://github.com/vim/vim.git', namespace: group) } - - context 'when user has developer access to group and import is in progress' do - let(:import_state) { project.import_state } - - before do - group.add_developer(user) - import_state.update!(status: :started) - end - - context 'when group prohibits developers to import projects' do - let(:group) { create(:group, project_creation_level: Gitlab::Access::MAINTAINER_PROJECT_ACCESS) } - - it 'returns 404 response' do - get :show, params: { namespace_id: project.namespace.to_param, project_id: project } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - end - end end describe 'POST #create' do diff --git a/spec/lib/gitlab/import_export/project/import_task_spec.rb b/spec/lib/gitlab/import_export/project/import_task_spec.rb index d38905992d9..1f198d91f33 100644 --- a/spec/lib/gitlab/import_export/project/import_task_spec.rb +++ b/spec/lib/gitlab/import_export/project/import_task_spec.rb @@ -19,6 +19,10 @@ RSpec.describe Gitlab::ImportExport::Project::ImportTask, :request_store, :silen } end + before do + stub_application_setting(import_sources: ['gitlab_project']) + end + subject { rake_task.import } context 'when project import is valid' do diff --git a/spec/lib/gitlab/import_sources_spec.rb b/spec/lib/gitlab/import_sources_spec.rb index b0419f6259a..5e756fe9ed3 100644 --- a/spec/lib/gitlab/import_sources_spec.rb +++ b/spec/lib/gitlab/import_sources_spec.rb @@ -15,6 +15,7 @@ RSpec.describe Gitlab::ImportSources, feature_category: :importers do gitlab_project gitea manifest + gitlab_built_in_project_template ] expect(described_class.values).to eq(expected) @@ -31,6 +32,7 @@ RSpec.describe Gitlab::ImportSources, feature_category: :importers do fogbugz gitlab_project gitea + gitlab_built_in_project_template ] without_importer = %w[git manifest doesnotexist] @@ -55,6 +57,7 @@ RSpec.describe Gitlab::ImportSources, feature_category: :importers do 'gitlab_project' => Gitlab::ImportExport::Importer, 'gitea' => Gitlab::LegacyGithubImport::Importer, 'manifest' => nil, + 'gitlab_built_in_project_template' => Gitlab::ImportExport::Importer, 'doesnotexist' => nil, nil => nil } @@ -76,6 +79,7 @@ RSpec.describe Gitlab::ImportSources, feature_category: :importers do 'gitlab_project' => 'GitLab export', 'gitea' => 'Gitea', 'manifest' => 'Manifest file', + 'gitlab_built_in_project_template' => 'GitLab built-in project template', 'doesnotexist' => nil, nil => nil } @@ -88,7 +92,7 @@ RSpec.describe Gitlab::ImportSources, feature_category: :importers do end describe 'imports_repository? checker' do - let(:allowed_importers) { %w[github gitlab_project bitbucket bitbucket_server] } + let(:allowed_importers) { %w[github gitlab_project bitbucket bitbucket_server gitlab_built_in_project_template] } it 'fails if any importer other than the allowed ones implements this method' do current_importers = described_class.values.select { |kind| described_class.importer(kind).try(:imports_repository?) } @@ -125,6 +129,12 @@ RSpec.describe Gitlab::ImportSources, feature_category: :importers do describe '.template?' do subject { described_class.template?(template) } + context 'when importer is project template importer' do + let(:template) { 'gitlab_built_in_project_template' } + + it { is_expected.to be_truthy } + end + context 'when importer is not project template importer' do let(:template) { 'github' } @@ -145,8 +155,17 @@ RSpec.describe Gitlab::ImportSources, feature_category: :importers do end describe '.project_template_importers' do - it 'does not include non-project template importers' do + it 'returns names of project template importers' do + expect(described_class.project_template_importers).to include('gitlab_built_in_project_template') expect(described_class.project_template_importers).not_to include('github') end end + + describe 'gitlab_built_in_project_template' do + subject(:importer) { described_class.import_source('gitlab_built_in_project_template') } + + it 'uses Gitlab::ImportExport::Importer' do + expect(importer.importer).to eq(Gitlab::ImportExport::Importer) + end + end end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index 401ef948275..d5a652ae657 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -925,9 +925,11 @@ RSpec.describe ApplicationSetting, feature_category: :shared, type: :model do end describe 'import_sources' do - let(:valid_import_sources) { Gitlab::ImportSources.values } + let(:invalid_import_sources) { ['gitlab_built_in_project_template'] } + let(:valid_import_sources) { Gitlab::ImportSources.values - invalid_import_sources } it { is_expected.to allow_value(valid_import_sources).for(:import_sources) } + it { is_expected.not_to allow_value(invalid_import_sources).for(:import_sources) } end describe 'default_artifacts_expire_in' do diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index 06af787bb0d..cfe742ed3ba 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -64,7 +64,7 @@ RSpec.describe API::ProjectImport, :aggregate_failures, feature_category: :impor it 'executes a limited number of queries', :use_clean_rails_redis_caching do control = ActiveRecord::QueryRecorder.new { perform_archive_upload } - expect(control.count).to be <= 128 + expect(control.count).to be <= 134 end it 'schedules an import using a namespace' do @@ -73,6 +73,14 @@ RSpec.describe API::ProjectImport, :aggregate_failures, feature_category: :impor perform_archive_upload + expect(json_response).to include({ + 'id' => kind_of(Integer), + 'name' => 'test-import', + 'name_with_namespace' => "#{namespace.name} / test-import", + 'path' => 'test-import', + 'import_type' => 'gitlab_project', + 'path_with_namespace' => "#{namespace.path}/test-import" + }) expect(response).to have_gitlab_http_status(:created) end @@ -337,7 +345,7 @@ RSpec.describe API::ProjectImport, :aggregate_failures, feature_category: :impor def stub_import(namespace) expect_any_instance_of(ProjectImportState).to receive(:schedule) - expect(::Projects::CreateService).to receive(:new).with(user, hash_including(namespace_id: namespace.id)).and_call_original + expect(::Projects::CreateService).to receive(:new).with(user, hash_including(namespace_id: namespace.id, import_type: 'gitlab_project')).and_call_original end end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 2ddf150b0ec..7887c3e25fb 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -1512,7 +1512,7 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :groups_and project = Project.find(json_response['id']) expect(project).to be_saved - expect(project.import_type).to eq('gitlab_project') + expect(project.import_type).to eq('gitlab_built_in_project_template') end it 'returns 400 for an invalid template' do diff --git a/spec/services/projects/gitlab_projects_import_service_spec.rb b/spec/services/projects/gitlab_projects_import_service_spec.rb index b1468a40212..1b720288551 100644 --- a/spec/services/projects/gitlab_projects_import_service_spec.rb +++ b/spec/services/projects/gitlab_projects_import_service_spec.rb @@ -10,9 +10,13 @@ RSpec.describe Projects::GitlabProjectsImportService, feature_category: :importe let(:overwrite) { false } let(:import_params) { { namespace_id: namespace.id, path: path, file: file, overwrite: overwrite } } - subject { described_class.new(namespace.owner, import_params) } + subject { described_class.new(namespace.owner, import_params, import_type: 'gitlab_project') } + + before do + stub_application_setting(import_sources: ['gitlab_project']) + end describe '#execute' do - it_behaves_like 'gitlab projects import validations' + it_behaves_like 'gitlab projects import validations', import_type: 'gitlab_project' end end diff --git a/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb b/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb index 366fa4763e1..239cc2273ec 100644 --- a/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb +++ b/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.shared_examples 'gitlab projects import validations' do +RSpec.shared_examples 'gitlab projects import validations' do |import_type:| context 'with an invalid path' do let(:path) { '/invalid-path/' } @@ -18,13 +18,14 @@ RSpec.shared_examples 'gitlab projects import validations' do expect(project).to be_persisted expect(project).to be_valid + expect(project.import_type).to eq(import_type.to_s) end end context 'override params' do it 'stores them as import data when passed' do project = described_class - .new(namespace.owner, import_params, description: 'Hello') + .new(namespace.owner, import_params, { description: 'Hello' }, import_type: import_type) .execute expect(project.import_data.data['override_params']['description']).to eq('Hello')