问题
Ruby: 2.3.1
Rails: 5.0.0
I need to access and update my private Google Sheet through Google Sheets API. Eventually this shall be done by a recurring background job in my Rails application. Hence I have setup a service account in my Google Cloud Console.
But to start playing with Google Sheets API I have created a script:
require 'google/apis/sheets_v4'
ENV["GOOGLE_ACCOUNT_TYPE"] = 'service_account'
ENV["GOOGLE_CLIENT_EMAIL"] = '<client_email_from_downloaded_json_here>'
ENV["GOOGLE_PRIVATE_KEY"] = "<private_key_from_downloaded_json_here>"
SCOPE = Google::Apis::SheetsV4::AUTH_SPREADSHEETS
authorization = Google::Auth.get_application_default(SCOPE)
# Initialize the API
service = Google::Apis::SheetsV4::SheetsService.new
service.authorization = authorization
spreadsheet_id = '<MY SPREADSHEET ID HERE>'
sheet_name = 'Sheet1'
range = "#{sheet_name}!A2:B"
response = service.get_spreadsheet_values(spreadsheet_id, range)
puts 'No data found.' if response.values.empty?
response.values.each do |row|
# Print columns A and B, which correspond to indices 0 and 1.
puts "#{row[0]}, #{row[1]}"
end
When I run that script
$ rails runner -e development lib/scripts/google_sheets_api_demo.rb
/home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/google-api-client-0.9.12/lib/google/apis/core/http_command.rb:211:in `check_status': forbidden: The caller does not have permission (Google::Apis::ClientError)
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/google-api-client-0.9.12/lib/google/apis/core/api_command.rb:102:in `check_status'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/google-api-client-0.9.12/lib/google/apis/core/http_command.rb:179:in `process_response'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/google-api-client-0.9.12/lib/google/apis/core/http_command.rb:286:in `execute_once'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/google-api-client-0.9.12/lib/google/apis/core/http_command.rb:107:in `block (2 levels) in execute'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/retriable-2.1.0/lib/retriable.rb:54:in `block in retriable'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/retriable-2.1.0/lib/retriable.rb:48:in `times'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/retriable-2.1.0/lib/retriable.rb:48:in `retriable'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/google-api-client-0.9.12/lib/google/apis/core/http_command.rb:104:in `block in execute'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/retriable-2.1.0/lib/retriable.rb:54:in `block in retriable'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/retriable-2.1.0/lib/retriable.rb:48:in `times'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/retriable-2.1.0/lib/retriable.rb:48:in `retriable'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/google-api-client-0.9.12/lib/google/apis/core/http_command.rb:96:in `execute'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/google-api-client-0.9.12/lib/google/apis/core/base_service.rb:346:in `execute_or_queue_command'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/google-api-client-0.9.12/generated/google/apis/sheets_v4/service.rb:322:in `get_spreadsheet_values'
from lib/scripts/google_sheets_api_demo.rb:35:in `<top (required)>'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/railties-5.0.0/lib/rails/commands/runner.rb:60:in `load'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/railties-5.0.0/lib/rails/commands/runner.rb:60:in `<top (required)>'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/railties-5.0.0/lib/rails/commands/commands_tasks.rb:138:in `require'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/railties-5.0.0/lib/rails/commands/commands_tasks.rb:138:in `require_command!'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/railties-5.0.0/lib/rails/commands/commands_tasks.rb:104:in `runner'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/railties-5.0.0/lib/rails/commands/commands_tasks.rb:49:in `run_command!'
from /home/jignesh/.rvm/gems/ruby-2.3.1@my_integrations/gems/railties-5.0.0/lib/rails/commands.rb:18:in `<top (required)>'
from bin/rails:9:in `require'
from bin/rails:9:in `<main>'
If I try to access a public sheet it works. I have tried a lot but unable to figure out what is causing this. Initially I used API Key approach for authorization but as per the documentation found at https://developers.google.com/api-client-library/ruby/auth/api-keys it says
When calling APIs that do not access private user data, you can use simple API keys. These keys are used to authenticate your application for accounting purposes. The Google Developers Console documentation also describes API keys.
Note: If you do need to access private user data, you must use OAuth 2.0. See Using OAuth 2.0 for Web Server Applications, Using OAuth 2.0 for Installed Applications, and Using OAuth 2.0 for Server to Server Applications for more information.
Hence I switched to Service Account approach. But it didn't helped.
Can anybody please let me know what is it I am missing?
Additional Findings:
The following post https://stackoverflow.com/a/38530755/936494 looks helpful but as per my requirement I want to avoid 3-Legged oauth the reason being the sheet is supposed to be accessed and updated through a background-job.
回答1:
This is resolved. Thanks to Steve Bazyl (Google member) who provided the following valuable suggestion which straight-away resolved the problem:
Did you share the sheet with the service account? Service accounts are effectively separate users and need to be on the ACLs the same as any other user.
Reference: https://github.com/google/google-api-ruby-client/issues/461#issuecomment-243328234
来源:https://stackoverflow.com/questions/39216040/google-sheets-api-v4-service-account-forbidden-the-caller-does-not-have-permi