fix(authz): balayer le motif au lieu d attendre la passe suivante
Le commit precedent finissait teams.py en notant que le defaut venait d'une
correction appliquee a un seul endroit. Balayer les autres modules
immediatement, plutot que d'attendre qu'une passe d'audit les retrouve, a
sorti les deux derniers.
tryouts.register_player lisait int(request.form.get('player_id')) -- 500 sur
une valeur non numerique -- et verifiait le role sans regarder
is_active_account. Un compte desactive pouvait donc etre inscrit a une
selection.
users/contracts._selectable_players ne filtrait pas non plus les comptes
desactives dans sa branche non-coach : la liste de depot de contrat proposait
encore des gens partis du club. Un contrat est un document nominatif signe.
PlayerSelectionSchema porte desormais le champ, et TeamPlayerSchema en herite
en ajoutant son statut. Un schema partage est ce qui empeche le prochain
appelant d'etre oublie -- c'est precisement parce que chaque route avait le
sien, ecrit a la main, que la correction a du etre faite trois fois.
Verifie par mutation.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -296,3 +296,66 @@ class TestEndedTryoutsAreClosed:
|
||||
|
||||
with app.app_context():
|
||||
assert Match.query.count() == 0
|
||||
|
||||
|
||||
class TestManualRegistrationChecksTheAccount:
|
||||
"""SEC-16, the last two places the pattern survived.
|
||||
|
||||
`register_player` read `int(request.form.get('player_id'))` — a 500 on a
|
||||
non-numeric value — and checked the role but not whether the account had
|
||||
been deactivated. The contract listing had the same gap in its player
|
||||
select. Both were found by sweeping for the pattern after fixing
|
||||
teams.py, rather than by waiting for the next audit pass to find them.
|
||||
"""
|
||||
|
||||
@pytest.fixture
|
||||
def tryout_id(self, app, make_user):
|
||||
from app.models import Tryout
|
||||
|
||||
admin_id = make_user('admin')
|
||||
with app.app_context():
|
||||
tryout = Tryout(
|
||||
title='Spring',
|
||||
game='Valorant',
|
||||
date=date(2030, 4, 1),
|
||||
created_by=admin_id,
|
||||
)
|
||||
db.session.add(tryout)
|
||||
db.session.commit()
|
||||
return tryout.id
|
||||
|
||||
def test_a_non_numeric_id_is_not_a_500(self, app, client, tryout_id, as_role):
|
||||
as_role('admin')
|
||||
|
||||
response = client.post(
|
||||
f'/tryouts/{tryout_id}/register_player', data={'player_id': 'not-a-number'}
|
||||
)
|
||||
|
||||
assert response.status_code < 500
|
||||
|
||||
def test_an_active_player_registers(self, app, client, tryout_id, as_role, make_user):
|
||||
"""The premise: without it the test below passes against a route
|
||||
that registers nobody."""
|
||||
from app.models import TryoutRegistration
|
||||
|
||||
player_id = make_user('player')
|
||||
as_role('admin')
|
||||
|
||||
client.post(f'/tryouts/{tryout_id}/register_player', data={'player_id': str(player_id)})
|
||||
|
||||
with app.app_context():
|
||||
assert TryoutRegistration.query.filter_by(tryout_id=tryout_id).count() == 1
|
||||
|
||||
def test_a_deactivated_player_is_refused(self, app, client, tryout_id, as_role, make_user):
|
||||
from app.models import TryoutRegistration, User
|
||||
|
||||
player_id = make_user('player')
|
||||
with app.app_context():
|
||||
db.session.get(User, player_id).is_active_account = False
|
||||
db.session.commit()
|
||||
as_role('admin')
|
||||
|
||||
client.post(f'/tryouts/{tryout_id}/register_player', data={'player_id': str(player_id)})
|
||||
|
||||
with app.app_context():
|
||||
assert TryoutRegistration.query.filter_by(tryout_id=tryout_id).count() == 0
|
||||
|
||||
Reference in New Issue
Block a user