mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-30 10:58:31 -05:00
FEATURE: Support for a whitelist for embeddable host paths
This commit is contained in:
parent
43a3210c20
commit
c3a3aff120
12 changed files with 51 additions and 31 deletions
|
@ -30,10 +30,11 @@ export default Ember.Component.extend(bufferedProperty('host'), {
|
||||||
save() {
|
save() {
|
||||||
if (this.get('cantSave')) { return; }
|
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');
|
props.category_id = this.get('categoryId');
|
||||||
|
|
||||||
const host = this.get('host');
|
const host = this.get('host');
|
||||||
|
|
||||||
host.save(props).then(() => {
|
host.save(props).then(() => {
|
||||||
host.set('category', Discourse.Category.findById(this.get('categoryId')));
|
host.set('category', Discourse.Category.findById(this.get('categoryId')));
|
||||||
this.set('editToggled', false);
|
this.set('editToggled', false);
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
<td>
|
<td>
|
||||||
{{input value=buffered.host placeholder="example.com" enter="save" class="host-name"}}
|
{{input value=buffered.host placeholder="example.com" enter="save" class="host-name"}}
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
{{input value=buffered.path_whitelist placeholder="/blog/.*" enter="save" class="path-whitelist"}}
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{category-chooser value=categoryId}}
|
{{category-chooser value=categoryId}}
|
||||||
</td>
|
</td>
|
||||||
|
@ -11,6 +14,7 @@
|
||||||
</td>
|
</td>
|
||||||
{{else}}
|
{{else}}
|
||||||
<td>{{host.host}}</td>
|
<td>{{host.host}}</td>
|
||||||
|
<td>{{host.path_whitelist}}</td>
|
||||||
<td>{{category-badge host.category}}</td>
|
<td>{{category-badge host.category}}</td>
|
||||||
<td>
|
<td>
|
||||||
{{d-button icon="pencil" action="edit"}}
|
{{d-button icon="pencil" action="edit"}}
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
{{#if embedding.embeddable_hosts}}
|
{{#if embedding.embeddable_hosts}}
|
||||||
<table class='embedding'>
|
<table class='embedding'>
|
||||||
<tr>
|
<tr>
|
||||||
<th style='width: 50%'>{{i18n "admin.embedding.host"}}</th>
|
<th style='width: 30%'>{{i18n "admin.embedding.host"}}</th>
|
||||||
|
<th style='width: 30%'>{{i18n "admin.embedding.path_whitelist"}}</th>
|
||||||
<th style='width: 30%'>{{i18n "admin.embedding.category"}}</th>
|
<th style='width: 30%'>{{i18n "admin.embedding.category"}}</th>
|
||||||
<th style='width: 20%'> </th>
|
<th style='width: 10%'> </th>
|
||||||
</tr>
|
</tr>
|
||||||
{{#each embedding.embeddable_hosts as |host|}}
|
{{#each embedding.embeddable_hosts as |host|}}
|
||||||
{{embeddable-host host=host deleteHost="deleteHost"}}
|
{{embeddable-host host=host deleteHost="deleteHost"}}
|
||||||
|
|
|
@ -21,6 +21,7 @@ class Admin::EmbeddableHostsController < Admin::AdminController
|
||||||
|
|
||||||
def save_host(host)
|
def save_host(host)
|
||||||
host.host = params[:embeddable_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 = params[:embeddable_host][:category_id]
|
||||||
host.category_id = SiteSetting.uncategorized_category_id if host.category_id.blank?
|
host.category_id = SiteSetting.uncategorized_category_id if host.category_id.blank?
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ class EmbedController < ApplicationController
|
||||||
def ensure_embeddable
|
def ensure_embeddable
|
||||||
|
|
||||||
if !(Rails.env.development? && current_user.try(:admin?))
|
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
|
end
|
||||||
|
|
||||||
response.headers['X-Frame-Options'] = "ALLOWALL"
|
response.headers['X-Frame-Options'] = "ALLOWALL"
|
||||||
|
|
|
@ -7,8 +7,11 @@ class EmbeddableHost < ActiveRecord::Base
|
||||||
self.host.sub!(/\/.*$/, '')
|
self.host.sub!(/\/.*$/, '')
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.record_for_host(host)
|
def self.record_for_url(uri)
|
||||||
uri = URI(host) rescue nil
|
|
||||||
|
if uri.is_a?(String)
|
||||||
|
uri = URI(uri) rescue nil
|
||||||
|
end
|
||||||
return false unless uri.present?
|
return false unless uri.present?
|
||||||
|
|
||||||
host = uri.host
|
host = uri.host
|
||||||
|
@ -17,8 +20,13 @@ class EmbeddableHost < ActiveRecord::Base
|
||||||
where("lower(host) = ?", host).first
|
where("lower(host) = ?", host).first
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.host_allowed?(host)
|
def self.url_allowed?(url)
|
||||||
record_for_host(host).present?
|
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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -33,7 +33,7 @@ class TopicEmbed < ActiveRecord::Base
|
||||||
# If there is no embed, create a topic, post and the embed.
|
# If there is no embed, create a topic, post and the embed.
|
||||||
if embed.blank?
|
if embed.blank?
|
||||||
Topic.transaction do
|
Topic.transaction do
|
||||||
eh = EmbeddableHost.record_for_host(url)
|
eh = EmbeddableHost.record_for_url(url)
|
||||||
|
|
||||||
creator = PostCreator.new(user,
|
creator = PostCreator.new(user,
|
||||||
title: title,
|
title: title,
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
class EmbeddableHostSerializer < ApplicationSerializer
|
class EmbeddableHostSerializer < ApplicationSerializer
|
||||||
attributes :id, :host, :category_id
|
|
||||||
|
|
||||||
def id
|
TO_SERIALIZE = [:id, :host, :path_whitelist, :category_id]
|
||||||
object.id
|
|
||||||
|
attributes *TO_SERIALIZE
|
||||||
|
|
||||||
|
TO_SERIALIZE.each do |attr|
|
||||||
|
define_method(attr) { object.send(attr) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def host
|
|
||||||
object.host
|
|
||||||
end
|
|
||||||
|
|
||||||
def category_id
|
|
||||||
object.category_id
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3113,6 +3113,7 @@ en:
|
||||||
sample: "Use the following HTML code into your site to create and embed discourse topics. Replace <b>REPLACE_ME</b> with the canonical URL of the page you are embedding it on."
|
sample: "Use the following HTML code into your site to create and embed discourse topics. Replace <b>REPLACE_ME</b> with the canonical URL of the page you are embedding it on."
|
||||||
title: "Embedding"
|
title: "Embedding"
|
||||||
host: "Allowed Hosts"
|
host: "Allowed Hosts"
|
||||||
|
path_whitelist: "Path Whitelist"
|
||||||
edit: "edit"
|
edit: "edit"
|
||||||
category: "Post to Category"
|
category: "Post to Category"
|
||||||
add_host: "Add Host"
|
add_host: "Add Host"
|
||||||
|
|
|
@ -7,13 +7,13 @@ class TopicRetriever
|
||||||
end
|
end
|
||||||
|
|
||||||
def retrieve
|
def retrieve
|
||||||
perform_retrieve unless (invalid_host? || retrieved_recently?)
|
perform_retrieve unless (invalid_url? || retrieved_recently?)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def invalid_host?
|
def invalid_url?
|
||||||
!EmbeddableHost.host_allowed?(@embed_url)
|
!EmbeddableHost.url_allowed?(@embed_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def retrieved_recently?
|
def retrieved_recently?
|
||||||
|
|
|
@ -10,7 +10,7 @@ describe TopicRetriever do
|
||||||
describe "#retrieve" do
|
describe "#retrieve" do
|
||||||
context "when host is invalid" do
|
context "when host is invalid" do
|
||||||
before do
|
before do
|
||||||
topic_retriever.stubs(:invalid_host?).returns(true)
|
topic_retriever.stubs(:invalid_url?).returns(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not perform_retrieve" do
|
it "does not perform_retrieve" do
|
||||||
|
@ -32,7 +32,7 @@ describe TopicRetriever do
|
||||||
|
|
||||||
context "when host is not invalid" do
|
context "when host is not invalid" do
|
||||||
before do
|
before do
|
||||||
topic_retriever.stubs(:invalid_host?).returns(false)
|
topic_retriever.stubs(:invalid_url?).returns(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when topics have been retrieived recently" do
|
context "when topics have been retrieived recently" do
|
||||||
|
|
|
@ -49,19 +49,28 @@ describe EmbeddableHost do
|
||||||
expect(eh).not_to be_valid
|
expect(eh).not_to be_valid
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "allows_embeddable_host" do
|
describe "url_allowed?" do
|
||||||
let!(:host) { Fabricate(:embeddable_host) }
|
let!(:host) { Fabricate(:embeddable_host) }
|
||||||
|
|
||||||
it 'works as expected' do
|
it 'works as expected' do
|
||||||
expect(EmbeddableHost.host_allowed?('http://eviltrout.com')).to eq(true)
|
expect(EmbeddableHost.url_allowed?('http://eviltrout.com')).to eq(true)
|
||||||
expect(EmbeddableHost.host_allowed?('https://eviltrout.com')).to eq(true)
|
expect(EmbeddableHost.url_allowed?('https://eviltrout.com')).to eq(true)
|
||||||
expect(EmbeddableHost.host_allowed?('https://not-eviltrout.com')).to eq(false)
|
expect(EmbeddableHost.url_allowed?('https://not-eviltrout.com')).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'works with multiple hosts' do
|
it 'works with multiple hosts' do
|
||||||
Fabricate(:embeddable_host, host: 'discourse.org')
|
Fabricate(:embeddable_host, host: 'discourse.org')
|
||||||
expect(EmbeddableHost.host_allowed?('http://eviltrout.com')).to eq(true)
|
expect(EmbeddableHost.url_allowed?('http://eviltrout.com')).to eq(true)
|
||||||
expect(EmbeddableHost.host_allowed?('http://discourse.org')).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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue