mirror of
https://github.com/yingziwu/mastodon.git
synced 2026-02-20 17:23:17 +00:00
Merge tag 'v4.2.0-beta2'
This commit is contained in:
commit
9e38d55101
3010 changed files with 81215 additions and 55173 deletions
|
|
@ -4,13 +4,11 @@ class DomainValidator < ActiveModel::EachValidator
|
|||
def validate_each(record, attribute, value)
|
||||
return if value.blank?
|
||||
|
||||
domain = begin
|
||||
if options[:acct]
|
||||
value.split('@').last
|
||||
else
|
||||
value
|
||||
end
|
||||
end
|
||||
domain = if options[:acct]
|
||||
value.split('@').last
|
||||
else
|
||||
value
|
||||
end
|
||||
|
||||
record.errors.add(attribute, I18n.t('domain_validator.invalid_domain')) unless compliant?(domain)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ class Ed25519KeyValidator < ActiveModel::EachValidator
|
|||
|
||||
key = Base64.decode64(value)
|
||||
|
||||
record.errors[attribute] << I18n.t('crypto.errors.invalid_key') unless verified?(key)
|
||||
record.errors.add(attribute, I18n.t('crypto.errors.invalid_key')) unless verified?(key)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ class Ed25519SignatureValidator < ActiveModel::EachValidator
|
|||
signature = Base64.decode64(value)
|
||||
message = option_to_value(record, :message)
|
||||
|
||||
record.errors[attribute] << I18n.t('crypto.errors.invalid_signature') unless verified?(verify_key, signature, message)
|
||||
record.errors.add(attribute, I18n.t('crypto.errors.invalid_signature')) unless verified?(verify_key, signature, message)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@ class EmailMxValidator < ActiveModel::Validator
|
|||
|
||||
domain = get_domain(user.email)
|
||||
|
||||
if domain.blank?
|
||||
user.errors.add(:email, :invalid)
|
||||
elsif domain.include?('..')
|
||||
if domain.blank? || domain.include?('..')
|
||||
user.errors.add(:email, :invalid)
|
||||
elsif !on_allowlist?(domain)
|
||||
resolved_ips, resolved_domains = resolve_mx(domain)
|
||||
|
|
|
|||
|
|
@ -4,15 +4,13 @@ class ExistingUsernameValidator < ActiveModel::EachValidator
|
|||
def validate_each(record, attribute, value)
|
||||
return if value.blank?
|
||||
|
||||
usernames_and_domains = begin
|
||||
value.split(',').map do |str|
|
||||
username, domain = str.strip.gsub(/\A@/, '').split('@', 2)
|
||||
domain = nil if TagManager.instance.local_domain?(domain)
|
||||
usernames_and_domains = value.split(',').filter_map do |str|
|
||||
username, domain = str.strip.gsub(/\A@/, '').split('@', 2)
|
||||
domain = nil if TagManager.instance.local_domain?(domain)
|
||||
|
||||
next if username.blank?
|
||||
next if username.blank?
|
||||
|
||||
[str, username, domain]
|
||||
end.compact
|
||||
[str, username, domain]
|
||||
end
|
||||
|
||||
usernames_with_no_accounts = usernames_and_domains.filter_map do |(str, username, domain)|
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ class FollowLimitValidator < ActiveModel::Validator
|
|||
|
||||
def validate(follow)
|
||||
return if follow.account.nil? || !follow.account.local?
|
||||
|
||||
follow.errors.add(:base, I18n.t('users.follow_limit_reached', limit: self.class.limit_for_account(follow.account))) if limit_reached?(follow.account)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class HtmlValidator < ActiveModel::EachValidator
|
||||
ERROR_RE = /Opening and ending tag mismatch|Unexpected end tag/
|
||||
|
||||
def validate_each(record, attribute, value)
|
||||
return if value.blank?
|
||||
|
||||
errors = html_errors(value)
|
||||
|
||||
record.errors.add(attribute, I18n.t('html_validator.invalid_markup', error: errors.first.to_s)) unless errors.empty?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def html_errors(str)
|
||||
fragment = Nokogiri::HTML.fragment(options[:wrap_with] ? "<#{options[:wrap_with]}>#{str}</#{options[:wrap_with]}>" : str)
|
||||
fragment.errors.select { |error| ERROR_RE.match?(error.message) }
|
||||
end
|
||||
end
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'csv'
|
||||
|
||||
class ImportValidator < ActiveModel::Validator
|
||||
KNOWN_HEADERS = [
|
||||
'Account address',
|
||||
'#domain',
|
||||
'#uri',
|
||||
].freeze
|
||||
|
||||
def validate(import)
|
||||
return if import.type.blank? || import.data.blank?
|
||||
|
||||
# We parse because newlines could be part of individual rows. This
|
||||
# runs on create so we should be reading the local file here before
|
||||
# it is uploaded to object storage or moved anywhere...
|
||||
csv_data = CSV.parse(import.data.queued_for_write[:original].read)
|
||||
|
||||
row_count = csv_data.size
|
||||
row_count -= 1 if KNOWN_HEADERS.include?(csv_data.first&.first)
|
||||
|
||||
import.errors.add(:data, I18n.t('imports.errors.over_rows_processing_limit', count: ImportService::ROWS_PROCESSING_LIMIT)) if row_count > ImportService::ROWS_PROCESSING_LIMIT
|
||||
|
||||
case import.type
|
||||
when 'following'
|
||||
validate_following_import(import, row_count)
|
||||
end
|
||||
rescue CSV::MalformedCSVError
|
||||
import.errors.add(:data, :malformed)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_following_import(import, row_count)
|
||||
base_limit = FollowLimitValidator.limit_for_account(import.account)
|
||||
|
||||
limit = begin
|
||||
if import.overwrite?
|
||||
base_limit
|
||||
else
|
||||
base_limit - import.account.following_count
|
||||
end
|
||||
end
|
||||
|
||||
import.errors.add(:data, I18n.t('users.follow_limit_reached', limit: base_limit)) if row_count > limit
|
||||
end
|
||||
end
|
||||
|
|
@ -4,18 +4,20 @@ class LanguageValidator < ActiveModel::EachValidator
|
|||
include LanguagesHelper
|
||||
|
||||
def validate_each(record, attribute, value)
|
||||
record.errors.add(attribute, :invalid) unless valid?(value)
|
||||
@value = value
|
||||
|
||||
record.errors.add(attribute, :invalid) unless valid_locale_value?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def valid?(str)
|
||||
if str.nil?
|
||||
def valid_locale_value?
|
||||
if @value.nil?
|
||||
true
|
||||
elsif str.is_a?(Array)
|
||||
str.all? { |x| valid_locale?(x) }
|
||||
elsif @value.is_a?(Array)
|
||||
@value.all? { |x| valid_locale?(x) }
|
||||
else
|
||||
valid_locale?(str)
|
||||
valid_locale?(@value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class StatusLengthValidator < ActiveModel::Validator
|
|||
|
||||
def rewrite_entities(str, entities)
|
||||
entities.sort_by! { |entity| entity[:indices].first }
|
||||
result = ''.dup
|
||||
result = +''
|
||||
|
||||
last_index = entities.reduce(0) do |index, entity|
|
||||
result << str[index...entity[:indices].first]
|
||||
|
|
@ -53,7 +53,7 @@ class StatusLengthValidator < ActiveModel::Validator
|
|||
entity[:indices].last
|
||||
end
|
||||
|
||||
result << str[last_index..-1]
|
||||
result << str[last_index..]
|
||||
result
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,12 +13,14 @@ class UnreservedUsernameValidator < ActiveModel::Validator
|
|||
|
||||
def pam_controlled?
|
||||
return false unless Devise.pam_authentication && Devise.pam_controlled_service
|
||||
|
||||
Rpam2.account(Devise.pam_controlled_service, @username).present?
|
||||
end
|
||||
|
||||
def reserved_username?
|
||||
return true if pam_controlled?
|
||||
return false unless Setting.reserved_usernames
|
||||
|
||||
Setting.reserved_usernames.include?(@username.downcase)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,16 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class URLValidator < ActiveModel::EachValidator
|
||||
VALID_SCHEMES = %w(http https).freeze
|
||||
|
||||
def validate_each(record, attribute, value)
|
||||
record.errors.add(attribute, :invalid) unless compliant?(value)
|
||||
@value = value
|
||||
|
||||
record.errors.add(attribute, :invalid) unless compliant_url?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def compliant?(url)
|
||||
parsed_url = Addressable::URI.parse(url)
|
||||
parsed_url && %w(http https).include?(parsed_url.scheme) && parsed_url.host
|
||||
def compliant_url?
|
||||
parsed_url.present? && valid_url_scheme? && valid_url_host?
|
||||
end
|
||||
|
||||
def parsed_url
|
||||
Addressable::URI.parse(@value)
|
||||
rescue Addressable::URI::InvalidURIError
|
||||
false
|
||||
end
|
||||
|
||||
def valid_url_scheme?
|
||||
VALID_SCHEMES.include?(parsed_url.scheme)
|
||||
end
|
||||
|
||||
def valid_url_host?
|
||||
parsed_url.host.present?
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,19 +2,27 @@
|
|||
|
||||
class VoteValidator < ActiveModel::Validator
|
||||
def validate(vote)
|
||||
vote.errors.add(:base, I18n.t('polls.errors.expired')) if vote.poll.expired?
|
||||
vote.errors.add(:base, I18n.t('polls.errors.expired')) if vote.poll_expired?
|
||||
vote.errors.add(:base, I18n.t('polls.errors.invalid_choice')) if invalid_choice?(vote)
|
||||
vote.errors.add(:base, I18n.t('polls.errors.self_vote')) if self_vote?(vote)
|
||||
|
||||
if vote.poll.multiple? && vote.poll.votes.where(account: vote.account, choice: vote.choice).exists?
|
||||
vote.errors.add(:base, I18n.t('polls.errors.already_voted'))
|
||||
elsif !vote.poll.multiple? && vote.poll.votes.where(account: vote.account).exists?
|
||||
vote.errors.add(:base, I18n.t('polls.errors.already_voted'))
|
||||
end
|
||||
vote.errors.add(:base, I18n.t('polls.errors.already_voted')) if additional_voting_not_allowed?(vote)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def additional_voting_not_allowed?(vote)
|
||||
poll_multiple_and_already_voted?(vote) || poll_non_multiple_and_already_voted?(vote)
|
||||
end
|
||||
|
||||
def poll_multiple_and_already_voted?(vote)
|
||||
vote.poll_multiple? && already_voted_for_same_choice_on_multiple_poll?(vote)
|
||||
end
|
||||
|
||||
def poll_non_multiple_and_already_voted?(vote)
|
||||
!vote.poll_multiple? && already_voted_on_non_multiple_poll?(vote)
|
||||
end
|
||||
|
||||
def invalid_choice?(vote)
|
||||
vote.choice.negative? || vote.choice >= vote.poll.options.size
|
||||
end
|
||||
|
|
@ -22,4 +30,24 @@ class VoteValidator < ActiveModel::Validator
|
|||
def self_vote?(vote)
|
||||
vote.account_id == vote.poll.account_id
|
||||
end
|
||||
|
||||
def already_voted_for_same_choice_on_multiple_poll?(vote)
|
||||
if vote.persisted?
|
||||
account_votes_on_same_poll(vote).where(choice: vote.choice).where.not(poll_votes: { id: vote }).exists?
|
||||
else
|
||||
account_votes_on_same_poll(vote).where(choice: vote.choice).exists?
|
||||
end
|
||||
end
|
||||
|
||||
def already_voted_on_non_multiple_poll?(vote)
|
||||
if vote.persisted?
|
||||
account_votes_on_same_poll(vote).where.not(poll_votes: { id: vote }).exists?
|
||||
else
|
||||
account_votes_on_same_poll(vote).exists?
|
||||
end
|
||||
end
|
||||
|
||||
def account_votes_on_same_poll(vote)
|
||||
vote.poll.votes.where(account: vote.account)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue