fix(auth): garder l identite Discord du cote verifie
This commit is contained in:
@@ -29,10 +29,13 @@ Usage
|
||||
# Also look for the seeded admin/password account (SEC-003)
|
||||
python app/supporting_scripts/schema_report.py --check-seed-accounts
|
||||
|
||||
# Find Discord identities that must be reconciled before UNIQUE (SEC-012)
|
||||
python app/supporting_scripts/schema_report.py --check-discord-identities
|
||||
|
||||
Exit codes
|
||||
----------
|
||||
0 the live schema matches the models
|
||||
1 drift found — the report says what
|
||||
1 drift or requested data risk found — the report says what
|
||||
2 could not connect or read the catalogue
|
||||
|
||||
Reading the output
|
||||
@@ -315,6 +318,42 @@ def find_seed_accounts(engine):
|
||||
return results
|
||||
|
||||
|
||||
def find_duplicate_discord_identities(engine):
|
||||
"""Discord snowflakes claimed by more than one account (SEC-012).
|
||||
|
||||
New links are now refused in application code, but existing production
|
||||
rows predate that guard. These groups must be reconciled before Alembic
|
||||
can add the database-level UNIQUE constraint.
|
||||
|
||||
Returns:
|
||||
list[tuple]: (discord_user_id, comma-separated usernames, count).
|
||||
"""
|
||||
from sqlalchemy import text
|
||||
|
||||
with engine.connect() as connection:
|
||||
rows = connection.execute(
|
||||
text(
|
||||
'SELECT discord_user_id, COUNT(*) AS account_count '
|
||||
'FROM users '
|
||||
"WHERE discord_user_id IS NOT NULL AND discord_user_id <> '' "
|
||||
'GROUP BY discord_user_id HAVING COUNT(*) > 1 '
|
||||
'ORDER BY discord_user_id'
|
||||
)
|
||||
).fetchall()
|
||||
|
||||
duplicates = []
|
||||
for discord_user_id, account_count in rows:
|
||||
usernames = connection.execute(
|
||||
text(
|
||||
'SELECT username FROM users '
|
||||
'WHERE discord_user_id = :discord_user_id ORDER BY username'
|
||||
),
|
||||
{'discord_user_id': discord_user_id},
|
||||
).scalars()
|
||||
duplicates.append((discord_user_id, ', '.join(usernames), account_count))
|
||||
return duplicates
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser(description=__doc__.split('\n')[0])
|
||||
parser.add_argument(
|
||||
@@ -327,6 +366,11 @@ def main(argv=None):
|
||||
action='store_true',
|
||||
help='Also look for the admin/password account seeded by clear_db.py (SEC-003).',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--check-discord-identities',
|
||||
action='store_true',
|
||||
help='Find duplicate Discord IDs that block the SEC-012 UNIQUE constraint.',
|
||||
)
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
if not args.url:
|
||||
@@ -375,9 +419,24 @@ def main(argv=None):
|
||||
)
|
||||
print(f' {username} ({role}): {verdict}')
|
||||
|
||||
duplicate_discord_identities = []
|
||||
if args.check_discord_identities:
|
||||
print('\n' + '=' * 78)
|
||||
print('Duplicate Discord identities (SEC-012)')
|
||||
print('=' * 78)
|
||||
try:
|
||||
duplicate_discord_identities = find_duplicate_discord_identities(engine)
|
||||
except SQLAlchemyError as exc:
|
||||
print(f'Could not check: {exc}')
|
||||
else:
|
||||
if not duplicate_discord_identities:
|
||||
print('No Discord identity is shared by multiple accounts.')
|
||||
for discord_user_id, usernames, account_count in duplicate_discord_identities:
|
||||
print(f' {discord_user_id}: {account_count} accounts ({usernames})')
|
||||
|
||||
blocking = sum(1 for f in findings if f.severity == BLOCKING)
|
||||
print(f'\n{len(findings)} finding(s), {blocking} blocking.')
|
||||
return 1 if findings else 0
|
||||
return 1 if findings or duplicate_discord_identities else 0
|
||||
|
||||
|
||||
if __name__ == '__main__': # pragma: no cover
|
||||
|
||||
Reference in New Issue
Block a user