109 lines
3.8 KiB
Python
109 lines
3.8 KiB
Python
from cryptography.fernet import Fernet
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import sessionmaker
|
|
from sqlalchemy.pool import StaticPool
|
|
|
|
from app.auth.account import authenticate_self_service
|
|
from app.db.models import Base
|
|
from app.db.repositories import UserRepository
|
|
from app.security.credentials import CredentialCipher
|
|
|
|
|
|
def _make_session_and_cipher():
|
|
engine = create_engine("sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool)
|
|
Base.metadata.create_all(engine)
|
|
factory = sessionmaker(bind=engine, expire_on_commit=False)
|
|
cipher = CredentialCipher(Fernet.generate_key().decode("ascii"))
|
|
return factory(), cipher
|
|
|
|
|
|
def _seed_user(session, cipher, **overrides):
|
|
values = dict(
|
|
name="Max",
|
|
enabled=True,
|
|
mywhoosh_email_enc=cipher.encrypt("max@mywhoosh.example"),
|
|
mywhoosh_password_enc=cipher.encrypt("mw-secret"),
|
|
garmin_email_enc=cipher.encrypt("max@garmin.example"),
|
|
garmin_password_enc=cipher.encrypt("garmin-secret"),
|
|
)
|
|
values.update(overrides)
|
|
return UserRepository(session).create(**values)
|
|
|
|
|
|
def test_authenticates_with_mywhoosh_credentials() -> None:
|
|
session, cipher = _make_session_and_cipher()
|
|
user = _seed_user(session, cipher)
|
|
|
|
result = authenticate_self_service(session, cipher, "max@mywhoosh.example", "mw-secret")
|
|
|
|
assert result is not None
|
|
assert result.id == user.id
|
|
|
|
|
|
def test_authenticates_with_garmin_credentials() -> None:
|
|
session, cipher = _make_session_and_cipher()
|
|
user = _seed_user(session, cipher)
|
|
|
|
result = authenticate_self_service(session, cipher, "max@garmin.example", "garmin-secret")
|
|
|
|
assert result is not None
|
|
assert result.id == user.id
|
|
|
|
|
|
def test_rejects_wrong_password() -> None:
|
|
session, cipher = _make_session_and_cipher()
|
|
_seed_user(session, cipher)
|
|
|
|
assert authenticate_self_service(session, cipher, "max@mywhoosh.example", "wrong") is None
|
|
|
|
|
|
def test_rejects_unknown_email() -> None:
|
|
session, cipher = _make_session_and_cipher()
|
|
_seed_user(session, cipher)
|
|
|
|
assert authenticate_self_service(session, cipher, "nobody@example.com", "mw-secret") is None
|
|
|
|
|
|
def test_rejects_mixed_email_and_password_from_different_accounts() -> None:
|
|
"""A MyWhoosh email paired with the Garmin password (or vice versa) for
|
|
the same user must not authenticate -- each pair is checked together."""
|
|
session, cipher = _make_session_and_cipher()
|
|
_seed_user(session, cipher)
|
|
|
|
assert authenticate_self_service(session, cipher, "max@mywhoosh.example", "garmin-secret") is None
|
|
assert authenticate_self_service(session, cipher, "max@garmin.example", "mw-secret") is None
|
|
|
|
|
|
def test_rejects_empty_password() -> None:
|
|
session, cipher = _make_session_and_cipher()
|
|
_seed_user(session, cipher)
|
|
|
|
assert authenticate_self_service(session, cipher, "max@mywhoosh.example", "") is None
|
|
|
|
|
|
def test_picks_correct_user_among_several() -> None:
|
|
session, cipher = _make_session_and_cipher()
|
|
_seed_user(
|
|
session,
|
|
cipher,
|
|
name="Anna",
|
|
mywhoosh_email_enc=cipher.encrypt("anna@mywhoosh.example"),
|
|
mywhoosh_password_enc=cipher.encrypt("anna-secret"),
|
|
garmin_email_enc=cipher.encrypt("anna@garmin.example"),
|
|
garmin_password_enc=cipher.encrypt("anna-garmin-secret"),
|
|
)
|
|
bob = _seed_user(
|
|
session,
|
|
cipher,
|
|
name="Bob",
|
|
mywhoosh_email_enc=cipher.encrypt("bob@mywhoosh.example"),
|
|
mywhoosh_password_enc=cipher.encrypt("bob-secret"),
|
|
garmin_email_enc=cipher.encrypt("bob@garmin.example"),
|
|
garmin_password_enc=cipher.encrypt("bob-garmin-secret"),
|
|
)
|
|
|
|
result = authenticate_self_service(session, cipher, "bob@mywhoosh.example", "bob-secret")
|
|
|
|
assert result is not None
|
|
assert result.id == bob.id
|