· 7 years ago · Dec 17, 2017, 04:40 PM
1# https://apps.twitter.com/
2# Create new App and get the four strings
3
4def oauth():
5 return {"consumer_key": "...your key...",
6 "consumer_secret":...your key...",
7 "token_key": "...your key...",
8 "token_secret": "...your key..."}
9
10import urllib.request, urllib.parse, urllib.error
11import oauth
12import hidden
13
14# https://apps.twitter.com/
15# Create App and get the four strings, put them in hidden.py
16
17def augment(url, parameters):
18 secrets = hidden.oauth()
19 consumer = oauth.OAuthConsumer(secrets['consumer_key'],
20 secrets['consumer_secret'])
21 token = oauth.OAuthToken(secrets['token_key'], secrets['token_secret'])
22
23 oauth_request = oauth.OAuthRequest.from_consumer_and_token(consumer,
24 token=token, http_method='GET', http_url=url,
25 parameters=parameters)
26 oauth_request.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(),
27 consumer, token)
28 return oauth_request.to_url()
29
30
31def test_me():
32 print('* Calling Twitter...')
33 url = augment('https://api.twitter.com/1.1/statuses/user_timeline.json',
34 {'screen_name': 'Ali', 'count': '2'})
35 print(url)
36 connection = urllib.request.urlopen(url)
37 data = connection.read()
38 print(data)
39 headers = dict(connection.getheaders())
40 print(headers)
41
42import urllib.request, urllib.parse, urllib.error
43import twurl
44import ssl
45
46# https://apps.twitter.com/
47# Create App and get the four strings, put them in hidden.py
48
49TWITTER_URL = 'https://api.twitter.com/1.1/statuses/user_timeline.json'
50
51# Ignore SSL certificate errors
52ctx = ssl.create_default_context()
53ctx.check_hostname = False
54ctx.verify_mode = ssl.CERT_NONE
55
56while True:
57 print('')
58 acct = input('Enter Twitter Account:')
59 if (len(acct) < 1): break
60 url = twurl.augment(TWITTER_URL,
61 {'screen_name': acct, 'count': '2'})
62 print('Retrieving', url)
63 connection = urllib.request.urlopen(url, context=ctx)
64 data = connection.read().decode()
65 print(data[:250])
66 headers = dict(connection.getheaders())
67 # print headers
68 print('Remaining', headers['x-rate-limit-remaining'])
69
70import cgi
71import urllib
72import time
73import random
74import urlparse
75import hmac
76import binascii
77
78
79VERSION = '1.0' # Hi Blaine!
80HTTP_METHOD = 'GET'
81SIGNATURE_METHOD = 'PLAINTEXT'
82
83
84class OAuthError(RuntimeError):
85 """Generic exception class."""
86 def __init__(self, message='OAuth error occured.'):
87 self.mymessage = message
88
89def build_authenticate_header(realm=''):
90 """Optional WWW-Authenticate header (401 error)"""
91 return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
92
93def escape(s):
94 """Escape a URL including any /."""
95 return urllib.quote(s, safe='~')
96
97def _utf8_str(s):
98 """Convert unicode to utf-8."""
99 if isinstance(s, unicode):
100 return s.encode("utf-8")
101 else:
102 return str(s)
103
104def generate_timestamp():
105 """Get seconds since epoch (UTC)."""
106 return int(time.time())
107
108def generate_nonce(length=8):
109 """Generate pseudorandom number."""
110 return ''.join([str(random.randint(0, 9)) for i in range(length)])
111
112
113class OAuthConsumer(object):
114 """Consumer of OAuth authentication.
115
116 OAuthConsumer is a data type that represents the identity of the Consumer
117 via its shared secret with the Service Provider.
118
119 """
120 key = None
121 secret = None
122
123 def __init__(self, key, secret):
124 self.key = key
125 self.secret = secret
126
127
128class OAuthToken(object):
129 """OAuthToken is a data type that represents an End User via either an access
130 or request token.
131
132 key -- the token
133 secret -- the token secret
134
135 """
136 key = None
137 secret = None
138
139 def __init__(self, key, secret):
140 self.key = key
141 self.secret = secret
142
143 def to_string(self):
144 return urllib.urlencode({'oauth_token': self.key,
145 'oauth_token_secret': self.secret})
146
147 def from_string(s):
148 """ Returns a token from something like:
149 oauth_token_secret=xxx&oauth_token=xxx
150 """
151 params = cgi.parse_qs(s, keep_blank_values=False)
152 key = params['oauth_token'][0]
153 secret = params['oauth_token_secret'][0]
154 return OAuthToken(key, secret)
155 from_string = staticmethod(from_string)
156
157 def __str__(self):
158 return self.to_string()
159
160
161class OAuthRequest(object):
162 """OAuthRequest represents the request and can be serialized.
163
164 OAuth parameters:
165 - oauth_consumer_key
166 - oauth_token
167 - oauth_signature_method
168 - oauth_signature
169 - oauth_timestamp
170 - oauth_nonce
171 - oauth_version
172 ... any additional parameters, as defined by the Service Provider.
173 """
174 parameters = None # OAuth parameters.
175 http_method = HTTP_METHOD
176 http_url = None
177 version = VERSION
178
179 def __init__(self, http_method=HTTP_METHOD, http_url=None, parameters=None):
180 self.http_method = http_method
181 self.http_url = http_url
182 self.parameters = parameters or {}
183
184 def set_parameter(self, parameter, value):
185 self.parameters[parameter] = value
186
187 def get_parameter(self, parameter):
188 try:
189 return self.parameters[parameter]
190 except:
191 if parameter == "oauth_token" : return None
192 raise OAuthError('Parameter not found: %s' % parameter)
193
194 def _get_timestamp_nonce(self):
195 return self.get_parameter('oauth_timestamp'), self.get_parameter(
196 'oauth_nonce')
197
198 def get_nonoauth_parameters(self):
199 """Get any non-OAuth parameters."""
200 parameters = {}
201 for k, v in self.parameters.iteritems():
202 # Ignore oauth parameters.
203 if k.find('oauth_') < 0:
204 parameters[k] = v
205 return parameters
206
207 def to_header(self, realm=''):
208 """Serialize as a header for an HTTPAuth request."""
209 auth_header = 'OAuth realm="%s"' % realm
210 # Add the oauth parameters.
211 if self.parameters:
212 for k, v in self.parameters.iteritems():
213 if k[:6] == 'oauth_':
214 auth_header += ', %s="%s"' % (k, escape(str(v)))
215 return {'Authorization': auth_header}
216
217 def to_postdata(self):
218 """Serialize as post data for a POST request."""
219 return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v)))
220 for k, v in self.parameters.iteritems()])
221
222 def to_url(self):
223 """Serialize as a URL for a GET request."""
224 return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata())
225
226 def get_normalized_parameters(self):
227 """Return a string that contains the parameters that must be signed."""
228 # Chuck - Make a copy of the parameters so we can modify them
229 params = dict(self.parameters)
230 try:
231 # Exclude the signature if it exists.
232 del params['oauth_signature']
233 except:
234 pass
235 # Escape key values before sorting.
236 key_values = [(escape(_utf8_str(k)), escape(_utf8_str(v)))
237 for k,v in params.items()]
238 # Sort lexicographically, first after key, then after value.
239 key_values.sort()
240 # Combine key value pairs into a string.
241 return '&'.join(['%s=%s' % (k, v) for k, v in key_values])
242
243 def get_normalized_http_method(self):
244 """Uppercases the http method."""
245 return self.http_method.upper()
246
247 def get_normalized_http_url(self):
248 """Parses the URL and rebuilds it to be scheme://host/path."""
249 parts = urlparse.urlparse(self.http_url)
250 scheme, netloc, path = parts[:3]
251 # Exclude default port numbers.
252 if scheme == 'http' and netloc[-3:] == ':80':
253 netloc = netloc[:-3]
254 elif scheme == 'https' and netloc[-4:] == ':443':
255 netloc = netloc[:-4]
256 return '%s://%s%s' % (scheme, netloc, path)
257
258 def sign_request(self, signature_method, consumer, token):
259 """Set the signature parameter to the result of build_signature."""
260 # Set the signature method.
261 self.set_parameter('oauth_signature_method',
262 signature_method.get_name())
263 # Set the signature.
264 self.set_parameter('oauth_signature',
265 self.build_signature(signature_method, consumer, token))
266
267 def build_signature(self, signature_method, consumer, token):
268 """Calls the build signature method within the signature method."""
269 return signature_method.build_signature(self, consumer, token)
270
271 def from_request(http_method, http_url, headers=None, parameters=None,
272 query_string=None):
273 """Combines multiple parameter sources."""
274 if parameters is None:
275 parameters = {}
276
277 # Headers
278 if headers and 'Authorization' in headers:
279 auth_header = headers['Authorization']
280 # Check that the authorization header is OAuth.
281 if auth_header.index('OAuth') > -1:
282 auth_header = auth_header.lstrip('OAuth ')
283 try:
284 # Get the parameters from the header.
285 header_params = OAuthRequest._split_header(auth_header)
286 parameters.update(header_params)
287 except:
288 raise OAuthError('Unable to parse OAuth parameters from '
289 'Authorization header.')
290
291 # GET or POST query string.
292 if query_string:
293 query_params = OAuthRequest._split_url_string(query_string)
294 parameters.update(query_params)
295
296 # URL parameters.
297 param_str = urlparse.urlparse(http_url)[4] # query
298 url_params = OAuthRequest._split_url_string(param_str)
299 parameters.update(url_params)
300
301 if parameters:
302 return OAuthRequest(http_method, http_url, parameters)
303
304 return None
305 from_request = staticmethod(from_request)
306
307 def from_consumer_and_token(oauth_consumer, token=None,
308 http_method=HTTP_METHOD, http_url=None, parameters=None):
309 if not parameters:
310 parameters = {}
311
312 defaults = {
313 'oauth_consumer_key': oauth_consumer.key,
314 'oauth_timestamp': generate_timestamp(),
315 'oauth_nonce': generate_nonce(),
316 'oauth_version': OAuthRequest.version,
317 }
318
319 defaults.update(parameters)
320 parameters = defaults
321
322 if token:
323 parameters['oauth_token'] = token.key
324
325 return OAuthRequest(http_method, http_url, parameters)
326 from_consumer_and_token = staticmethod(from_consumer_and_token)
327
328 def from_token_and_callback(token, callback=None, http_method=HTTP_METHOD,
329 http_url=None, parameters=None):
330 if not parameters:
331 parameters = {}
332
333 parameters['oauth_token'] = token.key
334
335 if callback:
336 parameters['oauth_callback'] = callback
337
338 return OAuthRequest(http_method, http_url, parameters)
339 from_token_and_callback = staticmethod(from_token_and_callback)
340
341 def _split_header(header):
342 """Turn Authorization: header into parameters."""
343 params = {}
344 parts = header.split(',')
345 for param in parts:
346 # Ignore realm parameter.
347 if param.find('realm') > -1:
348 continue
349 # Remove whitespace.
350 param = param.strip()
351 # Split key-value.
352 param_parts = param.split('=', 1)
353 # Remove quotes and unescape the value.
354 params[param_parts[0]] = urllib.unquote(param_parts[1].strip('"'))
355 return params
356 _split_header = staticmethod(_split_header)
357
358 def _split_url_string(param_str):
359 """Turn URL string into parameters."""
360 parameters = cgi.parse_qs(param_str, keep_blank_values=False)
361 for k, v in parameters.iteritems():
362 parameters[k] = urllib.unquote(v[0])
363 return parameters
364 _split_url_string = staticmethod(_split_url_string)
365
366class OAuthServer(object):
367 """A worker to check the validity of a request against a data store."""
368 timestamp_threshold = 300 # In seconds, five minutes.
369 version = VERSION
370 signature_methods = None
371 data_store = None
372
373 def __init__(self, data_store=None, signature_methods=None):
374 self.data_store = data_store
375 self.signature_methods = signature_methods or {}
376
377 def set_data_store(self, data_store):
378 self.data_store = data_store
379
380 def get_data_store(self):
381 return self.data_store
382
383 def add_signature_method(self, signature_method):
384 self.signature_methods[signature_method.get_name()] = signature_method
385 return self.signature_methods
386
387 def fetch_request_token(self, oauth_request):
388 """Processes a request_token request and returns the
389 request token on success.
390 """
391 try:
392 # Get the request token for authorization.
393 token = self._get_token(oauth_request, 'request')
394 except OAuthError:
395 # No token required for the initial token request.
396 version = self._get_version(oauth_request)
397 consumer = self._get_consumer(oauth_request)
398 self._check_signature(oauth_request, consumer, None)
399 # Fetch a new token.
400 token = self.data_store.fetch_request_token(consumer)
401 return token
402
403 def fetch_access_token(self, oauth_request):
404 """Processes an access_token request and returns the
405 access token on success.
406 """
407 version = self._get_version(oauth_request)
408 consumer = self._get_consumer(oauth_request)
409 # Get the request token.
410 token = self._get_token(oauth_request, 'request')
411 self._check_signature(oauth_request, consumer, token)
412 new_token = self.data_store.fetch_access_token(consumer, token)
413 return new_token
414
415 def verify_request(self, oauth_request):
416 """Verifies an api call and checks all the parameters."""
417 # -> consumer and token
418 version = self._get_version(oauth_request)
419 consumer = self._get_consumer(oauth_request)
420 # Get the access token.
421 token = self._get_token(oauth_request, 'access')
422 self._check_signature(oauth_request, consumer, token)
423 parameters = oauth_request.get_nonoauth_parameters()
424 return consumer, token, parameters
425
426 def authorize_token(self, token, user):
427 """Authorize a request token."""
428 return self.data_store.authorize_request_token(token, user)
429
430 def get_callback(self, oauth_request):
431 """Get the callback URL."""
432 return oauth_request.get_parameter('oauth_callback')
433
434 def build_authenticate_header(self, realm=''):
435 """Optional support for the authenticate header."""
436 return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
437
438 def _get_version(self, oauth_request):
439 """Verify the correct version request for this server."""
440 try:
441 version = oauth_request.get_parameter('oauth_version')
442 except:
443 version = VERSION
444 if version and version != self.version:
445 raise OAuthError('OAuth version %s not supported.' % str(version))
446 return version
447
448 def _get_signature_method(self, oauth_request):
449 """Figure out the signature with some defaults."""
450 try:
451 signature_method = oauth_request.get_parameter(
452 'oauth_signature_method')
453 except:
454 signature_method = SIGNATURE_METHOD
455 try:
456 # Get the signature method object.
457 signature_method = self.signature_methods[signature_method]
458 except:
459 signature_method_names = ', '.join(self.signature_methods.keys())
460 raise OAuthError('Signature method %s not supported try one of the '
461 'following: %s' % (signature_method, signature_method_names))
462
463 return signature_method
464
465 def _get_consumer(self, oauth_request):
466 consumer_key = oauth_request.get_parameter('oauth_consumer_key')
467 consumer = self.data_store.lookup_consumer(consumer_key)
468 if not consumer:
469 raise OAuthError('Invalid consumer.')
470 return consumer
471
472 def _get_token(self, oauth_request, token_type='access'):
473 """Try to find the token for the provided request token key."""
474 token_field = oauth_request.get_parameter('oauth_token')
475 token = self.data_store.lookup_token(token_type, token_field)
476 if not token:
477 raise OAuthError('Invalid %s token: %s' % (token_type, token_field))
478 return token
479
480 def _check_signature(self, oauth_request, consumer, token):
481 timestamp, nonce = oauth_request._get_timestamp_nonce()
482 self._check_timestamp(timestamp)
483 self._check_nonce(consumer, token, nonce)
484 signature_method = self._get_signature_method(oauth_request)
485 try:
486 signature = oauth_request.get_parameter('oauth_signature')
487 except:
488 raise OAuthError('Missing signature.')
489 # Validate the signature.
490 valid_sig = signature_method.check_signature(oauth_request, consumer,
491 token, signature)
492 if not valid_sig:
493 key, base = signature_method.build_signature_base_string(
494 oauth_request, consumer, token)
495 raise OAuthError('Invalid signature. Expected signature base '
496 'string: %s' % base)
497 built = signature_method.build_signature(oauth_request, consumer, token)
498
499 def _check_timestamp(self, timestamp):
500 """Verify that timestamp is recentish."""
501 timestamp = int(timestamp)
502 now = int(time.time())
503 lapsed = now - timestamp
504 if lapsed > self.timestamp_threshold:
505 raise OAuthError('Expired timestamp: given %d and now %s has a '
506 'greater difference than threshold %d' %
507 (timestamp, now, self.timestamp_threshold))
508
509 def _check_nonce(self, consumer, token, nonce):
510 """Verify that the nonce is uniqueish."""
511 nonce = self.data_store.lookup_nonce(consumer, token, nonce)
512 if nonce:
513 raise OAuthError('Nonce already used: %s' % str(nonce))
514
515
516class OAuthClient(object):
517 """OAuthClient is a worker to attempt to execute a request."""
518 consumer = None
519 token = None
520
521 def __init__(self, oauth_consumer, oauth_token):
522 self.consumer = oauth_consumer
523 self.token = oauth_token
524
525 def get_consumer(self):
526 return self.consumer
527
528 def get_token(self):
529 return self.token
530
531 def fetch_request_token(self, oauth_request):
532 """-> OAuthToken."""
533 raise NotImplementedError
534
535 def fetch_access_token(self, oauth_request):
536 """-> OAuthToken."""
537 raise NotImplementedError
538
539 def access_resource(self, oauth_request):
540 """-> Some protected resource."""
541 raise NotImplementedError
542
543
544class OAuthDataStore(object):
545 """A database abstraction used to lookup consumers and tokens."""
546
547 def lookup_consumer(self, key):
548 """-> OAuthConsumer."""
549 raise NotImplementedError
550
551 def lookup_token(self, oauth_consumer, token_type, token_token):
552 """-> OAuthToken."""
553 raise NotImplementedError
554
555 def lookup_nonce(self, oauth_consumer, oauth_token, nonce):
556 """-> OAuthToken."""
557 raise NotImplementedError
558
559 def fetch_request_token(self, oauth_consumer):
560 """-> OAuthToken."""
561 raise NotImplementedError
562
563 def fetch_access_token(self, oauth_consumer, oauth_token):
564 """-> OAuthToken."""
565 raise NotImplementedError
566
567 def authorize_request_token(self, oauth_token, user):
568 """-> OAuthToken."""
569 raise NotImplementedError
570
571
572class OAuthSignatureMethod(object):
573 """A strategy class that implements a signature method."""
574 def get_name(self):
575 """-> str."""
576 raise NotImplementedError
577
578 def build_signature_base_string(self, oauth_request, oauth_consumer, oauth_token):
579 """-> str key, str raw."""
580 raise NotImplementedError
581
582 def build_signature(self, oauth_request, oauth_consumer, oauth_token):
583 """-> str."""
584 raise NotImplementedError
585
586 def check_signature(self, oauth_request, consumer, token, signature):
587 built = self.build_signature(oauth_request, consumer, token)
588 return built == signature
589
590
591class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod):
592
593 def get_name(self):
594 return 'HMAC-SHA1'
595
596 def build_signature_base_string(self, oauth_request, consumer, token):
597 sig = (
598 escape(oauth_request.get_normalized_http_method()),
599 escape(oauth_request.get_normalized_http_url()),
600 escape(oauth_request.get_normalized_parameters()),
601 )
602
603 key = '%s&' % escape(consumer.secret)
604 if token and token.secret:
605 key += escape(token.secret)
606 raw = '&'.join(sig)
607 return key, raw
608
609 def build_signature(self, oauth_request, consumer, token):
610 """Builds the base signature string."""
611 key, raw = self.build_signature_base_string(oauth_request, consumer,
612 token)
613
614 # HMAC object.
615 try:
616 import hashlib # 2.5
617 hashed = hmac.new(key, raw, hashlib.sha1)
618 except:
619 import sha # Deprecated
620 hashed = hmac.new(key, raw, sha)
621
622 # Calculate the digest base 64.
623 return binascii.b2a_base64(hashed.digest())[:-1]
624
625
626class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod):
627
628 def get_name(self):
629 return 'PLAINTEXT'
630
631 def build_signature_base_string(self, oauth_request, consumer, token):
632 """Concatenates the consumer key and secret."""
633 sig = '%s&' % escape(consumer.secret)
634 if token:
635 sig = sig + escape(token.secret)
636 return sig, sig
637
638 def build_signature(self, oauth_request, consumer, token):
639 key, raw = self.build_signature_base_string(oauth_request, consumer,
640 token)
641 return key