discourse/app/models/username_validator.rb

121 lines
3.3 KiB
Ruby
Raw Normal View History

require_dependency 'user'
class UsernameValidator
# Public: Perform the validation of a field in a given object
# it adds the errors (if any) to the object that we're giving as parameter
#
# object - Object in which we're performing the validation
# field_name - name of the field that we're validating
# user_id - skip id while validating username uniqueness
#
# Example: UsernameValidator.perform_validation(user, 'name')
def self.perform_validation(object, field_name, user_id = nil)
validator = UsernameValidator.new(object.send(field_name), user_id)
unless validator.valid_format?
validator.errors.each { |e| object.errors.add(field_name.to_sym, e) }
end
end
def initialize(username, user_id = nil)
@username = username
@user_id = user_id
@errors = []
end
attr_accessor :errors
attr_reader :username, :user_id
def user
@user ||= User.new(user)
end
def valid_format?
username_exist?
username_length_min?
username_length_max?
username_char_valid?
username_first_char_valid?
username_last_char_valid?
username_no_double_special?
username_does_not_end_with_confusing_suffix?
username_unique?
errors.empty?
end
CONFUSING_EXTENSIONS ||= /\.(js|json|css|htm|html|xml|jpg|jpeg|png|gif|bmp|ico|tif|tiff|woff)$/i
private
def username_exist?
return unless errors.empty?
unless username
self.errors << I18n.t(:'user.username.blank')
end
end
def username_length_min?
return unless errors.empty?
if username.length < User.username_length.begin
self.errors << I18n.t(:'user.username.short', min: User.username_length.begin)
end
end
def username_length_max?
return unless errors.empty?
if username.length > User.username_length.end
self.errors << I18n.t(:'user.username.long', max: User.username_length.end)
end
end
def username_char_valid?
return unless errors.empty?
if username =~ /[^\w.-]/
self.errors << I18n.t(:'user.username.characters')
end
end
def username_first_char_valid?
return unless errors.empty?
if username[0] =~ /\W/
self.errors << I18n.t(:'user.username.must_begin_with_alphanumeric_or_underscore')
end
end
def username_last_char_valid?
return unless errors.empty?
if username[-1] =~ /[^A-Za-z0-9]/
self.errors << I18n.t(:'user.username.must_end_with_alphanumeric')
end
end
def username_no_double_special?
return unless errors.empty?
if username =~ /[-_.]{2,}/
self.errors << I18n.t(:'user.username.must_not_contain_two_special_chars_in_seq')
end
end
def username_does_not_end_with_confusing_suffix?
return unless errors.empty?
if username =~ CONFUSING_EXTENSIONS
self.errors << I18n.t(:'user.username.must_not_end_with_confusing_suffix')
end
end
def username_unique?
lower = username.downcase
sql = <<~SQL
(SELECT 1 FROM users
WHERE users.username_lower = :username#{" AND users.id != #{user_id}" if user_id.present?})
UNION ALL
(SELECT 1 FROM groups
WHERE lower(groups.name) = :username)
SQL
if User.exec_sql(sql, username: lower).values.present?
self.errors << user_id.present? ? I18n.t(:'user.username.unique') : I18n.t("activerecord.errors.messages.taken")
end
end
end