64934ebf173831c93146d4a58cb9bb192b96c2a7
[kiosk.git] / gdata_oauth.py
1 # https://developers.google.com/accounts/docs/OAuth2ForDevices
2 # https://developers.google.com/drive/web/auth/web-server
3 # https://developers.google.com/google-apps/calendar/v3/reference/calendars
4 # https://developers.google.com/picasa-web/
5
6 import sys
7 import urllib
8 try:
9     import httplib     # python2
10 except ImportError:
11     import http.client # python3
12 import os.path
13 import json
14 import time
15 from oauth2client.client import OAuth2Credentials
16 import gdata.calendar.service
17 import gdata.docs.service
18 import gdata.photos.service, gdata.photos
19 from apiclient.discovery import build
20 import httplib2
21 from apiclient.discovery import build
22 import datetime
23 import ssl
24
25 class OAuth:
26     def __init__(self, client_id, client_secret):
27         print("gdata: initializing oauth token...")
28         self.client_id = client_id
29         self.client_secret = client_secret
30         self.user_code = None
31         #print 'Client id: %s' % (client_id)
32         #print 'Client secret: %s' % (client_secret)
33         self.token = None
34         self.device_code = None
35         self.verfication_url = None
36         self.token_file = 'client_secrets.json'
37         self.scope = [
38             #'https://www.googleapis.com/auth/calendar',
39             #'https://www.googleapis.com/auth/drive',
40             #'https://docs.google.com/feeds',
41             #'https://www.googleapis.com/auth/calendar.readonly',
42             #'https://picasaweb.google.com/data/',
43             'https://www.googleapis.com/auth/photoslibrary.readonly',
44             #'http://picasaweb.google.com/data/',
45             #'https://www.google.com/calendar/feeds/',
46         ]
47         self.host = 'accounts.google.com'
48         self.reset_connection()
49         self.load_token()
50         self.last_action = 0
51         self.ssl_ctx = None
52
53     # this setup is isolated because it eventually generates a BadStatusLine
54     # exception, after which we always get httplib.CannotSendRequest errors.
55     # When this happens, we try re-creating the exception.
56     def reset_connection(self):
57         self.ssl_ctx = ssl.create_default_context(cafile='/usr/local/etc/ssl/cert.pem')
58         httplib.HTTPConnection.debuglevel = 2
59         self.conn = httplib.HTTPSConnection(self.host, context=self.ssl_ctx)
60
61     def load_token(self):
62         token = None
63         if os.path.isfile(self.token_file):
64             f = open(self.token_file)
65             json_token = f.read()
66             self.token = json.loads(json_token)
67             f.close()
68
69     def save_token(self):
70         f = open(self.token_file, 'w')
71         f.write(json.dumps(self.token))
72         f.close()
73
74     def has_token(self):
75         if self.token != None:
76             print("gdata: we have a token!")
77         else:
78             print("gdata: we have no token.")
79         return self.token != None
80
81     def get_user_code(self):
82         self.conn.request(
83             "POST",
84             "/o/oauth2/device/code",
85             urllib.urlencode({
86                 'client_id': self.client_id,
87                 'scope'    : ' '.join(self.scope)
88             }),
89             {"Content-type": "application/x-www-form-urlencoded"})
90         response = self.conn.getresponse()
91         if response.status == 200:
92             data = json.loads(response.read())
93             self.device_code = data['device_code']
94             self.user_code = data['user_code']
95             self.verification_url = data['verification_url']
96             self.retry_interval = data['interval']
97         else:
98             print("gdata: %d" % response.status)
99             print(response.read())
100             sys.exit()
101         return self.user_code
102
103     def get_new_token(self):
104         # call get_device_code if not already set
105         if self.user_code == None:
106             print("gdata: getting user code")
107             self.get_user_code()
108
109         while self.token == None:
110             self.conn.request(
111                 "POST",
112                 "/o/oauth2/token",
113                 urllib.urlencode({
114                     'client_id'     : self.client_id,
115                     'client_secret' : self.client_secret,
116                     'code'          : self.device_code,
117                     'grant_type'    : 'http://oauth.net/grant_type/device/1.0'
118                     }),
119                 {"Content-type": "application/x-www-form-urlencoded"})
120             response = self.conn.getresponse()
121             if response.status == 200:
122                 data = json.loads(response.read())
123                 if 'access_token' in data:
124                     self.token = data
125                     self.save_token()
126                 else:
127                     time.sleep(self.retry_interval + 2)
128             else:
129                 print("gdata: failed to get token")
130                 print(response.status)
131                 print(response.read())
132
133     def refresh_token(self):
134         if self.checking_too_often():
135             print("gdata: not refreshing yet, too soon...")
136             return False
137         else:
138             print('gdata: trying to refresh oauth token...')
139         self.reset_connection()
140         refresh_token = self.token['refresh_token']
141         self.conn.request(
142             "POST",
143             "/o/oauth2/token",
144             urllib.urlencode({
145                 'client_id'     : self.client_id,
146                 'client_secret' : self.client_secret,
147                 'refresh_token' : refresh_token,
148                 'grant_type'    : 'refresh_token'
149                 }),
150             {"Content-type": "application/x-www-form-urlencoded"})
151
152         response = self.conn.getresponse()
153         self.last_action = time.time()
154         if response.status == 200:
155             data = json.loads(response.read())
156             if 'access_token' in data:
157                 self.token = data
158                 # in fact we NEVER get a new refresh token at this point
159                 if not 'refresh_token' in self.token:
160                     self.token['refresh_token'] = refresh_token
161                     self.save_token()
162                 return True
163         print("gdata: unexpected response %d to renewal request" % response.status)
164         print(response.read())
165         return False
166
167     def checking_too_often(self):
168         now = time.time()
169         return (now - self.last_action) <= 30
170
171     # https://developers.google.com/picasa-web/
172     def photos_service(self):
173         headers = {
174             "Authorization": "%s %s"  % (self.token['token_type'], self.token['access_token'])
175         }
176         client = gdata.photos.service.PhotosService(additional_headers=headers)
177         return client
178
179     # https://developers.google.com/drive/
180     def docs_service(self):
181         cred = OAuth2Credentials(self.token['access_token'],
182                                  self.client_id,
183                                  self.client_secret,
184                                  self.token['refresh_token'],
185                                  datetime.datetime.now(),
186                                  'http://accounts.google.com/o/oauth2/token',
187                                  'KitchenKiosk/0.9')
188         http = httplib2.Http(disable_ssl_certificate_validation=True)
189         http = cred.authorize(http)
190         service = build('drive', 'v2', http)
191         return service
192
193     # https://developers.google.com/google-apps/calendar/
194     def calendar_service(self):
195         cred = OAuth2Credentials(self.token['access_token'],
196                                  self.client_id,
197                                  self.client_secret,
198                                  self.token['refresh_token'],
199                                  datetime.datetime.now(),
200                                  'http://accounts.google.com/o/oauth2/token',
201                                  'KitchenKiosk/0.9')
202         http = httplib2.Http(disable_ssl_certificate_validation=True)
203         http = cred.authorize(http)
204         service = build('calendar', 'v3', http)
205         return service