Newer
Older
gnexus-creds / tests / test_core.py
from datetime import UTC, datetime, timedelta

from gnexus_creds.schemas import SecretCreate, SecretFieldIn, SecretUpdate
from gnexus_creds.services import (
    create_secret,
    is_expired,
    list_versions,
    reveal_secret,
    update_secret,
)


def test_secret_versioning_and_reveal(db_session, actor):
    created = create_secret(
        db_session,
        actor,
        SecretCreate(
            title="Facebook",
            purpose="facebook.com",
            category="social",
            tags=["Social", "Login"],
            fields=[
                SecretFieldIn(name="login", value="me@example.test", encrypted=False, position=1),
                SecretFieldIn(
                    name="password", value="secret", encrypted=True, masked=True, position=2
                ),
            ],
        ),
    )
    db_session.commit()

    versions = list_versions(db_session, actor, created.id)
    assert len(versions) == 1

    update_secret(db_session, actor, created.id, SecretUpdate(notes="short note"))
    db_session.commit()
    assert len(list_versions(db_session, actor, created.id)) == 1

    update_secret(
        db_session,
        actor,
        created.id,
        SecretUpdate(
            fields=[SecretFieldIn(name="password", value="new-secret", encrypted=True, position=1)]
        ),
    )
    db_session.commit()
    assert len(list_versions(db_session, actor, created.id)) == 2

    revealed = reveal_secret(db_session, actor, created.id)
    values = {field.name: field.value for field in revealed.fields}
    assert values["password"] == "new-secret"


def test_is_expired_handles_sqlite_naive_datetime():
    now = datetime.now(UTC)
    naive_start = (now - timedelta(minutes=2)).replace(tzinfo=None)
    assert is_expired(naive_start, now=now, delta=timedelta(minutes=1))


def test_reveal_works_and_transplant_is_caught(db_session, actor):
    import pytest as _pytest
    from cryptography.exceptions import InvalidTag

    from gnexus_creds.models import SecretVersion, utcnow, Secret as _Secret  # noqa: F401

    created = create_secret(
        db_session,
        actor,
        SecretCreate(
            title="Vault",
            fields=[SecretFieldIn(name="password", value="v1", encrypted=True, position=1)],
        ),
    )
    db_session.commit()
    update_secret(
        db_session,
        actor,
        created.id,
        SecretUpdate(fields=[SecretFieldIn(name="password", value="v2", encrypted=True, position=1)]),
    )
    db_session.commit()

    versions = list_versions(db_session, actor, created.id)
    revealed = reveal_secret(db_session, actor, created.id)
    assert {f.name: f.value for f in revealed.fields}["password"] == "v2"

    # swap the two versions' password ciphertexts — same user, same key, so
    # only the aad binding can notice
    stored = db_session.query(SecretVersion).filter_by(secret_id=created.id).all()
    a, b = stored
    a.fields, b.fields = (
        [dict(f, value=other["value"]) for f, other in
         ((f, next(x for x in b.fields if x["name"] == f["name"])) for f in a.fields)],
        [dict(f, value=other["value"]) for f, other in
         ((f, next(x for x in a.fields if x["name"] == f["name"])) for f in b.fields)],
    )
    db_session.flush()
    with _pytest.raises(InvalidTag):
        reveal_secret(db_session, actor, created.id)


def test_notes_accept_up_to_255_chars(db_session, actor):
    long_notes = "н" * 255
    created = create_secret(
        db_session, actor, SecretCreate(title="NoteCap", notes=long_notes)
    )
    db_session.commit()
    assert created.notes == long_notes


def test_tags_update_audit_keeps_old_value(db_session, actor):
    from gnexus_creds.models import AuditEvent

    created = create_secret(
        db_session, actor, SecretCreate(title="Tagged", tags=["old"])
    )
    db_session.commit()
    update_secret(db_session, actor, created.id, SecretUpdate(tags=["new"]))
    db_session.commit()
    row = db_session.query(AuditEvent).filter_by(action="secret.metadata_updated").one()
    diff = row.audit_metadata["diff"]["tags"]
    assert diff == {"old": ["old"], "new": ["new"]}