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