From bf82e16c012461f12ddf17f68def36f6239ed8c6 Mon Sep 17 00:00:00 2001 From: Ben Sima Date: Fri, 5 Sep 2025 13:18:44 -0400 Subject: Refactor Login Flow and Improve User Handling This fixes a bug where the 'pending' notification didn't show up in the main UI when a new user tried to register. --- Biz/PodcastItLater/Web.py | 184 ++++++++++++++++++++++++++-------------------- 1 file changed, 106 insertions(+), 78 deletions(-) diff --git a/Biz/PodcastItLater/Web.py b/Biz/PodcastItLater/Web.py index a6eb1f6..c1698f4 100644 --- a/Biz/PodcastItLater/Web.py +++ b/Biz/PodcastItLater/Web.py @@ -616,6 +616,86 @@ def index(request: Request) -> HomePage: ) +def _handle_test_login(email: str, request: Request) -> Response: + """Handle login in test mode.""" + user = Core.Database.get_user_by_email(email) + if not user: + user_id, token = Core.Database.create_user(email) + user = { + "id": user_id, + "email": email, + "token": token, + "status": "pending", + } + + # Check if user is active + if user.get("status") != "active": + return Response( + '
' + "Your account is pending approval. " + 'Please email ' + "ben@bensima.com " + 'or message @bensima on x.com ' + "to get approved.
", + status_code=403, + ) + + # Set session with extended lifetime + request.session["user_id"] = user["id"] + request.session["permanent"] = True + + return Response( + '
✓ Logged in (dev mode)
', + status_code=200, + headers={"HX-Redirect": "/"}, + ) + + +def _handle_production_login(email: str) -> Response: + """Handle login in production mode.""" + pending_message = ( + '
' + "Account created, currently pending. " + 'Email ben@bensima.com ' + 'or message @bensima ' + "to get your account activated.
" + ) + + # Get or create user + user = Core.Database.get_user_by_email(email) + if not user: + user_id, token = Core.Database.create_user(email) + user = { + "id": user_id, + "email": email, + "token": token, + "status": "pending", + } + # For new users, show the pending message + return Response(pending_message, status_code=200) + + # Check if user is active + if user.get("status") != "active": + return Response(pending_message, status_code=200) + + # Generate magic link token + magic_token = magic_link_serializer.dumps({ + "user_id": user["id"], + "email": email, + }) + + # Send email + send_magic_link(email, magic_token) + + return Response( + f'
✓ Magic link sent to {email}. ' + f"Check your email!
", + status_code=200, + ) + + @app.post("/login") def login(request: Request, data: FormData) -> Response: """Handle login/registration.""" @@ -632,78 +712,8 @@ def login(request: Request, data: FormData) -> Response: area = App.from_env() if area == App.Area.Test: - # Development mode: instant login - user = Core.Database.get_user_by_email(email) - if not user: - user_id, token = Core.Database.create_user( - email, - ) - user = { - "id": user_id, - "email": email, - "token": token, - "status": "pending", - } - - # Check if user is active - if user.get("status") != "active": - return Response( - '
' - "Your account is pending approval. " - 'Please email ' - "ben@bensima.com " - 'or message @bensima on x.com ' - "to get approved.
", - status_code=403, - ) - - # Set session with extended lifetime - request.session["user_id"] = user["id"] - request.session["permanent"] = True - - return Response( - '
✓ Logged in (dev mode)
', - status_code=200, - headers={"HX-Redirect": "/"}, - ) - - # Production mode: send magic link - # Get or create user - user = Core.Database.get_user_by_email(email) - if not user: - user_id, token = Core.Database.create_user( - email, - ) - user = {"id": user_id, "email": email, "token": token} - - # Check if user is active - if user.get("status") != "active": - return Response( - '
' - "Your account is pending approval. " - 'Please email ' - "ben@bensima.com " - 'or message @bensima on x.com ' - "to get approved.
", - status_code=403, - ) - - # Generate magic link token - magic_token = magic_link_serializer.dumps({ - "user_id": user["id"], - "email": email, - }) - - # Send email - send_magic_link(email, magic_token) - - return Response( - f'
✓ Magic link sent to {email}. ' - f"Check your email!
", - status_code=200, - ) + return _handle_test_login(email, request) + return _handle_production_login(email) except Exception as e: logger.exception("Login error") @@ -952,10 +962,10 @@ class TestAuthentication(BaseWebTest): response = self.client.post("/login", data={"email": "new@example.com"}) - self.assertEqual(response.status_code, 403) - self.assertIn("Your account is pending approval", response.text) + self.assertEqual(response.status_code, 200) + self.assertIn("Account created, currently pending", response.text) self.assertIn("ben@bensima.com", response.text) - self.assertIn("@bensima on x.com", response.text) + self.assertIn("@bensima", response.text) # Verify user was created with pending status user = Core.Database.get_user_by_email( @@ -983,6 +993,24 @@ class TestAuthentication(BaseWebTest): self.assertEqual(response.status_code, 200) self.assertIn("HX-Redirect", response.headers) + def test_login_existing_pending_user(self) -> None: + """Existing pending users should see the pending message.""" + # Create a pending user + _user_id, _ = Core.Database.create_user( + "pending@example.com", + ) + # User is pending by default + + response = self.client.post( + "/login", + data={"email": "pending@example.com"}, + ) + + self.assertEqual(response.status_code, 200) + self.assertIn("Account created, currently pending", response.text) + self.assertIn("ben@bensima.com", response.text) + self.assertIn("@bensima", response.text) + def test_login_disabled_user(self) -> None: """Disabled users should not be able to login.""" # Create user and set to disabled @@ -999,8 +1027,8 @@ class TestAuthentication(BaseWebTest): data={"email": "disabled@example.com"}, ) - self.assertEqual(response.status_code, 403) - self.assertIn("Your account is pending approval", response.text) + self.assertEqual(response.status_code, 200) + self.assertIn("Account created, currently pending", response.text) def test_login_invalid_email(self) -> None: """Reject malformed emails.""" @@ -1030,7 +1058,7 @@ class TestAuthentication(BaseWebTest): "/login", data={"email": "pending@example.com"}, ) - self.assertEqual(response.status_code, 403) + self.assertEqual(response.status_code, 200) # Should not have session response = self.client.get("/") -- cgit v1.2.3