From 5652da8bb4ea26acd35a241683e242439fefdd33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 16 Dec 2016 18:58:24 +0100 Subject: [PATCH 1/2] Allow unauthenticated access to Repositories Files API GET endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- changelogs/unreleased/4269-public-api.yml | 2 +- .../unreleased/4269-public-files-api.yml | 4 ++ .../4269-public-repositories-api.yml | 2 +- doc/api/repository_files.md | 4 +- lib/api/files.rb | 2 - spec/requests/api/files_spec.rb | 37 +++++++++++++------ 6 files changed, 35 insertions(+), 16 deletions(-) create mode 100644 changelogs/unreleased/4269-public-files-api.yml diff --git a/changelogs/unreleased/4269-public-api.yml b/changelogs/unreleased/4269-public-api.yml index 560bc6a4f13..9de739d0cad 100644 --- a/changelogs/unreleased/4269-public-api.yml +++ b/changelogs/unreleased/4269-public-api.yml @@ -1,4 +1,4 @@ --- -title: Allow public access to some Project API endpoints +title: Allow unauthenticated access to some Project API GET endpoints merge_request: 7843 author: diff --git a/changelogs/unreleased/4269-public-files-api.yml b/changelogs/unreleased/4269-public-files-api.yml new file mode 100644 index 00000000000..e8f9e9b5ed3 --- /dev/null +++ b/changelogs/unreleased/4269-public-files-api.yml @@ -0,0 +1,4 @@ +--- +title: Allow unauthenticated access to Repositories Files API GET endpoints +merge_request: +author: diff --git a/changelogs/unreleased/4269-public-repositories-api.yml b/changelogs/unreleased/4269-public-repositories-api.yml index 861307a022b..38984eed904 100644 --- a/changelogs/unreleased/4269-public-repositories-api.yml +++ b/changelogs/unreleased/4269-public-repositories-api.yml @@ -1,4 +1,4 @@ --- -title: Allow Repositories API GET endpoints to be requested anonymously +title: Allow unauthenticated access to Repositories API GET endpoints merge_request: 8148 author: diff --git a/doc/api/repository_files.md b/doc/api/repository_files.md index b8c9eb2c9a8..8a6baed5987 100644 --- a/doc/api/repository_files.md +++ b/doc/api/repository_files.md @@ -6,7 +6,9 @@ ## Get file from repository -Allows you to receive information about file in repository like name, size, content. Note that file content is Base64 encoded. +Allows you to receive information about file in repository like name, size, +content. Note that file content is Base64 encoded. This endpoint can be accessed +without authentication if the repository is publicly accessible. ``` GET /projects/:id/repository/files diff --git a/lib/api/files.rb b/lib/api/files.rb index 28f306e45f3..532a317c89e 100644 --- a/lib/api/files.rb +++ b/lib/api/files.rb @@ -1,8 +1,6 @@ module API # Projects API class Files < Grape::API - before { authenticate! } - helpers do def commit_params(attrs) { diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index 2081f80ccc1..4dd312e2852 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -24,19 +24,34 @@ describe API::Files, api: true do before { project.team << [user, :developer] } describe "GET /projects/:id/repository/files" do - it "returns file info" do - params = { - file_path: file_path, - ref: 'master', - } + shared_examples_for 'repository files' do + it "returns file info" do + params = { + file_path: file_path, + ref: 'master', + } - get api("/projects/#{project.id}/repository/files", user), params + get api("/projects/#{project.id}/repository/files", current_user), params - expect(response).to have_http_status(200) - expect(json_response['file_path']).to eq(file_path) - expect(json_response['file_name']).to eq('popen.rb') - expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') - expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n") + expect(response).to have_http_status(200) + expect(json_response['file_path']).to eq(file_path) + expect(json_response['file_name']).to eq('popen.rb') + expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') + expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n") + end + end + + context 'when unauthenticated' do + it_behaves_like 'repository files' do + let(:project) { create(:project, :public) } + let(:current_user) { nil } + end + end + + context 'when authenticated' do + it_behaves_like 'repository files' do + let(:current_user) { user } + end end it "returns a 400 bad request if no params given" do From 0349e83aa74b42c3f564fd1bc34104300a41ddf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 20 Dec 2016 15:05:57 +0100 Subject: [PATCH 2/2] Improve specs for Files API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- spec/requests/api/files_spec.rb | 74 +++++++++++++------ spec/requests/api/repositories_spec.rb | 11 --- .../api/repositories_shared_context.rb | 10 +++ 3 files changed, 61 insertions(+), 34 deletions(-) create mode 100644 spec/support/api/repositories_shared_context.rb diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index 4dd312e2852..685da28c673 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -4,7 +4,14 @@ describe API::Files, api: true do include ApiHelpers let(:user) { create(:user) } let!(:project) { create(:project, namespace: user.namespace ) } + let(:guest) { create(:user).tap { |u| create(:project_member, :guest, user: u, project: project) } } let(:file_path) { 'files/ruby/popen.rb' } + let(:params) do + { + file_path: file_path, + ref: 'master' + } + end let(:author_email) { FFaker::Internet.email } # I have to remove periods from the end of the name @@ -24,14 +31,11 @@ describe API::Files, api: true do before { project.team << [user, :developer] } describe "GET /projects/:id/repository/files" do + let(:route) { "/projects/#{project.id}/repository/files" } + shared_examples_for 'repository files' do it "returns file info" do - params = { - file_path: file_path, - ref: 'master', - } - - get api("/projects/#{project.id}/repository/files", current_user), params + get api(route, current_user), params expect(response).to have_http_status(200) expect(json_response['file_path']).to eq(file_path) @@ -39,36 +43,60 @@ describe API::Files, api: true do expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n") end + + context 'when no params are given' do + it_behaves_like '400 response' do + let(:request) { get api(route, current_user) } + end + end + + context 'when file_path does not exist' do + let(:params) do + { + file_path: 'app/models/application.rb', + ref: 'master', + } + end + + it_behaves_like '404 response' do + let(:request) { get api(route, current_user), params } + let(:message) { '404 File Not Found' } + end + end + + context 'when repository is disabled' do + include_context 'disabled repository' + + it_behaves_like '403 response' do + let(:request) { get api(route, current_user), params } + end + end end - context 'when unauthenticated' do + context 'when unauthenticated', 'and project is public' do it_behaves_like 'repository files' do let(:project) { create(:project, :public) } let(:current_user) { nil } end end - context 'when authenticated' do + context 'when unauthenticated', 'and project is private' do + it_behaves_like '404 response' do + let(:request) { get api(route), params } + let(:message) { '404 Project Not Found' } + end + end + + context 'when authenticated', 'as a developer' do it_behaves_like 'repository files' do let(:current_user) { user } end end - it "returns a 400 bad request if no params given" do - get api("/projects/#{project.id}/repository/files", user) - - expect(response).to have_http_status(400) - end - - it "returns a 404 if such file does not exist" do - params = { - file_path: 'app/models/application.rb', - ref: 'master', - } - - get api("/projects/#{project.id}/repository/files", user), params - - expect(response).to have_http_status(404) + context 'when authenticated', 'as a guest' do + it_behaves_like '403 response' do + let(:request) { get api(route, guest), params } + end end end diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index fe28ad1d1a1..0b19fa38c55 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -11,17 +11,6 @@ describe API::Repositories, api: true do let!(:project) { create(:project, creator_id: user.id) } let!(:master) { create(:project_member, :master, user: user, project: project) } - shared_context 'disabled repository' do - before do - project.project_feature.update_attributes!( - repository_access_level: ProjectFeature::DISABLED, - merge_requests_access_level: ProjectFeature::DISABLED, - builds_access_level: ProjectFeature::DISABLED - ) - expect(project.feature_available?(:repository, current_user)).to be false - end - end - describe "GET /projects/:id/repository/tree" do let(:route) { "/projects/#{project.id}/repository/tree" } diff --git a/spec/support/api/repositories_shared_context.rb b/spec/support/api/repositories_shared_context.rb new file mode 100644 index 00000000000..ea38fe4f5b8 --- /dev/null +++ b/spec/support/api/repositories_shared_context.rb @@ -0,0 +1,10 @@ +shared_context 'disabled repository' do + before do + project.project_feature.update_attributes!( + repository_access_level: ProjectFeature::DISABLED, + merge_requests_access_level: ProjectFeature::DISABLED, + builds_access_level: ProjectFeature::DISABLED + ) + expect(project.feature_available?(:repository, current_user)).to be false + end +end