##// END OF EJS Templates
feed-token, user, performance: lazy load the feed_token. We only need it for...
marcink -
r2424:76f7c7cb default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

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