users: don't allow users to choose reserved names.
Don't allow users to choose names that conflict with search syntax, like `any` or `none`, or names that impersonate user levels, like `Admin`, `Moderator`, `Anonymous`, etc.
This commit is contained in:
@@ -9,6 +9,13 @@
|
|||||||
class UserNameValidator < ActiveModel::EachValidator
|
class UserNameValidator < ActiveModel::EachValidator
|
||||||
ALLOWED_PUNCTUATION = "_.-" # All other punctuation characters are forbidden
|
ALLOWED_PUNCTUATION = "_.-" # All other punctuation characters are forbidden
|
||||||
|
|
||||||
|
RESERVED_NAMES = [
|
||||||
|
"any", "none", # conflicts with `approver:any` search syntax
|
||||||
|
"new", "deactivate", "custom_style", # conflicts with user routes (/users/new, /users/deactivate, /users/custom_style)
|
||||||
|
"mod", "administrator", # mod impersonation
|
||||||
|
*User::Roles.map(&:to_s) # owner, admin, moderator, anonymous, banned, etc
|
||||||
|
]
|
||||||
|
|
||||||
def validate_each(rec, attr, name)
|
def validate_each(rec, attr, name)
|
||||||
forbidden_characters = name.delete(ALLOWED_PUNCTUATION).chars.grep(/[[:punct:]]/).uniq
|
forbidden_characters = name.delete(ALLOWED_PUNCTUATION).chars.grep(/[[:punct:]]/).uniq
|
||||||
|
|
||||||
@@ -34,6 +41,8 @@ class UserNameValidator < ActiveModel::EachValidator
|
|||||||
rec.errors.add(attr, "must contain only basic letters or numbers")
|
rec.errors.add(attr, "must contain only basic letters or numbers")
|
||||||
elsif name =~ /\Auser_\d+\z/i
|
elsif name =~ /\Auser_\d+\z/i
|
||||||
rec.errors.add(attr, "can't be the same as a deleted user")
|
rec.errors.add(attr, "can't be the same as a deleted user")
|
||||||
|
elsif name.downcase.in?(RESERVED_NAMES)
|
||||||
|
rec.errors.add(attr, "is a reserved name and can't be used")
|
||||||
elsif name =~ Regexp.union(Danbooru.config.user_name_blacklist)
|
elsif name =~ Regexp.union(Danbooru.config.user_name_blacklist)
|
||||||
rec.errors.add(attr, "is not allowed")
|
rec.errors.add(attr, "is not allowed")
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class BansControllerTest < ActionDispatch::IntegrationTest
|
|||||||
|
|
||||||
context "index action" do
|
context "index action" do
|
||||||
setup do
|
setup do
|
||||||
@mod = create(:mod_user, name: "mod")
|
@mod = create(:mod_user, name: "mod123")
|
||||||
@ban1 = create(:ban, created_at: 1.week.ago, duration: 1.day)
|
@ban1 = create(:ban, created_at: 1.week.ago, duration: 1.day)
|
||||||
@ban2 = create(:ban, user: build(:builder_user), reason: "blah", banner: @mod, duration: 100.years)
|
@ban2 = create(:ban, user: build(:builder_user), reason: "blah", banner: @mod, duration: 100.years)
|
||||||
end
|
end
|
||||||
@@ -47,7 +47,7 @@ class BansControllerTest < ActionDispatch::IntegrationTest
|
|||||||
should respond_to_search(expired: "false").with { @ban2 }
|
should respond_to_search(expired: "false").with { @ban2 }
|
||||||
should respond_to_search(duration: "<1w").with { @ban1 }
|
should respond_to_search(duration: "<1w").with { @ban1 }
|
||||||
|
|
||||||
should respond_to_search(banner_name: "mod").with { @ban2 }
|
should respond_to_search(banner_name: "mod123").with { @ban2 }
|
||||||
should respond_to_search(banner: { level: User::Levels::MODERATOR }).with { @ban2 }
|
should respond_to_search(banner: { level: User::Levels::MODERATOR }).with { @ban2 }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ class PostFlagsControllerTest < ActionDispatch::IntegrationTest
|
|||||||
@user = create(:user)
|
@user = create(:user)
|
||||||
@flagger = create(:gold_user, id: 999, created_at: 2.weeks.ago)
|
@flagger = create(:gold_user, id: 999, created_at: 2.weeks.ago)
|
||||||
@uploader = create(:mod_user, name: "chen", created_at: 2.weeks.ago)
|
@uploader = create(:mod_user, name: "chen", created_at: 2.weeks.ago)
|
||||||
@mod = create(:mod_user, name: "mod")
|
@mod = create(:mod_user, name: "mod123")
|
||||||
@post = create(:post, id: 101, uploader: @uploader)
|
@post = create(:post, id: 101, uploader: @uploader)
|
||||||
@post_flag = create(:post_flag, reason: "xxx", post: @post, creator: @flagger)
|
@post_flag = create(:post_flag, reason: "xxx", post: @post, creator: @flagger)
|
||||||
end
|
end
|
||||||
@@ -113,7 +113,7 @@ class PostFlagsControllerTest < ActionDispatch::IntegrationTest
|
|||||||
@post_flag = create(:post_flag, creator: @mod, post: build(:post, uploader: @mod))
|
@post_flag = create(:post_flag, creator: @mod, post: build(:post, uploader: @mod))
|
||||||
end
|
end
|
||||||
|
|
||||||
should respond_to_search(creator_name: "mod").with { @post_flag }
|
should respond_to_search(creator_name: "mod123").with { @post_flag }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when the user is the flagger" do
|
context "when the user is the flagger" do
|
||||||
|
|||||||
@@ -67,18 +67,18 @@ class UserNameChangeRequestsControllerTest < ActionDispatch::IntegrationTest
|
|||||||
end
|
end
|
||||||
|
|
||||||
should "fail for a moderator trying to change the name of someone above Builder level" do
|
should "fail for a moderator trying to change the name of someone above Builder level" do
|
||||||
@user = create(:moderator_user, name: "mod")
|
@user = create(:moderator_user, name: "bob")
|
||||||
post_auth user_name_change_requests_path, create(:moderator_user), params: { user_name_change_request: { user_id: @user.id, desired_name: "zun" }}
|
post_auth user_name_change_requests_path, create(:moderator_user), params: { user_name_change_request: { user_id: @user.id, desired_name: "zun" }}
|
||||||
|
|
||||||
assert_response 403
|
assert_response 403
|
||||||
assert_equal("mod", @user.reload.name)
|
assert_equal("bob", @user.reload.name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "show action" do
|
context "show action" do
|
||||||
setup do
|
setup do
|
||||||
@change_request = as(@user) { create(:user_name_change_request, user_id: @user.id) }
|
@change_request = as(@user) { create(:user_name_change_request, user_id: @user.id) }
|
||||||
@user.update!(name: "user_#{@user.id}")
|
@user.update!(is_deleted: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "render" do
|
should "render" do
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ class PostVoteTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
context "deleting a vote by another user" do
|
context "deleting a vote by another user" do
|
||||||
should "leave a mod action" do
|
should "leave a mod action" do
|
||||||
admin = create(:admin_user, name: "admin")
|
admin = create(:admin_user)
|
||||||
vote = create(:post_vote, post: @post, score: 1)
|
vote = create(:post_vote, post: @post, score: 1)
|
||||||
|
|
||||||
vote.soft_delete!(updater: admin)
|
vote.soft_delete!(updater: admin)
|
||||||
@@ -114,7 +114,7 @@ class PostVoteTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
context "undeleting a vote by another user" do
|
context "undeleting a vote by another user" do
|
||||||
setup do
|
setup do
|
||||||
@admin = create(:admin_user, name: "admin")
|
@admin = create(:admin_user)
|
||||||
@vote = create(:post_vote, post: @post, score: 1)
|
@vote = create(:post_vote, post: @post, score: 1)
|
||||||
|
|
||||||
@vote.soft_delete!(updater: @admin)
|
@vote.soft_delete!(updater: @admin)
|
||||||
|
|||||||
@@ -205,6 +205,13 @@ class UserTest < ActiveSupport::TestCase
|
|||||||
user.save
|
user.save
|
||||||
assert_equal(["Name is not allowed"], user.errors.full_messages)
|
assert_equal(["Name is not allowed"], user.errors.full_messages)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
should_not allow_value("any").for(:name)
|
||||||
|
should_not allow_value("none").for(:name)
|
||||||
|
should_not allow_value("new").for(:name)
|
||||||
|
should_not allow_value("admin").for(:name)
|
||||||
|
should_not allow_value("mod").for(:name)
|
||||||
|
should_not allow_value("moderator").for(:name)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "searching for users by name" do
|
context "searching for users by name" do
|
||||||
|
|||||||
Reference in New Issue
Block a user