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