diff --git a/frontend/__snapshots__/scenes-other-onboarding--onboarding-billing--dark.png b/frontend/__snapshots__/scenes-other-onboarding--onboarding-billing--dark.png index 032e85e82d703..a4e1979e55cf6 100644 Binary files a/frontend/__snapshots__/scenes-other-onboarding--onboarding-billing--dark.png and b/frontend/__snapshots__/scenes-other-onboarding--onboarding-billing--dark.png differ diff --git a/frontend/src/layout/ErrorProjectUnavailable.tsx b/frontend/src/layout/ErrorProjectUnavailable.tsx index 8aa6719e4346b..1888661bb42de 100644 --- a/frontend/src/layout/ErrorProjectUnavailable.tsx +++ b/frontend/src/layout/ErrorProjectUnavailable.tsx @@ -2,6 +2,7 @@ import { Link } from '@posthog/lemon-ui' import { useValues } from 'kea' import { PageHeader } from 'lib/components/PageHeader' import { useEffect, useState } from 'react' +import { CreateOrganizationModal } from 'scenes/organization/CreateOrganizationModal' import { urls } from 'scenes/urls' import { userLogic } from 'scenes/userLogic' @@ -42,7 +43,9 @@ export function ErrorProjectUnavailable(): JSX.Element { return (
diff --git a/posthog/api/test/test_organization.py b/posthog/api/test/test_organization.py index 5d15a5c94b565..2396f78e3c557 100644 --- a/posthog/api/test/test_organization.py +++ b/posthog/api/test/test_organization.py @@ -4,6 +4,9 @@ from posthog.models.personal_api_key import PersonalAPIKey, hash_key_value from posthog.models.utils import generate_random_token_personal from posthog.test.base import APIBaseTest +from posthog.api.organization import OrganizationSerializer +from rest_framework.test import APIRequestFactory +from posthog.user_permissions import UserPermissions class TestOrganizationAPI(APIBaseTest): @@ -212,3 +215,62 @@ def create_organization(name: str) -> Organization: with real world scenarios. """ return Organization.objects.create(name=name) + + +class TestOrganizationSerializer(APIBaseTest): + def setUp(self): + super().setUp() + self.factory = APIRequestFactory() + self.request = self.factory.get("/") + self.request.user = self.user + + # Create a mock view with user_permissions + class MockView: + def __init__(self, user_permissions): + self.user_permissions = user_permissions + + self.view = MockView(UserPermissions(self.user)) + self.context = {"request": self.request, "view": self.view} + + def test_get_teams_with_no_org(self): + # Clear current_team reference before deleting organization + self.user.current_team = None + self.user.current_organization = None + self.user.save() + + self.organization.delete() + + serializer = OrganizationSerializer(context=self.context) + self.assertEqual(serializer.user_permissions.team_ids_visible_for_user, []) + + def test_get_teams_with_single_org_no_teams(self): + # Delete default team created by APIBaseTest + self.team.delete() + + serializer = OrganizationSerializer(self.organization, context=self.context) + self.assertEqual(serializer.get_teams(self.organization), []) + + def test_get_teams_with_single_org_multiple_teams(self): + team2 = Team.objects.create(organization=self.organization, name="Test Team 2") + team3 = Team.objects.create(organization=self.organization, name="Test Team 3") + + serializer = OrganizationSerializer(self.organization, context=self.context) + teams = serializer.get_teams(self.organization) + + self.assertEqual(len(teams), 3) + team_names = {team["name"] for team in teams} + self.assertEqual(team_names, {self.team.name, team2.name, team3.name}) + + def test_get_teams_with_multiple_orgs(self): + org2, _, _ = Organization.objects.bootstrap(self.user) + team2 = Team.objects.create(organization=org2, name="Org 2 Team") + + serializer = OrganizationSerializer(self.organization, context=self.context) + teams1 = serializer.get_teams(self.organization) + teams2 = serializer.get_teams(org2) + + self.assertEqual(len(teams1), 1) + self.assertEqual(teams1[0]["name"], self.team.name) + + self.assertEqual(len(teams2), 2) + self.assertEqual([teams2[0]["name"], teams2[1]["name"]], ["Default project", team2.name]) diff --git a/posthog/rbac/test/test_user_access_control.py b/posthog/rbac/test/test_user_access_control.py index 69eb51431c587..d398f246589a5 100644 --- a/posthog/rbac/test/test_user_access_control.py +++ b/posthog/rbac/test/test_user_access_control.py @@ -4,6 +4,7 @@ from posthog.models.organization import OrganizationMembership from posthog.models.team.team import Team from posthog.models.user import User +from posthog.models.organization import Organization from posthog.rbac.user_access_control import UserAccessControl from posthog.test.base import BaseTest @@ -78,6 +79,23 @@ def test_no_organization_id_passed(self): assert user_access_control._organization is None assert user_access_control._user_role_ids == [] + def test_organization_with_no_project_or_team(self): + organization = Organization.objects.create(name="No project or team") + user = User.objects.create_and_join(organization, "no-project-or-team@posthog.com", "testtest") + user_access_control = UserAccessControl(user, organization_id=organization.id) + + assert user_access_control._organization_membership is not None + assert user_access_control._organization == organization + + def test_organization_with_no_project_or_team_and_no_organization_id(self): + organization = Organization.objects.create(name="No project or team") + user = User.objects.create_and_join(organization, "no-project-or-team@posthog.com", "testtest") + user_access_control = UserAccessControl(user) + + assert user_access_control._organization_membership is None + assert user_access_control._organization is None + assert user_access_control._user_role_ids == [] + def test_without_available_product_features(self): self.organization.available_product_features = [] self.organization.save() diff --git a/posthog/rbac/user_access_control.py b/posthog/rbac/user_access_control.py index 12c82e61e8ddd..4cff394bc9c40 100644 --- a/posthog/rbac/user_access_control.py +++ b/posthog/rbac/user_access_control.py @@ -197,7 +197,7 @@ def _access_controls_filters_for_queryset(self, resource: APIScopeObject) -> dic if self._team and resource != "project": common_filters["team_id"] = self._team.id - else: + elif self._organization_id: common_filters["team__organization_id"] = str(self._organization_id) return common_filters diff --git a/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr b/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr index b386da3b52700..80349bbc75121 100644 --- a/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr +++ b/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr @@ -640,12 +640,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '438' + AND "ee_accesscontrol"."resource_id" = '437' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '438' + AND "ee_accesscontrol"."resource_id" = '437' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -1688,12 +1688,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -2441,12 +2441,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -3129,12 +3129,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -3881,12 +3881,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -4597,12 +4597,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -5395,12 +5395,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -5659,12 +5659,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -6091,12 +6091,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -6556,12 +6556,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -7248,12 +7248,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL @@ -7997,12 +7997,12 @@ LEFT OUTER JOIN "posthog_organizationmembership" ON ("ee_accesscontrol"."organization_member_id" = "posthog_organizationmembership"."id") WHERE (("ee_accesscontrol"."organization_member_id" IS NULL AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("posthog_organizationmembership"."user_id" = 99999 AND "ee_accesscontrol"."resource" = 'project' - AND "ee_accesscontrol"."resource_id" = '445' + AND "ee_accesscontrol"."resource_id" = '444' AND "ee_accesscontrol"."role_id" IS NULL AND "ee_accesscontrol"."team_id" = 99999) OR ("ee_accesscontrol"."organization_member_id" IS NULL