From 756533553c6b5073ff627aca52aede53a317980f Mon Sep 17 00:00:00 2001 From: Arne Date: Thu, 6 Mar 2014 07:25:31 +0100 Subject: [PATCH 1/3] Add seperate digests for subscribing and publishing --- app/assets/javascripts/private_pub.js | 17 +++++++++-- lib/private_pub.rb | 9 ++++-- lib/private_pub/faye_extension.rb | 42 ++++++++++++++++++++------- lib/private_pub/view_helpers.rb | 5 ++-- 4 files changed, 56 insertions(+), 17 deletions(-) diff --git a/app/assets/javascripts/private_pub.js b/app/assets/javascripts/private_pub.js index 43e19e4..e294a7a 100644 --- a/app/assets/javascripts/private_pub.js +++ b/app/assets/javascripts/private_pub.js @@ -31,15 +31,26 @@ function buildPrivatePub(doc) { }; }, + // TODO: Make method fail-safe + // TODO: Remove debug messages fayeExtension: { outgoing: function(message, callback) { - if (message.channel == "/meta/subscribe") { - // Attach the signature and timestamp to subscription messages + if (!message.channel.match(/^\/meta\/(?!subscribe).*/)) { var subscription = self.subscriptions[message.subscription]; if (!message.ext) message.ext = {}; - message.ext.private_pub_signature = subscription.signature; + // Attach the timestamp to messages message.ext.private_pub_timestamp = subscription.timestamp; + + if (message.channel == "/meta/subscribe") { + // Attach the signature to subscription messages + message.ext.private_pub_signature = subscription.sub_signature; + } else { + // Attach the signature to subscription messages + message.ext.private_pub_signature = subscription.pub_signature; + } } + console.debug(message.ext); + callback(message); } }, diff --git a/lib/private_pub.rb b/lib/private_pub.rb index a595cf8..0cb92c6 100644 --- a/lib/private_pub.rb +++ b/lib/private_pub.rb @@ -56,11 +56,16 @@ def message(channel, data) # Returns a subscription hash to pass to the PrivatePub.sign call in JavaScript. # Any options passed are merged to the hash. def subscription(options = {}) - sub = {:server => config[:server], :timestamp => (Time.now.to_f * 1000).round}.merge(options) - sub[:signature] = Digest::SHA1.hexdigest([config[:secret_token], sub[:channel], sub[:timestamp]].join) + sub = {:publish => false, :server => config[:server], :timestamp => (Time.now.to_f * 1000).round}.merge(options) + sub[:sub_signature] = generate_signature(sub, false) + sub[:pub_signature] = generate_signature(sub, true) if sub[:publish] sub end + def generate_signature(options, publish) + Digest::SHA1.hexdigest([publish ? "pub" : "sub", config[:secret_token], options[:channel], options[:timestamp]].join) + end + # Determine if the signature has expired given a timestamp. def signature_expired?(timestamp) timestamp < ((Time.now.to_f - config[:signature_expiration])*1000).round if config[:signature_expiration] diff --git a/lib/private_pub/faye_extension.rb b/lib/private_pub/faye_extension.rb index 4a89fc5..e342551 100644 --- a/lib/private_pub/faye_extension.rb +++ b/lib/private_pub/faye_extension.rb @@ -5,34 +5,56 @@ class FayeExtension # Callback to handle incoming Faye messages. This authenticates both # subscribe and publish calls. def incoming(message, callback) - if message["channel"] == "/meta/subscribe" - authenticate_subscribe(message) - elsif message["channel"] !~ %r{^/meta/} + puts "message to #{message["channel"]}: #{message["data"]}" # TODO: Remove! + if is_subscription? message + check_signature(message) + elsif is_not_meta? message authenticate_publish(message) end + callback.call(message) end private - # Ensure the subscription signature is correct and that it has not expired. - def authenticate_subscribe(message) - subscription = PrivatePub.subscription(:channel => message["subscription"], :timestamp => message["ext"]["private_pub_timestamp"]) - if message["ext"]["private_pub_signature"] != subscription[:signature] + def is_subscription?(message) + message["channel"] == "/meta/subscribe" + end + + def is_not_meta?(message) + message["channel"] !~ %r{^/meta/} + end + + # Ensure a signature is correct and that it has not expired. + def check_signature(message) + subscription = PrivatePub.subscription(:publish => true, :channel => message["subscription"], :timestamp => message["ext"]["private_pub_timestamp"]) + expected_signature = is_subscription?(message) ? subscription[:sub_signature] : subscription[:pub_signature] + + if message["ext"]["private_pub_signature"] != expected_signature message["error"] = "Incorrect signature." + puts "lol1" elsif PrivatePub.signature_expired? message["ext"]["private_pub_timestamp"].to_i message["error"] = "Signature has expired." + puts "lol2" end end # Ensures the secret token is correct before publishing. + # TODO: change format to allow js clients to publish def authenticate_publish(message) if PrivatePub.config[:secret_token].nil? raise Error, "No secret_token config set, ensure private_pub.yml is loaded properly." - elsif message["ext"]["private_pub_token"] != PrivatePub.config[:secret_token] - message["error"] = "Incorrect token." + end + + if message["ext"]["private_pub_token"] + if message["ext"]["private_pub_token"] != PrivatePub.config[:secret_token] + message["error"] = "Incorrect token." + puts "lol3" + else + message["ext"]["private_pub_token"] = nil + end else - message["ext"]["private_pub_token"] = nil + check_signature message end end end diff --git a/lib/private_pub/view_helpers.rb b/lib/private_pub/view_helpers.rb index 9048f36..9c95990 100644 --- a/lib/private_pub/view_helpers.rb +++ b/lib/private_pub/view_helpers.rb @@ -12,8 +12,9 @@ def publish_to(channel, data = nil, &block) # Subscribe the client to the given channel. This generates # some JavaScript calling PrivatePub.sign with the subscription # options. - def subscribe_to(channel) - subscription = PrivatePub.subscription(:channel => channel) + def subscribe_to(channel, options = {}) + options = {publish: false}.merge! options + subscription = PrivatePub.subscription(channel: channel, publish: options[:publish]) content_tag "script", :type => "text/javascript" do raw("PrivatePub.sign(#{subscription.to_json});") end From 441aba74ebd5fe1b7c5bc53be32e7db90694a880 Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 17 Mar 2014 20:02:41 +0100 Subject: [PATCH 2/3] Fix publishing through js -> used to generate signature inconsistently --- app/assets/javascripts/private_pub.js | 14 +++++++++----- lib/private_pub/faye_extension.rb | 13 +++++++------ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/private_pub.js b/app/assets/javascripts/private_pub.js index e294a7a..4e8139c 100644 --- a/app/assets/javascripts/private_pub.js +++ b/app/assets/javascripts/private_pub.js @@ -31,14 +31,13 @@ function buildPrivatePub(doc) { }; }, - // TODO: Make method fail-safe - // TODO: Remove debug messages fayeExtension: { outgoing: function(message, callback) { if (!message.channel.match(/^\/meta\/(?!subscribe).*/)) { - var subscription = self.subscriptions[message.subscription]; + var channel = message.channel == '/meta/subscribe' ? message.subscription : message.channel; + var subscription = self.subscriptions[channel]; if (!message.ext) message.ext = {}; - // Attach the timestamp to messages + // Attach the timestamp to messages message.ext.private_pub_timestamp = subscription.timestamp; if (message.channel == "/meta/subscribe") { @@ -49,7 +48,6 @@ function buildPrivatePub(doc) { message.ext.private_pub_signature = subscription.pub_signature; } } - console.debug(message.ext); callback(message); } @@ -100,6 +98,12 @@ function buildPrivatePub(doc) { subscribe: function(channel, callback) { self.subscriptionCallbacks[channel] = callback; + }, + + publish: function(channel, message) { + self.faye(function(faye) { + faye.publish(channel, {channel: channel, data: message}); + }); } }; return self; diff --git a/lib/private_pub/faye_extension.rb b/lib/private_pub/faye_extension.rb index e342551..6111ae0 100644 --- a/lib/private_pub/faye_extension.rb +++ b/lib/private_pub/faye_extension.rb @@ -25,22 +25,24 @@ def is_not_meta?(message) message["channel"] !~ %r{^/meta/} end + def channel_of(message) + message[is_subscription?(message) ? "subscription" : "channel"] + end + # Ensure a signature is correct and that it has not expired. def check_signature(message) - subscription = PrivatePub.subscription(:publish => true, :channel => message["subscription"], :timestamp => message["ext"]["private_pub_timestamp"]) + subscription = PrivatePub.subscription(:publish => true, :channel => channel_of(message), :timestamp => message["ext"]["private_pub_timestamp"]) expected_signature = is_subscription?(message) ? subscription[:sub_signature] : subscription[:pub_signature] if message["ext"]["private_pub_signature"] != expected_signature message["error"] = "Incorrect signature." - puts "lol1" + puts "soll: #{expected_signature}, ist: #{message["ext"]["private_pub_signature"]}" elsif PrivatePub.signature_expired? message["ext"]["private_pub_timestamp"].to_i message["error"] = "Signature has expired." - puts "lol2" end end - # Ensures the secret token is correct before publishing. - # TODO: change format to allow js clients to publish + # Ensures either the correct secret token or publish signature is set def authenticate_publish(message) if PrivatePub.config[:secret_token].nil? raise Error, "No secret_token config set, ensure private_pub.yml is loaded properly." @@ -49,7 +51,6 @@ def authenticate_publish(message) if message["ext"]["private_pub_token"] if message["ext"]["private_pub_token"] != PrivatePub.config[:secret_token] message["error"] = "Incorrect token." - puts "lol3" else message["ext"]["private_pub_token"] = nil end From 5473ca85d2565652a061617e11d2d50cecbfde18 Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 26 May 2014 13:19:50 +0200 Subject: [PATCH 3/3] Remove printf debugging --- lib/private_pub/faye_extension.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/private_pub/faye_extension.rb b/lib/private_pub/faye_extension.rb index 6111ae0..f7cd69c 100644 --- a/lib/private_pub/faye_extension.rb +++ b/lib/private_pub/faye_extension.rb @@ -5,7 +5,6 @@ class FayeExtension # Callback to handle incoming Faye messages. This authenticates both # subscribe and publish calls. def incoming(message, callback) - puts "message to #{message["channel"]}: #{message["data"]}" # TODO: Remove! if is_subscription? message check_signature(message) elsif is_not_meta? message @@ -36,7 +35,6 @@ def check_signature(message) if message["ext"]["private_pub_signature"] != expected_signature message["error"] = "Incorrect signature." - puts "soll: #{expected_signature}, ist: #{message["ext"]["private_pub_signature"]}" elsif PrivatePub.signature_expired? message["ext"]["private_pub_timestamp"].to_i message["error"] = "Signature has expired." end