allura
Revision | 5b7bb04f125bf5c323c9b7b79d571dc9cfb21bde (tree) |
---|---|
Zeit | 2010-06-03 23:40:48 |
Autor | Wolf <wolf@geek...> |
Commiter | Wolf |
Teach the REST API authentication to better handle Unicode
@@ -23,6 +23,49 @@ from .session import project_doc_session, project_orm_session | ||
23 | 23 | |
24 | 24 | log = logging.getLogger(__name__) |
25 | 25 | |
26 | +def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'): | |
27 | + """ | |
28 | + Returns a bytestring version of 's', encoded as specified in 'encoding'. | |
29 | + | |
30 | + If strings_only is True, don't convert (some) non-string-like objects. | |
31 | + | |
32 | + This function was borrowed from Django | |
33 | + """ | |
34 | + if strings_only and isinstance(s, (types.NoneType, int)): | |
35 | + return s | |
36 | + elif not isinstance(s, basestring): | |
37 | + try: | |
38 | + return str(s) | |
39 | + except UnicodeEncodeError: | |
40 | + if isinstance(s, Exception): | |
41 | + # An Exception subclass containing non-ASCII data that doesn't | |
42 | + # know how to print itself properly. We shouldn't raise a | |
43 | + # further exception. | |
44 | + return ' '.join([smart_str(arg, encoding, strings_only, | |
45 | + errors) for arg in s]) | |
46 | + return unicode(s).encode(encoding, errors) | |
47 | + elif isinstance(s, unicode): | |
48 | + r = s.encode(encoding, errors) | |
49 | + return r | |
50 | + elif s and encoding != 'utf-8': | |
51 | + return s.decode('utf-8', errors).encode(encoding, errors) | |
52 | + else: | |
53 | + return s | |
54 | + | |
55 | +def generate_smart_str(params): | |
56 | + for (key, value) in params: | |
57 | + if value is None: continue | |
58 | + yield smart_str(key), smart_str(value) | |
59 | + | |
60 | +def urlencode(params): | |
61 | + """ | |
62 | + A version of Python's urllib.urlencode() function that can operate on | |
63 | + unicode strings. The parameters are first case to UTF-8 encoded strings and | |
64 | + then encoded as per normal. | |
65 | + """ | |
66 | + return urllib.urlencode([i for i in generate_smart_str(params)]) | |
67 | + | |
68 | + | |
26 | 69 | class ApiToken(MappedClass): |
27 | 70 | class __mongometa__: |
28 | 71 | name='api_token' |
@@ -45,7 +88,7 @@ class ApiToken(MappedClass): | ||
45 | 88 | # Validate signature |
46 | 89 | api_signature = params['api_signature'] |
47 | 90 | params = sorted((k,v) for k,v in params.iteritems() if k != 'api_signature') |
48 | - string_to_sign = path + '?' + urllib.urlencode(params) | |
91 | + string_to_sign = path + '?' + urlencode(params) | |
49 | 92 | digest = hmac.new(self.secret_key, string_to_sign, hashlib.sha256) |
50 | 93 | return digest.hexdigest() == api_signature |
51 | 94 | except KeyError: |
@@ -62,7 +105,7 @@ class ApiToken(MappedClass): | ||
62 | 105 | if not has_api_timestamp: |
63 | 106 | params.append(('api_timestamp', datetime.utcnow().isoformat())) |
64 | 107 | if not has_api_signature: |
65 | - string_to_sign = path + '?' + urllib.urlencode(sorted(params)) | |
108 | + string_to_sign = path + '?' + urlencode(sorted(params)) | |
66 | 109 | digest = hmac.new(self.secret_key, string_to_sign, hashlib.sha256) |
67 | 110 | params.append(('api_signature', digest.hexdigest())) |
68 | 111 | return params |
@@ -4,12 +4,55 @@ from sys import stdin, stdout | ||
4 | 4 | import hmac, hashlib |
5 | 5 | from datetime import datetime |
6 | 6 | import os |
7 | -from urllib import urlencode | |
8 | -from urllib2 import urlopen | |
7 | +import urllib | |
8 | +from urllib2 import urlopen, HTTPError | |
9 | 9 | from urlparse import urlparse, urljoin |
10 | 10 | from optparse import OptionParser |
11 | 11 | from ConfigParser import ConfigParser |
12 | 12 | |
13 | +def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'): | |
14 | + """ | |
15 | + Returns a bytestring version of 's', encoded as specified in 'encoding'. | |
16 | + | |
17 | + If strings_only is True, don't convert (some) non-string-like objects. | |
18 | + | |
19 | + This function was borrowed from Django | |
20 | + """ | |
21 | + if strings_only and isinstance(s, (types.NoneType, int)): | |
22 | + return s | |
23 | + elif not isinstance(s, basestring): | |
24 | + try: | |
25 | + return str(s) | |
26 | + except UnicodeEncodeError: | |
27 | + if isinstance(s, Exception): | |
28 | + # An Exception subclass containing non-ASCII data that doesn't | |
29 | + # know how to print itself properly. We shouldn't raise a | |
30 | + # further exception. | |
31 | + return ' '.join([smart_str(arg, encoding, strings_only, | |
32 | + errors) for arg in s]) | |
33 | + return unicode(s).encode(encoding, errors) | |
34 | + elif isinstance(s, unicode): | |
35 | + r = s.encode(encoding, errors) | |
36 | + return r | |
37 | + elif s and encoding != 'utf-8': | |
38 | + return s.decode('utf-8', errors).encode(encoding, errors) | |
39 | + else: | |
40 | + return s | |
41 | + | |
42 | +def generate_smart_str(params): | |
43 | + for (key, value) in params: | |
44 | + if value is None: continue | |
45 | + yield smart_str(key), smart_str(value) | |
46 | + | |
47 | +def urlencode(params): | |
48 | + """ | |
49 | + A version of Python's urllib.urlencode() function that can operate on | |
50 | + unicode strings. The parameters are first case to UTF-8 encoded strings and | |
51 | + then encoded as per normal. | |
52 | + """ | |
53 | + return urllib.urlencode([i for i in generate_smart_str(params)]) | |
54 | + | |
55 | + | |
13 | 56 | class Signer(object): |
14 | 57 | |
15 | 58 | def __init__(self, secret_key, api_key): |
@@ -34,7 +77,7 @@ def main(): | ||
34 | 77 | (options, args) = op.parse_args() |
35 | 78 | |
36 | 79 | page = args[0] |
37 | - f = open(args[1], 'r') if len(args)>=1 else stdin | |
80 | + f = open(args[1], 'r') if len(args)>1 else stdin | |
38 | 81 | markdown = f.read() |
39 | 82 | |
40 | 83 | config = ConfigParser() |
@@ -48,8 +91,11 @@ def main(): | ||
48 | 91 | |
49 | 92 | sign = Signer(secret_key, api_key) |
50 | 93 | params = sign(urlparse(url).path, [('text', markdown)]) |
51 | - result = urlopen(url, urlencode(params)) | |
52 | - stdout.write(result.read()) | |
94 | + try: | |
95 | + result = urlopen(url, urlencode(params)) | |
96 | + stdout.write(result.read()) | |
97 | + except HTTPError, e: | |
98 | + stdout.write(e.read()) | |
53 | 99 | |
54 | 100 | if __name__ == '__main__': |
55 | 101 | main() |