diff --git a/mcp-servers/python/mcp-rss-search/src/mcp_rss_search/__init__.py b/mcp-servers/python/mcp-rss-search/src/mcp_rss_search/__init__.py index 889f4b21e..ff996880e 100644 --- a/mcp-servers/python/mcp-rss-search/src/mcp_rss_search/__init__.py +++ b/mcp-servers/python/mcp-rss-search/src/mcp_rss_search/__init__.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # -*- coding: utf-8 -*- """MCP RSS Search Server - Advanced RSS feed parsing, searching, and analysis.""" diff --git a/mcp-servers/python/mcp-rss-search/src/mcp_rss_search/server_fastmcp.py b/mcp-servers/python/mcp-rss-search/src/mcp_rss_search/server_fastmcp.py old mode 100644 new mode 100755 diff --git a/mcp-servers/python/mcp-rss-search/tests/test_server.py b/mcp-servers/python/mcp-rss-search/tests/test_server.py index 82337b59e..64af423ef 100644 --- a/mcp-servers/python/mcp-rss-search/tests/test_server.py +++ b/mcp-servers/python/mcp-rss-search/tests/test_server.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Tests for MCP RSS Search Server.""" diff --git a/mcpgateway/admin.py b/mcpgateway/admin.py index 99988c9aa..ceb4d853e 100644 --- a/mcpgateway/admin.py +++ b/mcpgateway/admin.py @@ -2608,53 +2608,56 @@ async def admin_login_handler(request: Request, db: Session = Depends(get_db)) - root_path = request.scope.get("root_path", "") return RedirectResponse(url=f"{root_path}/admin/login?error=missing_fields", status_code=303) - # Authenticate using the email auth service # First-Party - from mcpgateway.services.email_auth_service import EmailAuthService # pylint: disable=import-outside-toplevel # pylint: disable=import-outside-toplevel + from mcpgateway.services.email_auth_service import EmailAuthService # pylint: disable=import-outside-toplevel auth_service = EmailAuthService(db) - try: - # Authenticate user - LOGGER.debug(f"Attempting authentication for {email}") - user = await auth_service.authenticate_user(email, password) - LOGGER.debug(f"Authentication result: {user}") - - if not user: - LOGGER.warning(f"Authentication failed for {email} - user is None") - root_path = request.scope.get("root_path", "") - return RedirectResponse(url=f"{root_path}/admin/login?error=invalid_credentials", status_code=303) + LOGGER.debug(f"Attempting authentication for {email}") + user = await auth_service.authenticate_user(email, password) + LOGGER.debug(f"Authentication result: {user}") - # Create JWT token with proper audience and issuer claims - # First-Party - from mcpgateway.routers.email_auth import create_access_token # pylint: disable=import-outside-toplevel + if not user: + root_path = request.scope.get("root_path", "") + return RedirectResponse(url=f"{root_path}/admin/login?error=invalid_credentials", status_code=303) - token, _ = await create_access_token(user) # expires_seconds not needed here + # First-Party + from mcpgateway.routers.email_auth import create_access_token # pylint: disable=import-outside-toplevel - # Create redirect response - root_path = request.scope.get("root_path", "") - response = RedirectResponse(url=f"{root_path}/admin", status_code=303) + token, _ = await create_access_token(user) - # Set JWT token as secure cookie - # First-Party - from mcpgateway.utils.security_cookies import set_auth_cookie # pylint: disable=import-outside-toplevel + root_path = request.scope.get("root_path", "") + response = RedirectResponse(url=f"{root_path}/admin", status_code=303) - set_auth_cookie(response, token, remember_me=False) + # First-Party + from mcpgateway.utils.security_cookies import set_auth_cookie # pylint: disable=import-outside-toplevel - LOGGER.info(f"Admin user {email} logged in successfully") - return response + set_auth_cookie(response, token, remember_me=False) + default_admin_username = settings.platform_admin_email + default_password = settings.platform_admin_password - except Exception as e: - LOGGER.warning(f"Login failed for {email}: {e}") + is_default_password = email.lower() == default_admin_username and password == default_password - if settings.secure_cookies and settings.environment == "development": - LOGGER.warning("Login failed - set SECURE_COOKIES to false in config for HTTP development") + # ✅ Set or clear the security reminder cookie + if is_default_password: + response.set_cookie( + key="pwd_is_default", + value="true", + max_age=3600 * 24, # 1 day + httponly=False, # JS needs to read it + secure=False, # set True for HTTPS environments + samesite="Lax", + ) + LOGGER.debug("Set cookie: pwd_is_default=true for admin@example.com") + else: + response.delete_cookie("pwd_is_default") + LOGGER.debug("Cleared cookie: pwd_is_default") - root_path = request.scope.get("root_path", "") - return RedirectResponse(url=f"{root_path}/admin/login?error=invalid_credentials", status_code=303) + LOGGER.info(f"Admin user {email} logged in successfully") + return response except Exception as e: - LOGGER.error(f"Login handler error: {e}") + LOGGER.exception(f"Login handler error: {e}") root_path = request.scope.get("root_path", "") return RedirectResponse(url=f"{root_path}/admin/login?error=server_error", status_code=303) @@ -4477,75 +4480,110 @@ async def admin_get_user_edit( async def admin_update_user( user_email: str, request: Request, - db: Session = Depends(get_db), + db: Optional[Session] = Depends(get_db), _user=Depends(get_current_user_with_permissions), ) -> HTMLResponse: - """Update user via admin UI. + """ + Update a user's details via the admin UI. + + This includes updating the full name, admin status, and password. It also ensures + the last admin cannot be demoted and manages a "pwd_is_default" cookie for + the platform admin if the default password is used. Args: - user_email: Email of user to update - request: FastAPI request object - db: Database session + user_email (str): Email of the user to update. + request (Request): FastAPI request object. + db (Optional[Session]): Database session. + _user: Current user with permissions (dependency). Returns: - HTMLResponse: Success message or error response + HTMLResponse: Success message or error response. """ if not settings.email_auth_enabled: - return HTMLResponse(content='
User updated successfully