##// END OF EJS Templates
tests: added some more artifact access tests.
marcink -
r4008:786403b1 default
parent child Browse files
Show More
@@ -1,126 +1,261 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 import os
20 import os
21 import pytest
21 import pytest
22
22
23 from rhodecode.lib.ext_json import json
23 from rhodecode.lib.ext_json import json
24 from rhodecode.model.db import Session, FileStore
24 from rhodecode.model.auth_token import AuthTokenModel
25 from rhodecode.model.db import Session, FileStore, Repository, User
25 from rhodecode.tests import TestController
26 from rhodecode.tests import TestController
26 from rhodecode.apps.file_store import utils, config_keys
27 from rhodecode.apps.file_store import utils, config_keys
27
28
28
29
29 def route_path(name, params=None, **kwargs):
30 def route_path(name, params=None, **kwargs):
30 import urllib
31 import urllib
31
32
32 base_url = {
33 base_url = {
33 'upload_file': '/_file_store/upload',
34 'upload_file': '/_file_store/upload',
34 'download_file': '/_file_store/download/{fid}',
35 'download_file': '/_file_store/download/{fid}',
36 'download_file_by_token': '/_file_store/token-download/{_auth_token}/{fid}'
35
37
36 }[name].format(**kwargs)
38 }[name].format(**kwargs)
37
39
38 if params:
40 if params:
39 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
41 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
40 return base_url
42 return base_url
41
43
42
44
43 class TestFileStoreViews(TestController):
45 class TestFileStoreViews(TestController):
44
46
45 @pytest.mark.parametrize("fid, content, exists", [
47 @pytest.mark.parametrize("fid, content, exists", [
46 ('abcde-0.jpg', "xxxxx", True),
48 ('abcde-0.jpg', "xxxxx", True),
47 ('abcde-0.exe', "1234567", True),
49 ('abcde-0.exe', "1234567", True),
48 ('abcde-0.jpg', "xxxxx", False),
50 ('abcde-0.jpg', "xxxxx", False),
49 ])
51 ])
50 def test_get_files_from_store(self, fid, content, exists, tmpdir, user_util):
52 def test_get_files_from_store(self, fid, content, exists, tmpdir, user_util):
51 user = self.log_user()
53 user = self.log_user()
52 user_id = user['user_id']
54 user_id = user['user_id']
53 repo_id = user_util.create_repo().repo_id
55 repo_id = user_util.create_repo().repo_id
54 store_path = self.app._pyramid_settings[config_keys.store_path]
56 store_path = self.app._pyramid_settings[config_keys.store_path]
55 store_uid = fid
57 store_uid = fid
56
58
57 if exists:
59 if exists:
58 status = 200
60 status = 200
59 store = utils.get_file_storage({config_keys.store_path: store_path})
61 store = utils.get_file_storage({config_keys.store_path: store_path})
60 filesystem_file = os.path.join(str(tmpdir), fid)
62 filesystem_file = os.path.join(str(tmpdir), fid)
61 with open(filesystem_file, 'wb') as f:
63 with open(filesystem_file, 'wb') as f:
62 f.write(content)
64 f.write(content)
63
65
64 with open(filesystem_file, 'rb') as f:
66 with open(filesystem_file, 'rb') as f:
65 store_uid, metadata = store.save_file(f, fid, extra_metadata={'filename': fid})
67 store_uid, metadata = store.save_file(f, fid, extra_metadata={'filename': fid})
66
68
67 entry = FileStore.create(
69 entry = FileStore.create(
68 file_uid=store_uid, filename=metadata["filename"],
70 file_uid=store_uid, filename=metadata["filename"],
69 file_hash=metadata["sha256"], file_size=metadata["size"],
71 file_hash=metadata["sha256"], file_size=metadata["size"],
70 file_display_name='file_display_name',
72 file_display_name='file_display_name',
71 file_description='repo artifact `{}`'.format(metadata["filename"]),
73 file_description='repo artifact `{}`'.format(metadata["filename"]),
72 check_acl=True, user_id=user_id,
74 check_acl=True, user_id=user_id,
73 scope_repo_id=repo_id
75 scope_repo_id=repo_id
74 )
76 )
75 Session().add(entry)
77 Session().add(entry)
76 Session().commit()
78 Session().commit()
77
79
78 else:
80 else:
79 status = 404
81 status = 404
80
82
81 response = self.app.get(route_path('download_file', fid=store_uid), status=status)
83 response = self.app.get(route_path('download_file', fid=store_uid), status=status)
82
84
83 if exists:
85 if exists:
84 assert response.text == content
86 assert response.text == content
85 file_store_path = os.path.dirname(store.resolve_name(store_uid, store_path)[1])
87 file_store_path = os.path.dirname(store.resolve_name(store_uid, store_path)[1])
86 metadata_file = os.path.join(file_store_path, store_uid + '.meta')
88 metadata_file = os.path.join(file_store_path, store_uid + '.meta')
87 assert os.path.exists(metadata_file)
89 assert os.path.exists(metadata_file)
88 with open(metadata_file, 'rb') as f:
90 with open(metadata_file, 'rb') as f:
89 json_data = json.loads(f.read())
91 json_data = json.loads(f.read())
90
92
91 assert json_data
93 assert json_data
92 assert 'size' in json_data
94 assert 'size' in json_data
93
95
94 def test_upload_files_without_content_to_store(self):
96 def test_upload_files_without_content_to_store(self):
95 self.log_user()
97 self.log_user()
96 response = self.app.post(
98 response = self.app.post(
97 route_path('upload_file'),
99 route_path('upload_file'),
98 params={'csrf_token': self.csrf_token},
100 params={'csrf_token': self.csrf_token},
99 status=200)
101 status=200)
100
102
101 assert response.json == {
103 assert response.json == {
102 u'error': u'store_file data field is missing',
104 u'error': u'store_file data field is missing',
103 u'access_path': None,
105 u'access_path': None,
104 u'store_fid': None}
106 u'store_fid': None}
105
107
106 def test_upload_files_bogus_content_to_store(self):
108 def test_upload_files_bogus_content_to_store(self):
107 self.log_user()
109 self.log_user()
108 response = self.app.post(
110 response = self.app.post(
109 route_path('upload_file'),
111 route_path('upload_file'),
110 params={'csrf_token': self.csrf_token, 'store_file': 'bogus'},
112 params={'csrf_token': self.csrf_token, 'store_file': 'bogus'},
111 status=200)
113 status=200)
112
114
113 assert response.json == {
115 assert response.json == {
114 u'error': u'filename cannot be read from the data field',
116 u'error': u'filename cannot be read from the data field',
115 u'access_path': None,
117 u'access_path': None,
116 u'store_fid': None}
118 u'store_fid': None}
117
119
118 def test_upload_content_to_store(self):
120 def test_upload_content_to_store(self):
119 self.log_user()
121 self.log_user()
120 response = self.app.post(
122 response = self.app.post(
121 route_path('upload_file'),
123 route_path('upload_file'),
122 upload_files=[('store_file', 'myfile.txt', 'SOME CONTENT')],
124 upload_files=[('store_file', 'myfile.txt', 'SOME CONTENT')],
123 params={'csrf_token': self.csrf_token},
125 params={'csrf_token': self.csrf_token},
124 status=200)
126 status=200)
125
127
126 assert response.json['store_fid']
128 assert response.json['store_fid']
129
130 @pytest.fixture()
131 def create_artifact_factory(self, tmpdir):
132 def factory(user_id, content):
133 store_path = self.app._pyramid_settings[config_keys.store_path]
134 store = utils.get_file_storage({config_keys.store_path: store_path})
135 fid = 'example.txt'
136
137 filesystem_file = os.path.join(str(tmpdir), fid)
138 with open(filesystem_file, 'wb') as f:
139 f.write(content)
140
141 with open(filesystem_file, 'rb') as f:
142 store_uid, metadata = store.save_file(f, fid, extra_metadata={'filename': fid})
143
144 entry = FileStore.create(
145 file_uid=store_uid, filename=metadata["filename"],
146 file_hash=metadata["sha256"], file_size=metadata["size"],
147 file_display_name='file_display_name',
148 file_description='repo artifact `{}`'.format(metadata["filename"]),
149 check_acl=True, user_id=user_id,
150 )
151 Session().add(entry)
152 Session().commit()
153 return entry
154 return factory
155
156 def test_download_file_non_scoped(self, user_util, create_artifact_factory):
157 user = self.log_user()
158 user_id = user['user_id']
159 content = 'HELLO MY NAME IS ARTIFACT !'
160
161 artifact = create_artifact_factory(user_id, content)
162 file_uid = artifact.file_uid
163 response = self.app.get(route_path('download_file', fid=file_uid), status=200)
164 assert response.text == content
165
166 # log-in to new user and test download again
167 user = user_util.create_user(password='qweqwe')
168 self.log_user(user.username, 'qweqwe')
169 response = self.app.get(route_path('download_file', fid=file_uid), status=200)
170 assert response.text == content
171
172 def test_download_file_scoped_to_repo(self, user_util, create_artifact_factory):
173 user = self.log_user()
174 user_id = user['user_id']
175 content = 'HELLO MY NAME IS ARTIFACT !'
176
177 artifact = create_artifact_factory(user_id, content)
178 # bind to repo
179 repo = user_util.create_repo()
180 repo_id = repo.repo_id
181 artifact.scope_repo_id = repo_id
182 Session().add(artifact)
183 Session().commit()
184
185 file_uid = artifact.file_uid
186 response = self.app.get(route_path('download_file', fid=file_uid), status=200)
187 assert response.text == content
188
189 # log-in to new user and test download again
190 user = user_util.create_user(password='qweqwe')
191 self.log_user(user.username, 'qweqwe')
192 response = self.app.get(route_path('download_file', fid=file_uid), status=200)
193 assert response.text == content
194
195 # forbid user the rights to repo
196 repo = Repository.get(repo_id)
197 user_util.grant_user_permission_to_repo(repo, user, 'repository.none')
198 self.app.get(route_path('download_file', fid=file_uid), status=404)
199
200 def test_download_file_scoped_to_user(self, user_util, create_artifact_factory):
201 user = self.log_user()
202 user_id = user['user_id']
203 content = 'HELLO MY NAME IS ARTIFACT !'
204
205 artifact = create_artifact_factory(user_id, content)
206 # bind to user
207 user = user_util.create_user(password='qweqwe')
208
209 artifact.scope_user_id = user.user_id
210 Session().add(artifact)
211 Session().commit()
212
213 # artifact creator doesn't have access since it's bind to another user
214 file_uid = artifact.file_uid
215 self.app.get(route_path('download_file', fid=file_uid), status=404)
216
217 # log-in to new user and test download again, should be ok since we're bind to this artifact
218 self.log_user(user.username, 'qweqwe')
219 response = self.app.get(route_path('download_file', fid=file_uid), status=200)
220 assert response.text == content
221
222 def test_download_file_scoped_to_repo_with_bad_token(self, user_util, create_artifact_factory):
223 user_id = User.get_first_super_admin().user_id
224 content = 'HELLO MY NAME IS ARTIFACT !'
225
226 artifact = create_artifact_factory(user_id, content)
227 # bind to repo
228 repo = user_util.create_repo()
229 repo_id = repo.repo_id
230 artifact.scope_repo_id = repo_id
231 Session().add(artifact)
232 Session().commit()
233
234 file_uid = artifact.file_uid
235 self.app.get(route_path('download_file_by_token',
236 _auth_token='bogus', fid=file_uid), status=302)
237
238 def test_download_file_scoped_to_repo_with_token(self, user_util, create_artifact_factory):
239 user = User.get_first_super_admin()
240 AuthTokenModel().create(user, 'test artifact token',
241 role=AuthTokenModel.cls.ROLE_ARTIFACT_DOWNLOAD)
242
243 user = User.get_first_super_admin()
244 artifact_token = user.artifact_token
245
246 user_id = User.get_first_super_admin().user_id
247 content = 'HELLO MY NAME IS ARTIFACT !'
248
249 artifact = create_artifact_factory(user_id, content)
250 # bind to repo
251 repo = user_util.create_repo()
252 repo_id = repo.repo_id
253 artifact.scope_repo_id = repo_id
254 Session().add(artifact)
255 Session().commit()
256
257 file_uid = artifact.file_uid
258 response = self.app.get(
259 route_path('download_file_by_token',
260 _auth_token=artifact_token, fid=file_uid), status=200)
261 assert response.text == content
@@ -1,166 +1,174 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 import logging
20 import logging
21
21
22 from pyramid.view import view_config
22 from pyramid.view import view_config
23 from pyramid.response import FileResponse
23 from pyramid.response import FileResponse
24 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
24 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
25
25
26 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps.file_store import utils
27 from rhodecode.apps.file_store import utils
28 from rhodecode.apps.file_store.exceptions import (
28 from rhodecode.apps.file_store.exceptions import (
29 FileNotAllowedException, FileOverSizeException)
29 FileNotAllowedException, FileOverSizeException)
30
30
31 from rhodecode.lib import helpers as h
31 from rhodecode.lib import helpers as h
32 from rhodecode.lib import audit_logger
32 from rhodecode.lib import audit_logger
33 from rhodecode.lib.auth import (
33 from rhodecode.lib.auth import (
34 CSRFRequired, NotAnonymous, HasRepoPermissionAny, HasRepoGroupPermissionAny,
34 CSRFRequired, NotAnonymous, HasRepoPermissionAny, HasRepoGroupPermissionAny,
35 LoginRequired)
35 LoginRequired)
36 from rhodecode.model.db import Session, FileStore, UserApiKeys
36 from rhodecode.model.db import Session, FileStore, UserApiKeys
37
37
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40
40
41 class FileStoreView(BaseAppView):
41 class FileStoreView(BaseAppView):
42 upload_key = 'store_file'
42 upload_key = 'store_file'
43
43
44 def load_default_context(self):
44 def load_default_context(self):
45 c = self._get_local_tmpl_context()
45 c = self._get_local_tmpl_context()
46 self.storage = utils.get_file_storage(self.request.registry.settings)
46 self.storage = utils.get_file_storage(self.request.registry.settings)
47 return c
47 return c
48
48
49 @LoginRequired()
49 @LoginRequired()
50 @NotAnonymous()
50 @NotAnonymous()
51 @CSRFRequired()
51 @CSRFRequired()
52 @view_config(route_name='upload_file', request_method='POST', renderer='json_ext')
52 @view_config(route_name='upload_file', request_method='POST', renderer='json_ext')
53 def upload_file(self):
53 def upload_file(self):
54 self.load_default_context()
54 self.load_default_context()
55 file_obj = self.request.POST.get(self.upload_key)
55 file_obj = self.request.POST.get(self.upload_key)
56
56
57 if file_obj is None:
57 if file_obj is None:
58 return {'store_fid': None,
58 return {'store_fid': None,
59 'access_path': None,
59 'access_path': None,
60 'error': '{} data field is missing'.format(self.upload_key)}
60 'error': '{} data field is missing'.format(self.upload_key)}
61
61
62 if not hasattr(file_obj, 'filename'):
62 if not hasattr(file_obj, 'filename'):
63 return {'store_fid': None,
63 return {'store_fid': None,
64 'access_path': None,
64 'access_path': None,
65 'error': 'filename cannot be read from the data field'}
65 'error': 'filename cannot be read from the data field'}
66
66
67 filename = file_obj.filename
67 filename = file_obj.filename
68
68
69 metadata = {
69 metadata = {
70 'user_uploaded': {'username': self._rhodecode_user.username,
70 'user_uploaded': {'username': self._rhodecode_user.username,
71 'user_id': self._rhodecode_user.user_id,
71 'user_id': self._rhodecode_user.user_id,
72 'ip': self._rhodecode_user.ip_addr}}
72 'ip': self._rhodecode_user.ip_addr}}
73 try:
73 try:
74 store_uid, metadata = self.storage.save_file(
74 store_uid, metadata = self.storage.save_file(
75 file_obj.file, filename, extra_metadata=metadata)
75 file_obj.file, filename, extra_metadata=metadata)
76 except FileNotAllowedException:
76 except FileNotAllowedException:
77 return {'store_fid': None,
77 return {'store_fid': None,
78 'access_path': None,
78 'access_path': None,
79 'error': 'File {} is not allowed.'.format(filename)}
79 'error': 'File {} is not allowed.'.format(filename)}
80
80
81 except FileOverSizeException:
81 except FileOverSizeException:
82 return {'store_fid': None,
82 return {'store_fid': None,
83 'access_path': None,
83 'access_path': None,
84 'error': 'File {} is exceeding allowed limit.'.format(filename)}
84 'error': 'File {} is exceeding allowed limit.'.format(filename)}
85
85
86 try:
86 try:
87 entry = FileStore.create(
87 entry = FileStore.create(
88 file_uid=store_uid, filename=metadata["filename"],
88 file_uid=store_uid, filename=metadata["filename"],
89 file_hash=metadata["sha256"], file_size=metadata["size"],
89 file_hash=metadata["sha256"], file_size=metadata["size"],
90 file_description='upload attachment',
90 file_description=u'upload attachment',
91 check_acl=False, user_id=self._rhodecode_user.user_id
91 check_acl=False, user_id=self._rhodecode_user.user_id
92 )
92 )
93 Session().add(entry)
93 Session().add(entry)
94 Session().commit()
94 Session().commit()
95 log.debug('Stored upload in DB as %s', entry)
95 log.debug('Stored upload in DB as %s', entry)
96 except Exception:
96 except Exception:
97 log.exception('Failed to store file %s', filename)
97 log.exception('Failed to store file %s', filename)
98 return {'store_fid': None,
98 return {'store_fid': None,
99 'access_path': None,
99 'access_path': None,
100 'error': 'File {} failed to store in DB.'.format(filename)}
100 'error': 'File {} failed to store in DB.'.format(filename)}
101
101
102 return {'store_fid': store_uid,
102 return {'store_fid': store_uid,
103 'access_path': h.route_path('download_file', fid=store_uid)}
103 'access_path': h.route_path('download_file', fid=store_uid)}
104
104
105 def _serve_file(self, file_uid):
105 def _serve_file(self, file_uid):
106
106
107 if not self.storage.exists(file_uid):
107 if not self.storage.exists(file_uid):
108 store_path = self.storage.store_path(file_uid)
108 store_path = self.storage.store_path(file_uid)
109 log.debug('File with FID:%s not found in the store under `%s`',
109 log.debug('File with FID:%s not found in the store under `%s`',
110 file_uid, store_path)
110 file_uid, store_path)
111 raise HTTPNotFound()
111 raise HTTPNotFound()
112
112
113 db_obj = FileStore().query().filter(FileStore.file_uid == file_uid).scalar()
113 db_obj = FileStore().query().filter(FileStore.file_uid == file_uid).scalar()
114 if not db_obj:
114 if not db_obj:
115 raise HTTPNotFound()
115 raise HTTPNotFound()
116
116
117 # private upload for user
117 # private upload for user
118 if db_obj.check_acl and db_obj.scope_user_id:
118 if db_obj.check_acl and db_obj.scope_user_id:
119 log.debug('Artifact: checking scope access for bound artifact user: `%s`',
120 db_obj.scope_user_id)
119 user = db_obj.user
121 user = db_obj.user
120 if self._rhodecode_db_user.user_id != user.user_id:
122 if self._rhodecode_db_user.user_id != user.user_id:
121 log.warning('Access to file store object forbidden')
123 log.warning('Access to file store object forbidden')
122 raise HTTPNotFound()
124 raise HTTPNotFound()
123
125
124 # scoped to repository permissions
126 # scoped to repository permissions
125 if db_obj.check_acl and db_obj.scope_repo_id:
127 if db_obj.check_acl and db_obj.scope_repo_id:
128 log.debug('Artifact: checking scope access for bound artifact repo: `%s`',
129 db_obj.scope_repo_id)
126 repo = db_obj.repo
130 repo = db_obj.repo
127 perm_set = ['repository.read', 'repository.write', 'repository.admin']
131 perm_set = ['repository.read', 'repository.write', 'repository.admin']
128 has_perm = HasRepoPermissionAny(*perm_set)(repo.repo_name, 'FileStore check')
132 has_perm = HasRepoPermissionAny(*perm_set)(repo.repo_name, 'FileStore check')
129 if not has_perm:
133 if not has_perm:
130 log.warning('Access to file store object `%s` forbidden', file_uid)
134 log.warning('Access to file store object `%s` forbidden', file_uid)
131 raise HTTPNotFound()
135 raise HTTPNotFound()
132
136
133 # scoped to repository group permissions
137 # scoped to repository group permissions
134 if db_obj.check_acl and db_obj.scope_repo_group_id:
138 if db_obj.check_acl and db_obj.scope_repo_group_id:
139 log.debug('Artifact: checking scope access for bound artifact repo group: `%s`',
140 db_obj.scope_repo_group_id)
135 repo_group = db_obj.repo_group
141 repo_group = db_obj.repo_group
136 perm_set = ['group.read', 'group.write', 'group.admin']
142 perm_set = ['group.read', 'group.write', 'group.admin']
137 has_perm = HasRepoGroupPermissionAny(*perm_set)(repo_group.group_name, 'FileStore check')
143 has_perm = HasRepoGroupPermissionAny(*perm_set)(repo_group.group_name, 'FileStore check')
138 if not has_perm:
144 if not has_perm:
139 log.warning('Access to file store object `%s` forbidden', file_uid)
145 log.warning('Access to file store object `%s` forbidden', file_uid)
140 raise HTTPNotFound()
146 raise HTTPNotFound()
141
147
142 FileStore.bump_access_counter(file_uid)
148 FileStore.bump_access_counter(file_uid)
143
149
144 file_path = self.storage.store_path(file_uid)
150 file_path = self.storage.store_path(file_uid)
145 return FileResponse(file_path)
151 return FileResponse(file_path)
146
152
147 # ACL is checked by scopes, if no scope the file is accessible to all
153 # ACL is checked by scopes, if no scope the file is accessible to all
148 @view_config(route_name='download_file')
154 @view_config(route_name='download_file')
149 def download_file(self):
155 def download_file(self):
150 self.load_default_context()
156 self.load_default_context()
151 file_uid = self.request.matchdict['fid']
157 file_uid = self.request.matchdict['fid']
152 log.debug('Requesting FID:%s from store %s', file_uid, self.storage)
158 log.debug('Requesting FID:%s from store %s', file_uid, self.storage)
153 return self._serve_file(file_uid)
159 return self._serve_file(file_uid)
154
160
161 # in addition to @LoginRequired ACL is checked by scopes
155 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_ARTIFACT_DOWNLOAD])
162 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_ARTIFACT_DOWNLOAD])
163 @NotAnonymous()
156 @view_config(route_name='download_file_by_token')
164 @view_config(route_name='download_file_by_token')
157 def download_file_by_token(self):
165 def download_file_by_token(self):
158 """
166 """
159 Special view that allows to access the download file by special URL that
167 Special view that allows to access the download file by special URL that
160 is stored inside the URL.
168 is stored inside the URL.
161
169
162 http://example.com/_file_store/token-download/TOKEN/FILE_UID
170 http://example.com/_file_store/token-download/TOKEN/FILE_UID
163 """
171 """
164 self.load_default_context()
172 self.load_default_context()
165 file_uid = self.request.matchdict['fid']
173 file_uid = self.request.matchdict['fid']
166 return self._serve_file(file_uid)
174 return self._serve_file(file_uid)
General Comments 0
You need to be logged in to leave comments. Login now