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