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
1=== modified file 'license_protected_downloads/tests/test_views.py'
2--- license_protected_downloads/tests/test_views.py 2013-01-30 10:46:11 +0000
3+++ license_protected_downloads/tests/test_views.py 2013-02-21 12:12:22 +0000
4@@ -359,7 +359,7 @@
5 r'lp:linaro-license-protection</a> r' +
6 str(bzr_version.get_my_bzr_revno())))
7
8- def test_exception_ip_remote_addr(self):
9+ def test_exception_internal_host_for_lic(self):
10 internal_host = INTERNAL_HOSTS[0]
11 target_file = 'build-info/origen-blob.txt'
12 url = urlparse.urljoin("http://testserver/", target_file)
13@@ -371,6 +371,30 @@
14 file_path = os.path.join(TESTSERVER_ROOT, target_file)
15 self.assertEqual(response['X-Sendfile'], file_path)
16
17+ def test_exception_internal_host_for_openid(self):
18+ internal_host = INTERNAL_HOSTS[0]
19+ target_file = 'build-info/openid.txt'
20+ url = urlparse.urljoin("http://testserver/", target_file)
21+ response = self.client.get(url, follow=True,
22+ REMOTE_ADDR=internal_host)
23+
24+ # If we have access to the file, we will get an X-Sendfile response
25+ self.assertEqual(response.status_code, 200)
26+ file_path = os.path.join(TESTSERVER_ROOT, target_file)
27+ self.assertEqual(response['X-Sendfile'], file_path)
28+
29+ def test_exception_internal_host_for_lic_and_openid(self):
30+ internal_host = INTERNAL_HOSTS[0]
31+ target_file = 'build-info/origen-blob-openid.txt'
32+ url = urlparse.urljoin("http://testserver/", target_file)
33+ response = self.client.get(url, follow=True,
34+ REMOTE_ADDR=internal_host)
35+
36+ # If we have access to the file, we will get an X-Sendfile response
37+ self.assertEqual(response.status_code, 200)
38+ file_path = os.path.join(TESTSERVER_ROOT, target_file)
39+ self.assertEqual(response['X-Sendfile'], file_path)
40+
41 def test_no_exception_ip(self):
42 internal_host = '10.1.2.3'
43 target_file = 'build-info/origen-blob.txt'
44
45=== added file 'license_protected_downloads/tests/testserver_root/build-info/origen-blob-openid.txt'
46--- license_protected_downloads/tests/testserver_root/build-info/origen-blob-openid.txt 1970-01-01 00:00:00 +0000
47+++ license_protected_downloads/tests/testserver_root/build-info/origen-blob-openid.txt 2013-02-21 12:12:22 +0000
48@@ -0,0 +1,1 @@
49+This is protected with click-through Samsung license and OpenID group.
50\ No newline at end of file
51
52=== modified file 'license_protected_downloads/views.py'
53--- license_protected_downloads/views.py 2012-11-08 09:42:36 +0000
54+++ license_protected_downloads/views.py 2013-02-21 12:12:22 +0000
55@@ -362,6 +362,23 @@
56 return found
57
58
59+def send_file(path):
60+ "Return HttpResponse which will send path to user's browser."
61+ file_name = os.path.basename(path)
62+ mimetypes.init()
63+ mime = mimetypes.guess_type(path)[0]
64+ if mime is None:
65+ mime = "application/force-download"
66+ response = HttpResponse(mimetype=mime)
67+ response['Content-Disposition'] = ('attachment; filename=%s' %
68+ smart_str(file_name))
69+ response['X-Sendfile'] = smart_str(path)
70+ #response['Content-Length'] = os.path.getsize(path)
71+ # TODO: Is it possible to add a redirect to response so we can take
72+ # the user back to the original directory this file is in?
73+ return response
74+
75+
76 def file_server(request, path):
77 """Serve up a file / directory listing or license page as required"""
78 url = path
79@@ -372,6 +389,10 @@
80 type = result[0]
81 path = result[1]
82
83+ if get_client_ip(request) in config.INTERNAL_HOSTS:
84+ # For internal trusted hosts, short-circuit any further checks
85+ return send_file(path)
86+
87 if BuildInfo.build_info_exists(path):
88 try:
89 build_info = BuildInfo(path)
90@@ -415,47 +436,37 @@
91 RenderTextFiles.find_and_render(path)
92 })
93
94- file_name = os.path.basename(path)
95-
96 # If the file listing doesn't contain the file requested for download,
97 # return a 404. This prevents the download of BUILD-INFO.txt and other
98 # hidden files.
99 if not file_listed(path, url):
100 raise Http404
101
102- response = None
103 if get_client_ip(request) in config.INTERNAL_HOSTS or\
104 is_whitelisted(os.path.join('/', url)):
105 digests = 'OPEN'
106 else:
107 digests = is_protected(path)
108+
109+ response = None
110 if not digests:
111 # File has no license text but is protected
112 response = HttpResponseForbidden(
113 "You do not have permission to access this file.")
114-
115- # Return a file...
116+ elif digests == "OPEN":
117+ response = None
118 else:
119- if digests == "OPEN":
120- response = None
121- else:
122- for digest in digests:
123- if not license_accepted(request, digest):
124- response = redirect(
125- '/license?lic=' + digest + "&url=" + url)
126-
127- if not response:
128- mimetypes.init()
129- mime = mimetypes.guess_type(path)[0]
130- if mime is None:
131- mime = "application/force-download"
132- response = HttpResponse(mimetype=mime)
133- response['Content-Disposition'] = ('attachment; filename=%s' %
134- smart_str(file_name))
135- response['X-Sendfile'] = smart_str(path)
136- #response['Content-Length'] = os.path.getsize(path)
137- # TODO: Is it possible to add a redirect to response so we can take
138- # the user back to the original directory this file is in?
139+ for digest in digests:
140+ if not license_accepted(request, digest):
141+ # Make sure that user accepted each license one by one
142+ response = redirect(
143+ '/license?lic=' + digest + "&url=" + url)
144+ break
145+
146+ # If we didn't have any other response, it's ok to send file now
147+ if not response:
148+ response = send_file(path)
149+
150 return response
151
152

Subscribers

People subscribed via source and target branches