diff --git a/app/models/project.rb b/app/models/project.rb index 8f8f6cbf81f16605ad64e39dd8b5aca0fadccf58..c0d00408c1c5c0fffa3d6135c7e163895e7e0177 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -656,7 +656,9 @@ def self.integration_association_name(name) preload(:project_feature, :route, namespace: [:route, :owner]) } + scope :created_by, -> (user) { where(creator: user) } scope :imported_from, -> (type) { where(import_type: type) } + scope :imported, -> { where.not(import_type: nil) } scope :with_tracing_enabled, -> { joins(:tracing_setting) } scope :with_enabled_error_tracking, -> { joins(:error_tracking_setting).where(project_error_tracking_settings: { enabled: true }) } diff --git a/doc/api/projects.md b/doc/api/projects.md index 3ca4125a2fa40d688c73837979cf87f00f811896..33fbf01d3276a58b26500c1f35937b826d144d05 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -49,6 +49,7 @@ GET /projects | `archived` | boolean | **{dotted-circle}** No | Limit by archived status. | | `id_after` | integer | **{dotted-circle}** No | Limit results to projects with IDs greater than the specified ID. | | `id_before` | integer | **{dotted-circle}** No | Limit results to projects with IDs less than the specified ID. | +| `imported` | boolean | **{dotted-circle}** No | Limit results to projects which were imported from external systems by current user. | | `last_activity_after` | datetime | **{dotted-circle}** No | Limit results to projects with last_activity after specified time. Format: ISO 8601 (`YYYY-MM-DDTHH:MM:SSZ`) | | `last_activity_before` | datetime | **{dotted-circle}** No | Limit results to projects with last_activity before specified time. Format: ISO 8601 (`YYYY-MM-DDTHH:MM:SSZ`) | | `membership` | boolean | **{dotted-circle}** No | Limit by projects that the current user is a member of. | diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb index 8f9a8add9386770b419264aa8f24279248cc08c5..60cc5167c41c52fa4a8e445e26481f47b1cd1523 100644 --- a/lib/api/entities/project.rb +++ b/lib/api/entities/project.rb @@ -85,8 +85,11 @@ class Project < BasicProjectDetails end expose :mr_default_target_self, if: -> (project) { project.forked? } + expose :import_url, if: -> (project, options) { Ability.allowed?(options[:current_user], :admin_project, project) } do |project| + project[:import_url] + end + expose :import_type, if: -> (project, options) { Ability.allowed?(options[:current_user], :admin_project, project) } expose :import_status - expose :import_error, if: lambda { |_project, options| options[:user_can_admin_project] } do |project| project.import_state&.last_error end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index d772079372ce7cc5a32483f7fb9cbae733cfb1f7..b5948cb18273848c2679b270ad16017230542281 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -20,6 +20,7 @@ def apply_filters(projects) projects = projects.with_merge_requests_enabled if params[:with_merge_requests_enabled] projects = projects.with_statistics if params[:statistics] projects = projects.joins(:statistics) if params[:order_by].include?('project_statistics') # rubocop: disable CodeReuse/ActiveRecord + projects = projects.created_by(current_user).imported.with_import_state if params[:imported] lang = params[:with_programming_language] projects = projects.with_programming_language(lang) if lang @@ -125,6 +126,7 @@ def check_import_by_url_is_enabled optional :search_namespaces, type: Boolean, desc: "Include ancestor namespaces when matching search criteria" optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user' optional :starred, type: Boolean, default: false, desc: 'Limit by starred status' + optional :imported, type: Boolean, default: false, desc: 'Limit by imported by authenticated user' optional :membership, type: Boolean, default: false, desc: 'Limit by projects that the current user is a member of' optional :with_issues_enabled, type: Boolean, default: false, desc: 'Limit by enabled issues feature' optional :with_merge_requests_enabled, type: Boolean, default: false, desc: 'Limit by enabled merge requests feature' diff --git a/spec/requests/api/project_attributes.yml b/spec/requests/api/project_attributes.yml index 02d377efd95e8aa6dbed9ea4b78b79adc88af95d..e55f5820b0faff33d273400872f711b5e5860c8c 100644 --- a/spec/requests/api/project_attributes.yml +++ b/spec/requests/api/project_attributes.yml @@ -11,8 +11,6 @@ itself: # project - has_external_wiki - hidden - import_source - - import_type - - import_url - jobs_cache_index - last_repository_check_at - last_repository_check_failed @@ -63,6 +61,8 @@ itself: # project - empty_repo - forks_count - http_url_to_repo + - import_status + - import_url - name_with_namespace - open_issues_count - owner diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 877c68f87918308a612e4c8b49619d8d91d24343..a45862976b52a5c9223a2d16fb63d253c2e5bda3 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -683,6 +683,33 @@ end end + context 'and imported=true' do + before do + other_user = create(:user) + # imported project by other user + create(:project, creator: other_user, import_type: 'github', import_url: 'http://foo.com') + # project created by current user directly instead of importing + create(:project) + project.update_attribute(:import_url, 'http://user:password@host/path') + project.update_attribute(:import_type, 'github') + end + + it 'returns only imported projects owned by current user' do + get api('/projects?imported=true', user) + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |p| p['id'] }).to eq [project.id] + end + + it 'does not expose import credentials' do + get api('/projects?imported=true', user) + + expect(json_response.first['import_url']).to eq 'http://host/path' + end + end + context 'when authenticated as a different user' do it_behaves_like 'projects response' do let(:filter) { {} }