123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687 |
- # frozen_string_literal: true
- class Account::Field < ActiveModelSerializers::Model
- MAX_CHARACTERS_LOCAL = 255
- MAX_CHARACTERS_COMPAT = 2_047
- ACCEPTED_SCHEMES = %w(https).freeze
- attributes :name, :value, :verified_at, :account
- def initialize(account, attributes)
- # Keeping this as reference allows us to update the field on the account
- # from methods in this class, so that changes can be saved.
- @original_field = attributes
- @account = account
- super(
- name: sanitize(attributes['name']),
- value: sanitize(attributes['value']),
- verified_at: attributes['verified_at']&.to_datetime,
- )
- end
- def verified?
- verified_at.present?
- end
- def value_for_verification
- @value_for_verification ||= if account.local?
- value
- else
- extract_url_from_html
- end
- end
- def verifiable?
- return false if value_for_verification.blank?
- # This is slower than checking through a regular expression, but we
- # need to confirm that it's not an IDN domain.
- parsed_url = Addressable::URI.parse(value_for_verification)
- ACCEPTED_SCHEMES.include?(parsed_url.scheme) &&
- parsed_url.user.nil? &&
- parsed_url.password.nil? &&
- parsed_url.host.present? &&
- parsed_url.normalized_host == parsed_url.host &&
- (parsed_url.path.empty? || parsed_url.path == parsed_url.normalized_path)
- rescue Addressable::URI::InvalidURIError, IDN::Idna::IdnaError
- false
- end
- def requires_verification?
- !verified? && verifiable?
- end
- def mark_verified!
- @original_field['verified_at'] = self.verified_at = Time.now.utc
- end
- def to_h
- { name: name, value: value, verified_at: verified_at }
- end
- private
- def sanitize(str)
- str.strip[0, character_limit]
- end
- def character_limit
- account.local? ? MAX_CHARACTERS_LOCAL : MAX_CHARACTERS_COMPAT
- end
- def extract_url_from_html
- doc = Nokogiri::HTML5.fragment(value)
- return if doc.nil?
- return if doc.children.size != 1
- element = doc.children.first
- return if element.name != 'a' || element['href'] != element.text
- element['href']
- end
- end
|