diff --git a/app/controllers/admin/base_controller.rb b/app/controllers/admin/base_controller.rb index 14338dd2938..42965696da1 100644 --- a/app/controllers/admin/base_controller.rb +++ b/app/controllers/admin/base_controller.rb @@ -7,6 +7,7 @@ module Admin layout 'admin' + before_action :require_moderator_or_admin_permissions before_action :set_referrer_policy_header after_action :verify_authorized @@ -20,5 +21,10 @@ module Admin def set_user @user = Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound) end + + def require_moderator_or_admin_permissions + # not using #authorize here, so #verify_authorized still makes sure more fine-grained rules are enforced down the line + forbidden unless Admin::BasePolicy.new(current_account, :base).access? + end end end diff --git a/app/policies/admin/base_policy.rb b/app/policies/admin/base_policy.rb new file mode 100644 index 00000000000..77a458c0a60 --- /dev/null +++ b/app/policies/admin/base_policy.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class Admin::BasePolicy < ApplicationPolicy + def access? + role.administrator? || role.can?(*UserRole::Flags::CATEGORIES.fetch_values(:moderation, :administration, :devops).flatten) + end +end diff --git a/spec/policies/admin/base_policy_spec.rb b/spec/policies/admin/base_policy_spec.rb new file mode 100644 index 00000000000..3cd89d38be6 --- /dev/null +++ b/spec/policies/admin/base_policy_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Admin::BasePolicy do + let(:policy) { described_class } + let(:custom_devops_role) do + Fabricate(:user_role, permissions_as_keys: [:view_devops]) + end + let(:custom_role_with_one_admin_permission) do + Fabricate(:user_role, permissions_as_keys: [:manage_announcements]) + end + let(:custom_role_with_one_moderator_permission) do + Fabricate(:user_role, permissions_as_keys: [:manage_invites]) + end + let(:admin) { Fabricate(:admin_user).account } + let(:moderator) { Fabricate(:moderator_user).account } + let(:devops_user) { Fabricate(:user, role: custom_devops_role).account } + let(:custom_admin) { Fabricate(:user, role: custom_role_with_one_admin_permission).account } + let(:custom_moderator) { Fabricate(:user, role: custom_role_with_one_moderator_permission).account } + let(:regular_user) { Fabricate(:account) } + + permissions :access? do + context 'with an admin' do + it 'permits' do + expect(policy).to permit(admin, :base) + end + end + + context 'with a moderator' do + it 'permits' do + expect(policy).to permit(moderator, :base) + end + end + + context 'with a user with devops permissions' do + it 'permits' do + expect(policy).to permit(devops_user, :base) + end + end + + context 'with a user with a custom role that grants a single admin permission' do + it 'permits' do + expect(policy).to permit(custom_admin, :base) + end + end + + context 'with a user with a custom role that grants a single moderator permission' do + it 'permits' do + expect(policy).to permit(custom_moderator, :base) + end + end + + context 'with a non-admin' do + it 'denies' do + expect(policy).to_not permit(regular_user, :base) + end + end + end +end