diff --git a/requirements.txt b/requirements.txt --- a/requirements.txt +++ b/requirements.txt @@ -33,7 +33,7 @@ channelstream==0.7.1 gevent==24.2.1 greenlet==3.0.3 zope.event==5.0.0 - zope.interface==6.4.post2 + zope.interface==7.0.3 itsdangerous==1.1.0 marshmallow==2.18.0 pyramid==2.0.2 @@ -46,7 +46,7 @@ channelstream==0.7.1 venusian==3.0.0 webob==1.8.7 zope.deprecation==5.0.0 - zope.interface==6.4.post2 + zope.interface==7.0.3 pyramid-jinja2==2.10 jinja2==3.1.2 markupsafe==2.1.2 @@ -61,7 +61,7 @@ channelstream==0.7.1 venusian==3.0.0 webob==1.8.7 zope.deprecation==5.0.0 - zope.interface==6.4.post2 + zope.interface==7.0.3 zope.deprecation==5.0.0 python-dateutil==2.8.2 six==1.16.0 @@ -87,13 +87,13 @@ dogpile.cache==1.3.3 pbr==5.11.1 formencode==2.1.0 six==1.16.0 -fsspec==2024.6.0 +fsspec==2024.9.0 gunicorn==23.0.0 packaging==24.1 gevent==24.2.1 greenlet==3.0.3 zope.event==5.0.0 - zope.interface==6.4.post2 + zope.interface==7.0.3 ipython==8.26.0 decorator==5.1.1 jedi==0.19.1 @@ -167,7 +167,7 @@ nbconvert==7.7.3 tinycss2==1.2.1 webencodings==0.5.1 traitlets==5.14.3 -orjson==3.10.6 +orjson==3.10.7 paste==3.10.1 premailer==3.10.0 cachetools==5.3.3 @@ -201,13 +201,13 @@ pyramid-mailer==0.15.1 venusian==3.0.0 webob==1.8.7 zope.deprecation==5.0.0 - zope.interface==6.4.post2 + zope.interface==7.0.3 repoze.sendmail==4.4.1 - transaction==3.1.0 - zope.interface==6.4.post2 - zope.interface==6.4.post2 - transaction==3.1.0 - zope.interface==6.4.post2 + transaction==5.0.0 + zope.interface==7.0.3 + zope.interface==7.0.3 + transaction==5.0.0 + zope.interface==7.0.3 pyramid-mako==1.1.0 mako==1.2.4 markupsafe==2.1.2 @@ -221,7 +221,7 @@ pyramid-mako==1.1.0 venusian==3.0.0 webob==1.8.7 zope.deprecation==5.0.0 - zope.interface==6.4.post2 + zope.interface==7.0.3 python-ldap==3.4.3 pyasn1==0.4.8 pyasn1-modules==0.2.8 @@ -236,13 +236,13 @@ python3-saml==1.16.0 xmlsec==1.3.14 lxml==5.3.0 pyyaml==6.0.1 -redis==5.0.4 +redis==5.1.0 async-timeout==4.0.3 regex==2022.10.31 routes==2.5.1 repoze.lru==0.7 six==1.16.0 -s3fs==2024.6.0 +s3fs==2024.9.0 aiobotocore==2.13.0 aiohttp==3.9.5 aiosignal==1.3.1 @@ -269,7 +269,7 @@ s3fs==2024.6.0 yarl==1.9.4 idna==3.4 multidict==6.0.5 - fsspec==2024.6.0 + fsspec==2024.9.0 simplejson==3.19.2 sshpubkeys==3.3.1 cryptography==40.0.2 diff --git a/rhodecode/apps/admin/__init__.py b/rhodecode/apps/admin/__init__.py --- a/rhodecode/apps/admin/__init__.py +++ b/rhodecode/apps/admin/__init__.py @@ -67,7 +67,7 @@ def admin_routes(config): config.add_route( name='admin_security_modify_allowed_vcs_client_versions', - pattern='/security/modify/allowed_vcs_client_versions') + pattern=ADMIN_PREFIX + '/security/modify/allowed_vcs_client_versions') config.add_view( AdminSecurityView, attr='vcs_whitelisted_client_versions_edit', diff --git a/rhodecode/apps/admin/views/security.py b/rhodecode/apps/admin/views/security.py --- a/rhodecode/apps/admin/views/security.py +++ b/rhodecode/apps/admin/views/security.py @@ -17,13 +17,8 @@ # and proprietary license terms, please see https://rhodecode.com/licenses/ import logging -import formencode -from rhodecode import BACKENDS from rhodecode.apps._base import BaseAppView -from rhodecode.model.meta import Session -from rhodecode.model.settings import SettingsModel -from rhodecode.model.forms import WhitelistedVcsClientsForm from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator log = logging.getLogger(__name__) @@ -42,31 +37,10 @@ class AdminSecurityView(BaseAppView): c.active = 'security' return self._get_template_context(c) + @LoginRequired() @HasPermissionAllDecorator('hg.admin') - def vcs_whitelisted_client_versions_edit(self): - _ = self.request.translate + def admin_security_modify_allowed_vcs_client_versions(self): c = self.load_default_context() - render_ctx = {} - settings = SettingsModel() - form = WhitelistedVcsClientsForm(_, )() - if self.request.method == 'POST': - try: - result = form.to_python(self.request.POST) - for k, v in result.items(): - if v: - setting = settings.create_or_update_setting(name=f'{k}_allowed_clients', val=v) - Session().add(setting) - Session().commit() - - except formencode.Invalid as errors: - render_ctx.update({ - 'errors': errors.error_dict - }) - for key in BACKENDS.keys(): - verbose_name = f"initial_{key}" - if existing := settings.get_setting_by_name(name=f'{key}_allowed_clients'): - render_ctx[verbose_name] = existing.app_settings_value - else: - render_ctx[verbose_name] = '*' - return self._get_template_context(c, **render_ctx) + c.active = 'security' + return self._get_template_context(c) diff --git a/rhodecode/apps/admin/views/settings.py b/rhodecode/apps/admin/views/settings.py --- a/rhodecode/apps/admin/views/settings.py +++ b/rhodecode/apps/admin/views/settings.py @@ -82,7 +82,7 @@ class AdminSettingsView(BaseAppView): if k == '/': k = 'root_path' - if k in ['push_ssl', 'publish', 'enabled']: + if k in ['publish', 'enabled']: v = str2bool(v) if k.find('.') != -1: @@ -164,7 +164,6 @@ class AdminSettingsView(BaseAppView): return Response(html) try: - model.update_global_ssl_setting(form_result['web_push_ssl']) model.update_global_hook_settings(form_result) model.create_or_update_global_svn_settings(form_result) diff --git a/rhodecode/config/config_maker.py b/rhodecode/config/config_maker.py --- a/rhodecode/config/config_maker.py +++ b/rhodecode/config/config_maker.py @@ -52,7 +52,8 @@ def sanitize_settings_and_apply_defaults default=False, parser='bool') - logging_conf = jn(os.path.dirname(global_config.get('__file__')), 'logging.ini') + ini_loc = os.path.dirname(global_config.get('__file__')) + logging_conf = jn(ini_loc, 'logging.ini') settings_maker.enable_logging(logging_conf, level='INFO' if debug_enabled else 'DEBUG') # Default includes, possible to change as a user @@ -95,6 +96,11 @@ def sanitize_settings_and_apply_defaults settings_maker.make_setting('gzip_responses', False, parser='bool') settings_maker.make_setting('startup.import_repos', 'false', parser='bool') + # License settings. + settings_maker.make_setting('license.hide_license_info', False, parser='bool') + settings_maker.make_setting('license.import_path', jn(ini_loc, 'rhodecode_enterprise.license')) + settings_maker.make_setting('license.import_path_mode', 'if-missing') + # statsd settings_maker.make_setting('statsd.enabled', False, parser='bool') settings_maker.make_setting('statsd.statsd_host', 'statsd-exporter', parser='string') diff --git a/rhodecode/config/middleware.py b/rhodecode/config/middleware.py --- a/rhodecode/config/middleware.py +++ b/rhodecode/config/middleware.py @@ -50,7 +50,7 @@ from rhodecode.lib.utils2 import Attribu from rhodecode.lib.exc_tracking import store_exception, format_exc from rhodecode.subscribers import ( scan_repositories_if_enabled, write_js_routes_if_enabled, - write_metadata_if_needed, write_usage_data) + write_metadata_if_needed, write_usage_data, import_license_if_present) from rhodecode.lib.statsd_client import StatsdClient log = logging.getLogger(__name__) @@ -400,7 +400,8 @@ def includeme(config, auth_resources=Non pyramid.events.ApplicationCreated) config.add_subscriber(write_js_routes_if_enabled, pyramid.events.ApplicationCreated) - + config.add_subscriber(import_license_if_present, + pyramid.events.ApplicationCreated) # Set the default renderer for HTML templates to mako. config.add_mako_renderer('.html') diff --git a/rhodecode/lib/db_manage.py b/rhodecode/lib/db_manage.py --- a/rhodecode/lib/db_manage.py +++ b/rhodecode/lib/db_manage.py @@ -570,7 +570,6 @@ class DbManage(object): self.create_ui_settings(path) ui_config = [ - ('web', 'push_ssl', 'False'), ('web', 'allow_archive', 'gz zip bz2'), ('web', 'allow_push', '*'), ('web', 'baseurl', '/'), diff --git a/rhodecode/lib/middleware/simplevcs.py b/rhodecode/lib/middleware/simplevcs.py --- a/rhodecode/lib/middleware/simplevcs.py +++ b/rhodecode/lib/middleware/simplevcs.py @@ -339,21 +339,6 @@ class SimpleVCS(object): log.exception('Failed to read http scheme') return 'http' - def _check_ssl(self, environ, start_response): - """ - Checks the SSL check flag and returns False if SSL is not present - and required True otherwise - """ - org_proto = environ['wsgi._org_proto'] - # check if we have SSL required ! if not it's a bad request ! - require_ssl = str2bool(self.repo_vcs_config.get('web', 'push_ssl')) - if require_ssl and org_proto == 'http': - log.debug( - 'Bad request: detected protocol is `%s` and ' - 'SSL/HTTPS is required.', org_proto) - return False - return True - def _get_default_cache_ttl(self): # take AUTH_CACHE_TTL from the `rhodecode` auth plugin plugin = loadplugin('egg:rhodecode-enterprise-ce#rhodecode') @@ -373,12 +358,6 @@ class SimpleVCS(object): meta.Session.remove() def _handle_request(self, environ, start_response): - if not self._check_ssl(environ, start_response): - reason = ('SSL required, while RhodeCode was unable ' - 'to detect this as SSL request') - log.debug('User not allowed to proceed, %s', reason) - return HTTPNotAcceptable(reason)(environ, start_response) - if not self.url_repo_name: log.warning('Repository name is empty: %s', self.url_repo_name) # failed to get repo name, we fail now diff --git a/rhodecode/lib/middleware/vcs.py b/rhodecode/lib/middleware/vcs.py --- a/rhodecode/lib/middleware/vcs.py +++ b/rhodecode/lib/middleware/vcs.py @@ -159,11 +159,18 @@ def detect_vcs_request(environ, backends # favicon often requested by browsers 'favicon.ico', + # static files no detection + '_static++', + + # debug-toolbar + '_debug_toolbar++', + # e.g /_file_store/download '_file_store++', # login - "_admin/login", + f"{ADMIN_PREFIX}/login", + f"{ADMIN_PREFIX}/logout", # 2fa f"{ADMIN_PREFIX}/check_2fa", @@ -178,12 +185,6 @@ def detect_vcs_request(environ, backends # _admin/my_account is safe too f'{ADMIN_PREFIX}/my_account++', - # static files no detection - '_static++', - - # debug-toolbar - '_debug_toolbar++', - # skip ops ping, status f'{ADMIN_PREFIX}/ops/ping', f'{ADMIN_PREFIX}/ops/status', @@ -193,11 +194,14 @@ def detect_vcs_request(environ, backends '++/repo_creating_check' ] + path_info = get_path_info(environ) path_url = path_info.lstrip('/') req_method = environ.get('REQUEST_METHOD') for item in white_list: + item = item.lstrip('/') + if item.endswith('++') and path_url.startswith(item[:-2]): log.debug('path `%s` in whitelist (match:%s), skipping...', path_url, item) return handler diff --git a/rhodecode/lib/rc_commands/setup_rc.py b/rhodecode/lib/rc_commands/setup_rc.py --- a/rhodecode/lib/rc_commands/setup_rc.py +++ b/rhodecode/lib/rc_commands/setup_rc.py @@ -108,11 +108,10 @@ def command(ini_path, force_yes, user, e dbmanage.create_permissions() dbmanage.populate_default_permissions() if apply_license_key: - try: - from rc_license.models import apply_trial_license_if_missing - apply_trial_license_if_missing(force=True) - except ImportError: - pass + from rhodecode.model.license import apply_license_from_file + license_file_path = config.get('license.import_path') + if license_file_path: + apply_license_from_file(license_file_path, force=True) Session().commit() diff --git a/rhodecode/lib/utils.py b/rhodecode/lib/utils.py --- a/rhodecode/lib/utils.py +++ b/rhodecode/lib/utils.py @@ -391,8 +391,7 @@ def prepare_config_data(clear_session=Tr safe_str(setting.section), safe_str(setting.key), safe_str(setting.value))) if setting.key == 'push_ssl': - # force set push_ssl requirement to False, rhodecode - # handles that + # force set push_ssl requirement to False this is deprecated, and we must force it to False config.append(( safe_str(setting.section), safe_str(setting.key), False)) config_getter = ConfigGet() diff --git a/rhodecode/model/forms.py b/rhodecode/model/forms.py --- a/rhodecode/model/forms.py +++ b/rhodecode/model/forms.py @@ -468,7 +468,6 @@ def ApplicationUiSettingsForm(localizer) _ = localizer class _ApplicationUiSettingsForm(_BaseVcsSettingsForm): - web_push_ssl = v.StringBoolean(if_missing=False) extensions_hggit = v.StringBoolean(if_missing=False) new_svn_branch = v.ValidSvnPattern(localizer, section='vcs_svn_branch') new_svn_tag = v.ValidSvnPattern(localizer, section='vcs_svn_tag') diff --git a/rhodecode/model/license.py b/rhodecode/model/license.py new file mode 100644 --- /dev/null +++ b/rhodecode/model/license.py @@ -0,0 +1,17 @@ + +def apply_license(*args, **kwargs): + pass + +try: + from rc_license.models import apply_license +except ImportError: + pass + + +def apply_license_from_file(*args, **kwargs): + pass + +try: + from rc_license.models import apply_license_from_file +except ImportError: + pass diff --git a/rhodecode/model/settings.py b/rhodecode/model/settings.py --- a/rhodecode/model/settings.py +++ b/rhodecode/model/settings.py @@ -499,7 +499,6 @@ class VcsSettingsModel(object): SVN_BRANCH_SECTION = 'vcs_svn_branch' SVN_TAG_SECTION = 'vcs_svn_tag' - SSL_SETTING = ('web', 'push_ssl') PATH_SETTING = ('paths', '/') def __init__(self, sa=None, repo=None): @@ -704,10 +703,6 @@ class VcsSettingsModel(object): # branch/tags patterns self._create_svn_settings(self.global_settings, data) - def update_global_ssl_setting(self, value): - self._create_or_update_ui( - self.global_settings, *self.SSL_SETTING, value=value) - @assert_repo_settings def delete_repo_svn_pattern(self, id_): ui = self.repo_settings.UiDbModel.get(id_) diff --git a/rhodecode/subscribers.py b/rhodecode/subscribers.py --- a/rhodecode/subscribers.py +++ b/rhodecode/subscribers.py @@ -205,7 +205,7 @@ def write_usage_data(event): return def get_update_age(dest_file): - now = datetime.datetime.utcnow() + now = datetime.datetime.now(datetime.UTC) with open(dest_file, 'rb') as f: data = ext_json.json.loads(f.read()) @@ -216,10 +216,9 @@ def write_usage_data(event): return 0 - utc_date = datetime.datetime.utcnow() + utc_date = datetime.datetime.now(datetime.UTC) hour_quarter = int(math.ceil((utc_date.hour + utc_date.minute/60.0) / 6.)) - fname = '.rc_usage_{date.year}{date.month:02d}{date.day:02d}_{hour}.json'.format( - date=utc_date, hour=hour_quarter) + fname = f'.rc_usage_{utc_date.year}{utc_date.month:02d}{utc_date.day:02d}_{hour_quarter}.json' ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__')) usage_dir = os.path.join(ini_loc, '.rcusage') @@ -314,6 +313,22 @@ def write_js_routes_if_enabled(event): log.exception('Failed to write routes.js into %s', jsroutes_file_path) +def import_license_if_present(event): + """ + This is subscribed to the `pyramid.events.ApplicationCreated` event. It + does a import license key based on a presence of the file. + """ + settings = event.app.registry.settings + + license_file_path = settings.get('license.import_path') + force = settings.get('license.import_path_mode') == 'force' + if license_file_path: + from rhodecode.model.meta import Session + from rhodecode.model.license import apply_license_from_file + apply_license_from_file(license_file_path, force=force) + Session().commit() + + class Subscriber(object): """ Base class for subscribers to the pyramid event system. diff --git a/rhodecode/templates/admin/security/edit_allowed_vcs_client_versions.mako b/rhodecode/templates/admin/security/edit_allowed_vcs_client_versions.mako deleted file mode 100644 --- a/rhodecode/templates/admin/security/edit_allowed_vcs_client_versions.mako +++ /dev/null @@ -1,62 +0,0 @@ - - -
-
- ${h.secure_form(h.route_path('check_2fa'), request=request, id='allowed_clients_form')} -

- ${h.text('git', class_="form-control", value=initial_git)}

-

- ${h.text('hg', class_="form-control", value=initial_hg)}

-

- ${h.text('svn', class_="form-control", value=initial_svn)}

- %for k, v in errors.items(): - ${k}: ${v} -
- %endfor -

${_('Set rules for allowed git, hg or svn client versions. You can set exact version (for example 2.0.9) or use comparison operators to set earliest or latest version (>=2.6.0)')}

- - ${h.submit('send', _('Save'), class_="btn btn-primary")} - ${h.end_form()} -
-
diff --git a/rhodecode/templates/admin/security/security.mako b/rhodecode/templates/admin/security/security.mako --- a/rhodecode/templates/admin/security/security.mako +++ b/rhodecode/templates/admin/security/security.mako @@ -38,42 +38,13 @@

${_('Allowed client versions')}

- %if c.rhodecode_edition_id != 'EE':

${_('This feature is available in RhodeCode EE edition only. Contact {sales_email} to obtain a trial license.').format(sales_email='sales@rhodecode.com')|n}

${_('Some outdated client versions may have security vulnerabilities. This section have rules for whitelisting versions of clients for Git, Mercurial and SVN.')}

- %else: -
-
- %endif
+ - - diff --git a/rhodecode/templates/base/vcs_settings.mako b/rhodecode/templates/base/vcs_settings.mako --- a/rhodecode/templates/base/vcs_settings.mako +++ b/rhodecode/templates/base/vcs_settings.mako @@ -5,22 +5,7 @@ <%def name="vcs_settings_fields(suffix='', svn_branch_patterns=None, svn_tag_patterns=None, repo_type=None, display_globals=False, **kwargs)"> % if display_globals: -
-
-

${_('General')}

-
-
-
-
- ${h.checkbox('web_push_ssl' + suffix, 'True')} - -
-
- ${_('Activate to set RhodeCode to require SSL for pushing or pulling. If SSL certificate is missing it will return a HTTP Error 406: Not Acceptable.')} -
-
-
-
+ % endif % if display_globals or repo_type in ['git', 'hg']: diff --git a/rhodecode/tests/lib/middleware/test_simplehg.py b/rhodecode/tests/lib/middleware/test_simplehg.py --- a/rhodecode/tests/lib/middleware/test_simplehg.py +++ b/rhodecode/tests/lib/middleware/test_simplehg.py @@ -120,7 +120,6 @@ def test_get_config(user_util, baseapp, expected_config = [ ('vcs_svn_tag', 'ff89f8c714d135d865f44b90e5413b88de19a55f', '/tags/*'), - ('web', 'push_ssl', 'False'), ('web', 'allow_push', '*'), ('web', 'allow_archive', 'gz zip bz2'), ('web', 'baseurl', '/'), diff --git a/rhodecode/tests/lib/middleware/test_simplevcs.py b/rhodecode/tests/lib/middleware/test_simplevcs.py --- a/rhodecode/tests/lib/middleware/test_simplevcs.py +++ b/rhodecode/tests/lib/middleware/test_simplevcs.py @@ -239,7 +239,6 @@ class TestShadowRepoExposure(object): """ controller = StubVCSController( baseapp.config.get_settings(), request_stub.registry) - controller._check_ssl = mock.Mock() controller.is_shadow_repo = True controller._action = 'pull' controller._is_shadow_repo_dir = True @@ -267,7 +266,6 @@ class TestShadowRepoExposure(object): """ controller = StubVCSController( baseapp.config.get_settings(), request_stub.registry) - controller._check_ssl = mock.Mock() controller.is_shadow_repo = True controller._action = 'pull' controller._is_shadow_repo_dir = False @@ -291,7 +289,6 @@ class TestShadowRepoExposure(object): """ controller = StubVCSController( baseapp.config.get_settings(), request_stub.registry) - controller._check_ssl = mock.Mock() controller.is_shadow_repo = True controller._action = 'push' controller.stub_response_body = (b'dummy body value',) diff --git a/rhodecode/tests/models/settings/test_vcs_settings.py b/rhodecode/tests/models/settings/test_vcs_settings.py --- a/rhodecode/tests/models/settings/test_vcs_settings.py +++ b/rhodecode/tests/models/settings/test_vcs_settings.py @@ -578,17 +578,6 @@ class TestCreateOrUpdateRepoHgSettings(o assert str(exc_info.value) == 'Repository is not specified' -class TestUpdateGlobalSslSetting(object): - def test_updates_global_hg_settings(self): - model = VcsSettingsModel() - with mock.patch.object(model, '_create_or_update_ui') as create_mock: - model.update_global_ssl_setting('False') - Session().commit() - - create_mock.assert_called_once_with( - model.global_settings, 'web', 'push_ssl', value='False') - - class TestCreateOrUpdateGlobalHgSettings(object): FORM_DATA = { 'extensions_largefiles': False,