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