@@ -43,7 +43,7 @@ class DjangoJSONEncoder(JSONEncoder):
43
43
44
44
def default (self , obj ):
45
45
date_obj = self .default_date (obj )
46
- if date_obj :
46
+ if date_obj is not None :
47
47
return date_obj
48
48
elif isinstance (obj , decimal .Decimal ):
49
49
return str (obj )
@@ -75,6 +75,8 @@ def default_date(self, obj):
75
75
if obj .microsecond :
76
76
r = r [:12 ]
77
77
return r
78
+ elif isinstance (obj , datetime .timedelta ):
79
+ return obj .seconds
78
80
79
81
dumps = curry (dumps , cls = DjangoJSONEncoder )
80
82
@@ -100,14 +102,19 @@ def get_form_class(self):
100
102
except KeyError :
101
103
raise Http404
102
104
105
+ def get_form_kwargs (self ):
106
+ kwargs = super (API , self ).get_form_kwargs ()
107
+ if self .api_key :
108
+ kwargs ['api_key' ] = self .api_key
109
+ return kwargs
110
+
103
111
def get_access_params (self ):
104
112
key = self .request .REQUEST .get ('key' )
105
113
sign = self .request .REQUEST .get ('sign' )
106
114
return key , sign
107
115
108
116
def sign_ok (self , sign ):
109
- pairs = ((field , self .request .REQUEST .get (field ))
110
- for field in sorted (self .get_form_class ()().fields .keys ()))
117
+ pairs = self .normalized_parameters ()
111
118
filtered_pairs = itertools .ifilter (lambda x : x [1 ] is not None , pairs )
112
119
query_string = '&' .join (('=' .join (pair ) for pair in filtered_pairs ))
113
120
query_string = urllib2 .quote (query_string .encode ('utf-8' ))
@@ -117,6 +124,20 @@ def sign_ok(self, sign):
117
124
sha1 ).hexdigest ()
118
125
return constant_time_compare (sign , digest )
119
126
127
+ def normalized_parameters (self ):
128
+ """
129
+ Normalize django request to key value pairs sorted by key first and then value
130
+ """
131
+ for field in sorted (self .get_form (self .get_form_class ()).fields .keys ()):
132
+ value = self .request .REQUEST .getlist (field ) or None
133
+ if not value :
134
+ continue
135
+ if len (value ) == 1 :
136
+ yield field , value [0 ]
137
+ else :
138
+ for item in sorted (value ):
139
+ yield field , item
140
+
120
141
def render_to_json_response (self , context , ** response_kwargs ):
121
142
data = dumps (context )
122
143
response_kwargs ['content_type' ] = 'application/json'
@@ -191,6 +212,6 @@ def dispatch(self, request, *args, **kwargs):
191
212
return super (API , self ).dispatch (request , * args , ** kwargs )
192
213
193
214
# Access denied
194
- self .log .info ('Access Denied %s' , self .request .REQUEST )
215
+ self .log .warning ('Access Denied %s' , self .request .REQUEST )
195
216
196
217
return HttpResponse (status = 401 )
0 commit comments