From 81a0843bd916bf712280566eb53fab34552a4cab Mon Sep 17 00:00:00 2001 From: Rob Florence Date: Thu, 21 Dec 2017 15:12:26 -0500 Subject: [PATCH 1/3] use new firebase admin sdk private key --- README.md | 6 ++++-- firebase.gemspec | 1 + lib/firebase.rb | 18 ++++++++++++++---- spec/firebase_spec.rb | 18 +++++++++++++++++- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2a4e65f..6fb7b4f 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,11 @@ response.raw_body # => '{"name":"-INOQPH-aV_psbk3ZXEX"}' If you have a read-only namespace, set your secret key as follows: ```ruby -firebase = Firebase::Client.new(base_uri, secret_key) +# Using Firebase Admin SDK private key +firebase = Firebase::Client.new(base_uri, "/path/to/private_key.json") -response = firebase.push("todos", { :name => 'Pick the milk', :'.priority' => 1 }) +# Using Firebase Database Secret (deprecated) +firebase = Firebase::Client.new(base_uri, db_secret) ``` You can now pass custom query options to firebase: diff --git a/firebase.gemspec b/firebase.gemspec index 98cbeec..344a8f3 100644 --- a/firebase.gemspec +++ b/firebase.gemspec @@ -23,6 +23,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'httpclient', '>= 2.5.3' s.add_runtime_dependency 'json' + s.add_runtime_dependency 'googleauth' s.add_development_dependency 'rake' s.add_development_dependency 'rdoc' s.add_development_dependency 'rspec' diff --git a/lib/firebase.rb b/lib/firebase.rb index cc23aa4..21a515c 100644 --- a/lib/firebase.rb +++ b/lib/firebase.rb @@ -1,5 +1,6 @@ require 'firebase/response' require 'firebase/server_value' +require 'googleauth' require 'httpclient' require 'json' require 'uri' @@ -13,13 +14,22 @@ def initialize(base_uri, auth=nil) raise ArgumentError.new('base_uri must be a valid https uri') end base_uri += '/' unless base_uri.end_with?('/') + default_header = { + 'Content-Type' => 'application/json' + } + if auth && File.file?(auth) + # Using Admin SDK private key file + scopes = %w(https://www.googleapis.com/auth/firebase.database https://www.googleapis.com/auth/userinfo.email) + credentials = Google::Auth::DefaultCredentials.make_creds(json_key_io: File.open(auth), scope: scopes) + default_header = credentials.apply(default_header) + else + # Using deprecated Database Secret + @auth = auth + end @request = HTTPClient.new({ :base_url => base_uri, - :default_header => { - 'Content-Type' => 'application/json' - } + :default_header => default_header }) - @auth = auth end # Writes and returns the data diff --git a/spec/firebase_spec.rb b/spec/firebase_spec.rb index 0a987a9..cab7362 100644 --- a/spec/firebase_spec.rb +++ b/spec/firebase_spec.rb @@ -124,7 +124,7 @@ end describe "http processing" do - it "sends custom auth" do + it "sends custom auth query" do firebase = Firebase::Client.new('https://test.firebaseio.com', 'secret') expect(firebase.request).to receive(:request).with(:get, "todos.json", { :body => nil, @@ -133,5 +133,21 @@ }) firebase.get('todos', :foo => 'bar') end + + it "sets custom auth header" do + expect(File).to receive(:file?).with('test_private_key.json').and_return(true) + expect(File).to receive(:open).with('test_private_key.json').and_return('private key') + credentials = double() + expect(credentials).to receive(:apply).with({ 'Content-Type' => 'application/json' }).and_return({ :authorization => 'Bearer abcdef', 'Content-Type' => 'application/json' }) + expect(Google::Auth::DefaultCredentials).to receive(:make_creds).with(json_key_io: 'private key', scope: instance_of(Array)).and_return(credentials) + expect(HTTPClient).to receive(:new).with({ + :base_url => 'https://test.firebaseio.com/', + :default_header => { + :authorization => 'Bearer abcdef', + 'Content-Type' => 'application/json' + } + }) + firebase = Firebase::Client.new('https://test.firebaseio.com/', 'test_private_key.json') + end end end From 02cc3c1f2da6cd4b85d4b5f1a0f9a81a90ffcecd Mon Sep 17 00:00:00 2001 From: Rob Florence Date: Tue, 2 Jan 2018 09:16:21 -0500 Subject: [PATCH 2/3] use private key string contents as argument instead of file path --- lib/firebase.rb | 15 ++++++++++++--- spec/firebase_spec.rb | 10 +++++----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/lib/firebase.rb b/lib/firebase.rb index 21a515c..17b1db6 100644 --- a/lib/firebase.rb +++ b/lib/firebase.rb @@ -17,10 +17,10 @@ def initialize(base_uri, auth=nil) default_header = { 'Content-Type' => 'application/json' } - if auth && File.file?(auth) - # Using Admin SDK private key file + if auth && valid_json?(auth) + # Using Admin SDK private key scopes = %w(https://www.googleapis.com/auth/firebase.database https://www.googleapis.com/auth/userinfo.email) - credentials = Google::Auth::DefaultCredentials.make_creds(json_key_io: File.open(auth), scope: scopes) + credentials = Google::Auth::DefaultCredentials.make_creds(json_key_io: StringIO.new(auth), scope: scopes) default_header = credentials.apply(default_header) else # Using deprecated Database Secret @@ -72,5 +72,14 @@ def process(verb, path, data=nil, query={}) :follow_redirect => true }) end + + def valid_json?(json) + begin + JSON.parse(json) + return true + rescue JSON::ParserError + return false + end + end end end diff --git a/spec/firebase_spec.rb b/spec/firebase_spec.rb index cab7362..5a2d2b7 100644 --- a/spec/firebase_spec.rb +++ b/spec/firebase_spec.rb @@ -133,13 +133,13 @@ }) firebase.get('todos', :foo => 'bar') end - + it "sets custom auth header" do - expect(File).to receive(:file?).with('test_private_key.json').and_return(true) - expect(File).to receive(:open).with('test_private_key.json').and_return('private key') + private_key = '{ "private_key": true }' credentials = double() expect(credentials).to receive(:apply).with({ 'Content-Type' => 'application/json' }).and_return({ :authorization => 'Bearer abcdef', 'Content-Type' => 'application/json' }) - expect(Google::Auth::DefaultCredentials).to receive(:make_creds).with(json_key_io: 'private key', scope: instance_of(Array)).and_return(credentials) + expect(StringIO).to receive(:new).with(private_key).and_return('StringIO private key') + expect(Google::Auth::DefaultCredentials).to receive(:make_creds).with(json_key_io: 'StringIO private key', scope: instance_of(Array)).and_return(credentials) expect(HTTPClient).to receive(:new).with({ :base_url => 'https://test.firebaseio.com/', :default_header => { @@ -147,7 +147,7 @@ 'Content-Type' => 'application/json' } }) - firebase = Firebase::Client.new('https://test.firebaseio.com/', 'test_private_key.json') + Firebase::Client.new('https://test.firebaseio.com/', private_key) end end end From b1c6e326fd6f07ffb01acda9ca90081f26fdf4b9 Mon Sep 17 00:00:00 2001 From: Rob Florence Date: Tue, 2 Jan 2018 09:19:44 -0500 Subject: [PATCH 3/3] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6fb7b4f..7b39585 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ response.raw_body # => '{"name":"-INOQPH-aV_psbk3ZXEX"}' If you have a read-only namespace, set your secret key as follows: ```ruby # Using Firebase Admin SDK private key -firebase = Firebase::Client.new(base_uri, "/path/to/private_key.json") +firebase = Firebase::Client.new(base_uri, private_key_json_string) # Using Firebase Database Secret (deprecated) firebase = Firebase::Client.new(base_uri, db_secret)