-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathauth.py
More file actions
358 lines (336 loc) · 9.72 KB
/
auth.py
File metadata and controls
358 lines (336 loc) · 9.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
#
#Copyright (C) 2009 asylumfunk
#
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU General Public License for more details.
#
#You may obtain a copy of this license at:
# http://www.gnu.org/licenses/gpl-3.0.html
#Standard modules
import urllib2
#Third-party modules
import oauthtwitter
import twitter
import xbmc
import xbmcgui
#Project modules
import alert
import crypt
import gui
import urlshortener
from default import cfg
from default import i18n
methods = { "basic" : "basic" , "oauth" : "oauth" }
"""
Handles API authentication
"""
class Authentication:
_consumerKey = crypt.de( cfg.get( "auth.consumerKey" ) )
_consumerSecret = crypt.de( cfg.get( "auth.consumerSecret" ) )
"""
Description:
Default constructor
"""
def __init__( self ):
self.isAuthenticated = False
self.api = None
"""
Description:
Performs either Basic or OAuth Authentication, dependent upon user's settings
Args:
editing::bool - if the user is editing existing credentials
Returns:
True::bool - the user successfully authenticated
False::bool - the user did not successfully authenticate
"""
def authenticate( self, editing = False ):
method = cfg.get( "auth.method" )
if not editing and method == methods[ "oauth" ]:
self.authenticate_oauth()
else:
if self.authenticate_basic( editing = editing ):
if editing or not method:
if self.promptUseOAuth():
self.authenticate_oauth()
return self.isAuthenticated
"""
Description:
Performs Basic Authentication
Args:
editing::bool - if the user is editing existing credentials
Returns:
True::bool - the user successfully authenticated
False::bool - the user did not successfully authenticate
"""
def authenticate_basic( self, editing ):
username, password = self.getUsernameAndPassword()
isValid = False
if editing or not username or not password:
needsVerified = False
else:
needsVerified = True
while not isValid:
if needsVerified:
api = twitter.Api( username, password )
if self.verifyCredentials( api ):
self.setUsernameAndPassword( username, password )
return True
else:
alert.invalidUsernamePassword()
username, password = self.promptUsernameAndPassword()
if not username or not password:
return False
else:
needsVerified = True
"""
Description:
Performs OAuth Authentication
Returns:
True::bool - the user successfully authenticated
False::bool - the user did not successfully authenticate
"""
def authenticate_oauth( self ):
accessToken = self.getAccessToken()
if accessToken:
api = oauthtwitter.OAuthApi( self._consumerKey, self._consumerSecret, accessToken )
if self.verifyCredentials( api ):
return True
else:
alert.invalidOAuthPin()
if not self.authenticate_basic( editing = True ):
return False
requestToken, authorizationUrl = self.generateTokenAndUrl()
if not self.sendAuthorizationMessage( authorizationUrl ):
return False
self.displayOAuthInstructions()
while True:
pin = self.promptPin()
if not pin:
return False
else:
accessToken = self.requestAccessToken( requestToken, pin )
if accessToken:
api = oauthtwitter.OAuthApi( self._consumerKey, self._consumerSecret, accessToken )
if self.verifyCredentials( api ):
self.setAccessToken( accessToken )
return True
alert.invalidOAuthPin()
"""
Description:
Displays OAuth instructions
"""
def displayOAuthInstructions( self ):
instructions = xbmcgui.Dialog()
return instructions.ok( i18n( "auth.secureLogin.heading" ) , i18n( "auth.secureLogin.instructions.line1" ), i18n( "auth.secureLogin.instructions.line2" ), i18n( "auth.secureLogin.instructions.line3" ) )
"""
Description:
Generates an OAuth request token and authorization url
Returns:
( requestToken::oauthtwitter.oauth.OAuthToken, authorizationUrl::string )
"""
def generateTokenAndUrl( self ):
api = oauthtwitter.OAuthApi( self._consumerKey, self._consumerSecret )
requestToken = api.getRequestToken()
authorizationUrl = api.getAuthorizationURL( requestToken )
return requestToken, authorizationUrl
"""
Description:
Retrieves the user's OAuth access token
Returns:
if the value exists:
oauthtwitter.oauth.OAuthToken - the user's OAuth access token
else:
None
"""
def getAccessToken( self ):
tokenString = crypt.de( cfg.get( "auth.accessToken" ) )
if tokenString:
return oauthtwitter.oauth.OAuthToken.from_string( tokenString )
else:
return None
"""
Description:
Retrieves the user's current username and password
Returns:
( username, password ) - a tuple of the user's current username and password (plain-text)
"""
def getUsernameAndPassword( self ):
username = cfg.get( "auth.username" )
password = cfg.get( "auth.password" )
password = crypt.de( password )
return username, password
"""
Description:
Prompts for a password
Returns:
Accept: the user-supplied input
Cancel: None
"""
def promptPassword( self ):
return gui.promptInput( headerText = i18n( "EnterPassword" ), maskInput = True )
"""
Description:
Prompts the user to enter their authorization PIN
Returns:
Accept: str - the PIN
Cancel: None
"""
def promptPin( self ):
prompt = xbmcgui.Dialog()
ShowAndGetNumber = 0
pin = prompt.numeric( ShowAndGetNumber, i18n( "auth.promptPin.heading" ) )
if pin:
return pin
else:
return None
"""
Description:
Prompts the user to see if they want to use OAuth
Returns:
bool - whether or not the user wants to use OAuth
"""
def promptUseOAuth( self ):
prompt = xbmcgui.Dialog()
return prompt.yesno( i18n( "auth.secureLogin.heading" ), i18n( "auth.secureLogin.prompt.line1" ), i18n( "auth.secureLogin.prompt.line2" ), i18n( "auth.secureLogin.prompt.line3" ) )
"""
Description:
Prompts for a username
Args:
username::string - the user's current username
Returns:
Accept: the user-supplied input
Cancel: None
"""
def promptUsername( self, username ):
return gui.promptInput( headerText = i18n( "EnterUsername" ), defaultValue = username )
"""
Description:
Prompts for a username and password
Returns:
( username, password ) - if both are entered
( None, None ) - if the prompt is cancelled
"""
def promptUsernameAndPassword( self ):
username = cfg.get( "auth.username" )
while True:
username = self.promptUsername( username )
if not username:
return None, None
password = self.promptPassword()
if password:
return username, password
"""
Description:
Requests an OAuth access token
Args:
requestToken::oauthtwitter.oauth.OAuthToken - the user's OAuth request token
pin::int - the user's validation PIN
Returns:
if successful:
oauthtwitter.oauth.OAuthToken - the user's OAuth access token
else:
None
"""
def requestAccessToken( self, requestToken, pin ):
try:
api = oauthtwitter.OAuthApi( self._consumerKey, self._consumerSecret, requestToken )
accessToken = api.getAccessToken( pin )
return accessToken
except urllib2.HTTPError, e:
if e.code == 401:
return None
else:
raise
"""
Description:
Sends an authorization message to the user's account
Args:
authorizationUrl::string - a Twitter.com authorization url
"""
def sendAuthorizationMessage( self, authorizationUrl ):
username = cfg.get( "auth.username" )
shortUrl = urlshortener.create( authorizationUrl )
if shortUrl:
message = i18n( "auth.oauth.sendAuthorizationMessage.messageFormat" ) % { "url" : shortUrl }
else:
message = authorizationUrl
try:
self.api.PostDirectMessage( username, message )
return True
except:
alert.oauthMessageNotSent()
return False
"""
Description:
Sets the user's OAuth access token
Args:
accessToken::oauthtwitter.oauth.OAuthToken - the user's OAuth access token
"""
def setAccessToken( self, accessToken ):
cfg.set({
"auth.accessToken" : crypt.en( str( accessToken ) ),
"auth.method" : methods[ "oauth" ],
"auth.password" : ""
})
"""
Description:
Updates the user configuration if values have changed
Args:
newUsername::string - new username
newPassword::string - new, plaintext password
Returns:
True::bool - the configuration was updated
False::bool - the configuration was not updated
"""
def setUsernameAndPassword( self, newUsername, newPassword ):
oldUsername, oldPassword = self.getUsernameAndPassword()
oldMethod = cfg.get( "auth.method" )
newMethod = methods[ "basic" ]
if newUsername != oldUsername or \
newPassword != oldPassword or \
newMethod != methods[ "basic" ]:
cfg.set({
"auth.username" : newUsername,
"auth.password" : crypt.en( newPassword ),
"auth.method" : newMethod,
"auth.accessToken" : ""
})
return True
else:
return False
"""
Description:
Verifies that the user has access to the requested account
Args:
api::twitter.Api - the API used for verification
Returns:
True::bool - user has access to the account
False::bool - user does not have access to the account
TODO:
add a progress bar while waiting
"""
def verifyCredentials( self, api ):
progress = xbmcgui.DialogProgress()
progress.create( i18n( "auth.verifyCredentials.heading" ), i18n( "auth.verifyCredentials.line1" ) )
try:
user = api.VerifyCredentials()
if user and user.GetScreenName():
self.isAuthenticated = True
self.api = api
progress.update( 100 )
return True
except urllib2.HTTPError, e:
if e.code == 401:
pass
else:
progress.update( 100 )
raise
progress.update( 100 )
return False