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