Skip to content

ENHANCEMENT: Implement centralized access control with decorators and resource ownership checks #25

Description

@Senthil455

Description

Role-based access control is implemented inconsistently across routes. While most routes check session['role'] before processing, the approach varies:

  • Some routes use a helper-like pattern inline
  • There are gaps where a user could potentially access another role's routes by guessing or manipulating URLs
  • There is no centralized middleware or decorator for access control

Current Approach

# Inconsistent - different patterns used across routes
# Pattern 1: Inline check at start
if 'userid' not in session or session['role'] != 'teacher':
    return redirect(url_for('login'))

# Pattern 2: Redirect to role-specific dashboard
if session['role'] == 'admin':
    return redirect(url_for('admin_dashboard'))

# Pattern 3: No explicit check (relying on session presence)

Potential Gaps

  • Routes like export_quizzes check for admin but redirect behavior could be exploited
  • Some teacher routes may not properly validate that the teacher owns the resource (e.g., a teacher could potentially view another teacher's quiz data via direct IDs)
  • The view_responses and list_responses routes serve both teachers and students but may not properly scope data

Recommended Enhancement

  1. Create a centralized access control decorator:

    from functools import wraps
    
    def login_required(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            if 'userid' not in session:
                flash('Please log in first.', 'warning')
                return redirect(url_for('login'))
            return f(*args, **kwargs)
        return decorated_function
    
    def role_required(*roles):
        def decorator(f):
            @wraps(f)
            def decorated_function(*args, **kwargs):
                if 'userid' not in session:
                    return redirect(url_for('login'))
                if session['role'] not in roles:
                    flash('You do not have permission to access this page.', 'danger')
                    return redirect(url_for('login'))
                return f(*args, **kwargs)
            return decorated_function
        return decorator
  2. Apply decorators to routes:

    @app.route('/teacher_dashboard')
    @login_required
    @role_required('teacher')
    def teacher_dashboard():
        ...
  3. Add resource ownership validation:

    # Verify teacher owns the quiz before allowing modifications
    cur.execute("SELECT createdby FROM quizzes WHERE quizid = %s", (quiz_id,))
    if cur.fetchone()[0] != session['userid']:
        flash('You do not have permission to modify this quiz.', 'danger')
        return redirect(url_for('teacher_dashboard'))

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestsecuritySecurity vulnerability or concern

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions