chore(release): merged default into stable branch

This commit is contained in:
RhodeCode Admin 2024-10-17 18:15:31 +02:00
commit bf3658ddf7
18 changed files with 317 additions and 85 deletions

View file

@ -1,5 +1,5 @@
[bumpversion]
current_version = 5.2.1
current_version = 5.3.0
message = release: Bump version {current_version} to {new_version}
[bumpversion:file:rhodecode/VERSION]

View file

@ -71,6 +71,9 @@ use = egg:rhodecode-enterprise-ce
; enable proxy prefix middleware, defined above
#filter-with = proxy-prefix
; control if environmental variables to be expanded into the .ini settings
#rhodecode.env_expand = true
; #############
; DEBUG OPTIONS
; #############

View file

@ -71,6 +71,9 @@ use = egg:rhodecode-enterprise-ce
; enable proxy prefix middleware, defined above
#filter-with = proxy-prefix
; control if environmental variables to be expanded into the .ini settings
#rhodecode.env_expand = true
; encryption key used to encrypt social plugin tokens,
; remote_urls with credentials etc, if not set it defaults to
; `beaker.session.secret`

View file

@ -0,0 +1,40 @@
|RCE| 5.2.1 |RNS|
-----------------
Release Date
^^^^^^^^^^^^
- 2024-09-16
New Features
^^^^^^^^^^^^
General
^^^^^^^
Security
^^^^^^^^
Performance
^^^^^^^^^^^
Fixes
^^^^^
- Fixed problems with incorrect user agent errors
Upgrade notes
^^^^^^^^^^^^^
- RhodeCode 5.2.1 is unscheduled bugfix release to address some build issues with 5.2 images

View file

@ -0,0 +1,45 @@
|RCE| 5.3.0 |RNS|
-----------------
Release Date
^^^^^^^^^^^^
- 2024-09-17
New Features
^^^^^^^^^^^^
- System-info: expose rhodecode config for better visibility of set settings for RhodeCode system.
General
^^^^^^^
Security
^^^^^^^^
- Permissions: fixed security problem with apply-to-children from a repo group functionality breaking
permissions for private repositories exposing them despite repo being private.
- Git-lfs: fixed security problem with allowing off-chain attacks to replace OID data without validating hash for already present oids.
This allowed to replace an LFS OID content with malicious request tailored to open RhodeCode server.
Performance
^^^^^^^^^^^
Fixes
^^^^^
- Fixed problems with incorrect user agent errors
Upgrade notes
^^^^^^^^^^^^^
- RhodeCode 5.3.0 is unscheduled security release to address some build issues with 5.X images

View file

@ -9,6 +9,8 @@ Release Notes
.. toctree::
:maxdepth: 1
release-notes-5.3.0.rst
release-notes-5.2.1.rst
release-notes-5.2.0.rst
release-notes-5.1.2.rst
release-notes-5.1.1.rst

View file

@ -1 +1 @@
5.2.1
5.3.0

View file

@ -199,8 +199,12 @@ class AdminSystemInfoSettingsView(BaseAppView):
]
c.rhodecode_data_items = [
(k, v) for k, v in sorted((val('rhodecode_server_config') or {}).items(), key=lambda x: x[0].lower())
]
c.vcsserver_data_items = [
(k, v) for k, v in (val('vcs_server_config') or {}).items()
(k, v) for k, v in sorted((val('vcs_server_config') or {}).items(), key=lambda x: x[0].lower())
]
if snapshot:

View file

@ -100,22 +100,16 @@ class RepoSettingsPermissionsView(RepoAppView):
self.load_default_context()
private_flag = str2bool(self.request.POST.get('private'))
changes = {
'repo_private': private_flag
}
try:
repo = RepoModel().get(self.db_repo.repo_id)
repo.private = private_flag
Session().add(repo)
RepoModel().grant_user_permission(
repo=self.db_repo, user=User.DEFAULT_USER, perm='repository.none'
)
RepoModel().update(repo, **changes)
Session().commit()
h.flash(_('Repository `{}` private mode set successfully').format(self.db_repo_name),
category='success')
# NOTE(dan): we change repo private mode we need to notify all USERS
affected_user_ids = User.get_all_user_ids()
PermissionModel().trigger_permission_flush(affected_user_ids)
except Exception:
log.exception("Exception during update of repository")

View file

@ -109,6 +109,9 @@ class SettingsMaker:
return envvar_value
def env_expand(self):
if self.settings.get('rhodecode.env_expand') == 'false':
return
replaced = {}
for k, v in self.settings.items():
if k not in set_keys:

View file

@ -167,7 +167,8 @@ def _is_request_chunked(environ):
def _maybe_stream_request(environ):
path = get_path_info(environ)
stream = _is_request_chunked(environ)
log.debug('handling request `%s` with stream support: %s', path, stream)
req_method = environ['REQUEST_METHOD']
log.debug('handling scm request: %s `%s` with stream support: %s', req_method, path, stream)
if stream:
# set stream by 256k

View file

@ -665,6 +665,32 @@ def vcs_server_config():
return SysInfoRes(value=value, state=state, human_value=human_value)
@register_sysinfo
def rhodecode_server_config():
import rhodecode
state = STATE_OK_DEFAULT
config = rhodecode.CONFIG.copy()
secrets_lits = [
f'rhodecode_{LicenseModel.LICENSE_DB_KEY}',
'sqlalchemy.db1.url',
'channelstream.secret',
'beaker.session.secret',
'rhodecode.encrypted_values.secret',
'appenlight.api_key',
'smtp_password',
'file_store.objectstore.secret',
'archive_cache.objectstore.secret',
'app.service_api.token',
]
for k in secrets_lits:
if k in config:
config[k] = '**OBFUSCATED**'
value = human_value = config
return SysInfoRes(value=value, state=state, human_value=human_value)
@register_sysinfo
def rhodecode_app_info():
@ -851,6 +877,7 @@ def get_system_info(environ):
'vcs_server': SysInfo(vcs_server)(),
'vcs_server_config': SysInfo(vcs_server_config)(),
'rhodecode_server_config': SysInfo(rhodecode_server_config)(),
'git': SysInfo(git_info)(),
'hg': SysInfo(hg_info)(),

View file

@ -452,15 +452,24 @@ class RepoModel(BaseModel):
setattr(cur_repo, k, val)
new_name = cur_repo.get_new_name(kwargs['repo_name'])
cur_repo.repo_name = new_name
new_name = source_repo_name
if 'repo_name' in kwargs:
new_name = cur_repo.get_new_name(kwargs['repo_name'])
cur_repo.repo_name = new_name
if 'repo_private' in kwargs:
# if private flag is set to True, reset default permission to NONE
set_private_to = kwargs.get('repo_private')
if set_private_to:
EMPTY_PERM = 'repository.none'
RepoModel().grant_user_permission(
repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
)
if set_private_to != cur_repo.private:
# NOTE(dan): we change repo private mode we need to notify all USERS
# this is just by having this value set to a different value then it was before
affected_user_ids = User.get_all_user_ids()
# if private flag is set, reset default permission to NONE
if kwargs.get('repo_private'):
EMPTY_PERM = 'repository.none'
RepoModel().grant_user_permission(
repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
)
if kwargs.get('repo_landing_rev'):
landing_rev_val = kwargs['repo_landing_rev']
RepoModel().set_landing_rev(cur_repo, landing_rev_val)

View file

@ -1,4 +1,4 @@
# Copyright (C) 2011-2023 RhodeCode GmbH
# Copyright (C) 2011-2024 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
@ -30,7 +30,6 @@ import time
import traceback
import string
from zope.cachedescriptors.property import Lazy as LazyProperty
from rhodecode import events
from rhodecode.model import BaseModel
@ -38,7 +37,7 @@ from rhodecode.model.db import (_hash_key, func, or_, in_filter_generator,
Session, RepoGroup, UserRepoGroupToPerm, User, Permission, UserGroupRepoGroupToPerm,
UserGroup, Repository)
from rhodecode.model.permission import PermissionModel
from rhodecode.model.settings import VcsSettingsModel, SettingsModel
from rhodecode.model.settings import SettingsModel
from rhodecode.lib.caching_query import FromCache
from rhodecode.lib.utils2 import action_logger_generic
@ -350,46 +349,45 @@ class RepoGroupModel(BaseModel):
'default_user_changed': None
}
def _set_perm_user(obj, user, perm):
if isinstance(obj, RepoGroup):
self.grant_user_permission(
repo_group=obj, user=user, perm=perm)
elif isinstance(obj, Repository):
def _set_perm_user(_obj: RepoGroup | Repository, _user_obj: User, _perm):
if isinstance(_obj, RepoGroup):
self.grant_user_permission(repo_group=_obj, user=_user_obj, perm=_perm)
elif isinstance(_obj, Repository):
# private repos will not allow to change the default
# permissions using recursive mode
if obj.private and user == User.DEFAULT_USER:
if _obj.private and _user_obj.username == User.DEFAULT_USER:
log.debug('Skipping private repo %s for user %s', _obj, _user_obj)
return
# we set group permission but we have to switch to repo
# permission
perm = perm.replace('group.', 'repository.')
RepoModel().grant_user_permission(
repo=obj, user=user, perm=perm)
# we set group permission, we have to switch to repo permission definition
new_perm = _perm.replace('group.', 'repository.')
RepoModel().grant_user_permission(repo=_obj, user=_user_obj, perm=new_perm)
def _set_perm_group(obj, users_group, perm):
if isinstance(obj, RepoGroup):
self.grant_user_group_permission(
repo_group=obj, group_name=users_group, perm=perm)
elif isinstance(obj, Repository):
# we set group permission but we have to switch to repo
# permission
perm = perm.replace('group.', 'repository.')
RepoModel().grant_user_group_permission(
repo=obj, group_name=users_group, perm=perm)
def _set_perm_group(_obj: RepoGroup | Repository, users_group: UserGroup, _perm):
if isinstance(_obj, RepoGroup):
self.grant_user_group_permission(repo_group=_obj, group_name=users_group, perm=_perm)
elif isinstance(_obj, Repository):
# we set group permission, we have to switch to repo permission definition
new_perm = _perm.replace('group.', 'repository.')
RepoModel().grant_user_group_permission(repo=_obj, group_name=users_group, perm=new_perm)
def _revoke_perm_user(obj, user):
if isinstance(obj, RepoGroup):
self.revoke_user_permission(repo_group=obj, user=user)
elif isinstance(obj, Repository):
RepoModel().revoke_user_permission(repo=obj, user=user)
def _revoke_perm_user(_obj: RepoGroup | Repository, _user_obj: User):
if isinstance(_obj, RepoGroup):
self.revoke_user_permission(repo_group=_obj, user=_user_obj)
elif isinstance(_obj, Repository):
# private repos will not allow to change the default
# permissions using recursive mode, also there's no revocation fo default user, just update
if _user_obj.username == User.DEFAULT_USER:
log.debug('Skipping private repo %s for user %s', _obj, _user_obj)
return
RepoModel().revoke_user_permission(repo=_obj, user=_user_obj)
def _revoke_perm_group(obj, user_group):
if isinstance(obj, RepoGroup):
self.revoke_user_group_permission(
repo_group=obj, group_name=user_group)
elif isinstance(obj, Repository):
RepoModel().revoke_user_group_permission(
repo=obj, group_name=user_group)
def _revoke_perm_group(_obj: RepoGroup | Repository, user_group: UserGroup):
if isinstance(_obj, RepoGroup):
self.revoke_user_group_permission(repo_group=_obj, group_name=user_group)
elif isinstance(_obj, Repository):
RepoModel().revoke_user_group_permission(repo=_obj, group_name=user_group)
# start updates
log.debug('Now updating permissions for %s in recursive mode:%s',
@ -423,7 +421,8 @@ class RepoGroupModel(BaseModel):
for member_id, perm, member_type in perm_updates:
member_id = int(member_id)
if member_type == 'user':
member_name = User.get(member_id).username
member_obj = User.get(member_id)
member_name = member_obj.username
if isinstance(obj, RepoGroup) and obj == repo_group and member_name == User.DEFAULT_USER:
# NOTE(dan): detect if we changed permissions for default user
perm_obj = self.sa.query(UserRepoGroupToPerm) \
@ -434,15 +433,16 @@ class RepoGroupModel(BaseModel):
changes['default_user_changed'] = True
# this updates also current one if found
_set_perm_user(obj, user=member_id, perm=perm)
_set_perm_user(obj, member_obj, perm)
elif member_type == 'user_group':
member_name = UserGroup.get(member_id).users_group_name
if not check_perms or has_group_perm(member_name,
user=cur_user):
_set_perm_group(obj, users_group=member_id, perm=perm)
member_obj = UserGroup.get(member_id)
member_name = member_obj.users_group_name
if not check_perms or has_group_perm(member_name, user=cur_user):
_set_perm_group(obj, member_obj, perm)
else:
raise ValueError("member_type must be 'user' or 'user_group' "
"got {} instead".format(member_type))
raise ValueError(
f"member_type must be 'user' or 'user_group' got {member_type} instead"
)
changes['updated'].append(
{'change_obj': change_obj, 'type': member_type,
@ -452,17 +452,19 @@ class RepoGroupModel(BaseModel):
for member_id, perm, member_type in perm_additions:
member_id = int(member_id)
if member_type == 'user':
member_name = User.get(member_id).username
_set_perm_user(obj, user=member_id, perm=perm)
member_obj = User.get(member_id)
member_name = member_obj.username
_set_perm_user(obj, member_obj, perm)
elif member_type == 'user_group':
# check if we have permissions to alter this usergroup
member_name = UserGroup.get(member_id).users_group_name
if not check_perms or has_group_perm(member_name,
user=cur_user):
_set_perm_group(obj, users_group=member_id, perm=perm)
member_obj = UserGroup.get(member_id)
member_name = member_obj.users_group_name
if not check_perms or has_group_perm(member_name, user=cur_user):
_set_perm_group(obj, member_obj, perm)
else:
raise ValueError("member_type must be 'user' or 'user_group' "
"got {} instead".format(member_type))
raise ValueError(
f"member_type must be 'user' or 'user_group' got {member_type} instead"
)
changes['added'].append(
{'change_obj': change_obj, 'type': member_type,
@ -472,18 +474,19 @@ class RepoGroupModel(BaseModel):
for member_id, perm, member_type in perm_deletions:
member_id = int(member_id)
if member_type == 'user':
member_name = User.get(member_id).username
_revoke_perm_user(obj, user=member_id)
member_obj = User.get(member_id)
member_name = member_obj.username
_revoke_perm_user(obj, member_obj)
elif member_type == 'user_group':
# check if we have permissions to alter this usergroup
member_name = UserGroup.get(member_id).users_group_name
if not check_perms or has_group_perm(member_name,
user=cur_user):
_revoke_perm_group(obj, user_group=member_id)
member_obj = UserGroup.get(member_id)
member_name = member_obj.users_group_name
if not check_perms or has_group_perm(member_name, user=cur_user):
_revoke_perm_group(obj, member_obj)
else:
raise ValueError("member_type must be 'user' or 'user_group' "
"got {} instead".format(member_type))
raise ValueError(
f"member_type must be 'user' or 'user_group' got {member_type} instead"
)
changes['deleted'].append(
{'change_obj': change_obj, 'type': member_type,
'id': member_id, 'name': member_name, 'new_perm': perm})

View file

@ -42,7 +42,7 @@ class UpdateModel(BaseModel):
ver = rhodecode.__version__
log.debug('Checking for upgrade on `%s` server', update_url)
opener = urllib.request.build_opener()
opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
opener.addheaders = [('User-agent', f'RhodeCode-SCM/{ver.strip()}')]
response = opener.open(update_url)
response_data = response.read()
data = json.loads(response_data)

View file

@ -29,7 +29,21 @@
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">${_('VCS Server')}</h3>
<h3 class="panel-title">${_('RhodeCode Server Config')}</h3>
</div>
<div class="panel-body">
<dl class="dl-horizontal settings dt-400">
% for dt, dd in c.rhodecode_data_items:
<dt>${dt}${':' if dt else '---'}</dt>
<dd>${dd}${'' if dt else '---'}</dd>
% endfor
</dl>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">${_('VCS Server Config')}</h3>
</div>
<div class="panel-body">
<dl class="dl-horizontal settings dt-400">

View file

@ -166,6 +166,74 @@ class TestPermissions(object):
test_user_group.user = org_owner
def test_propagated_permissions_from_repo_group_to_private_repo(self, repo_name):
# make group
self.g1 = fixture.create_repo_group('TOP_LEVEL', skip_if_exists=True)
# both perms should be read !
assert group_perms(self.anon) == {
'TOP_LEVEL': 'group.read'
}
# Create repo inside the TOP_LEVEL
repo_name_in_group = RepoGroup.url_sep().join([self.g1.group_name, 'test_perm_on_private_repo'])
self.test_repo = fixture.create_repo(name=repo_name_in_group,
repo_type='hg',
repo_group=self.g1,
cur_user=self.u1,)
assert repo_perms(self.anon) == {
repo_name_in_group: 'repository.read',
'vcs_test_git': 'repository.read',
'vcs_test_hg': 'repository.read',
'vcs_test_svn': 'repository.read',
}
# Now change default user permissions
new_perm = 'repository.write'
perm_updates = [
[self.anon.user_id, new_perm, 'user']
]
RepoGroupModel().update_permissions(
repo_group=self.g1, perm_updates=perm_updates, recursive='all')
Session().commit()
assert repo_perms(self.anon) == {
repo_name_in_group: new_perm,
'vcs_test_git': 'repository.read',
'vcs_test_hg': 'repository.read',
'vcs_test_svn': 'repository.read',
}
# NOW MARK repo as private
changes = {
'repo_private': True
}
repo = RepoModel().get_by_repo_name(repo_name_in_group)
RepoModel().update(repo, **changes)
Session().commit()
# Private repo sets 'none' permission for default user
assert repo_perms(self.anon) == {
repo_name_in_group: 'repository.none',
'vcs_test_git': 'repository.read',
'vcs_test_hg': 'repository.read',
'vcs_test_svn': 'repository.read',
}
# apply same logic of "updated" recursive, but now the anon permissions should be not be impacted
new_perm = 'repository.write'
perm_updates = [
[self.anon.user_id, new_perm, 'user']
]
RepoGroupModel().update_permissions(
repo_group=self.g1, perm_updates=perm_updates, recursive='all')
Session().commit()
assert repo_perms(self.anon) == {
repo_name_in_group: 'repository.none',
'vcs_test_git': 'repository.read',
'vcs_test_hg': 'repository.read',
'vcs_test_svn': 'repository.read',
}
def test_propagated_permission_from_users_group_by_explicit_perms_exist(
self, repo_name):
# make group

View file

@ -71,6 +71,9 @@ use = egg:rhodecode-enterprise-ce
; enable proxy prefix middleware, defined above
#filter-with = proxy-prefix
; control if environmental variables to be expanded into the .ini settings
rhodecode.env_expand = false
; encryption key used to encrypt social plugin tokens,
; remote_urls with credentials etc, if not set it defaults to
; `beaker.session.secret`
@ -225,6 +228,13 @@ license_token = abra-cada-bra1-rce3
; This flag hides sensitive information on the license page such as token, and license data
license.hide_license_info = false
; Import EE license from this license path
#license.import_path = %(here)s/rhodecode_enterprise.license
; import license 'if-missing' or 'force' (always override)
; if-missing means apply license if it doesn't exist. 'force' option always overrides it
license.import_path_mode = if-missing
; supervisor connection uri, for managing supervisor and logs.
supervisor.uri =
@ -658,6 +668,12 @@ vcs.connection_timeout = 3600
; It uses cache_region `cache_repo`
vcs.methods.cache = false
; Filesystem location where Git lfs objects should be stored
vcs.git.lfs.storage_location = /var/opt/rhodecode_repo_store/.cache/git_lfs_store
; Filesystem location where Mercurial largefile objects should be stored
vcs.hg.largefiles.storage_location = /var/opt/rhodecode_repo_store/.cache/hg_largefiles_store
; ####################################################
; Subversion proxy support (mod_dav_svn)
; Maps RhodeCode repo groups into SVN paths for Apache