2019-05-03 06:17:27 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2015-08-19 05:15:46 +08:00
|
|
|
class EmbeddableHost < ActiveRecord::Base
|
2016-01-12 00:06:09 +08:00
|
|
|
validate :host_must_be_valid
|
2015-08-19 05:15:46 +08:00
|
|
|
belongs_to :category
|
2019-03-30 00:05:51 +08:00
|
|
|
after_destroy :reset_embedding_settings
|
2015-08-19 05:15:46 +08:00
|
|
|
|
|
|
|
before_validation do
|
|
|
|
self.host.sub!(/^https?:\/\//, '')
|
|
|
|
self.host.sub!(/\/.*$/, '')
|
|
|
|
end
|
|
|
|
|
2016-08-24 02:55:52 +08:00
|
|
|
def self.record_for_url(uri)
|
|
|
|
|
|
|
|
if uri.is_a?(String)
|
2018-03-28 16:20:08 +08:00
|
|
|
uri = begin
|
|
|
|
URI(UrlHelper.escape_uri(uri))
|
2018-08-14 18:23:32 +08:00
|
|
|
rescue URI::Error
|
2018-03-28 16:20:08 +08:00
|
|
|
end
|
2016-08-24 02:55:52 +08:00
|
|
|
end
|
2015-08-19 05:15:46 +08:00
|
|
|
return false unless uri.present?
|
|
|
|
|
|
|
|
host = uri.host
|
|
|
|
return false unless host.present?
|
|
|
|
|
2017-02-28 01:17:52 +08:00
|
|
|
if uri.port.present? && uri.port != 80 && uri.port != 443
|
|
|
|
host << ":#{uri.port}"
|
|
|
|
end
|
|
|
|
|
2016-08-27 00:47:21 +08:00
|
|
|
path = uri.path
|
|
|
|
path << "?" << uri.query if uri.query.present?
|
|
|
|
|
2017-02-18 01:39:33 +08:00
|
|
|
where("lower(host) = ?", host).each do |eh|
|
2017-12-13 00:56:28 +08:00
|
|
|
return eh if eh.path_whitelist.blank?
|
|
|
|
|
|
|
|
path_regexp = Regexp.new(eh.path_whitelist)
|
|
|
|
return eh if path_regexp.match(path) || path_regexp.match(URI.unescape(path))
|
2017-02-18 01:39:33 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
nil
|
|
|
|
end
|
2016-08-27 00:47:21 +08:00
|
|
|
|
2017-02-18 01:39:33 +08:00
|
|
|
def self.url_allowed?(url)
|
2017-08-03 04:43:31 +08:00
|
|
|
# Work around IFRAME reload on WebKit where the referer will be set to the Forum URL
|
2019-09-30 08:51:59 +08:00
|
|
|
return true if url&.starts_with?(Discourse.base_url) && EmbeddableHost.exists?
|
2017-08-03 04:43:31 +08:00
|
|
|
|
2018-03-28 16:20:08 +08:00
|
|
|
uri = begin
|
|
|
|
URI(UrlHelper.escape_uri(url))
|
2018-08-14 18:23:32 +08:00
|
|
|
rescue URI::Error
|
2018-03-28 16:20:08 +08:00
|
|
|
end
|
|
|
|
|
2017-02-18 01:39:33 +08:00
|
|
|
uri.present? && record_for_url(uri).present?
|
2015-08-19 05:15:46 +08:00
|
|
|
end
|
|
|
|
|
2016-01-12 00:06:09 +08:00
|
|
|
private
|
|
|
|
|
2019-03-30 00:05:51 +08:00
|
|
|
def reset_embedding_settings
|
|
|
|
unless EmbeddableHost.exists?
|
|
|
|
Embedding.settings.each { |s| SiteSetting.set(s.to_s, SiteSetting.defaults[s]) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-06-07 13:28:18 +08:00
|
|
|
def host_must_be_valid
|
Fix "Host is invalid" error when TLD >10 chars (#7948)
Related to https://meta.discourse.org/t/host-is-invalid-error-when-tld-is-longer-than-7-characters/46081.
Using Discourse `v2.4.0.beta2 +119`, I can't add an host (when embedding, cf. `/admin/customize/embedding`) ending with `.engineering`.
Turns out current regex limits to 10 characters.
Fix is dumb: it only allows for up to 24 chars, which is the **current** max TLD length, see https://stackoverflow.com/a/22038535/1907212.
---
Maybe a better (and longer-term) fix would be to allow for up to 64 chars, which I understand comes from the RFC.
I'm not at ease with regexes, so can't be sure about it, but [this suggestion](https://meta.discourse.org/t/host-is-invalid-error-when-tld-is-longer-than-7-characters/46081/8?u=julienma) seems pretty good:
> rules of DNS labels are:
>
> - All labels are 1 to 63 characters, case insensitive A to Z, 0 to 9 and - (hyphen), all from ASCII.
> - No labels may start with a hyphen.
> - No top level domain label may start with a number.
>
>That means a regexp for a valid domain name would look like:
>
>`/^([a-z0-9][a-z0-9-]{0,62}\.)+[a-z][a-z0-9-]{0,62}\.?$/`
>
>Domains that are just a TLD are sufficiently bizarre as to be worth ignoring.
2019-07-27 04:29:48 +08:00
|
|
|
if host !~ /\A[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,24}(:[0-9]{1,5})?(\/.*)?\Z/i &&
|
2018-06-07 13:28:18 +08:00
|
|
|
host !~ /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(:[0-9]{1,5})?(\/.*)?\Z/ &&
|
|
|
|
host !~ /\A([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.)?localhost(\:[0-9]{1,5})?(\/.*)?\Z/i
|
|
|
|
errors.add(:host, I18n.t('errors.messages.invalid'))
|
2016-01-12 00:06:09 +08:00
|
|
|
end
|
2018-06-07 13:28:18 +08:00
|
|
|
end
|
2015-08-19 05:15:46 +08:00
|
|
|
end
|
2015-09-18 08:41:10 +08:00
|
|
|
|
|
|
|
# == Schema Information
|
|
|
|
#
|
|
|
|
# Table name: embeddable_hosts
|
|
|
|
#
|
2016-10-31 17:32:11 +08:00
|
|
|
# id :integer not null, primary key
|
2019-01-12 03:29:56 +08:00
|
|
|
# host :string not null
|
2016-10-31 17:32:11 +08:00
|
|
|
# category_id :integer not null
|
2019-01-12 03:29:56 +08:00
|
|
|
# created_at :datetime not null
|
|
|
|
# updated_at :datetime not null
|
2016-10-31 17:32:11 +08:00
|
|
|
# path_whitelist :string
|
2017-05-13 02:46:57 +08:00
|
|
|
# class_name :string
|
2015-09-18 08:41:10 +08:00
|
|
|
#
|