##// END OF EJS Templates
chore(release): merged default into stable branch
super-admin -
r5558:77c2b736 merge v5.3.0 stable
parent child Browse files
Show More
@@ -0,0 +1,40 b''
1 |RCE| 5.2.1 |RNS|
2 -----------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2024-09-16
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13
14
15 General
16 ^^^^^^^
17
18
19
20 Security
21 ^^^^^^^^
22
23
24
25 Performance
26 ^^^^^^^^^^^
27
28
29
30
31 Fixes
32 ^^^^^
33
34 - Fixed problems with incorrect user agent errors
35
36
37 Upgrade notes
38 ^^^^^^^^^^^^^
39
40 - RhodeCode 5.2.1 is unscheduled bugfix release to address some build issues with 5.2 images
@@ -0,0 +1,45 b''
1 |RCE| 5.3.0 |RNS|
2 -----------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2024-09-17
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13 - System-info: expose rhodecode config for better visibility of set settings for RhodeCode system.
14
15
16 General
17 ^^^^^^^
18
19
20
21 Security
22 ^^^^^^^^
23
24 - Permissions: fixed security problem with apply-to-children from a repo group functionality breaking
25 permissions for private repositories exposing them despite repo being private.
26 - Git-lfs: fixed security problem with allowing off-chain attacks to replace OID data without validating hash for already present oids.
27 This allowed to replace an LFS OID content with malicious request tailored to open RhodeCode server.
28
29
30 Performance
31 ^^^^^^^^^^^
32
33
34
35
36 Fixes
37 ^^^^^
38
39 - Fixed problems with incorrect user agent errors
40
41
42 Upgrade notes
43 ^^^^^^^^^^^^^
44
45 - RhodeCode 5.3.0 is unscheduled security release to address some build issues with 5.X images
@@ -1,5 +1,5 b''
1 1 [bumpversion]
2 current_version = 5.2.1
2 current_version = 5.3.0
3 3 message = release: Bump version {current_version} to {new_version}
4 4
5 5 [bumpversion:file:rhodecode/VERSION]
@@ -71,6 +71,9 b' use = egg:rhodecode-enterprise-ce'
71 71 ; enable proxy prefix middleware, defined above
72 72 #filter-with = proxy-prefix
73 73
74 ; control if environmental variables to be expanded into the .ini settings
75 #rhodecode.env_expand = true
76
74 77 ; #############
75 78 ; DEBUG OPTIONS
76 79 ; #############
@@ -71,6 +71,9 b' use = egg:rhodecode-enterprise-ce'
71 71 ; enable proxy prefix middleware, defined above
72 72 #filter-with = proxy-prefix
73 73
74 ; control if environmental variables to be expanded into the .ini settings
75 #rhodecode.env_expand = true
76
74 77 ; encryption key used to encrypt social plugin tokens,
75 78 ; remote_urls with credentials etc, if not set it defaults to
76 79 ; `beaker.session.secret`
@@ -9,6 +9,8 b' Release Notes'
9 9 .. toctree::
10 10 :maxdepth: 1
11 11
12 release-notes-5.3.0.rst
13 release-notes-5.2.1.rst
12 14 release-notes-5.2.0.rst
13 15 release-notes-5.1.2.rst
14 16 release-notes-5.1.1.rst
@@ -1,1 +1,1 b''
1 5.2.1 No newline at end of file
1 5.3.0
@@ -199,8 +199,12 b' class AdminSystemInfoSettingsView(BaseAp'
199 199
200 200 ]
201 201
202 c.rhodecode_data_items = [
203 (k, v) for k, v in sorted((val('rhodecode_server_config') or {}).items(), key=lambda x: x[0].lower())
204 ]
205
202 206 c.vcsserver_data_items = [
203 (k, v) for k, v in (val('vcs_server_config') or {}).items()
207 (k, v) for k, v in sorted((val('vcs_server_config') or {}).items(), key=lambda x: x[0].lower())
204 208 ]
205 209
206 210 if snapshot:
@@ -100,22 +100,16 b' class RepoSettingsPermissionsView(RepoAp'
100 100 self.load_default_context()
101 101
102 102 private_flag = str2bool(self.request.POST.get('private'))
103
103 changes = {
104 'repo_private': private_flag
105 }
104 106 try:
105 107 repo = RepoModel().get(self.db_repo.repo_id)
106 repo.private = private_flag
107 Session().add(repo)
108 RepoModel().grant_user_permission(
109 repo=self.db_repo, user=User.DEFAULT_USER, perm='repository.none'
110 )
111
108 RepoModel().update(repo, **changes)
112 109 Session().commit()
113 110
114 111 h.flash(_('Repository `{}` private mode set successfully').format(self.db_repo_name),
115 112 category='success')
116 # NOTE(dan): we change repo private mode we need to notify all USERS
117 affected_user_ids = User.get_all_user_ids()
118 PermissionModel().trigger_permission_flush(affected_user_ids)
119 113
120 114 except Exception:
121 115 log.exception("Exception during update of repository")
@@ -109,6 +109,9 b' class SettingsMaker:'
109 109 return envvar_value
110 110
111 111 def env_expand(self):
112 if self.settings.get('rhodecode.env_expand') == 'false':
113 return
114
112 115 replaced = {}
113 116 for k, v in self.settings.items():
114 117 if k not in set_keys:
@@ -167,7 +167,8 b' def _is_request_chunked(environ):'
167 167 def _maybe_stream_request(environ):
168 168 path = get_path_info(environ)
169 169 stream = _is_request_chunked(environ)
170 log.debug('handling request `%s` with stream support: %s', path, stream)
170 req_method = environ['REQUEST_METHOD']
171 log.debug('handling scm request: %s `%s` with stream support: %s', req_method, path, stream)
171 172
172 173 if stream:
173 174 # set stream by 256k
@@ -665,6 +665,32 b' def vcs_server_config():'
665 665
666 666 return SysInfoRes(value=value, state=state, human_value=human_value)
667 667
668 @register_sysinfo
669 def rhodecode_server_config():
670 import rhodecode
671
672 state = STATE_OK_DEFAULT
673 config = rhodecode.CONFIG.copy()
674
675 secrets_lits = [
676 f'rhodecode_{LicenseModel.LICENSE_DB_KEY}',
677 'sqlalchemy.db1.url',
678 'channelstream.secret',
679 'beaker.session.secret',
680 'rhodecode.encrypted_values.secret',
681 'appenlight.api_key',
682 'smtp_password',
683 'file_store.objectstore.secret',
684 'archive_cache.objectstore.secret',
685 'app.service_api.token',
686 ]
687 for k in secrets_lits:
688 if k in config:
689 config[k] = '**OBFUSCATED**'
690
691 value = human_value = config
692 return SysInfoRes(value=value, state=state, human_value=human_value)
693
668 694
669 695 @register_sysinfo
670 696 def rhodecode_app_info():
@@ -851,6 +877,7 b' def get_system_info(environ):'
851 877 'vcs_server': SysInfo(vcs_server)(),
852 878
853 879 'vcs_server_config': SysInfo(vcs_server_config)(),
880 'rhodecode_server_config': SysInfo(rhodecode_server_config)(),
854 881
855 882 'git': SysInfo(git_info)(),
856 883 'hg': SysInfo(hg_info)(),
@@ -452,15 +452,24 b' class RepoModel(BaseModel):'
452 452
453 453 setattr(cur_repo, k, val)
454 454
455 new_name = cur_repo.get_new_name(kwargs['repo_name'])
456 cur_repo.repo_name = new_name
455 new_name = source_repo_name
456 if 'repo_name' in kwargs:
457 new_name = cur_repo.get_new_name(kwargs['repo_name'])
458 cur_repo.repo_name = new_name
457 459
458 # if private flag is set, reset default permission to NONE
459 if kwargs.get('repo_private'):
460 EMPTY_PERM = 'repository.none'
461 RepoModel().grant_user_permission(
462 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
463 )
460 if 'repo_private' in kwargs:
461 # if private flag is set to True, reset default permission to NONE
462 set_private_to = kwargs.get('repo_private')
463 if set_private_to:
464 EMPTY_PERM = 'repository.none'
465 RepoModel().grant_user_permission(
466 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
467 )
468 if set_private_to != cur_repo.private:
469 # NOTE(dan): we change repo private mode we need to notify all USERS
470 # this is just by having this value set to a different value then it was before
471 affected_user_ids = User.get_all_user_ids()
472
464 473 if kwargs.get('repo_landing_rev'):
465 474 landing_rev_val = kwargs['repo_landing_rev']
466 475 RepoModel().set_landing_rev(cur_repo, landing_rev_val)
@@ -1,4 +1,4 b''
1 # Copyright (C) 2011-2023 RhodeCode GmbH
1 # Copyright (C) 2011-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
@@ -30,7 +30,6 b' import time'
30 30 import traceback
31 31 import string
32 32
33 from zope.cachedescriptors.property import Lazy as LazyProperty
34 33
35 34 from rhodecode import events
36 35 from rhodecode.model import BaseModel
@@ -38,7 +37,7 b' from rhodecode.model.db import (_hash_ke'
38 37 Session, RepoGroup, UserRepoGroupToPerm, User, Permission, UserGroupRepoGroupToPerm,
39 38 UserGroup, Repository)
40 39 from rhodecode.model.permission import PermissionModel
41 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
40 from rhodecode.model.settings import SettingsModel
42 41 from rhodecode.lib.caching_query import FromCache
43 42 from rhodecode.lib.utils2 import action_logger_generic
44 43
@@ -350,46 +349,45 b' class RepoGroupModel(BaseModel):'
350 349 'default_user_changed': None
351 350 }
352 351
353 def _set_perm_user(obj, user, perm):
354 if isinstance(obj, RepoGroup):
355 self.grant_user_permission(
356 repo_group=obj, user=user, perm=perm)
357 elif isinstance(obj, Repository):
352 def _set_perm_user(_obj: RepoGroup | Repository, _user_obj: User, _perm):
353
354 if isinstance(_obj, RepoGroup):
355 self.grant_user_permission(repo_group=_obj, user=_user_obj, perm=_perm)
356 elif isinstance(_obj, Repository):
358 357 # private repos will not allow to change the default
359 358 # permissions using recursive mode
360 if obj.private and user == User.DEFAULT_USER:
359 if _obj.private and _user_obj.username == User.DEFAULT_USER:
360 log.debug('Skipping private repo %s for user %s', _obj, _user_obj)
361 361 return
362 362
363 # we set group permission but we have to switch to repo
364 # permission
365 perm = perm.replace('group.', 'repository.')
366 RepoModel().grant_user_permission(
367 repo=obj, user=user, perm=perm)
363 # we set group permission, we have to switch to repo permission definition
364 new_perm = _perm.replace('group.', 'repository.')
365 RepoModel().grant_user_permission(repo=_obj, user=_user_obj, perm=new_perm)
366
367 def _set_perm_group(_obj: RepoGroup | Repository, users_group: UserGroup, _perm):
368 if isinstance(_obj, RepoGroup):
369 self.grant_user_group_permission(repo_group=_obj, group_name=users_group, perm=_perm)
370 elif isinstance(_obj, Repository):
371 # we set group permission, we have to switch to repo permission definition
372 new_perm = _perm.replace('group.', 'repository.')
373 RepoModel().grant_user_group_permission(repo=_obj, group_name=users_group, perm=new_perm)
368 374
369 def _set_perm_group(obj, users_group, perm):
370 if isinstance(obj, RepoGroup):
371 self.grant_user_group_permission(
372 repo_group=obj, group_name=users_group, perm=perm)
373 elif isinstance(obj, Repository):
374 # we set group permission but we have to switch to repo
375 # permission
376 perm = perm.replace('group.', 'repository.')
377 RepoModel().grant_user_group_permission(
378 repo=obj, group_name=users_group, perm=perm)
375 def _revoke_perm_user(_obj: RepoGroup | Repository, _user_obj: User):
376 if isinstance(_obj, RepoGroup):
377 self.revoke_user_permission(repo_group=_obj, user=_user_obj)
378 elif isinstance(_obj, Repository):
379 # private repos will not allow to change the default
380 # permissions using recursive mode, also there's no revocation fo default user, just update
381 if _user_obj.username == User.DEFAULT_USER:
382 log.debug('Skipping private repo %s for user %s', _obj, _user_obj)
383 return
384 RepoModel().revoke_user_permission(repo=_obj, user=_user_obj)
379 385
380 def _revoke_perm_user(obj, user):
381 if isinstance(obj, RepoGroup):
382 self.revoke_user_permission(repo_group=obj, user=user)
383 elif isinstance(obj, Repository):
384 RepoModel().revoke_user_permission(repo=obj, user=user)
385
386 def _revoke_perm_group(obj, user_group):
387 if isinstance(obj, RepoGroup):
388 self.revoke_user_group_permission(
389 repo_group=obj, group_name=user_group)
390 elif isinstance(obj, Repository):
391 RepoModel().revoke_user_group_permission(
392 repo=obj, group_name=user_group)
386 def _revoke_perm_group(_obj: RepoGroup | Repository, user_group: UserGroup):
387 if isinstance(_obj, RepoGroup):
388 self.revoke_user_group_permission(repo_group=_obj, group_name=user_group)
389 elif isinstance(_obj, Repository):
390 RepoModel().revoke_user_group_permission(repo=_obj, group_name=user_group)
393 391
394 392 # start updates
395 393 log.debug('Now updating permissions for %s in recursive mode:%s',
@@ -423,7 +421,8 b' class RepoGroupModel(BaseModel):'
423 421 for member_id, perm, member_type in perm_updates:
424 422 member_id = int(member_id)
425 423 if member_type == 'user':
426 member_name = User.get(member_id).username
424 member_obj = User.get(member_id)
425 member_name = member_obj.username
427 426 if isinstance(obj, RepoGroup) and obj == repo_group and member_name == User.DEFAULT_USER:
428 427 # NOTE(dan): detect if we changed permissions for default user
429 428 perm_obj = self.sa.query(UserRepoGroupToPerm) \
@@ -434,15 +433,16 b' class RepoGroupModel(BaseModel):'
434 433 changes['default_user_changed'] = True
435 434
436 435 # this updates also current one if found
437 _set_perm_user(obj, user=member_id, perm=perm)
436 _set_perm_user(obj, member_obj, perm)
438 437 elif member_type == 'user_group':
439 member_name = UserGroup.get(member_id).users_group_name
440 if not check_perms or has_group_perm(member_name,
441 user=cur_user):
442 _set_perm_group(obj, users_group=member_id, perm=perm)
438 member_obj = UserGroup.get(member_id)
439 member_name = member_obj.users_group_name
440 if not check_perms or has_group_perm(member_name, user=cur_user):
441 _set_perm_group(obj, member_obj, perm)
443 442 else:
444 raise ValueError("member_type must be 'user' or 'user_group' "
445 "got {} instead".format(member_type))
443 raise ValueError(
444 f"member_type must be 'user' or 'user_group' got {member_type} instead"
445 )
446 446
447 447 changes['updated'].append(
448 448 {'change_obj': change_obj, 'type': member_type,
@@ -452,17 +452,19 b' class RepoGroupModel(BaseModel):'
452 452 for member_id, perm, member_type in perm_additions:
453 453 member_id = int(member_id)
454 454 if member_type == 'user':
455 member_name = User.get(member_id).username
456 _set_perm_user(obj, user=member_id, perm=perm)
455 member_obj = User.get(member_id)
456 member_name = member_obj.username
457 _set_perm_user(obj, member_obj, perm)
457 458 elif member_type == 'user_group':
458 459 # check if we have permissions to alter this usergroup
459 member_name = UserGroup.get(member_id).users_group_name
460 if not check_perms or has_group_perm(member_name,
461 user=cur_user):
462 _set_perm_group(obj, users_group=member_id, perm=perm)
460 member_obj = UserGroup.get(member_id)
461 member_name = member_obj.users_group_name
462 if not check_perms or has_group_perm(member_name, user=cur_user):
463 _set_perm_group(obj, member_obj, perm)
463 464 else:
464 raise ValueError("member_type must be 'user' or 'user_group' "
465 "got {} instead".format(member_type))
465 raise ValueError(
466 f"member_type must be 'user' or 'user_group' got {member_type} instead"
467 )
466 468
467 469 changes['added'].append(
468 470 {'change_obj': change_obj, 'type': member_type,
@@ -472,18 +474,19 b' class RepoGroupModel(BaseModel):'
472 474 for member_id, perm, member_type in perm_deletions:
473 475 member_id = int(member_id)
474 476 if member_type == 'user':
475 member_name = User.get(member_id).username
476 _revoke_perm_user(obj, user=member_id)
477 member_obj = User.get(member_id)
478 member_name = member_obj.username
479 _revoke_perm_user(obj, member_obj)
477 480 elif member_type == 'user_group':
478 481 # check if we have permissions to alter this usergroup
479 member_name = UserGroup.get(member_id).users_group_name
480 if not check_perms or has_group_perm(member_name,
481 user=cur_user):
482 _revoke_perm_group(obj, user_group=member_id)
482 member_obj = UserGroup.get(member_id)
483 member_name = member_obj.users_group_name
484 if not check_perms or has_group_perm(member_name, user=cur_user):
485 _revoke_perm_group(obj, member_obj)
483 486 else:
484 raise ValueError("member_type must be 'user' or 'user_group' "
485 "got {} instead".format(member_type))
486
487 raise ValueError(
488 f"member_type must be 'user' or 'user_group' got {member_type} instead"
489 )
487 490 changes['deleted'].append(
488 491 {'change_obj': change_obj, 'type': member_type,
489 492 'id': member_id, 'name': member_name, 'new_perm': perm})
@@ -42,7 +42,7 b' class UpdateModel(BaseModel):'
42 42 ver = rhodecode.__version__
43 43 log.debug('Checking for upgrade on `%s` server', update_url)
44 44 opener = urllib.request.build_opener()
45 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
45 opener.addheaders = [('User-agent', f'RhodeCode-SCM/{ver.strip()}')]
46 46 response = opener.open(update_url)
47 47 response_data = response.read()
48 48 data = json.loads(response_data)
@@ -29,7 +29,21 b''
29 29
30 30 <div class="panel panel-default">
31 31 <div class="panel-heading">
32 <h3 class="panel-title">${_('VCS Server')}</h3>
32 <h3 class="panel-title">${_('RhodeCode Server Config')}</h3>
33 </div>
34 <div class="panel-body">
35 <dl class="dl-horizontal settings dt-400">
36 % for dt, dd in c.rhodecode_data_items:
37 <dt>${dt}${':' if dt else '---'}</dt>
38 <dd>${dd}${'' if dt else '---'}</dd>
39 % endfor
40 </dl>
41 </div>
42 </div>
43
44 <div class="panel panel-default">
45 <div class="panel-heading">
46 <h3 class="panel-title">${_('VCS Server Config')}</h3>
33 47 </div>
34 48 <div class="panel-body">
35 49 <dl class="dl-horizontal settings dt-400">
@@ -166,6 +166,74 b' class TestPermissions(object):'
166 166
167 167 test_user_group.user = org_owner
168 168
169 def test_propagated_permissions_from_repo_group_to_private_repo(self, repo_name):
170 # make group
171 self.g1 = fixture.create_repo_group('TOP_LEVEL', skip_if_exists=True)
172 # both perms should be read !
173 assert group_perms(self.anon) == {
174 'TOP_LEVEL': 'group.read'
175 }
176
177 # Create repo inside the TOP_LEVEL
178 repo_name_in_group = RepoGroup.url_sep().join([self.g1.group_name, 'test_perm_on_private_repo'])
179 self.test_repo = fixture.create_repo(name=repo_name_in_group,
180 repo_type='hg',
181 repo_group=self.g1,
182 cur_user=self.u1,)
183 assert repo_perms(self.anon) == {
184 repo_name_in_group: 'repository.read',
185 'vcs_test_git': 'repository.read',
186 'vcs_test_hg': 'repository.read',
187 'vcs_test_svn': 'repository.read',
188 }
189 # Now change default user permissions
190 new_perm = 'repository.write'
191 perm_updates = [
192 [self.anon.user_id, new_perm, 'user']
193 ]
194 RepoGroupModel().update_permissions(
195 repo_group=self.g1, perm_updates=perm_updates, recursive='all')
196
197 Session().commit()
198 assert repo_perms(self.anon) == {
199 repo_name_in_group: new_perm,
200 'vcs_test_git': 'repository.read',
201 'vcs_test_hg': 'repository.read',
202 'vcs_test_svn': 'repository.read',
203 }
204
205 # NOW MARK repo as private
206 changes = {
207 'repo_private': True
208 }
209 repo = RepoModel().get_by_repo_name(repo_name_in_group)
210 RepoModel().update(repo, **changes)
211 Session().commit()
212
213 # Private repo sets 'none' permission for default user
214 assert repo_perms(self.anon) == {
215 repo_name_in_group: 'repository.none',
216 'vcs_test_git': 'repository.read',
217 'vcs_test_hg': 'repository.read',
218 'vcs_test_svn': 'repository.read',
219 }
220
221 # apply same logic of "updated" recursive, but now the anon permissions should be not be impacted
222 new_perm = 'repository.write'
223 perm_updates = [
224 [self.anon.user_id, new_perm, 'user']
225 ]
226 RepoGroupModel().update_permissions(
227 repo_group=self.g1, perm_updates=perm_updates, recursive='all')
228
229 Session().commit()
230 assert repo_perms(self.anon) == {
231 repo_name_in_group: 'repository.none',
232 'vcs_test_git': 'repository.read',
233 'vcs_test_hg': 'repository.read',
234 'vcs_test_svn': 'repository.read',
235 }
236
169 237 def test_propagated_permission_from_users_group_by_explicit_perms_exist(
170 238 self, repo_name):
171 239 # make group
@@ -71,6 +71,9 b' use = egg:rhodecode-enterprise-ce'
71 71 ; enable proxy prefix middleware, defined above
72 72 #filter-with = proxy-prefix
73 73
74 ; control if environmental variables to be expanded into the .ini settings
75 rhodecode.env_expand = false
76
74 77 ; encryption key used to encrypt social plugin tokens,
75 78 ; remote_urls with credentials etc, if not set it defaults to
76 79 ; `beaker.session.secret`
@@ -225,6 +228,13 b' license_token = abra-cada-bra1-rce3'
225 228 ; This flag hides sensitive information on the license page such as token, and license data
226 229 license.hide_license_info = false
227 230
231 ; Import EE license from this license path
232 #license.import_path = %(here)s/rhodecode_enterprise.license
233
234 ; import license 'if-missing' or 'force' (always override)
235 ; if-missing means apply license if it doesn't exist. 'force' option always overrides it
236 license.import_path_mode = if-missing
237
228 238 ; supervisor connection uri, for managing supervisor and logs.
229 239 supervisor.uri =
230 240
@@ -658,6 +668,12 b' vcs.connection_timeout = 3600'
658 668 ; It uses cache_region `cache_repo`
659 669 vcs.methods.cache = false
660 670
671 ; Filesystem location where Git lfs objects should be stored
672 vcs.git.lfs.storage_location = /var/opt/rhodecode_repo_store/.cache/git_lfs_store
673
674 ; Filesystem location where Mercurial largefile objects should be stored
675 vcs.hg.largefiles.storage_location = /var/opt/rhodecode_repo_store/.cache/hg_largefiles_store
676
661 677 ; ####################################################
662 678 ; Subversion proxy support (mod_dav_svn)
663 679 ; Maps RhodeCode repo groups into SVN paths for Apache
General Comments 0
You need to be logged in to leave comments. Login now