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