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