Status: | Merged |
---|---|
Approved by: | Soren Hansen |
Approved revision: | 21 |
Merged at revision: | 21 |
Proposed branch: | lp:~soren/surveilr/auth |
Merge into: | lp:surveilr |
Diff against target: |
643 lines (+387/-22) 15 files modified
surveilr/api/__init__.py (+19/-0) surveilr/api/server/__init__.py (+21/-0) surveilr/api/server/app.py (+42/-10) surveilr/api/server/auth.py (+48/-0) surveilr/api/server/factory.py (+29/-0) surveilr/defaults.cfg (+12/-3) surveilr/models.py (+6/-0) surveilr/tests/api/server/__init__.py (+19/-0) surveilr/tests/api/server/test_app.py (+30/-9) surveilr/tests/api/server/test_auth.py (+82/-0) surveilr/tests/api/server/test_factory.py (+43/-0) surveilr/tests/test_utils.py (+6/-0) surveilr/utils.py (+6/-0) surveilr/who.ini (+23/-0) tools/pip-requirements.txt (+1/-0) |
To merge this branch: | bzr merge lp:~soren/surveilr/auth |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Soren Hansen | Pending | ||
Review via email: mp+87544@code.launchpad.net |
Commit message
Add authentication
Use repoze.who to provide a simple authentication system.
Description of the change
To post a comment you must log in.
lp:~soren/surveilr/auth
updated
- 21. By Soren Hansen
-
Add authentication
Use repoze.who to provide a simple authentication system.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'surveilr/api/__init__.py' |
2 | --- surveilr/api/__init__.py 2011-11-19 22:01:55 +0000 |
3 | +++ surveilr/api/__init__.py 2012-01-04 23:13:32 +0000 |
4 | @@ -0,0 +1,19 @@ |
5 | +""" |
6 | + Surveilr - Log aggregation, analysis and visualisation |
7 | + |
8 | + Copyright (C) 2011 Linux2Go |
9 | + |
10 | + This program is free software: you can redistribute it and/or |
11 | + modify it under the terms of the GNU Affero General Public License |
12 | + as published by the Free Software Foundation, either version 3 of |
13 | + the License, or (at your option) any later version. |
14 | + |
15 | + This program is distributed in the hope that it will be useful, |
16 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | + GNU Affero General Public License for more details. |
19 | + |
20 | + You should have received a copy of the GNU Affero General Public |
21 | + License along with this program. If not, see |
22 | + <http://www.gnu.org/licenses/>. |
23 | +""" |
24 | |
25 | === added directory 'surveilr/api/server' |
26 | === added file 'surveilr/api/server/__init__.py' |
27 | --- surveilr/api/server/__init__.py 1970-01-01 00:00:00 +0000 |
28 | +++ surveilr/api/server/__init__.py 2012-01-04 23:13:32 +0000 |
29 | @@ -0,0 +1,21 @@ |
30 | +""" |
31 | + Surveilr - Log aggregation, analysis and visualisation |
32 | + |
33 | + Copyright (C) 2011 Linux2Go |
34 | + |
35 | + This program is free software: you can redistribute it and/or |
36 | + modify it under the terms of the GNU Affero General Public License |
37 | + as published by the Free Software Foundation, either version 3 of |
38 | + the License, or (at your option) any later version. |
39 | + |
40 | + This program is distributed in the hope that it will be useful, |
41 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
42 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
43 | + GNU Affero General Public License for more details. |
44 | + |
45 | + You should have received a copy of the GNU Affero General Public |
46 | + License along with this program. If not, see |
47 | + <http://www.gnu.org/licenses/>. |
48 | + |
49 | + Log collection server implementation |
50 | +""" |
51 | |
52 | === renamed file 'surveilr/api/server.py' => 'surveilr/api/server/app.py' |
53 | --- surveilr/api/server.py 2011-12-18 14:46:48 +0000 |
54 | +++ surveilr/api/server/app.py 2012-01-04 23:13:32 +0000 |
55 | @@ -18,11 +18,12 @@ |
56 | License along with this program. If not, see |
57 | <http://www.gnu.org/licenses/>. |
58 | |
59 | - Log collection server implementation |
60 | + API server implementation |
61 | """ |
62 | |
63 | import eventlet |
64 | import eventlet.wsgi |
65 | +import functools |
66 | import json |
67 | import time |
68 | |
69 | @@ -34,12 +35,29 @@ |
70 | from webob import Response |
71 | from webob.dec import wsgify |
72 | from webob.exc import HTTPNotFound |
73 | +from webob.exc import HTTPForbidden |
74 | |
75 | from surveilr import config |
76 | from surveilr import messaging |
77 | from surveilr import models |
78 | from surveilr import utils |
79 | |
80 | +def is_privileged(req): |
81 | + if 'surveilr.user' in req.environ: |
82 | + return req.environ['surveilr.user'].credentials['admin'] |
83 | + # This feels pretty scary |
84 | + return True |
85 | + |
86 | + |
87 | +def privileged(f): |
88 | + @functools.wraps(f) |
89 | + def wrapped(self, req, *args): |
90 | + if is_privileged(req): |
91 | + return f(self, req, *args) |
92 | + else: |
93 | + return HTTPForbidden() |
94 | + return wrapped |
95 | + |
96 | |
97 | class NotificationController(object): |
98 | """Routes style controller for notifications""" |
99 | @@ -58,15 +76,30 @@ |
100 | class UserController(object): |
101 | """Routes style controller for actions related to users""" |
102 | |
103 | + @privileged |
104 | def create(self, req): |
105 | """Called for POST requests to /users |
106 | |
107 | Creates the user, returns a JSON object with the ID assigned |
108 | to the user""" |
109 | data = json.loads(req.body) |
110 | - user = models.User(**data) |
111 | + |
112 | + obj_data = {} |
113 | + |
114 | + obj_data['credentials'] = {} |
115 | + |
116 | + if 'admin' in data: |
117 | + obj_data['credentials']['admin'] = data['admin'] |
118 | + |
119 | + for key in ['messaging_driver', 'messaging_address']: |
120 | + if key in data: |
121 | + obj_data[key] = data[key] |
122 | + |
123 | + user = models.User(**obj_data) |
124 | user.save() |
125 | - response = {'id': user.key} |
126 | + response = {'id': user.key, |
127 | + 'key': user.api_key, |
128 | + 'admin': user.credentials.get('admin', False)} |
129 | return Response(json.dumps(response)) |
130 | |
131 | def show(self, req, id): |
132 | @@ -77,7 +110,8 @@ |
133 | user = models.User.get(id) |
134 | resp_dict = {'id': user.key, |
135 | 'messaging_driver': user.messaging_driver, |
136 | - 'messaging_address': user.messaging_address} |
137 | + 'messaging_address': user.messaging_address, |
138 | + 'admin': user.credentials.get('admin', False)} |
139 | return Response(json.dumps(resp_dict)) |
140 | except riakalchemy.NoSuchObjectError: |
141 | return HTTPNotFound() |
142 | @@ -183,7 +217,10 @@ |
143 | path_prefix='/users/{user_id}') |
144 | |
145 | def __init__(self, global_config): |
146 | - pass |
147 | + riak_host = config.get_str('riak', 'host') |
148 | + riak_port = config.get_int('riak', 'port') |
149 | + |
150 | + riakalchemy.connect(host=riak_host, port=riak_port) |
151 | |
152 | @wsgify |
153 | def __call__(self, req): |
154 | @@ -216,11 +253,6 @@ |
155 | |
156 | |
157 | def main(): |
158 | - riak_host = config.get_str('riak', 'host') |
159 | - riak_port = config.get_int('riak', 'port') |
160 | - |
161 | - riakalchemy.connect(host=riak_host, port=riak_port) |
162 | - |
163 | server_factory({}, '', 9877)(SurveilrApplication({})) |
164 | |
165 | |
166 | |
167 | === added file 'surveilr/api/server/auth.py' |
168 | --- surveilr/api/server/auth.py 1970-01-01 00:00:00 +0000 |
169 | +++ surveilr/api/server/auth.py 2012-01-04 23:13:32 +0000 |
170 | @@ -0,0 +1,48 @@ |
171 | +""" |
172 | + Surveilr - Log aggregation, analysis and visualisation |
173 | + |
174 | + Copyright (C) 2011 Linux2Go |
175 | + |
176 | + This program is free software: you can redistribute it and/or |
177 | + modify it under the terms of the GNU Affero General Public License |
178 | + as published by the Free Software Foundation, either version 3 of |
179 | + the License, or (at your option) any later version. |
180 | + |
181 | + This program is distributed in the hope that it will be useful, |
182 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
183 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
184 | + GNU Affero General Public License for more details. |
185 | + |
186 | + You should have received a copy of the GNU Affero General Public |
187 | + License along with this program. If not, see |
188 | + <http://www.gnu.org/licenses/>. |
189 | + |
190 | + API server auth implementation |
191 | +""" |
192 | + |
193 | +from surveilr import models |
194 | + |
195 | +from riakalchemy import NoSuchObjectError |
196 | + |
197 | +class AlwaysRequireAuth(object): |
198 | + def __call__(self, environ, status, headers): |
199 | + return 'repoze.who.identity' not in environ |
200 | + |
201 | + |
202 | +class SurveilrAuthPlugin(object): |
203 | + def authenticate(self, environ, identity): |
204 | + try: |
205 | + login = identity['login'] |
206 | + password = identity['password'] |
207 | + except KeyError: |
208 | + return None |
209 | + |
210 | + try: |
211 | + user = models.User.get(key=login) |
212 | + except NoSuchObjectError: |
213 | + return None |
214 | + |
215 | + if user.api_key == password: |
216 | + environ['surveilr.user'] = user |
217 | + return login |
218 | + return None |
219 | |
220 | === added file 'surveilr/api/server/factory.py' |
221 | --- surveilr/api/server/factory.py 1970-01-01 00:00:00 +0000 |
222 | +++ surveilr/api/server/factory.py 2012-01-04 23:13:32 +0000 |
223 | @@ -0,0 +1,29 @@ |
224 | +""" |
225 | + Surveilr - Log aggregation, analysis and visualisation |
226 | + |
227 | + Copyright (C) 2011 Linux2Go |
228 | + |
229 | + This program is free software: you can redistribute it and/or |
230 | + modify it under the terms of the GNU Affero General Public License |
231 | + as published by the Free Software Foundation, either version 3 of |
232 | + the License, or (at your option) any later version. |
233 | + |
234 | + This program is distributed in the hope that it will be useful, |
235 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
236 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
237 | + GNU Affero General Public License for more details. |
238 | + |
239 | + You should have received a copy of the GNU Affero General Public |
240 | + License along with this program. If not, see |
241 | + <http://www.gnu.org/licenses/>. |
242 | + |
243 | + API server factory |
244 | +""" |
245 | +import eventlet |
246 | + |
247 | +def server_factory(global_conf, host, port): |
248 | + port = int(port) |
249 | + def serve(application): |
250 | + socket = eventlet.listen((host, port)) |
251 | + eventlet.wsgi.server(socket, application) |
252 | + return serve |
253 | |
254 | === modified file 'surveilr/defaults.cfg' |
255 | --- surveilr/defaults.cfg 2011-12-20 10:59:35 +0000 |
256 | +++ surveilr/defaults.cfg 2012-01-04 23:13:32 +0000 |
257 | @@ -12,13 +12,22 @@ |
258 | port = 8098 |
259 | |
260 | [server:main] |
261 | -paste.server_factory = surveilr.api.server:server_factory |
262 | +paste.server_factory = surveilr.api.server.factory:server_factory |
263 | host = 0.0.0.0 |
264 | port = 8977 |
265 | |
266 | [composite:main] |
267 | use = egg:Paste#urlmap |
268 | -/surveilr = core |
269 | +/surveilr = secured |
270 | + |
271 | +[pipeline:secured] |
272 | +pipeline = who core |
273 | |
274 | [app:core] |
275 | -paste.app_factory = surveilr.api.server:SurveilrApplication |
276 | +paste.app_factory = surveilr.api.server.app:SurveilrApplication |
277 | + |
278 | +[filter:who] |
279 | +use = egg:repoze.who#config |
280 | +config_file = %(here)s/who.ini |
281 | +log_file = stdout |
282 | +log_level = debug |
283 | |
284 | === modified file 'surveilr/models.py' |
285 | --- surveilr/models.py 2011-12-09 16:17:30 +0000 |
286 | +++ surveilr/models.py 2012-01-04 23:13:32 +0000 |
287 | @@ -22,6 +22,7 @@ |
288 | from riakalchemy import RiakObject |
289 | from riakalchemy.types import Integer, String, Dict, RelatedObjects |
290 | |
291 | +from surveilr import utils |
292 | |
293 | class Service(RiakObject): |
294 | """A service that is referenced by many LogEntry's |
295 | @@ -43,9 +44,14 @@ |
296 | """A user of the service""" |
297 | bucket_name = 'users' |
298 | |
299 | + api_key = String() |
300 | + credentials = Dict() |
301 | messaging_driver = String() |
302 | messaging_address = String() |
303 | |
304 | + def pre_save(self): |
305 | + if not hasattr(self, 'api_key'): |
306 | + self.api_key = utils.generate_key() |
307 | |
308 | class LogEntry(RiakObject): |
309 | """A log entry holding one or more metrics |
310 | |
311 | === added directory 'surveilr/tests/api/server' |
312 | === added file 'surveilr/tests/api/server/__init__.py' |
313 | --- surveilr/tests/api/server/__init__.py 1970-01-01 00:00:00 +0000 |
314 | +++ surveilr/tests/api/server/__init__.py 2012-01-04 23:13:32 +0000 |
315 | @@ -0,0 +1,19 @@ |
316 | +""" |
317 | + Surveilr - Log aggregation, analysis and visualisation |
318 | + |
319 | + Copyright (C) 2011 Linux2Go |
320 | + |
321 | + This program is free software: you can redistribute it and/or |
322 | + modify it under the terms of the GNU Affero General Public License |
323 | + as published by the Free Software Foundation, either version 3 of |
324 | + the License, or (at your option) any later version. |
325 | + |
326 | + This program is distributed in the hope that it will be useful, |
327 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
328 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
329 | + GNU Affero General Public License for more details. |
330 | + |
331 | + You should have received a copy of the GNU Affero General Public |
332 | + License along with this program. If not, see |
333 | + <http://www.gnu.org/licenses/>. |
334 | +""" |
335 | |
336 | === renamed file 'surveilr/tests/api/test_server.py' => 'surveilr/tests/api/server/test_app.py' |
337 | --- surveilr/tests/api/test_server.py 2011-12-20 10:59:35 +0000 |
338 | +++ surveilr/tests/api/server/test_app.py 2012-01-04 23:13:32 +0000 |
339 | @@ -27,8 +27,8 @@ |
340 | from surveilr import models |
341 | from surveilr import tests |
342 | from surveilr import utils |
343 | -from surveilr.api import server |
344 | -from surveilr.api.server import SurveilrApplication |
345 | +from surveilr.api.server import app |
346 | +from surveilr.api.server.app import SurveilrApplication |
347 | |
348 | |
349 | class APIServerTests(tests.TestCase): |
350 | @@ -36,12 +36,32 @@ |
351 | super(APIServerTests, self).setUp() |
352 | self.application = SurveilrApplication({}) |
353 | |
354 | + def test_create_user_denied(self): |
355 | + """Creating a user is refused for non-privileged users""" |
356 | + req = Request.blank('/users', |
357 | + method='POST', |
358 | + POST=json.dumps({'messaging_driver': 'fake', |
359 | + 'messaging_address': 'foo'})) |
360 | + class FakeUser(object): |
361 | + credentials = {'admin': False} |
362 | + |
363 | + req.environ['surveilr.user'] = FakeUser() |
364 | + resp = self.application(req) |
365 | + self.assertEquals(resp.status_int, 403) |
366 | + |
367 | def test_create_retrieve_user(self): |
368 | - """Create, retrieve, delete, attempt to retrieve again""" |
369 | + self._test_create_retrieve_user() |
370 | + |
371 | + def test_create_retrieve_admin_user(self): |
372 | + self._test_create_retrieve_user(admin=True) |
373 | + |
374 | + def _test_create_retrieve_user(self, admin=False): |
375 | + """Create, retrieve, delete, attempt to retrieve again (user)""" |
376 | req = Request.blank('/users', |
377 | method='POST', |
378 | POST=json.dumps({'messaging_driver': 'fake', |
379 | - 'messaging_address': 'foo'})) |
380 | + 'messaging_address': 'foo', |
381 | + 'admin': True})) |
382 | resp = self.application(req) |
383 | self.assertEquals(resp.status_int, 200) |
384 | |
385 | @@ -54,6 +74,7 @@ |
386 | user = json.loads(resp.body) |
387 | self.assertEquals(user['messaging_driver'], 'fake') |
388 | self.assertEquals(user['messaging_address'], 'foo') |
389 | + self.assertEquals(user['admin'], True) |
390 | |
391 | req = Request.blank('/users/%s' % service_id, method='DELETE') |
392 | resp = self.application(req) |
393 | @@ -81,7 +102,7 @@ |
394 | self.assertEquals(resp.status_int, 200) |
395 | |
396 | def test_create_retrieve_service(self): |
397 | - """Create, retrieve, delete, attempt to retrieve again""" |
398 | + """Create, retrieve, delete, attempt to retrieve again (service)""" |
399 | req = Request.blank('/services', |
400 | method='POST', |
401 | POST=json.dumps({'name': 'this_or_the_other'})) |
402 | @@ -149,7 +170,7 @@ |
403 | 'timestamp': 13217362355575, |
404 | 'metrics': {'duration': 85000, |
405 | 'response_size': 12435}})) |
406 | - with mock.patch('surveilr.api.server.eventlet') as eventlet: |
407 | + with mock.patch('surveilr.api.server.app.eventlet') as eventlet: |
408 | resp = self.application(req) |
409 | |
410 | self.assertEquals(eventlet.spawn_n.call_args[0][0], |
411 | @@ -167,12 +188,12 @@ |
412 | resp = self.application(req) |
413 | self.assertEquals(resp.status_int, 404) |
414 | |
415 | - @mock.patch('surveilr.api.server.eventlet', spec=['listen', 'wsgi']) |
416 | - @mock.patch('surveilr.api.server.riakalchemy', spec=['connect']) |
417 | + @mock.patch('surveilr.api.server.app.eventlet', spec=['listen', 'wsgi']) |
418 | + @mock.patch('surveilr.api.server.app.riakalchemy', spec=['connect']) |
419 | def test_main(self, riakalchemy, eventlet): |
420 | socket_sentinel = mock.sentinel.return_value |
421 | eventlet.listen.return_value = socket_sentinel |
422 | - server.main() |
423 | + app.main() |
424 | |
425 | riakalchemy.connect.assert_called_with(host='127.0.0.1', port=8098) |
426 | eventlet.listen.assert_called_with(('', 9877)) |
427 | |
428 | === added file 'surveilr/tests/api/server/test_auth.py' |
429 | --- surveilr/tests/api/server/test_auth.py 1970-01-01 00:00:00 +0000 |
430 | +++ surveilr/tests/api/server/test_auth.py 2012-01-04 23:13:32 +0000 |
431 | @@ -0,0 +1,82 @@ |
432 | +""" |
433 | + Surveilr - Log aggregation, analysis and visualisation |
434 | + |
435 | + Copyright (C) 2011 Linux2Go |
436 | + |
437 | + This program is free software: you can redistribute it and/or |
438 | + modify it under the terms of the GNU Affero General Public License |
439 | + as published by the Free Software Foundation, either version 3 of |
440 | + the License, or (at your option) any later version. |
441 | + |
442 | + This program is distributed in the hope that it will be useful, |
443 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
444 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
445 | + GNU Affero General Public License for more details. |
446 | + |
447 | + You should have received a copy of the GNU Affero General Public |
448 | + License along with this program. If not, see |
449 | + <http://www.gnu.org/licenses/>. |
450 | + |
451 | + Tests for API server auth |
452 | +""" |
453 | + |
454 | +from surveilr import models |
455 | +from surveilr import tests |
456 | +from surveilr.api.server import auth |
457 | + |
458 | +class TestAPIServerAuth(tests.TestCase): |
459 | + def test_AlwaysRequireAuth_unauthenticated(self): |
460 | + decider = auth.AlwaysRequireAuth() |
461 | + self.assertEquals(decider({}, '200 OK', {}), True) |
462 | + |
463 | + def test_AlwaysRequireAuth_already_authenticated(self): |
464 | + decider = auth.AlwaysRequireAuth() |
465 | + self.assertEquals(decider({'repoze.who.identity': 'someone'}, '200 OK', {}), False) |
466 | + |
467 | +class TestSurveilrAuthPlugin(tests.TestCase): |
468 | + def test_authenticate_invcomplete_identity(self): |
469 | + env = {} |
470 | + identity = {'password': 'apikey'} |
471 | + |
472 | + self.assertIsNone(auth.SurveilrAuthPlugin().authenticate(env, |
473 | + identity)) |
474 | + self.assertEquals(env, {}) |
475 | + |
476 | + |
477 | + def test_authenticate_invalid_identity(self): |
478 | + env = {} |
479 | + identity = {'login': 'testuser', |
480 | + 'password': 'apikey'} |
481 | + |
482 | + self.assertIsNone(auth.SurveilrAuthPlugin().authenticate(env, |
483 | + identity)) |
484 | + self.assertEquals(env, {}) |
485 | + |
486 | + |
487 | + def test_authenticate_valid_credentials(self): |
488 | + env = {} |
489 | + |
490 | + user = models.User() |
491 | + user.save() |
492 | + identity = {'login': user.key, |
493 | + 'password': user.api_key} |
494 | + |
495 | + self.assertEquals(auth.SurveilrAuthPlugin().authenticate(env, |
496 | + identity), |
497 | + user.key) |
498 | + self.assertEquals(env['surveilr.user'].key, user.key) |
499 | + |
500 | + def test_authenticate_wrong_key(self): |
501 | + env = {} |
502 | + |
503 | + user = models.User() |
504 | + user.save() |
505 | + self.addCleanup(user.delete) |
506 | + |
507 | + identity = {'login': user.key, |
508 | + 'password': 'not the right key'} |
509 | + |
510 | + self.assertIsNone(auth.SurveilrAuthPlugin().authenticate(env, |
511 | + identity), |
512 | + user.key) |
513 | + self.assertEquals(env, {}) |
514 | |
515 | === added file 'surveilr/tests/api/server/test_factory.py' |
516 | --- surveilr/tests/api/server/test_factory.py 1970-01-01 00:00:00 +0000 |
517 | +++ surveilr/tests/api/server/test_factory.py 2012-01-04 23:13:32 +0000 |
518 | @@ -0,0 +1,43 @@ |
519 | +""" |
520 | + Surveilr - Log aggregation, analysis and visualisation |
521 | + |
522 | + Copyright (C) 2011 Linux2Go |
523 | + |
524 | + This program is free software: you can redistribute it and/or |
525 | + modify it under the terms of the GNU Affero General Public License |
526 | + as published by the Free Software Foundation, either version 3 of |
527 | + the License, or (at your option) any later version. |
528 | + |
529 | + This program is distributed in the hope that it will be useful, |
530 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
531 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
532 | + GNU Affero General Public License for more details. |
533 | + |
534 | + You should have received a copy of the GNU Affero General Public |
535 | + License along with this program. If not, see |
536 | + <http://www.gnu.org/licenses/>. |
537 | + |
538 | + Tests for API server factory |
539 | +""" |
540 | + |
541 | +import mock |
542 | + |
543 | +from surveilr import tests |
544 | +from surveilr.api.server import factory |
545 | + |
546 | +class APIServerFactoryTests(tests.TestCase): |
547 | + def test_server_factory_returns_callable(self): |
548 | + self.assertTrue(callable(factory.server_factory({}, 'somehost', 1234))) |
549 | + |
550 | + @mock.patch('surveilr.api.server.factory.eventlet') |
551 | + def test_server_factory_returns_server(self, eventlet): |
552 | + testhost = 'somehost' |
553 | + testport = '1234' |
554 | + serve = factory.server_factory({}, testhost, testport) |
555 | + app = mock.sentinel.app |
556 | + |
557 | + serve(app) |
558 | + |
559 | + eventlet.listen.assert_called_with((testhost, int(testport))) |
560 | + eventlet.wsgi.server.assert_called_with(eventlet.listen.return_value, |
561 | + app) |
562 | |
563 | === modified file 'surveilr/tests/test_utils.py' |
564 | --- surveilr/tests/test_utils.py 2011-12-20 09:49:02 +0000 |
565 | +++ surveilr/tests/test_utils.py 2012-01-04 23:13:32 +0000 |
566 | @@ -23,6 +23,7 @@ |
567 | import json |
568 | import time |
569 | import mock |
570 | +import string |
571 | |
572 | from surveilr import models |
573 | from surveilr import tests |
574 | @@ -94,3 +95,8 @@ |
575 | self.assertEquals(utils.truncate(133, 20), 120) |
576 | self.assertEquals(utils.truncate(133, 100), 100) |
577 | self.assertEquals(utils.truncate(133, 200), 0) |
578 | + |
579 | + def test_generate_key(self): |
580 | + key = utils.generate_key() |
581 | + self.assertEquals(len(key), 32) |
582 | + self.assertEquals(len(key.strip(string.letters + string.digits)), 0) |
583 | |
584 | === modified file 'surveilr/utils.py' |
585 | --- surveilr/utils.py 2011-12-11 22:11:47 +0000 |
586 | +++ surveilr/utils.py 2012-01-04 23:13:32 +0000 |
587 | @@ -22,6 +22,8 @@ |
588 | |
589 | import httplib2 |
590 | import json |
591 | +import random |
592 | +import string |
593 | |
594 | |
595 | def truncate(number, rounding_factor): |
596 | @@ -39,6 +41,10 @@ |
597 | return (int(number) / int(rounding_factor)) * int(rounding_factor) |
598 | |
599 | |
600 | +def generate_key(): |
601 | + return ''.join([random.choice(string.letters + string.digits) |
602 | + for x in range(32)]) |
603 | + |
604 | def enhance_data_point(data_point): |
605 | http = httplib2.Http(timeout=10) |
606 | |
607 | |
608 | === added file 'surveilr/who.ini' |
609 | --- surveilr/who.ini 1970-01-01 00:00:00 +0000 |
610 | +++ surveilr/who.ini 2012-01-04 23:13:32 +0000 |
611 | @@ -0,0 +1,23 @@ |
612 | +[plugin:basicauth] |
613 | +use = repoze.who.plugins.basicauth:make_plugin |
614 | +realm = 'Surveilr' |
615 | + |
616 | +[plugin:surveilrauth] |
617 | +use = surveilr.api.server.auth:SurveilrAuthPlugin |
618 | + |
619 | +[general] |
620 | +request_classifier = repoze.who.classifiers:default_request_classifier |
621 | +challenge_decider = surveilr.api.server.auth:AlwaysRequireAuth |
622 | +remote_user_key = REMOTE_USER |
623 | + |
624 | +[identifiers] |
625 | +plugins = |
626 | + basicauth |
627 | + |
628 | +[authenticators] |
629 | +plugins = |
630 | + surveilrauth |
631 | + |
632 | +[challengers] |
633 | +plugins = |
634 | + basicauth |
635 | |
636 | === modified file 'tools/pip-requirements.txt' |
637 | --- tools/pip-requirements.txt 2011-12-20 10:59:35 +0000 |
638 | +++ tools/pip-requirements.txt 2012-01-04 23:13:32 +0000 |
639 | @@ -13,3 +13,4 @@ |
640 | paste |
641 | PasteScript |
642 | -e hg+https://code.google.com/p/soren-bulksms/#egg=BulkSMS |
643 | +repoze.who |