Commit 298777b0 authored by Jarka Kadlecova's avatar Jarka Kadlecova

Support uploads for groups

parent 80de2bab
module UploadsActions module UploadsActions
include Gitlab::Utils::StrongMemoize
def create def create
link_to_file = UploadService.new(model, params[:file], uploader_class).execute link_to_file = UploadService.new(model, params[:file], uploader_class).execute
...@@ -24,4 +26,25 @@ module UploadsActions ...@@ -24,4 +26,25 @@ module UploadsActions
send_file uploader.file.path, disposition: disposition send_file uploader.file.path, disposition: disposition
end end
private
def uploader
strong_memoize(:uploader) do
return if show_model.nil?
file_uploader = FileUploader.new(show_model, params[:secret])
file_uploader.retrieve_from_store!(params[:filename])
file_uploader
end
end
def image_or_video?
uploader && uploader.exists? && uploader.image_or_video?
end
def uploader_class
FileUploader
end
end end
class Groups::UploadsController < Groups::ApplicationController
include UploadsActions
skip_before_action :group, if: -> { action_name == 'show' && image_or_video? }
before_action :authorize_upload_file!, only: [:create]
private
def show_model
strong_memoize(:show_model) do
group_id = params[:group_id]
Group.find_by_full_path(group_id)
end
end
def authorize_upload_file!
render_404 unless can?(current_user, :upload_file, group)
end
def uploader
strong_memoize(:uploader) do
file_uploader = uploader_class.new(show_model, params[:secret])
file_uploader.retrieve_from_store!(params[:filename])
file_uploader
end
end
def uploader_class
NamespaceFileUploader
end
alias_method :model, :group
end
...@@ -8,31 +8,13 @@ class Projects::UploadsController < Projects::ApplicationController ...@@ -8,31 +8,13 @@ class Projects::UploadsController < Projects::ApplicationController
private private
def uploader def show_model
return @uploader if defined?(@uploader) strong_memoize(:show_model) do
namespace = params[:namespace_id]
id = params[:project_id]
namespace = params[:namespace_id] Project.find_by_full_path("#{namespace}/#{id}")
id = params[:project_id]
file_project = Project.find_by_full_path("#{namespace}/#{id}")
if file_project.nil?
@uploader = nil
return
end end
@uploader = FileUploader.new(file_project, params[:secret])
@uploader.retrieve_from_store!(params[:filename])
@uploader
end
def image_or_video?
uploader && uploader.exists? && uploader.image_or_video?
end
def uploader_class
FileUploader
end end
alias_method :model, :project alias_method :model, :project
......
...@@ -344,6 +344,10 @@ class Group < Namespace ...@@ -344,6 +344,10 @@ class Group < Namespace
end end
end end
def hashed_storage?(_feature)
false
end
private private
def update_two_factor_requirement def update_two_factor_requirement
......
...@@ -40,6 +40,7 @@ class GroupPolicy < BasePolicy ...@@ -40,6 +40,7 @@ class GroupPolicy < BasePolicy
rule { guest }.policy do rule { guest }.policy do
enable :read_group enable :read_group
enable :read_list enable :read_list
enable :upload_file
end end
rule { admin } .enable :read_group rule { admin } .enable :read_group
......
...@@ -2,7 +2,7 @@ module Geo ...@@ -2,7 +2,7 @@ module Geo
class FileService class FileService
attr_reader :object_type, :object_db_id attr_reader :object_type, :object_db_id
DEFAULT_OBJECT_TYPES = %w[attachment avatar file personal_file].freeze DEFAULT_OBJECT_TYPES = %w[attachment avatar file namespace_file personal_file].freeze
DEFAULT_SERVICE_TYPE = 'file'.freeze DEFAULT_SERVICE_TYPE = 'file'.freeze
def initialize(object_type, object_db_id) def initialize(object_type, object_db_id)
......
...@@ -29,11 +29,11 @@ class FileUploader < GitlabUploader ...@@ -29,11 +29,11 @@ class FileUploader < GitlabUploader
# model - Object that responds to `full_path` and `disk_path` # model - Object that responds to `full_path` and `disk_path`
# #
# Returns a String without a trailing slash # Returns a String without a trailing slash
def self.dynamic_path_segment(project) def self.dynamic_path_segment(model)
if project.hashed_storage?(:attachments) if model.hashed_storage?(:attachments)
dynamic_path_builder(project.disk_path) dynamic_path_builder(model.disk_path)
else else
dynamic_path_builder(project.full_path) dynamic_path_builder(model.full_path)
end end
end end
......
class NamespaceFileUploader < FileUploader
def self.base_dir
File.join(root_dir, '-', 'system', 'namespace')
end
def self.dynamic_path_segment(model)
dynamic_path_builder(model.id.to_s)
end
private
def secure_url
File.join('/uploads', @secret, file.filename)
end
end
...@@ -4,4 +4,10 @@ ...@@ -4,4 +4,10 @@
- nav "group" - nav "group"
- @left_sidebar = true - @left_sidebar = true
- content_for :page_specific_javascripts do
- if current_user
-# haml-lint:disable InlineJavaScript
:javascript
window.uploads_path = "#{group_uploads_path(@group)}";
= render template: "layouts/application" = render template: "layouts/application"
...@@ -52,6 +52,12 @@ constraints(GroupUrlConstrainer.new) do ...@@ -52,6 +52,12 @@ constraints(GroupUrlConstrainer.new) do
patch :override, on: :member ## EE-specific patch :override, on: :member ## EE-specific
end end
resources :uploads, only: [:create] do
collection do
get ":secret/:filename", action: :show, as: :show, constraints: { filename: /[^\/]+/ }
end
end
## EE-specific ## EE-specific
resource :analytics, only: [:show] resource :analytics, only: [:show]
resource :ldap, only: [] do resource :ldap, only: [] do
......
...@@ -8,7 +8,7 @@ module Banzai ...@@ -8,7 +8,7 @@ module Banzai
# #
class UploadLinkFilter < HTML::Pipeline::Filter class UploadLinkFilter < HTML::Pipeline::Filter
def call def call
return doc unless project return doc unless project || group
doc.xpath('descendant-or-self::a[starts-with(@href, "/uploads/")]').each do |el| doc.xpath('descendant-or-self::a[starts-with(@href, "/uploads/")]').each do |el|
process_link_attr el.attribute('href') process_link_attr el.attribute('href')
...@@ -28,10 +28,20 @@ module Banzai ...@@ -28,10 +28,20 @@ module Banzai
end end
def build_url(uri) def build_url(uri)
if Gitlab::Geo.secondary? base_path = if Gitlab::Geo.secondary?
File.join(Gitlab::Geo.primary_node.url, project.full_path, uri) Gitlab::Geo.primary_node.url
else
Gitlab.config.gitlab.url
end
if group
urls = Gitlab::Routing.url_helpers
# we need to get last 2 parts of the uri which are secret and filename
uri_parts = uri.split(File::SEPARATOR)
file_path = urls.show_group_uploads_path(group, uri_parts[-2], uri_parts[-1])
File.join(base_path, file_path)
else else
File.join(Gitlab.config.gitlab.url, project.full_path, uri) File.join(base_path, project.full_path, uri)
end end
end end
...@@ -39,6 +49,10 @@ module Banzai ...@@ -39,6 +49,10 @@ module Banzai
context[:project] context[:project]
end end
def group
context[:group]
end
# Ensure that a :project key exists in context # Ensure that a :project key exists in context
# #
# Note that while the key might exist, its value could be nil! # Note that while the key might exist, its value could be nil!
......
module Gitlab
module Geo
class NamespaceFileDownloader < FileDownloader
end
end
end
module Gitlab
module Geo
class NamespaceFileUploader < FileUploader
end
end
end
require 'spec_helper'
describe Groups::UploadsController do
let(:model) { create(:group, :public) }
let(:params) do
{ group_id: model }
end
it_behaves_like 'handle uploads'
end
require('spec_helper') require 'spec_helper'
describe Projects::UploadsController do describe Projects::UploadsController do
let(:project) { create(:project) } let(:model) { create(:project, :public) }
let(:user) { create(:user) } let(:params) do
let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') } { namespace_id: model.namespace.to_param, project_id: model }
let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
describe "POST #create" do
before do
sign_in(user)
project.team << [user, :developer]
end
context "without params['file']" do
it "returns an error" do
post :create,
namespace_id: project.namespace.to_param,
project_id: project,
format: :json
expect(response).to have_gitlab_http_status(422)
end
end
context 'with valid image' do
before do
post :create,
namespace_id: project.namespace.to_param,
project_id: project,
file: jpg,
format: :json
end
it 'returns a content with original filename, new link, and correct type.' do
expect(response.body).to match '\"alt\":\"rails_sample\"'
expect(response.body).to match "\"url\":\"/uploads"
end
# NOTE: This is as close as we're getting to an Integration test for this
# behavior. We're avoiding a proper Feature test because those should be
# testing things entirely user-facing, which the Upload model is very much
# not.
it 'creates a corresponding Upload record' do
upload = Upload.last
aggregate_failures do
expect(upload).to exist
expect(upload.model).to eq project
end
end
end
context 'with valid non-image file' do
before do
post :create,
namespace_id: project.namespace.to_param,
project_id: project,
file: txt,
format: :json
end
it 'returns a content with original filename, new link, and correct type.' do
expect(response.body).to match '\"alt\":\"doc_sample.txt\"'
expect(response.body).to match "\"url\":\"/uploads"
end
end
end end
describe "GET #show" do it_behaves_like 'handle uploads'
let(:go) do
get :show,
namespace_id: project.namespace.to_param,
project_id: project,
secret: "123456",
filename: "image.jpg"
end
context "when the project is public" do
before do
project.update_attribute(:visibility_level, Project::PUBLIC)
end
context "when not signed in" do
context "when the file exists" do
before do
allow_any_instance_of(FileUploader).to receive(:file).and_return(jpg)
allow(jpg).to receive(:exists?).and_return(true)
end
it "responds with status 200" do
go
expect(response).to have_gitlab_http_status(200)
end
end
context "when the file doesn't exist" do
it "responds with status 404" do
go
expect(response).to have_gitlab_http_status(404)
end
end
end
context "when signed in" do
before do
sign_in(user)
end
context "when the file exists" do
before do
allow_any_instance_of(FileUploader).to receive(:file).and_return(jpg)
allow(jpg).to receive(:exists?).and_return(true)
end
it "responds with status 200" do
go
expect(response).to have_gitlab_http_status(200)
end
end
context "when the file doesn't exist" do
it "responds with status 404" do
go
expect(response).to have_gitlab_http_status(404)
end
end
end
end
context "when the project is private" do
before do
project.update_attribute(:visibility_level, Project::PRIVATE)
end
context "when not signed in" do
context "when the file exists" do
before do
allow_any_instance_of(FileUploader).to receive(:file).and_return(jpg)
allow(jpg).to receive(:exists?).and_return(true)
end
context "when the file is an image" do
before do
allow_any_instance_of(FileUploader).to receive(:image?).and_return(true)
end
it "responds with status 200" do
go
expect(response).to have_gitlab_http_status(200)
end
end
context "when the file is not an image" do
it "redirects to the sign in page" do
go
expect(response).to redirect_to(new_user_session_path)
end
end
end
context "when the file doesn't exist" do
it "redirects to the sign in page" do
go
expect(response).to redirect_to(new_user_session_path)
end
end
end
context "when signed in" do
before do
sign_in(user)
end
context "when the user has access to the project" do
before do
project.team << [user, :master]
end
context "when the file exists" do
before do
allow_any_instance_of(FileUploader).to receive(:file).and_return(jpg)
allow(jpg).to receive(:exists?).and_return(true)
end
it "responds with status 200" do
go
expect(response).to have_gitlab_http_status(200)
end
end
context "when the file doesn't exist" do
it "responds with status 404" do
go
expect(response).to have_gitlab_http_status(404)
end
end
end
context "when the user doesn't have access to the project" do
context "when the file exists" do
before do
allow_any_instance_of(FileUploader).to receive(:file).and_return(jpg)
allow(jpg).to receive(:exists?).and_return(true)
end
context "when the file is an image" do
before do
allow_any_instance_of(FileUploader).to receive(:image?).and_return(true)
end
it "responds with status 200" do
go
expect(response).to have_gitlab_http_status(200)
end
end
context "when the file is not an image" do
it "responds with status 404" do
go
expect(response).to have_gitlab_http_status(404)
end
end
end
context "when the file doesn't exist" do
it "responds with status 404" do
go
expect(response).to have_gitlab_http_status(404)
end
end
end
end
end
end
end end
require 'spec_helper' require 'spec_helper'
feature 'Update Epic', :js do feature 'Update Epic', :js do
include DropzoneHelper
let(:user) { create(:user) } let(:user) { create(:user) }
let(:group) { create(:group, :public) } let(:group) { create(:group, :public) }
...@@ -35,29 +37,59 @@ feature 'Update Epic', :js do ...@@ -35,29 +37,59 @@ feature 'Update Epic', :js do
wait_for_requests wait_for_requests
end end
it 'updates the issue' do context 'update form' do
find('.btn-edit').click before do
find('.btn-edit').click
end
fill_in 'issuable-title', with: 'New epic title' it 'updates the epic' do
fill_in 'issue-description', with: 'New epic description' fill_in 'issuable-title', with: 'New epic title'
click_button 'Save changes' fill_in 'issue-description', with: 'New epic description'
expect(find('.issuable-details h2.title')).to have_content('New epic title') click_link('Preview')
expect(find('.issuable-details .description')).to have_content('New epic description') expect(find('.md-preview')).to have_content('New epic description')
end
it 'edits full screen' do click_button 'Save changes'
find('.btn-edit').click
find('.js-zen-enter').click
expect(page).to have_selector('.div-dropzone-wrapper.fullscreen') expect(find('.issuable-details h2.title')).to have_content('New epic title')
end expect(find('.issuable-details .description')).to have_content('New epic description')
end
# File attachment feature is not implemented yet for epics it 'edits full screen' do
it 'cannot attach files' do find('.js-zen-enter').click
find('.btn-edit').click
expect(page).to have_selector('.div-dropzone-wrapper.fullscreen')
end
it 'uploads a file when dragging into textarea' do
link_css = 'a.no-attachment-icon img[alt="banana_sample"]'
link_match = %r{/groups/#{Regexp.escape(group.full_path)}/-/uploads/\h{32}/banana_sample\.gif\z}
dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
expect(page).not_to have_selector('.uploading-container .button-attach-file') expect(page.find_field("issue-description").value).to have_content('banana_sample')
click_link('Preview')
wait_for_requests
within('.md-preview') do
link = find(link_css)['src']
expect(link).to match(link_match)
end
click_button 'Save changes'
wait_for_requests
link = find(link_css)['src']
expect(link).to match(link_match)
end
# Autocomplete is disabled for epics until #4084 is resolved
describe 'autocomplete disabled' do
it 'does not open atwho container' do
find('#issue-description').native.send_keys('@')
expect(page).not_to have_selector('.atwho-container')
end
end
end end
it 'updates the tasklist' do it 'updates the tasklist' do
...@@ -69,16 +101,6 @@ feature 'Update Epic', :js do ...@@ -69,16 +101,6 @@ feature 'Update Epic', :js do
expect(page).to have_selector('ul input[checked]', count: 1) expect(page).to have_selector('ul input[checked]', count: 1)
end end
# Autocomplete is disabled for epics until #4084 is resolved
describe 'autocomplete disabled' do
it 'does not open atwho container' do
find('.btn-edit').click
find('#issue-description').native.send_keys('@')
expect(page).not_to have_selector('.atwho-container')
end
end
end end
context 'when user with owner access displays the epic' do context 'when user with owner access displays the epic' do
......
...@@ -14,5 +14,11 @@ FactoryGirl.define do ...@@ -14,5 +14,11 @@ FactoryGirl.define do
path { "#{SecureRandom.hex}/myfile.jpg" } path { "#{SecureRandom.hex}/myfile.jpg" }
uploader "FileUploader" uploader "FileUploader"
end end
trait :namespace_upload do
path { "#{SecureRandom.hex}/myfile.jpg" }
model { build(:group) }
uploader "NamespaceFileUploader"
end
end end
end end
require 'spec_helper' require 'spec_helper'
describe Banzai::Filter::UploadLinkFilter do describe Banzai::Filter::UploadLinkFilter do
include ::EE::GeoHelpers
set(:primary) { create(:geo_node, :primary) }
set(:secondary) { create(:geo_node) }
def filter(doc, contexts = {}) def filter(doc, contexts = {})
contexts.reverse_merge!({ contexts.reverse_merge!({
project: project project: project
...@@ -89,23 +94,20 @@ describe Banzai::Filter::UploadLinkFilter do ...@@ -89,23 +94,20 @@ describe Banzai::Filter::UploadLinkFilter do
end end
context 'in a geo secondary node' do context 'in a geo secondary node' do
let(:geo_url) { 'http://geo.example.com' }
before do before do
allow(Gitlab::Geo).to receive(:secondary?) { true } stub_current_geo_node(secondary)
allow(Gitlab::Geo).to receive_message_chain(:primary_node, :url) { geo_url }
end end
it 'rebuilds relative URL for a link' do it 'rebuilds relative URL for a link' do
doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
expect(doc.at_css('a')['href']) expect(doc.at_css('a')['href'])
.to eq "#{geo_url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" .to eq "#{primary.url}#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
end end
it 'rebuilds relative URL for an image' do it 'rebuilds relative URL for an image' do
doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
expect(doc.at_css('a')['href']) expect(doc.at_css('a')['href'])
.to eq "#{geo_url}/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" .to eq "#{primary.url}#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
end end
it 'does not modify absolute URL' do it 'does not modify absolute URL' do
...@@ -115,7 +117,53 @@ describe Banzai::Filter::UploadLinkFilter do ...@@ -115,7 +117,53 @@ describe Banzai::Filter::UploadLinkFilter do
end end
end end
context 'when project context does not exist' do context 'in group context' do
let(:upload_link) { link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg') }
let(:group) { create(:group) }
let(:filter_context) { { project: nil, group: group } }
let(:relative_path) { "groups/#{group.full_path}/-/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" }
it 'rewrites the link correctly' do
doc = raw_filter(upload_link, filter_context)
expect(doc.at_css('a')['href']).to eq("#{Gitlab.config.gitlab.url}/#{relative_path}")
end
it 'rewrites the link correctly for subgroup' do
subgroup = create(:group, parent: group)
relative_path = "groups/#{subgroup.full_path}/-/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
doc = raw_filter(upload_link, { project: nil, group: subgroup })
expect(doc.at_css('a')['href']).to eq("#{Gitlab.config.gitlab.url}/#{relative_path}")
end
it 'does not modify absolute URL' do
doc = filter(link('http://example.com'), filter_context)
expect(doc.at_css('a')['href']).to eq 'http://example.com'
end
context 'in a geo secondary node' do
before do
stub_current_geo_node(secondary)
end
it 'rebuilds relative URL for a link' do
doc = filter(upload_link, filter_context)
expect(doc.at_css('a')['href']).to eq "#{primary.url}#{relative_path}"
end
it 'does not modify absolute URL' do
doc = filter(link('http://example.com'), filter_context)
expect(doc.at_css('a')['href']).to eq 'http://example.com'
end
end
end
context 'when project or group context does not exist' do
let(:upload_link) { link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg') } let(:upload_link) { link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg') }
it 'does not raise error' do it 'does not raise error' do
......
...@@ -10,6 +10,8 @@ describe GroupPolicy do ...@@ -10,6 +10,8 @@ describe GroupPolicy do
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:group) { create(:group) } let(:group) { create(:group) }
let(:guest_permissions) { [:read_group, :upload_file, :read_namespace] }
let(:reporter_permissions) { [:admin_label] } let(:reporter_permissions) { [:admin_label] }
let(:developer_permissions) { [:admin_milestones] } let(:developer_permissions) { [:admin_milestones] }
...@@ -53,6 +55,7 @@ describe GroupPolicy do ...@@ -53,6 +55,7 @@ describe GroupPolicy do
it do it do
expect_allowed(:read_group) expect_allowed(:read_group)
expect_disallowed(:upload_file)
expect_disallowed(*reporter_permissions) expect_disallowed(*reporter_permissions)
expect_disallowed(*developer_permissions) expect_disallowed(*developer_permissions)
expect_disallowed(*master_permissions) expect_disallowed(*master_permissions)
...@@ -65,7 +68,7 @@ describe GroupPolicy do ...@@ -65,7 +68,7 @@ describe GroupPolicy do
let(:current_user) { guest } let(:current_user) { guest }
it do it do
expect_allowed(:read_group, :read_namespace) expect_allowed(*guest_permissions)
expect_disallowed(*reporter_permissions) expect_disallowed(*reporter_permissions)
expect_disallowed(*developer_permissions) expect_disallowed(*developer_permissions)
expect_disallowed(*master_permissions) expect_disallowed(*master_permissions)
...@@ -77,7 +80,7 @@ describe GroupPolicy do ...@@ -77,7 +80,7 @@ describe GroupPolicy do
let(:current_user) { reporter } let(:current_user) { reporter }
it do it do
expect_allowed(:read_group, :read_namespace) expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions) expect_allowed(*reporter_permissions)
expect_disallowed(*developer_permissions) expect_disallowed(*developer_permissions)
expect_disallowed(*master_permissions) expect_disallowed(*master_permissions)
...@@ -89,7 +92,7 @@ describe GroupPolicy do ...@@ -89,7 +92,7 @@ describe GroupPolicy do
let(:current_user) { developer } let(:current_user) { developer }
it do it do
expect_allowed(:read_group, :read_namespace) expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions) expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions) expect_allowed(*developer_permissions)
expect_disallowed(*master_permissions) expect_disallowed(*master_permissions)
...@@ -101,7 +104,7 @@ describe GroupPolicy do ...@@ -101,7 +104,7 @@ describe GroupPolicy do
let(:current_user) { master } let(:current_user) { master }
it do it do
expect_allowed(:read_group, :read_namespace) expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions) expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions) expect_allowed(*developer_permissions)
expect_allowed(*master_permissions) expect_allowed(*master_permissions)
...@@ -115,7 +118,7 @@ describe GroupPolicy do ...@@ -115,7 +118,7 @@ describe GroupPolicy do
it do it do
allow(Group).to receive(:supports_nested_groups?).and_return(true) allow(Group).to receive(:supports_nested_groups?).and_return(true)
expect_allowed(:read_group, :read_namespace) expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions) expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions) expect_allowed(*developer_permissions)
expect_allowed(*master_permissions) expect_allowed(*master_permissions)
...@@ -129,7 +132,7 @@ describe GroupPolicy do ...@@ -129,7 +132,7 @@ describe GroupPolicy do
it do it do
allow(Group).to receive(:supports_nested_groups?).and_return(true) allow(Group).to receive(:supports_nested_groups?).and_return(true)
expect_allowed(:read_group, :read_namespace) expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions) expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions) expect_allowed(*developer_permissions)
expect_allowed(*master_permissions) expect_allowed(*master_permissions)
...@@ -188,7 +191,7 @@ describe GroupPolicy do ...@@ -188,7 +191,7 @@ describe GroupPolicy do
let(:current_user) { nil } let(:current_user) { nil }
it do it do
expect_disallowed(:read_group) expect_disallowed(*guest_permissions)
expect_disallowed(*reporter_permissions) expect_disallowed(*reporter_permissions)
expect_disallowed(*developer_permissions) expect_disallowed(*developer_permissions)
expect_disallowed(*master_permissions) expect_disallowed(*master_permissions)
...@@ -200,7 +203,7 @@ describe GroupPolicy do ...@@ -200,7 +203,7 @@ describe GroupPolicy do
let(:current_user) { guest } let(:current_user) { guest }
it do it do
expect_allowed(:read_group) expect_allowed(*guest_permissions)
expect_disallowed(*reporter_permissions) expect_disallowed(*reporter_permissions)
expect_disallowed(*developer_permissions) expect_disallowed(*developer_permissions)
expect_disallowed(*master_permissions) expect_disallowed(*master_permissions)
...@@ -212,7 +215,7 @@ describe GroupPolicy do ...@@ -212,7 +215,7 @@ describe GroupPolicy do
let(:current_user) { reporter } let(:current_user) { reporter }
it do it do
expect_allowed(:read_group) expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions) expect_allowed(*reporter_permissions)
expect_disallowed(*developer_permissions) expect_disallowed(*developer_permissions)
expect_disallowed(*master_permissions) expect_disallowed(*master_permissions)
...@@ -224,7 +227,7 @@ describe GroupPolicy do ...@@ -224,7 +227,7 @@ describe GroupPolicy do
let(:current_user) { developer } let(:current_user) { developer }
it do it do
expect_allowed(:read_group) expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions) expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions) expect_allowed(*developer_permissions)
expect_disallowed(*master_permissions) expect_disallowed(*master_permissions)
...@@ -236,7 +239,7 @@ describe GroupPolicy do ...@@ -236,7 +239,7 @@ describe GroupPolicy do
let(:current_user) { master } let(:current_user) { master }
it do it do
expect_allowed(:read_group) expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions) expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions) expect_allowed(*developer_permissions)
expect_allowed(*master_permissions) expect_allowed(*master_permissions)
...@@ -250,7 +253,7 @@ describe GroupPolicy do ...@@ -250,7 +253,7 @@ describe GroupPolicy do
it do it do
allow(Group).to receive(:supports_nested_groups?).and_return(true) allow(Group).to receive(:supports_nested_groups?).and_return(true)
expect_allowed(:read_group) expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions) expect_allowed(*reporter_permissions)
expect_allowed(*developer_permissions) expect_allowed(*developer_permissions)
expect_allowed(*master_permissions) expect_allowed(*master_permissions)
...@@ -262,7 +265,8 @@ describe GroupPolicy do ...@@ -262,7 +265,8 @@ describe GroupPolicy do
let(:current_user) { auditor } let(:current_user) { auditor }
it do it do
is_expected.to be_allowed(:read_group) expect_allowed(:read_group)
expect_disallowed(:upload_file)
is_expected.to be_disallowed(*master_permissions) is_expected.to be_disallowed(*master_permissions)
is_expected.to be_disallowed(*owner_permissions) is_expected.to be_disallowed(*owner_permissions)
end end
......
...@@ -138,6 +138,29 @@ describe Geo::FileDownloadService do ...@@ -138,6 +138,29 @@ describe Geo::FileDownloadService do
end end
end end
context 'with namespace file upload' do
let(:group) { create(:group) }
let(:upload) { Upload.find_by(model: group, uploader: 'NamespaceFileUploader') }
subject { described_class.new(:file, upload.id) }
before do
NamespaceFileUploader.new(group).store!(fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/png'))
end
it 'downloads the file' do
stub_transfer(Gitlab::Geo::FileTransfer, 100)
expect { subject.execute }.to change { Geo::FileRegistry.synced.count }.by(1)
end
it 'registers when the download fails' do
stub_transfer(Gitlab::Geo::FileTransfer, -1)
expect { subject.execute }.to change { Geo::FileRegistry.failed.count }.by(1)
end
end
context 'LFS object' do context 'LFS object' do
let(:lfs_object) { create(:lfs_object) } let(:lfs_object) { create(:lfs_object) }
......
...@@ -129,6 +129,35 @@ describe Geo::FileUploadService do ...@@ -129,6 +129,35 @@ describe Geo::FileUploadService do
end end
end end
context 'namespace file upload' do
let(:group) { create(:group) }
let(:upload) { Upload.find_by(model: group, uploader: 'NamespaceFileUploader') }
let(:params) { { id: upload.id, type: 'file' } }
let(:file_transfer) { Gitlab::Geo::FileTransfer.new(:file, upload) }
let(:transfer_request) { Gitlab::Geo::TransferRequest.new(file_transfer.request_data) }
let(:req_header) { transfer_request.headers['Authorization'] }
let(:file) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/png') }
before do
NamespaceFileUploader.new(group).store!(file)
end
it 'sends the file' do
service = described_class.new(params, req_header)
response = service.execute
expect(response[:code]).to eq(:ok)
expect(response[:file].path).to end_with('dk.png')
end
it 'returns nil if no authorization' do
service = described_class.new(params, nil)
expect(service.execute).to be_nil
end
end
context 'LFS Object' do context 'LFS Object' do
let(:lfs_object) { create(:lfs_object, :with_file) } let(:lfs_object) { create(:lfs_object, :with_file) }
let(:params) { { id: lfs_object.id, type: 'lfs' } } let(:params) { { id: lfs_object.id, type: 'lfs' } }
......
shared_examples 'handle uploads' do
let(:user) { create(:user) }
let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') }
let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
describe "POST #create" do
context 'when a user is not authorized to upload a file' do
it 'returns 404 status' do
post :create, params.merge(file: jpg, format: :json)
expect(response.status).to eq(404)
end
end
context 'when a user can upload a file' do
before do
sign_in(user)
model.add_developer(user)
end
context "without params['file']" do
it "returns an error" do
post :create, params.merge(format: :json)
expect(response).to have_gitlab_http_status(422)
end
end
context 'with valid image' do
before do
post :create, params.merge(file: jpg, format: :json)
end
it 'returns a content with original filename, new link, and correct type.' do
expect(response.body).to match '\"alt\":\"rails_sample\"'
expect(response.body).to match "\"url\":\"/uploads"
end
# NOTE: This is as close as we're getting to an Integration test for this
# behavior. We're avoiding a proper Feature test because those should be
# testing things entirely user-facing, which the Upload model is very much
# not.
it 'creates a corresponding Upload record' do
upload = Upload.last
aggregate_failures do
expect(upload).to exist
expect(upload.model).to eq(model)
end
end
end
context 'with valid non-image file' do
before do
post :create, params.merge(file: txt, format: :json)
end
it 'returns a content with original filename, new link, and correct type.' do
expect(response.body).to match '\"alt\":\"doc_sample.txt\"'
expect(response.body).to match "\"url\":\"/uploads"
end
end
end
end
describe "GET #show" do
let(:show_upload) do
get :show, params.merge(secret: "123456", filename: "image.jpg")
end
context "when the model is public" do
before do
model.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PUBLIC)
end
context "when not signed in" do
context "when the file exists" do
before do
allow_any_instance_of(FileUploader).to receive(:file).and_return(jpg)
allow(jpg).to receive(:exists?).and_return(true)
end
it "responds with status 200" do
show_upload
expect(response).to have_gitlab_http_status(200)
end
end
context "when the file doesn't exist" do
it "responds with status 404" do
show_upload
expect(response).to have_gitlab_http_status(404)
end
end
end
context "when signed in" do
before do
sign_in(user)
end
context "when the file exists" do
before do
allow_any_instance_of(FileUploader).to receive(:file).and_return(jpg)
allow(jpg).to receive(:exists?).and_return(true)
end
it "responds with status 200" do
show_upload
expect(response).to have_gitlab_http_status(200)
end
end
context "when the file doesn't exist" do
it "responds with status 404" do
show_upload
expect(response).to have_gitlab_http_status(404)
end
end
end
end
context "when the model is private" do
before do
model.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PRIVATE)
end
context "when not signed in" do
context "when the file exists" do
before do
allow_any_instance_of(FileUploader).to receive(:file).and_return(jpg)
allow(jpg).to receive(:exists?).and_return(true)
end
context "when the file is an image" do
before do
allow_any_instance_of(FileUploader).to receive(:image?).and_return(true)
end
it "responds with status 200" do
show_upload
expect(response).to have_gitlab_http_status(200)
end
end
context "when the file is not an image" do
it "redirects to the sign in page" do
show_upload
expect(response).to redirect_to(new_user_session_path)
end
end
end
context "when the file doesn't exist" do
it "redirects to the sign in page" do
show_upload
expect(response).to redirect_to(new_user_session_path)
end
end
end
context "when signed in" do
before do
sign_in(user)
end
context "when the user has access to the project" do
before do
model.add_developer(user)
end
context "when the file exists" do
before do
allow_any_instance_of(FileUploader).to receive(:file).and_return(jpg)
allow(jpg).to receive(:exists?).and_return(true)
end
it "responds with status 200" do
show_upload
expect(response).to have_gitlab_http_status(200)
end
end
context "when the file doesn't exist" do
it "responds with status 404" do
show_upload
expect(response).to have_gitlab_http_status(404)
end
end
end
context "when the user doesn't have access to the model" do
context "when the file exists" do
before do
allow_any_instance_of(FileUploader).to receive(:file).and_return(jpg)
allow(jpg).to receive(:exists?).and_return(true)
end
context "when the file is an image" do
before do
allow_any_instance_of(FileUploader).to receive(:image?).and_return(true)
end
it "responds with status 200" do
show_upload
expect(response).to have_gitlab_http_status(200)
end
end
context "when the file is not an image" do
it "responds with status 404" do
show_upload
expect(response).to have_gitlab_http_status(404)
end
end
end
context "when the file doesn't exist" do
it "responds with status 404" do
show_upload
expect(response).to have_gitlab_http_status(404)
end
end
end
end
end
end
end
require 'spec_helper'
describe NamespaceFileUploader do
let(:group) { build_stubbed(:group) }
let(:uploader) { described_class.new(group) }
describe "#store_dir" do
it "stores in the namespace id directory" do
expect(uploader.store_dir).to include(group.id.to_s)
end
end
describe ".absolute_path" do
it "stores in thecorrect directory" do
upload_record = create(:upload, :namespace_upload, model: group)
expect(described_class.absolute_path(upload_record))
.to include("-/system/namespace/#{group.id}")
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