From 6ab7f51328ed03ac16936f5b2b79568f8f596e64 Mon Sep 17 00:00:00 2001 From: Jakob Holmgren Date: Mon, 10 Aug 2026 15:02:43 +0200 Subject: [PATCH 1/2] fix: match Scale-Up plan ids case-insensitively Scale-Up v4 Chargebee plan ids are mixed-case (Scale-Up-v4-USD-Yearly), but get_scaleup_plan_version() and ReadOnlyIfNotValidPlanMixin matched plan ids case-sensitively, so v4 organisations were treated as pre-v4 (unlimited audit log visibility) and plan-gated project fields became writable. Part of #8074. Co-Authored-By: Claude Fable 5 --- api/organisations/models.py | 2 +- api/organisations/subscriptions/serializers/mixins.py | 7 ++++++- .../test_unit_subscriptions_serializers_mixins.py | 1 + .../unit/organisations/test_unit_organisations_models.py | 2 ++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/api/organisations/models.py b/api/organisations/models.py index 540e4f9b9355..46dbe634d8e3 100644 --- a/api/organisations/models.py +++ b/api/organisations/models.py @@ -312,7 +312,7 @@ def is_enterprise(self) -> bool: return self.subscription_plan_family == SubscriptionPlanFamily.ENTERPRISE def get_scaleup_plan_version(self) -> int: - if match := re.match(r"scale-up-v(\d+)", self.plan or ""): + if match := re.match(r"scale-up-v(\d+)", self.plan or "", re.IGNORECASE): return int(match.group(1)) return 1 diff --git a/api/organisations/subscriptions/serializers/mixins.py b/api/organisations/subscriptions/serializers/mixins.py index c1093815d099..15a67ba48487 100644 --- a/api/organisations/subscriptions/serializers/mixins.py +++ b/api/organisations/subscriptions/serializers/mixins.py @@ -29,8 +29,13 @@ def get_subscription(self): def __init__(self, *args, **kwargs): # type: ignore[no-untyped-def] super().__init__(*args, **kwargs) + # Chargebee plan ids cannot be renamed and are mixed-case as of + # Scale-Up v4 (e.g. "Scale-Up-v4-USD-Yearly"), so match them + # case-insensitively. self.invalid_plans_regex_matcher = ( - re.compile(self.invalid_plans_regex) if self.invalid_plans_regex else None + re.compile(self.invalid_plans_regex, re.IGNORECASE) + if self.invalid_plans_regex + else None ) def get_fields(self, *args, **kwargs): # type: ignore[no-untyped-def] diff --git a/api/tests/unit/organisations/subscriptions/serializers/test_unit_subscriptions_serializers_mixins.py b/api/tests/unit/organisations/subscriptions/serializers/test_unit_subscriptions_serializers_mixins.py index fefc0ab420f5..68cf93495313 100644 --- a/api/tests/unit/organisations/subscriptions/serializers/test_unit_subscriptions_serializers_mixins.py +++ b/api/tests/unit/organisations/subscriptions/serializers/test_unit_subscriptions_serializers_mixins.py @@ -14,6 +14,7 @@ ( ("invalid-plan-id", ("invalid-plan-id",), ""), ("invalid-plan-id", tuple(), "invalid-.*"), + ("Scale-Up-v4-USD-Yearly", tuple(), r"^(free|startup.*|scale-up.*)$"), ), ) def test_read_only_if_not_valid_plan_mixin__invalid_plan__sets_fields_read_only( diff --git a/api/tests/unit/organisations/test_unit_organisations_models.py b/api/tests/unit/organisations/test_unit_organisations_models.py index 91656dd991ae..9cfae8f3b2ac 100644 --- a/api/tests/unit/organisations/test_unit_organisations_models.py +++ b/api/tests/unit/organisations/test_unit_organisations_models.py @@ -304,6 +304,8 @@ def test_is_auto_seat_upgrade_available__given_plan_and_seat_count__returns_expe ("scale-up", 1), ("startup-v2", 1), (None, 1), + ("Scale-Up-v4-USD-Yearly", 4), + ("Scale-Up-v4-USD-Monthly", 4), ], ) def test_get_scaleup_plan_version__given_plan__returns_expected( From ae75e80b7b150fe14b0ea60d1c033203d8a3d27a Mon Sep 17 00:00:00 2001 From: "flagsmith-engineering[bot]" Date: Mon, 10 Aug 2026 13:08:09 +0000 Subject: [PATCH 2/2] chore: Update documentation artefacts --- .../observability/_events-catalogue.md | 2 +- openapi.yaml | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/docs/deployment-self-hosting/observability/_events-catalogue.md b/docs/docs/deployment-self-hosting/observability/_events-catalogue.md index 591712fced24..af4f83009db0 100644 --- a/docs/docs/deployment-self-hosting/observability/_events-catalogue.md +++ b/docs/docs/deployment-self-hosting/observability/_events-catalogue.md @@ -103,7 +103,7 @@ Attributes: ### `core.encrypted_field.decrypt_failed` Logged at `warning` from: - - `api/core/fields.py:37` + - `api/core/fields.py:62` Attributes: - `exc_info` diff --git a/openapi.yaml b/openapi.yaml index ccfdf0f08579..86cd0495cb33 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -22243,6 +22243,7 @@ components: readOnly: true url: type: string + format: uri maxLength: 200 enabled: type: boolean @@ -24193,6 +24194,7 @@ components: readOnly: true url: type: string + format: uri maxLength: 200 enabled: type: boolean @@ -25045,6 +25047,7 @@ components: url: type: string format: uri + maxLength: 200 enabled: type: boolean created_at: @@ -25066,6 +25069,7 @@ components: readOnly: true url: type: string + format: uri maxLength: 200 secret: type: string @@ -28035,6 +28039,7 @@ components: url: type: string format: uri + maxLength: 200 enabled: type: boolean created_at: @@ -28058,6 +28063,7 @@ components: readOnly: true url: type: string + format: uri maxLength: 200 secret: type: string