diff --git a/posthog/api/__init__.py b/posthog/api/__init__.py index 416d860f2b0..01a670dc43d 100644 --- a/posthog/api/__init__.py +++ b/posthog/api/__init__.py @@ -6,7 +6,6 @@ from posthog.ee import is_ee_enabled from . import ( action, annotation, - authentication, cohort, dashboard, element, @@ -33,16 +32,8 @@ def api_not_found(request): router = DefaultRouterPlusPlus() -# Legacy endpoints (to be removed eventually) -router.register(r"annotation", annotation.AnnotationsViewSet) -router.register(r"feature_flag", feature_flag.FeatureFlagViewSet) -router.register(r"dashboard", dashboard.DashboardsViewSet) -router.register(r"dashboard_item", dashboard.DashboardItemsViewSet) -router.register(r"plugin_config", plugin.PluginConfigViewSet) -router.register(r"personal_api_keys", personal_api_key.PersonalAPIKeyViewSet, "personal_api_keys") -router.register(r"sessions_filter", sessions_filter.SessionsFilterViewSet) -# Nested endpoints +# Organization & Project endpoints projects_router = router.register(r"projects", team.TeamViewSet) organizations_router = router.register(r"organizations", organization.OrganizationViewSet) organizations_router.register(r"plugins", plugin.PluginViewSet, "organization_plugins", ["organization_id"]) @@ -56,8 +47,16 @@ organizations_router.register( r"onboarding", organization.OrganizationOnboardingViewset, "organization_onboarding", ["organization_id"], ) -# General endpoints (shared across EE & FOSS) -router.register(r"login", authentication.LoginViewSet) + +# TODO: Legacy endpoints (to be removed eventually) +router.register(r"annotation", annotation.AnnotationsViewSet) +router.register(r"feature_flag", feature_flag.FeatureFlagViewSet) +router.register(r"dashboard", dashboard.DashboardsViewSet) +router.register(r"dashboard_item", dashboard.DashboardItemsViewSet) +router.register(r"plugin_config", plugin.PluginConfigViewSet) +router.register(r"personal_api_keys", personal_api_key.PersonalAPIKeyViewSet, "personal_api_keys") +router.register(r"sessions_filter", sessions_filter.SessionsFilterViewSet) + if is_ee_enabled(): try: diff --git a/posthog/api/authentication.py b/posthog/api/authentication.py index a32f414a4d8..ec8123c459d 100644 --- a/posthog/api/authentication.py +++ b/posthog/api/authentication.py @@ -2,7 +2,11 @@ from typing import Any, Dict, Optional, cast from django.conf import settings from django.contrib.auth import authenticate, login +from django.contrib.auth import views as auth_views from django.http import JsonResponse +from django.shortcuts import redirect +from django.views.decorators.csrf import csrf_exempt, csrf_protect +from loginas.utils import is_impersonated_session, restore_original_login from rest_framework import mixins, permissions, serializers, status, viewsets from rest_framework.request import Request from rest_framework.response import Response @@ -24,6 +28,22 @@ def axess_lockout(*args, **kwargs): ) +@csrf_protect +def logout(request): + if request.user.is_authenticated: + request.user.temporary_token = None + request.user.save() + + if is_impersonated_session(request): + restore_original_login(request) + return redirect("/") + + response = auth_views.logout_then_login(request) + response.delete_cookie(settings.TOOLBAR_COOKIE_NAME, "/") + + return response + + class LoginSerializer(serializers.Serializer): email = serializers.EmailField() password = serializers.CharField() diff --git a/posthog/urls.py b/posthog/urls.py index f929a2083fa..7d0d39c4716 100644 --- a/posthog/urls.py +++ b/posthog/urls.py @@ -9,9 +9,8 @@ from django.core.exceptions import ValidationError from django.http import HttpResponse from django.shortcuts import redirect, render from django.urls import URLPattern, include, path, re_path, reverse -from django.views.decorators.csrf import csrf_exempt, csrf_protect +from django.views.decorators.csrf import csrf_exempt from django.views.generic.base import TemplateView -from loginas.utils import is_impersonated_session, restore_original_login from rest_framework import exceptions from sentry_sdk import capture_exception from social_core.pipeline.partial import partial @@ -19,6 +18,7 @@ from social_django.strategy import DjangoStrategy from posthog.api import ( api_not_found, + authentication, capture, dashboard, decide, @@ -145,23 +145,6 @@ def social_create_user(strategy: DjangoStrategy, details, backend, request, user return {"is_new": True, "user": user} -@csrf_protect -def logout(request): - if request.user.is_authenticated: - request.user.temporary_token = None - request.user.save() - - if is_impersonated_session(request): - restore_original_login(request) - return redirect("/") - - restore_original_login(request) - response = auth_views.logout_then_login(request) - response.delete_cookie(settings.TOOLBAR_COOKIE_NAME, "/") - - return response - - def authorize_and_redirect(request): if not request.GET.get("redirect"): return HttpResponse("You need to pass a url to ?redirect=", status=401) @@ -196,39 +179,19 @@ def opt_slash_path(route: str, view: Callable, name: Optional[str] = None) -> UR urlpatterns = [ - # internals + # Internals opt_slash_path("_health", health), opt_slash_path("_stats", stats), opt_slash_path("_preflight", preflight_check), opt_slash_path("_system_status", system_status), - # admin - path("admin/", admin.site.urls), - path("admin/", include("loginas.urls")), - # api - path("api/", include(router.urls)), + # Authentication + path("login", authentication.login, name="login"), + path("logout", authentication.logout, name="logout"), + path("", include("social_django.urls", namespace="social")), opt_slash_path("api/user/redirect_to_site", user.redirect_to_site), opt_slash_path("api/user/change_password", user.change_password), opt_slash_path("api/user/test_slack_webhook", user.test_slack_webhook), opt_slash_path("api/user", user.user), - opt_slash_path("api/signup", signup.SignupViewset.as_view()), - opt_slash_path("api/social_signup", signup.SocialSignupViewset.as_view()), - path("api/signup//", signup.InviteSignupViewset.as_view()), - re_path(r"^api.+", api_not_found), - path("authorize_and_redirect/", login_required(authorize_and_redirect)), - path("shared_dashboard/", dashboard.shared_dashboard), - re_path(r"^demo.*", login_required(demo)), - # ingestion - opt_slash_path("decide", decide.get_decide), - opt_slash_path("e", capture.get_event), - opt_slash_path("engage", capture.get_event), - opt_slash_path("track", capture.get_event), - opt_slash_path("capture", capture.get_event), - opt_slash_path("batch", capture.get_event), - opt_slash_path("s", capture.get_event), # session recordings - # auth - path("logout", logout, name="login"), - path("signup/finish/", finish_social_signup, name="signup_finish"), - path("", include("social_django.urls", namespace="social")), *( [] if is_email_available() @@ -245,6 +208,28 @@ urlpatterns = [ ), ), path("accounts/", include("django.contrib.auth.urls")), + # Sign up + opt_slash_path("api/signup", signup.SignupViewset.as_view()), + opt_slash_path("api/social_signup", signup.SocialSignupViewset.as_view()), + path("api/signup//", signup.InviteSignupViewset.as_view()), + path("signup/finish/", finish_social_signup, name="signup_finish"), + # API + path("api/", include(router.urls)), + re_path(r"^api.+", api_not_found), + path("authorize_and_redirect/", login_required(authorize_and_redirect)), + path("shared_dashboard/", dashboard.shared_dashboard), + re_path(r"^demo.*", login_required(demo)), + # Ingestion + opt_slash_path("decide", decide.get_decide), + opt_slash_path("e", capture.get_event), + opt_slash_path("engage", capture.get_event), + opt_slash_path("track", capture.get_event), + opt_slash_path("capture", capture.get_event), + opt_slash_path("batch", capture.get_event), + opt_slash_path("s", capture.get_event), # session recordings + # Admin + path("admin/", admin.site.urls), + path("admin/", include("loginas.urls")), ]