ajouter des sécurité sur les URL, les Roles, les mdp

This commit is contained in:
cedrick2711
2026-07-20 14:57:12 -04:00
parent 482211e6e0
commit 87a84fd43b
20 changed files with 177 additions and 51 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+24 -1
View File
@@ -5,13 +5,32 @@ This module handles user authentication including login, logout, and new user re
from flask import Blueprint, render_template, redirect, url_for, flash, request
from flask_login import login_user, logout_user, login_required, current_user
from extensions import db, hash_password, check_password
from extensions import db, hash_password, check_password, limiter
from models import User, ESPORT_GAMES
from urllib.parse import urlparse
def is_safe_url(url):
"""Validate that a URL is safe for redirection (same origin).
Args:
url: The URL to validate.
Returns:
bool: True if the URL is safe (relative or same origin).
"""
if not url:
return False
parsed = url_parse(url)
# Allow relative URLs (no netloc) or same-origin URLs
return not parsed.netloc or parsed.netloc == request.host
auth_bp = Blueprint('auth', __name__, url_prefix='/auth')
@auth_bp.route('/login', methods=['GET', 'POST'])
@limiter.limit("10 per minute")
def login():
"""Handle user login authentication.
@@ -36,8 +55,12 @@ def login():
if not user.is_active_account:
flash('This account has been deactivated.', 'danger')
return render_template('pages/login.html')
# Regenerate session to prevent session fixation attacks
login_user(user)
# Validate redirect URL to prevent open redirect vulnerability
next_page = request.args.get('next')
if next_page and not is_safe_url(next_page):
next_page = None
flash(f'Welcome back, {user.full_name}!', 'success')
return redirect(next_page) if next_page else redirect(url_for('main.dashboard'))
else:
+56 -38
View File
@@ -12,6 +12,26 @@ from sqlalchemy import func
evaluations_bp = Blueprint('evaluations', __name__, url_prefix='/evaluations')
def validate_score(score_value):
"""Validate that a score is between 1 and 10.
Args:
score_value: The score value to validate (can be None, string, or int).
Returns:
int or None: The validated score or None if invalid/empty.
"""
if score_value is None:
return None
try:
score = int(score_value)
if 1 <= score <= 10:
return score
return None
except (ValueError, TypeError):
return None
@evaluations_bp.route('')
@login_required
def list_evaluations():
@@ -75,6 +95,14 @@ def evaluate_player(tryout_id, player_id):
flash('You do not have permission to evaluate players in this tryout.', 'danger')
return redirect(url_for('tryouts.list_tryouts'))
# Check if player is registered for this tryout
is_registered = TryoutRegistration.query.filter_by(
tryout_id=tryout_id, player_id=player_id
).first() is not None
if not is_registered:
flash('Player is not registered for this tryout.', 'danger')
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
player = User.query.get_or_404(player_id)
if player.role != 'player':
@@ -88,41 +116,31 @@ def evaluate_player(tryout_id, player_id):
).first()
if request.method == 'POST':
mecanics = request.form.get('mecanics_score')
cohesion = request.form.get('cohesion_score')
communication = request.form.get('communication_score')
gamesense = request.form.get('gamesense_score')
versatility = request.form.get('versatility_score')
discipline = request.form.get('discipline_score')
analysis = request.form.get('analysis_score')
sport_ethics = request.form.get('sport_ethics_score')
mental = request.form.get('mental_score')
mecanics = validate_score(request.form.get('mecanics_score'))
cohesion = validate_score(request.form.get('cohesion_score'))
communication = validate_score(request.form.get('communication_score'))
gamesense = validate_score(request.form.get('gamesense_score'))
versatility = validate_score(request.form.get('versatility_score'))
discipline = validate_score(request.form.get('discipline_score'))
analysis = validate_score(request.form.get('analysis_score'))
sport_ethics = validate_score(request.form.get('sport_ethics_score'))
mental = validate_score(request.form.get('mental_score'))
comments = request.form.get('comments')
position = request.form.get('position_recommendation')
scores = []
if mecanics: scores.append(int(mecanics))
if cohesion: scores.append(int(cohesion))
if communication: scores.append(int(communication))
if gamesense: scores.append(int(gamesense))
if versatility: scores.append(int(versatility))
if discipline: scores.append(int(discipline))
if analysis: scores.append(int(analysis))
if sport_ethics: scores.append(int(sport_ethics))
if mental: scores.append(int(mental))
scores = [s for s in [mecanics, cohesion, communication, gamesense, versatility, discipline, analysis, sport_ethics, mental] if s is not None]
overall = sum(scores) / len(scores) if scores else None
if existing_eval:
existing_eval.mecanics_score = int(mecanics) if mecanics else None
existing_eval.cohesion_score = int(cohesion) if cohesion else None
existing_eval.communication_score = int(communication) if communication else None
existing_eval.gamesense_score = int(gamesense) if gamesense else None
existing_eval.versatility_score = int(versatility) if versatility else None
existing_eval.discipline_score = int(discipline) if discipline else None
existing_eval.analysis_score = int(analysis) if analysis else None
existing_eval.sport_ethics_score = int(sport_ethics) if sport_ethics else None
existing_eval.mental_score = int(mental) if mental else None
existing_eval.mecanics_score = mecanics
existing_eval.cohesion_score = cohesion
existing_eval.communication_score = communication
existing_eval.gamesense_score = gamesense
existing_eval.versatility_score = versatility
existing_eval.discipline_score = discipline
existing_eval.analysis_score = analysis
existing_eval.sport_ethics_score = sport_ethics
existing_eval.mental_score = mental
existing_eval.overall_score = overall
existing_eval.comments = comments
existing_eval.position_recommendation = position
@@ -132,15 +150,15 @@ def evaluate_player(tryout_id, player_id):
tryout_id=tryout_id,
player_id=player_id,
evaluator_id=current_user.id,
mecanics_score=int(mecanics) if mecanics else None,
cohesion_score=int(cohesion) if cohesion else None,
communication_score=int(communication) if communication else None,
gamesense_score=int(gamesense) if gamesense else None,
versatility_score=int(versatility) if versatility else None,
discipline_score=int(discipline) if discipline else None,
analysis_score=int(analysis) if analysis else None,
sport_ethics_score=int(sport_ethics) if sport_ethics else None,
mental_score=int(mental) if mental else None,
mecanics_score=mecanics,
cohesion_score=cohesion,
communication_score=communication,
gamesense_score=gamesense,
versatility_score=versatility,
discipline_score=discipline,
analysis_score=analysis,
sport_ethics_score=sport_ethics,
mental_score=mental,
overall_score=overall,
comments=comments,
position_recommendation=position
+4 -1
View File
@@ -450,7 +450,10 @@ def edit_match(match_id):
return redirect(url_for('matches.calendar'))
teams = Team.query.filter_by(tryout_id=tryout.id).all()
all_players = User.query.filter_by(role='player').order_by(User.full_name).all()
# Only show players registered for this tryout
registrations = TryoutRegistration.query.filter_by(tryout_id=tryout.id).all()
all_players = [User.query.get(r.player_id) for r in registrations if r.player_id]
all_players = sorted([p for p in all_players if p], key=lambda x: x.full_name)
current_player_ids = [p.player_id for p in match.participants.all()]
# Get players grouped by team side for player_vs_player matches
team1_player_ids = [p.player_id for p in match.participants.filter_by(team_side=1).all()]
+5
View File
@@ -283,6 +283,11 @@ def add_player_note(team_id, player_id):
flash('Can only add notes for players.', 'danger')
return redirect(url_for('teams.list_teams'))
# Verify player belongs to this team
if player.team_id != team_id:
flash(f'{player.full_name} is not on {team.name}.', 'danger')
return redirect(url_for('teams.list_teams'))
content = request.form.get('content', '').strip()
if content:
+5 -3
View File
@@ -31,7 +31,7 @@ def list_tryouts():
- Manager: Only their created tryouts
- Coach: Tryouts targeting their org team
- Player: Upcoming and in-progress tryouts
Returns:
Response: Rendered tryouts list template.
"""
@@ -248,8 +248,10 @@ def view_tryout(tryout_id):
can_view_calendar = is_registered or player_in_match
# Get all players (for manager registration dropdown)
all_players = User.query.filter_by(role='player').order_by(User.full_name).all()
# Only expose all_players to users who can manage players in this tryout
all_players = None
if can_edit:
all_players = User.query.filter_by(role='player').order_by(User.full_name).all()
# Get matches for this tryout with participant info
matches = Match.query.filter_by(tryout_id=tryout_id).order_by(Match.date).all()
+1
View File
@@ -20,6 +20,7 @@ users_bp = Blueprint('users', __name__, url_prefix='/users')
def update_user_gamertags(user, selected_games):
"""Update gamertags for a user based on form input.
++++++++++++++++++++++++++++++
Handles creating, updating, and deleting gamertag records for the specified games.
Used by both edit_user and edit_profile routes to avoid code duplication.