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