Skip to content

Commit

Permalink
initial commit for rollout and upgrade logic
Browse files Browse the repository at this point in the history
  • Loading branch information
zlwaterfield committed Jan 28, 2025
1 parent 6a70e47 commit 357ee83
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 5 deletions.
3 changes: 2 additions & 1 deletion frontend/src/scenes/dashboard/DashboardCollaborators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ export function DashboardCollaboration({ dashboardId }: { dashboardId: Dashboard
return null
}

if (newAccessControl) {
// Only render the new access control if they are not using the old dashboard permissions (v1) and have the feature flag enabled
if (newAccessControl && dashboard.access_control_version === 'v2') {
return (
<AccessControlPopoutCTA
callback={() => {
Expand Down
22 changes: 19 additions & 3 deletions frontend/src/scenes/settings/environment/TeamAccessControl.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IconCrown, IconLeave, IconLock, IconUnlock } from '@posthog/icons'
import { LemonButton, LemonSelect, LemonSelectOption, LemonSnack, LemonSwitch, LemonTable } from '@posthog/lemon-ui'
import { LemonBanner, LemonButton, LemonSelect, LemonSelectOption, LemonSnack, LemonSwitch, LemonTable } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { RestrictionScope, useRestrictedArea } from 'lib/components/RestrictedArea'
import { upgradeModalLogic } from 'lib/components/UpgradeModal/upgradeModalLogic'
Expand Down Expand Up @@ -209,21 +209,37 @@ export function TeamMembers(): JSX.Element | null {
export function TeamAccessControl(): JSX.Element {
const { currentOrganization, currentOrganizationLoading } = useValues(organizationLogic)
const { currentTeam, currentTeamLoading } = useValues(teamLogic)
const { updateCurrentTeam } = useActions(teamLogic)
const { updateCurrentTeam, updateAccessControlVersion } = useActions(teamLogic)
const { guardAvailableFeature } = useValues(upgradeModalLogic)

const restrictionReason = useRestrictedArea({
minimumAccessLevel: OrganizationMembershipLevel.Admin,
})

const newAccessControl = useFeatureFlag('ROLE_BASED_ACCESS_CONTROL')
if (newAccessControl) {

console.log('currentTeam', currentTeam)
// Only render the new access control if they are not using the old dashboard permissions (v1) and have the feature flag enabled
if (newAccessControl && currentTeam?.access_control_version === 'v2') {
return <AccessControlObject resource="project" resource_id={`${currentTeam?.id}`} />
}

return (
<>
<p>
{newAccessControl && (
<LemonBanner
className="mb-4"
type="warning"
action={{
children: 'Upgrade now',
onClick: () => updateAccessControlVersion(),
}}
>
You're eligible to upgrade to our new access control system. This will allow you to better manage
access to your project.
</LemonBanner>
)}
{currentTeam?.access_control ? (
<>
This project is{' '}
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/scenes/settings/environment/teamMembersLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ export const teamMembersLogic = kea<teamMembersLogicType>([
(explicitMembersLoading, organizationMembersLoading) =>
explicitMembersLoading || organizationMembersLoading,
],
// isUpdateAccessControlVersionLoading: [
// (s) => [s.updateAccessControlVersionLoading],
// (updateAccessControlVersionLoading) => updateAccessControlVersionLoading,
// ],
admins: [
(s) => [s.allMembers],
(allMembers: FusedTeamMemberType[]) => allMembers.filter(({ level }) => level >= TeamMembershipLevel.Admin),
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/scenes/teamLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@ export const teamLogic = kea<teamLogicType>([
await api.update(`api/environments/${values.currentTeamId}/complete_product_onboarding`, {
product_type,
}),
updateAccessControlVersion: async () => {
await api.create(`api/environments/${teamLogic.values.currentTeamId}/migrate_team_rbac`)
// Reload the page to reflect the new access control version
window.location.reload()
},
},
],
})),
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ export interface TeamBasicType extends WithAccessControl {
timezone: string
/** Whether the project is private. */
access_control: boolean
access_control_version: 'v1' | 'v2'
}

export interface CorrelationConfigType {
Expand Down Expand Up @@ -1885,6 +1886,7 @@ export interface DashboardBasicType extends WithAccessControl {
restriction_level: DashboardRestrictionLevel
effective_restriction_level: DashboardRestrictionLevel
effective_privilege_level: DashboardPrivilegeLevel
access_control_version: 'v1' | 'v2'
tags?: string[]
/** Purely local value to determine whether the dashboard should be highlighted, e.g. as a fresh duplicate. */
_highlight?: boolean
Expand Down
12 changes: 11 additions & 1 deletion posthog/api/dashboards/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ class DashboardBasicSerializer(
created_by = UserBasicSerializer(read_only=True)
effective_privilege_level = serializers.SerializerMethodField()
effective_restriction_level = serializers.SerializerMethodField()
access_control_version = serializers.SerializerMethodField()
is_shared = serializers.BooleanField(source="is_sharing_enabled", read_only=True, required=False)

class Meta:
Expand All @@ -112,6 +113,7 @@ class Meta:
"effective_restriction_level",
"effective_privilege_level",
"user_access_level",
"access_control_version",
]
read_only_fields = fields

Expand All @@ -125,6 +127,12 @@ def get_effective_privilege_level(self, dashboard: Dashboard) -> Dashboard.Privi
return Dashboard.PrivilegeLevel.CAN_VIEW
return self.user_permissions.dashboard(dashboard).effective_privilege_level

def get_access_control_version(self, dashboard: Dashboard) -> str:
# This effectively means that the dashboard they are using the old dashboard permissions
if dashboard.restriction_level > Dashboard.RestrictionLevel.EVERYONE_IN_PROJECT_CAN_EDIT:
return "v1"
return "v2"


class DashboardSerializer(DashboardBasicSerializer):
tiles = serializers.SerializerMethodField()
Expand All @@ -136,6 +144,7 @@ class DashboardSerializer(DashboardBasicSerializer):
delete_insights = serializers.BooleanField(write_only=True, required=False, default=False)
effective_privilege_level = serializers.SerializerMethodField()
effective_restriction_level = serializers.SerializerMethodField()
access_control_version = serializers.SerializerMethodField()
is_shared = serializers.BooleanField(source="is_sharing_enabled", read_only=True, required=False)

class Meta:
Expand All @@ -161,6 +170,7 @@ class Meta:
"effective_restriction_level",
"effective_privilege_level",
"user_access_level",
"access_control_version",
]
read_only_fields = ["creation_mode", "effective_restriction_level", "is_shared", "user_access_level"]

Expand Down Expand Up @@ -439,7 +449,7 @@ def get_variables(self, dashboard: Dashboard) -> dict:
return variables_override

return dashboard.variables

def validate(self, data):
if data.get("use_dashboard", None) and data.get("use_template", None):
raise serializers.ValidationError("`use_dashboard` and `use_template` cannot be used together")
Expand Down
10 changes: 10 additions & 0 deletions posthog/api/team.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
get_ip_address,
get_week_start_for_country_code,
)
from ee.models.rbac.access_control import AccessControl


class PremiumMultiProjectPermissions(BasePermission): # TODO: Rename to include "Env" in name
Expand Down Expand Up @@ -154,6 +155,7 @@ class TeamSerializer(serializers.ModelSerializer, UserPermissionsSerializerMixin
has_group_types = serializers.SerializerMethodField()
live_events_token = serializers.SerializerMethodField()
product_intents = serializers.SerializerMethodField()
access_control_version = serializers.SerializerMethodField()

class Meta:
model = Team
Expand Down Expand Up @@ -220,6 +222,7 @@ class Meta:
"user_access_level",
"default_data_theme",
"revenue_tracking_config",
"access_control_version",
)
read_only_fields = (
"id",
Expand All @@ -236,6 +239,7 @@ class Meta:
"person_on_events_querying_enabled",
"live_events_token",
"user_access_level",
"access_control_version",
)

def to_representation(self, instance):
Expand All @@ -251,6 +255,12 @@ def get_effective_membership_level(self, team: Team) -> Optional[OrganizationMem
# TODO: Map from user_access_controls
return self.user_permissions.team(team).effective_membership_level

def get_access_control_version(self, team: Team) -> str:
# If they have a private project (team/environment) and no access control objects on the team then assume they are using the old access control
if bool(team.access_control) and not AccessControl.objects.filter(team_id=team.id).exists():
return "v1"
return "v2"

def get_has_group_types(self, team: Team) -> bool:
return GroupTypeMapping.objects.filter(project_id=team.project_id).exists()

Expand Down

0 comments on commit 357ee83

Please sign in to comment.