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