Commit d982bbf4 authored by Vijay Hawoldar's avatar Vijay Hawoldar

Allow unauthenticated public snippet API calls

To be consistent with other areas of the product, we
are allowing REST API calls for public snippets to be made
without user authentication
parent 6b19d505
---
title: Allow unauthenticated users access to public Personal Snippets via the REST API
merge_request: 44135
author:
type: fixed
...@@ -5,8 +5,6 @@ module API ...@@ -5,8 +5,6 @@ module API
class Snippets < Grape::API::Instance class Snippets < Grape::API::Instance
include PaginationParams include PaginationParams
before { authenticate! }
resource :snippets do resource :snippets do
helpers Helpers::SnippetsHelpers helpers Helpers::SnippetsHelpers
helpers do helpers do
...@@ -23,7 +21,7 @@ module API ...@@ -23,7 +21,7 @@ module API
end end
end end
desc 'Get a snippets list for authenticated user' do desc 'Get a snippets list for an authenticated user' do
detail 'This feature was introduced in GitLab 8.15.' detail 'This feature was introduced in GitLab 8.15.'
success Entities::Snippet success Entities::Snippet
end end
...@@ -31,6 +29,8 @@ module API ...@@ -31,6 +29,8 @@ module API
use :pagination use :pagination
end end
get do get do
authenticate!
present paginate(snippets_for_current_user), with: Entities::Snippet, current_user: current_user present paginate(snippets_for_current_user), with: Entities::Snippet, current_user: current_user
end end
...@@ -42,6 +42,8 @@ module API ...@@ -42,6 +42,8 @@ module API
use :pagination use :pagination
end end
get 'public' do get 'public' do
authenticate!
present paginate(public_snippets), with: Entities::PersonalSnippet, current_user: current_user present paginate(public_snippets), with: Entities::PersonalSnippet, current_user: current_user
end end
...@@ -74,6 +76,8 @@ module API ...@@ -74,6 +76,8 @@ module API
use :create_file_params use :create_file_params
end end
post do post do
authenticate!
authorize! :create_snippet authorize! :create_snippet
attrs = process_create_params(declared_params(include_missing: false)) attrs = process_create_params(declared_params(include_missing: false))
...@@ -109,6 +113,8 @@ module API ...@@ -109,6 +113,8 @@ module API
use :minimum_update_params use :minimum_update_params
end end
put ':id' do put ':id' do
authenticate!
snippet = snippets_for_current_user.find_by_id(params.delete(:id)) snippet = snippets_for_current_user.find_by_id(params.delete(:id))
break not_found!('Snippet') unless snippet break not_found!('Snippet') unless snippet
...@@ -139,6 +145,8 @@ module API ...@@ -139,6 +145,8 @@ module API
requires :id, type: Integer, desc: 'The ID of a snippet' requires :id, type: Integer, desc: 'The ID of a snippet'
end end
delete ':id' do delete ':id' do
authenticate!
snippet = snippets_for_current_user.find_by_id(params.delete(:id)) snippet = snippets_for_current_user.find_by_id(params.delete(:id))
break not_found!('Snippet') unless snippet break not_found!('Snippet') unless snippet
......
...@@ -432,6 +432,14 @@ RSpec.describe API::ProjectSnippets do ...@@ -432,6 +432,14 @@ RSpec.describe API::ProjectSnippets do
describe 'GET /projects/:project_id/snippets/:id/files/:ref/:file_path/raw' do describe 'GET /projects/:project_id/snippets/:id/files/:ref/:file_path/raw' do
let_it_be(:snippet) { create(:project_snippet, :repository, author: admin, project: project) } let_it_be(:snippet) { create(:project_snippet, :repository, author: admin, project: project) }
context 'with no user' do
it 'requires authentication' do
get api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/files/master/%2Egitattributes/raw", nil)
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
it_behaves_like 'raw snippet files' do it_behaves_like 'raw snippet files' do
let(:api_path) { "/projects/#{snippet.project.id}/snippets/#{snippet_id}/files/#{ref}/#{file_path}/raw" } let(:api_path) { "/projects/#{snippet.project.id}/snippets/#{snippet_id}/files/#{ref}/#{file_path}/raw" }
end end
......
...@@ -5,14 +5,15 @@ require 'spec_helper' ...@@ -5,14 +5,15 @@ require 'spec_helper'
RSpec.describe API::Snippets do RSpec.describe API::Snippets do
include SnippetHelpers include SnippetHelpers
let_it_be(:admin) { create(:user, :admin) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:other_user) { create(:user) }
let_it_be(:public_snippet) { create(:personal_snippet, :repository, :public, author: user) }
let_it_be(:private_snippet) { create(:personal_snippet, :repository, :private, author: user) }
let_it_be(:internal_snippet) { create(:personal_snippet, :repository, :internal, author: user) }
describe 'GET /snippets/' do describe 'GET /snippets/' do
it 'returns snippets available' do it 'returns snippets available for user' do
public_snippet = create(:personal_snippet, :repository, :public, author: user)
private_snippet = create(:personal_snippet, :repository, :private, author: user)
internal_snippet = create(:personal_snippet, :repository, :internal, author: user)
get api("/snippets/", user) get api("/snippets/", user)
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
...@@ -29,9 +30,7 @@ RSpec.describe API::Snippets do ...@@ -29,9 +30,7 @@ RSpec.describe API::Snippets do
end end
it 'hides private snippets from regular user' do it 'hides private snippets from regular user' do
create(:personal_snippet, :private) get api("/snippets/", other_user)
get api("/snippets/", user)
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers expect(response).to include_pagination_headers
...@@ -39,9 +38,7 @@ RSpec.describe API::Snippets do ...@@ -39,9 +38,7 @@ RSpec.describe API::Snippets do
expect(json_response.size).to eq(0) expect(json_response.size).to eq(0)
end end
it 'returns 404 for non-authenticated' do it 'returns 401 for non-authenticated' do
create(:personal_snippet, :internal)
get api("/snippets/") get api("/snippets/")
expect(response).to have_gitlab_http_status(:unauthorized) expect(response).to have_gitlab_http_status(:unauthorized)
...@@ -62,10 +59,6 @@ RSpec.describe API::Snippets do ...@@ -62,10 +59,6 @@ RSpec.describe API::Snippets do
end end
describe 'GET /snippets/public' do describe 'GET /snippets/public' do
let_it_be(:other_user) { create(:user) }
let_it_be(:public_snippet) { create(:personal_snippet, :repository, :public, author: user) }
let_it_be(:private_snippet) { create(:personal_snippet, :repository, :private, author: user) }
let_it_be(:internal_snippet) { create(:personal_snippet, :repository, :internal, author: user) }
let_it_be(:public_snippet_other) { create(:personal_snippet, :repository, :public, author: other_user) } let_it_be(:public_snippet_other) { create(:personal_snippet, :repository, :public, author: other_user) }
let_it_be(:private_snippet_other) { create(:personal_snippet, :repository, :private, author: other_user) } let_it_be(:private_snippet_other) { create(:personal_snippet, :repository, :private, author: other_user) }
let_it_be(:internal_snippet_other) { create(:personal_snippet, :repository, :internal, author: other_user) } let_it_be(:internal_snippet_other) { create(:personal_snippet, :repository, :internal, author: other_user) }
...@@ -73,8 +66,10 @@ RSpec.describe API::Snippets do ...@@ -73,8 +66,10 @@ RSpec.describe API::Snippets do
let_it_be(:private_snippet_project) { create(:project_snippet, :repository, :private, author: user) } let_it_be(:private_snippet_project) { create(:project_snippet, :repository, :private, author: user) }
let_it_be(:internal_snippet_project) { create(:project_snippet, :repository, :internal, author: user) } let_it_be(:internal_snippet_project) { create(:project_snippet, :repository, :internal, author: user) }
it 'returns all snippets with public visibility from all users' do let(:path) { "/snippets/public" }
get api("/snippets/public", user)
it 'returns only public snippets from all users when authenticated' do
get api(path, user)
aggregate_failures do aggregate_failures do
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
...@@ -90,20 +85,23 @@ RSpec.describe API::Snippets do ...@@ -90,20 +85,23 @@ RSpec.describe API::Snippets do
expect(json_response[1]['files'].first).to eq snippet_blob_file(public_snippet.blobs.first) expect(json_response[1]['files'].first).to eq snippet_blob_file(public_snippet.blobs.first)
end end
end end
end
describe 'GET /snippets/:id/raw' do
let_it_be(:author) { create(:user) }
let_it_be(:snippet) { create(:personal_snippet, :repository, :private, author: author) }
it 'requires authentication' do it 'requires authentication' do
get api("/snippets/#{snippet.id}", nil) get api(path, nil)
expect(response).to have_gitlab_http_status(:unauthorized) expect(response).to have_gitlab_http_status(:unauthorized)
end end
end
describe 'GET /snippets/:id/raw' do
let(:snippet) { private_snippet }
it_behaves_like 'snippet access with different users' do
let(:path) { "/snippets/#{snippet.id}/raw" }
end
it 'returns raw text' do it 'returns raw text' do
get api("/snippets/#{snippet.id}/raw", author) get api("/snippets/#{snippet.id}/raw", user)
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(response.media_type).to eq 'text/plain' expect(response.media_type).to eq 'text/plain'
...@@ -113,28 +111,14 @@ RSpec.describe API::Snippets do ...@@ -113,28 +111,14 @@ RSpec.describe API::Snippets do
it 'returns 404 for invalid snippet id' do it 'returns 404 for invalid snippet id' do
snippet.destroy! snippet.destroy!
get api("/snippets/#{snippet.id}/raw", author)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('404 Snippet Not Found')
end
it 'hides private snippets from ordinary users' do
get api("/snippets/#{snippet.id}/raw", user) get api("/snippets/#{snippet.id}/raw", user)
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end expect(json_response['message']).to eq('404 Snippet Not Found')
it 'shows internal snippets to ordinary users' do
internal_snippet = create(:personal_snippet, :internal, author: author)
get api("/snippets/#{internal_snippet.id}/raw", user)
expect(response).to have_gitlab_http_status(:ok)
end end
it_behaves_like 'snippet blob content' do it_behaves_like 'snippet blob content' do
let_it_be(:snippet_with_empty_repo) { create(:personal_snippet, :empty_repo, :private, author: author) } let_it_be(:snippet_with_empty_repo) { create(:personal_snippet, :empty_repo, :private, author: user) }
subject { get api("/snippets/#{snippet.id}/raw", snippet.author) } subject { get api("/snippets/#{snippet.id}/raw", snippet.author) }
end end
...@@ -149,33 +133,11 @@ RSpec.describe API::Snippets do ...@@ -149,33 +133,11 @@ RSpec.describe API::Snippets do
end end
describe 'GET /snippets/:id' do describe 'GET /snippets/:id' do
let_it_be(:admin) { create(:user, :admin) } let(:snippet_id) { private_snippet.id }
let_it_be(:author) { create(:user) }
let_it_be(:private_snippet) { create(:personal_snippet, :repository, :private, author: author) }
let_it_be(:internal_snippet) { create(:personal_snippet, :repository, :internal, author: author) }
let(:snippet) { private_snippet }
subject { get api("/snippets/#{snippet.id}", user) }
it 'hides private snippets from an ordinary user' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
context 'without a user' do
let(:user) { nil }
it 'requires authentication' do
subject
expect(response).to have_gitlab_http_status(:unauthorized) subject { get api("/snippets/#{snippet_id}", user) }
end
end
context 'with the author' do context 'with the author' do
let(:user) { author }
it 'returns snippet json' do it 'returns snippet json' do
subject subject
...@@ -191,18 +153,10 @@ RSpec.describe API::Snippets do ...@@ -191,18 +153,10 @@ RSpec.describe API::Snippets do
end end
end end
context 'with an admin' do context 'with a non-existent snippet ID' do
let(:user) { admin } let(:snippet_id) { 0 }
it 'shows private snippets to an admin' do
subject
expect(response).to have_gitlab_http_status(:ok)
end
it 'returns 404 for invalid snippet id' do
private_snippet.destroy!
it 'returns 404' do
subject subject
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
...@@ -210,19 +164,11 @@ RSpec.describe API::Snippets do ...@@ -210,19 +164,11 @@ RSpec.describe API::Snippets do
end end
end end
context 'with an internal snippet' do it_behaves_like 'snippet access with different users' do
let(:snippet) { internal_snippet } let(:path) { "/snippets/#{snippet.id}" }
it 'shows internal snippets to an ordinary user' do
subject
expect(response).to have_gitlab_http_status(:ok)
end
end end
it_behaves_like 'snippet_multiple_files feature disabled' do it_behaves_like 'snippet_multiple_files feature disabled'
let(:user) { author }
end
end end
describe 'POST /snippets/' do describe 'POST /snippets/' do
......
...@@ -8,7 +8,7 @@ module SnippetHelpers ...@@ -8,7 +8,7 @@ module SnippetHelpers
def snippet_blob_file(blob) def snippet_blob_file(blob)
{ {
"path" => blob.path, "path" => blob.path,
"raw_url" => gitlab_raw_snippet_blob_url(blob.container, blob.path) "raw_url" => gitlab_raw_snippet_blob_url(blob.container, blob.path, host: 'localhost')
} }
end end
end end
...@@ -7,14 +7,6 @@ RSpec.shared_examples 'raw snippet files' do ...@@ -7,14 +7,6 @@ RSpec.shared_examples 'raw snippet files' do
let(:file_path) { '%2Egitattributes' } let(:file_path) { '%2Egitattributes' }
let(:ref) { 'master' } let(:ref) { 'master' }
context 'with no user' do
it 'requires authentication' do
get api(api_path)
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
shared_examples 'not found' do shared_examples 'not found' do
it 'returns 404' do it 'returns 404' do
get api(api_path, user) get api(api_path, user)
...@@ -216,3 +208,58 @@ RSpec.shared_examples 'invalid snippet updates' do ...@@ -216,3 +208,58 @@ RSpec.shared_examples 'invalid snippet updates' do
expect(json_response['error']).to eq 'title is empty' expect(json_response['error']).to eq 'title is empty'
end end
end end
RSpec.shared_examples 'snippet access with different users' do
using RSpec::Parameterized::TableSyntax
where(:requester, :visibility, :status) do
:admin | :public | :ok
:admin | :private | :ok
:admin | :internal | :ok
:author | :public | :ok
:author | :private | :ok
:author | :internal | :ok
:other | :public | :ok
:other | :private | :not_found
:other | :internal | :ok
nil | :public | :ok
nil | :private | :not_found
nil | :internal | :not_found
end
with_them do
let(:snippet) { snippet_for(visibility) }
it 'returns the correct response' do
request_user = user_for(requester)
get api(path, request_user)
expect(response).to have_gitlab_http_status(status)
end
end
def user_for(user_type)
case user_type
when :author
user
when :other
other_user
when :admin
admin
else
nil
end
end
def snippet_for(snippet_type)
case snippet_type
when :private
private_snippet
when :internal
internal_snippet
when :public
public_snippet
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment