##// END OF EJS Templates
file-store: implement check-acl permissions.
marcink -
r3674:6a107e97 new-ui
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,115 +1,144 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 (CSRFRequired, NotAnonymous)
33 from rhodecode.lib.auth import (CSRFRequired, NotAnonymous, HasRepoPermissionAny, HasRepoGroupPermissionAny)
34 from rhodecode.model.db import Session, FileStore
34 from rhodecode.model.db import Session, FileStore
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38
38
39 class FileStoreView(BaseAppView):
39 class FileStoreView(BaseAppView):
40 upload_key = 'store_file'
40 upload_key = 'store_file'
41
41
42 def load_default_context(self):
42 def load_default_context(self):
43 c = self._get_local_tmpl_context()
43 c = self._get_local_tmpl_context()
44 self.storage = utils.get_file_storage(self.request.registry.settings)
44 self.storage = utils.get_file_storage(self.request.registry.settings)
45 return c
45 return c
46
46
47 @NotAnonymous()
47 @NotAnonymous()
48 @CSRFRequired()
48 @CSRFRequired()
49 @view_config(route_name='upload_file', request_method='POST', renderer='json_ext')
49 @view_config(route_name='upload_file', request_method='POST', renderer='json_ext')
50 def upload_file(self):
50 def upload_file(self):
51 self.load_default_context()
51 self.load_default_context()
52 file_obj = self.request.POST.get(self.upload_key)
52 file_obj = self.request.POST.get(self.upload_key)
53
53
54 if file_obj is None:
54 if file_obj is None:
55 return {'store_fid': None,
55 return {'store_fid': None,
56 'access_path': None,
56 'access_path': None,
57 'error': '{} data field is missing'.format(self.upload_key)}
57 'error': '{} data field is missing'.format(self.upload_key)}
58
58
59 if not hasattr(file_obj, 'filename'):
59 if not hasattr(file_obj, 'filename'):
60 return {'store_fid': None,
60 return {'store_fid': None,
61 'access_path': None,
61 'access_path': None,
62 'error': 'filename cannot be read from the data field'}
62 'error': 'filename cannot be read from the data field'}
63
63
64 filename = file_obj.filename
64 filename = file_obj.filename
65
65
66 metadata = {
66 metadata = {
67 'user_uploaded': {'username': self._rhodecode_user.username,
67 'user_uploaded': {'username': self._rhodecode_user.username,
68 'user_id': self._rhodecode_user.user_id,
68 'user_id': self._rhodecode_user.user_id,
69 'ip': self._rhodecode_user.ip_addr}}
69 'ip': self._rhodecode_user.ip_addr}}
70 try:
70 try:
71 store_fid, metadata = self.storage.save_file(
71 store_fid, metadata = self.storage.save_file(
72 file_obj.file, filename, extra_metadata=metadata)
72 file_obj.file, filename, extra_metadata=metadata)
73 except FileNotAllowedException:
73 except FileNotAllowedException:
74 return {'store_fid': None,
74 return {'store_fid': None,
75 'access_path': None,
75 'access_path': None,
76 'error': 'File {} is not allowed.'.format(filename)}
76 'error': 'File {} is not allowed.'.format(filename)}
77
77
78 except FileOverSizeException:
78 except FileOverSizeException:
79 return {'store_fid': None,
79 return {'store_fid': None,
80 'access_path': None,
80 'access_path': None,
81 'error': 'File {} is exceeding allowed limit.'.format(filename)}
81 'error': 'File {} is exceeding allowed limit.'.format(filename)}
82
82
83 try:
83 try:
84 entry = FileStore.create(
84 entry = FileStore.create(
85 file_uid=store_fid, filename=metadata["filename"],
85 file_uid=store_fid, filename=metadata["filename"],
86 file_hash=metadata["sha256"], file_size=metadata["size"],
86 file_hash=metadata["sha256"], file_size=metadata["size"],
87 file_description='upload attachment',
87 file_description='upload attachment',
88 check_acl=False, user_id=self._rhodecode_user.user_id
88 check_acl=False, user_id=self._rhodecode_user.user_id
89 )
89 )
90 Session().add(entry)
90 Session().add(entry)
91 Session().commit()
91 Session().commit()
92 log.debug('Stored upload in DB as %s', entry)
92 log.debug('Stored upload in DB as %s', entry)
93 except Exception:
93 except Exception:
94 log.exception('Failed to store file %s', filename)
94 log.exception('Failed to store file %s', filename)
95 return {'store_fid': None,
95 return {'store_fid': None,
96 'access_path': None,
96 'access_path': None,
97 'error': 'File {} failed to store in DB.'.format(filename)}
97 'error': 'File {} failed to store in DB.'.format(filename)}
98
98
99 return {'store_fid': store_fid,
99 return {'store_fid': store_fid,
100 'access_path': h.route_path('download_file', fid=store_fid)}
100 'access_path': h.route_path('download_file', fid=store_fid)}
101
101
102 @view_config(route_name='download_file')
102 @view_config(route_name='download_file')
103 def download_file(self):
103 def download_file(self):
104 self.load_default_context()
104 self.load_default_context()
105 file_uid = self.request.matchdict['fid']
105 file_uid = self.request.matchdict['fid']
106 log.debug('Requesting FID:%s from store %s', file_uid, self.storage)
106 log.debug('Requesting FID:%s from store %s', file_uid, self.storage)
107
107
108 if not self.storage.exists(file_uid):
108 if not self.storage.exists(file_uid):
109 log.debug('File with FID:%s not found in the store', file_uid)
109 log.debug('File with FID:%s not found in the store', file_uid)
110 raise HTTPNotFound()
110 raise HTTPNotFound()
111
111
112 db_obj = FileStore().query().filter(FileStore.file_uid == file_uid).scalar()
113 if not db_obj:
114 raise HTTPNotFound()
115
116 # private upload for user
117 if db_obj.check_acl and db_obj.scope_user_id:
118 user = db_obj.user
119 if self._rhodecode_db_user.user_id != user.user_id:
120 log.warning('Access to file store object forbidden')
121 raise HTTPNotFound()
122
123 # scoped to repository permissions
124 if db_obj.check_acl and db_obj.scope_repo_id:
125 repo = db_obj.repo
126 perm_set = ['repository.read', 'repository.write', 'repository.admin']
127 has_perm = HasRepoPermissionAny(*perm_set)(repo.repo_name, 'FileStore check')
128 if not has_perm:
129 log.warning('Access to file store object forbidden')
130 raise HTTPNotFound()
131
132 # scoped to repository group permissions
133 if db_obj.check_acl and db_obj.scope_repo_group_id:
134 repo_group = db_obj.repo_group
135 perm_set = ['group.read', 'group.write', 'group.admin']
136 has_perm = HasRepoGroupPermissionAny(*perm_set)(repo_group.group_name, 'FileStore check')
137 if not has_perm:
138 log.warning('Access to file store object forbidden')
139 raise HTTPNotFound()
140
112 FileStore.bump_access_counter(file_uid)
141 FileStore.bump_access_counter(file_uid)
113
142
114 file_path = self.storage.store_path(file_uid)
143 file_path = self.storage.store_path(file_uid)
115 return FileResponse(file_path)
144 return FileResponse(file_path)
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now