From f5e42f602f8a4eb85a7087bc0f407f9510df0ea8 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 10 Mar 2015 14:50:42 +0100 Subject: [PATCH] Reject access to group/project avatar if the user doesn't have access. --- CHANGELOG | 1 + app/controllers/uploads_controller.rb | 48 ++-- spec/controllers/uploads_controller_spec.rb | 296 ++++++++++++++++++++ 3 files changed, 329 insertions(+), 16 deletions(-) create mode 100644 spec/controllers/uploads_controller_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 81468d4013e..03fd68e299f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -35,6 +35,7 @@ v 7.9.0 (unreleased) - Move groups page from profile to dashboard - Starred projects page at dashboard - Blocking user does not remove him/her from project/groups but show blocked label + - Reject access to group/project avatar if the user doesn't have access. v 7.8.2 - Fix service migration issue when upgrading from versions prior to 7.3 diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb index 810ac9f34bd..c5f3da54ea2 100644 --- a/app/controllers/uploads_controller.rb +++ b/app/controllers/uploads_controller.rb @@ -1,24 +1,15 @@ class UploadsController < ApplicationController - skip_before_filter :authenticate_user!, :reject_blocked! - before_filter :authorize_access + skip_before_filter :authenticate_user! + before_filter :find_model, :authorize_access! def show - unless upload_model && upload_mount - return not_found! - end - - model = upload_model.find(params[:id]) - uploader = model.send(upload_mount) - - if model.respond_to?(:project) && !can?(current_user, :read_project, model.project) - return not_found! - end + uploader = @model.send(upload_mount) unless uploader.file_storage? return redirect_to uploader.url end - unless uploader.file.exists? + unless uploader.file && uploader.file.exists? return not_found! end @@ -28,9 +19,34 @@ class UploadsController < ApplicationController private - def authorize_access - unless params[:mounted_as] == 'avatar' - authenticate_user! && reject_blocked! + def find_model + unless upload_model && upload_mount + return not_found! + end + + @model = upload_model.find(params[:id]) + end + + def authorize_access! + authorized = + case @model + when Project + can?(current_user, :read_project, @model) + when Group + can?(current_user, :read_group, @model) + when Note + can?(current_user, :read_project, @model.project) + else + # No authentication required for user avatars. + true + end + + return if authorized + + if current_user + not_found! + else + authenticate_user! end end diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb new file mode 100644 index 00000000000..0f9780356b1 --- /dev/null +++ b/spec/controllers/uploads_controller_spec.rb @@ -0,0 +1,296 @@ +require 'spec_helper' + +describe UploadsController do + let!(:user) { create(:user, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + + describe "GET show" do + context "when viewing a user avatar" do + context "when signed in" do + before do + sign_in(user) + end + + context "when the user is blocked" do + before do + user.block + end + + it "redirects to the sign in page" do + get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png" + + expect(response).to redirect_to(new_user_session_path) + end + end + + context "when the user isn't blocked" do + it "responds with status 200" do + get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + end + + context "when not signed in" do + it "responds with status 200" do + get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + end + + context "when viewing a project avatar" do + let!(:project) { create(:project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + + context "when the project is public" do + before do + project.update_attribute(:visibility_level, Project::PUBLIC) + end + + context "when not signed in" do + it "responds with status 200" do + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + + context "when signed in" do + before do + sign_in(user) + end + + it "responds with status 200" do + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + + expect(response.status).to eq(200) + 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 + it "redirects to the sign in page" do + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + + expect(response).to redirect_to(new_user_session_path) + 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 user is blocked" do + before do + user.block + project.team << [user, :master] + end + + it "redirects to the sign in page" do + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + + expect(response).to redirect_to(new_user_session_path) + end + end + + context "when the user isn't blocked" do + it "responds with status 200" do + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + end + + context "when the user doesn't have access to the project" do + it "responds with status 404" do + get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" + + expect(response.status).to eq(404) + end + end + end + end + end + + context "when viewing a group avatar" do + let!(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } + let!(:project) { create(:project, namespace: group) } + + context "when the group has public projects" do + before do + project.update_attribute(:visibility_level, Project::PUBLIC) + end + + context "when not signed in" do + it "responds with status 200" do + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + + context "when signed in" do + before do + sign_in(user) + end + + it "responds with status 200" do + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + end + + context "when the project doesn't have public projects" do + context "when not signed in" do + it "redirects to the sign in page" do + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + + expect(response).to redirect_to(new_user_session_path) + 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 user is blocked" do + before do + user.block + project.team << [user, :master] + end + + it "redirects to the sign in page" do + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + + expect(response).to redirect_to(new_user_session_path) + end + end + + context "when the user isn't blocked" do + it "responds with status 200" do + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + end + + context "when the user doesn't have access to the project" do + it "responds with status 404" do + get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" + + expect(response.status).to eq(404) + end + end + end + end + end + + context "when viewing a note attachment" do + let!(:note) { create(:note, :with_attachment) } + let(:project) { note.project } + + context "when the project is public" do + before do + project.update_attribute(:visibility_level, Project::PUBLIC) + end + + context "when not signed in" do + it "responds with status 200" do + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + + context "when signed in" do + before do + sign_in(user) + end + + it "responds with status 200" do + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + + expect(response.status).to eq(200) + 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 + it "redirects to the sign in page" do + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + + expect(response).to redirect_to(new_user_session_path) + 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 user is blocked" do + before do + user.block + project.team << [user, :master] + end + + it "redirects to the sign in page" do + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + + expect(response).to redirect_to(new_user_session_path) + end + end + + context "when the user isn't blocked" do + it "responds with status 200" do + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + + expect(response.status).to eq(200) + end + end + end + + context "when the user doesn't have access to the project" do + it "responds with status 404" do + get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" + + expect(response.status).to eq(404) + end + end + end + end + end + end +end