147 lines
4.8 KiB
Python
147 lines
4.8 KiB
Python
import pytest
|
|
|
|
from app.core.config import settings
|
|
from app.crud import role as role_crud
|
|
from app.crud import user as user_crud
|
|
from app.services import ldap as ldap_service
|
|
|
|
|
|
@pytest.fixture
|
|
def ldap_enabled(monkeypatch):
|
|
monkeypatch.setattr(settings, "ldap_enabled", True)
|
|
|
|
|
|
def _fake_identity(username="jdoe", full_name="Jane Doe"):
|
|
return ldap_service.LdapIdentity(
|
|
username=username,
|
|
email=f"{username}@example.com",
|
|
full_name=full_name,
|
|
)
|
|
|
|
|
|
async def test_ldap_login_provisions_user_with_default_role(
|
|
client, seeded, db_sessionmaker, monkeypatch, ldap_enabled
|
|
):
|
|
# An admin has marked the "user" role as default.
|
|
async with db_sessionmaker() as db:
|
|
role = await role_crud.get(db, seeded["roles"]["user"].id)
|
|
await role_crud.set_default(db, role)
|
|
|
|
async def fake_authenticate(username, password):
|
|
assert (username, password) == ("jdoe", "secret")
|
|
return _fake_identity()
|
|
|
|
monkeypatch.setattr(ldap_service, "authenticate", fake_authenticate)
|
|
|
|
resp = await client.post(
|
|
"/api/auth/ldap-login",
|
|
data={"username": "jdoe", "password": "secret"},
|
|
)
|
|
assert resp.status_code == 200, resp.text
|
|
access = resp.json()["access_token"]
|
|
|
|
# A local profile was provisioned from the username with the default role.
|
|
async with db_sessionmaker() as db:
|
|
user = await user_crud.get_by_username(db, "jdoe")
|
|
assert user is not None
|
|
assert user.email == "jdoe@example.com"
|
|
assert user.full_name == "Jane Doe"
|
|
assert user.auth_source == "ldap"
|
|
assert user.hashed_password is None
|
|
assert {r.name for r in user.roles} == {"user"}
|
|
|
|
# The issued token authenticates.
|
|
me = await client.get(
|
|
"/api/users/me", headers={"Authorization": f"Bearer {access}"}
|
|
)
|
|
assert me.status_code == 200
|
|
assert me.json()["username"] == "jdoe"
|
|
|
|
|
|
async def test_ldap_login_is_idempotent(
|
|
client, seeded, db_sessionmaker, monkeypatch, ldap_enabled
|
|
):
|
|
async with db_sessionmaker() as db:
|
|
role = await role_crud.get(db, seeded["roles"]["user"].id)
|
|
await role_crud.set_default(db, role)
|
|
|
|
async def fake_authenticate(username, password):
|
|
return _fake_identity()
|
|
|
|
monkeypatch.setattr(ldap_service, "authenticate", fake_authenticate)
|
|
|
|
for _ in range(2):
|
|
resp = await client.post(
|
|
"/api/auth/ldap-login", data={"username": "jdoe", "password": "secret"}
|
|
)
|
|
assert resp.status_code == 200
|
|
|
|
async with db_sessionmaker() as db:
|
|
assert await user_crud.count(db) == 1
|
|
|
|
|
|
async def test_ldap_login_bad_credentials(client, seeded, monkeypatch, ldap_enabled):
|
|
async def fake_authenticate(username, password):
|
|
return None
|
|
|
|
monkeypatch.setattr(ldap_service, "authenticate", fake_authenticate)
|
|
|
|
resp = await client.post(
|
|
"/api/auth/ldap-login", data={"username": "jdoe", "password": "nope"}
|
|
)
|
|
assert resp.status_code == 401
|
|
|
|
|
|
async def test_ldap_login_disabled_returns_404(client, seeded):
|
|
# ldap_enabled defaults to False.
|
|
resp = await client.post(
|
|
"/api/auth/ldap-login", data={"username": "jdoe", "password": "secret"}
|
|
)
|
|
assert resp.status_code == 404
|
|
|
|
|
|
async def test_ldap_authenticate_short_circuits_when_disabled():
|
|
# No network call: disabled or empty credentials return None immediately.
|
|
assert await ldap_service.authenticate("jdoe", "secret") is None
|
|
|
|
|
|
async def test_set_default_role_is_exclusive(db_sessionmaker):
|
|
async with db_sessionmaker() as db:
|
|
a = await role_crud.create(db, name="alpha")
|
|
b = await role_crud.create(db, name="beta")
|
|
|
|
await role_crud.set_default(db, a)
|
|
assert (await role_crud.get_default(db)).id == a.id
|
|
|
|
# Marking another default clears the previous one.
|
|
await role_crud.set_default(db, b)
|
|
default = await role_crud.get_default(db)
|
|
assert default.id == b.id
|
|
|
|
await db.refresh(a)
|
|
assert a.is_default is False
|
|
|
|
|
|
async def test_local_login_rejects_ldap_user(
|
|
client, seeded, db_sessionmaker, monkeypatch, ldap_enabled
|
|
):
|
|
"""An LDAP-provisioned user (no local password) can't use the local login."""
|
|
async with db_sessionmaker() as db:
|
|
role = await role_crud.get(db, seeded["roles"]["user"].id)
|
|
await role_crud.set_default(db, role)
|
|
|
|
async def fake_authenticate(username, password):
|
|
return _fake_identity()
|
|
|
|
monkeypatch.setattr(ldap_service, "authenticate", fake_authenticate)
|
|
await client.post(
|
|
"/api/auth/ldap-login", data={"username": "jdoe", "password": "secret"}
|
|
)
|
|
|
|
# Local login with the synthesized email must fail (no local password).
|
|
resp = await client.post(
|
|
"/api/auth/login",
|
|
data={"username": "jdoe@example.com", "password": "secret"},
|
|
)
|
|
assert resp.status_code == 401
|