feat: configure Gitea deployment and environment-managed WorldQuant credentials
Deploy production / deploy (push) Has been cancelled
Deploy production / deploy (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
"""Environment credential precedence, rotation and account ownership boundaries."""
|
||||
|
||||
import pytest
|
||||
from pydantic import SecretStr, ValidationError
|
||||
|
||||
from app.config import Settings
|
||||
from app.models import Account, Admin
|
||||
from app.security import bootstrap, cipher
|
||||
|
||||
|
||||
def test_dotenv_and_process_precedence(settings, tmp_path, monkeypatch):
|
||||
env = tmp_path / '.env'
|
||||
env.write_text("WQ_EMAIL=local@example.com\nWQ_PASSWORD='literal-$value#password'\n")
|
||||
values = settings.model_dump(exclude={'wq_email', 'wq_password'})
|
||||
local = Settings(_env_file=env, **values)
|
||||
assert local.wq_email == 'local@example.com'
|
||||
assert local.wq_password.get_secret_value() == 'literal-$value#password'
|
||||
monkeypatch.setenv('WQ_EMAIL', 'production@example.com')
|
||||
monkeypatch.setenv('WQ_PASSWORD', 'production-secret')
|
||||
production = Settings(_env_file=env, **values)
|
||||
assert production.wq_email == 'production@example.com'
|
||||
assert production.wq_password.get_secret_value() == 'production-secret'
|
||||
assert 'production-secret' not in repr(production)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('email,password', [('person@example.com', ''), ('', 'private-value')])
|
||||
def test_partial_configuration_fails_without_leaking(settings, email, password):
|
||||
values = settings.model_dump(exclude={'wq_email', 'wq_password'})
|
||||
with pytest.raises(ValidationError) as error:
|
||||
Settings(_env_file=None, **values, wq_email=email, wq_password=password)
|
||||
assert 'must be configured together' in str(error.value)
|
||||
assert 'private-value' not in str(error.value)
|
||||
assert 'person@example.com' not in str(error.value)
|
||||
|
||||
|
||||
async def test_env_rotation_api_lock_and_bound_account(app, logged_in):
|
||||
settings = app.state.settings
|
||||
settings.wq_email = 'person@example.com'
|
||||
settings.wq_password = SecretStr('initial-env-secret')
|
||||
async with app.state.sessions() as db:
|
||||
original_admin = (await db.get(Admin, 1)).password_hash
|
||||
await bootstrap(db, settings)
|
||||
account = await db.get(Account, 1)
|
||||
assert cipher(settings).decrypt(account.password_encrypted.encode()) == b'initial-env-secret'
|
||||
account.wq_user_id = 'bound-user'
|
||||
await db.commit()
|
||||
settings.wq_password = SecretStr('rotated-env-secret')
|
||||
async with app.state.sessions() as db:
|
||||
await bootstrap(db, settings)
|
||||
account = await db.get(Account, 1)
|
||||
assert cipher(settings).decrypt(account.password_encrypted.encode()) == b'rotated-env-secret'
|
||||
assert (await db.get(Admin, 1)).password_hash == original_admin
|
||||
response = await logged_in.get('/api/v1/account')
|
||||
assert response.json()['credentials_source'] == 'environment'
|
||||
assert response.json()['configured'] is True
|
||||
assert 'secret' not in response.text and 'password' not in response.text
|
||||
response = await logged_in.put('/api/v1/account/credentials', json={
|
||||
'email': 'person@example.com', 'password': 'manual-secret',
|
||||
})
|
||||
assert response.status_code == 409
|
||||
settings.wq_email = 'other@example.com'
|
||||
async with app.state.sessions() as db:
|
||||
with pytest.raises(ValueError, match='bound WorldQuant account'):
|
||||
await bootstrap(db, settings)
|
||||
await db.rollback()
|
||||
assert (await db.get(Account, 1)).email == 'person@example.com'
|
||||
Reference in New Issue
Block a user