mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
395 lines
9.2 KiB
Ruby
395 lines
9.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# -- copyright
|
|
# OpenProject is an open source project management software.
|
|
# Copyright (C) the OpenProject GmbH
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License version 3.
|
|
#
|
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
|
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
# Copyright (C) 2010-2013 the ChiliProject Team
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
# See COPYRIGHT and LICENSE files for more details.
|
|
# ++
|
|
|
|
require "spec_helper"
|
|
|
|
RSpec.describe ApplicationController, "enforcement of authorization" do # rubocop:disable RSpec/SpecFilePathFormat
|
|
shared_let(:user) { create(:user) }
|
|
|
|
controller_setup = Module.new do
|
|
extend ActiveSupport::Concern
|
|
|
|
included do
|
|
def index
|
|
render plain: "OK"
|
|
end
|
|
|
|
private
|
|
|
|
def require_admin; end
|
|
def authorize_global; end
|
|
|
|
def authorize; end
|
|
|
|
def load_and_authorize_in_optional_project; end
|
|
|
|
def other_before_action; end
|
|
end
|
|
end
|
|
|
|
current_user { user }
|
|
|
|
shared_examples "succeeds" do |action_name = :index|
|
|
it "succeeds" do
|
|
get action_name
|
|
|
|
expect(response)
|
|
.to have_http_status :ok
|
|
end
|
|
end
|
|
|
|
shared_examples "is prevented" do |action_name = :index|
|
|
it "fails with a RuntimeError" do
|
|
expect { get action_name }
|
|
.to raise_error RuntimeError
|
|
end
|
|
end
|
|
|
|
context "without authorization or authorization forfeiting" do
|
|
controller do
|
|
include controller_setup
|
|
end
|
|
|
|
it_behaves_like "is prevented"
|
|
end
|
|
|
|
context "with authorization checked with require_admin" do
|
|
controller do
|
|
before_action :require_admin
|
|
|
|
include controller_setup
|
|
end
|
|
|
|
it_behaves_like "succeeds"
|
|
end
|
|
|
|
context "with authorization checked with authorize_global" do
|
|
controller do
|
|
before_action :authorize_global
|
|
|
|
include controller_setup
|
|
end
|
|
|
|
it_behaves_like "succeeds"
|
|
end
|
|
|
|
context "with authorization checked with authorize" do
|
|
controller do
|
|
before_action :authorize
|
|
|
|
include controller_setup
|
|
end
|
|
|
|
it_behaves_like "succeeds"
|
|
end
|
|
|
|
context "with authorization checked with authorize_with_permission" do
|
|
controller do
|
|
include Accounts::Authorization
|
|
include controller_setup
|
|
|
|
authorize_with_permission :view_project
|
|
|
|
def do_authorize(*)
|
|
true
|
|
end
|
|
end
|
|
|
|
it_behaves_like "succeeds"
|
|
|
|
it "calls the authorization method" do
|
|
allow(controller).to receive(:do_authorize)
|
|
|
|
get :index
|
|
|
|
expect(controller).to have_received(:do_authorize).with(:view_project, global: false)
|
|
end
|
|
end
|
|
|
|
context "with authorization checked with authorize_with_permission on a single action" do
|
|
controller do
|
|
include Accounts::Authorization
|
|
include controller_setup
|
|
|
|
authorize_with_permission :view_project, only: %i[test]
|
|
|
|
def test
|
|
render plain: "OK"
|
|
end
|
|
|
|
def do_authorize(*)
|
|
true
|
|
end
|
|
end
|
|
|
|
before do
|
|
@routes.draw do # rubocop:disable RSpec/InstanceVariable
|
|
get "/anonymous/index"
|
|
get "/anonymous/test"
|
|
end
|
|
end
|
|
|
|
it "allows calling test" do
|
|
allow(controller).to receive(:do_authorize)
|
|
|
|
get :test
|
|
|
|
expect(controller).to have_received(:do_authorize).with(:view_project, global: false)
|
|
end
|
|
|
|
it_behaves_like "is prevented", :index
|
|
end
|
|
|
|
context "with authorization checked with authorize_with_global_permission" do
|
|
controller do
|
|
include Accounts::Authorization
|
|
include controller_setup
|
|
|
|
authorize_with_global_permission :view_project
|
|
|
|
def do_authorize(*)
|
|
true
|
|
end
|
|
end
|
|
|
|
it "does not load the project and calls global authorization" do
|
|
allow(controller).to receive(:do_authorize)
|
|
allow(Project).to receive(:find)
|
|
|
|
get :index, params: { project_id: "1" }
|
|
|
|
expect(Project).not_to have_received(:find)
|
|
expect(controller).to have_received(:do_authorize).with(:view_project, global: true)
|
|
end
|
|
end
|
|
|
|
context "with authorization checked with load_and_authorize_with_permission_in_project" do
|
|
controller do
|
|
include Accounts::Authorization
|
|
include controller_setup
|
|
|
|
load_and_authorize_with_permission_in_project :view_project
|
|
|
|
def do_authorize(*)
|
|
true
|
|
end
|
|
end
|
|
|
|
it "loads the project and calls project authorization" do
|
|
allow(controller).to receive(:do_authorize)
|
|
allow(Project).to receive(:find)
|
|
|
|
get :index, params: { project_id: "1" }
|
|
|
|
expect(Project).to have_received(:find).with("1")
|
|
expect(controller).to have_received(:do_authorize).with(:view_project, global: false)
|
|
end
|
|
|
|
it "renders 404 if project_id is missing" do
|
|
allow(controller).to receive(:do_authorize)
|
|
|
|
get :index
|
|
|
|
expect(response).to have_http_status :not_found
|
|
expect(controller).not_to have_received(:do_authorize)
|
|
end
|
|
end
|
|
|
|
context "with authorization checked with load_and_authorize_in_optional_project" do
|
|
controller do
|
|
before_action :load_and_authorize_in_optional_project
|
|
|
|
include controller_setup
|
|
end
|
|
|
|
it_behaves_like "succeeds"
|
|
end
|
|
|
|
context "with authorization checked via prepend_before_action" do
|
|
controller do
|
|
prepend_before_action :authorize
|
|
|
|
include controller_setup
|
|
end
|
|
|
|
it_behaves_like "succeeds"
|
|
end
|
|
|
|
context "with authorization checked via append_before_action" do
|
|
controller do
|
|
append_before_action :authorize
|
|
|
|
include controller_setup
|
|
end
|
|
|
|
it_behaves_like "succeeds"
|
|
end
|
|
|
|
context "with another before action specified" do
|
|
controller do
|
|
before_action :other_before_action
|
|
|
|
include controller_setup
|
|
end
|
|
|
|
it_behaves_like "is prevented"
|
|
end
|
|
|
|
context "with authorization check in the superclass" do
|
|
controller(described_class) do
|
|
before_action :require_admin
|
|
end
|
|
|
|
controller(controller_class) do
|
|
include controller_setup
|
|
end
|
|
|
|
it_behaves_like "succeeds"
|
|
end
|
|
|
|
context "when forfeiting authorization checks on this specific action" do
|
|
controller do
|
|
no_authorization_required! :index
|
|
|
|
include controller_setup
|
|
end
|
|
|
|
it_behaves_like "succeeds"
|
|
end
|
|
|
|
context "when stating that authorization has been checked in the superclass" do
|
|
controller(described_class) do
|
|
authorization_checked! :index
|
|
end
|
|
|
|
controller(controller_class) do
|
|
include controller_setup
|
|
end
|
|
|
|
it_behaves_like "succeeds"
|
|
end
|
|
|
|
context "when forfeiting authorization checks in the superclass" do
|
|
controller(described_class) do
|
|
no_authorization_required! :index
|
|
end
|
|
|
|
controller(controller_class) do
|
|
include controller_setup
|
|
end
|
|
|
|
it_behaves_like "succeeds"
|
|
end
|
|
|
|
context "when forfeiting authorization checks on another action" do
|
|
controller do
|
|
no_authorization_required! :some_other_action
|
|
|
|
include controller_setup
|
|
end
|
|
|
|
it_behaves_like "is prevented"
|
|
end
|
|
|
|
context "with authorization checked on another action with only" do
|
|
controller do
|
|
before_action :require_admin, only: %i[some_other_action]
|
|
|
|
include controller_setup
|
|
end
|
|
|
|
it_behaves_like "is prevented"
|
|
end
|
|
|
|
context "with authorization checked on the action with only" do
|
|
controller do
|
|
before_action :require_admin, only: %i[index]
|
|
|
|
include controller_setup
|
|
end
|
|
|
|
it_behaves_like "succeeds"
|
|
end
|
|
|
|
context "with authorization checked on all but this action with except" do
|
|
controller do
|
|
before_action :require_admin, except: %i[index]
|
|
|
|
include controller_setup
|
|
end
|
|
|
|
it_behaves_like "is prevented"
|
|
end
|
|
|
|
context "with authorization checked on all but another action with except" do
|
|
controller do
|
|
before_action :require_admin, except: %i[another_action]
|
|
|
|
def another_action; end
|
|
|
|
include controller_setup
|
|
end
|
|
|
|
it_behaves_like "succeeds"
|
|
end
|
|
|
|
context "with authorization checked in a sibling class" do
|
|
# Superclass
|
|
controller do
|
|
include controller_setup
|
|
end
|
|
|
|
anonymous_superclass = controller_class
|
|
|
|
# Sibling class
|
|
controller(anonymous_superclass) do
|
|
before_action :require_admin
|
|
end
|
|
|
|
# actually tested class
|
|
controller(anonymous_superclass) do
|
|
# Nothing extra
|
|
end
|
|
|
|
it_behaves_like "is prevented"
|
|
end
|
|
|
|
context "with authorization checked by a number of different actions" do
|
|
controller do
|
|
before_action :require_admin, except: %i[index]
|
|
before_action :authorize, only: %i[index]
|
|
|
|
include controller_setup
|
|
end
|
|
|
|
it_behaves_like "succeeds"
|
|
end
|
|
end
|