Google Calendar Python API со служебным аккаунтом не возвращает результатов

Я пытаюсь загрузить список календарей Google, которые видит мой пользователь. У меня настроена служебная учетная запись, и у меня есть этот код (который работает с другой учетной записью для 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)

Что приводит к:

{u'etag': u'"YXs7dfDSFSFD9F0k/sofIhuT4dSLyxKaRH7vNpx5BOEU"',
u'items': [],
u'kind': u'calendar#calendarList',
u'nextSyncToken': u'00001405024697194000'}

Дело в том, что я знаю, что у меня есть доступ к календарям, и я даже использовал "Попробуй!" раздел этой страницы: https://developers.google.com/google-apps/calendar/v3/reference/calendarList/list, который возвращает ожидаемые результаты, около 10 календарей. Я пробовал разные версии параметра области ('https://www.googleapis.com/auth/calendar.readonly 'и [' https://www.googleapis.com/auth/calendar ',' https://www.googleapis.com/auth/calendar.readonly ']), и я также попытался добавить свой собственный адрес электронной почты в качестве параметров «prn» и «sub», каждый из которых дает мне ошибку «доступ запрещен». Я не могу понять, почему я могу авторизоваться с помощью своей учетной записи службы, но я не могу фактически просмотреть результаты, когда эти результаты явно просматриваются с помощью другого метода аутентификации.

Что я делаю неправильно?


person futilerebel    schedule 10.07.2014    source источник


Ответы (4)


Просто хотел обновить этот вопрос с помощью решения, с которым я действительно остановился.

Если вы хотите, чтобы служебная учетная запись могла иметь права владельца в календаре, похоже, что единственный способ сделать это - использовать API для создания дополнительного календаря при аутентификации как учетная запись службы (https://developers.google.com/google-apps/calendar/v3/reference/calendars/insert). У учетной записи службы нет основных календарей, владельцем которых она является, но она будет владельцем нового вторичного календаря.

Затем вы можете использовать конечную точку API правил контроля доступа (https://developers.google.com/google-apps/calendar/v3/reference/acl/insert), чтобы поделиться этим календарем со своей учетной записью Google (или любой другой учетной записью, не связанной с услугами, которая может войти в систему для просмотра календаря в браузер). Нового пользователя также можно назначить владельцем.

Изменить: приведенное ниже решение не является точным.

Этого не было в вопросе, но я нигде не мог его найти, и я думаю, что люди, вероятно, захотят узнать, поэтому я тоже отвечу на него здесь:

Если вы хотите, чтобы участники получали уведомление по электронной почте, когда вы вставляете или обновляете событие, установка для параметра sendNotifications значения True - не единственное, что вам нужно сделать. После предоставления доступа к календарю этому пользователю пользователю также необходимо перейти в «Настройки календаря» для календаря вашей учетной записи службы и активировать уведомления по электронной почте на вкладке «Напоминания и уведомления».

Изменить: это неточно. Любой, кто изменит свои настройки для получения уведомлений в этом календаре, получит их независимо от того, были ли они приглашены на мероприятие. Я не нашел способа пригласить людей на мероприятие через API с помощью сервисный аккаунт, и только приглашенные получат электронное письмо с приглашением. Я задам еще один вопрос по этому поводу на StackOverflow.

person futilerebel    schedule 18.07.2014

Список календарей конкретного пользователя можно увидеть только при выполнении вызова с учетными данными этого пользователя. Учетная запись службы всегда будет предоставлять вам список календаря, принадлежащий этой учетной записи службы. Я предлагаю вам использовать токен Oauth2 для своей учетной записи, если вы хотите получить доступ к своему списку календарей.

person luc    schedule 11.07.2014
comment
Проблема с использованием OAuth заключается в том, что я считаю, что ночной скрипт не может получить доступ к календарю, пока меня нет, если только я чего-то не упускаю. Мне нужно, чтобы сценарий Python мог получить доступ и изменить эти данные в одночасье без вмешательства пользователя. - person futilerebel; 11.07.2014
comment
Я идиот, совместное использование календаря со своим сервисным аккаунтом полностью заработало! Спасибо!! - person futilerebel; 11.07.2014
comment
Однако, похоже, я могу дать ему только разрешение на чтение. Вы знаете, как предоставить учетной записи службы доступ на запись? - person futilerebel; 11.07.2014
comment
Существуют обновляемые токены Oauth, которые позволяют получать учетные данные, когда пользователь не в сети (при первом обмене токенами вы получаете токен доступа и токен обновления, которые затем можно использовать для получения дополнительных токенов доступа). Обычно вы должны иметь возможность предоставить учетной записи службы все разрешения, если только ваша учетная запись не находится внутри домена, для которого установлены максимальные разрешения. - person luc; 12.07.2014
comment
Подробнее о токенах см. Здесь: developers.google.com/accounts/docs/OAuth2WebServer#refresh Обратите внимание, что вам нужно отключить access_type, чтобы получать токены обновления. - person luc; 12.07.2014
comment
Круто, я попытаюсь заставить своих администраторов предоставить моей учетной записи службы доступ на запись. Если это не сработает, я посмотрю на токены обновления. - person futilerebel; 12.07.2014

После некоторого покопания вы можете разрешить доступ пользователям за пределами вашего домена (учетные записи службы API) доступ к календарю Google.

Проверьте следующие настройки в: Google Admin, Google Apps, Календарь, Настройки общего доступа и выберите один из следующих вариантов:

( ) 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

Затем вы можете предоставить доступ к своим календарям службе api в настройках общего доступа к каждому календарю в разделе Share with specific people

Как это кому-то помогает.

person limeyd    schedule 17.11.2014

Изначально я тоже боролся с этим. Но я думаю, что добился достаточного прогресса, чтобы быть чем-то полезным.

Похоже, что каждой учетной записи службы назначен уникальный «client_email». Это значение представляет собой поле, содержащееся в загружаемом файле JSON из раздела API -> Credentials вашего проекта. Формат выглядит так: [email protected]. В приведенном выше коде похоже, что вы ссылаетесь на это значение с помощью переменной service_account_email_address.

Поскольку адрес client_email не совпадает с вашим личным адресом электронной почты, основной и дополнительный календари, на которые вы ссылаетесь, - это не одно и то же. По умолчанию я не думаю, что с электронной почтой учетной записи службы связаны какие-либо календари, и именно поэтому вы не видите никаких календарей в своих результатах. Это становится очевидным, когда вы добавляете два календаря, а затем используете методы calendarList.list (). Но прежде чем вы сможете добавлять какие-либо календари, вы должны изменить область действия с «только чтение» на «чтение / запись». Вы можете сделать это, изменив свой код на:

## 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'

После того, как вы разрешите чтение / запись, вы можете добавить два календаря, а затем перечислить их, выполнив следующие действия:

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)

Как только календари будут созданы, попробуйте запустить свой код еще раз, и вы должны увидеть два новых календаря, перечисленных в результатах «данных». Однако эти два календаря по-прежнему связаны только с учетной записью службы. Поэтому, если вы войдете в свой личный адрес электронной почты ([email protected]), эти календари не будут отображаться в вашем личном профиле (http://calendar.google.com).

Чтобы они отображались в вашем личном списке календарей, вы должны разрешить своему личному адресу электронной почты просматривать и / или изменять календарь. Для этого вам нужно будет вставить ACL, который дает соответствующие разрешения для вашей личной электронной почты ([email protected]).

Однако, если вы пытаетесь пойти в другом направлении (разрешите учетной записи службы доступ к вашему личному календарю). Вы должны войти в свою личную учетную запись электронной почты ([email protected]), предоставить общий доступ к конкретному календарю служебной учетной записи client_email и назначить соответствующий уровень разрешений (вносить изменения и управлять совместным доступом и т. Д.). После того, как вы предоставите доступ к календарю учетной записи службы, календарь будет указан в ваших результатах calendarList (). List (), и вы сможете просматривать события должным образом.

person Randy B.    schedule 22.10.2015
comment
Когда вы переходите к просмотру отдельных событий календаря, вы должны указать используемый CalendarId. По умолчанию он использует «первичный», что неверно. eventsResult = service.events().list(calendarId=gCalendarId, singleEvents=True, orderBy='startTime').execute() events = eventsResult.get('items', []) - person Randy B.; 14.11.2015