FIX: httpshttps ultra secure URLs

This commit is contained in:
Régis Hanol 2016-06-30 16:55:01 +02:00
parent 7a1e99dacb
commit 5169bcdb6e
9 changed files with 54 additions and 75 deletions

View file

@ -35,7 +35,7 @@ class TopicLinkClick < ActiveRecord::Base
cdn_uri = URI.parse(Discourse.asset_host) rescue nil cdn_uri = URI.parse(Discourse.asset_host) rescue nil
if cdn_uri && cdn_uri.hostname == uri.hostname && uri.path.starts_with?(cdn_uri.path) if cdn_uri && cdn_uri.hostname == uri.hostname && uri.path.starts_with?(cdn_uri.path)
is_cdn_link = true is_cdn_link = true
urls << uri.path[(cdn_uri.path.length)..-1] urls << uri.path[cdn_uri.path.length..-1]
end end
end end
@ -43,8 +43,7 @@ class TopicLinkClick < ActiveRecord::Base
cdn_uri = URI.parse(SiteSetting.s3_cdn_url) rescue nil cdn_uri = URI.parse(SiteSetting.s3_cdn_url) rescue nil
if cdn_uri && cdn_uri.hostname == uri.hostname && uri.path.starts_with?(cdn_uri.path) if cdn_uri && cdn_uri.hostname == uri.hostname && uri.path.starts_with?(cdn_uri.path)
is_cdn_link = true is_cdn_link = true
path = uri.path[cdn_uri.path.length..-1]
path = uri.path[(cdn_uri.path.length)..-1]
urls << path urls << path
urls << "#{Discourse.store.absolute_base_url}#{path}" urls << "#{Discourse.store.absolute_base_url}#{path}"
end end
@ -66,7 +65,7 @@ class TopicLinkClick < ActiveRecord::Base
# If no link is found... # If no link is found...
unless link.present? unless link.present?
# ... return the url for relative links or when using the same host # ... return the url for relative links or when using the same host
return url if url =~ /^\// || uri.try(:host) == Discourse.current_hostname return url if url =~ /^\/[^\/]/ || uri.try(:host) == Discourse.current_hostname
# If we have it somewhere else on the site, just allow the redirect. # If we have it somewhere else on the site, just allow the redirect.
# This is likely due to a onebox of another topic. # This is likely due to a onebox of another topic.

View file

@ -223,9 +223,9 @@ class Upload < ActiveRecord::Base
def self.get_from_url(url) def self.get_from_url(url)
return if url.blank? return if url.blank?
# we store relative urls, so we need to remove any host/cdn # we store relative urls, so we need to remove any host/cdn
url = url.sub(/^#{Discourse.asset_host}/i, "") if Discourse.asset_host.present? url = url.sub(Discourse.asset_host, "") if Discourse.asset_host.present?
# when using s3, we need to replace with the absolute base url # when using s3, we need to replace with the absolute base url
url = url.sub(/^#{SiteSetting.s3_cdn_url}/i, Discourse.store.absolute_base_url) if SiteSetting.s3_cdn_url.present? url = url.sub(SiteSetting.s3_cdn_url, Discourse.store.absolute_base_url) if SiteSetting.s3_cdn_url.present?
Upload.find_by(url: url) Upload.find_by(url: url)
end end

View file

@ -325,20 +325,20 @@ class CookedPostProcessor
end end
end end
use_s3_cdn = SiteSetting.s3_cdn_url.present? && SiteSetting.enable_s3_uploads use_s3_cdn = SiteSetting.enable_s3_uploads && SiteSetting.s3_cdn_url.present?
%w{href data-download-href}.each do |selector| %w{href data-download-href}.each do |selector|
@doc.css("a[#{selector}]").each do |a| @doc.css("a[#{selector}]").each do |a|
href = a[selector].to_s href = a[selector].to_s
a[selector] = UrlHelper.schemaless UrlHelper.absolute(href) if UrlHelper.is_local(href) a[selector] = UrlHelper.schemaless UrlHelper.absolute(href) if UrlHelper.is_local(href)
a[selector] = a[selector].sub(Discourse.store.absolute_base_url, SiteSetting.s3_cdn_url) if use_s3_cdn a[selector] = Discourse.store.cnd_url(a[selector]) if use_s3_cdn
end end
end end
@doc.css("img[src]").each do |img| @doc.css("img[src]").each do |img|
src = img["src"].to_s src = img["src"].to_s
img["src"] = UrlHelper.schemaless UrlHelper.absolute(src) if UrlHelper.is_local(src) img["src"] = UrlHelper.schemaless UrlHelper.absolute(src) if UrlHelper.is_local(src)
img["src"] = img["src"].sub(Discourse.store.absolute_base_url, SiteSetting.s3_cdn_url) if use_s3_cdn img["src"] = Discourse.store.cnd_url(img["src"]) if use_s3_cdn
end end
end end

View file

@ -131,7 +131,7 @@ module Discourse
end end
def self.disabled_plugin_names def self.disabled_plugin_names
plugins.select {|p| !p.enabled?}.map(&:name) plugins.select { |p| !p.enabled? }.map(&:name)
end end
def self.plugins def self.plugins
@ -179,36 +179,18 @@ module Discourse
# Get the current base URL for the current site # Get the current base URL for the current site
def self.current_hostname def self.current_hostname
if SiteSetting.force_hostname.present? SiteSetting.force_hostname.presence || RailsMultisite::ConnectionManagement.current_hostname
SiteSetting.force_hostname
else
RailsMultisite::ConnectionManagement.current_hostname
end
end end
def self.base_uri(default_value = "") def self.base_uri(default_value = "")
if !ActionController::Base.config.relative_url_root.blank? ActionController::Base.config.relative_url_root.presence || default_value
ActionController::Base.config.relative_url_root
else
default_value
end
end end
def self.base_url_no_prefix def self.base_url_no_prefix
default_port = 80 protocol, default_port = SiteSetting.force_https? ? ["https", 443] : ["http", 80]
protocol = "http" url = "#{protocol}://#{current_hostname}"
url << ":#{SiteSetting.port}" if SiteSetting.port.to_i > 0 && SiteSetting.port.to_i != default_port
if SiteSetting.force_https? url
protocol = "https"
default_port = 443
end
result = "#{protocol}://#{current_hostname}"
port = SiteSetting.port.present? && SiteSetting.port.to_i > 0 ? SiteSetting.port.to_i : default_port
result << ":#{SiteSetting.port}" if port != default_port
result
end end
def self.base_url def self.base_url

View file

@ -33,7 +33,6 @@ module FileStore
end end
def cdn_url(url) def cdn_url(url)
url
end end
def absolute_base_url def absolute_base_url

View file

@ -11,18 +11,16 @@ module FileStore
def remove_file(url) def remove_file(url)
return unless is_relative?(url) return unless is_relative?(url)
path = public_dir + url source = "#{public_dir}#{url}"
return if !File.exists?(path) return unless File.exists?(source)
tombstone = public_dir + url.sub("/uploads/", "/uploads/tombstone/") destination = "#{public_dir}#{url.sub("/uploads/", "/uploads/tombstone/")}"
FileUtils.mkdir_p(tombstone_dir) dir = Pathname.new(destination).dirname
FileUtils.move(path, tombstone, force: true) FileUtils.mkdir_p(dir) unless Dir.exists?(dir)
FileUtils.move(source, destination, force: true)
end end
def has_been_uploaded?(url) def has_been_uploaded?(url)
return false if url.blank? is_relative?(url) || is_local?(url)
return true if is_relative?(url)
return true if is_local?(url)
false
end end
def absolute_base_url def absolute_base_url
@ -50,6 +48,11 @@ module FileStore
"#{relative_base_url}/#{upload.sha1}" "#{relative_base_url}/#{upload.sha1}"
end end
def cdn_url(url)
return url if Discourse.asset_host.blank?
url.sub(Discourse.base_url_no_prefix, Discourse.asset_host)
end
def path_for(upload) def path_for(upload)
url = upload.try(:url) url = upload.try(:url)
"#{public_dir}#{upload.url}" if url && url[0] == "/" && url[1] != "/" "#{public_dir}#{upload.url}" if url && url[0] == "/" && url[1] != "/"
@ -64,7 +67,8 @@ module FileStore
end end
def copy_file(file, path) def copy_file(file, path)
FileUtils.mkdir_p(Pathname.new(path).dirname) dir = Pathname.new(path).dirname
FileUtils.mkdir_p(dir) unless Dir.exists?(dir)
# move the file to the right location # move the file to the right location
# not using mv, cause permissions are no good on move # not using mv, cause permissions are no good on move
File.open(path, "wb") { |f| f.write(file.read) } File.open(path, "wb") { |f| f.write(file.read) }
@ -85,7 +89,7 @@ module FileStore
end end
def tombstone_dir def tombstone_dir
public_dir + relative_base_url.sub("/uploads/", "/uploads/tombstone/") "#{public_dir}#{relative_base_url.sub("/uploads/", "/uploads/tombstone/")}"
end end
end end

View file

@ -1,3 +1,4 @@
require "uri"
require_dependency "file_store/base_store" require_dependency "file_store/base_store"
require_dependency "file_store/local_store" require_dependency "file_store/local_store"
require_dependency "s3_helper" require_dependency "s3_helper"
@ -23,8 +24,8 @@ module FileStore
# - content_type # - content_type
# - cache_locally # - cache_locally
def store_file(file, path, opts={}) def store_file(file, path, opts={})
filename = opts[:filename].presence filename = opts[:filename].presence
content_type = opts[:content_type].presence content_type = opts[:content_type].presence
# cache file locally when needed # cache file locally when needed
cache_file(file, File.basename(path)) if opts[:cache_locally] cache_file(file, File.basename(path)) if opts[:cache_locally]
# stored uploaded are public by default # stored uploaded are public by default
@ -48,9 +49,13 @@ module FileStore
def has_been_uploaded?(url) def has_been_uploaded?(url)
return false if url.blank? return false if url.blank?
return true if url.start_with?(absolute_base_url)
return true if SiteSetting.s3_cdn_url.present? && url.start_with?(SiteSetting.s3_cdn_url) base_hostname = URI.parse(absolute_base_url).hostname
false return true if url[base_hostname]
return false if SiteSetting.s3_cdn_url.blank?
cdn_hostname = URI.parse(SiteSetting.s3_cdn_url || "").hostname
cdn_hostname.presence && url[cdn_hostname]
end end
def absolute_base_url def absolute_base_url
@ -72,12 +77,13 @@ module FileStore
def path_for(upload) def path_for(upload)
url = upload.try(:url) url = upload.try(:url)
FileStore::LocalStore.new.path_for(upload) if url && url[0] == "/" && url[1] != "/" FileStore::LocalStore.new.path_for(upload) if url && url[/^\/[^\/]/]
end end
def cdn_url(url) def cdn_url(url)
return url if SiteSetting.s3_cdn_url.blank? return url if SiteSetting.s3_cdn_url.blank?
url.sub(absolute_base_url, SiteSetting.s3_cdn_url) schema = url[/^(https?:)?\/\//, 1]
url.sub("#{schema}#{absolute_base_url}", SiteSetting.s3_cdn_url)
end end
def cache_avatar(avatar, user_id) def cache_avatar(avatar, user_id)
@ -91,9 +97,10 @@ module FileStore
end end
def s3_bucket def s3_bucket
return @s3_bucket if @s3_bucket @s3_bucket ||= begin
raise Discourse::SiteSettingMissing.new("s3_upload_bucket") if SiteSetting.s3_upload_bucket.blank? raise Discourse::SiteSettingMissing.new("s3_upload_bucket") if SiteSetting.s3_upload_bucket.blank?
@s3_bucket = SiteSetting.s3_upload_bucket.downcase SiteSetting.s3_upload_bucket.downcase
end
end end
end end

View file

@ -37,18 +37,11 @@ module PrettyText
UrlHelper.schemaless UrlHelper.absolute avatar_template UrlHelper.schemaless UrlHelper.absolute avatar_template
end end
def mention_lookup(username) def mention_lookup(name)
return false unless username return false if name.blank?
if Group.exec_sql('SELECT 1 FROM groups WHERE name = ?', username).values.length == 1 return "group" if Group.where(name: name).exists?
"group" return "user" if User.where(username_lower: name.downcase).exists?
else nil
username = username.downcase
if User.exec_sql('SELECT 1 FROM users WHERE username_lower = ?', username).values.length == 1
"user"
else
nil
end
end
end end
def category_hashtag_lookup(category_slug) def category_hashtag_lookup(category_slug)
@ -306,12 +299,7 @@ JS
def self.add_s3_cdn(doc) def self.add_s3_cdn(doc)
doc.css("img").each do |img| doc.css("img").each do |img|
next unless img["src"] next unless img["src"]
if img["src"].include? Discourse.store.absolute_base_url img["src"] = Discourse.store.cdn_url(img["src"])
src = img["src"].sub(Discourse.store.absolute_base_url, SiteSetting.s3_cdn_url)
# absolute is // style so we may have added an extra https:// here
src = src.sub(/https?:h/, "h")
img["src"] = src
end
end end
end end

View file

@ -18,7 +18,7 @@ class UrlHelper
end end
def self.schemaless(url) def self.schemaless(url)
url.sub(/^http:/, "") url.sub(/^http:/i, "")
end end
end end