0
0
mirror of https://github.com/PostHog/posthog.git synced 2024-11-21 13:39:22 +01:00

feat: rbac initial set up (#25745)

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Zach Waterfield 2024-10-29 17:17:27 -04:00 committed by GitHub
parent 447a18930f
commit 8d01d5ef54
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 128 additions and 85 deletions

View File

@ -1,10 +1,10 @@
from rest_framework import exceptions, mixins, serializers, viewsets
from rest_framework.permissions import SAFE_METHODS, BasePermission
from ee.api.role import RoleSerializer
from ee.api.rbac.role import RoleSerializer
from ee.models.feature_flag_role_access import FeatureFlagRoleAccess
from ee.models.organization_resource_access import OrganizationResourceAccess
from ee.models.role import Role
from ee.models.rbac.organization_resource_access import OrganizationResourceAccess
from ee.models.rbac.role import Role
from posthog.api.feature_flag import FeatureFlagSerializer
from posthog.api.routing import TeamAndOrgViewSetMixin
from posthog.models import FeatureFlag

View File

@ -1,7 +1,7 @@
from rest_framework import mixins, serializers, viewsets
from ee.api.role import RolePermissions
from ee.models.organization_resource_access import OrganizationResourceAccess
from ee.api.rbac.role import RolePermissions
from ee.models.rbac.organization_resource_access import OrganizationResourceAccess
from posthog.api.routing import TeamAndOrgViewSetMixin

View File

@ -5,8 +5,8 @@ from rest_framework import mixins, serializers, viewsets
from rest_framework.permissions import SAFE_METHODS, BasePermission
from ee.models.feature_flag_role_access import FeatureFlagRoleAccess
from ee.models.organization_resource_access import OrganizationResourceAccess
from ee.models.role import Role, RoleMembership
from ee.models.rbac.organization_resource_access import OrganizationResourceAccess
from ee.models.rbac.role import Role, RoleMembership
from posthog.api.organization_member import OrganizationMemberSerializer
from posthog.api.routing import TeamAndOrgViewSetMixin
from posthog.api.shared import UserBasicSerializer

View File

@ -1,6 +1,6 @@
from ee.api.test.base import APILicensedTest
from ee.models.organization_resource_access import OrganizationResourceAccess
from ee.models.role import Role, RoleMembership
from ee.models.rbac.organization_resource_access import OrganizationResourceAccess
from ee.models.rbac.role import Role, RoleMembership
from posthog.models.feature_flag import FeatureFlag
from posthog.models.organization import OrganizationMembership

View File

@ -2,8 +2,8 @@ from rest_framework import status
from ee.api.test.base import APILicensedTest
from ee.models.feature_flag_role_access import FeatureFlagRoleAccess
from ee.models.organization_resource_access import OrganizationResourceAccess
from ee.models.role import Role
from ee.models.rbac.organization_resource_access import OrganizationResourceAccess
from ee.models.rbac.role import Role
from posthog.models.feature_flag import FeatureFlag
from posthog.models.organization import OrganizationMembership
from posthog.models.user import User

View File

@ -2,7 +2,7 @@ from django.db import IntegrityError
from rest_framework import status
from ee.api.test.base import APILicensedTest
from ee.models.organization_resource_access import OrganizationResourceAccess
from ee.models.rbac.organization_resource_access import OrganizationResourceAccess
from posthog.models.organization import Organization, OrganizationMembership
from posthog.test.base import QueryMatchingTest, snapshot_postgres_queries, FuzzyInt

View File

@ -2,8 +2,8 @@ from django.db import IntegrityError
from rest_framework import status
from ee.api.test.base import APILicensedTest
from ee.models.organization_resource_access import OrganizationResourceAccess
from ee.models.role import Role
from ee.models.rbac.organization_resource_access import OrganizationResourceAccess
from ee.models.rbac.role import Role
from posthog.models.organization import Organization, OrganizationMembership

View File

@ -1,7 +1,7 @@
from rest_framework import status
from ee.api.test.base import APILicensedTest
from ee.models.role import Role, RoleMembership
from ee.models.rbac.role import Role, RoleMembership
from posthog.models.organization import Organization, OrganizationMembership
from posthog.models.user import User

View File

@ -5,7 +5,7 @@ from .feature_flag_role_access import FeatureFlagRoleAccess
from .hook import Hook
from .license import License
from .property_definition import EnterprisePropertyDefinition
from .role import Role, RoleMembership
from .rbac.role import Role, RoleMembership
__all__ = [
"EnterpriseEventDefinition",

View File

@ -2,6 +2,8 @@ from django.db import models
from posthog.models.organization import Organization
# NOTE: This will be deprecated in favour of the AccessControl model
class OrganizationResourceAccess(models.Model):
class AccessLevel(models.IntegerChoices):

View File

@ -1,6 +1,6 @@
from django.db import models
from ee.models.organization_resource_access import OrganizationResourceAccess
from ee.models.rbac.organization_resource_access import OrganizationResourceAccess
from posthog.models.utils import UUIDModel

View File

@ -6,6 +6,7 @@ from django.urls import include
from django.urls.conf import path
from ee.api import integration
from .api.rbac import organization_resource_access, role
from .api import (
authentication,
@ -15,8 +16,6 @@ from .api import (
feature_flag_role_access,
hooks,
license,
organization_resource_access,
role,
sentry_stats,
subscription,
)
@ -49,6 +48,7 @@ def extend_api_router() -> None:
"organization_role_memberships",
["organization_id", "role_id"],
)
# Start: routes to be deprecated
project_feature_flags_router.register(
r"role_access",
feature_flag_role_access.FeatureFlagRoleAccessViewSet,
@ -61,6 +61,7 @@ def extend_api_router() -> None:
"organization_resource_access",
["organization_id"],
)
# End: routes to be deprecated
register_grandfathered_environment_nested_viewset(r"hooks", hooks.HookViewSet, "environment_hooks", ["team_id"])
register_grandfathered_environment_nested_viewset(
r"explicit_members",

View File

@ -220,6 +220,7 @@ export const FEATURE_FLAGS = {
LEGACY_ACTION_WEBHOOKS: 'legacy-action-webhooks', // owner: @mariusandra #team-cdp
SESSION_REPLAY_URL_TRIGGER: 'session-replay-url-trigger', // owner: @richard-better #team-replay
REPLAY_TEMPLATES: 'replay-templates', // owner: @raquelmsmith #team-replay
ROLE_BASED_ACCESS_CONTROL: 'role-based-access-control', // owner: @zach
EXPERIMENTS_HOLDOUTS: 'experiments-holdouts', // owner: @jurajmajerik #team-experiments
MESSAGING: 'messaging', // owner @mariusandra #team-cdp
SESSION_REPLAY_URL_BLOCKLIST: 'session-replay-url-blocklist', // owner: @richard-better #team-replay

View File

@ -9,7 +9,7 @@ import { lemonToast } from 'lib/lemon-ui/LemonToast/LemonToast'
import { urls } from 'scenes/urls'
import { userLogic } from 'scenes/userLogic'
import { OrganizationBasicType, PersonalAPIKeyType, TeamBasicType } from '~/types'
import { APIScopeObject, OrganizationBasicType, PersonalAPIKeyType, TeamBasicType } from '~/types'
import type { personalAPIKeysLogicType } from './personalAPIKeysLogicType'
@ -32,7 +32,7 @@ export const API_KEY_SCOPE_PRESETS = [
]
export type APIScope = {
key: string
key: APIScopeObject
info?: string | JSX.Element
disabledActions?: ('read' | 'write')[]
disabledWhenProjectScoped?: boolean

View File

@ -3821,6 +3821,37 @@ export interface RoleMemberType {
user_uuid: string
}
export type APIScopeObject =
| 'action'
| 'activity_log'
| 'annotation'
| 'batch_export'
| 'cohort'
| 'dashboard'
| 'dashboard_template'
| 'early_access_feature'
| 'event_definition'
| 'experiment'
| 'export'
| 'feature_flag'
| 'group'
| 'insight'
| 'query'
| 'notebook'
| 'organization'
| 'organization_member'
| 'person'
| 'plugin'
| 'project'
| 'property_definition'
| 'session_recording'
| 'session_recording_playlist'
| 'sharing_configuration'
| 'subscription'
| 'survey'
| 'user'
| 'webhook'
export interface OrganizationResourcePermissionType {
id: string
resource: Resource

View File

@ -5,7 +5,8 @@ from rest_framework import response, serializers, viewsets
from rest_framework.permissions import IsAuthenticated
from posthog.models import PersonalAPIKey, User
from posthog.models.personal_api_key import API_SCOPE_ACTIONS, API_SCOPE_OBJECTS, hash_key_value, mask_key_value
from posthog.models.personal_api_key import hash_key_value, mask_key_value
from posthog.models.scopes import API_SCOPE_ACTIONS, API_SCOPE_OBJECTS
from posthog.models.team.team import Team
from posthog.models.utils import generate_random_token_personal
from posthog.permissions import TimeSensitiveActionPermission

View File

@ -30,7 +30,7 @@ from posthog.models.activity_logging.activity_page import activity_page_response
from posthog.models.async_deletion import AsyncDeletion, DeletionType
from posthog.models.group_type_mapping import GroupTypeMapping
from posthog.models.organization import OrganizationMembership
from posthog.models.personal_api_key import APIScopeObjectOrNotSupported
from posthog.models.scopes import APIScopeObjectOrNotSupported
from posthog.models.product_intent.product_intent import ProductIntent
from posthog.models.project import Project
from posthog.models.signals import mute_selected_signals

View File

@ -18,7 +18,7 @@ from posthog.auth import (
SharingAccessTokenAuthentication,
)
from posthog.models.organization import Organization
from posthog.models.personal_api_key import APIScopeObjectOrNotSupported
from posthog.models.scopes import APIScopeObjectOrNotSupported
from posthog.models.project import Project
from posthog.models.team import Team
from posthog.models.user import User

View File

@ -28,7 +28,7 @@ from posthog.models.activity_logging.activity_page import activity_page_response
from posthog.models.async_deletion import AsyncDeletion, DeletionType
from posthog.models.group_type_mapping import GroupTypeMapping
from posthog.models.organization import OrganizationMembership
from posthog.models.personal_api_key import APIScopeObjectOrNotSupported
from posthog.models.scopes import APIScopeObjectOrNotSupported
from posthog.models.project import Project
from posthog.models.signals import mute_selected_signals
from posthog.models.team.util import delete_batch_exports, delete_bulky_postgres_data

View File

@ -70,9 +70,9 @@
'/home/runner/work/posthog/posthog/posthog/api/project.py: Warning [ProjectViewSet > ProjectBackwardCompatSerializer]: unable to resolve type hint for function "get_product_intents". Consider using a type hint or @extend_schema_field. Defaulting to string.',
'/home/runner/work/posthog/posthog/posthog/api/proxy_record.py: Warning [ProxyRecordViewset]: could not derive type of path parameter "organization_id" because it is untyped and obtaining queryset from the viewset failed. Consider adding a type to the path (e.g. <int:organization_id>) or annotating the parameter type with @extend_schema. Defaulting to "string".',
'/home/runner/work/posthog/posthog/posthog/api/proxy_record.py: Warning [ProxyRecordViewset]: could not derive type of path parameter "id" because it is untyped and obtaining queryset from the viewset failed. Consider adding a type to the path (e.g. <int:id>) or annotating the parameter type with @extend_schema. Defaulting to "string".',
'/home/runner/work/posthog/posthog/ee/api/role.py: Warning [RoleViewSet > RoleSerializer]: unable to resolve type hint for function "get_members". Consider using a type hint or @extend_schema_field. Defaulting to string.',
'/home/runner/work/posthog/posthog/ee/api/role.py: Warning [RoleViewSet > RoleSerializer]: unable to resolve type hint for function "get_associated_flags". Consider using a type hint or @extend_schema_field. Defaulting to string.',
'/home/runner/work/posthog/posthog/ee/api/role.py: Warning [RoleMembershipViewSet]: could not derive type of path parameter "organization_id" because model "ee.models.role.RoleMembership" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".',
'/home/runner/work/posthog/posthog/ee/api/rbac/role.py: Warning [RoleViewSet > RoleSerializer]: unable to resolve type hint for function "get_members". Consider using a type hint or @extend_schema_field. Defaulting to string.',
'/home/runner/work/posthog/posthog/ee/api/rbac/role.py: Warning [RoleViewSet > RoleSerializer]: unable to resolve type hint for function "get_associated_flags". Consider using a type hint or @extend_schema_field. Defaulting to string.',
'/home/runner/work/posthog/posthog/ee/api/rbac/role.py: Warning [RoleMembershipViewSet]: could not derive type of path parameter "organization_id" because model "ee.models.rbac.role.RoleMembership" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".',
'/home/runner/work/posthog/posthog/posthog/api/action.py: Warning [ActionViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.action.action.Action" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".',
'/home/runner/work/posthog/posthog/posthog/api/action.py: Warning [ActionViewSet > ActionSerializer]: unable to resolve type hint for function "get_creation_context". Consider using a type hint or @extend_schema_field. Defaulting to string.',
'/home/runner/work/posthog/posthog/posthog/api/activity_log.py: Warning [ActivityLogViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.activity_logging.activity_log.ActivityLog" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".',

View File

@ -3,7 +3,7 @@ from unittest.mock import ANY
from rest_framework import status
from ee.models.organization_resource_access import OrganizationResourceAccess
from ee.models.rbac.organization_resource_access import OrganizationResourceAccess
from posthog.api.dashboards.dashboard import Dashboard
from posthog.constants import AvailableFeature
from posthog.models import FeatureFlag

View File

@ -6,7 +6,7 @@ def can_user_edit_feature_flag(request, feature_flag):
# self hosted check for enterprise models that may not exist
try:
from ee.models.feature_flag_role_access import FeatureFlagRoleAccess
from ee.models.organization_resource_access import OrganizationResourceAccess
from ee.models.rbac.organization_resource_access import OrganizationResourceAccess
except:
return True
else:

View File

@ -1,4 +1,4 @@
from typing import Optional, Literal, get_args
from typing import Optional, Literal
import hashlib
from django.contrib.auth.hashers import PBKDF2PasswordHasher
@ -66,56 +66,3 @@ class PersonalAPIKey(models.Model):
null=True,
blank=True,
)
## API Scopes
# These are the scopes that are used to define the permissions of the API tokens.
# Not every model needs a scope - it should more be for top-level things
# Typically each object should have `read` and `write` scopes, but some objects may have more specific scopes
# WARNING: Make sure to keep in sync with the frontend!
APIScopeObject = Literal[
"action",
"activity_log",
"annotation",
"batch_export",
"cohort",
"dashboard",
"dashboard_template",
"early_access_feature",
"event_definition",
"experiment",
"export",
"feature_flag",
"group",
"insight",
"query", # Covers query and events endpoints
"notebook",
"organization",
"organization_member",
"person",
"plugin",
"project",
"property_definition",
"session_recording",
"session_recording_playlist",
"sharing_configuration",
"subscription",
"survey",
"user",
"webhook",
]
APIScopeActions = Literal[
"read",
"write",
]
APIScopeObjectOrNotSupported = Literal[
APIScopeObject,
"INTERNAL",
]
API_SCOPE_OBJECTS: tuple[APIScopeObject, ...] = get_args(APIScopeObject)
API_SCOPE_ACTIONS: tuple[APIScopeActions, ...] = get_args(APIScopeActions)

60
posthog/models/scopes.py Normal file
View File

@ -0,0 +1,60 @@
## API Scopes
# These are the scopes that are used to define the permissions of the API tokens.
# Not every model needs a scope - it should more be for top-level things
# Typically each object should have `read` and `write` scopes, but some objects may have more specific scopes
# WARNING: Make sure to keep in sync with the frontend!
from typing import Literal, get_args
## API Scopes
# These are the scopes that are used to define the permissions of the API tokens.
# Not every model needs a scope - it should more be for top-level things
# Typically each object should have `read` and `write` scopes, but some objects may have more specific scopes
# WARNING: Make sure to keep in sync with the frontend!
APIScopeObject = Literal[
"action",
"activity_log",
"annotation",
"batch_export",
"cohort",
"dashboard",
"dashboard_template",
"early_access_feature",
"event_definition",
"experiment",
"export",
"feature_flag",
"group",
"insight",
"query", # Covers query and events endpoints
"notebook",
"organization",
"organization_member",
"person",
"plugin",
"project",
"property_definition",
"session_recording",
"session_recording_playlist",
"sharing_configuration",
"subscription",
"survey",
"user",
"webhook",
]
APIScopeActions = Literal[
"read",
"write",
]
APIScopeObjectOrNotSupported = Literal[
APIScopeObject,
"INTERNAL",
]
API_SCOPE_OBJECTS: tuple[APIScopeObject, ...] = get_args(APIScopeObject)
API_SCOPE_ACTIONS: tuple[APIScopeActions, ...] = get_args(APIScopeActions)

View File

@ -19,7 +19,7 @@ from posthog.auth import (
from posthog.cloud_utils import is_cloud
from posthog.exceptions import EnterpriseFeatureException
from posthog.models import Organization, OrganizationMembership, Team, User
from posthog.models.personal_api_key import APIScopeObjectOrNotSupported
from posthog.models.scopes import APIScopeObjectOrNotSupported
from posthog.utils import get_can_create_org
CREATE_METHODS = ["POST", "PUT"]