Spaces:
Sleeping
Sleeping
dhruv575 commited on
Commit ·
7d66e3d
1
Parent(s): ecbc4c7
Fixing department routes
Browse files- controllers/department_controller.py +1 -1
- fix/auth_utils_fix.py +92 -0
- fix/department_routes_fix.py +34 -0
- routes/department_routes.py +2 -0
- utils/auth.py +4 -2
controllers/department_controller.py
CHANGED
|
@@ -259,7 +259,7 @@ def add_member(department_id):
|
|
| 259 |
logger.error(f"Error adding member: {str(e)}")
|
| 260 |
return jsonify({'message': f'Error adding member: {str(e)}'}), 500
|
| 261 |
|
| 262 |
-
def get_department_members(department_id):
|
| 263 |
"""Get all members of a department"""
|
| 264 |
department = Department.find_by_id(department_id)
|
| 265 |
if not department:
|
|
|
|
| 259 |
logger.error(f"Error adding member: {str(e)}")
|
| 260 |
return jsonify({'message': f'Error adding member: {str(e)}'}), 500
|
| 261 |
|
| 262 |
+
def get_department_members(department_id, current_user=None):
|
| 263 |
"""Get all members of a department"""
|
| 264 |
department = Department.find_by_id(department_id)
|
| 265 |
if not department:
|
fix/auth_utils_fix.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import jwt
|
| 3 |
+
from datetime import datetime, timedelta
|
| 4 |
+
from functools import wraps
|
| 5 |
+
from flask import request, jsonify
|
| 6 |
+
from models.user import User
|
| 7 |
+
|
| 8 |
+
# Secret key for JWT
|
| 9 |
+
SECRET_KEY = os.environ.get('JWT_SECRET')
|
| 10 |
+
|
| 11 |
+
def generate_token(user_id, permissions, expiration_hours=24*30): # 30-day token by default
|
| 12 |
+
"""Generate JWT token for authentication"""
|
| 13 |
+
payload = {
|
| 14 |
+
'user_id': str(user_id),
|
| 15 |
+
'permissions': permissions,
|
| 16 |
+
'exp': datetime.utcnow() + timedelta(hours=expiration_hours)
|
| 17 |
+
}
|
| 18 |
+
return jwt.encode(payload, SECRET_KEY, algorithm='HS256')
|
| 19 |
+
|
| 20 |
+
def decode_token(token):
|
| 21 |
+
"""Decode JWT token and return payload"""
|
| 22 |
+
try:
|
| 23 |
+
return jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
|
| 24 |
+
except:
|
| 25 |
+
return None
|
| 26 |
+
|
| 27 |
+
def token_required(f):
|
| 28 |
+
"""
|
| 29 |
+
Fixed decorator for routes that require a valid token.
|
| 30 |
+
This version passes current_user as a kwarg instead of an arg,
|
| 31 |
+
avoiding parameter conflicts with URL path parameters.
|
| 32 |
+
"""
|
| 33 |
+
@wraps(f)
|
| 34 |
+
def decorated(*args, **kwargs):
|
| 35 |
+
token = None
|
| 36 |
+
auth_header = request.headers.get('Authorization')
|
| 37 |
+
|
| 38 |
+
if auth_header:
|
| 39 |
+
if auth_header.startswith('Bearer '):
|
| 40 |
+
token = auth_header.split(" ")[1]
|
| 41 |
+
|
| 42 |
+
if not token:
|
| 43 |
+
return jsonify({'message': 'Token is missing'}), 401
|
| 44 |
+
|
| 45 |
+
data = decode_token(token)
|
| 46 |
+
if not data:
|
| 47 |
+
return jsonify({'message': 'Token is invalid or expired'}), 401
|
| 48 |
+
|
| 49 |
+
current_user = User.find_by_id(data['user_id'])
|
| 50 |
+
if not current_user:
|
| 51 |
+
return jsonify({'message': 'User not found'}), 401
|
| 52 |
+
|
| 53 |
+
# Pass current_user as a keyword argument instead of positional argument
|
| 54 |
+
kwargs['current_user'] = current_user
|
| 55 |
+
return f(*args, **kwargs)
|
| 56 |
+
|
| 57 |
+
return decorated
|
| 58 |
+
|
| 59 |
+
def admin_required(f):
|
| 60 |
+
"""
|
| 61 |
+
Fixed decorator for routes that require admin permissions.
|
| 62 |
+
This version passes current_user as a kwarg instead of an arg,
|
| 63 |
+
avoiding parameter conflicts with URL path parameters.
|
| 64 |
+
"""
|
| 65 |
+
@wraps(f)
|
| 66 |
+
def decorated(*args, **kwargs):
|
| 67 |
+
token = None
|
| 68 |
+
auth_header = request.headers.get('Authorization')
|
| 69 |
+
|
| 70 |
+
if auth_header:
|
| 71 |
+
if auth_header.startswith('Bearer '):
|
| 72 |
+
token = auth_header.split(" ")[1]
|
| 73 |
+
|
| 74 |
+
if not token:
|
| 75 |
+
return jsonify({'message': 'Token is missing'}), 401
|
| 76 |
+
|
| 77 |
+
data = decode_token(token)
|
| 78 |
+
if not data:
|
| 79 |
+
return jsonify({'message': 'Token is invalid or expired'}), 401
|
| 80 |
+
|
| 81 |
+
if data['permissions'] != 'Admin':
|
| 82 |
+
return jsonify({'message': 'Admin permissions required'}), 403
|
| 83 |
+
|
| 84 |
+
current_user = User.find_by_id(data['user_id'])
|
| 85 |
+
if not current_user:
|
| 86 |
+
return jsonify({'message': 'User not found'}), 401
|
| 87 |
+
|
| 88 |
+
# Pass current_user as a keyword argument instead of positional argument
|
| 89 |
+
kwargs['current_user'] = current_user
|
| 90 |
+
return f(*args, **kwargs)
|
| 91 |
+
|
| 92 |
+
return decorated
|
fix/department_routes_fix.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Blueprint
|
| 2 |
+
from controllers.department_controller import (
|
| 3 |
+
create_department, get_department, update_department,
|
| 4 |
+
delete_department, get_all_departments, add_members_csv,
|
| 5 |
+
add_member, get_department_members, remove_member, update_member_permissions
|
| 6 |
+
)
|
| 7 |
+
from utils.auth import token_required, admin_required
|
| 8 |
+
|
| 9 |
+
# Create blueprint
|
| 10 |
+
department_bp = Blueprint('departments', __name__)
|
| 11 |
+
|
| 12 |
+
# Public route for creating a new department with first admin
|
| 13 |
+
department_bp.route('', methods=['POST'])(create_department)
|
| 14 |
+
|
| 15 |
+
# Routes that require authentication
|
| 16 |
+
department_bp.route('/', methods=['GET'])(token_required(get_all_departments))
|
| 17 |
+
department_bp.route('/<department_id>', methods=['GET'])(token_required(get_department))
|
| 18 |
+
|
| 19 |
+
# Routes that require admin permissions
|
| 20 |
+
department_bp.route('/<department_id>', methods=['PUT'])(admin_required(update_department))
|
| 21 |
+
department_bp.route('/<department_id>', methods=['DELETE'])(admin_required(delete_department))
|
| 22 |
+
|
| 23 |
+
# Member management routes (admin only)
|
| 24 |
+
# FIX: Wrap the function to avoid parameter conflict with department_id
|
| 25 |
+
@department_bp.route('/<department_id>/members', methods=['GET'])
|
| 26 |
+
@token_required
|
| 27 |
+
def get_members_route(current_user, department_id):
|
| 28 |
+
"""Route wrapper to avoid parameter conflict"""
|
| 29 |
+
return get_department_members(department_id)
|
| 30 |
+
|
| 31 |
+
department_bp.route('/<department_id>/members', methods=['POST'])(admin_required(add_member))
|
| 32 |
+
department_bp.route('/<department_id>/members/csv', methods=['POST'])(admin_required(add_members_csv))
|
| 33 |
+
department_bp.route('/<department_id>/members/<user_id>', methods=['DELETE'])(admin_required(remove_member))
|
| 34 |
+
department_bp.route('/<department_id>/members/<user_id>/permissions', methods=['PUT'])(admin_required(update_member_permissions))
|
routes/department_routes.py
CHANGED
|
@@ -21,6 +21,8 @@ department_bp.route('/<department_id>', methods=['PUT'])(admin_required(update_d
|
|
| 21 |
department_bp.route('/<department_id>', methods=['DELETE'])(admin_required(delete_department))
|
| 22 |
|
| 23 |
# Member management routes (admin only)
|
|
|
|
|
|
|
| 24 |
department_bp.route('/<department_id>/members', methods=['GET'])(token_required(get_department_members))
|
| 25 |
department_bp.route('/<department_id>/members', methods=['POST'])(admin_required(add_member))
|
| 26 |
department_bp.route('/<department_id>/members/csv', methods=['POST'])(admin_required(add_members_csv))
|
|
|
|
| 21 |
department_bp.route('/<department_id>', methods=['DELETE'])(admin_required(delete_department))
|
| 22 |
|
| 23 |
# Member management routes (admin only)
|
| 24 |
+
# This now works because we've fixed the token_required decorator to pass current_user as a kwarg
|
| 25 |
+
# instead of as a positional argument, avoiding the parameter conflict with department_id
|
| 26 |
department_bp.route('/<department_id>/members', methods=['GET'])(token_required(get_department_members))
|
| 27 |
department_bp.route('/<department_id>/members', methods=['POST'])(admin_required(add_member))
|
| 28 |
department_bp.route('/<department_id>/members/csv', methods=['POST'])(admin_required(add_members_csv))
|
utils/auth.py
CHANGED
|
@@ -48,7 +48,8 @@ def token_required(f):
|
|
| 48 |
if not current_user:
|
| 49 |
return jsonify({'message': 'User not found'}), 401
|
| 50 |
|
| 51 |
-
|
|
|
|
| 52 |
|
| 53 |
return decorated
|
| 54 |
|
|
@@ -77,6 +78,7 @@ def admin_required(f):
|
|
| 77 |
if not current_user:
|
| 78 |
return jsonify({'message': 'User not found'}), 401
|
| 79 |
|
| 80 |
-
|
|
|
|
| 81 |
|
| 82 |
return decorated
|
|
|
|
| 48 |
if not current_user:
|
| 49 |
return jsonify({'message': 'User not found'}), 401
|
| 50 |
|
| 51 |
+
kwargs['current_user'] = current_user
|
| 52 |
+
return f(*args, **kwargs)
|
| 53 |
|
| 54 |
return decorated
|
| 55 |
|
|
|
|
| 78 |
if not current_user:
|
| 79 |
return jsonify({'message': 'User not found'}), 401
|
| 80 |
|
| 81 |
+
kwargs['current_user'] = current_user
|
| 82 |
+
return f(*args, **kwargs)
|
| 83 |
|
| 84 |
return decorated
|