Fix indentation.
[kiosk.git] / gdata_oauth.py
1 #!/usr/bin/env python3
2
3 # https://developers.google.com/accounts/docs/OAuth2ForDevices
4 # https://developers.google.com/drive/web/auth/web-server
5 # https://developers.google.com/google-apps/calendar/v3/reference/calendars
6 # https://developers.google.com/picasa-web/
7
8 import logging
9 import os
10 import pickle
11 import sys
12 import urllib.request, urllib.parse, urllib.error
13
14 from apiclient.discovery import build
15 from google_auth_oauthlib.flow import InstalledAppFlow
16
17
18 logger = logging.getLogger(__file__)
19
20
21 class OAuth:
22     def __init__(self, client_secret_file='client_secret.json'):
23         self.credentials = None
24         self.credentials_pickle = './credentials.pickle'
25         if os.path.exists(self.credentials_pickle):
26             logger.debug(
27                 f'Refreshing credentials from disk pickle file {self.credentials_pickle}'
28             )
29             self.credentials = pickle.load(open(self.credentials_pickle, 'rb'))
30         else:
31             logger.debug(
32                 f'{self.credentials_pickle} does not exist; calling Google.'
33             )
34             self.refresh_credentials(client_secret_file)
35             self.save()
36         assert self.credentials is not None
37
38     def refresh_credentials(self, client_secret_file):
39         scopes = [
40             'https://www.googleapis.com/auth/calendar.events.readonly',
41             'https://www.googleapis.com/auth/calendar.readonly',
42             'https://www.googleapis.com/auth/drive.readonly',
43             'https://www.googleapis.com/auth/drive.photos.readonly',
44             'https://www.googleapis.com/auth/photoslibrary.readonly',
45             # 'https://www.googleapis.com/auth/keep.readonly',
46         ]
47         flow = InstalledAppFlow.from_client_secrets_file(
48             self.client_secret_file, scopes=scopes
49         )
50         self.credentials = flow.run_console()
51
52     def save(self):
53         pickle.dump(self.credentials, open(self.credentials_pickle, 'wb'))
54
55     def calendar_service(self):
56         return build("calendar", "v3", credentials=self.credentials)
57
58     def keep_service(self):
59         return build('keep', 'v1',
60                      discoveryServiceUrl='https://keep.googleapis.com/$discovery/rest?version=v1',
61                      credentials=self.credentials)
62         #print(gkeep_service.notes().list().execute())
63
64
65 # class OAuth:
66 #     def __init__(self, client_id: str, client_secret: str) -> None:
67 #         print("gdata: initializing oauth token...")
68 #         self.client_id = client_id
69 #         self.client_secret = client_secret
70 #         self.user_code: Optional[str] = None
71 #         # print 'Client id: %s' % (client_id)
72 #         # print 'Client secret: %s' % (client_secret)
73 #         self.token: Optional[Dict] = None
74 #         self.device_code = None
75 #         self.verfication_url = None
76 #         self.token_file = "client_secrets.json"
77 #         scopes = [
78 #             'https://www.googleapis.com/auth/calendar.events.readonly',
79 #             'https://www.googleapis.com/auth/calendar.readonly',
80 #             'https://www.googleapis.com/auth/drive.readonly',
81 #             'https://www.googleapis.com/auth/drive.photos.readonly',
82 #             'https://www.googleapis.com/auth/photoslibrary.readonly',
83 #             # 'https://www.googleapis.com/auth/keep.readonly',
84 #         ]
85 #         self.host = "accounts.google.com"
86 #         self.reset_connection()
87 #         self.load_token()
88 #         self.last_action = 0.0
89 #         self.ssl_ctx: Optional[ssl.SSLContext] = None
90
91 #     # this setup is isolated because it eventually generates a BadStatusLine
92 #     # exception, after which we always get httplib.CannotSendRequest errors.
93 #     # When this happens, we try re-creating the exception.
94 #     def reset_connection(self) -> None:
95 #         self.ssl_ctx = ssl.create_default_context() #cafile="/usr/local/etc/ssl/cert.pem")
96 #         http.client.HTTPConnection.debuglevel = 2
97 #         self.conn = http.client.HTTPSConnection(self.host, context=self.ssl_ctx)
98
99 #     def load_token(self) -> None:
100 #         token = None
101 #         if os.path.isfile(self.token_file):
102 #             f = open(self.token_file)
103 #             json_token = f.read()
104 #             self.token = json.loads(json_token)
105 #             f.close()
106
107 #     def save_token(self) -> None:
108 #         f = open(self.token_file, "w")
109 #         f.write(json.dumps(self.token))
110 #         f.close()
111
112 #     def has_token(self) -> bool:
113 #         if self.token is not None:
114 #             print("gdata: we have a token!")
115 #         else:
116 #             print("gdata: we have no token.")
117 #         return self.token is not None
118
119 #     def get_user_code(self) -> Optional[str]:
120 #         self.conn.request(
121 #             "POST",
122 #             "/o/oauth2/device/code",
123 #             urllib.parse.urlencode(
124 #                 {"client_id": self.client_id, "scope": " ".join(self.scope)}
125 #             ),
126 #             {"Content-type": "application/x-www-form-urlencoded"},
127 #         )
128 #         response = self.conn.getresponse()
129 #         if response.status == 200:
130 #             data = json.loads(response.read())
131 #             self.device_code = data["device_code"]
132 #             self.user_code = data["user_code"]
133 #             self.verification_url = data["verification_url"]
134 #             self.retry_interval = data["interval"]
135 #         else:
136 #             self.user_code = None
137 #             print(f"gdata: {response.status}")
138 #             print(response.read())
139 #             sys.exit(-1)
140 #         return self.user_code
141
142 #     def get_new_token(self) -> None:
143 #         # call get_device_code if not already set
144 #         if self.user_code is None:
145 #             print("gdata: getting user code")
146 #             self.get_user_code()
147
148 #         while self.token is None:
149 #             self.conn.request(
150 #                 "POST",
151 #                 "/o/oauth2/token",
152 #                 urllib.parse.urlencode(
153 #                     {
154 #                         "client_id": self.client_id,
155 #                         "client_secret": self.client_secret,
156 #                         "code": self.device_code,
157 #                         "grant_type": "http://oauth.net/grant_type/device/1.0",
158 #                     }
159 #                 ),
160 #                 {"Content-type": "application/x-www-form-urlencoded"},
161 #             )
162 #             response = self.conn.getresponse()
163 #             if response.status == 200:
164 #                 data = json.loads(response.read())
165 #                 if "access_token" in data:
166 #                     self.token = data
167 #                     self.save_token()
168 #                 else:
169 #                     time.sleep(self.retry_interval + 2)
170 #             else:
171 #                 print("gdata: failed to get token")
172 #                 print((response.status))
173 #                 print((response.read()))
174
175 #     def refresh_token(self) -> bool:
176 #         if self.checking_too_often():
177 #             print("gdata: not refreshing yet, too soon...")
178 #             return False
179 #         else:
180 #             print("gdata: trying to refresh oauth token...")
181 #         self.reset_connection()
182 #         if self.token is None:
183 #             return False
184
185 #         refresh_token = self.token["refresh_token"]
186 #         self.conn.request(
187 #             "POST",
188 #             "/o/oauth2/token",
189 #             urllib.parse.urlencode(
190 #                 {
191 #                     "client_id": self.client_id,
192 #                     "client_secret": self.client_secret,
193 #                     "refresh_token": refresh_token,
194 #                     "grant_type": "refresh_token",
195 #                 }
196 #             ),
197 #             {"Content-type": "application/x-www-form-urlencoded"},
198 #         )
199
200 #         response = self.conn.getresponse()
201 #         self.last_action = time.time()
202 #         if response.status == 200:
203 #             data: Dict = json.loads(response.read())
204 #             if "access_token" in data:
205 #                 self.token = data
206 #                 # in fact we NEVER get a new refresh token at this point
207 #                 if not "refresh_token" in self.token:
208 #                     self.token["refresh_token"] = refresh_token
209 #                     self.save_token()
210 #                 return True
211 #         print(("gdata: unexpected response %d to renewal request" % response.status))
212 #         print((response.read()))
213 #         return False
214
215 #     def checking_too_often(self) -> bool:
216 #         now = time.time()
217 #         return (now - self.last_action) <= 30
218
219 #     # https://developers.google.com/picasa-web/
220 #     def photos_service(self):
221 #         headers = {
222 #             "Authorization": "%s %s"
223 #             % (self.token["token_type"], self.token["access_token"])
224 #         }
225 #         client = gdata.photos.service.PhotosService(additional_headers=headers)
226 #         return client
227
228 #     # https://developers.google.com/drive/
229 #     def docs_service(self):
230 #         cred = OAuth2Credentials(
231 #             self.token["access_token"],
232 #             self.client_id,
233 #             self.client_secret,
234 #             self.token["refresh_token"],
235 #             datetime.datetime.now(),
236 #             "http://accounts.google.com/o/oauth2/token",
237 #             "KitchenKiosk/0.9",
238 #         )
239 #         http = httplib2.Http(disable_ssl_certificate_validation=True)
240 #         http = cred.authorize(http)
241 #         service = build("drive", "v2", http)
242 #         return service
243
244 #     # https://developers.google.com/google-apps/calendar/
245 #     def calendar_service(self):
246 #         cred = OAuth2Credentials(
247 #             self.token["access_token"],
248 #             self.client_id,
249 #             self.client_secret,
250 #             self.token["refresh_token"],
251 #             datetime.datetime.now(),
252 #             "http://accounts.google.com/o/oauth2/token",
253 #             "KitchenKiosk/0.9",
254 #         )
255 #         http = httplib2.Http(disable_ssl_certificate_validation=True)
256 #         http = cred.authorize(http)
257 #         service = build("calendar", "v3", http)
258 #         return service