Reputation: 343
I'm trying to download a list of Google calendars my user can see. I've got a service account set up, and I've got this code (which works for a different account for the Analytics API):
from apiclient.discovery import build
from oauth2client.client import SignedJwtAssertionCredentials
import pprint
def prepare_credentials():
f = file(keyFilePath, 'rb')
key = f.read()
f.close()
credentials = SignedJwtAssertionCredentials(
service_account_email_address, key,
scope='https://www.googleapis.com/auth/calendar.readonly')
return credentials
http = httplib2.Http()
credentials = prepare_credentials()
http = credentials.authorize(http)
service = build('calendar', 'v3', http=http)
httpRequest = service.calendarList().list()
data = httpRequest.execute()
pprint.pprint(data)
Which results in:
{u'etag': u'"YXs7dfDSFSFD9F0k/sofIhuT4dSLyxKaRH7vNpx5BOEU"',
u'items': [],
u'kind': u'calendar#calendarList',
u'nextSyncToken': u'00001405024697194000'}
The thing is, I know I have access to calendars, and I've even used the "Try It!" section of this page: https://developers.google.com/google-apps/calendar/v3/reference/calendarList/list, which returns the expected results, about 10 calendars. I've tried different versions of the scope parameter ('https://www.googleapis.com/auth/calendar.readonly', and ['https://www.googleapis.com/auth/calendar','https://www.googleapis.com/auth/calendar.readonly']), and I've also tried adding my own email address as both the "prn" and "sub" parameters, all of which give me "access denied" errors. I can't figure out why I can get authorized with my service account, but am unable to actually view the results, when those results are clearly viewable with a different authentication method.
What am I doing wrong?
Upvotes: 4
Views: 4734
Reputation: 73
Initially, I was struggling with this myself too. But I think that I made enough progress to be of some help.
It looks like each service account is assigned a unique "client_email". This value is a field contained in the downloadable JSON file, from the API -> Credentials section of your project. The format looks like [email protected]. In your code above, it appears that you reference this value by the "service_account_email_address" variable.
Since the client_email address is not the same as your personal email, the primary and secondary calendars you are referencing are not the same thing. By default, I don't think the service account email has any calendars associated with them and this is why you aren't seeing any calendars in your results. This becomes apparent when you add two calendars and then then use the calendarList.list() methods. But before you can add any calendars, you must change your scope from readonly to read/write. You can do this by changing your code to:
## Original Scope needs to be changed...
#scope='https://www.googleapis.com/auth/calendar.readonly'
## Must allow the ability to write to calendars...
scope='https://www.googleapis.com/auth/calendar'
Once you allow for read/write, you can add two calendars and then list them by doing the following:
def createCalendar(): ## Create the calendar...
calendar = {
'summary': 'Calendar used to communicate lease end dates.',
'timeZone': 'America/Los_Angeles'
}
created_calendar = service.calendars().insert(body=calendar).execute()
print "Created Calendar ID: ", created_calendar['id']
## In main block of code, call the createCalendar() twice...
createCalendar()
createCalendar()
## Show the current list of calendars (should have 2 listed)...
httpRequest = service.calendarList().list()
data = httpRequest.execute()
pprint.pprint(data)
Once the calendars are created, try running your code again and you should see the two new calendars listed within your "data" results now. However, these two calendars are still associated with the service account only. So if you log into your personal email ([email protected]), these calendars won't show up in your personal profile (http://calendar.google.com).
To have them show up in your personal calendar list, you must give your personal email address permissions to view and/or modify the calendar. In order to do this, you would have to insert an ACL which gives the appropriate permissions for your personal email ([email protected]).
However, if you are trying to go the other direction (allow service account to access your personal calendar). You have to log into your personal email account ([email protected]), share the specific calendar with the service account client_email, and assign the appropriate permissions level (make changes and manage sharing, etc.). Once you share the calendar with the service account, the calendar will be listed in your calendarList().list() results and you should be able to view the events as expected.
Upvotes: 0
Reputation: 210
After some digging you can allow access to users outside of you domain (api service accounts) access to the google calendar.
Check the following setting in: Google Admin, Google Apps, Calendar, Share Settings and choose one of the following:
( ) Only free/busy information (hide event details) Default
( ) Share all information, but outsiders cannot change calendars
(X) Share all information, and outsiders can change calendars
( ) Share all information, and allow managing of calendars
Then you can share your calendars to the api service in each individual calendar sharing settings under Share with specific people
How this helps someone.
Upvotes: 0
Reputation: 343
Just wanted to update this question with the solution I actually ended up going with.
If you want a service account to be able to have owner privileges on a calendar, it appears as though the only way to do this is to use the API to create a secondary calendar while authenticated as the service account (https://developers.google.com/google-apps/calendar/v3/reference/calendars/insert). The service account does not have any primary calendars it is the owner of, but it will be the owner of the new secondary calendar.
You can then use the Access Control Rules API endpoint (https://developers.google.com/google-apps/calendar/v3/reference/acl/insert) to share this calendar with your own Google account (or any non-service account that can log in to view the calendar in a browser). The new user can also be set as the owner.
Edit: The solution below is not accurate.
This wasn't in the question, but I couldn't find it anywhere and I think people will probably want to know, so I'll answer it here too:
If you want the attendees to receive an email notification when you insert or update an event, setting the 'sendNotifications' parameter to True is not the only thing you need to do. After sharing the calendar with that user, the user also needs to go into "Calendar settings" for your service account's calendar and activate email notifications in the "Reminders and Notifications" tab.
Edit: This is inaccurate. Anyone who changes their settings to receive notifications on this calendar will receive them regardless of whether they've been invited to the event. I have not found a way to invite people to an event via the API with a service account and have only the invitees receive an invitation email. I will ask another question on StackOverflow about this.
Upvotes: 2
Reputation: 3782
The list of calendars of a specific user can only ever be seen when performing the call with this user's credentials. Service account will always give you the calendar list belonging to that service account. I propose you use Oauth2 token for your account if you want to access your calendar list.
Upvotes: 1