* Studio: reject whitespace-only passwords * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Studio: reject any whitespace in passwords * Studio: surface whitespace error in setup form, isolate auth test import --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Daniel Han <danielhanchen@gmail.com>
75 lines
2.5 KiB
Python
75 lines
2.5 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-only
|
|
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
|
|
|
|
import asyncio
|
|
import importlib.util
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from fastapi import HTTPException
|
|
|
|
_BACKEND_ROOT = Path(__file__).resolve().parents[1]
|
|
if str(_BACKEND_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(_BACKEND_ROOT))
|
|
|
|
from models.auth import ChangePasswordRequest # noqa: E402
|
|
|
|
# Load routes/auth.py directly so collection does not execute routes/__init__.py,
|
|
# which pulls in the heavy training/models/inference routers.
|
|
_route_path = _BACKEND_ROOT / "routes" / "auth.py"
|
|
_spec = importlib.util.spec_from_file_location("_change_password_route", _route_path)
|
|
assert _spec is not None and _spec.loader is not None
|
|
auth_routes = importlib.util.module_from_spec(_spec)
|
|
_spec.loader.exec_module(auth_routes)
|
|
|
|
|
|
@pytest.fixture
|
|
def _user(monkeypatch):
|
|
monkeypatch.setattr(
|
|
auth_routes.storage,
|
|
"get_user_and_secret",
|
|
lambda username: ("salt", "hash", "jwt-secret", False),
|
|
)
|
|
monkeypatch.setattr(
|
|
auth_routes.hashing,
|
|
"verify_password",
|
|
lambda password, salt, pwd_hash: password == "bootstrap-pw",
|
|
)
|
|
|
|
|
|
def _change(new_password):
|
|
payload = ChangePasswordRequest(
|
|
current_password = "bootstrap-pw",
|
|
new_password = new_password,
|
|
)
|
|
return asyncio.run(auth_routes.change_password(payload, None, "unsloth"))
|
|
|
|
|
|
def test_rejects_whitespace_only_password(_user):
|
|
with pytest.raises(HTTPException) as excinfo:
|
|
_change(" " * 8)
|
|
assert excinfo.value.status_code == 400
|
|
assert "spaces" in excinfo.value.detail
|
|
|
|
|
|
def test_rejects_tabs_and_spaces_password(_user):
|
|
with pytest.raises(HTTPException) as excinfo:
|
|
_change(" \t \t \t \t ")
|
|
assert excinfo.value.status_code == 400
|
|
|
|
|
|
def test_rejects_password_containing_spaces(_user):
|
|
with pytest.raises(HTTPException) as excinfo:
|
|
_change("correct horse battery")
|
|
assert excinfo.value.status_code == 400
|
|
assert "spaces" in excinfo.value.detail
|
|
|
|
|
|
def test_allows_password_without_spaces(_user, monkeypatch):
|
|
monkeypatch.setattr(auth_routes.storage, "update_password", lambda *args, **kwargs: True)
|
|
monkeypatch.setattr(auth_routes, "create_access_token", lambda subject: "at")
|
|
monkeypatch.setattr(auth_routes, "create_refresh_token", lambda subject: "rt")
|
|
token = _change("correct-horse-battery")
|
|
assert token.access_token == "at"
|
|
assert token.must_change_password is False
|