ajouter des sécurité sur les URL, les Roles, les mdp
This commit is contained in:
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
@@ -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
@@ -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
@@ -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()]
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user