users: add stricter checks for user promotions.

New rules for user promotions:

* Moderators can no longer promote other users to moderator level. Only
  Admins can promote users to Mod level. Mods can only promote up to Builder level.
* Admins can no longer promote other users to Admin level. Only Owners
  can promote users to Admin. Admins can only promote up to Mod level.
* Admins can no longer demote themselves or other admins.

These rules are being changed to account for the new Owner user level.

Also change it so that when a user upgrades their account, the promotion
is done by DanbooruBot. This means that the inviter and the mod action
will show DanbooruBot as the promoter instead of the user themselves.
This commit is contained in:
evazion
2020-12-13 16:54:45 -06:00
parent b3ad13e6e3
commit c82e05d828
7 changed files with 131 additions and 38 deletions

View File

@@ -6,7 +6,13 @@ module Admin
def update def update
@user = authorize User.find(params[:id]), :promote? @user = authorize User.find(params[:id]), :promote?
@user.promote_to!(params[:user][:level], params[:user])
@level = params.dig(:user, :level)
@can_upload_free = params.dig(:user, :can_upload_free)
@can_approve_posts = params.dig(:user, :can_approve_posts)
@user.promote_to!(@level, CurrentUser.user, can_upload_free: @can_upload_free, can_approve_posts: @can_approve_posts)
redirect_to edit_admin_user_path(@user), :notice => "User updated" redirect_to edit_admin_user_path(@user), :notice => "User updated"
end end
end end

View File

@@ -48,7 +48,7 @@ class UserUpgradesController < ApplicationController
:card => params[:stripeToken], :card => params[:stripeToken],
:description => params[:desc] :description => params[:desc]
) )
@user.promote_to!(level, is_upgrade: true) @user.promote_to!(level, User.system, is_upgrade: true)
flash[:success] = true flash[:success] = true
rescue Stripe::CardError => e rescue Stripe::CardError => e
DanbooruLogger.log(e) DanbooruLogger.log(e)

View File

@@ -1,33 +1,28 @@
class UserPromotion class UserPromotion
attr_reader :user, :promoter, :new_level, :options, :old_can_approve_posts, :old_can_upload_free attr_reader :user, :promoter, :new_level, :old_can_approve_posts, :old_can_upload_free, :can_upload_free, :can_approve_posts, :is_upgrade
def initialize(user, promoter, new_level, options = {}) def initialize(user, promoter, new_level, can_upload_free: nil, can_approve_posts: nil, is_upgrade: false)
@user = user @user = user
@promoter = promoter @promoter = promoter
@new_level = new_level @new_level = new_level.to_i
@options = options @can_upload_free = can_upload_free
@can_approve_posts = can_approve_posts
@is_upgrade = is_upgrade
end end
def promote! def promote!
validate validate!
@old_can_approve_posts = user.can_approve_posts? @old_can_approve_posts = user.can_approve_posts?
@old_can_upload_free = user.can_upload_free? @old_can_upload_free = user.can_upload_free?
user.level = new_level user.level = new_level
user.can_upload_free = can_upload_free unless can_upload_free.nil?
user.can_approve_posts = can_approve_posts unless can_approve_posts.nil?
user.inviter = promoter
if options.key?(:can_approve_posts) create_user_feedback unless is_upgrade
user.can_approve_posts = options[:can_approve_posts] create_dmail
end
if options.key?(:can_upload_free)
user.can_upload_free = options[:can_upload_free]
end
user.inviter_id = promoter.id
create_user_feedback unless options[:is_upgrade]
create_dmail unless options[:skip_dmail]
create_mod_actions create_mod_actions
user.save user.save
@@ -45,20 +40,21 @@ class UserPromotion
end end
if user.level_changed? if user.level_changed?
category = options[:is_upgrade] ? :user_account_upgrade : :user_level_change category = is_upgrade ? :user_account_upgrade : :user_level_change
ModAction.log(%{"#{user.name}":/users/#{user.id} level changed #{user.level_string_was} -> #{user.level_string}}, category) ModAction.log(%{"#{user.name}":/users/#{user.id} level changed #{user.level_string_was} -> #{user.level_string}}, category)
end end
end end
def validate def validate!
# admins can do anything if !promoter.is_moderator?
return if promoter.is_admin? raise User::PrivilegeError, "You can't promote or demote other users"
elsif promoter == user
# can't promote/demote moderators raise User::PrivilegeError, "You can't promote or demote yourself"
raise User::PrivilegeError if user.is_moderator? elsif new_level >= promoter.level
raise User::PrivilegeError, "You can't promote other users to your rank or above"
# can't promote to admin elsif user.level >= promoter.level
raise User::PrivilegeError if new_level.to_i >= User::Levels::ADMIN raise User::PrivilegeError, "You can't promote or demote other users at your rank or above"
end
end end
def build_messages def build_messages

View File

@@ -250,8 +250,8 @@ class User < ApplicationRecord
end end
end end
def promote_to!(new_level, options = {}) def promote_to!(new_level, promoter = CurrentUser.user, **options)
UserPromotion.new(self, CurrentUser.user, new_level, options).promote! UserPromotion.new(self, promoter, new_level, **options).promote!
end end
def promote_to_admin_if_first_user def promote_to_admin_if_first_user

View File

@@ -53,7 +53,7 @@ if User.count == 0
password: "password1", password: "password1",
password_confirmation: "password1" password_confirmation: "password1"
) )
newuser.promote_to!(User::Levels.const_get(level), :is_upgrade => true, :skip_dmail => true) newuser.promote_to!(User::Levels.const_get(level), user)
end end
newuser = User.create( newuser = User.create(

View File

@@ -24,6 +24,24 @@ class Admin::UsersControllerTest < ActionDispatch::IntegrationTest
assert_equal(@mod.id, @user.inviter_id) assert_equal(@mod.id, @user.inviter_id)
end end
should "promote the user to unrestricted uploads" do
put_auth admin_user_path(@user), @mod, params: { user: { level: User::Levels::BUILDER, can_upload_free: true }}
assert_redirected_to(edit_admin_user_path(@user.reload))
assert_equal(true, @user.is_builder?)
assert_equal(true, @user.can_upload_free?)
assert_equal(false, @user.can_approve_posts?)
end
should "promote the user to approver" do
put_auth admin_user_path(@user), @mod, params: { user: { level: User::Levels::BUILDER, can_approve_posts: true }}
assert_redirected_to(edit_admin_user_path(@user.reload))
assert_equal(true, @user.is_builder?)
assert_equal(false, @user.can_upload_free?)
assert_equal(true, @user.can_approve_posts?)
end
context "promoted to an admin" do context "promoted to an admin" do
should "fail" do should "fail" do
put_auth admin_user_path(@user), @mod, params: {:user => {:level => "50"}} put_auth admin_user_path(@user), @mod, params: {:user => {:level => "50"}}

View File

@@ -1,6 +1,19 @@
require 'test_helper' require 'test_helper'
class UserTest < ActiveSupport::TestCase class UserTest < ActiveSupport::TestCase
def assert_promoted_to(new_level, user, promoter)
user.promote_to!(new_level, promoter)
assert_equal(new_level, user.reload.level)
end
def assert_not_promoted_to(new_level, user, promoter)
assert_raise(User::PrivilegeError) do
user.promote_to!(new_level, promoter)
end
assert_not_equal(new_level, user.reload.level)
end
context "A user" do context "A user" do
setup do setup do
@user = FactoryBot.create(:user) @user = FactoryBot.create(:user)
@@ -15,14 +28,74 @@ class UserTest < ActiveSupport::TestCase
context "promoting a user" do context "promoting a user" do
setup do setup do
CurrentUser.user = FactoryBot.create(:moderator_user) @builder = create(:builder_user)
@mod = create(:moderator_user)
@admin = create(:admin_user)
@owner = create(:owner_user)
end
should "allow moderators to promote users up to builder level" do
assert_promoted_to(User::Levels::GOLD, @user, @mod)
assert_promoted_to(User::Levels::PLATINUM, @user, @mod)
assert_promoted_to(User::Levels::BUILDER, @user, @mod)
assert_not_promoted_to(User::Levels::MODERATOR, @user, @mod)
assert_not_promoted_to(User::Levels::ADMIN, @user, @mod)
assert_not_promoted_to(User::Levels::OWNER, @user, @mod)
end
should "allow admins to promote users up to moderator level" do
assert_promoted_to(User::Levels::GOLD, @user, @admin)
assert_promoted_to(User::Levels::PLATINUM, @user, @admin)
assert_promoted_to(User::Levels::BUILDER, @user, @admin)
assert_promoted_to(User::Levels::MODERATOR, @user, @admin)
assert_not_promoted_to(User::Levels::ADMIN, @user, @admin)
assert_not_promoted_to(User::Levels::OWNER, @user, @admin)
end
should "allow the owner to promote users up to admin level" do
assert_promoted_to(User::Levels::GOLD, @user, @owner)
assert_promoted_to(User::Levels::PLATINUM, @user, @owner)
assert_promoted_to(User::Levels::BUILDER, @user, @owner)
assert_promoted_to(User::Levels::MODERATOR, @user, @owner)
assert_promoted_to(User::Levels::ADMIN, @user, @owner)
assert_not_promoted_to(User::Levels::OWNER, @user, @owner)
end
should "not allow non-moderators to promote other users" do
assert_not_promoted_to(User::Levels::GOLD, @user, @builder)
assert_not_promoted_to(User::Levels::PLATINUM, @user, @builder)
assert_not_promoted_to(User::Levels::BUILDER, @user, @builder)
assert_not_promoted_to(User::Levels::MODERATOR, @user, @builder)
assert_not_promoted_to(User::Levels::ADMIN, @user, @builder)
assert_not_promoted_to(User::Levels::OWNER, @user, @builder)
end
should "not allow users to promote or demote other users at their rank or above" do
assert_not_promoted_to(User::Levels::ADMIN, create(:moderator_user), @mod)
assert_not_promoted_to(User::Levels::BUILDER, create(:moderator_user), @mod)
assert_not_promoted_to(User::Levels::OWNER, create(:admin_user), @admin)
assert_not_promoted_to(User::Levels::MODERATOR, create(:admin_user), @admin)
assert_not_promoted_to(User::Levels::ADMIN, create(:owner_user), @owner)
end
should "not allow users to promote themselves" do
assert_not_promoted_to(User::Levels::ADMIN, @mod, @mod)
assert_not_promoted_to(User::Levels::OWNER, @admin, @admin)
end
should "not allow users to demote themselves" do
assert_not_promoted_to(User::Levels::MEMBER, @mod, @mod)
assert_not_promoted_to(User::Levels::MEMBER, @admin, @admin)
assert_not_promoted_to(User::Levels::MEMBER, @owner, @owner)
end end
should "create a neutral feedback" do should "create a neutral feedback" do
assert_difference("UserFeedback.count") do @user.promote_to!(User::Levels::GOLD, @mod)
@user.promote_to!(User::Levels::GOLD)
end
assert_equal("You have been promoted to a Gold level account from Member.", @user.feedback.last.body) assert_equal("You have been promoted to a Gold level account from Member.", @user.feedback.last.body)
end end
@@ -31,7 +104,7 @@ class UserTest < ActiveSupport::TestCase
User.stubs(:system).returns(bot) User.stubs(:system).returns(bot)
assert_difference("Dmail.count", 1) do assert_difference("Dmail.count", 1) do
@user.promote_to!(User::Levels::GOLD) @user.promote_to!(User::Levels::GOLD, @admin)
end end
assert(@user.dmails.exists?(from: bot, to: @user, title: "Your account has been updated")) assert(@user.dmails.exists?(from: bot, to: @user, title: "Your account has been updated"))