Skip to content

Commit

Permalink
Started adding access level
Browse files Browse the repository at this point in the history
  • Loading branch information
benjackwhite committed Mar 22, 2024
1 parent 9734354 commit f2a6f50
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 17 deletions.
1 change: 1 addition & 0 deletions frontend/src/lib/api.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export const MOCK_DEFAULT_TEAM: TeamType = {
autocapture_exceptions_opt_in: false,
autocapture_exceptions_errors_to_ignore: [],
effective_membership_level: OrganizationMembershipLevel.Admin,
user_access_level: 'admin',
access_control: true,
has_group_types: true,
primary_dashboard: 1,
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lib/components/RestrictedArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export function useRestrictedArea({ scope, minimumAccessLevel }: UseRestrictedAr
if (!isAuthenticatedTeam(currentTeam)) {
return 'Loading current project…'
}
// TODO: Check the user_access_level
scopeAccessLevel = currentTeam.effective_membership_level
} else {
if (!currentOrganization) {
Expand Down
10 changes: 6 additions & 4 deletions frontend/src/scenes/teamLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const teamLogic = kea<teamLogicType>([
}
}

const patchedTeam = (await api.update(`api/projects/${values.currentTeam.id}`, payload)) as TeamType
const patchedTeam = await api.update(`api/projects/${values.currentTeam.id}`, payload)
breakpoint()

actions.loadUser()
Expand Down Expand Up @@ -148,7 +148,8 @@ export const teamLogic = kea<teamLogicType>([
(selectors) => [selectors.currentTeam, selectors.currentTeamLoading],
// If project has been loaded and is still null, it means the user just doesn't have access.
(currentTeam, currentTeamLoading): boolean =>
!currentTeam?.effective_membership_level && !currentTeamLoading,
(!currentTeam?.effective_membership_level || currentTeam.user_access_level === 'none') &&
!currentTeamLoading,
],
demoOnlyProject: [
(selectors) => [selectors.currentTeam, organizationLogic.selectors.currentOrganization],
Expand All @@ -170,8 +171,9 @@ export const teamLogic = kea<teamLogicType>([
isTeamTokenResetAvailable: [
(selectors) => [selectors.currentTeam],
(currentTeam): boolean =>
!!currentTeam?.effective_membership_level &&
currentTeam.effective_membership_level >= OrganizationMembershipLevel.Admin,
(!!currentTeam?.effective_membership_level &&
currentTeam.effective_membership_level >= OrganizationMembershipLevel.Admin) ||
currentTeam?.user_access_level === 'admin',
],
testAccountFilterWarningLabels: [
(selectors) => [selectors.currentTeam],
Expand Down
19 changes: 12 additions & 7 deletions frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@ export interface ColumnConfig {
active: ColumnChoice
}

export type WithAccessControl = {
user_access_level: 'none' | 'member' | 'admin' | 'viewer' | 'editor'
}

interface UserBaseType {
uuid: string
distinct_id: string
Expand Down Expand Up @@ -420,7 +424,7 @@ export interface SessionRecordingAIConfig {
important_user_properties: string[]
}

export interface TeamType extends TeamBasicType {
export interface TeamType extends TeamBasicType, WithAccessControl {
created_at: string
updated_at: string
anonymize_ips: boolean
Expand Down Expand Up @@ -3527,12 +3531,13 @@ export type NotebookListItemType = {
last_modified_by?: UserBasicType | null
}

export type NotebookType = NotebookListItemType & {
content: JSONContent | null
version: number
// used to power text-based search
text_content?: string | null
}
export type NotebookType = NotebookListItemType &
WithAccessControl & {
content: JSONContent | null
version: number
// used to power text-based search
text_content?: string | null
}

export enum NotebookNodeType {
Mention = 'ph-mention',
Expand Down
5 changes: 4 additions & 1 deletion posthog/api/team.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
get_organization_from_view,
)
from posthog.rbac.access_control_api_mixin import AccessControlViewSetMixin
from posthog.rbac.user_access_control import UserAccessControlSerializerMixin
from posthog.tasks.demo_create_data import create_data_for_demo_team
from posthog.user_permissions import UserPermissions, UserPermissionsSerializerMixin
from posthog.utils import get_ip_address, get_week_start_for_country_code
Expand Down Expand Up @@ -115,7 +116,7 @@ class Meta:
]


class TeamSerializer(serializers.ModelSerializer, UserPermissionsSerializerMixin):
class TeamSerializer(serializers.ModelSerializer, UserPermissionsSerializerMixin, UserAccessControlSerializerMixin):
effective_membership_level = serializers.SerializerMethodField()
has_group_types = serializers.SerializerMethodField()
groups_on_events_querying_enabled = serializers.SerializerMethodField()
Expand Down Expand Up @@ -167,6 +168,7 @@ class Meta:
"extra_settings",
"has_completed_onboarding_for",
"surveys_opt_in",
"user_access_level",
)
read_only_fields = (
"id",
Expand All @@ -180,6 +182,7 @@ class Meta:
"has_group_types",
"person_on_events_querying_enabled",
"groups_on_events_querying_enabled",
"user_access_level",
)

def get_effective_membership_level(self, team: Team) -> Optional[OrganizationMembership.Level]:
Expand Down
11 changes: 8 additions & 3 deletions posthog/rbac/user_access_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,13 @@ class UserAccessControlSerializerMixin(serializers.Serializer):
help_text="The effective access level the user has for this object",
)

@cached_property
def user_access_control(self) -> UserAccessControl:
# NOTE: The user_access_control is typically on the view but in specific cases such as the posthog_app_context it is set at the context level
if "user_access_control" in self.context:
return self.context["user_access_control"]
return self.context["view"].user_access_control

def get_user_access_level(self, obj: Model) -> Optional[str]:
access_control = cast(UserAccessControl, self.context["view"].user_access_control).access_control_for_object(
obj
)
access_control = self.user_access_control.access_control_for_object(obj)
return access_control.access_level if access_control else None
14 changes: 12 additions & 2 deletions posthog/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ def render_template(
from posthog.api.team import TeamSerializer
from posthog.api.user import UserSerializer
from posthog.user_permissions import UserPermissions
from posthog.rbac.user_access_control import UserAccessControl
from posthog.views import preflight_check

posthog_app_context = {
Expand All @@ -362,17 +363,26 @@ def render_template(
elif request.user.pk:
user = cast("User", request.user)
user_permissions = UserPermissions(user=user, team=user.team)
user_access_control = UserAccessControl(user=user, team=user.team)
user_serialized = UserSerializer(
request.user,
context={"request": request, "user_permissions": user_permissions},
context={
"request": request,
"user_permissions": user_permissions,
"user_access_control": user_access_control,
},
many=False,
)
posthog_app_context["current_user"] = user_serialized.data
posthog_distinct_id = user_serialized.data.get("distinct_id")
if user.team:
team_serialized = TeamSerializer(
user.team,
context={"request": request, "user_permissions": user_permissions},
context={
"request": request,
"user_permissions": user_permissions,
"user_access_control": user_access_control,
},
many=False,
)
posthog_app_context["current_team"] = team_serialized.data
Expand Down

0 comments on commit f2a6f50

Please sign in to comment.