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