Skip to content

Enhance Student API: Fix Security Issues, Add Input Validation & Error Handling #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 74 additions & 62 deletions flask_postgresql_app/app.py
Original file line number Diff line number Diff line change
@@ -1,77 +1,89 @@
from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
from dotenv import load_dotenv
from flask import Flask, request, jsonify, abort
from pymongo import MongoClient
from flask_cors import CORS
from bson.objectid import ObjectId
from bson.errors import InvalidId
import os
from werkzeug.exceptions import HTTPException

# Load environment variables from .env file
load_dotenv()
app = Flask(_name_)

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('DATABASE_URL')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# Config from environment variables

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new code uses MONGO_URI and DB_NAME env variables. Please update the README to document these and provide a sample .env file.

MONGO_URI = os.getenv('MONGO_URI', 'mongodb://localhost:27017/')
DB_NAME = os.getenv('DB_NAME', 'studentsdb')

db = SQLAlchemy(app)
# Secure CORS (adjust in production)
CORS(app, resources={r"/api/*": {"origins": ["http://localhost:3000"]}})

class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), nullable=False)
client = MongoClient(MONGO_URI)
db = client[DB_NAME]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have used name and main instead of name and main in:

app = Flask(_name_)
if _name_ == '_main_':

This will cause the app not to run as intended. Please fix to name and main.

students_collection = db['students']
students_collection.create_index('student_id', unique=True) # Prevent duplicates

def __init__(self, name):
self.name = name
@app.errorhandler(HTTPException)
def handle_error(e):
return jsonify(error=str(e.description)), e.code

# Create the database tables
@app.before_request
def create_tables():
db.create_all()
# Input validation helper
def validate_student_data(data, is_update=False):
required_fields = ['student_id', 'name', 'email']
for field in required_fields:
if not is_update and field not in data:
abort(400, description=f"Missing required field: {field}")
if field in data and not isinstance(data[field], str):
abort(400, description=f"{field} must be a string")

# Home route
@app.route('/', methods=['GET'])
def home():
return jsonify({"message": "Welcome to the User Management API!"}), 200
@app.route('/students', methods=['GET'])
def get_students():
students = list(students_collection.find({}, {'_id': 0}))
return jsonify(students)

# GET all users
@app.route('/users', methods=['GET'])
def get_users():
users = User.query.all()
return jsonify([{'id': user.id, 'name': user.name} for user in users])
@app.route('/students/<student_id>', methods=['GET'])
def get_student(student_id):
student = students_collection.find_one({'student_id': student_id}, {'_id': 0})
if not student:
abort(404, description="Student not found")
return jsonify(student)

# POST a new user
@app.route('/users', methods=['POST'])
def add_user():
name = request.json.get('name')
if not name:
return jsonify({"error": "Name is required."}), 400
user = User(name=name)
db.session.add(user)
db.session.commit()
return jsonify({"message": f"User {name} added.", "id": user.id}), 201

# PUT to update a user
@app.route('/users/<int:id>', methods=['PUT'])
def update_user(id):
user = User.query.get(id)
if user is None:
return jsonify({"error": "User not found."}), 404
@app.route('/students', methods=['POST'])
def create_student():
data = request.get_json()
if not data:
abort(400, description="No data provided")

validate_student_data(data)

name = request.json.get('name')
if name:
user.name = name
db.session.commit()
return jsonify({"message": f"User {id} updated."})
try:
result = students_collection.insert_one(data)
except Exception as e:
abort(400, description=str(e))

return jsonify({"error": "Name is required."}), 400
return jsonify({'message': 'Student created'}), 201

# DELETE a user
@app.route('/users/<int:id>', methods=['DELETE'])
def delete_user(id):
user = User.query.get(id)
if user is None:
return jsonify({"error": "User not found."}), 404
@app.route('/students/<student_id>', methods=['PUT'])
def update_student(student_id):
data = request.get_json()
if not data:
abort(400, description="No data provided")

validate_student_data(data, is_update=True)

db.session.delete(user)
db.session.commit()
return jsonify({"message": f"User {id} deleted."})
result = students_collection.update_one(
{'student_id': student_id},
{'$set': data}
)

if result.matched_count == 0:
abort(404, description="Student not found")

return jsonify({'message': 'Student updated'})

@app.route('/students/<student_id>', methods=['DELETE'])
def delete_student(student_id):
result = students_collection.delete_one({'student_id': student_id})
if result.deleted_count == 0:
abort(404, description="Student not found")
return jsonify({'message': 'Student deleted'})

if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
if _name_ == '_main_':
app.run(host='0.0.0.0', port=6000)