Friday, December 21, 2012

Ruby meets the Google Calendar API (or Ruby vs. Google API)

Setup: Windows 7 (64Bit) with Ruby 1.9.3p125 (Railsinstaller 2.1.0 for Windows) and google-api-client 0.5.0

Some time ago i tried to access the Google Calendar via a small Ruby script. I found some frameworks that should do the job but realized at some point that none of the frameworks covered the complete Google Calendar API (e. g. you couldn’t delete an event). I put the idear by side and didn’t thought much about it.

Last week i tried it again. This time i thought i will try the ruby api client from Google directly. So i installed the google api client (Setup instructions can be found here):

gem install google-api-client
Configured a Service account with the Google API Console (more infos here).

I took one of the examples from Google (analytics.rb) and modified it slightly to use the Calendar API instead of the Analytics API.

require 'rubygems'
require 'google/api_client'
require 'json'

key_file = "privatekey.p12"
key_secret = "notasecret"

client = Google::APIClient.new()

#This call leads to an "Invalid keyfile or passphrase (ArgumentError)" Exception
key = Google::APIClient::PKCS12.load_key(key_file, key_secret)

asserter = Google::APIClient::JWTAsserter.new(
   '###################################@developer.gserviceaccount.com',
   'https://www.googleapis.com/auth/calendar',
   key)

client.authorization = asserter.authorize()
service = client.discovered_api('calendar', 'v3')

result = client.execute(
       :api_method => service.calendar_list.list,
       :headers => {'Content-Type' => 'application/json'})

parsed = JSON.parse(result.data.to_json)

parsed["items"].each { |item| puts item["summary"] }
This leads to an "Invalid keyfile or passphrase (ArgumentError)" exception from google-api-client-0.5.0/lib/google/api_client/auth/pkcs12.rb:43 (BTW: This script works perfectly on Linux!)

I took a closer look at the File pkcs12.rb and since there is nothing special in here i just copied the code and removed the exception handling to see what is really going on here.

require 'rubygems'
require 'google/api_client'
require 'json'

key_file = "privatekey.p12"
key_secret = "notasecret"

client = Google::APIClient.new()

content = File.read(key_file)

#The gives the PKCS12_parse Error
pkcs12 = OpenSSL::PKCS12.new(content, key_secret)
key = pkcs12.key

asserter = Google::APIClient::JWTAsserter.new(
   '###################################@developer.gserviceaccount.com',
   'https://www.googleapis.com/auth/calendar',
   key)

client.authorization = asserter.authorize()
service = client.discovered_api('calendar', 'v3')

result = client.execute(
       :api_method => service.calendar_list.list,
       :headers => {'Content-Type' => 'application/json'})

parsed = JSON.parse(result.data.to_json)

parsed["items"].each { |item| puts item["summary"] }
this time i got a “PKCS12_parse Error: mac verify failure”. So now i got a different Error. I searched the net an found a hint on Stackoverflow. The cause of the Error is that ruby on Windows by default read the keyfile as text but it must be read as binary. Well the given solution for the question is not working with ruby 1.9.3 but i found another way:
require 'rubygems'
require 'google/api_client'
require 'json'

key_file = "privatekey.p12"
key_secret = "notasecret"

client = Google::APIClient.new()

#We must tell ruby to read the keyfile in binary mode.
content = File.read(key_file, :mode => 'rb')

pkcs12 = OpenSSL::PKCS12.new(content, key_secret)
key = pkcs12.key

asserter = Google::APIClient::JWTAsserter.new(
   '###################################@developer.gserviceaccount.com',
   'https://www.googleapis.com/auth/calendar',
   key)

client.authorization = asserter.authorize()
service = client.discovered_api('calendar', 'v3')

result = client.execute(
       :api_method => service.calendar_list.list,
       :headers => {'Content-Type' => 'application/json'})

parsed = JSON.parse(result.data.to_json)

parsed["items"].each { |item| puts item["summary"] }
Now i get a list of all Calendars for the E-Mail address of the Service Account. Great. But i want to work with the Calendar from my normal google account and not the one for the Service Account.

The Solution is easy: Share which ever Calendar you want to access with the E-Mail address of the Service Account.

Now you can access the Calender and for example list all events.

Google Calendar API Reference:
https://developers.google.com/accounts/docs/OAuth2?hl=de#scenarios Some information about OAuth 2.0 to Access Google APIs:
https://developers.google.com/accounts/docs/OAuth2?hl=de#scenarios

No comments:

Post a Comment