Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
3eb7cd6
Update pyproject versions
jordanrfrazier Aug 27, 2025
cc975df
fix: Avoid namespace collision for Astra (#9544)
erichare Aug 27, 2025
e423526
fix: Revert to a working composio release for module import (#9569)
erichare Aug 27, 2025
7fec73d
fix: Knowledge base component refactor (#9543)
erichare Aug 27, 2025
d8b5731
fix: Fix env file handling in Windows build scripts (#9414)
Cristhianzl Aug 27, 2025
49ddc8c
fix: update agent_llm display name to "Model Provider" in AgentCompon…
edwinjosechittilappilly Aug 27, 2025
e494afb
fix: use custom file handler on chat view, disable mcp_composer by de…
lucaseduoli Aug 27, 2025
48fe917
fix: Use the newest file component in Vector Store RAG Template (#9571)
erichare Aug 27, 2025
45512a5
fix: AI/ML icon is missing (#9553)
deon-sanchez Aug 27, 2025
d38e8f4
fix: Allow updates to the file component in templates using it (#9572)
erichare Aug 27, 2025
7a4bcfb
fix: Fixes filtering so legacy components aren't shown by default (#9…
mfortman11 Aug 27, 2025
e07d489
fix: changed name on tool mode to slug, added close button to sidebar…
lucaseduoli Aug 28, 2025
9e871e2
fix: enhance scroll behavior on playground (#9586)
lucaseduoli Aug 28, 2025
f792525
fix: delete duplicate Serper api from google bundle (#9601)
lucaseduoli Aug 28, 2025
d9c005b
fix: allow deletion of mcp servers, add tests for mcp sidebar (#9587)
lucaseduoli Aug 29, 2025
5860a8b
fix: change zoom in and out limit, create tests for zooming in and ou…
lucaseduoli Aug 29, 2025
f82e514
fix: Add localStorage persistence for feature toggles (#9597)
Cristhianzl Aug 29, 2025
b4e4435
fix: Add help text to Lock Flow option (#9600)
Cristhianzl Aug 29, 2025
2c21fbd
fix: Add comprehensive tests and improve minimal condition logic (#9611)
Cristhianzl Aug 29, 2025
133fe6c
fix: change icon color for mcp, remove color setting of icons (#9594)
lucaseduoli Aug 29, 2025
d715fa1
fix: remove unsupported styling options from chats components (#9610)
italojohnny Aug 29, 2025
9798500
fix: disable mcp auto install for not installed applications, refacto…
lucaseduoli Aug 29, 2025
349295a
fix: Properly allow the non-specification of an OCR Engine (#9617)
erichare Sep 1, 2025
dfd4037
fix: Support objects with data attribute in body processing (#9644)
Cristhianzl Sep 2, 2025
f8da854
fix: Add comprehensive message sorting + tests (#9641)
Cristhianzl Sep 2, 2025
cfd10f8
fix: Fix audio recording resource cleanup (#9623)
Cristhianzl Sep 2, 2025
f854b70
fix: Add voice mode availability detection (#9621)
Cristhianzl Sep 2, 2025
8b0ddde
fix: Remove formatting from agent input text content (#9638)
Cristhianzl Sep 2, 2025
9f09250
fix: added most important types at the beginning of the extensions ar…
lucaseduoli Sep 2, 2025
8d26d52
fix: Include flow ID in webhook URLs (#9624)
Cristhianzl Sep 2, 2025
169fd32
fix(logger): add optional cache to configure; update caching behavior…
ogabrielluiz Sep 2, 2025
d10854a
fix: Update sidebar border styles (#9625)
mfortman11 Sep 2, 2025
a290c46
fix: Remove top padding from sidebar groups (#9636)
Cristhianzl Sep 2, 2025
ea49ead
fix: disable message editing on playground, fix new session not persi…
lucaseduoli Sep 2, 2025
9d04d56
fix: disable elevate edges on node select (#9658)
lucaseduoli Sep 2, 2025
e61cb46
fix: Properly respect the order parameter for Message History (#9605)
erichare Sep 3, 2025
e20ff66
fix: Return multi-row dataframe when Structured Output data supports …
erichare Sep 3, 2025
68288aa
fix: apply to fields in settings page (#9602)
edwinjosechittilappilly Sep 3, 2025
617e94b
fix: Segmented Sidebar switch to search on value change (#9615)
mfortman11 Sep 3, 2025
d1998b3
fix: deprecate claude 3 sonnet model (#9622)
edwinjosechittilappilly Sep 3, 2025
375e672
fix: Properly import Langchain ToolMessage for Message options (#9675)
erichare Sep 3, 2025
032c7fa
fix: fixed user settings test (#9690)
lucaseduoli Sep 3, 2025
bc13c5b
fix: Remove warning log for unset TRACELOOP_API_KEY in configuration …
ogabrielluiz Sep 3, 2025
57686a7
fix: knowledge base fixes for 1.6 pointing to release branch (#9683)
deon-sanchez Sep 3, 2025
320cd00
fix: remove github link on discord button (#9655)
lucaseduoli Sep 4, 2025
d9a97cc
fix: remove python code component, fix placeholder not appearing (#9660)
lucaseduoli Sep 4, 2025
f51575e
fix: add margins to <p> tag in markdown (#9656)
lucaseduoli Sep 4, 2025
bf32bdd
fix: delete unused components, delete [deprecated] tag on the compone…
lucaseduoli Sep 4, 2025
74952e8
fix: Ensure correct Docling Remote URL for API (#9708)
erichare Sep 4, 2025
5687903
feat: remove agent dual output (#9700)
edwinjosechittilappilly Sep 4, 2025
3b7601f
refactor: Agent component enhancements for release v1.6 (#9685)
deon-sanchez Sep 4, 2025
b551b0c
feat: mcp composer integration (#9506)
jordanrfrazier Sep 5, 2025
f2f6129
fix: Adjust padding and layout for improved spacing (#9711)
Cristhianzl Sep 5, 2025
2f9dc00
fix: remove Groq from Agents model provider list (#9616)
edwinjosechittilappilly Sep 5, 2025
06e00c6
fix: conditional scheduling logic to not run branch (#9722)
jordanrfrazier Sep 8, 2025
1c262de
fix: disable keys when flow is locked (#9726)
lucaseduoli Sep 8, 2025
cf08d19
fix: superuser credential handling and AUTO_LOGIN security (#9542)
edwinjosechittilappilly Sep 8, 2025
6cabf2a
chore: Update version to 1.6.0 in package files (#9746)
ogabrielluiz Sep 8, 2025
d9c4802
fix: update logs position to be absolute (#9724)
lucaseduoli Sep 8, 2025
8cddf6c
fix: make entire playground chat area be clickable (#9721)
lucaseduoli Sep 8, 2025
86815cb
fix: Restore old description from file description. (#9752)
erichare Sep 8, 2025
5a8f988
fix: Preserve flows and components during project updates (#9750)
Cristhianzl Sep 9, 2025
c8e1efb
fix(langwatch): prevent trace errors with proper API key validation (…
italojohnny Sep 9, 2025
eed7a87
fix(URLComponent): filter out `None` in headers to avoid silent seria…
ogabrielluiz Sep 9, 2025
4bba723
fix: put knowledge bases under feature flag (#9749)
lucaseduoli Sep 9, 2025
389c68a
remove old templates
deon-sanchez Sep 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: superuser credential handling and AUTO_LOGIN security (#9542)
* Refactor superuser credential handling for security

* [autofix.ci] apply automated fixes

* refactor: enhance superuser credential handling in setup process (#9574)

* [autofix.ci] apply automated fixes

* refactor: streamline superuser flow tests and enhance error handling (#9577)

* [autofix.ci] apply automated fixes

* None Super user is not allowed hence for a valid string resetting it to

* None Super user is not allowed hence for a valid string resetting it to ""

* use secret str for password everywhere

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jordan Frazier <[email protected]>
  • Loading branch information
3 people authored Sep 8, 2025
commit cf08d19c8de8caa8dc4d2c38c6783b4c0390cdb7
4 changes: 2 additions & 2 deletions src/backend/base/langflow/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ async def _create_superuser(username: str, password: str, auth_token: str | None
if settings_service.auth_settings.AUTO_LOGIN:
# Force default credentials for AUTO_LOGIN mode
username = DEFAULT_SUPERUSER
password = DEFAULT_SUPERUSER_PASSWORD
password = DEFAULT_SUPERUSER_PASSWORD.get_secret_value()
else:
# Production mode - prompt for credentials if not provided
if not username:
Expand Down Expand Up @@ -697,7 +697,7 @@ async def _create_superuser(username: str, password: str, auth_token: str | None
raise typer.Exit(1)

typer.echo(f"AUTO_LOGIN enabled. Creating default superuser '{username}'...")
typer.echo(f"Note: Default credentials are {DEFAULT_SUPERUSER}/{DEFAULT_SUPERUSER_PASSWORD}")
# Do not echo the default password to avoid exposing it in logs.
# AUTO_LOGIN is false - production mode
elif is_first_setup:
typer.echo("No superusers found. Creating first superuser...")
Expand Down
10 changes: 7 additions & 3 deletions src/backend/base/langflow/initial_setup/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -1002,12 +1002,16 @@ async def create_or_update_starter_projects(all_types_dict: dict) -> None:
await logger.adebug(f"Successfully created {successfully_created_projects} starter projects")


async def initialize_super_user_if_needed() -> None:
async def initialize_auto_login_default_superuser() -> None:
settings_service = get_settings_service()
if not settings_service.auth_settings.AUTO_LOGIN:
return
username = settings_service.auth_settings.SUPERUSER
password = settings_service.auth_settings.SUPERUSER_PASSWORD
# In AUTO_LOGIN mode, always use the default credentials for initial bootstrapping
# without persisting the password in memory after setup.
from langflow.services.settings.constants import DEFAULT_SUPERUSER, DEFAULT_SUPERUSER_PASSWORD

username = DEFAULT_SUPERUSER
password = DEFAULT_SUPERUSER_PASSWORD.get_secret_value()
if not username or not password:
msg = "SUPERUSER and SUPERUSER_PASSWORD must be set in the settings if AUTO_LOGIN is true."
raise ValueError(msg)
Expand Down
22 changes: 20 additions & 2 deletions src/backend/base/langflow/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from langflow.api.v1.mcp_projects import init_mcp_servers
from langflow.initial_setup.setup import (
create_or_update_starter_projects,
initialize_super_user_if_needed,
initialize_auto_login_default_superuser,
load_bundles_from_urls,
load_flows_from_directory,
sync_flows_from_fs,
Expand Down Expand Up @@ -144,9 +144,27 @@ async def lifespan(_app: FastAPI):
setup_llm_caching()
await logger.adebug(f"LLM caching setup in {asyncio.get_event_loop().time() - current_time:.2f}s")

if get_settings_service().auth_settings.AUTO_LOGIN:
current_time = asyncio.get_event_loop().time()
await logger.adebug("Initializing default super user")
await initialize_auto_login_default_superuser()
await logger.adebug(
f"Default super user initialized in {asyncio.get_event_loop().time() - current_time:.2f}s"
)

current_time = asyncio.get_event_loop().time()
await logger.adebug("Loading bundles")
temp_dirs, bundles_components_paths = await load_bundles_with_error_handling()
get_settings_service().settings.components_path.extend(bundles_components_paths)
await logger.adebug(f"Bundles loaded in {asyncio.get_event_loop().time() - current_time:.2f}s")

current_time = asyncio.get_event_loop().time()
await logger.adebug("Caching types")
all_types_dict = await get_and_cache_all_types_dict(get_settings_service())
await logger.adebug(f"Types cached in {asyncio.get_event_loop().time() - current_time:.2f}s")

await logger.adebug("Initializing super user")
await initialize_super_user_if_needed()
await initialize_auto_login_default_superuser()
await logger.adebug(f"Super user initialized in {asyncio.get_event_loop().time() - current_time:.2f}s")

current_time = asyncio.get_event_loop().time()
Expand Down
12 changes: 12 additions & 0 deletions src/backend/base/langflow/services/auth/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,21 @@ async def create_super_user(

async def create_user_longterm_token(db: AsyncSession) -> tuple[UUID, dict]:
settings_service = get_settings_service()
if not settings_service.auth_settings.AUTO_LOGIN:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail="Auto login required to create a long-term token"
)

# Prefer configured username; fall back to default or any existing superuser
# NOTE: This user name cannot be a dynamic current user name since it is only used when autologin is True
username = settings_service.auth_settings.SUPERUSER
super_user = await get_user_by_username(db, username)
if not super_user:
from langflow.services.database.models.user.crud import get_all_superusers

superusers = await get_all_superusers(db)
super_user = superusers[0] if superusers else None

if not super_user:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Super user hasn't been created")
access_token_expires_longterm = timedelta(days=365)
Expand Down
25 changes: 14 additions & 11 deletions src/backend/base/langflow/services/settings/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ class AuthSettings(BaseSettings):

NEW_USER_IS_ACTIVE: bool = False
SUPERUSER: str = DEFAULT_SUPERUSER
SUPERUSER_PASSWORD: str = DEFAULT_SUPERUSER_PASSWORD
# Store password as SecretStr to prevent accidental plaintext exposure
SUPERUSER_PASSWORD: SecretStr = Field(default=DEFAULT_SUPERUSER_PASSWORD)

REFRESH_SAME_SITE: Literal["lax", "strict", "none"] = "none"
"""The SameSite attribute of the refresh token cookie."""
Expand All @@ -71,8 +72,8 @@ class AuthSettings(BaseSettings):
model_config = SettingsConfigDict(validate_assignment=True, extra="ignore", env_prefix="LANGFLOW_")

def reset_credentials(self) -> None:
self.SUPERUSER = DEFAULT_SUPERUSER
self.SUPERUSER_PASSWORD = DEFAULT_SUPERUSER_PASSWORD
# Preserve the configured username but scrub the password from memory to avoid plaintext exposure.
self.SUPERUSER_PASSWORD = SecretStr("")

# If autologin is true, then we need to set the credentials to
# the default values
Expand All @@ -81,15 +82,17 @@ def reset_credentials(self) -> None:
@field_validator("SUPERUSER", "SUPERUSER_PASSWORD", mode="before")
@classmethod
def validate_superuser(cls, value, info):
# When AUTO_LOGIN is enabled, force superuser to use default values.
if info.data.get("AUTO_LOGIN"):
if value != DEFAULT_SUPERUSER:
value = DEFAULT_SUPERUSER
logger.debug("Resetting superuser to default value")
if info.data.get("SUPERUSER_PASSWORD") != DEFAULT_SUPERUSER_PASSWORD:
info.data["SUPERUSER_PASSWORD"] = DEFAULT_SUPERUSER_PASSWORD
logger.debug("Resetting superuser password to default value")

return value
logger.debug("Auto login is enabled, forcing superuser to use default values")
if info.field_name == "SUPERUSER":
if value != DEFAULT_SUPERUSER:
logger.debug("Resetting superuser to default value")
return DEFAULT_SUPERUSER
if info.field_name == "SUPERUSER_PASSWORD":
if value != DEFAULT_SUPERUSER_PASSWORD.get_secret_value():
logger.debug("Resetting superuser password to default value")
return DEFAULT_SUPERUSER_PASSWORD

return value

Expand Down
5 changes: 4 additions & 1 deletion src/backend/base/langflow/services/settings/constants.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from pydantic import SecretStr

DEFAULT_SUPERUSER = "langflow"
DEFAULT_SUPERUSER_PASSWORD = "langflow" # noqa: S105
DEFAULT_SUPERUSER_PASSWORD = SecretStr("langflow")

VARIABLES_TO_GET_FROM_ENVIRONMENT = [
"COMPOSIO_API_KEY",
"OPENAI_API_KEY",
Expand Down
11 changes: 7 additions & 4 deletions src/backend/base/langflow/services/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,20 @@ async def setup_superuser(settings_service: SettingsService, session: AsyncSessi
if settings_service.auth_settings.AUTO_LOGIN:
await logger.adebug("AUTO_LOGIN is set to True. Creating default superuser.")
username = DEFAULT_SUPERUSER
password = DEFAULT_SUPERUSER_PASSWORD
password = DEFAULT_SUPERUSER_PASSWORD.get_secret_value()
else:
# Remove the default superuser if it exists
await teardown_superuser(settings_service, session)
username = settings_service.auth_settings.SUPERUSER
password = settings_service.auth_settings.SUPERUSER_PASSWORD
# If AUTO_LOGIN is disabled, attempt to use configured credentials
# or fall back to default credentials if none are provided.
username = settings_service.auth_settings.SUPERUSER or DEFAULT_SUPERUSER
password = (settings_service.auth_settings.SUPERUSER_PASSWORD or DEFAULT_SUPERUSER_PASSWORD).get_secret_value()

if not username or not password:
msg = "Username and password must be set"
raise ValueError(msg)

is_default = (username == DEFAULT_SUPERUSER) and (password == DEFAULT_SUPERUSER_PASSWORD)
is_default = (username == DEFAULT_SUPERUSER) and (password == DEFAULT_SUPERUSER_PASSWORD.get_secret_value())

try:
user = await get_or_create_super_user(
Expand All @@ -96,6 +98,7 @@ async def setup_superuser(settings_service: SettingsService, session: AsyncSessi
msg = "Could not create superuser. Please create a superuser manually."
raise RuntimeError(msg) from exc
finally:
# Scrub credentials from in-memory settings after setup
settings_service.auth_settings.reset_credentials()


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import pytest
from langflow.services.auth.utils import create_user_longterm_token
from langflow.services.deps import get_db_service, get_settings_service
from langflow.services.utils import initialize_services


@pytest.mark.asyncio
async def test_mcp_longterm_token_headless_superuser_integration():
"""Integration-style check that without explicit credentials, AUTO_LOGIN=false path.

Creates a headless superuser via initialize_services and allows minting a long-term token.
"""
settings = get_settings_service()
settings.auth_settings.AUTO_LOGIN = False
settings.auth_settings.SUPERUSER = ""
settings.auth_settings.SUPERUSER_PASSWORD = ""

await initialize_services()

async with get_db_service().with_session() as session:
user_id, tokens = await create_user_longterm_token(session)
assert user_id is not None
assert tokens.get("access_token")
4 changes: 2 additions & 2 deletions src/backend/tests/performance/test_server_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ def test_setup_llm_caching():

async def test_initialize_super_user():
"""Benchmark super user initialization."""
from langflow.initial_setup.setup import initialize_super_user_if_needed
from langflow.initial_setup.setup import initialize_auto_login_default_superuser
from langflow.services.utils import initialize_services

await initialize_services(fix_migration=False)
await initialize_super_user_if_needed()
await initialize_auto_login_default_superuser()
settings_service = get_settings_service()
assert "test_performance.db" in settings_service.settings.database_url

Expand Down
60 changes: 56 additions & 4 deletions src/backend/tests/unit/api/v1/test_mcp_projects.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import asyncio
from unittest.mock import AsyncMock, MagicMock, patch
from uuid import uuid4

import pytest
from fastapi import status
from fastapi import HTTPException, status
from httpx import AsyncClient
from langflow.api.v1.mcp_projects import (
get_project_mcp_server,
Expand All @@ -11,12 +12,14 @@
project_mcp_servers,
project_sse_transports,
)
from langflow.services.auth.utils import get_password_hash
from langflow.services.auth.utils import create_user_longterm_token, get_password_hash
from langflow.services.database.models.flow import Flow
from langflow.services.database.models.folder import Folder
from langflow.services.database.models.user import User
from langflow.services.deps import session_scope
from langflow.services.database.models.user.model import User
from langflow.services.deps import get_db_service, get_settings_service, session_scope
from langflow.services.utils import initialize_services
from mcp.server.sse import SseServerTransport
from sqlmodel import select

# Mark all tests in this module as asyncio
pytestmark = pytest.mark.asyncio
Expand Down Expand Up @@ -62,6 +65,7 @@ async def __aenter__(self):
return (MagicMock(), MagicMock())

async def __aexit__(self, exc_type, exc_val, exc_tb):
# No teardown required for this mock context manager in tests
pass


Expand Down Expand Up @@ -601,6 +605,8 @@ async def test_project_sse_creation(user_test_project):

assert sse_transport2 is sse_transport
assert mcp_server2 is mcp_server
# Yield control to the event loop to satisfy async usage in this test
await asyncio.sleep(0)


async def test_init_mcp_servers(user_test_project, other_test_project):
Expand Down Expand Up @@ -652,3 +658,49 @@ def mock_get_project_sse(project_id):
with patch("langflow.api.v1.mcp_projects.get_project_sse", side_effect=mock_get_project_sse):
# This should not raise any exception, as the error should be caught
await init_mcp_servers()


@pytest.mark.asyncio
async def test_mcp_longterm_token_fails_without_superuser():
"""When AUTO_LOGIN is false and no superuser exists, creating a long-term token should raise 400.

This simulates a clean DB with AUTO_LOGIN disabled and without provisioning a superuser.
"""
settings_service = get_settings_service()
settings_service.auth_settings.AUTO_LOGIN = False

# Ensure no superuser exists in DB
async with get_db_service().with_session() as session:
result = await session.exec(select(User).where(User.is_superuser == True)) # noqa: E712
users = result.all()
for user in users:
await session.delete(user)
await session.commit()

# Now attempt to create long-term token -> expect HTTPException 400
async with get_db_service().with_session() as session:
with pytest.raises(HTTPException, match="Super user hasn't been created"):
await create_user_longterm_token(session)


@pytest.mark.asyncio
async def test_mcp_longterm_token_succeeds_with_headless_fallback():
"""When AUTO_LOGIN is false and no credentials are provided.

The headless fallback should create an internal superuser so MCP can mint a token without raising 400.
"""
settings_service = get_settings_service()
settings_service.auth_settings.AUTO_LOGIN = False
# Clear any configured credentials
settings_service.auth_settings.SUPERUSER = ""
settings_service.auth_settings.SUPERUSER_PASSWORD = "" # SecretStr handled in service

# Re-initialize core services which now create the headless superuser fallback
await initialize_services()

# Should now be able to create a long-term token
async with get_db_service().with_session() as session:
user_id, tokens = await create_user_longterm_token(session)
assert user_id is not None
assert tokens.get("access_token")
assert tokens.get("token_type") == "bearer"
50 changes: 50 additions & 0 deletions src/backend/tests/unit/test_auth_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from pathlib import Path

import pytest
from langflow.services.settings.auth import AuthSettings
from langflow.services.settings.constants import DEFAULT_SUPERUSER, DEFAULT_SUPERUSER_PASSWORD
from pydantic import SecretStr


@pytest.mark.parametrize("auto_login", [True, False])
def test_superuser_password_is_secretstr(auto_login, tmp_path: Path):
cfg_dir = tmp_path.as_posix()
settings = AuthSettings(CONFIG_DIR=cfg_dir, AUTO_LOGIN=auto_login)
assert isinstance(settings.SUPERUSER_PASSWORD, SecretStr)


def test_auto_login_true_forces_default_and_scrubs_password(tmp_path: Path):
cfg_dir = tmp_path.as_posix()
settings = AuthSettings(
CONFIG_DIR=cfg_dir,
AUTO_LOGIN=True,
SUPERUSER="custom",
SUPERUSER_PASSWORD=DEFAULT_SUPERUSER_PASSWORD + "_changed",
)
# Validator forces default username and scrubs password
assert settings.SUPERUSER == DEFAULT_SUPERUSER
assert isinstance(settings.SUPERUSER_PASSWORD, SecretStr)
assert settings.SUPERUSER_PASSWORD.get_secret_value() == ""

# reset_credentials keeps default username (AUTO_LOGIN on) and keeps password scrubbed
settings.reset_credentials()
assert settings.SUPERUSER == DEFAULT_SUPERUSER
assert settings.SUPERUSER_PASSWORD.get_secret_value() == ""


def test_auto_login_false_preserves_username_and_scrubs_password_on_reset(tmp_path: Path):
cfg_dir = tmp_path.as_posix()
settings = AuthSettings(
CONFIG_DIR=cfg_dir,
AUTO_LOGIN=False,
SUPERUSER="admin",
SUPERUSER_PASSWORD="strongpass", # noqa: S106
)
# Values preserved at init
assert settings.SUPERUSER == "admin"
assert settings.SUPERUSER_PASSWORD.get_secret_value() == "strongpass"

# After reset, username preserved, password scrubbed
settings.reset_credentials()
assert settings.SUPERUSER == "admin"
assert settings.SUPERUSER_PASSWORD.get_secret_value() == ""
6 changes: 5 additions & 1 deletion src/backend/tests/unit/test_initial_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,11 @@ async def test_load_bundles_from_urls():
async with session_scope() as session:
await create_super_user(
username=settings_service.auth_settings.SUPERUSER,
password=settings_service.auth_settings.SUPERUSER_PASSWORD,
password=(
settings_service.auth_settings.SUPERUSER_PASSWORD.get_secret_value()
if hasattr(settings_service.auth_settings.SUPERUSER_PASSWORD, "get_secret_value")
else settings_service.auth_settings.SUPERUSER_PASSWORD
),
db=session,
)

Expand Down
Loading
Loading