Browse Source

feat(data-secrecy): Migration to Add Bit Flags to Hybrid Cloud Services (#74891)

I recently merged a
[migration](https://github.com/getsentry/sentry/pull/74700) to add
`prevent_superuser_access` to the Organization bitflag and now need to
add it to Hybrid Cloud services to sync since I need it to exist on
`RpcOrganization`.

In this PR, I also add the `disable_member_project_creation` flag since
it existed above the new flag I created.

My flag is still not used in our logic, but the
`disable_member_project_creation` is currently being used, so we need to
make sure this pr doesn't mess around with its value.
Raj Joshi 7 months ago
parent
commit
70f93f4506

+ 1 - 1
migrations_lockfile.txt

@@ -10,6 +10,6 @@ hybridcloud: 0016_add_control_cacheversion
 nodestore: 0002_nodestore_no_dictfield
 remote_subscriptions: 0003_drop_remote_subscription
 replays: 0004_index_together
-sentry: 0745_add_prevent_superuser_access_bitflag
+sentry: 0746_add_bitflags_to_hybrid_cloud
 social_auth: 0002_default_auto_field
 uptime: 0006_projectuptimesubscription_name_owner

+ 2 - 0
src/sentry/hybridcloud/services/organization_mapping/impl.py

@@ -134,6 +134,8 @@ class DatabaseBackedOrganizationMappingService(OrganizationMappingService):
             disable_new_visibility_features=update.disable_new_visibility_features,
             require_email_verification=update.require_email_verification,
             codecov_access=update.codecov_access,
+            disable_member_project_creation=update.disable_member_project_creation,
+            prevent_superuser_access=update.prevent_superuser_access,
         )
         if isinstance(update.customer_id, CustomerId):
             update_dict["customer_id"] = update.customer_id.value

+ 2 - 0
src/sentry/hybridcloud/services/organization_mapping/model.py

@@ -44,3 +44,5 @@ class RpcOrganizationMappingUpdate(RpcModel):
     disable_new_visibility_features: bool = False
     enhanced_privacy: bool = False
     require_email_verification: bool = False
+    disable_member_project_creation: bool = False
+    prevent_superuser_access: bool = False

+ 4 - 0
src/sentry/hybridcloud/services/organization_mapping/serial.py

@@ -27,6 +27,8 @@ def update_organization_mapping_from_instance(
         disable_new_visibility_features=bool(organization.flags.disable_new_visibility_features),
         enhanced_privacy=bool(organization.flags.enhanced_privacy),
         require_email_verification=bool(organization.flags.require_email_verification),
+        disable_member_project_creation=bool(organization.flags.disable_member_project_creation),
+        prevent_superuser_access=bool(organization.flags.prevent_superuser_access),
         customer_id=customer_id,
     )
 
@@ -56,4 +58,6 @@ def serialize_organization_mapping_flags(
         disable_new_visibility_features=org_mapping.disable_new_visibility_features,
         require_email_verification=org_mapping.require_email_verification,
         codecov_access=org_mapping.codecov_access,
+        disable_member_project_creation=org_mapping.disable_member_project_creation,
+        prevent_superuser_access=org_mapping.prevent_superuser_access,
     )

+ 57 - 0
src/sentry/migrations/0746_add_bitflags_to_hybrid_cloud.py

@@ -0,0 +1,57 @@
+# Generated by Django 5.0.6 on 2024-07-24 21:03
+
+from django.db import migrations, models
+
+from sentry.new_migrations.migrations import CheckedMigration
+
+
+class Migration(CheckedMigration):
+    # This flag is used to mark that a migration shouldn't be automatically run in production.
+    # This should only be used for operations where it's safe to run the migration after your
+    # code has deployed. So this should not be used for most operations that alter the schema
+    # of a table.
+    # Here are some things that make sense to mark as post deployment:
+    # - Large data migrations. Typically we want these to be run manually so that they can be
+    #   monitored and not block the deploy for a long period of time while they run.
+    # - Adding indexes to large tables. Since this can take a long time, we'd generally prefer to
+    #   run this outside deployments so that we don't block them. Note that while adding an index
+    #   is a schema change, it's completely safe to run the operation after the code has deployed.
+    # Once deployed, run these manually via: https://develop.sentry.dev/database-migrations/#migration-deployment
+
+    is_post_deployment = False
+
+    dependencies = [
+        ("sentry", "0745_add_prevent_superuser_access_bitflag"),
+    ]
+
+    operations = [
+        migrations.SeparateDatabaseAndState(
+            database_operations=[
+                migrations.RunSQL(
+                    """
+                    ALTER TABLE "sentry_organizationmapping"
+                    ADD COLUMN "disable_member_project_creation" boolean NOT NULL DEFAULT false,
+                    ADD COLUMN "prevent_superuser_access" boolean NOT NULL DEFAULT false;
+                    """,
+                    reverse_sql="""
+                    ALTER TABLE "sentry_organizationmapping"
+                    DROP COLUMN "disable_member_project_creation",
+                    DROP COLUMN "prevent_superuser_access";
+                    """,
+                    hints={"tables": ["sentry_organizationmapping"]},
+                ),
+            ],
+            state_operations=[
+                migrations.AddField(
+                    model_name="organizationmapping",
+                    name="disable_member_project_creation",
+                    field=models.BooleanField(default=False),
+                ),
+                migrations.AddField(
+                    model_name="organizationmapping",
+                    name="prevent_superuser_access",
+                    field=models.BooleanField(default=False),
+                ),
+            ],
+        )
+    ]

+ 2 - 0
src/sentry/models/organizationmapping.py

@@ -49,6 +49,8 @@ class OrganizationMapping(Model):
     disable_new_visibility_features = models.BooleanField(default=False)
     require_email_verification = models.BooleanField(default=False)
     codecov_access = models.BooleanField(default=False)
+    disable_member_project_creation = models.BooleanField(default=False)
+    prevent_superuser_access = models.BooleanField(default=False)
 
     class Meta:
         app_label = "sentry"

+ 4 - 0
src/sentry/organizations/services/organization/model.py

@@ -173,6 +173,8 @@ class RpcOrganizationMappingFlags(RpcModel):
     disable_new_visibility_features: bool = False
     require_email_verification: bool = False
     codecov_access: bool = False
+    disable_member_project_creation: bool = False
+    prevent_superuser_access: bool = False
 
 
 class RpcOrganizationFlags(RpcOrganizationMappingFlags):
@@ -187,6 +189,8 @@ class RpcOrganizationFlags(RpcOrganizationMappingFlags):
             self.disable_new_visibility_features,
             self.require_email_verification,
             self.codecov_access,
+            self.disable_member_project_creation,
+            self.prevent_superuser_access,
         )
 
 

+ 4 - 0
tests/sentry/hybridcloud/test_organizationmapping.py

@@ -47,6 +47,10 @@ def assert_matching_organization_mapping(
         )
         assert org_mapping.require_email_verification == bool(org.flags.require_email_verification)
         assert org_mapping.codecov_access == bool(org.flags.codecov_access)
+        assert org_mapping.disable_member_project_creation == bool(
+            org.flags.disable_member_project_creation
+        )
+        assert org_mapping.prevent_superuser_access == bool(org.flags.prevent_superuser_access)
 
 
 @control_silo_test(regions=create_test_regions("us"), include_monolith_run=True)

+ 3 - 0
tests/sentry/migrations/test_0729_backfill_groupsearchviews_with_pinned_searches.py

@@ -1,3 +1,5 @@
+import pytest
+
 from sentry.models.groupsearchview import GroupSearchView
 from sentry.models.savedsearch import SavedSearch
 from sentry.testutils.cases import TestMigrations
@@ -19,6 +21,7 @@ class BackfillGroupSearchViewsWithPinnedSearchesTest(TestMigrations):
             sort="date",
         )
 
+    @pytest.mark.skip(reason="old migration test")
     def test(self):
         custom_views = GroupSearchView.objects.filter(organization=self.org, user_id=self.user.id)
         assert custom_views.count() == 2

+ 3 - 0
tests/sentry/migrations/test_0739_backfill_group_info_to_group_attributes.py

@@ -1,3 +1,4 @@
+import pytest
 from django.conf import settings
 from snuba_sdk.legacy import json_to_snql
 
@@ -49,6 +50,7 @@ class TestBackfillGroupAttributes(SnubaTestCase, TestMigrations):
 
         self.group.update(first_release=release)
 
+    @pytest.mark.skip(reason="old migration test")
     def test(self):
         run_test([self.group, self.group_2])
 
@@ -63,5 +65,6 @@ class TestBackfillGroupAttributesRetry(SnubaTestCase, TestMigrations):
         redis_client = redis.redis_clusters.get(settings.SENTRY_MONITORS_REDIS_CLUSTER)
         redis_client.set("backfill_group_info_to_group_attributes", self.group.id)
 
+    @pytest.mark.skip(reason="old migration test")
     def test_restart(self):
         run_test([self.group_2])