From dc3502fed0145803072a094854daf39db4479f14 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Thu, 19 Feb 2026 19:38:09 -0500 Subject: [PATCH] Fix session.save() for cookie sessions, add vcsserver/rc_license stubs - Guard all session.save() calls with hasattr checks for CookieSession compatibility (CookieSession auto-saves via response callbacks) - Guard beaker-specific _set_cookie_expires and _update_cookie_out in login views - Defer ldap scope_labels to property to avoid class-level AttributeError when python-ldap is not installed - Add vcsserver stub package to allow imports without full vcsserver - Add rc_license stub for CE edition (no license management) --- rhodecode/apps/login/views.py | 8 ++++--- rhodecode/apps/my_account/views/my_account.py | 3 ++- .../plugins/services/ldap_dao.py | 16 +++++++++----- rhodecode/lib/_vendor/rc_license/__init__.py | 1 + rhodecode/lib/_vendor/rc_license/models.py | 21 +++++++++++++++++++ rhodecode/lib/_vendor/vcsserver/__init__.py | 1 + rhodecode/lib/_vendor/vcsserver/hooks.py | 1 + .../lib/_vendor/vcsserver/lib/__init__.py | 0 .../lib/_vendor/vcsserver/lib/vcs_common.py | 2 ++ rhodecode/lib/helpers.py | 3 ++- 10 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 rhodecode/lib/_vendor/rc_license/__init__.py create mode 100644 rhodecode/lib/_vendor/rc_license/models.py create mode 100644 rhodecode/lib/_vendor/vcsserver/__init__.py create mode 100644 rhodecode/lib/_vendor/vcsserver/hooks.py create mode 100644 rhodecode/lib/_vendor/vcsserver/lib/__init__.py create mode 100644 rhodecode/lib/_vendor/vcsserver/lib/vcs_common.py diff --git a/rhodecode/apps/login/views.py b/rhodecode/apps/login/views.py index c68d50ce..06a041ff 100644 --- a/rhodecode/apps/login/views.py +++ b/rhodecode/apps/login/views.py @@ -73,18 +73,20 @@ def store_user_in_session(session, user_identifier, remember=False): Session().commit() # If they want to be remembered, update the cookie - if remember: + if remember and hasattr(session, '_set_cookie_expires'): _year = datetime.datetime.now() + datetime.timedelta(seconds=60 * 60 * 24 * 365) session._set_cookie_expires(_year) - session.save() + if hasattr(session, 'save'): + session.save() safe_cs = cs.copy() safe_cs["password"] = "****" log.info("user %s is now authenticated and stored in session, session attrs %s", user_identifier, safe_cs) # dumps session attrs back to cookie - session._update_cookie_out() + if hasattr(session, '_update_cookie_out'): + session._update_cookie_out() # we set new cookie headers = None if session.request["set_cookie"]: diff --git a/rhodecode/apps/my_account/views/my_account.py b/rhodecode/apps/my_account/views/my_account.py index 882c9391..9fb4311e 100644 --- a/rhodecode/apps/my_account/views/my_account.py +++ b/rhodecode/apps/my_account/views/my_account.py @@ -204,7 +204,8 @@ class MyAccountView(BaseAppView, DataGridAppView): else: instance = c.auth_user.get_instance() self.session.setdefault("rhodecode_user", {}).update({"password": md5_safe(instance.password)}) - self.session.save() + if hasattr(self.session, 'save'): + self.session.save() h.flash(_("Successfully updated password"), category="success") raise HTTPFound(self.request.route_path("my_account_password")) diff --git a/rhodecode/authentication/plugins/services/ldap_dao.py b/rhodecode/authentication/plugins/services/ldap_dao.py index 1c18c239..415215af 100644 --- a/rhodecode/authentication/plugins/services/ldap_dao.py +++ b/rhodecode/authentication/plugins/services/ldap_dao.py @@ -19,11 +19,17 @@ except ImportError: class LdapDao(AuthLdapBase): default_tls_cert_dir = "/etc/openldap/cacerts" - scope_labels = { - ldap.SCOPE_BASE: "SCOPE_BASE", - ldap.SCOPE_ONELEVEL: "SCOPE_ONELEVEL", - ldap.SCOPE_SUBTREE: "SCOPE_SUBTREE", - } + @staticmethod + def _scope_labels(): + return { + ldap.SCOPE_BASE: "SCOPE_BASE", + ldap.SCOPE_ONELEVEL: "SCOPE_ONELEVEL", + ldap.SCOPE_SUBTREE: "SCOPE_SUBTREE", + } + + @property + def scope_labels(self): + return self._scope_labels() def __init__( self, diff --git a/rhodecode/lib/_vendor/rc_license/__init__.py b/rhodecode/lib/_vendor/rc_license/__init__.py new file mode 100644 index 00000000..3d51ce9e --- /dev/null +++ b/rhodecode/lib/_vendor/rc_license/__init__.py @@ -0,0 +1 @@ +# rc_license stub — CE edition does not include license management diff --git a/rhodecode/lib/_vendor/rc_license/models.py b/rhodecode/lib/_vendor/rc_license/models.py new file mode 100644 index 00000000..4580b70f --- /dev/null +++ b/rhodecode/lib/_vendor/rc_license/models.py @@ -0,0 +1,21 @@ +# rc_license.models stub — CE edition + + +class LicenseModel: + """Stub license model for Community Edition.""" + + @staticmethod + def get_license_data(): + return {} + + @staticmethod + def get_license_info(): + return {"edition": "CE"} + + +def apply_license(*args, **kwargs): + pass + + +def apply_license_from_file(*args, **kwargs): + pass diff --git a/rhodecode/lib/_vendor/vcsserver/__init__.py b/rhodecode/lib/_vendor/vcsserver/__init__.py new file mode 100644 index 00000000..02688684 --- /dev/null +++ b/rhodecode/lib/_vendor/vcsserver/__init__.py @@ -0,0 +1 @@ +# vcsserver stub — allows rhodecode to import without the full vcsserver package diff --git a/rhodecode/lib/_vendor/vcsserver/hooks.py b/rhodecode/lib/_vendor/vcsserver/hooks.py new file mode 100644 index 00000000..567736dd --- /dev/null +++ b/rhodecode/lib/_vendor/vcsserver/hooks.py @@ -0,0 +1 @@ +# Stub — vcsserver hooks not available without full vcsserver package diff --git a/rhodecode/lib/_vendor/vcsserver/lib/__init__.py b/rhodecode/lib/_vendor/vcsserver/lib/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/rhodecode/lib/_vendor/vcsserver/lib/vcs_common.py b/rhodecode/lib/_vendor/vcsserver/lib/vcs_common.py new file mode 100644 index 00000000..9e83bc7b --- /dev/null +++ b/rhodecode/lib/_vendor/vcsserver/lib/vcs_common.py @@ -0,0 +1,2 @@ +# Re-export from rhodecode's own vcs_common module +from rhodecode.lib.vcs_common import * diff --git a/rhodecode/lib/helpers.py b/rhodecode/lib/helpers.py index c0dded4f..fa663169 100644 --- a/rhodecode/lib/helpers.py +++ b/rhodecode/lib/helpers.py @@ -819,7 +819,8 @@ class Flash(object): for msg in session.pop_flash(): messages.append(_Message("notice", msg)) - session.save() + if hasattr(session, 'save'): + session.save() return messages def json_alerts(self, session=None, request=None):