##// END OF EJS Templates
artifacts: expose a special auth-token based artifacts download urls....
marcink -
r4003:09f31efc default
parent child Browse files
Show More

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

@@ -1,49 +1,52 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20 import os
21 21 from rhodecode.apps.file_store import config_keys
22 22 from rhodecode.config.middleware import _bool_setting, _string_setting
23 23
24 24
25 25 def _sanitize_settings_and_apply_defaults(settings):
26 26 """
27 27 Set defaults, convert to python types and validate settings.
28 28 """
29 29 _bool_setting(settings, config_keys.enabled, 'true')
30 30
31 31 _string_setting(settings, config_keys.backend, 'local')
32 32
33 33 default_store = os.path.join(os.path.dirname(settings['__file__']), 'upload_store')
34 34 _string_setting(settings, config_keys.store_path, default_store)
35 35
36 36
37 37 def includeme(config):
38 38 settings = config.registry.settings
39 39 _sanitize_settings_and_apply_defaults(settings)
40 40
41 41 config.add_route(
42 42 name='upload_file',
43 43 pattern='/_file_store/upload')
44 44 config.add_route(
45 45 name='download_file',
46 46 pattern='/_file_store/download/{fid}')
47 config.add_route(
48 name='download_file_by_token',
49 pattern='/_file_store/token-download/{_auth_token}/{fid}')
47 50
48 51 # Scan module for configuration decorators.
49 52 config.scan('.views', ignore='.tests')
@@ -1,146 +1,166 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20 import logging
21 21
22 22 from pyramid.view import view_config
23 23 from pyramid.response import FileResponse
24 24 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
25 25
26 26 from rhodecode.apps._base import BaseAppView
27 27 from rhodecode.apps.file_store import utils
28 28 from rhodecode.apps.file_store.exceptions import (
29 29 FileNotAllowedException, FileOverSizeException)
30 30
31 31 from rhodecode.lib import helpers as h
32 32 from rhodecode.lib import audit_logger
33 from rhodecode.lib.auth import (CSRFRequired, NotAnonymous, HasRepoPermissionAny, HasRepoGroupPermissionAny)
34 from rhodecode.model.db import Session, FileStore
33 from rhodecode.lib.auth import (
34 CSRFRequired, NotAnonymous, HasRepoPermissionAny, HasRepoGroupPermissionAny,
35 LoginRequired)
36 from rhodecode.model.db import Session, FileStore, UserApiKeys
35 37
36 38 log = logging.getLogger(__name__)
37 39
38 40
39 41 class FileStoreView(BaseAppView):
40 42 upload_key = 'store_file'
41 43
42 44 def load_default_context(self):
43 45 c = self._get_local_tmpl_context()
44 46 self.storage = utils.get_file_storage(self.request.registry.settings)
45 47 return c
46 48
49 @LoginRequired()
47 50 @NotAnonymous()
48 51 @CSRFRequired()
49 52 @view_config(route_name='upload_file', request_method='POST', renderer='json_ext')
50 53 def upload_file(self):
51 54 self.load_default_context()
52 55 file_obj = self.request.POST.get(self.upload_key)
53 56
54 57 if file_obj is None:
55 58 return {'store_fid': None,
56 59 'access_path': None,
57 60 'error': '{} data field is missing'.format(self.upload_key)}
58 61
59 62 if not hasattr(file_obj, 'filename'):
60 63 return {'store_fid': None,
61 64 'access_path': None,
62 65 'error': 'filename cannot be read from the data field'}
63 66
64 67 filename = file_obj.filename
65 68
66 69 metadata = {
67 70 'user_uploaded': {'username': self._rhodecode_user.username,
68 71 'user_id': self._rhodecode_user.user_id,
69 72 'ip': self._rhodecode_user.ip_addr}}
70 73 try:
71 74 store_uid, metadata = self.storage.save_file(
72 75 file_obj.file, filename, extra_metadata=metadata)
73 76 except FileNotAllowedException:
74 77 return {'store_fid': None,
75 78 'access_path': None,
76 79 'error': 'File {} is not allowed.'.format(filename)}
77 80
78 81 except FileOverSizeException:
79 82 return {'store_fid': None,
80 83 'access_path': None,
81 84 'error': 'File {} is exceeding allowed limit.'.format(filename)}
82 85
83 86 try:
84 87 entry = FileStore.create(
85 88 file_uid=store_uid, filename=metadata["filename"],
86 89 file_hash=metadata["sha256"], file_size=metadata["size"],
87 90 file_description='upload attachment',
88 91 check_acl=False, user_id=self._rhodecode_user.user_id
89 92 )
90 93 Session().add(entry)
91 94 Session().commit()
92 95 log.debug('Stored upload in DB as %s', entry)
93 96 except Exception:
94 97 log.exception('Failed to store file %s', filename)
95 98 return {'store_fid': None,
96 99 'access_path': None,
97 100 'error': 'File {} failed to store in DB.'.format(filename)}
98 101
99 102 return {'store_fid': store_uid,
100 103 'access_path': h.route_path('download_file', fid=store_uid)}
101 104
102 @view_config(route_name='download_file')
103 def download_file(self):
104 self.load_default_context()
105 file_uid = self.request.matchdict['fid']
106 log.debug('Requesting FID:%s from store %s', file_uid, self.storage)
105 def _serve_file(self, file_uid):
107 106
108 107 if not self.storage.exists(file_uid):
109 108 store_path = self.storage.store_path(file_uid)
110 109 log.debug('File with FID:%s not found in the store under `%s`',
111 110 file_uid, store_path)
112 111 raise HTTPNotFound()
113 112
114 113 db_obj = FileStore().query().filter(FileStore.file_uid == file_uid).scalar()
115 114 if not db_obj:
116 115 raise HTTPNotFound()
117 116
118 117 # private upload for user
119 118 if db_obj.check_acl and db_obj.scope_user_id:
120 119 user = db_obj.user
121 120 if self._rhodecode_db_user.user_id != user.user_id:
122 121 log.warning('Access to file store object forbidden')
123 122 raise HTTPNotFound()
124 123
125 124 # scoped to repository permissions
126 125 if db_obj.check_acl and db_obj.scope_repo_id:
127 126 repo = db_obj.repo
128 127 perm_set = ['repository.read', 'repository.write', 'repository.admin']
129 128 has_perm = HasRepoPermissionAny(*perm_set)(repo.repo_name, 'FileStore check')
130 129 if not has_perm:
131 log.warning('Access to file store object forbidden')
130 log.warning('Access to file store object `%s` forbidden', file_uid)
132 131 raise HTTPNotFound()
133 132
134 133 # scoped to repository group permissions
135 134 if db_obj.check_acl and db_obj.scope_repo_group_id:
136 135 repo_group = db_obj.repo_group
137 136 perm_set = ['group.read', 'group.write', 'group.admin']
138 137 has_perm = HasRepoGroupPermissionAny(*perm_set)(repo_group.group_name, 'FileStore check')
139 138 if not has_perm:
140 log.warning('Access to file store object forbidden')
139 log.warning('Access to file store object `%s` forbidden', file_uid)
141 140 raise HTTPNotFound()
142 141
143 142 FileStore.bump_access_counter(file_uid)
144 143
145 144 file_path = self.storage.store_path(file_uid)
146 145 return FileResponse(file_path)
146
147 # ACL is checked by scopes, if no scope the file is accessible to all
148 @view_config(route_name='download_file')
149 def download_file(self):
150 self.load_default_context()
151 file_uid = self.request.matchdict['fid']
152 log.debug('Requesting FID:%s from store %s', file_uid, self.storage)
153 return self._serve_file(file_uid)
154
155 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_ARTIFACT_DOWNLOAD])
156 @view_config(route_name='download_file_by_token')
157 def download_file_by_token(self):
158 """
159 Special view that allows to access the download file by special URL that
160 is stored inside the URL.
161
162 http://example.com/_file_store/token-download/TOKEN/FILE_UID
163 """
164 self.load_default_context()
165 file_uid = self.request.matchdict['fid']
166 return self._serve_file(file_uid)
@@ -1,2352 +1,2369 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 authentication and permission libraries
23 23 """
24 24
25 25 import os
26 26 import time
27 27 import inspect
28 28 import collections
29 29 import fnmatch
30 30 import hashlib
31 31 import itertools
32 32 import logging
33 33 import random
34 34 import traceback
35 35 from functools import wraps
36 36
37 37 import ipaddress
38 38
39 39 from pyramid.httpexceptions import HTTPForbidden, HTTPFound, HTTPNotFound
40 40 from sqlalchemy.orm.exc import ObjectDeletedError
41 41 from sqlalchemy.orm import joinedload
42 42 from zope.cachedescriptors.property import Lazy as LazyProperty
43 43
44 44 import rhodecode
45 45 from rhodecode.model import meta
46 46 from rhodecode.model.meta import Session
47 47 from rhodecode.model.user import UserModel
48 48 from rhodecode.model.db import (
49 49 User, Repository, Permission, UserToPerm, UserGroupToPerm, UserGroupMember,
50 50 UserIpMap, UserApiKeys, RepoGroup, UserGroup)
51 51 from rhodecode.lib import rc_cache
52 52 from rhodecode.lib.utils2 import safe_unicode, aslist, safe_str, md5, safe_int, sha1
53 53 from rhodecode.lib.utils import (
54 54 get_repo_slug, get_repo_group_slug, get_user_group_slug)
55 55 from rhodecode.lib.caching_query import FromCache
56 56
57 57
58 58 if rhodecode.is_unix:
59 59 import bcrypt
60 60
61 61 log = logging.getLogger(__name__)
62 62
63 63 csrf_token_key = "csrf_token"
64 64
65 65
66 66 class PasswordGenerator(object):
67 67 """
68 68 This is a simple class for generating password from different sets of
69 69 characters
70 70 usage::
71 71 passwd_gen = PasswordGenerator()
72 72 #print 8-letter password containing only big and small letters
73 73 of alphabet
74 74 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
75 75 """
76 76 ALPHABETS_NUM = r'''1234567890'''
77 77 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
78 78 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
79 79 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
80 80 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
81 81 + ALPHABETS_NUM + ALPHABETS_SPECIAL
82 82 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
83 83 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
84 84 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
85 85 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
86 86
87 87 def __init__(self, passwd=''):
88 88 self.passwd = passwd
89 89
90 90 def gen_password(self, length, type_=None):
91 91 if type_ is None:
92 92 type_ = self.ALPHABETS_FULL
93 93 self.passwd = ''.join([random.choice(type_) for _ in range(length)])
94 94 return self.passwd
95 95
96 96
97 97 class _RhodeCodeCryptoBase(object):
98 98 ENC_PREF = None
99 99
100 100 def hash_create(self, str_):
101 101 """
102 102 hash the string using
103 103
104 104 :param str_: password to hash
105 105 """
106 106 raise NotImplementedError
107 107
108 108 def hash_check_with_upgrade(self, password, hashed):
109 109 """
110 110 Returns tuple in which first element is boolean that states that
111 111 given password matches it's hashed version, and the second is new hash
112 112 of the password, in case this password should be migrated to new
113 113 cipher.
114 114 """
115 115 checked_hash = self.hash_check(password, hashed)
116 116 return checked_hash, None
117 117
118 118 def hash_check(self, password, hashed):
119 119 """
120 120 Checks matching password with it's hashed value.
121 121
122 122 :param password: password
123 123 :param hashed: password in hashed form
124 124 """
125 125 raise NotImplementedError
126 126
127 127 def _assert_bytes(self, value):
128 128 """
129 129 Passing in an `unicode` object can lead to hard to detect issues
130 130 if passwords contain non-ascii characters. Doing a type check
131 131 during runtime, so that such mistakes are detected early on.
132 132 """
133 133 if not isinstance(value, str):
134 134 raise TypeError(
135 135 "Bytestring required as input, got %r." % (value, ))
136 136
137 137
138 138 class _RhodeCodeCryptoBCrypt(_RhodeCodeCryptoBase):
139 139 ENC_PREF = ('$2a$10', '$2b$10')
140 140
141 141 def hash_create(self, str_):
142 142 self._assert_bytes(str_)
143 143 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
144 144
145 145 def hash_check_with_upgrade(self, password, hashed):
146 146 """
147 147 Returns tuple in which first element is boolean that states that
148 148 given password matches it's hashed version, and the second is new hash
149 149 of the password, in case this password should be migrated to new
150 150 cipher.
151 151
152 152 This implements special upgrade logic which works like that:
153 153 - check if the given password == bcrypted hash, if yes then we
154 154 properly used password and it was already in bcrypt. Proceed
155 155 without any changes
156 156 - if bcrypt hash check is not working try with sha256. If hash compare
157 157 is ok, it means we using correct but old hashed password. indicate
158 158 hash change and proceed
159 159 """
160 160
161 161 new_hash = None
162 162
163 163 # regular pw check
164 164 password_match_bcrypt = self.hash_check(password, hashed)
165 165
166 166 # now we want to know if the password was maybe from sha256
167 167 # basically calling _RhodeCodeCryptoSha256().hash_check()
168 168 if not password_match_bcrypt:
169 169 if _RhodeCodeCryptoSha256().hash_check(password, hashed):
170 170 new_hash = self.hash_create(password) # make new bcrypt hash
171 171 password_match_bcrypt = True
172 172
173 173 return password_match_bcrypt, new_hash
174 174
175 175 def hash_check(self, password, hashed):
176 176 """
177 177 Checks matching password with it's hashed value.
178 178
179 179 :param password: password
180 180 :param hashed: password in hashed form
181 181 """
182 182 self._assert_bytes(password)
183 183 try:
184 184 return bcrypt.hashpw(password, hashed) == hashed
185 185 except ValueError as e:
186 186 # we're having a invalid salt here probably, we should not crash
187 187 # just return with False as it would be a wrong password.
188 188 log.debug('Failed to check password hash using bcrypt %s',
189 189 safe_str(e))
190 190
191 191 return False
192 192
193 193
194 194 class _RhodeCodeCryptoSha256(_RhodeCodeCryptoBase):
195 195 ENC_PREF = '_'
196 196
197 197 def hash_create(self, str_):
198 198 self._assert_bytes(str_)
199 199 return hashlib.sha256(str_).hexdigest()
200 200
201 201 def hash_check(self, password, hashed):
202 202 """
203 203 Checks matching password with it's hashed value.
204 204
205 205 :param password: password
206 206 :param hashed: password in hashed form
207 207 """
208 208 self._assert_bytes(password)
209 209 return hashlib.sha256(password).hexdigest() == hashed
210 210
211 211
212 212 class _RhodeCodeCryptoTest(_RhodeCodeCryptoBase):
213 213 ENC_PREF = '_'
214 214
215 215 def hash_create(self, str_):
216 216 self._assert_bytes(str_)
217 217 return sha1(str_)
218 218
219 219 def hash_check(self, password, hashed):
220 220 """
221 221 Checks matching password with it's hashed value.
222 222
223 223 :param password: password
224 224 :param hashed: password in hashed form
225 225 """
226 226 self._assert_bytes(password)
227 227 return sha1(password) == hashed
228 228
229 229
230 230 def crypto_backend():
231 231 """
232 232 Return the matching crypto backend.
233 233
234 234 Selection is based on if we run tests or not, we pick sha1-test backend to run
235 235 tests faster since BCRYPT is expensive to calculate
236 236 """
237 237 if rhodecode.is_test:
238 238 RhodeCodeCrypto = _RhodeCodeCryptoTest()
239 239 else:
240 240 RhodeCodeCrypto = _RhodeCodeCryptoBCrypt()
241 241
242 242 return RhodeCodeCrypto
243 243
244 244
245 245 def get_crypt_password(password):
246 246 """
247 247 Create the hash of `password` with the active crypto backend.
248 248
249 249 :param password: The cleartext password.
250 250 :type password: unicode
251 251 """
252 252 password = safe_str(password)
253 253 return crypto_backend().hash_create(password)
254 254
255 255
256 256 def check_password(password, hashed):
257 257 """
258 258 Check if the value in `password` matches the hash in `hashed`.
259 259
260 260 :param password: The cleartext password.
261 261 :type password: unicode
262 262
263 263 :param hashed: The expected hashed version of the password.
264 264 :type hashed: The hash has to be passed in in text representation.
265 265 """
266 266 password = safe_str(password)
267 267 return crypto_backend().hash_check(password, hashed)
268 268
269 269
270 270 def generate_auth_token(data, salt=None):
271 271 """
272 272 Generates API KEY from given string
273 273 """
274 274
275 275 if salt is None:
276 276 salt = os.urandom(16)
277 277 return hashlib.sha1(safe_str(data) + salt).hexdigest()
278 278
279 279
280 280 def get_came_from(request):
281 281 """
282 282 get query_string+path from request sanitized after removing auth_token
283 283 """
284 284 _req = request
285 285
286 286 path = _req.path
287 287 if 'auth_token' in _req.GET:
288 288 # sanitize the request and remove auth_token for redirection
289 289 _req.GET.pop('auth_token')
290 290 qs = _req.query_string
291 291 if qs:
292 292 path += '?' + qs
293 293
294 294 return path
295 295
296 296
297 297 class CookieStoreWrapper(object):
298 298
299 299 def __init__(self, cookie_store):
300 300 self.cookie_store = cookie_store
301 301
302 302 def __repr__(self):
303 303 return 'CookieStore<%s>' % (self.cookie_store)
304 304
305 305 def get(self, key, other=None):
306 306 if isinstance(self.cookie_store, dict):
307 307 return self.cookie_store.get(key, other)
308 308 elif isinstance(self.cookie_store, AuthUser):
309 309 return self.cookie_store.__dict__.get(key, other)
310 310
311 311
312 312 def _cached_perms_data(user_id, scope, user_is_admin,
313 313 user_inherit_default_permissions, explicit, algo,
314 314 calculate_super_admin):
315 315
316 316 permissions = PermissionCalculator(
317 317 user_id, scope, user_is_admin, user_inherit_default_permissions,
318 318 explicit, algo, calculate_super_admin)
319 319 return permissions.calculate()
320 320
321 321
322 322 class PermOrigin(object):
323 323 SUPER_ADMIN = 'superadmin'
324 324 ARCHIVED = 'archived'
325 325
326 326 REPO_USER = 'user:%s'
327 327 REPO_USERGROUP = 'usergroup:%s'
328 328 REPO_OWNER = 'repo.owner'
329 329 REPO_DEFAULT = 'repo.default'
330 330 REPO_DEFAULT_NO_INHERIT = 'repo.default.no.inherit'
331 331 REPO_PRIVATE = 'repo.private'
332 332
333 333 REPOGROUP_USER = 'user:%s'
334 334 REPOGROUP_USERGROUP = 'usergroup:%s'
335 335 REPOGROUP_OWNER = 'group.owner'
336 336 REPOGROUP_DEFAULT = 'group.default'
337 337 REPOGROUP_DEFAULT_NO_INHERIT = 'group.default.no.inherit'
338 338
339 339 USERGROUP_USER = 'user:%s'
340 340 USERGROUP_USERGROUP = 'usergroup:%s'
341 341 USERGROUP_OWNER = 'usergroup.owner'
342 342 USERGROUP_DEFAULT = 'usergroup.default'
343 343 USERGROUP_DEFAULT_NO_INHERIT = 'usergroup.default.no.inherit'
344 344
345 345
346 346 class PermOriginDict(dict):
347 347 """
348 348 A special dict used for tracking permissions along with their origins.
349 349
350 350 `__setitem__` has been overridden to expect a tuple(perm, origin)
351 351 `__getitem__` will return only the perm
352 352 `.perm_origin_stack` will return the stack of (perm, origin) set per key
353 353
354 354 >>> perms = PermOriginDict()
355 355 >>> perms['resource'] = 'read', 'default'
356 356 >>> perms['resource']
357 357 'read'
358 358 >>> perms['resource'] = 'write', 'admin'
359 359 >>> perms['resource']
360 360 'write'
361 361 >>> perms.perm_origin_stack
362 362 {'resource': [('read', 'default'), ('write', 'admin')]}
363 363 """
364 364
365 365 def __init__(self, *args, **kw):
366 366 dict.__init__(self, *args, **kw)
367 367 self.perm_origin_stack = collections.OrderedDict()
368 368
369 369 def __setitem__(self, key, (perm, origin)):
370 370 self.perm_origin_stack.setdefault(key, []).append(
371 371 (perm, origin))
372 372 dict.__setitem__(self, key, perm)
373 373
374 374
375 375 class BranchPermOriginDict(PermOriginDict):
376 376 """
377 377 Dedicated branch permissions dict, with tracking of patterns and origins.
378 378
379 379 >>> perms = BranchPermOriginDict()
380 380 >>> perms['resource'] = '*pattern', 'read', 'default'
381 381 >>> perms['resource']
382 382 {'*pattern': 'read'}
383 383 >>> perms['resource'] = '*pattern', 'write', 'admin'
384 384 >>> perms['resource']
385 385 {'*pattern': 'write'}
386 386 >>> perms.perm_origin_stack
387 387 {'resource': {'*pattern': [('read', 'default'), ('write', 'admin')]}}
388 388 """
389 389 def __setitem__(self, key, (pattern, perm, origin)):
390 390
391 391 self.perm_origin_stack.setdefault(key, {}) \
392 392 .setdefault(pattern, []).append((perm, origin))
393 393
394 394 if key in self:
395 395 self[key].__setitem__(pattern, perm)
396 396 else:
397 397 patterns = collections.OrderedDict()
398 398 patterns[pattern] = perm
399 399 dict.__setitem__(self, key, patterns)
400 400
401 401
402 402 class PermissionCalculator(object):
403 403
404 404 def __init__(
405 405 self, user_id, scope, user_is_admin,
406 406 user_inherit_default_permissions, explicit, algo,
407 407 calculate_super_admin_as_user=False):
408 408
409 409 self.user_id = user_id
410 410 self.user_is_admin = user_is_admin
411 411 self.inherit_default_permissions = user_inherit_default_permissions
412 412 self.explicit = explicit
413 413 self.algo = algo
414 414 self.calculate_super_admin_as_user = calculate_super_admin_as_user
415 415
416 416 scope = scope or {}
417 417 self.scope_repo_id = scope.get('repo_id')
418 418 self.scope_repo_group_id = scope.get('repo_group_id')
419 419 self.scope_user_group_id = scope.get('user_group_id')
420 420
421 421 self.default_user_id = User.get_default_user(cache=True).user_id
422 422
423 423 self.permissions_repositories = PermOriginDict()
424 424 self.permissions_repository_groups = PermOriginDict()
425 425 self.permissions_user_groups = PermOriginDict()
426 426 self.permissions_repository_branches = BranchPermOriginDict()
427 427 self.permissions_global = set()
428 428
429 429 self.default_repo_perms = Permission.get_default_repo_perms(
430 430 self.default_user_id, self.scope_repo_id)
431 431 self.default_repo_groups_perms = Permission.get_default_group_perms(
432 432 self.default_user_id, self.scope_repo_group_id)
433 433 self.default_user_group_perms = \
434 434 Permission.get_default_user_group_perms(
435 435 self.default_user_id, self.scope_user_group_id)
436 436
437 437 # default branch perms
438 438 self.default_branch_repo_perms = \
439 439 Permission.get_default_repo_branch_perms(
440 440 self.default_user_id, self.scope_repo_id)
441 441
442 442 def calculate(self):
443 443 if self.user_is_admin and not self.calculate_super_admin_as_user:
444 444 return self._calculate_admin_permissions()
445 445
446 446 self._calculate_global_default_permissions()
447 447 self._calculate_global_permissions()
448 448 self._calculate_default_permissions()
449 449 self._calculate_repository_permissions()
450 450 self._calculate_repository_branch_permissions()
451 451 self._calculate_repository_group_permissions()
452 452 self._calculate_user_group_permissions()
453 453 return self._permission_structure()
454 454
455 455 def _calculate_admin_permissions(self):
456 456 """
457 457 admin user have all default rights for repositories
458 458 and groups set to admin
459 459 """
460 460 self.permissions_global.add('hg.admin')
461 461 self.permissions_global.add('hg.create.write_on_repogroup.true')
462 462
463 463 # repositories
464 464 for perm in self.default_repo_perms:
465 465 r_k = perm.UserRepoToPerm.repository.repo_name
466 466 archived = perm.UserRepoToPerm.repository.archived
467 467 p = 'repository.admin'
468 468 self.permissions_repositories[r_k] = p, PermOrigin.SUPER_ADMIN
469 469 # special case for archived repositories, which we block still even for
470 470 # super admins
471 471 if archived:
472 472 p = 'repository.read'
473 473 self.permissions_repositories[r_k] = p, PermOrigin.ARCHIVED
474 474
475 475 # repository groups
476 476 for perm in self.default_repo_groups_perms:
477 477 rg_k = perm.UserRepoGroupToPerm.group.group_name
478 478 p = 'group.admin'
479 479 self.permissions_repository_groups[rg_k] = p, PermOrigin.SUPER_ADMIN
480 480
481 481 # user groups
482 482 for perm in self.default_user_group_perms:
483 483 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
484 484 p = 'usergroup.admin'
485 485 self.permissions_user_groups[u_k] = p, PermOrigin.SUPER_ADMIN
486 486
487 487 # branch permissions
488 488 # since super-admin also can have custom rule permissions
489 489 # we *always* need to calculate those inherited from default, and also explicit
490 490 self._calculate_default_permissions_repository_branches(
491 491 user_inherit_object_permissions=False)
492 492 self._calculate_repository_branch_permissions()
493 493
494 494 return self._permission_structure()
495 495
496 496 def _calculate_global_default_permissions(self):
497 497 """
498 498 global permissions taken from the default user
499 499 """
500 500 default_global_perms = UserToPerm.query()\
501 501 .filter(UserToPerm.user_id == self.default_user_id)\
502 502 .options(joinedload(UserToPerm.permission))
503 503
504 504 for perm in default_global_perms:
505 505 self.permissions_global.add(perm.permission.permission_name)
506 506
507 507 if self.user_is_admin:
508 508 self.permissions_global.add('hg.admin')
509 509 self.permissions_global.add('hg.create.write_on_repogroup.true')
510 510
511 511 def _calculate_global_permissions(self):
512 512 """
513 513 Set global system permissions with user permissions or permissions
514 514 taken from the user groups of the current user.
515 515
516 516 The permissions include repo creating, repo group creating, forking
517 517 etc.
518 518 """
519 519
520 520 # now we read the defined permissions and overwrite what we have set
521 521 # before those can be configured from groups or users explicitly.
522 522
523 523 # In case we want to extend this list we should make sure
524 524 # this is in sync with User.DEFAULT_USER_PERMISSIONS definitions
525 525 _configurable = frozenset([
526 526 'hg.fork.none', 'hg.fork.repository',
527 527 'hg.create.none', 'hg.create.repository',
528 528 'hg.usergroup.create.false', 'hg.usergroup.create.true',
529 529 'hg.repogroup.create.false', 'hg.repogroup.create.true',
530 530 'hg.create.write_on_repogroup.false', 'hg.create.write_on_repogroup.true',
531 531 'hg.inherit_default_perms.false', 'hg.inherit_default_perms.true'
532 532 ])
533 533
534 534 # USER GROUPS comes first user group global permissions
535 535 user_perms_from_users_groups = Session().query(UserGroupToPerm)\
536 536 .options(joinedload(UserGroupToPerm.permission))\
537 537 .join((UserGroupMember, UserGroupToPerm.users_group_id ==
538 538 UserGroupMember.users_group_id))\
539 539 .filter(UserGroupMember.user_id == self.user_id)\
540 540 .order_by(UserGroupToPerm.users_group_id)\
541 541 .all()
542 542
543 543 # need to group here by groups since user can be in more than
544 544 # one group, so we get all groups
545 545 _explicit_grouped_perms = [
546 546 [x, list(y)] for x, y in
547 547 itertools.groupby(user_perms_from_users_groups,
548 548 lambda _x: _x.users_group)]
549 549
550 550 for gr, perms in _explicit_grouped_perms:
551 551 # since user can be in multiple groups iterate over them and
552 552 # select the lowest permissions first (more explicit)
553 553 # TODO(marcink): do this^^
554 554
555 555 # group doesn't inherit default permissions so we actually set them
556 556 if not gr.inherit_default_permissions:
557 557 # NEED TO IGNORE all previously set configurable permissions
558 558 # and replace them with explicitly set from this user
559 559 # group permissions
560 560 self.permissions_global = self.permissions_global.difference(
561 561 _configurable)
562 562 for perm in perms:
563 563 self.permissions_global.add(perm.permission.permission_name)
564 564
565 565 # user explicit global permissions
566 566 user_perms = Session().query(UserToPerm)\
567 567 .options(joinedload(UserToPerm.permission))\
568 568 .filter(UserToPerm.user_id == self.user_id).all()
569 569
570 570 if not self.inherit_default_permissions:
571 571 # NEED TO IGNORE all configurable permissions and
572 572 # replace them with explicitly set from this user permissions
573 573 self.permissions_global = self.permissions_global.difference(
574 574 _configurable)
575 575 for perm in user_perms:
576 576 self.permissions_global.add(perm.permission.permission_name)
577 577
578 578 def _calculate_default_permissions_repositories(self, user_inherit_object_permissions):
579 579 for perm in self.default_repo_perms:
580 580 r_k = perm.UserRepoToPerm.repository.repo_name
581 581 archived = perm.UserRepoToPerm.repository.archived
582 582 p = perm.Permission.permission_name
583 583 o = PermOrigin.REPO_DEFAULT
584 584 self.permissions_repositories[r_k] = p, o
585 585
586 586 # if we decide this user isn't inheriting permissions from
587 587 # default user we set him to .none so only explicit
588 588 # permissions work
589 589 if not user_inherit_object_permissions:
590 590 p = 'repository.none'
591 591 o = PermOrigin.REPO_DEFAULT_NO_INHERIT
592 592 self.permissions_repositories[r_k] = p, o
593 593
594 594 if perm.Repository.private and not (
595 595 perm.Repository.user_id == self.user_id):
596 596 # disable defaults for private repos,
597 597 p = 'repository.none'
598 598 o = PermOrigin.REPO_PRIVATE
599 599 self.permissions_repositories[r_k] = p, o
600 600
601 601 elif perm.Repository.user_id == self.user_id:
602 602 # set admin if owner
603 603 p = 'repository.admin'
604 604 o = PermOrigin.REPO_OWNER
605 605 self.permissions_repositories[r_k] = p, o
606 606
607 607 if self.user_is_admin:
608 608 p = 'repository.admin'
609 609 o = PermOrigin.SUPER_ADMIN
610 610 self.permissions_repositories[r_k] = p, o
611 611
612 612 # finally in case of archived repositories, we downgrade higher
613 613 # permissions to read
614 614 if archived:
615 615 current_perm = self.permissions_repositories[r_k]
616 616 if current_perm in ['repository.write', 'repository.admin']:
617 617 p = 'repository.read'
618 618 o = PermOrigin.ARCHIVED
619 619 self.permissions_repositories[r_k] = p, o
620 620
621 621 def _calculate_default_permissions_repository_branches(self, user_inherit_object_permissions):
622 622 for perm in self.default_branch_repo_perms:
623 623
624 624 r_k = perm.UserRepoToPerm.repository.repo_name
625 625 p = perm.Permission.permission_name
626 626 pattern = perm.UserToRepoBranchPermission.branch_pattern
627 627 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
628 628
629 629 if not self.explicit:
630 630 cur_perm = self.permissions_repository_branches.get(r_k)
631 631 if cur_perm:
632 632 cur_perm = cur_perm[pattern]
633 633 cur_perm = cur_perm or 'branch.none'
634 634
635 635 p = self._choose_permission(p, cur_perm)
636 636
637 637 # NOTE(marcink): register all pattern/perm instances in this
638 638 # special dict that aggregates entries
639 639 self.permissions_repository_branches[r_k] = pattern, p, o
640 640
641 641 def _calculate_default_permissions_repository_groups(self, user_inherit_object_permissions):
642 642 for perm in self.default_repo_groups_perms:
643 643 rg_k = perm.UserRepoGroupToPerm.group.group_name
644 644 p = perm.Permission.permission_name
645 645 o = PermOrigin.REPOGROUP_DEFAULT
646 646 self.permissions_repository_groups[rg_k] = p, o
647 647
648 648 # if we decide this user isn't inheriting permissions from default
649 649 # user we set him to .none so only explicit permissions work
650 650 if not user_inherit_object_permissions:
651 651 p = 'group.none'
652 652 o = PermOrigin.REPOGROUP_DEFAULT_NO_INHERIT
653 653 self.permissions_repository_groups[rg_k] = p, o
654 654
655 655 if perm.RepoGroup.user_id == self.user_id:
656 656 # set admin if owner
657 657 p = 'group.admin'
658 658 o = PermOrigin.REPOGROUP_OWNER
659 659 self.permissions_repository_groups[rg_k] = p, o
660 660
661 661 if self.user_is_admin:
662 662 p = 'group.admin'
663 663 o = PermOrigin.SUPER_ADMIN
664 664 self.permissions_repository_groups[rg_k] = p, o
665 665
666 666 def _calculate_default_permissions_user_groups(self, user_inherit_object_permissions):
667 667 for perm in self.default_user_group_perms:
668 668 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
669 669 p = perm.Permission.permission_name
670 670 o = PermOrigin.USERGROUP_DEFAULT
671 671 self.permissions_user_groups[u_k] = p, o
672 672
673 673 # if we decide this user isn't inheriting permissions from default
674 674 # user we set him to .none so only explicit permissions work
675 675 if not user_inherit_object_permissions:
676 676 p = 'usergroup.none'
677 677 o = PermOrigin.USERGROUP_DEFAULT_NO_INHERIT
678 678 self.permissions_user_groups[u_k] = p, o
679 679
680 680 if perm.UserGroup.user_id == self.user_id:
681 681 # set admin if owner
682 682 p = 'usergroup.admin'
683 683 o = PermOrigin.USERGROUP_OWNER
684 684 self.permissions_user_groups[u_k] = p, o
685 685
686 686 if self.user_is_admin:
687 687 p = 'usergroup.admin'
688 688 o = PermOrigin.SUPER_ADMIN
689 689 self.permissions_user_groups[u_k] = p, o
690 690
691 691 def _calculate_default_permissions(self):
692 692 """
693 693 Set default user permissions for repositories, repository branches,
694 694 repository groups, user groups taken from the default user.
695 695
696 696 Calculate inheritance of object permissions based on what we have now
697 697 in GLOBAL permissions. We check if .false is in GLOBAL since this is
698 698 explicitly set. Inherit is the opposite of .false being there.
699 699
700 700 .. note::
701 701
702 702 the syntax is little bit odd but what we need to check here is
703 703 the opposite of .false permission being in the list so even for
704 704 inconsistent state when both .true/.false is there
705 705 .false is more important
706 706
707 707 """
708 708 user_inherit_object_permissions = not ('hg.inherit_default_perms.false'
709 709 in self.permissions_global)
710 710
711 711 # default permissions inherited from `default` user permissions
712 712 self._calculate_default_permissions_repositories(
713 713 user_inherit_object_permissions)
714 714
715 715 self._calculate_default_permissions_repository_branches(
716 716 user_inherit_object_permissions)
717 717
718 718 self._calculate_default_permissions_repository_groups(
719 719 user_inherit_object_permissions)
720 720
721 721 self._calculate_default_permissions_user_groups(
722 722 user_inherit_object_permissions)
723 723
724 724 def _calculate_repository_permissions(self):
725 725 """
726 726 Repository access permissions for the current user.
727 727
728 728 Check if the user is part of user groups for this repository and
729 729 fill in the permission from it. `_choose_permission` decides of which
730 730 permission should be selected based on selected method.
731 731 """
732 732
733 733 # user group for repositories permissions
734 734 user_repo_perms_from_user_group = Permission\
735 735 .get_default_repo_perms_from_user_group(
736 736 self.user_id, self.scope_repo_id)
737 737
738 738 multiple_counter = collections.defaultdict(int)
739 739 for perm in user_repo_perms_from_user_group:
740 740 r_k = perm.UserGroupRepoToPerm.repository.repo_name
741 741 multiple_counter[r_k] += 1
742 742 p = perm.Permission.permission_name
743 743 o = PermOrigin.REPO_USERGROUP % perm.UserGroupRepoToPerm\
744 744 .users_group.users_group_name
745 745
746 746 if multiple_counter[r_k] > 1:
747 747 cur_perm = self.permissions_repositories[r_k]
748 748 p = self._choose_permission(p, cur_perm)
749 749
750 750 self.permissions_repositories[r_k] = p, o
751 751
752 752 if perm.Repository.user_id == self.user_id:
753 753 # set admin if owner
754 754 p = 'repository.admin'
755 755 o = PermOrigin.REPO_OWNER
756 756 self.permissions_repositories[r_k] = p, o
757 757
758 758 if self.user_is_admin:
759 759 p = 'repository.admin'
760 760 o = PermOrigin.SUPER_ADMIN
761 761 self.permissions_repositories[r_k] = p, o
762 762
763 763 # user explicit permissions for repositories, overrides any specified
764 764 # by the group permission
765 765 user_repo_perms = Permission.get_default_repo_perms(
766 766 self.user_id, self.scope_repo_id)
767 767 for perm in user_repo_perms:
768 768 r_k = perm.UserRepoToPerm.repository.repo_name
769 769 p = perm.Permission.permission_name
770 770 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
771 771
772 772 if not self.explicit:
773 773 cur_perm = self.permissions_repositories.get(
774 774 r_k, 'repository.none')
775 775 p = self._choose_permission(p, cur_perm)
776 776
777 777 self.permissions_repositories[r_k] = p, o
778 778
779 779 if perm.Repository.user_id == self.user_id:
780 780 # set admin if owner
781 781 p = 'repository.admin'
782 782 o = PermOrigin.REPO_OWNER
783 783 self.permissions_repositories[r_k] = p, o
784 784
785 785 if self.user_is_admin:
786 786 p = 'repository.admin'
787 787 o = PermOrigin.SUPER_ADMIN
788 788 self.permissions_repositories[r_k] = p, o
789 789
790 790 def _calculate_repository_branch_permissions(self):
791 791 # user group for repositories permissions
792 792 user_repo_branch_perms_from_user_group = Permission\
793 793 .get_default_repo_branch_perms_from_user_group(
794 794 self.user_id, self.scope_repo_id)
795 795
796 796 multiple_counter = collections.defaultdict(int)
797 797 for perm in user_repo_branch_perms_from_user_group:
798 798 r_k = perm.UserGroupRepoToPerm.repository.repo_name
799 799 p = perm.Permission.permission_name
800 800 pattern = perm.UserGroupToRepoBranchPermission.branch_pattern
801 801 o = PermOrigin.REPO_USERGROUP % perm.UserGroupRepoToPerm\
802 802 .users_group.users_group_name
803 803
804 804 multiple_counter[r_k] += 1
805 805 if multiple_counter[r_k] > 1:
806 806 cur_perm = self.permissions_repository_branches[r_k][pattern]
807 807 p = self._choose_permission(p, cur_perm)
808 808
809 809 self.permissions_repository_branches[r_k] = pattern, p, o
810 810
811 811 # user explicit branch permissions for repositories, overrides
812 812 # any specified by the group permission
813 813 user_repo_branch_perms = Permission.get_default_repo_branch_perms(
814 814 self.user_id, self.scope_repo_id)
815 815
816 816 for perm in user_repo_branch_perms:
817 817
818 818 r_k = perm.UserRepoToPerm.repository.repo_name
819 819 p = perm.Permission.permission_name
820 820 pattern = perm.UserToRepoBranchPermission.branch_pattern
821 821 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
822 822
823 823 if not self.explicit:
824 824 cur_perm = self.permissions_repository_branches.get(r_k)
825 825 if cur_perm:
826 826 cur_perm = cur_perm[pattern]
827 827 cur_perm = cur_perm or 'branch.none'
828 828 p = self._choose_permission(p, cur_perm)
829 829
830 830 # NOTE(marcink): register all pattern/perm instances in this
831 831 # special dict that aggregates entries
832 832 self.permissions_repository_branches[r_k] = pattern, p, o
833 833
834 834 def _calculate_repository_group_permissions(self):
835 835 """
836 836 Repository group permissions for the current user.
837 837
838 838 Check if the user is part of user groups for repository groups and
839 839 fill in the permissions from it. `_choose_permission` decides of which
840 840 permission should be selected based on selected method.
841 841 """
842 842 # user group for repo groups permissions
843 843 user_repo_group_perms_from_user_group = Permission\
844 844 .get_default_group_perms_from_user_group(
845 845 self.user_id, self.scope_repo_group_id)
846 846
847 847 multiple_counter = collections.defaultdict(int)
848 848 for perm in user_repo_group_perms_from_user_group:
849 849 rg_k = perm.UserGroupRepoGroupToPerm.group.group_name
850 850 multiple_counter[rg_k] += 1
851 851 o = PermOrigin.REPOGROUP_USERGROUP % perm.UserGroupRepoGroupToPerm\
852 852 .users_group.users_group_name
853 853 p = perm.Permission.permission_name
854 854
855 855 if multiple_counter[rg_k] > 1:
856 856 cur_perm = self.permissions_repository_groups[rg_k]
857 857 p = self._choose_permission(p, cur_perm)
858 858 self.permissions_repository_groups[rg_k] = p, o
859 859
860 860 if perm.RepoGroup.user_id == self.user_id:
861 861 # set admin if owner, even for member of other user group
862 862 p = 'group.admin'
863 863 o = PermOrigin.REPOGROUP_OWNER
864 864 self.permissions_repository_groups[rg_k] = p, o
865 865
866 866 if self.user_is_admin:
867 867 p = 'group.admin'
868 868 o = PermOrigin.SUPER_ADMIN
869 869 self.permissions_repository_groups[rg_k] = p, o
870 870
871 871 # user explicit permissions for repository groups
872 872 user_repo_groups_perms = Permission.get_default_group_perms(
873 873 self.user_id, self.scope_repo_group_id)
874 874 for perm in user_repo_groups_perms:
875 875 rg_k = perm.UserRepoGroupToPerm.group.group_name
876 876 o = PermOrigin.REPOGROUP_USER % perm.UserRepoGroupToPerm\
877 877 .user.username
878 878 p = perm.Permission.permission_name
879 879
880 880 if not self.explicit:
881 881 cur_perm = self.permissions_repository_groups.get(rg_k, 'group.none')
882 882 p = self._choose_permission(p, cur_perm)
883 883
884 884 self.permissions_repository_groups[rg_k] = p, o
885 885
886 886 if perm.RepoGroup.user_id == self.user_id:
887 887 # set admin if owner
888 888 p = 'group.admin'
889 889 o = PermOrigin.REPOGROUP_OWNER
890 890 self.permissions_repository_groups[rg_k] = p, o
891 891
892 892 if self.user_is_admin:
893 893 p = 'group.admin'
894 894 o = PermOrigin.SUPER_ADMIN
895 895 self.permissions_repository_groups[rg_k] = p, o
896 896
897 897 def _calculate_user_group_permissions(self):
898 898 """
899 899 User group permissions for the current user.
900 900 """
901 901 # user group for user group permissions
902 902 user_group_from_user_group = Permission\
903 903 .get_default_user_group_perms_from_user_group(
904 904 self.user_id, self.scope_user_group_id)
905 905
906 906 multiple_counter = collections.defaultdict(int)
907 907 for perm in user_group_from_user_group:
908 908 ug_k = perm.UserGroupUserGroupToPerm\
909 909 .target_user_group.users_group_name
910 910 multiple_counter[ug_k] += 1
911 911 o = PermOrigin.USERGROUP_USERGROUP % perm.UserGroupUserGroupToPerm\
912 912 .user_group.users_group_name
913 913 p = perm.Permission.permission_name
914 914
915 915 if multiple_counter[ug_k] > 1:
916 916 cur_perm = self.permissions_user_groups[ug_k]
917 917 p = self._choose_permission(p, cur_perm)
918 918
919 919 self.permissions_user_groups[ug_k] = p, o
920 920
921 921 if perm.UserGroup.user_id == self.user_id:
922 922 # set admin if owner, even for member of other user group
923 923 p = 'usergroup.admin'
924 924 o = PermOrigin.USERGROUP_OWNER
925 925 self.permissions_user_groups[ug_k] = p, o
926 926
927 927 if self.user_is_admin:
928 928 p = 'usergroup.admin'
929 929 o = PermOrigin.SUPER_ADMIN
930 930 self.permissions_user_groups[ug_k] = p, o
931 931
932 932 # user explicit permission for user groups
933 933 user_user_groups_perms = Permission.get_default_user_group_perms(
934 934 self.user_id, self.scope_user_group_id)
935 935 for perm in user_user_groups_perms:
936 936 ug_k = perm.UserUserGroupToPerm.user_group.users_group_name
937 937 o = PermOrigin.USERGROUP_USER % perm.UserUserGroupToPerm\
938 938 .user.username
939 939 p = perm.Permission.permission_name
940 940
941 941 if not self.explicit:
942 942 cur_perm = self.permissions_user_groups.get(ug_k, 'usergroup.none')
943 943 p = self._choose_permission(p, cur_perm)
944 944
945 945 self.permissions_user_groups[ug_k] = p, o
946 946
947 947 if perm.UserGroup.user_id == self.user_id:
948 948 # set admin if owner
949 949 p = 'usergroup.admin'
950 950 o = PermOrigin.USERGROUP_OWNER
951 951 self.permissions_user_groups[ug_k] = p, o
952 952
953 953 if self.user_is_admin:
954 954 p = 'usergroup.admin'
955 955 o = PermOrigin.SUPER_ADMIN
956 956 self.permissions_user_groups[ug_k] = p, o
957 957
958 958 def _choose_permission(self, new_perm, cur_perm):
959 959 new_perm_val = Permission.PERM_WEIGHTS[new_perm]
960 960 cur_perm_val = Permission.PERM_WEIGHTS[cur_perm]
961 961 if self.algo == 'higherwin':
962 962 if new_perm_val > cur_perm_val:
963 963 return new_perm
964 964 return cur_perm
965 965 elif self.algo == 'lowerwin':
966 966 if new_perm_val < cur_perm_val:
967 967 return new_perm
968 968 return cur_perm
969 969
970 970 def _permission_structure(self):
971 971 return {
972 972 'global': self.permissions_global,
973 973 'repositories': self.permissions_repositories,
974 974 'repository_branches': self.permissions_repository_branches,
975 975 'repositories_groups': self.permissions_repository_groups,
976 976 'user_groups': self.permissions_user_groups,
977 977 }
978 978
979 979
980 980 def allowed_auth_token_access(view_name, auth_token, whitelist=None):
981 981 """
982 982 Check if given controller_name is in whitelist of auth token access
983 983 """
984 984 if not whitelist:
985 985 from rhodecode import CONFIG
986 986 whitelist = aslist(
987 987 CONFIG.get('api_access_controllers_whitelist'), sep=',')
988 988 # backward compat translation
989 989 compat = {
990 990 # old controller, new VIEW
991 991 'ChangesetController:*': 'RepoCommitsView:*',
992 992 'ChangesetController:changeset_patch': 'RepoCommitsView:repo_commit_patch',
993 993 'ChangesetController:changeset_raw': 'RepoCommitsView:repo_commit_raw',
994 994 'FilesController:raw': 'RepoCommitsView:repo_commit_raw',
995 995 'FilesController:archivefile': 'RepoFilesView:repo_archivefile',
996 996 'GistsController:*': 'GistView:*',
997 997 }
998 998
999 999 log.debug(
1000 1000 'Allowed views for AUTH TOKEN access: %s', whitelist)
1001 1001 auth_token_access_valid = False
1002 1002
1003 1003 for entry in whitelist:
1004 1004 token_match = True
1005 1005 if entry in compat:
1006 1006 # translate from old Controllers to Pyramid Views
1007 1007 entry = compat[entry]
1008 1008
1009 1009 if '@' in entry:
1010 1010 # specific AuthToken
1011 1011 entry, allowed_token = entry.split('@', 1)
1012 1012 token_match = auth_token == allowed_token
1013 1013
1014 1014 if fnmatch.fnmatch(view_name, entry) and token_match:
1015 1015 auth_token_access_valid = True
1016 1016 break
1017 1017
1018 1018 if auth_token_access_valid:
1019 1019 log.debug('view: `%s` matches entry in whitelist: %s',
1020 1020 view_name, whitelist)
1021 1021
1022 1022 else:
1023 1023 msg = ('view: `%s` does *NOT* match any entry in whitelist: %s'
1024 1024 % (view_name, whitelist))
1025 1025 if auth_token:
1026 1026 # if we use auth token key and don't have access it's a warning
1027 1027 log.warning(msg)
1028 1028 else:
1029 1029 log.debug(msg)
1030 1030
1031 1031 return auth_token_access_valid
1032 1032
1033 1033
1034 1034 class AuthUser(object):
1035 1035 """
1036 1036 A simple object that handles all attributes of user in RhodeCode
1037 1037
1038 1038 It does lookup based on API key,given user, or user present in session
1039 1039 Then it fills all required information for such user. It also checks if
1040 1040 anonymous access is enabled and if so, it returns default user as logged in
1041 1041 """
1042 1042 GLOBAL_PERMS = [x[0] for x in Permission.PERMS]
1043 1043
1044 1044 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
1045 1045
1046 1046 self.user_id = user_id
1047 1047 self._api_key = api_key
1048 1048
1049 1049 self.api_key = None
1050 1050 self.username = username
1051 1051 self.ip_addr = ip_addr
1052 1052 self.name = ''
1053 1053 self.lastname = ''
1054 1054 self.first_name = ''
1055 1055 self.last_name = ''
1056 1056 self.email = ''
1057 1057 self.is_authenticated = False
1058 1058 self.admin = False
1059 1059 self.inherit_default_permissions = False
1060 1060 self.password = ''
1061 1061
1062 1062 self.anonymous_user = None # propagated on propagate_data
1063 1063 self.propagate_data()
1064 1064 self._instance = None
1065 1065 self._permissions_scoped_cache = {} # used to bind scoped calculation
1066 1066
1067 1067 @LazyProperty
1068 1068 def permissions(self):
1069 1069 return self.get_perms(user=self, cache=None)
1070 1070
1071 1071 @LazyProperty
1072 1072 def permissions_safe(self):
1073 1073 """
1074 1074 Filtered permissions excluding not allowed repositories
1075 1075 """
1076 1076 perms = self.get_perms(user=self, cache=None)
1077 1077
1078 1078 perms['repositories'] = {
1079 1079 k: v for k, v in perms['repositories'].items()
1080 1080 if v != 'repository.none'}
1081 1081 perms['repositories_groups'] = {
1082 1082 k: v for k, v in perms['repositories_groups'].items()
1083 1083 if v != 'group.none'}
1084 1084 perms['user_groups'] = {
1085 1085 k: v for k, v in perms['user_groups'].items()
1086 1086 if v != 'usergroup.none'}
1087 1087 perms['repository_branches'] = {
1088 1088 k: v for k, v in perms['repository_branches'].iteritems()
1089 1089 if v != 'branch.none'}
1090 1090 return perms
1091 1091
1092 1092 @LazyProperty
1093 1093 def permissions_full_details(self):
1094 1094 return self.get_perms(
1095 1095 user=self, cache=None, calculate_super_admin=True)
1096 1096
1097 1097 def permissions_with_scope(self, scope):
1098 1098 """
1099 1099 Call the get_perms function with scoped data. The scope in that function
1100 1100 narrows the SQL calls to the given ID of objects resulting in fetching
1101 1101 Just particular permission we want to obtain. If scope is an empty dict
1102 1102 then it basically narrows the scope to GLOBAL permissions only.
1103 1103
1104 1104 :param scope: dict
1105 1105 """
1106 1106 if 'repo_name' in scope:
1107 1107 obj = Repository.get_by_repo_name(scope['repo_name'])
1108 1108 if obj:
1109 1109 scope['repo_id'] = obj.repo_id
1110 1110 _scope = collections.OrderedDict()
1111 1111 _scope['repo_id'] = -1
1112 1112 _scope['user_group_id'] = -1
1113 1113 _scope['repo_group_id'] = -1
1114 1114
1115 1115 for k in sorted(scope.keys()):
1116 1116 _scope[k] = scope[k]
1117 1117
1118 1118 # store in cache to mimic how the @LazyProperty works,
1119 1119 # the difference here is that we use the unique key calculated
1120 1120 # from params and values
1121 1121 return self.get_perms(user=self, cache=None, scope=_scope)
1122 1122
1123 1123 def get_instance(self):
1124 1124 return User.get(self.user_id)
1125 1125
1126 1126 def propagate_data(self):
1127 1127 """
1128 1128 Fills in user data and propagates values to this instance. Maps fetched
1129 1129 user attributes to this class instance attributes
1130 1130 """
1131 1131 log.debug('AuthUser: starting data propagation for new potential user')
1132 1132 user_model = UserModel()
1133 1133 anon_user = self.anonymous_user = User.get_default_user(cache=True)
1134 1134 is_user_loaded = False
1135 1135
1136 1136 # lookup by userid
1137 1137 if self.user_id is not None and self.user_id != anon_user.user_id:
1138 1138 log.debug('Trying Auth User lookup by USER ID: `%s`', self.user_id)
1139 1139 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
1140 1140
1141 1141 # try go get user by api key
1142 1142 elif self._api_key and self._api_key != anon_user.api_key:
1143 log.debug('Trying Auth User lookup by API KEY: `%s`', self._api_key)
1143 log.debug('Trying Auth User lookup by API KEY: `...%s`', self._api_key[-4:])
1144 1144 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
1145 1145
1146 1146 # lookup by username
1147 1147 elif self.username:
1148 1148 log.debug('Trying Auth User lookup by USER NAME: `%s`', self.username)
1149 1149 is_user_loaded = user_model.fill_data(self, username=self.username)
1150 1150 else:
1151 1151 log.debug('No data in %s that could been used to log in', self)
1152 1152
1153 1153 if not is_user_loaded:
1154 1154 log.debug(
1155 1155 'Failed to load user. Fallback to default user %s', anon_user)
1156 1156 # if we cannot authenticate user try anonymous
1157 1157 if anon_user.active:
1158 1158 log.debug('default user is active, using it as a session user')
1159 1159 user_model.fill_data(self, user_id=anon_user.user_id)
1160 1160 # then we set this user is logged in
1161 1161 self.is_authenticated = True
1162 1162 else:
1163 1163 log.debug('default user is NOT active')
1164 1164 # in case of disabled anonymous user we reset some of the
1165 1165 # parameters so such user is "corrupted", skipping the fill_data
1166 1166 for attr in ['user_id', 'username', 'admin', 'active']:
1167 1167 setattr(self, attr, None)
1168 1168 self.is_authenticated = False
1169 1169
1170 1170 if not self.username:
1171 1171 self.username = 'None'
1172 1172
1173 1173 log.debug('AuthUser: propagated user is now %s', self)
1174 1174
1175 1175 def get_perms(self, user, scope=None, explicit=True, algo='higherwin',
1176 1176 calculate_super_admin=False, cache=None):
1177 1177 """
1178 1178 Fills user permission attribute with permissions taken from database
1179 1179 works for permissions given for repositories, and for permissions that
1180 1180 are granted to groups
1181 1181
1182 1182 :param user: instance of User object from database
1183 1183 :param explicit: In case there are permissions both for user and a group
1184 1184 that user is part of, explicit flag will defiine if user will
1185 1185 explicitly override permissions from group, if it's False it will
1186 1186 make decision based on the algo
1187 1187 :param algo: algorithm to decide what permission should be choose if
1188 1188 it's multiple defined, eg user in two different groups. It also
1189 1189 decides if explicit flag is turned off how to specify the permission
1190 1190 for case when user is in a group + have defined separate permission
1191 1191 :param calculate_super_admin: calculate permissions for super-admin in the
1192 1192 same way as for regular user without speedups
1193 1193 :param cache: Use caching for calculation, None = let the cache backend decide
1194 1194 """
1195 1195 user_id = user.user_id
1196 1196 user_is_admin = user.is_admin
1197 1197
1198 1198 # inheritance of global permissions like create repo/fork repo etc
1199 1199 user_inherit_default_permissions = user.inherit_default_permissions
1200 1200
1201 1201 cache_seconds = safe_int(
1202 1202 rhodecode.CONFIG.get('rc_cache.cache_perms.expiration_time'))
1203 1203
1204 1204 if cache is None:
1205 1205 # let the backend cache decide
1206 1206 cache_on = cache_seconds > 0
1207 1207 else:
1208 1208 cache_on = cache
1209 1209
1210 1210 log.debug(
1211 1211 'Computing PERMISSION tree for user %s scope `%s` '
1212 1212 'with caching: %s[TTL: %ss]', user, scope, cache_on, cache_seconds or 0)
1213 1213
1214 1214 cache_namespace_uid = 'cache_user_auth.{}'.format(user_id)
1215 1215 region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1216 1216
1217 1217 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
1218 1218 condition=cache_on)
1219 1219 def compute_perm_tree(cache_name,
1220 1220 user_id, scope, user_is_admin,user_inherit_default_permissions,
1221 1221 explicit, algo, calculate_super_admin):
1222 1222 return _cached_perms_data(
1223 1223 user_id, scope, user_is_admin, user_inherit_default_permissions,
1224 1224 explicit, algo, calculate_super_admin)
1225 1225
1226 1226 start = time.time()
1227 1227 result = compute_perm_tree(
1228 1228 'permissions', user_id, scope, user_is_admin,
1229 1229 user_inherit_default_permissions, explicit, algo,
1230 1230 calculate_super_admin)
1231 1231
1232 1232 result_repr = []
1233 1233 for k in result:
1234 1234 result_repr.append((k, len(result[k])))
1235 1235 total = time.time() - start
1236 1236 log.debug('PERMISSION tree for user %s computed in %.4fs: %s',
1237 1237 user, total, result_repr)
1238 1238
1239 1239 return result
1240 1240
1241 1241 @property
1242 1242 def is_default(self):
1243 1243 return self.username == User.DEFAULT_USER
1244 1244
1245 1245 @property
1246 1246 def is_admin(self):
1247 1247 return self.admin
1248 1248
1249 1249 @property
1250 1250 def is_user_object(self):
1251 1251 return self.user_id is not None
1252 1252
1253 1253 @property
1254 1254 def repositories_admin(self):
1255 1255 """
1256 1256 Returns list of repositories you're an admin of
1257 1257 """
1258 1258 return [
1259 1259 x[0] for x in self.permissions['repositories'].items()
1260 1260 if x[1] == 'repository.admin']
1261 1261
1262 1262 @property
1263 1263 def repository_groups_admin(self):
1264 1264 """
1265 1265 Returns list of repository groups you're an admin of
1266 1266 """
1267 1267 return [
1268 1268 x[0] for x in self.permissions['repositories_groups'].items()
1269 1269 if x[1] == 'group.admin']
1270 1270
1271 1271 @property
1272 1272 def user_groups_admin(self):
1273 1273 """
1274 1274 Returns list of user groups you're an admin of
1275 1275 """
1276 1276 return [
1277 1277 x[0] for x in self.permissions['user_groups'].items()
1278 1278 if x[1] == 'usergroup.admin']
1279 1279
1280 1280 def repo_acl_ids(self, perms=None, name_filter=None, cache=False):
1281 1281 """
1282 1282 Returns list of repository ids that user have access to based on given
1283 1283 perms. The cache flag should be only used in cases that are used for
1284 1284 display purposes, NOT IN ANY CASE for permission checks.
1285 1285 """
1286 1286 from rhodecode.model.scm import RepoList
1287 1287 if not perms:
1288 1288 perms = [
1289 1289 'repository.read', 'repository.write', 'repository.admin']
1290 1290
1291 1291 def _cached_repo_acl(user_id, perm_def, _name_filter):
1292 1292 qry = Repository.query()
1293 1293 if _name_filter:
1294 1294 ilike_expression = u'%{}%'.format(safe_unicode(_name_filter))
1295 1295 qry = qry.filter(
1296 1296 Repository.repo_name.ilike(ilike_expression))
1297 1297
1298 1298 return [x.repo_id for x in
1299 1299 RepoList(qry, perm_set=perm_def)]
1300 1300
1301 1301 return _cached_repo_acl(self.user_id, perms, name_filter)
1302 1302
1303 1303 def repo_group_acl_ids(self, perms=None, name_filter=None, cache=False):
1304 1304 """
1305 1305 Returns list of repository group ids that user have access to based on given
1306 1306 perms. The cache flag should be only used in cases that are used for
1307 1307 display purposes, NOT IN ANY CASE for permission checks.
1308 1308 """
1309 1309 from rhodecode.model.scm import RepoGroupList
1310 1310 if not perms:
1311 1311 perms = [
1312 1312 'group.read', 'group.write', 'group.admin']
1313 1313
1314 1314 def _cached_repo_group_acl(user_id, perm_def, _name_filter):
1315 1315 qry = RepoGroup.query()
1316 1316 if _name_filter:
1317 1317 ilike_expression = u'%{}%'.format(safe_unicode(_name_filter))
1318 1318 qry = qry.filter(
1319 1319 RepoGroup.group_name.ilike(ilike_expression))
1320 1320
1321 1321 return [x.group_id for x in
1322 1322 RepoGroupList(qry, perm_set=perm_def)]
1323 1323
1324 1324 return _cached_repo_group_acl(self.user_id, perms, name_filter)
1325 1325
1326 1326 def user_group_acl_ids(self, perms=None, name_filter=None, cache=False):
1327 1327 """
1328 1328 Returns list of user group ids that user have access to based on given
1329 1329 perms. The cache flag should be only used in cases that are used for
1330 1330 display purposes, NOT IN ANY CASE for permission checks.
1331 1331 """
1332 1332 from rhodecode.model.scm import UserGroupList
1333 1333 if not perms:
1334 1334 perms = [
1335 1335 'usergroup.read', 'usergroup.write', 'usergroup.admin']
1336 1336
1337 1337 def _cached_user_group_acl(user_id, perm_def, name_filter):
1338 1338 qry = UserGroup.query()
1339 1339 if name_filter:
1340 1340 ilike_expression = u'%{}%'.format(safe_unicode(name_filter))
1341 1341 qry = qry.filter(
1342 1342 UserGroup.users_group_name.ilike(ilike_expression))
1343 1343
1344 1344 return [x.users_group_id for x in
1345 1345 UserGroupList(qry, perm_set=perm_def)]
1346 1346
1347 1347 return _cached_user_group_acl(self.user_id, perms, name_filter)
1348 1348
1349 1349 @property
1350 1350 def ip_allowed(self):
1351 1351 """
1352 1352 Checks if ip_addr used in constructor is allowed from defined list of
1353 1353 allowed ip_addresses for user
1354 1354
1355 1355 :returns: boolean, True if ip is in allowed ip range
1356 1356 """
1357 1357 # check IP
1358 1358 inherit = self.inherit_default_permissions
1359 1359 return AuthUser.check_ip_allowed(self.user_id, self.ip_addr,
1360 1360 inherit_from_default=inherit)
1361 1361 @property
1362 1362 def personal_repo_group(self):
1363 1363 return RepoGroup.get_user_personal_repo_group(self.user_id)
1364 1364
1365 1365 @LazyProperty
1366 1366 def feed_token(self):
1367 1367 return self.get_instance().feed_token
1368 1368
1369 @LazyProperty
1370 def artifact_token(self):
1371 return self.get_instance().artifact_token
1372
1369 1373 @classmethod
1370 1374 def check_ip_allowed(cls, user_id, ip_addr, inherit_from_default):
1371 1375 allowed_ips = AuthUser.get_allowed_ips(
1372 1376 user_id, cache=True, inherit_from_default=inherit_from_default)
1373 1377 if check_ip_access(source_ip=ip_addr, allowed_ips=allowed_ips):
1374 1378 log.debug('IP:%s for user %s is in range of %s',
1375 1379 ip_addr, user_id, allowed_ips)
1376 1380 return True
1377 1381 else:
1378 1382 log.info('Access for IP:%s forbidden for user %s, '
1379 1383 'not in %s', ip_addr, user_id, allowed_ips)
1380 1384 return False
1381 1385
1382 1386 def get_branch_permissions(self, repo_name, perms=None):
1383 1387 perms = perms or self.permissions_with_scope({'repo_name': repo_name})
1384 1388 branch_perms = perms.get('repository_branches', {})
1385 1389 if not branch_perms:
1386 1390 return {}
1387 1391 repo_branch_perms = branch_perms.get(repo_name)
1388 1392 return repo_branch_perms or {}
1389 1393
1390 1394 def get_rule_and_branch_permission(self, repo_name, branch_name):
1391 1395 """
1392 1396 Check if this AuthUser has defined any permissions for branches. If any of
1393 1397 the rules match in order, we return the matching permissions
1394 1398 """
1395 1399
1396 1400 rule = default_perm = ''
1397 1401
1398 1402 repo_branch_perms = self.get_branch_permissions(repo_name=repo_name)
1399 1403 if not repo_branch_perms:
1400 1404 return rule, default_perm
1401 1405
1402 1406 # now calculate the permissions
1403 1407 for pattern, branch_perm in repo_branch_perms.items():
1404 1408 if fnmatch.fnmatch(branch_name, pattern):
1405 1409 rule = '`{}`=>{}'.format(pattern, branch_perm)
1406 1410 return rule, branch_perm
1407 1411
1408 1412 return rule, default_perm
1409 1413
1410 1414 def __repr__(self):
1411 1415 return "<AuthUser('id:%s[%s] ip:%s auth:%s')>"\
1412 1416 % (self.user_id, self.username, self.ip_addr, self.is_authenticated)
1413 1417
1414 1418 def set_authenticated(self, authenticated=True):
1415 1419 if self.user_id != self.anonymous_user.user_id:
1416 1420 self.is_authenticated = authenticated
1417 1421
1418 1422 def get_cookie_store(self):
1419 1423 return {
1420 1424 'username': self.username,
1421 1425 'password': md5(self.password or ''),
1422 1426 'user_id': self.user_id,
1423 1427 'is_authenticated': self.is_authenticated
1424 1428 }
1425 1429
1426 1430 @classmethod
1427 1431 def from_cookie_store(cls, cookie_store):
1428 1432 """
1429 1433 Creates AuthUser from a cookie store
1430 1434
1431 1435 :param cls:
1432 1436 :param cookie_store:
1433 1437 """
1434 1438 user_id = cookie_store.get('user_id')
1435 1439 username = cookie_store.get('username')
1436 1440 api_key = cookie_store.get('api_key')
1437 1441 return AuthUser(user_id, api_key, username)
1438 1442
1439 1443 @classmethod
1440 1444 def get_allowed_ips(cls, user_id, cache=False, inherit_from_default=False):
1441 1445 _set = set()
1442 1446
1443 1447 if inherit_from_default:
1444 1448 def_user_id = User.get_default_user(cache=True).user_id
1445 1449 default_ips = UserIpMap.query().filter(UserIpMap.user_id == def_user_id)
1446 1450 if cache:
1447 1451 default_ips = default_ips.options(
1448 1452 FromCache("sql_cache_short", "get_user_ips_default"))
1449 1453
1450 1454 # populate from default user
1451 1455 for ip in default_ips:
1452 1456 try:
1453 1457 _set.add(ip.ip_addr)
1454 1458 except ObjectDeletedError:
1455 1459 # since we use heavy caching sometimes it happens that
1456 1460 # we get deleted objects here, we just skip them
1457 1461 pass
1458 1462
1459 1463 # NOTE:(marcink) we don't want to load any rules for empty
1460 1464 # user_id which is the case of access of non logged users when anonymous
1461 1465 # access is disabled
1462 1466 user_ips = []
1463 1467 if user_id:
1464 1468 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
1465 1469 if cache:
1466 1470 user_ips = user_ips.options(
1467 1471 FromCache("sql_cache_short", "get_user_ips_%s" % user_id))
1468 1472
1469 1473 for ip in user_ips:
1470 1474 try:
1471 1475 _set.add(ip.ip_addr)
1472 1476 except ObjectDeletedError:
1473 1477 # since we use heavy caching sometimes it happens that we get
1474 1478 # deleted objects here, we just skip them
1475 1479 pass
1476 1480 return _set or {ip for ip in ['0.0.0.0/0', '::/0']}
1477 1481
1478 1482
1479 1483 def set_available_permissions(settings):
1480 1484 """
1481 1485 This function will propagate pyramid settings with all available defined
1482 1486 permission given in db. We don't want to check each time from db for new
1483 1487 permissions since adding a new permission also requires application restart
1484 1488 ie. to decorate new views with the newly created permission
1485 1489
1486 1490 :param settings: current pyramid registry.settings
1487 1491
1488 1492 """
1489 1493 log.debug('auth: getting information about all available permissions')
1490 1494 try:
1491 1495 sa = meta.Session
1492 1496 all_perms = sa.query(Permission).all()
1493 1497 settings.setdefault('available_permissions',
1494 1498 [x.permission_name for x in all_perms])
1495 1499 log.debug('auth: set available permissions')
1496 1500 except Exception:
1497 1501 log.exception('Failed to fetch permissions from the database.')
1498 1502 raise
1499 1503
1500 1504
1501 1505 def get_csrf_token(session, force_new=False, save_if_missing=True):
1502 1506 """
1503 1507 Return the current authentication token, creating one if one doesn't
1504 1508 already exist and the save_if_missing flag is present.
1505 1509
1506 1510 :param session: pass in the pyramid session, else we use the global ones
1507 1511 :param force_new: force to re-generate the token and store it in session
1508 1512 :param save_if_missing: save the newly generated token if it's missing in
1509 1513 session
1510 1514 """
1511 1515 # NOTE(marcink): probably should be replaced with below one from pyramid 1.9
1512 1516 # from pyramid.csrf import get_csrf_token
1513 1517
1514 1518 if (csrf_token_key not in session and save_if_missing) or force_new:
1515 1519 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
1516 1520 session[csrf_token_key] = token
1517 1521 if hasattr(session, 'save'):
1518 1522 session.save()
1519 1523 return session.get(csrf_token_key)
1520 1524
1521 1525
1522 1526 def get_request(perm_class_instance):
1523 1527 from pyramid.threadlocal import get_current_request
1524 1528 pyramid_request = get_current_request()
1525 1529 return pyramid_request
1526 1530
1527 1531
1528 1532 # CHECK DECORATORS
1529 1533 class CSRFRequired(object):
1530 1534 """
1531 1535 Decorator for authenticating a form
1532 1536
1533 1537 This decorator uses an authorization token stored in the client's
1534 1538 session for prevention of certain Cross-site request forgery (CSRF)
1535 1539 attacks (See
1536 1540 http://en.wikipedia.org/wiki/Cross-site_request_forgery for more
1537 1541 information).
1538 1542
1539 1543 For use with the ``webhelpers.secure_form`` helper functions.
1540 1544
1541 1545 """
1542 1546 def __init__(self, token=csrf_token_key, header='X-CSRF-Token',
1543 1547 except_methods=None):
1544 1548 self.token = token
1545 1549 self.header = header
1546 1550 self.except_methods = except_methods or []
1547 1551
1548 1552 def __call__(self, func):
1549 1553 return get_cython_compat_decorator(self.__wrapper, func)
1550 1554
1551 1555 def _get_csrf(self, _request):
1552 1556 return _request.POST.get(self.token, _request.headers.get(self.header))
1553 1557
1554 1558 def check_csrf(self, _request, cur_token):
1555 1559 supplied_token = self._get_csrf(_request)
1556 1560 return supplied_token and supplied_token == cur_token
1557 1561
1558 1562 def _get_request(self):
1559 1563 return get_request(self)
1560 1564
1561 1565 def __wrapper(self, func, *fargs, **fkwargs):
1562 1566 request = self._get_request()
1563 1567
1564 1568 if request.method in self.except_methods:
1565 1569 return func(*fargs, **fkwargs)
1566 1570
1567 1571 cur_token = get_csrf_token(request.session, save_if_missing=False)
1568 1572 if self.check_csrf(request, cur_token):
1569 1573 if request.POST.get(self.token):
1570 1574 del request.POST[self.token]
1571 1575 return func(*fargs, **fkwargs)
1572 1576 else:
1573 1577 reason = 'token-missing'
1574 1578 supplied_token = self._get_csrf(request)
1575 1579 if supplied_token and cur_token != supplied_token:
1576 1580 reason = 'token-mismatch [%s:%s]' % (
1577 1581 cur_token or ''[:6], supplied_token or ''[:6])
1578 1582
1579 1583 csrf_message = \
1580 1584 ("Cross-site request forgery detected, request denied. See "
1581 1585 "http://en.wikipedia.org/wiki/Cross-site_request_forgery for "
1582 1586 "more information.")
1583 1587 log.warn('Cross-site request forgery detected, request %r DENIED: %s '
1584 1588 'REMOTE_ADDR:%s, HEADERS:%s' % (
1585 1589 request, reason, request.remote_addr, request.headers))
1586 1590
1587 1591 raise HTTPForbidden(explanation=csrf_message)
1588 1592
1589 1593
1590 1594 class LoginRequired(object):
1591 1595 """
1592 1596 Must be logged in to execute this function else
1593 1597 redirect to login page
1594 1598
1595 1599 :param api_access: if enabled this checks only for valid auth token
1596 1600 and grants access based on valid token
1597 1601 """
1598 1602 def __init__(self, auth_token_access=None):
1599 1603 self.auth_token_access = auth_token_access
1604 if self.auth_token_access:
1605 valid_type = set(auth_token_access).intersection(set(UserApiKeys.ROLES))
1606 if not valid_type:
1607 raise ValueError('auth_token_access must be on of {}, got {}'.format(
1608 UserApiKeys.ROLES, auth_token_access))
1600 1609
1601 1610 def __call__(self, func):
1602 1611 return get_cython_compat_decorator(self.__wrapper, func)
1603 1612
1604 1613 def _get_request(self):
1605 1614 return get_request(self)
1606 1615
1607 1616 def __wrapper(self, func, *fargs, **fkwargs):
1608 1617 from rhodecode.lib import helpers as h
1609 1618 cls = fargs[0]
1610 1619 user = cls._rhodecode_user
1611 1620 request = self._get_request()
1612 1621 _ = request.translate
1613 1622
1614 1623 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
1615 1624 log.debug('Starting login restriction checks for user: %s', user)
1616 1625 # check if our IP is allowed
1617 1626 ip_access_valid = True
1618 1627 if not user.ip_allowed:
1619 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr,))),
1628 h.flash(h.literal(_('IP {} not allowed'.format(user.ip_addr))),
1620 1629 category='warning')
1621 1630 ip_access_valid = False
1622 1631
1623 # check if we used an APIKEY and it's a valid one
1632 # we used stored token that is extract from GET or URL param (if any)
1633 _auth_token = request.user_auth_token
1634
1635 # check if we used an AUTH_TOKEN and it's a valid one
1624 1636 # defined white-list of controllers which API access will be enabled
1625 _auth_token = request.GET.get(
1626 'auth_token', '') or request.GET.get('api_key', '')
1637 whitelist = None
1638 if self.auth_token_access:
1639 # since this location is allowed by @LoginRequired decorator it's our
1640 # only whitelist
1641 whitelist = [loc]
1627 1642 auth_token_access_valid = allowed_auth_token_access(
1628 loc, auth_token=_auth_token)
1643 loc, whitelist=whitelist, auth_token=_auth_token)
1629 1644
1630 1645 # explicit controller is enabled or API is in our whitelist
1631 if self.auth_token_access or auth_token_access_valid:
1646 if auth_token_access_valid:
1632 1647 log.debug('Checking AUTH TOKEN access for %s', cls)
1633 1648 db_user = user.get_instance()
1634 1649
1635 1650 if db_user:
1636 1651 if self.auth_token_access:
1637 1652 roles = self.auth_token_access
1638 1653 else:
1639 1654 roles = [UserApiKeys.ROLE_HTTP]
1655 log.debug('AUTH TOKEN: checking auth for user %s and roles %s',
1656 db_user, roles)
1640 1657 token_match = db_user.authenticate_by_token(
1641 1658 _auth_token, roles=roles)
1642 1659 else:
1643 1660 log.debug('Unable to fetch db instance for auth user: %s', user)
1644 1661 token_match = False
1645 1662
1646 1663 if _auth_token and token_match:
1647 1664 auth_token_access_valid = True
1648 1665 log.debug('AUTH TOKEN ****%s is VALID', _auth_token[-4:])
1649 1666 else:
1650 1667 auth_token_access_valid = False
1651 1668 if not _auth_token:
1652 1669 log.debug("AUTH TOKEN *NOT* present in request")
1653 1670 else:
1654 1671 log.warning("AUTH TOKEN ****%s *NOT* valid", _auth_token[-4:])
1655 1672
1656 1673 log.debug('Checking if %s is authenticated @ %s', user.username, loc)
1657 1674 reason = 'RHODECODE_AUTH' if user.is_authenticated \
1658 1675 else 'AUTH_TOKEN_AUTH'
1659 1676
1660 1677 if ip_access_valid and (
1661 1678 user.is_authenticated or auth_token_access_valid):
1662 1679 log.info('user %s authenticating with:%s IS authenticated on func %s',
1663 1680 user, reason, loc)
1664 1681
1665 1682 return func(*fargs, **fkwargs)
1666 1683 else:
1667 1684 log.warning(
1668 1685 'user %s authenticating with:%s NOT authenticated on '
1669 1686 'func: %s: IP_ACCESS:%s AUTH_TOKEN_ACCESS:%s',
1670 1687 user, reason, loc, ip_access_valid, auth_token_access_valid)
1671 1688 # we preserve the get PARAM
1672 1689 came_from = get_came_from(request)
1673 1690
1674 1691 log.debug('redirecting to login page with %s', came_from)
1675 1692 raise HTTPFound(
1676 1693 h.route_path('login', _query={'came_from': came_from}))
1677 1694
1678 1695
1679 1696 class NotAnonymous(object):
1680 1697 """
1681 1698 Must be logged in to execute this function else
1682 1699 redirect to login page
1683 1700 """
1684 1701
1685 1702 def __call__(self, func):
1686 1703 return get_cython_compat_decorator(self.__wrapper, func)
1687 1704
1688 1705 def _get_request(self):
1689 1706 return get_request(self)
1690 1707
1691 1708 def __wrapper(self, func, *fargs, **fkwargs):
1692 1709 import rhodecode.lib.helpers as h
1693 1710 cls = fargs[0]
1694 1711 self.user = cls._rhodecode_user
1695 1712 request = self._get_request()
1696 1713 _ = request.translate
1697 1714 log.debug('Checking if user is not anonymous @%s', cls)
1698 1715
1699 1716 anonymous = self.user.username == User.DEFAULT_USER
1700 1717
1701 1718 if anonymous:
1702 1719 came_from = get_came_from(request)
1703 1720 h.flash(_('You need to be a registered user to '
1704 1721 'perform this action'),
1705 1722 category='warning')
1706 1723 raise HTTPFound(
1707 1724 h.route_path('login', _query={'came_from': came_from}))
1708 1725 else:
1709 1726 return func(*fargs, **fkwargs)
1710 1727
1711 1728
1712 1729 class PermsDecorator(object):
1713 1730 """
1714 1731 Base class for controller decorators, we extract the current user from
1715 1732 the class itself, which has it stored in base controllers
1716 1733 """
1717 1734
1718 1735 def __init__(self, *required_perms):
1719 1736 self.required_perms = set(required_perms)
1720 1737
1721 1738 def __call__(self, func):
1722 1739 return get_cython_compat_decorator(self.__wrapper, func)
1723 1740
1724 1741 def _get_request(self):
1725 1742 return get_request(self)
1726 1743
1727 1744 def __wrapper(self, func, *fargs, **fkwargs):
1728 1745 import rhodecode.lib.helpers as h
1729 1746 cls = fargs[0]
1730 1747 _user = cls._rhodecode_user
1731 1748 request = self._get_request()
1732 1749 _ = request.translate
1733 1750
1734 1751 log.debug('checking %s permissions %s for %s %s',
1735 1752 self.__class__.__name__, self.required_perms, cls, _user)
1736 1753
1737 1754 if self.check_permissions(_user):
1738 1755 log.debug('Permission granted for %s %s', cls, _user)
1739 1756 return func(*fargs, **fkwargs)
1740 1757
1741 1758 else:
1742 1759 log.debug('Permission denied for %s %s', cls, _user)
1743 1760 anonymous = _user.username == User.DEFAULT_USER
1744 1761
1745 1762 if anonymous:
1746 1763 came_from = get_came_from(self._get_request())
1747 1764 h.flash(_('You need to be signed in to view this page'),
1748 1765 category='warning')
1749 1766 raise HTTPFound(
1750 1767 h.route_path('login', _query={'came_from': came_from}))
1751 1768
1752 1769 else:
1753 1770 # redirect with 404 to prevent resource discovery
1754 1771 raise HTTPNotFound()
1755 1772
1756 1773 def check_permissions(self, user):
1757 1774 """Dummy function for overriding"""
1758 1775 raise NotImplementedError(
1759 1776 'You have to write this function in child class')
1760 1777
1761 1778
1762 1779 class HasPermissionAllDecorator(PermsDecorator):
1763 1780 """
1764 1781 Checks for access permission for all given predicates. All of them
1765 1782 have to be meet in order to fulfill the request
1766 1783 """
1767 1784
1768 1785 def check_permissions(self, user):
1769 1786 perms = user.permissions_with_scope({})
1770 1787 if self.required_perms.issubset(perms['global']):
1771 1788 return True
1772 1789 return False
1773 1790
1774 1791
1775 1792 class HasPermissionAnyDecorator(PermsDecorator):
1776 1793 """
1777 1794 Checks for access permission for any of given predicates. In order to
1778 1795 fulfill the request any of predicates must be meet
1779 1796 """
1780 1797
1781 1798 def check_permissions(self, user):
1782 1799 perms = user.permissions_with_scope({})
1783 1800 if self.required_perms.intersection(perms['global']):
1784 1801 return True
1785 1802 return False
1786 1803
1787 1804
1788 1805 class HasRepoPermissionAllDecorator(PermsDecorator):
1789 1806 """
1790 1807 Checks for access permission for all given predicates for specific
1791 1808 repository. All of them have to be meet in order to fulfill the request
1792 1809 """
1793 1810 def _get_repo_name(self):
1794 1811 _request = self._get_request()
1795 1812 return get_repo_slug(_request)
1796 1813
1797 1814 def check_permissions(self, user):
1798 1815 perms = user.permissions
1799 1816 repo_name = self._get_repo_name()
1800 1817
1801 1818 try:
1802 1819 user_perms = {perms['repositories'][repo_name]}
1803 1820 except KeyError:
1804 1821 log.debug('cannot locate repo with name: `%s` in permissions defs',
1805 1822 repo_name)
1806 1823 return False
1807 1824
1808 1825 log.debug('checking `%s` permissions for repo `%s`',
1809 1826 user_perms, repo_name)
1810 1827 if self.required_perms.issubset(user_perms):
1811 1828 return True
1812 1829 return False
1813 1830
1814 1831
1815 1832 class HasRepoPermissionAnyDecorator(PermsDecorator):
1816 1833 """
1817 1834 Checks for access permission for any of given predicates for specific
1818 1835 repository. In order to fulfill the request any of predicates must be meet
1819 1836 """
1820 1837 def _get_repo_name(self):
1821 1838 _request = self._get_request()
1822 1839 return get_repo_slug(_request)
1823 1840
1824 1841 def check_permissions(self, user):
1825 1842 perms = user.permissions
1826 1843 repo_name = self._get_repo_name()
1827 1844
1828 1845 try:
1829 1846 user_perms = {perms['repositories'][repo_name]}
1830 1847 except KeyError:
1831 1848 log.debug(
1832 1849 'cannot locate repo with name: `%s` in permissions defs',
1833 1850 repo_name)
1834 1851 return False
1835 1852
1836 1853 log.debug('checking `%s` permissions for repo `%s`',
1837 1854 user_perms, repo_name)
1838 1855 if self.required_perms.intersection(user_perms):
1839 1856 return True
1840 1857 return False
1841 1858
1842 1859
1843 1860 class HasRepoGroupPermissionAllDecorator(PermsDecorator):
1844 1861 """
1845 1862 Checks for access permission for all given predicates for specific
1846 1863 repository group. All of them have to be meet in order to
1847 1864 fulfill the request
1848 1865 """
1849 1866 def _get_repo_group_name(self):
1850 1867 _request = self._get_request()
1851 1868 return get_repo_group_slug(_request)
1852 1869
1853 1870 def check_permissions(self, user):
1854 1871 perms = user.permissions
1855 1872 group_name = self._get_repo_group_name()
1856 1873 try:
1857 1874 user_perms = {perms['repositories_groups'][group_name]}
1858 1875 except KeyError:
1859 1876 log.debug(
1860 1877 'cannot locate repo group with name: `%s` in permissions defs',
1861 1878 group_name)
1862 1879 return False
1863 1880
1864 1881 log.debug('checking `%s` permissions for repo group `%s`',
1865 1882 user_perms, group_name)
1866 1883 if self.required_perms.issubset(user_perms):
1867 1884 return True
1868 1885 return False
1869 1886
1870 1887
1871 1888 class HasRepoGroupPermissionAnyDecorator(PermsDecorator):
1872 1889 """
1873 1890 Checks for access permission for any of given predicates for specific
1874 1891 repository group. In order to fulfill the request any
1875 1892 of predicates must be met
1876 1893 """
1877 1894 def _get_repo_group_name(self):
1878 1895 _request = self._get_request()
1879 1896 return get_repo_group_slug(_request)
1880 1897
1881 1898 def check_permissions(self, user):
1882 1899 perms = user.permissions
1883 1900 group_name = self._get_repo_group_name()
1884 1901
1885 1902 try:
1886 1903 user_perms = {perms['repositories_groups'][group_name]}
1887 1904 except KeyError:
1888 1905 log.debug(
1889 1906 'cannot locate repo group with name: `%s` in permissions defs',
1890 1907 group_name)
1891 1908 return False
1892 1909
1893 1910 log.debug('checking `%s` permissions for repo group `%s`',
1894 1911 user_perms, group_name)
1895 1912 if self.required_perms.intersection(user_perms):
1896 1913 return True
1897 1914 return False
1898 1915
1899 1916
1900 1917 class HasUserGroupPermissionAllDecorator(PermsDecorator):
1901 1918 """
1902 1919 Checks for access permission for all given predicates for specific
1903 1920 user group. All of them have to be meet in order to fulfill the request
1904 1921 """
1905 1922 def _get_user_group_name(self):
1906 1923 _request = self._get_request()
1907 1924 return get_user_group_slug(_request)
1908 1925
1909 1926 def check_permissions(self, user):
1910 1927 perms = user.permissions
1911 1928 group_name = self._get_user_group_name()
1912 1929 try:
1913 1930 user_perms = {perms['user_groups'][group_name]}
1914 1931 except KeyError:
1915 1932 return False
1916 1933
1917 1934 if self.required_perms.issubset(user_perms):
1918 1935 return True
1919 1936 return False
1920 1937
1921 1938
1922 1939 class HasUserGroupPermissionAnyDecorator(PermsDecorator):
1923 1940 """
1924 1941 Checks for access permission for any of given predicates for specific
1925 1942 user group. In order to fulfill the request any of predicates must be meet
1926 1943 """
1927 1944 def _get_user_group_name(self):
1928 1945 _request = self._get_request()
1929 1946 return get_user_group_slug(_request)
1930 1947
1931 1948 def check_permissions(self, user):
1932 1949 perms = user.permissions
1933 1950 group_name = self._get_user_group_name()
1934 1951 try:
1935 1952 user_perms = {perms['user_groups'][group_name]}
1936 1953 except KeyError:
1937 1954 return False
1938 1955
1939 1956 if self.required_perms.intersection(user_perms):
1940 1957 return True
1941 1958 return False
1942 1959
1943 1960
1944 1961 # CHECK FUNCTIONS
1945 1962 class PermsFunction(object):
1946 1963 """Base function for other check functions"""
1947 1964
1948 1965 def __init__(self, *perms):
1949 1966 self.required_perms = set(perms)
1950 1967 self.repo_name = None
1951 1968 self.repo_group_name = None
1952 1969 self.user_group_name = None
1953 1970
1954 1971 def __bool__(self):
1955 1972 frame = inspect.currentframe()
1956 1973 stack_trace = traceback.format_stack(frame)
1957 1974 log.error('Checking bool value on a class instance of perm '
1958 1975 'function is not allowed: %s', ''.join(stack_trace))
1959 1976 # rather than throwing errors, here we always return False so if by
1960 1977 # accident someone checks truth for just an instance it will always end
1961 1978 # up in returning False
1962 1979 return False
1963 1980 __nonzero__ = __bool__
1964 1981
1965 1982 def __call__(self, check_location='', user=None):
1966 1983 if not user:
1967 1984 log.debug('Using user attribute from global request')
1968 1985 request = self._get_request()
1969 1986 user = request.user
1970 1987
1971 1988 # init auth user if not already given
1972 1989 if not isinstance(user, AuthUser):
1973 1990 log.debug('Wrapping user %s into AuthUser', user)
1974 1991 user = AuthUser(user.user_id)
1975 1992
1976 1993 cls_name = self.__class__.__name__
1977 1994 check_scope = self._get_check_scope(cls_name)
1978 1995 check_location = check_location or 'unspecified location'
1979 1996
1980 1997 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
1981 1998 self.required_perms, user, check_scope, check_location)
1982 1999 if not user:
1983 2000 log.warning('Empty user given for permission check')
1984 2001 return False
1985 2002
1986 2003 if self.check_permissions(user):
1987 2004 log.debug('Permission to repo:`%s` GRANTED for user:`%s` @ %s',
1988 2005 check_scope, user, check_location)
1989 2006 return True
1990 2007
1991 2008 else:
1992 2009 log.debug('Permission to repo:`%s` DENIED for user:`%s` @ %s',
1993 2010 check_scope, user, check_location)
1994 2011 return False
1995 2012
1996 2013 def _get_request(self):
1997 2014 return get_request(self)
1998 2015
1999 2016 def _get_check_scope(self, cls_name):
2000 2017 return {
2001 2018 'HasPermissionAll': 'GLOBAL',
2002 2019 'HasPermissionAny': 'GLOBAL',
2003 2020 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
2004 2021 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
2005 2022 'HasRepoGroupPermissionAll': 'repo_group:%s' % self.repo_group_name,
2006 2023 'HasRepoGroupPermissionAny': 'repo_group:%s' % self.repo_group_name,
2007 2024 'HasUserGroupPermissionAll': 'user_group:%s' % self.user_group_name,
2008 2025 'HasUserGroupPermissionAny': 'user_group:%s' % self.user_group_name,
2009 2026 }.get(cls_name, '?:%s' % cls_name)
2010 2027
2011 2028 def check_permissions(self, user):
2012 2029 """Dummy function for overriding"""
2013 2030 raise Exception('You have to write this function in child class')
2014 2031
2015 2032
2016 2033 class HasPermissionAll(PermsFunction):
2017 2034 def check_permissions(self, user):
2018 2035 perms = user.permissions_with_scope({})
2019 2036 if self.required_perms.issubset(perms.get('global')):
2020 2037 return True
2021 2038 return False
2022 2039
2023 2040
2024 2041 class HasPermissionAny(PermsFunction):
2025 2042 def check_permissions(self, user):
2026 2043 perms = user.permissions_with_scope({})
2027 2044 if self.required_perms.intersection(perms.get('global')):
2028 2045 return True
2029 2046 return False
2030 2047
2031 2048
2032 2049 class HasRepoPermissionAll(PermsFunction):
2033 2050 def __call__(self, repo_name=None, check_location='', user=None):
2034 2051 self.repo_name = repo_name
2035 2052 return super(HasRepoPermissionAll, self).__call__(check_location, user)
2036 2053
2037 2054 def _get_repo_name(self):
2038 2055 if not self.repo_name:
2039 2056 _request = self._get_request()
2040 2057 self.repo_name = get_repo_slug(_request)
2041 2058 return self.repo_name
2042 2059
2043 2060 def check_permissions(self, user):
2044 2061 self.repo_name = self._get_repo_name()
2045 2062 perms = user.permissions
2046 2063 try:
2047 2064 user_perms = {perms['repositories'][self.repo_name]}
2048 2065 except KeyError:
2049 2066 return False
2050 2067 if self.required_perms.issubset(user_perms):
2051 2068 return True
2052 2069 return False
2053 2070
2054 2071
2055 2072 class HasRepoPermissionAny(PermsFunction):
2056 2073 def __call__(self, repo_name=None, check_location='', user=None):
2057 2074 self.repo_name = repo_name
2058 2075 return super(HasRepoPermissionAny, self).__call__(check_location, user)
2059 2076
2060 2077 def _get_repo_name(self):
2061 2078 if not self.repo_name:
2062 2079 _request = self._get_request()
2063 2080 self.repo_name = get_repo_slug(_request)
2064 2081 return self.repo_name
2065 2082
2066 2083 def check_permissions(self, user):
2067 2084 self.repo_name = self._get_repo_name()
2068 2085 perms = user.permissions
2069 2086 try:
2070 2087 user_perms = {perms['repositories'][self.repo_name]}
2071 2088 except KeyError:
2072 2089 return False
2073 2090 if self.required_perms.intersection(user_perms):
2074 2091 return True
2075 2092 return False
2076 2093
2077 2094
2078 2095 class HasRepoGroupPermissionAny(PermsFunction):
2079 2096 def __call__(self, group_name=None, check_location='', user=None):
2080 2097 self.repo_group_name = group_name
2081 2098 return super(HasRepoGroupPermissionAny, self).__call__(check_location, user)
2082 2099
2083 2100 def check_permissions(self, user):
2084 2101 perms = user.permissions
2085 2102 try:
2086 2103 user_perms = {perms['repositories_groups'][self.repo_group_name]}
2087 2104 except KeyError:
2088 2105 return False
2089 2106 if self.required_perms.intersection(user_perms):
2090 2107 return True
2091 2108 return False
2092 2109
2093 2110
2094 2111 class HasRepoGroupPermissionAll(PermsFunction):
2095 2112 def __call__(self, group_name=None, check_location='', user=None):
2096 2113 self.repo_group_name = group_name
2097 2114 return super(HasRepoGroupPermissionAll, self).__call__(check_location, user)
2098 2115
2099 2116 def check_permissions(self, user):
2100 2117 perms = user.permissions
2101 2118 try:
2102 2119 user_perms = {perms['repositories_groups'][self.repo_group_name]}
2103 2120 except KeyError:
2104 2121 return False
2105 2122 if self.required_perms.issubset(user_perms):
2106 2123 return True
2107 2124 return False
2108 2125
2109 2126
2110 2127 class HasUserGroupPermissionAny(PermsFunction):
2111 2128 def __call__(self, user_group_name=None, check_location='', user=None):
2112 2129 self.user_group_name = user_group_name
2113 2130 return super(HasUserGroupPermissionAny, self).__call__(check_location, user)
2114 2131
2115 2132 def check_permissions(self, user):
2116 2133 perms = user.permissions
2117 2134 try:
2118 2135 user_perms = {perms['user_groups'][self.user_group_name]}
2119 2136 except KeyError:
2120 2137 return False
2121 2138 if self.required_perms.intersection(user_perms):
2122 2139 return True
2123 2140 return False
2124 2141
2125 2142
2126 2143 class HasUserGroupPermissionAll(PermsFunction):
2127 2144 def __call__(self, user_group_name=None, check_location='', user=None):
2128 2145 self.user_group_name = user_group_name
2129 2146 return super(HasUserGroupPermissionAll, self).__call__(check_location, user)
2130 2147
2131 2148 def check_permissions(self, user):
2132 2149 perms = user.permissions
2133 2150 try:
2134 2151 user_perms = {perms['user_groups'][self.user_group_name]}
2135 2152 except KeyError:
2136 2153 return False
2137 2154 if self.required_perms.issubset(user_perms):
2138 2155 return True
2139 2156 return False
2140 2157
2141 2158
2142 2159 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
2143 2160 class HasPermissionAnyMiddleware(object):
2144 2161 def __init__(self, *perms):
2145 2162 self.required_perms = set(perms)
2146 2163
2147 2164 def __call__(self, auth_user, repo_name):
2148 2165 # repo_name MUST be unicode, since we handle keys in permission
2149 2166 # dict by unicode
2150 2167 repo_name = safe_unicode(repo_name)
2151 2168 log.debug(
2152 2169 'Checking VCS protocol permissions %s for user:%s repo:`%s`',
2153 2170 self.required_perms, auth_user, repo_name)
2154 2171
2155 2172 if self.check_permissions(auth_user, repo_name):
2156 2173 log.debug('Permission to repo:`%s` GRANTED for user:%s @ %s',
2157 2174 repo_name, auth_user, 'PermissionMiddleware')
2158 2175 return True
2159 2176
2160 2177 else:
2161 2178 log.debug('Permission to repo:`%s` DENIED for user:%s @ %s',
2162 2179 repo_name, auth_user, 'PermissionMiddleware')
2163 2180 return False
2164 2181
2165 2182 def check_permissions(self, user, repo_name):
2166 2183 perms = user.permissions_with_scope({'repo_name': repo_name})
2167 2184
2168 2185 try:
2169 2186 user_perms = {perms['repositories'][repo_name]}
2170 2187 except Exception:
2171 2188 log.exception('Error while accessing user permissions')
2172 2189 return False
2173 2190
2174 2191 if self.required_perms.intersection(user_perms):
2175 2192 return True
2176 2193 return False
2177 2194
2178 2195
2179 2196 # SPECIAL VERSION TO HANDLE API AUTH
2180 2197 class _BaseApiPerm(object):
2181 2198 def __init__(self, *perms):
2182 2199 self.required_perms = set(perms)
2183 2200
2184 2201 def __call__(self, check_location=None, user=None, repo_name=None,
2185 2202 group_name=None, user_group_name=None):
2186 2203 cls_name = self.__class__.__name__
2187 2204 check_scope = 'global:%s' % (self.required_perms,)
2188 2205 if repo_name:
2189 2206 check_scope += ', repo_name:%s' % (repo_name,)
2190 2207
2191 2208 if group_name:
2192 2209 check_scope += ', repo_group_name:%s' % (group_name,)
2193 2210
2194 2211 if user_group_name:
2195 2212 check_scope += ', user_group_name:%s' % (user_group_name,)
2196 2213
2197 2214 log.debug('checking cls:%s %s %s @ %s',
2198 2215 cls_name, self.required_perms, check_scope, check_location)
2199 2216 if not user:
2200 2217 log.debug('Empty User passed into arguments')
2201 2218 return False
2202 2219
2203 2220 # process user
2204 2221 if not isinstance(user, AuthUser):
2205 2222 user = AuthUser(user.user_id)
2206 2223 if not check_location:
2207 2224 check_location = 'unspecified'
2208 2225 if self.check_permissions(user.permissions, repo_name, group_name,
2209 2226 user_group_name):
2210 2227 log.debug('Permission to repo:`%s` GRANTED for user:`%s` @ %s',
2211 2228 check_scope, user, check_location)
2212 2229 return True
2213 2230
2214 2231 else:
2215 2232 log.debug('Permission to repo:`%s` DENIED for user:`%s` @ %s',
2216 2233 check_scope, user, check_location)
2217 2234 return False
2218 2235
2219 2236 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2220 2237 user_group_name=None):
2221 2238 """
2222 2239 implement in child class should return True if permissions are ok,
2223 2240 False otherwise
2224 2241
2225 2242 :param perm_defs: dict with permission definitions
2226 2243 :param repo_name: repo name
2227 2244 """
2228 2245 raise NotImplementedError()
2229 2246
2230 2247
2231 2248 class HasPermissionAllApi(_BaseApiPerm):
2232 2249 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2233 2250 user_group_name=None):
2234 2251 if self.required_perms.issubset(perm_defs.get('global')):
2235 2252 return True
2236 2253 return False
2237 2254
2238 2255
2239 2256 class HasPermissionAnyApi(_BaseApiPerm):
2240 2257 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2241 2258 user_group_name=None):
2242 2259 if self.required_perms.intersection(perm_defs.get('global')):
2243 2260 return True
2244 2261 return False
2245 2262
2246 2263
2247 2264 class HasRepoPermissionAllApi(_BaseApiPerm):
2248 2265 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2249 2266 user_group_name=None):
2250 2267 try:
2251 2268 _user_perms = {perm_defs['repositories'][repo_name]}
2252 2269 except KeyError:
2253 2270 log.warning(traceback.format_exc())
2254 2271 return False
2255 2272 if self.required_perms.issubset(_user_perms):
2256 2273 return True
2257 2274 return False
2258 2275
2259 2276
2260 2277 class HasRepoPermissionAnyApi(_BaseApiPerm):
2261 2278 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2262 2279 user_group_name=None):
2263 2280 try:
2264 2281 _user_perms = {perm_defs['repositories'][repo_name]}
2265 2282 except KeyError:
2266 2283 log.warning(traceback.format_exc())
2267 2284 return False
2268 2285 if self.required_perms.intersection(_user_perms):
2269 2286 return True
2270 2287 return False
2271 2288
2272 2289
2273 2290 class HasRepoGroupPermissionAnyApi(_BaseApiPerm):
2274 2291 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2275 2292 user_group_name=None):
2276 2293 try:
2277 2294 _user_perms = {perm_defs['repositories_groups'][group_name]}
2278 2295 except KeyError:
2279 2296 log.warning(traceback.format_exc())
2280 2297 return False
2281 2298 if self.required_perms.intersection(_user_perms):
2282 2299 return True
2283 2300 return False
2284 2301
2285 2302
2286 2303 class HasRepoGroupPermissionAllApi(_BaseApiPerm):
2287 2304 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2288 2305 user_group_name=None):
2289 2306 try:
2290 2307 _user_perms = {perm_defs['repositories_groups'][group_name]}
2291 2308 except KeyError:
2292 2309 log.warning(traceback.format_exc())
2293 2310 return False
2294 2311 if self.required_perms.issubset(_user_perms):
2295 2312 return True
2296 2313 return False
2297 2314
2298 2315
2299 2316 class HasUserGroupPermissionAnyApi(_BaseApiPerm):
2300 2317 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2301 2318 user_group_name=None):
2302 2319 try:
2303 2320 _user_perms = {perm_defs['user_groups'][user_group_name]}
2304 2321 except KeyError:
2305 2322 log.warning(traceback.format_exc())
2306 2323 return False
2307 2324 if self.required_perms.intersection(_user_perms):
2308 2325 return True
2309 2326 return False
2310 2327
2311 2328
2312 2329 def check_ip_access(source_ip, allowed_ips=None):
2313 2330 """
2314 2331 Checks if source_ip is a subnet of any of allowed_ips.
2315 2332
2316 2333 :param source_ip:
2317 2334 :param allowed_ips: list of allowed ips together with mask
2318 2335 """
2319 2336 log.debug('checking if ip:%s is subnet of %s', source_ip, allowed_ips)
2320 2337 source_ip_address = ipaddress.ip_address(safe_unicode(source_ip))
2321 2338 if isinstance(allowed_ips, (tuple, list, set)):
2322 2339 for ip in allowed_ips:
2323 2340 ip = safe_unicode(ip)
2324 2341 try:
2325 2342 network_address = ipaddress.ip_network(ip, strict=False)
2326 2343 if source_ip_address in network_address:
2327 2344 log.debug('IP %s is network %s', source_ip_address, network_address)
2328 2345 return True
2329 2346 # for any case we cannot determine the IP, don't crash just
2330 2347 # skip it and log as error, we want to say forbidden still when
2331 2348 # sending bad IP
2332 2349 except Exception:
2333 2350 log.error(traceback.format_exc())
2334 2351 continue
2335 2352 return False
2336 2353
2337 2354
2338 2355 def get_cython_compat_decorator(wrapper, func):
2339 2356 """
2340 2357 Creates a cython compatible decorator. The previously used
2341 2358 decorator.decorator() function seems to be incompatible with cython.
2342 2359
2343 2360 :param wrapper: __wrapper method of the decorator class
2344 2361 :param func: decorated function
2345 2362 """
2346 2363 @wraps(func)
2347 2364 def local_wrapper(*args, **kwds):
2348 2365 return wrapper(func, *args, **kwds)
2349 2366 local_wrapper.__wrapped__ = func
2350 2367 return local_wrapper
2351 2368
2352 2369
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,381 +1,382 b''
1 1
2 2 /******************************************************************************
3 3 * *
4 4 * DO NOT CHANGE THIS FILE MANUALLY *
5 5 * *
6 6 * *
7 7 * This file is automatically generated when the app starts up with *
8 8 * generate_js_files = true *
9 9 * *
10 10 * To add a route here pass jsroute=True to the route definition in the app *
11 11 * *
12 12 ******************************************************************************/
13 13 function registerRCRoutes() {
14 14 // routes registration
15 15 pyroutes.register('favicon', '/favicon.ico', []);
16 16 pyroutes.register('robots', '/robots.txt', []);
17 17 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
18 18 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
19 19 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
20 20 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
21 21 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
22 22 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
23 23 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/_settings/integrations', ['repo_group_name']);
24 24 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/_settings/integrations/new', ['repo_group_name']);
25 25 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/_settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
26 26 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/_settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
27 27 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/_settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
28 28 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
29 29 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
30 30 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
31 31 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
32 32 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
33 33 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
34 34 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
35 35 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
36 36 pyroutes.register('ops_ping_legacy', '/_admin/ping', []);
37 37 pyroutes.register('ops_error_test_legacy', '/_admin/error_test', []);
38 38 pyroutes.register('admin_home', '/_admin', []);
39 39 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
40 40 pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']);
41 41 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
42 42 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
43 43 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
44 44 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
45 45 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
46 46 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
47 47 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
48 48 pyroutes.register('admin_settings_exception_tracker', '/_admin/settings/exceptions', []);
49 49 pyroutes.register('admin_settings_exception_tracker_delete_all', '/_admin/settings/exceptions/delete', []);
50 50 pyroutes.register('admin_settings_exception_tracker_show', '/_admin/settings/exceptions/%(exception_id)s', ['exception_id']);
51 51 pyroutes.register('admin_settings_exception_tracker_delete', '/_admin/settings/exceptions/%(exception_id)s/delete', ['exception_id']);
52 52 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
53 53 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
54 54 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
55 55 pyroutes.register('admin_settings_process_management_data', '/_admin/settings/process_management/data', []);
56 56 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
57 57 pyroutes.register('admin_settings_process_management_master_signal', '/_admin/settings/process_management/master_signal', []);
58 58 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
59 59 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
60 60 pyroutes.register('admin_settings', '/_admin/settings', []);
61 61 pyroutes.register('admin_settings_update', '/_admin/settings/update', []);
62 62 pyroutes.register('admin_settings_global', '/_admin/settings/global', []);
63 63 pyroutes.register('admin_settings_global_update', '/_admin/settings/global/update', []);
64 64 pyroutes.register('admin_settings_vcs', '/_admin/settings/vcs', []);
65 65 pyroutes.register('admin_settings_vcs_update', '/_admin/settings/vcs/update', []);
66 66 pyroutes.register('admin_settings_vcs_svn_pattern_delete', '/_admin/settings/vcs/svn_pattern_delete', []);
67 67 pyroutes.register('admin_settings_mapping', '/_admin/settings/mapping', []);
68 68 pyroutes.register('admin_settings_mapping_update', '/_admin/settings/mapping/update', []);
69 69 pyroutes.register('admin_settings_visual', '/_admin/settings/visual', []);
70 70 pyroutes.register('admin_settings_visual_update', '/_admin/settings/visual/update', []);
71 71 pyroutes.register('admin_settings_issuetracker', '/_admin/settings/issue-tracker', []);
72 72 pyroutes.register('admin_settings_issuetracker_update', '/_admin/settings/issue-tracker/update', []);
73 73 pyroutes.register('admin_settings_issuetracker_test', '/_admin/settings/issue-tracker/test', []);
74 74 pyroutes.register('admin_settings_issuetracker_delete', '/_admin/settings/issue-tracker/delete', []);
75 75 pyroutes.register('admin_settings_email', '/_admin/settings/email', []);
76 76 pyroutes.register('admin_settings_email_update', '/_admin/settings/email/update', []);
77 77 pyroutes.register('admin_settings_hooks', '/_admin/settings/hooks', []);
78 78 pyroutes.register('admin_settings_hooks_update', '/_admin/settings/hooks/update', []);
79 79 pyroutes.register('admin_settings_hooks_delete', '/_admin/settings/hooks/delete', []);
80 80 pyroutes.register('admin_settings_search', '/_admin/settings/search', []);
81 81 pyroutes.register('admin_settings_labs', '/_admin/settings/labs', []);
82 82 pyroutes.register('admin_settings_labs_update', '/_admin/settings/labs/update', []);
83 83 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
84 84 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
85 85 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
86 86 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
87 87 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
88 88 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
89 89 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
90 90 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
91 91 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
92 92 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
93 93 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
94 94 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
95 95 pyroutes.register('users', '/_admin/users', []);
96 96 pyroutes.register('users_data', '/_admin/users_data', []);
97 97 pyroutes.register('users_create', '/_admin/users/create', []);
98 98 pyroutes.register('users_new', '/_admin/users/new', []);
99 99 pyroutes.register('user_edit', '/_admin/users/%(user_id)s/edit', ['user_id']);
100 100 pyroutes.register('user_edit_advanced', '/_admin/users/%(user_id)s/edit/advanced', ['user_id']);
101 101 pyroutes.register('user_edit_global_perms', '/_admin/users/%(user_id)s/edit/global_permissions', ['user_id']);
102 102 pyroutes.register('user_edit_global_perms_update', '/_admin/users/%(user_id)s/edit/global_permissions/update', ['user_id']);
103 103 pyroutes.register('user_update', '/_admin/users/%(user_id)s/update', ['user_id']);
104 104 pyroutes.register('user_delete', '/_admin/users/%(user_id)s/delete', ['user_id']);
105 105 pyroutes.register('user_enable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_enable', ['user_id']);
106 106 pyroutes.register('user_disable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_disable', ['user_id']);
107 107 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
108 108 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
109 109 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
110 110 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
111 111 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
112 112 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
113 113 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
114 114 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
115 115 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
116 116 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
117 117 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
118 118 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
119 119 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
120 120 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
121 121 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
122 122 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
123 123 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
124 124 pyroutes.register('edit_user_audit_logs_download', '/_admin/users/%(user_id)s/edit/audit/download', ['user_id']);
125 125 pyroutes.register('edit_user_caches', '/_admin/users/%(user_id)s/edit/caches', ['user_id']);
126 126 pyroutes.register('edit_user_caches_update', '/_admin/users/%(user_id)s/edit/caches/update', ['user_id']);
127 127 pyroutes.register('user_groups', '/_admin/user_groups', []);
128 128 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
129 129 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
130 130 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
131 131 pyroutes.register('repos', '/_admin/repos', []);
132 132 pyroutes.register('repo_new', '/_admin/repos/new', []);
133 133 pyroutes.register('repo_create', '/_admin/repos/create', []);
134 134 pyroutes.register('repo_groups', '/_admin/repo_groups', []);
135 135 pyroutes.register('repo_groups_data', '/_admin/repo_groups_data', []);
136 136 pyroutes.register('repo_group_new', '/_admin/repo_group/new', []);
137 137 pyroutes.register('repo_group_create', '/_admin/repo_group/create', []);
138 138 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
139 139 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
140 140 pyroutes.register('channelstream_proxy', '/_channelstream', []);
141 141 pyroutes.register('upload_file', '/_file_store/upload', []);
142 142 pyroutes.register('download_file', '/_file_store/download/%(fid)s', ['fid']);
143 pyroutes.register('download_file_by_token', '/_file_store/token-download/%(_auth_token)s/%(fid)s', ['_auth_token', 'fid']);
143 144 pyroutes.register('logout', '/_admin/logout', []);
144 145 pyroutes.register('reset_password', '/_admin/password_reset', []);
145 146 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
146 147 pyroutes.register('home', '/', []);
147 148 pyroutes.register('user_autocomplete_data', '/_users', []);
148 149 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
149 150 pyroutes.register('repo_list_data', '/_repos', []);
150 151 pyroutes.register('repo_group_list_data', '/_repo_groups', []);
151 152 pyroutes.register('goto_switcher_data', '/_goto_data', []);
152 153 pyroutes.register('markup_preview', '/_markup_preview', []);
153 154 pyroutes.register('file_preview', '/_file_preview', []);
154 155 pyroutes.register('store_user_session_value', '/_store_session_attr', []);
155 156 pyroutes.register('journal', '/_admin/journal', []);
156 157 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
157 158 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
158 159 pyroutes.register('journal_public', '/_admin/public_journal', []);
159 160 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
160 161 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
161 162 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
162 163 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
163 164 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
164 165 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
165 166 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
166 167 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
167 168 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
168 169 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
169 170 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
170 171 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
171 172 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
172 173 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
173 174 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
174 175 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
175 176 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
176 177 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
177 178 pyroutes.register('repo_commit_comment_attachment_upload', '/%(repo_name)s/changeset/%(commit_id)s/comment/attachment_upload', ['repo_name', 'commit_id']);
178 179 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
179 180 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
180 181 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
181 182 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
182 183 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
183 184 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
184 185 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
185 186 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
186 187 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
187 188 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
188 189 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
189 190 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
190 191 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
191 192 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
192 193 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
193 194 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
194 195 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
195 196 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
196 197 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
197 198 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
198 199 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
199 200 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
200 201 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
201 202 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
202 203 pyroutes.register('repo_files_upload_file', '/%(repo_name)s/upload_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
203 204 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
204 205 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
205 206 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
206 207 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
207 208 pyroutes.register('repo_commits', '/%(repo_name)s/commits', ['repo_name']);
208 209 pyroutes.register('repo_commits_file', '/%(repo_name)s/commits/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
209 210 pyroutes.register('repo_commits_elements', '/%(repo_name)s/commits_elements', ['repo_name']);
210 211 pyroutes.register('repo_commits_elements_file', '/%(repo_name)s/commits_elements/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
211 212 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
212 213 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
213 214 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
214 215 pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
215 216 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
216 217 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
217 218 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
218 219 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
219 220 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
220 221 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
221 222 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
222 223 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
223 224 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
224 225 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
225 226 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
226 227 pyroutes.register('pullrequest_repo_targets', '/%(repo_name)s/pull-request/repo-targets', ['repo_name']);
227 228 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
228 229 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
229 230 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
230 231 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
231 232 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
232 233 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
233 234 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
234 235 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
235 236 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
236 237 pyroutes.register('edit_repo_advanced_archive', '/%(repo_name)s/settings/advanced/archive', ['repo_name']);
237 238 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
238 239 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
239 240 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
240 241 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
241 242 pyroutes.register('edit_repo_advanced_hooks', '/%(repo_name)s/settings/advanced/hooks', ['repo_name']);
242 243 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
243 244 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
244 245 pyroutes.register('edit_repo_perms_set_private', '/%(repo_name)s/settings/permissions/set_private', ['repo_name']);
245 246 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
246 247 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
247 248 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
248 249 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
249 250 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
250 251 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
251 252 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
252 253 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
253 254 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
254 255 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
255 256 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
256 257 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
257 258 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
258 259 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
259 260 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
260 261 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
261 262 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
262 263 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
263 264 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
264 265 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
265 266 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
266 267 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
267 268 pyroutes.register('edit_repo_audit_logs', '/%(repo_name)s/settings/audit_logs', ['repo_name']);
268 269 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed-rss', ['repo_name']);
269 270 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed-atom', ['repo_name']);
270 271 pyroutes.register('rss_feed_home_old', '/%(repo_name)s/feed/rss', ['repo_name']);
271 272 pyroutes.register('atom_feed_home_old', '/%(repo_name)s/feed/atom', ['repo_name']);
272 273 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
273 274 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
274 275 pyroutes.register('edit_repo_group', '/%(repo_group_name)s/_edit', ['repo_group_name']);
275 276 pyroutes.register('edit_repo_group_advanced', '/%(repo_group_name)s/_settings/advanced', ['repo_group_name']);
276 277 pyroutes.register('edit_repo_group_advanced_delete', '/%(repo_group_name)s/_settings/advanced/delete', ['repo_group_name']);
277 278 pyroutes.register('edit_repo_group_perms', '/%(repo_group_name)s/_settings/permissions', ['repo_group_name']);
278 279 pyroutes.register('edit_repo_group_perms_update', '/%(repo_group_name)s/_settings/permissions/update', ['repo_group_name']);
279 280 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
280 281 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
281 282 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
282 283 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
283 284 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
284 285 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
285 286 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
286 287 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
287 288 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
288 289 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
289 290 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
290 291 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
291 292 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
292 293 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
293 294 pyroutes.register('search', '/_admin/search', []);
294 295 pyroutes.register('search_repo', '/%(repo_name)s/_search', ['repo_name']);
295 296 pyroutes.register('search_repo_alt', '/%(repo_name)s/search', ['repo_name']);
296 297 pyroutes.register('search_repo_group', '/%(repo_group_name)s/_search', ['repo_group_name']);
297 298 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
298 299 pyroutes.register('user_group_profile', '/_profile_user_group/%(user_group_name)s', ['user_group_name']);
299 300 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
300 301 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
301 302 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
302 303 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
303 304 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
304 305 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
305 306 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
306 307 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
307 308 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
308 309 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
309 310 pyroutes.register('my_account_user_group_membership', '/_admin/my_account/user_group_membership', []);
310 311 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
311 312 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
312 313 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
313 314 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
314 315 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
315 316 pyroutes.register('my_account_bookmarks', '/_admin/my_account/bookmarks', []);
316 317 pyroutes.register('my_account_bookmarks_update', '/_admin/my_account/bookmarks/update', []);
317 318 pyroutes.register('my_account_goto_bookmark', '/_admin/my_account/bookmark/%(bookmark_id)s', ['bookmark_id']);
318 319 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
319 320 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
320 321 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
321 322 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
322 323 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
323 324 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
324 325 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
325 326 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
326 327 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
327 328 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
328 329 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
329 330 pyroutes.register('gists_show', '/_admin/gists', []);
330 331 pyroutes.register('gists_new', '/_admin/gists/new', []);
331 332 pyroutes.register('gists_create', '/_admin/gists/create', []);
332 333 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
333 334 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
334 335 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
335 336 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
336 337 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
337 338 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
338 339 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
339 340 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
340 341 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
341 342 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
342 343 pyroutes.register('apiv2', '/_admin/api', []);
343 344 pyroutes.register('admin_settings_license', '/_admin/settings/license', []);
344 345 pyroutes.register('admin_settings_license_unlock', '/_admin/settings/license_unlock', []);
345 346 pyroutes.register('login', '/_admin/login', []);
346 347 pyroutes.register('register', '/_admin/register', []);
347 348 pyroutes.register('repo_reviewers_review_rule_new', '/%(repo_name)s/settings/review/rules/new', ['repo_name']);
348 349 pyroutes.register('repo_reviewers_review_rule_edit', '/%(repo_name)s/settings/review/rules/%(rule_id)s', ['repo_name', 'rule_id']);
349 350 pyroutes.register('repo_reviewers_review_rule_delete', '/%(repo_name)s/settings/review/rules/%(rule_id)s/delete', ['repo_name', 'rule_id']);
350 351 pyroutes.register('plugin_admin_chat', '/_admin/plugin_admin_chat/%(action)s', ['action']);
351 352 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
352 353 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
353 354 pyroutes.register('admin_settings_scheduler_show_tasks', '/_admin/settings/scheduler/_tasks', []);
354 355 pyroutes.register('admin_settings_scheduler_show_all', '/_admin/settings/scheduler', []);
355 356 pyroutes.register('admin_settings_scheduler_new', '/_admin/settings/scheduler/new', []);
356 357 pyroutes.register('admin_settings_scheduler_create', '/_admin/settings/scheduler/create', []);
357 358 pyroutes.register('admin_settings_scheduler_edit', '/_admin/settings/scheduler/%(schedule_id)s', ['schedule_id']);
358 359 pyroutes.register('admin_settings_scheduler_update', '/_admin/settings/scheduler/%(schedule_id)s/update', ['schedule_id']);
359 360 pyroutes.register('admin_settings_scheduler_delete', '/_admin/settings/scheduler/%(schedule_id)s/delete', ['schedule_id']);
360 361 pyroutes.register('admin_settings_scheduler_execute', '/_admin/settings/scheduler/%(schedule_id)s/execute', ['schedule_id']);
361 362 pyroutes.register('admin_settings_automation', '/_admin/settings/automation', []);
362 363 pyroutes.register('admin_settings_automation_update', '/_admin/settings/automation/%(entry_id)s/update', ['entry_id']);
363 364 pyroutes.register('admin_permissions_branch', '/_admin/permissions/branch', []);
364 365 pyroutes.register('admin_permissions_branch_update', '/_admin/permissions/branch/update', []);
365 366 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
366 367 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
367 368 pyroutes.register('my_account_external_identity', '/_admin/my_account/external-identity', []);
368 369 pyroutes.register('my_account_external_identity_delete', '/_admin/my_account/external-identity/delete', []);
369 370 pyroutes.register('repo_artifacts_list', '/%(repo_name)s/artifacts', ['repo_name']);
370 371 pyroutes.register('repo_artifacts_data', '/%(repo_name)s/artifacts_data', ['repo_name']);
371 372 pyroutes.register('repo_artifacts_new', '/%(repo_name)s/artifacts/new', ['repo_name']);
372 373 pyroutes.register('repo_artifacts_get', '/%(repo_name)s/artifacts/download/%(uid)s', ['repo_name', 'uid']);
373 374 pyroutes.register('repo_artifacts_store', '/%(repo_name)s/artifacts/store', ['repo_name']);
374 375 pyroutes.register('repo_artifacts_info', '/%(repo_name)s/artifacts/info/%(uid)s', ['repo_name', 'uid']);
375 376 pyroutes.register('repo_artifacts_delete', '/%(repo_name)s/artifacts/delete/%(uid)s', ['repo_name', 'uid']);
376 377 pyroutes.register('repo_automation', '/%(repo_name)s/settings/automation', ['repo_name']);
377 378 pyroutes.register('repo_automation_update', '/%(repo_name)s/settings/automation/%(entry_id)s/update', ['repo_name', 'entry_id']);
378 379 pyroutes.register('edit_repo_remote_push', '/%(repo_name)s/settings/remote/push', ['repo_name']);
379 380 pyroutes.register('edit_repo_perms_branch', '/%(repo_name)s/settings/branch_permissions', ['repo_name']);
380 381 pyroutes.register('edit_repo_perms_branch_delete', '/%(repo_name)s/settings/branch_permissions/%(rule_id)s/delete', ['repo_name', 'rule_id']);
381 382 }
General Comments 0
You need to be logged in to leave comments. Login now