Exemple #1
0
 def destroy(self, request: request.Request, pk=None, **kwargs) -> Response:  # type: ignore
     if not can_configure_plugins_via_api(self.team.organization_id):
         return Response(status=404)
     plugin_config = PluginConfig.objects.get(team_id=self.team_id, pk=pk)
     plugin_config.enabled = False
     plugin_config.save()
     return Response(status=204)
Exemple #2
0
 def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> PluginConfig:
     if not can_configure_plugins_via_api():
         raise ValidationError("Plugin configuration via the web is disabled!")
     request = self.context["request"]
     validated_data["team"] = request.user.team
     plugin_config = super().create(validated_data)
     reload_plugins_on_workers()
     return plugin_config
Exemple #3
0
 def get_queryset(self):
     queryset = super().get_queryset()
     if self.action == "get" or self.action == "list":
         if can_install_plugins_via_api(self.organization) or can_configure_plugins_via_api(self.organization):
             return queryset
     else:
         if can_install_plugins_via_api(self.organization):
             return queryset
     return queryset.none()
Exemple #4
0
 def destroy(self,
             request: request.Request,
             pk=None) -> Response:  # type: ignore
     if not can_configure_plugins_via_api():
         return Response(status=404)
     plugin_config = PluginConfig.objects.get(team=request.user.team, pk=pk)
     plugin_config.enabled = False
     plugin_config.save()
     return Response(status=204)
Exemple #5
0
 def create(self, validated_data: Dict, *args: Any, **kwargs: Any) -> PluginConfig:
     if not can_configure_plugins_via_api(Team.objects.get(id=self.context["team_id"]).organization_id):
         raise ValidationError("Plugin configuration via the web is disabled!")
     request = self.context["request"]
     validated_data["team"] = Team.objects.get(id=self.context["team_id"])
     self._fix_formdata_config_json(validated_data)
     plugin_config = super().create(validated_data)
     self._update_plugin_attachments(plugin_config)
     reload_plugins_on_workers()
     return plugin_config
Exemple #6
0
 def get_queryset(self):
     queryset = super().get_queryset()
     if self.action == "get" or self.action == "list":  # type: ignore
         if can_install_plugins_via_api() or can_configure_plugins_via_api():
             return queryset
     else:
         if can_install_plugins_via_api():
             # block update/delete for plugins that come from posthog.json
             return queryset.filter(from_json=False)
     return queryset.none()
Exemple #7
0
    def global_plugins(self, request: request.Request, **kwargs):
        if not can_configure_plugins_via_api(self.team.organization_id):
            return Response([])

        response = []
        plugin_configs = PluginConfig.objects.filter(team_id=None, enabled=True)  # type: ignore
        for plugin_config in plugin_configs:
            plugin = PluginConfigSerializer(plugin_config).data
            plugin["config"] = None
            plugin["error"] = None
            response.append(plugin)

        return Response(response)
Exemple #8
0
    def rearrange(self, request: request.Request, **kwargs):
        if not can_configure_plugins_via_api(self.team.organization_id):
            raise ValidationError(
                "Plugin configuration via the web is disabled!")

        orders = request.data.get("orders", {})

        did_save = False
        plugin_configs = PluginConfig.objects.filter(team_id=self.team.pk,
                                                     enabled=True)
        plugin_configs_dict = {p.plugin_id: p for p in plugin_configs}
        for plugin_id, order in orders.items():
            plugin_config = plugin_configs_dict.get(int(plugin_id), None)
            if plugin_config and plugin_config.order != order:
                plugin_config.order = order
                plugin_config.save()
                did_save = True

        if did_save:
            reload_plugins_on_workers()

        return Response(PluginConfigSerializer(plugin_configs, many=True).data)
Exemple #9
0
 def get_queryset(self):
     if not can_configure_plugins_via_api(self.team.organization_id):
         return self.queryset.none()
     return super().get_queryset()
Exemple #10
0
def user(request):
    organization: Optional[Organization] = request.user.organization
    organizations = list(
        request.user.organizations.order_by("-created_at").values(
            "name", "id"))
    team: Optional[Team] = request.user.team
    teams = list(
        request.user.teams.order_by("-created_at").values("name", "id"))
    user = cast(User, request.user)

    if request.method == "PATCH":
        data = json.loads(request.body)

        if team is not None and "team" in data:
            team.app_urls = data["team"].get("app_urls", team.app_urls)
            team.opt_out_capture = data["team"].get("opt_out_capture",
                                                    team.opt_out_capture)
            team.slack_incoming_webhook = data["team"].get(
                "slack_incoming_webhook", team.slack_incoming_webhook)
            team.anonymize_ips = data["team"].get("anonymize_ips",
                                                  team.anonymize_ips)
            team.session_recording_opt_in = data["team"].get(
                "session_recording_opt_in", team.session_recording_opt_in)
            team.session_recording_retention_period_days = data["team"].get(
                "session_recording_retention_period_days",
                team.session_recording_retention_period_days)
            if data["team"].get("plugins_opt_in") is not None:
                reload_plugins_on_workers()
            team.plugins_opt_in = data["team"].get("plugins_opt_in",
                                                   team.plugins_opt_in)
            team.completed_snippet_onboarding = data["team"].get(
                "completed_snippet_onboarding",
                team.completed_snippet_onboarding,
            )
            team.save()

        if "user" in data:
            try:
                user.current_organization = user.organizations.get(
                    id=data["user"]["current_organization_id"])
                assert user.organization is not None, "Organization should have been just set"
                user.current_team = user.organization.teams.first()
            except (KeyError, ValueError):
                pass
            except ObjectDoesNotExist:
                return JsonResponse(
                    {"detail": "Organization not found for user."}, status=404)
            except KeyError:
                pass
            except ObjectDoesNotExist:
                return JsonResponse(
                    {"detail": "Organization not found for user."}, status=404)
            if user.organization is not None:
                try:
                    user.current_team = user.organization.teams.get(
                        id=int(data["user"]["current_team_id"]))
                except (KeyError, TypeError):
                    pass
                except ValueError:
                    return JsonResponse(
                        {"detail": "Team ID must be an integer."}, status=400)
                except ObjectDoesNotExist:
                    return JsonResponse(
                        {
                            "detail":
                            "Team not found for user's current organization."
                        },
                        status=404)
            user.email_opt_in = data["user"].get("email_opt_in",
                                                 user.email_opt_in)
            user.anonymize_data = data["user"].get("anonymize_data",
                                                   user.anonymize_data)
            user.toolbar_mode = data["user"].get("toolbar_mode",
                                                 user.toolbar_mode)
            user.save()

    user_identify.identify_task.delay(user_id=user.id)

    return JsonResponse({
        "id":
        user.pk,
        "distinct_id":
        user.distinct_id,
        "name":
        user.first_name,
        "email":
        user.email,
        "email_opt_in":
        user.email_opt_in,
        "anonymize_data":
        user.anonymize_data,
        "toolbar_mode":
        user.toolbar_mode,
        "organization":
        None if organization is None else {
            "id":
            organization.id,
            "name":
            organization.name,
            "billing_plan":
            organization.billing_plan,
            "available_features":
            organization.available_features,
            "created_at":
            organization.created_at,
            "updated_at":
            organization.updated_at,
            "teams": [{
                "id": team.id,
                "name": team.name
            } for team in organization.teams.all().only("id", "name")],
        },
        "organizations":
        organizations,
        "team":
        None if team is None else {
            "id":
            team.id,
            "name":
            team.name,
            "app_urls":
            team.app_urls,
            "api_token":
            team.api_token,
            "opt_out_capture":
            team.opt_out_capture,
            "anonymize_ips":
            team.anonymize_ips,
            "slack_incoming_webhook":
            team.slack_incoming_webhook,
            "event_names":
            team.event_names,
            "event_names_with_usage":
            team.event_names_with_usage or [{
                "event": event,
                "volume": None,
                "usage_count": None
            } for event in team.event_names],
            "event_properties":
            team.event_properties,
            "event_properties_numerical":
            team.event_properties_numerical,
            "event_properties_with_usage":
            team.event_properties_with_usage or [{
                "key": key,
                "volume": None,
                "usage_count": None
            } for key in team.event_properties],
            "completed_snippet_onboarding":
            team.completed_snippet_onboarding,
            "session_recording_opt_in":
            team.session_recording_opt_in,
            "session_recording_retention_period_days":
            team.session_recording_retention_period_days,
            "plugins_opt_in":
            team.plugins_opt_in,
            "ingested_event":
            team.ingested_event,
        },
        "teams":
        teams,
        "has_password":
        user.has_usable_password(),
        "opt_out_capture":
        os.environ.get("OPT_OUT_CAPTURE"),
        "posthog_version":
        VERSION,
        "is_multi_tenancy":
        getattr(settings, "MULTI_TENANCY", False),
        "ee_available":
        settings.EE_AVAILABLE,
        "ee_enabled":
        is_ee_enabled(),
        "email_service_available":
        is_email_available(with_absolute_urls=True),
        "is_debug":
        getattr(settings, "DEBUG", False),
        "is_staff":
        user.is_staff,
        "is_impersonated":
        is_impersonated_session(request),
        "plugin_access": {
            "install": can_install_plugins_via_api(user.organization),
            "configure": can_configure_plugins_via_api(user.organization),
        },
    })
Exemple #11
0
def system_status(request):
    team = request.user.team
    is_multitenancy: bool = getattr(settings, "MULTI_TENANCY", False)

    if is_multitenancy and not request.user.is_staff:
        raise AuthenticationFailed(detail="You're not authorized.")

    from .models import Element, Event, SessionRecordingEvent

    redis_alive = is_redis_alive()
    postgres_alive = is_postgres_alive()

    metrics: List[Dict[str, Union[str, bool, int, float]]] = []

    metrics.append({
        "key": "analytics_database",
        "metric": "Analytics database in use",
        "value": "ClickHouse" if is_ee_enabled() else "Postgres",
    })

    metrics.append({
        "key":
        "ingestion_server",
        "metric":
        "Event ingestion via",
        "value":
        "Plugin Server" if settings.PLUGIN_SERVER_INGESTION else "Django",
    })

    metrics.append({
        "key": "db_alive",
        "metric": "Postgres database alive",
        "value": postgres_alive
    })
    if postgres_alive:
        postgres_version = connection.cursor().connection.server_version
        metrics.append({
            "key":
            "pg_version",
            "metric":
            "Postgres version",
            "value":
            f"{postgres_version // 10000}.{(postgres_version // 100) % 100}.{postgres_version % 100}",
        })
        event_table_count = get_table_approx_count(
            Event._meta.db_table)[0]["approx_count"]
        event_table_size = get_table_size(Event._meta.db_table)[0]["size"]

        element_table_count = get_table_approx_count(
            Element._meta.db_table)[0]["approx_count"]
        element_table_size = get_table_size(Element._meta.db_table)[0]["size"]

        session_recording_event_table_count = get_table_approx_count(
            SessionRecordingEvent._meta.db_table)[0]["approx_count"]
        session_recording_event_table_size = get_table_size(
            SessionRecordingEvent._meta.db_table)[0]["size"]

        metrics.append({
            "metric":
            "Postgres elements table size",
            "value":
            f"~{element_table_count} rows (~{element_table_size})"
        })
        metrics.append({
            "metric":
            "Postgres events table size",
            "value":
            f"~{event_table_count} rows (~{event_table_size})"
        })
        metrics.append({
            "metric":
            "Postgres session recording table size",
            "value":
            f"~{session_recording_event_table_count} rows (~{session_recording_event_table_size})",
        })

    metrics.append({
        "key": "redis_alive",
        "metric": "Redis alive",
        "value": redis_alive
    })
    if redis_alive:
        import redis

        try:
            redis_info = get_redis_info()
            redis_queue_depth = get_redis_queue_depth()
            metrics.append({
                "metric": "Redis version",
                "value": f"{redis_info.get('redis_version')}"
            })
            metrics.append({
                "metric": "Redis current queue depth",
                "value": f"{redis_queue_depth}"
            })
            metrics.append({
                "metric": "Redis connected client count",
                "value": f"{redis_info.get('connected_clients')}"
            })
            metrics.append({
                "metric":
                "Redis memory used",
                "value":
                f"{redis_info.get('used_memory_human', '?')}B"
            })
            metrics.append({
                "metric":
                "Redis memory peak",
                "value":
                f"{redis_info.get('used_memory_peak_human', '?')}B"
            })
            metrics.append({
                "metric":
                "Redis total memory available",
                "value":
                f"{redis_info.get('total_system_memory_human', '?')}B",
            })
        except redis.exceptions.ConnectionError as e:
            metrics.append({
                "metric":
                "Redis metrics",
                "value":
                f"Redis connected but then failed to return metrics: {e}"
            })

    metrics.append({
        "key": "plugin_sever_alive",
        "metric": "Plugin server alive",
        "value": is_plugin_server_alive()
    })
    metrics.append({
        "key": "plugin_sever_version",
        "metric": "Plugin server version",
        "value": get_plugin_server_version() or "unknown",
    })
    metrics.append({
        "key": "plugins_install",
        "metric": "Plugins can be installed",
        "value": can_install_plugins_via_api(team.organization),
    })
    metrics.append({
        "key": "plugins_configure",
        "metric": "Plugins can be configured",
        "value": can_configure_plugins_via_api(team.organization),
    })

    return JsonResponse({"results": metrics})
Exemple #12
0
 def get_queryset(self):
     queryset = super().get_queryset()
     if can_configure_plugins_via_api():
         return queryset.filter(team_id=self.request.user.team.pk)
     return queryset.none()
Exemple #13
0
def user(request):
    organization = request.user.organization
    organizations = list(
        request.user.organizations.order_by("-created_at").values(
            "name", "id"))
    team = request.user.team
    teams = list(
        request.user.teams.order_by("-created_at").values("name", "id"))
    user = cast(User, request.user)

    if request.method == "PATCH":
        data = json.loads(request.body)

        if "team" in data:
            team.app_urls = data["team"].get("app_urls", team.app_urls)
            team.opt_out_capture = data["team"].get("opt_out_capture",
                                                    team.opt_out_capture)
            team.slack_incoming_webhook = data["team"].get(
                "slack_incoming_webhook", team.slack_incoming_webhook)
            team.anonymize_ips = data["team"].get("anonymize_ips",
                                                  team.anonymize_ips)
            team.session_recording_opt_in = data["team"].get(
                "session_recording_opt_in", team.session_recording_opt_in)
            if data["team"].get("plugins_opt_in") is not None:
                reload_plugins_on_workers()
            team.plugins_opt_in = data["team"].get("plugins_opt_in",
                                                   team.plugins_opt_in)
            team.completed_snippet_onboarding = data["team"].get(
                "completed_snippet_onboarding",
                team.completed_snippet_onboarding,
            )
            team.save()

        if "user" in data:
            try:
                user.current_organization = user.organizations.get(
                    id=data["user"]["current_organization_id"])
                user.current_team = user.organization.teams.first()
            except KeyError:
                pass
            except ObjectDoesNotExist:
                return JsonResponse(
                    {"detail": "Organization not found for user."}, status=404)
            except KeyError:
                pass
            except ObjectDoesNotExist:
                return JsonResponse(
                    {"detail": "Organization not found for user."}, status=404)
            try:
                user.current_team = user.organization.teams.get(
                    id=int(data["user"]["current_team_id"]))
            except (KeyError, TypeError):
                pass
            except ValueError:
                return JsonResponse({"detail": "Team ID must be an integer."},
                                    status=400)
            except ObjectDoesNotExist:
                return JsonResponse(
                    {
                        "detail":
                        "Team not found for user's current organization."
                    },
                    status=404)
            user.email_opt_in = data["user"].get("email_opt_in",
                                                 user.email_opt_in)
            user.anonymize_data = data["user"].get("anonymize_data",
                                                   user.anonymize_data)
            user.toolbar_mode = data["user"].get("toolbar_mode",
                                                 user.toolbar_mode)
            posthoganalytics.identify(
                user.distinct_id,
                {
                    "email_opt_in":
                    user.email_opt_in,
                    "anonymize_data":
                    user.anonymize_data,
                    "email":
                    user.email if not user.anonymize_data else None,
                    "is_signed_up":
                    True,
                    "toolbar_mode":
                    user.toolbar_mode,
                    "billing_plan":
                    user.organization.billing_plan,
                    "is_team_unique_user": (team.users.count() == 1),
                    "team_setup_complete": (team.completed_snippet_onboarding
                                            and team.ingested_event),
                },
            )
            user.save()

    return JsonResponse({
        "id":
        user.pk,
        "distinct_id":
        user.distinct_id,
        "name":
        user.first_name,
        "email":
        user.email,
        "email_opt_in":
        user.email_opt_in,
        "anonymize_data":
        user.anonymize_data,
        "toolbar_mode":
        user.toolbar_mode,
        "organization": {
            "id":
            organization.id,
            "name":
            organization.name,
            "billing_plan":
            organization.billing_plan,
            "available_features":
            organization.available_features,
            "created_at":
            organization.created_at,
            "updated_at":
            organization.updated_at,
            "teams": [{
                "id": team.id,
                "name": team.name
            } for team in organization.teams.all().only("id", "name")],
        },
        "organizations":
        organizations,
        "team":
        team and {
            "id": team.id,
            "name": team.name,
            "app_urls": team.app_urls,
            "api_token": team.api_token,
            "opt_out_capture": team.opt_out_capture,
            "anonymize_ips": team.anonymize_ips,
            "slack_incoming_webhook": team.slack_incoming_webhook,
            "event_names": team.event_names,
            "event_properties": team.event_properties,
            "event_properties_numerical": team.event_properties_numerical,
            "completed_snippet_onboarding": team.completed_snippet_onboarding,
            "session_recording_opt_in": team.session_recording_opt_in,
            "plugins_opt_in": team.plugins_opt_in,
            "ingested_event": team.ingested_event,
        },
        "teams":
        teams,
        "has_password":
        user.has_usable_password(),
        "opt_out_capture":
        os.environ.get("OPT_OUT_CAPTURE"),
        "posthog_version":
        VERSION,
        "is_multi_tenancy":
        getattr(settings, "MULTI_TENANCY", False),
        "ee_available":
        user.ee_available,
        "plugin_access": {
            "install": can_install_plugins_via_api(),
            "configure": can_configure_plugins_via_api()
        },
    })
Exemple #14
0
 def get_queryset(self):
     if not can_configure_plugins_via_api():
         return self.queryset.none()
     return super().get_queryset()