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