Merge lp:~pfalcon/linaro-license-protection/openid-exception-for-internal-hosts into lp:~linaro-automation/linaro-license-protection/trunk

Proposed by Paul Sokolovsky
Status: Merged
Approved by: Данило Шеган
Approved revision: 168
Merged at revision: 168
Proposed branch: lp:~pfalcon/linaro-license-protection/openid-exception-for-internal-hosts
Merge into: lp:~linaro-automation/linaro-license-protection/trunk
Diff against target: 150 lines (+62/-26)
3 files modified
license_protected_downloads/tests/test_views.py (+25/-1)
license_protected_downloads/tests/testserver_root/build-info/origen-blob-openid.txt (+1/-0)
license_protected_downloads/views.py (+36/-25)
To merge this branch: bzr merge lp:~pfalcon/linaro-license-protection/openid-exception-for-internal-hosts
Reviewer Review Type Date Requested Status
Данило Шеган (community) Approve
Linaro Infrastructure Pending
Review via email: mp+149807@code.launchpad.net

Description of the change

This does some simplification refactoring on logic in file serving method and then introduces short-circuit action early in the processing for internal trusted hosts, to make sure this short-circuit applies for any restriction checks.

Location of this short-circuit check is something which really worth a review here. file_server() method does quote a lot of checks actually, and again, it should be early enough to not hit the case we fix here (when internal hosts are locked out from access to file). So, I just started with putting it pretty early (after file path validation) and then tried to reason if it should be moved later. There's for example check for presence of BUILD-INFO.txt and bail out if it's not there. Well, I don't see any benefits of putting INTERNAL_HOSTS check after it - then only outcome we can expect from this is that if someone put botched BUILD-INFO.txt in a build, then that build won't show LAVA results in android-build, hardly a useful feature. Later in the code is handling of dir listing generation - again, this should not be needed for the kind of API access we need from INTERNAL_HOSTS.

So, summing up, IMHO location of INTERNAL_HOSTS is pretty good, but I'm open to suggestions from reviewers.

To post a comment you must log in.
167. By Paul Sokolovsky

Add test data for test_exception_internal_host_for_lic_and_openid().

168. By Paul Sokolovsky

Handle API access check for INTERNAL_HOSTS early in file_server().

Revision history for this message
Данило Шеган (danilo) wrote :

I am generally fine with your changes. I dislike shortening names like "lic" instead of "license". I would also prefer if you created the data for the tests inside tests themselves, but considering ones you are modifying don't have that, I won't make a fuss about it.

With minor comments above which are not binding, approved.

review: Approve
Revision history for this message
Paul Sokolovsky (pfalcon) wrote :

I also prefer full names, but test_exception_internal_host_for_licence_and_openid is really too long a function name ;-)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'license_protected_downloads/tests/test_views.py'
--- license_protected_downloads/tests/test_views.py 2013-01-30 10:46:11 +0000
+++ license_protected_downloads/tests/test_views.py 2013-02-21 12:12:22 +0000
@@ -359,7 +359,7 @@
359 r'lp:linaro-license-protection</a> r' +359 r'lp:linaro-license-protection</a> r' +
360 str(bzr_version.get_my_bzr_revno())))360 str(bzr_version.get_my_bzr_revno())))
361361
362 def test_exception_ip_remote_addr(self):362 def test_exception_internal_host_for_lic(self):
363 internal_host = INTERNAL_HOSTS[0]363 internal_host = INTERNAL_HOSTS[0]
364 target_file = 'build-info/origen-blob.txt'364 target_file = 'build-info/origen-blob.txt'
365 url = urlparse.urljoin("http://testserver/", target_file)365 url = urlparse.urljoin("http://testserver/", target_file)
@@ -371,6 +371,30 @@
371 file_path = os.path.join(TESTSERVER_ROOT, target_file)371 file_path = os.path.join(TESTSERVER_ROOT, target_file)
372 self.assertEqual(response['X-Sendfile'], file_path)372 self.assertEqual(response['X-Sendfile'], file_path)
373373
374 def test_exception_internal_host_for_openid(self):
375 internal_host = INTERNAL_HOSTS[0]
376 target_file = 'build-info/openid.txt'
377 url = urlparse.urljoin("http://testserver/", target_file)
378 response = self.client.get(url, follow=True,
379 REMOTE_ADDR=internal_host)
380
381 # If we have access to the file, we will get an X-Sendfile response
382 self.assertEqual(response.status_code, 200)
383 file_path = os.path.join(TESTSERVER_ROOT, target_file)
384 self.assertEqual(response['X-Sendfile'], file_path)
385
386 def test_exception_internal_host_for_lic_and_openid(self):
387 internal_host = INTERNAL_HOSTS[0]
388 target_file = 'build-info/origen-blob-openid.txt'
389 url = urlparse.urljoin("http://testserver/", target_file)
390 response = self.client.get(url, follow=True,
391 REMOTE_ADDR=internal_host)
392
393 # If we have access to the file, we will get an X-Sendfile response
394 self.assertEqual(response.status_code, 200)
395 file_path = os.path.join(TESTSERVER_ROOT, target_file)
396 self.assertEqual(response['X-Sendfile'], file_path)
397
374 def test_no_exception_ip(self):398 def test_no_exception_ip(self):
375 internal_host = '10.1.2.3'399 internal_host = '10.1.2.3'
376 target_file = 'build-info/origen-blob.txt'400 target_file = 'build-info/origen-blob.txt'
377401
=== added file 'license_protected_downloads/tests/testserver_root/build-info/origen-blob-openid.txt'
--- license_protected_downloads/tests/testserver_root/build-info/origen-blob-openid.txt 1970-01-01 00:00:00 +0000
+++ license_protected_downloads/tests/testserver_root/build-info/origen-blob-openid.txt 2013-02-21 12:12:22 +0000
@@ -0,0 +1,1 @@
1This is protected with click-through Samsung license and OpenID group.
0\ No newline at end of file2\ No newline at end of file
13
=== modified file 'license_protected_downloads/views.py'
--- license_protected_downloads/views.py 2012-11-08 09:42:36 +0000
+++ license_protected_downloads/views.py 2013-02-21 12:12:22 +0000
@@ -362,6 +362,23 @@
362 return found362 return found
363363
364364
365def send_file(path):
366 "Return HttpResponse which will send path to user's browser."
367 file_name = os.path.basename(path)
368 mimetypes.init()
369 mime = mimetypes.guess_type(path)[0]
370 if mime is None:
371 mime = "application/force-download"
372 response = HttpResponse(mimetype=mime)
373 response['Content-Disposition'] = ('attachment; filename=%s' %
374 smart_str(file_name))
375 response['X-Sendfile'] = smart_str(path)
376 #response['Content-Length'] = os.path.getsize(path)
377 # TODO: Is it possible to add a redirect to response so we can take
378 # the user back to the original directory this file is in?
379 return response
380
381
365def file_server(request, path):382def file_server(request, path):
366 """Serve up a file / directory listing or license page as required"""383 """Serve up a file / directory listing or license page as required"""
367 url = path384 url = path
@@ -372,6 +389,10 @@
372 type = result[0]389 type = result[0]
373 path = result[1]390 path = result[1]
374391
392 if get_client_ip(request) in config.INTERNAL_HOSTS:
393 # For internal trusted hosts, short-circuit any further checks
394 return send_file(path)
395
375 if BuildInfo.build_info_exists(path):396 if BuildInfo.build_info_exists(path):
376 try:397 try:
377 build_info = BuildInfo(path)398 build_info = BuildInfo(path)
@@ -415,47 +436,37 @@
415 RenderTextFiles.find_and_render(path)436 RenderTextFiles.find_and_render(path)
416 })437 })
417438
418 file_name = os.path.basename(path)
419
420 # If the file listing doesn't contain the file requested for download,439 # If the file listing doesn't contain the file requested for download,
421 # return a 404. This prevents the download of BUILD-INFO.txt and other440 # return a 404. This prevents the download of BUILD-INFO.txt and other
422 # hidden files.441 # hidden files.
423 if not file_listed(path, url):442 if not file_listed(path, url):
424 raise Http404443 raise Http404
425444
426 response = None
427 if get_client_ip(request) in config.INTERNAL_HOSTS or\445 if get_client_ip(request) in config.INTERNAL_HOSTS or\
428 is_whitelisted(os.path.join('/', url)):446 is_whitelisted(os.path.join('/', url)):
429 digests = 'OPEN'447 digests = 'OPEN'
430 else:448 else:
431 digests = is_protected(path)449 digests = is_protected(path)
450
451 response = None
432 if not digests:452 if not digests:
433 # File has no license text but is protected453 # File has no license text but is protected
434 response = HttpResponseForbidden(454 response = HttpResponseForbidden(
435 "You do not have permission to access this file.")455 "You do not have permission to access this file.")
436456 elif digests == "OPEN":
437 # Return a file...457 response = None
438 else:458 else:
439 if digests == "OPEN":459 for digest in digests:
440 response = None460 if not license_accepted(request, digest):
441 else:461 # Make sure that user accepted each license one by one
442 for digest in digests:462 response = redirect(
443 if not license_accepted(request, digest):463 '/license?lic=' + digest + "&url=" + url)
444 response = redirect(464 break
445 '/license?lic=' + digest + "&url=" + url)465
446466 # If we didn't have any other response, it's ok to send file now
447 if not response:467 if not response:
448 mimetypes.init()468 response = send_file(path)
449 mime = mimetypes.guess_type(path)[0]469
450 if mime is None:
451 mime = "application/force-download"
452 response = HttpResponse(mimetype=mime)
453 response['Content-Disposition'] = ('attachment; filename=%s' %
454 smart_str(file_name))
455 response['X-Sendfile'] = smart_str(path)
456 #response['Content-Length'] = os.path.getsize(path)
457 # TODO: Is it possible to add a redirect to response so we can take
458 # the user back to the original directory this file is in?
459 return response470 return response
460471
461472

Subscribers

People subscribed via source and target branches