Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure JSON Key Path is an Actual File or IO Object #134

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion lib/fcm.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
require "googleauth"

class FCM
class InvalidCredentialError < StandardError; end

BASE_URI = "https://fcm.googleapis.com"
BASE_URI_V1 = "https://fcm.googleapis.com/v1/projects/"
DEFAULT_TIMEOUT = 30
Expand Down Expand Up @@ -291,11 +293,31 @@ def jwt_token
token["access_token"]
end

def credentials_error_msg(param)
error_msg = 'credentials must be an IO-like ' \
'object or path. You passed'

param_klass = param.nil? ? 'nil' : "a #{param.class.name}"
error_msg += " #{param_klass}."
raise InvalidCredentialError, error_msg
end

def valid_json_key_path?(path)
valid_io_object = path.respond_to?(:open)
return true if valid_io_object && File.file?(path)

max_path_len = 1024
valid_path = path.is_a?(String) && path.length <= max_path_len
valid_path && File.file?(path)
end

def json_key
@json_key ||= if @json_key_path.respond_to?(:read)
@json_key_path
else
elsif valid_json_key_path?(@json_key_path)
File.open(@json_key_path)
else
credentials_error_msg(@json_key_path)
end
end
end
91 changes: 85 additions & 6 deletions spec/fcm_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "spec_helper"
require 'tempfile'

describe FCM do
let(:project_name) { 'test-project' }
Expand All @@ -13,27 +14,105 @@
}
end

let(:client_email) do
'83315528762cf7e0-7bbcc3aad87e0083391bc7f234d487' \
'c8@developer.gserviceaccount.com'
end

let(:client_x509_cert_url) do
'https://www.googleapis.com/robot/v1/metadata/x509/' \
'fd6b61037dd2bb8585527679" + "-7bbcc3aad87e0083391b' \
'c7f234d487c8%40developer.gserviceaccount.com'
end

let(:large_file_name) do
Array.new(1021) { 'a' }.join('') + '.txt'
end

let(:creds_error) do
FCM::InvalidCredentialError
end

let(:json_credentials) do
{
"type": 'service_account',
"project_id": 'example',
"private_key_id": 'c09c4593eee53707ca9f4208fbd6fe72b29fc7ab',
"private_key": OpenSSL::PKey::RSA.new(2048).to_s,
"client_email": client_email,
"client_id": 'acedc3c0a63b3562376386f0.apps.googleusercontent.com',
"auth_uri": 'https://accounts.google.com/o/oauth2/auth',
"token_uri": 'https://oauth2.googleapis.com/token',
"auth_provider_x509_cert_url": 'https://www.googleapis.com/oauth2/v1/certs',
"client_x509_cert_url": client_x509_cert_url,
"universe_domain": 'googleapis.com'
}.to_json
end

before do
allow(client).to receive(:json_key)

# Mock the Google::Auth::ServiceAccountCredentials
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds).
and_return(double(fetch_access_token!: { 'access_token' => mock_token }))
allow(Google::Auth::ServiceAccountCredentials).to receive(:make_creds)
.and_return(double(fetch_access_token!: { 'access_token' => mock_token }))
end

it "should initialize" do
it 'should initialize' do
expect { client }.not_to raise_error
end

describe "credentials path" do
it "can be a path to a file" do
it 'can be a path to a file' do
fcm = FCM.new("README.md")
expect(fcm.__send__(:json_key).class).to eq(File)
end

it "can be an IO object" do
fcm = FCM.new(StringIO.new("hey"))
it 'raises an error when passed a large path' do
expect do
FCM.new(large_file_name).__send__(:json_key)
end.to raise_error(creds_error)
end

it 'can be an IO object' do
fcm = FCM.new(StringIO.new('hey'))
expect(fcm.__send__(:json_key).class).to eq(StringIO)

temp_file = Tempfile.new('hello_world.json')
temp_file.write(json_credentials)
fcm_with_temp_file = FCM.new(temp_file)

expect do
fcm_with_temp_file
end.not_to raise_error
temp_file.close
temp_file.unlink
end

it 'raises an error when passed a non IO-like object' do
expect do
FCM.new(nil, '', {}).__send__(:json_key)
end.to raise_error(creds_error, 'credentials must be' \
' an IO-like object or path. You passed nil.')

expect do
FCM.new(json_credentials, '', {}).__send__(:json_key)
end.to raise_error(creds_error, 'credentials must be' \
' an IO-like object or path. You passed a String.')

expect do
FCM.new({}, '', {}).__send__(:json_key)
end.to raise_error(creds_error, 'credentials must be' \
' an IO-like object or path. You passed a Hash.')
end

it 'raises an error when passed a non-existent credentials file path' do
fcm = FCM.new('spec/fake_credentials.json', '', {})
expect { fcm.__send__(:json_key) }.to raise_error(creds_error)
end

it 'raises an error when passed a string of a file that does not exist' do
fcm = FCM.new('example.txt', '', {})
expect { fcm.__send__(:json_key) }.to raise_error(creds_error)
end
end

Expand Down