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