diff --git a/app/assets/javascripts/admin/components/embeddable-host.js.es6 b/app/assets/javascripts/admin/components/embeddable-host.js.es6 index f33c75096..2a5d7c030 100644 --- a/app/assets/javascripts/admin/components/embeddable-host.js.es6 +++ b/app/assets/javascripts/admin/components/embeddable-host.js.es6 @@ -30,10 +30,11 @@ export default Ember.Component.extend(bufferedProperty('host'), { save() { if (this.get('cantSave')) { return; } - const props = this.get('buffered').getProperties('host'); + const props = this.get('buffered').getProperties('host', 'path_whitelist'); props.category_id = this.get('categoryId'); const host = this.get('host'); + host.save(props).then(() => { host.set('category', Discourse.Category.findById(this.get('categoryId'))); this.set('editToggled', false); diff --git a/app/assets/javascripts/admin/templates/components/embeddable-host.hbs b/app/assets/javascripts/admin/templates/components/embeddable-host.hbs index c35d40e1d..5f2581138 100644 --- a/app/assets/javascripts/admin/templates/components/embeddable-host.hbs +++ b/app/assets/javascripts/admin/templates/components/embeddable-host.hbs @@ -2,6 +2,9 @@ {{input value=buffered.host placeholder="example.com" enter="save" class="host-name"}} + + {{input value=buffered.path_whitelist placeholder="/blog/.*" enter="save" class="path-whitelist"}} + {{category-chooser value=categoryId}} @@ -11,6 +14,7 @@ {{else}} {{host.host}} + {{host.path_whitelist}} {{category-badge host.category}} {{d-button icon="pencil" action="edit"}} diff --git a/app/assets/javascripts/admin/templates/embedding.hbs b/app/assets/javascripts/admin/templates/embedding.hbs index 196ada30f..5db5cb6bf 100644 --- a/app/assets/javascripts/admin/templates/embedding.hbs +++ b/app/assets/javascripts/admin/templates/embedding.hbs @@ -2,9 +2,10 @@ {{#if embedding.embeddable_hosts}} - + + - + {{#each embedding.embeddable_hosts as |host|}} {{embeddable-host host=host deleteHost="deleteHost"}} diff --git a/app/controllers/admin/embeddable_hosts_controller.rb b/app/controllers/admin/embeddable_hosts_controller.rb index 2d90d46f2..7f15c9fd0 100644 --- a/app/controllers/admin/embeddable_hosts_controller.rb +++ b/app/controllers/admin/embeddable_hosts_controller.rb @@ -21,6 +21,7 @@ class Admin::EmbeddableHostsController < Admin::AdminController def save_host(host) host.host = params[:embeddable_host][:host] + host.path_whitelist = params[:embeddable_host][:path_whitelist] host.category_id = params[:embeddable_host][:category_id] host.category_id = SiteSetting.uncategorized_category_id if host.category_id.blank? diff --git a/app/controllers/embed_controller.rb b/app/controllers/embed_controller.rb index dd66c7e7e..f9daccb48 100644 --- a/app/controllers/embed_controller.rb +++ b/app/controllers/embed_controller.rb @@ -85,7 +85,7 @@ class EmbedController < ApplicationController def ensure_embeddable if !(Rails.env.development? && current_user.try(:admin?)) - raise Discourse::InvalidAccess.new('invalid referer host') unless EmbeddableHost.host_allowed?(request.referer) + raise Discourse::InvalidAccess.new('invalid referer host') unless EmbeddableHost.url_allowed?(request.referer) end response.headers['X-Frame-Options'] = "ALLOWALL" diff --git a/app/models/embeddable_host.rb b/app/models/embeddable_host.rb index d42879ad9..b70bf3220 100644 --- a/app/models/embeddable_host.rb +++ b/app/models/embeddable_host.rb @@ -7,8 +7,11 @@ class EmbeddableHost < ActiveRecord::Base self.host.sub!(/\/.*$/, '') end - def self.record_for_host(host) - uri = URI(host) rescue nil + def self.record_for_url(uri) + + if uri.is_a?(String) + uri = URI(uri) rescue nil + end return false unless uri.present? host = uri.host @@ -17,8 +20,13 @@ class EmbeddableHost < ActiveRecord::Base where("lower(host) = ?", host).first end - def self.host_allowed?(host) - record_for_host(host).present? + def self.url_allowed?(url) + uri = URI(url) rescue nil + return false unless uri.present? + + host = record_for_url(uri) + return host.present? && + (host.path_whitelist.blank? || !Regexp.new(host.path_whitelist).match(uri.path).nil?) end private diff --git a/app/models/topic_embed.rb b/app/models/topic_embed.rb index f98bd984c..4d2e4a956 100644 --- a/app/models/topic_embed.rb +++ b/app/models/topic_embed.rb @@ -33,7 +33,7 @@ class TopicEmbed < ActiveRecord::Base # If there is no embed, create a topic, post and the embed. if embed.blank? Topic.transaction do - eh = EmbeddableHost.record_for_host(url) + eh = EmbeddableHost.record_for_url(url) creator = PostCreator.new(user, title: title, diff --git a/app/serializers/embeddable_host_serializer.rb b/app/serializers/embeddable_host_serializer.rb index f5de82a0c..61cf16e69 100644 --- a/app/serializers/embeddable_host_serializer.rb +++ b/app/serializers/embeddable_host_serializer.rb @@ -1,16 +1,11 @@ class EmbeddableHostSerializer < ApplicationSerializer - attributes :id, :host, :category_id - def id - object.id + TO_SERIALIZE = [:id, :host, :path_whitelist, :category_id] + + attributes *TO_SERIALIZE + + TO_SERIALIZE.each do |attr| + define_method(attr) { object.send(attr) } end - def host - object.host - end - - def category_id - object.category_id - end end - diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 1613319ba..f5b107a34 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -3113,6 +3113,7 @@ en: sample: "Use the following HTML code into your site to create and embed discourse topics. Replace REPLACE_ME with the canonical URL of the page you are embedding it on." title: "Embedding" host: "Allowed Hosts" + path_whitelist: "Path Whitelist" edit: "edit" category: "Post to Category" add_host: "Add Host" diff --git a/lib/topic_retriever.rb b/lib/topic_retriever.rb index 2af8401af..fecccb657 100644 --- a/lib/topic_retriever.rb +++ b/lib/topic_retriever.rb @@ -7,13 +7,13 @@ class TopicRetriever end def retrieve - perform_retrieve unless (invalid_host? || retrieved_recently?) + perform_retrieve unless (invalid_url? || retrieved_recently?) end private - def invalid_host? - !EmbeddableHost.host_allowed?(@embed_url) + def invalid_url? + !EmbeddableHost.url_allowed?(@embed_url) end def retrieved_recently? diff --git a/spec/components/topic_retriever_spec.rb b/spec/components/topic_retriever_spec.rb index 89db716b2..a5180b8c3 100644 --- a/spec/components/topic_retriever_spec.rb +++ b/spec/components/topic_retriever_spec.rb @@ -10,7 +10,7 @@ describe TopicRetriever do describe "#retrieve" do context "when host is invalid" do before do - topic_retriever.stubs(:invalid_host?).returns(true) + topic_retriever.stubs(:invalid_url?).returns(true) end it "does not perform_retrieve" do @@ -32,7 +32,7 @@ describe TopicRetriever do context "when host is not invalid" do before do - topic_retriever.stubs(:invalid_host?).returns(false) + topic_retriever.stubs(:invalid_url?).returns(false) end context "when topics have been retrieived recently" do diff --git a/spec/models/embeddable_host_spec.rb b/spec/models/embeddable_host_spec.rb index c6596370a..72d413919 100644 --- a/spec/models/embeddable_host_spec.rb +++ b/spec/models/embeddable_host_spec.rb @@ -49,19 +49,28 @@ describe EmbeddableHost do expect(eh).not_to be_valid end - describe "allows_embeddable_host" do + describe "url_allowed?" do let!(:host) { Fabricate(:embeddable_host) } it 'works as expected' do - expect(EmbeddableHost.host_allowed?('http://eviltrout.com')).to eq(true) - expect(EmbeddableHost.host_allowed?('https://eviltrout.com')).to eq(true) - expect(EmbeddableHost.host_allowed?('https://not-eviltrout.com')).to eq(false) + expect(EmbeddableHost.url_allowed?('http://eviltrout.com')).to eq(true) + expect(EmbeddableHost.url_allowed?('https://eviltrout.com')).to eq(true) + expect(EmbeddableHost.url_allowed?('https://not-eviltrout.com')).to eq(false) end it 'works with multiple hosts' do Fabricate(:embeddable_host, host: 'discourse.org') - expect(EmbeddableHost.host_allowed?('http://eviltrout.com')).to eq(true) - expect(EmbeddableHost.host_allowed?('http://discourse.org')).to eq(true) + expect(EmbeddableHost.url_allowed?('http://eviltrout.com')).to eq(true) + expect(EmbeddableHost.url_allowed?('http://discourse.org')).to eq(true) + end + end + + describe "path_whitelist" do + let!(:host) { Fabricate(:embeddable_host, path_whitelist: '^/fp/\d{4}/\d{2}/\d{2}/.*$') } + + it "matches the path" do + expect(EmbeddableHost.url_allowed?('http://eviltrout.com')).to eq(false) + expect(EmbeddableHost.url_allowed?('http://eviltrout.com/fp/2016/08/25/test-page')).to eq(true) end end
{{i18n "admin.embedding.host"}}{{i18n "admin.embedding.host"}}{{i18n "admin.embedding.path_whitelist"}} {{i18n "admin.embedding.category"}}