Sql_Injection/backend/admin_dashborad.py
2025-11-25 14:30:38 +05:30

332 lines
11 KiB
Python

# dashboard.py
import base64
import re
from functools import wraps
from flask import Blueprint, request, jsonify, current_app
from werkzeug.security import check_password_hash
from admin_login import get_db # <-- corrected import (was admin_login)
dashboard_bp = Blueprint("dashboard", __name__)
# -------------------------
# Helper validations
# -------------------------
def validate_product_name(name):
if not name:
return False, "product_name is required"
if len(name) > 255:
return False, "product_name too long"
if not re.match(r"^[A-Za-z0-9\s\-']+$", name):
return False, "product_name contains invalid characters"
return True, None
def validate_quantity(quantity):
try:
qty = int(quantity)
if qty < 1:
return False, "quantity must be at least 1"
return True, None
except (ValueError, TypeError):
return False, "quantity must be a valid integer"
def validate_price(price):
try:
p = float(price)
if p <= 0:
return False, "price must be greater than 0"
return True, None
except (ValueError, TypeError):
return False, "price must be a valid number"
def validate_order_id(order_id):
try:
oid = int(order_id)
if oid < 1:
return False, "order_id must be a positive integer"
return True, None
except (ValueError, TypeError):
return False, "order_id must be a valid integer"
# -------------------------
# Basic auth helper & decorator
# -------------------------
def _auth_from_basic_header(auth_header):
if not auth_header:
return None, None
parts = auth_header.split(None, 1)
if len(parts) != 2:
return None, None
scheme, creds = parts
if scheme.lower() != "basic":
return None, None
try:
decoded = base64.b64decode(creds).decode("utf-8")
except Exception:
return None, None
if ":" not in decoded:
return None, None
username, password = decoded.split(":", 1)
return username, password
def basic_auth_required(f):
@wraps(f)
def wrapper(*args, **kwargs):
auth_header = request.headers.get("Authorization", "")
username, password = _auth_from_basic_header(auth_header)
# fallback: JSON body credentials
if not username:
try:
body = request.get_json(silent=True) or {}
username = (body.get("username") or "").strip()
password = (body.get("password") or "")
except Exception:
username = None
password = None
if not username or not password:
return jsonify({"error": "authentication required (Basic auth header or JSON username/password)"}), 401
cnx = None
cur = None
try:
cnx = get_db()
cur = cnx.cursor(dictionary=True)
cur.execute("SELECT id, username, password FROM admins_cred WHERE username = %s", (username,))
admin = cur.fetchone()
if not admin or not check_password_hash(admin["password"], password):
return jsonify({"error": "invalid credentials"}), 401
request.admin_id = int(admin["id"])
request.admin_username = admin["username"]
return f(*args, **kwargs)
except Exception:
current_app.logger.exception("auth verify error")
return jsonify({"error": "internal server error"}), 500
finally:
try:
if cur: cur.close()
except: pass
try:
if cnx: cnx.close()
except: pass
return wrapper
# -------------------------
# Create order item
# -------------------------
@dashboard_bp.route("/order-item", methods=["POST"])
@basic_auth_required
def create_order_item():
admin_id = getattr(request, "admin_id", None)
if not request.is_json:
return jsonify({"error": "expected JSON body"}), 400
body = request.get_json()
order_id = body.get("order_id")
product_name = (body.get("product_name") or "").strip()
quantity = body.get("quantity", 1)
price = body.get("price")
ok, err = validate_order_id(order_id)
if not ok:
return jsonify({"error": err}), 400
ok, err = validate_product_name(product_name)
if not ok:
return jsonify({"error": err}), 400
ok, err = validate_quantity(quantity)
if not ok:
return jsonify({"error": err}), 400
ok, err = validate_price(price)
if not ok:
return jsonify({"error": err}), 400
order_id_int = int(order_id)
qty_int = int(quantity)
price_f = float(price)
cnx = get_db()
cur = cnx.cursor(dictionary=True)
try:
cur.execute(
"SELECT id FROM order_items WHERE product_name = %s AND order_id = %s AND admin_id = %s",
(product_name, order_id_int, admin_id)
)
existing = cur.fetchone()
if existing:
return jsonify({"error": "order item already exists"}), 400
cur2 = cnx.cursor()
try:
cur2.execute(
"INSERT INTO order_items (admin_id, order_id, product_name, quantity, price, status, created_at) "
"VALUES (%s, %s, %s, %s, %s, %s, NOW())",
(admin_id, order_id_int, product_name, qty_int, price_f, "pending")
)
cnx.commit()
new_id = cur2.lastrowid
finally:
try: cur2.close()
except: pass
return jsonify({
"message": "order item created",
"id": new_id,
"admin_id": admin_id,
"order_id": order_id_int,
"product_name": product_name,
"quantity": qty_int,
"price": price_f,
"status": "pending"
}), 201
except Exception as e:
cnx.rollback()
current_app.logger.exception("create_order_item error")
return jsonify({"error": str(e)}), 500
finally:
try: cur.close()
except: pass
try: cnx.close()
except: pass
# -------------------------
# List / Get order item(s)
# -------------------------
@dashboard_bp.route("/order-item", methods=["GET"])
@basic_auth_required
def list_or_get_order_item():
admin_id = getattr(request, "admin_id", None)
id_param = request.args.get("id", "").strip()
order_id_param = request.args.get("order_id", "").strip()
cnx = get_db()
cur = cnx.cursor(dictionary=True)
try:
if id_param == "" and order_id_param == "":
cur.execute(
"SELECT id, admin_id, order_id, product_name, quantity, price, status, created_at "
"FROM order_items WHERE admin_id = %s",
(admin_id,)
)
rows = cur.fetchall()
return jsonify({"rows": rows}), 200
elif order_id_param != "":
try:
order_id_int = int(order_id_param)
except ValueError:
return jsonify({"error": "order_id must be integer"}), 400
cur.execute(
"SELECT id, admin_id, order_id, product_name, quantity, price, status, created_at "
"FROM order_items WHERE order_id = %s AND admin_id = %s",
(order_id_int, admin_id)
)
rows = cur.fetchall()
return jsonify({"rows": rows}), 200
else:
try:
id_int = int(id_param)
except ValueError:
return jsonify({"error": "id must be integer"}), 400
cur.execute(
"SELECT id, admin_id, order_id, product_name, quantity, price, status, created_at "
"FROM order_items WHERE id = %s AND admin_id = %s",
(id_int, admin_id)
)
row = cur.fetchone()
if not row:
return jsonify({"error": "order item not found"}), 404
return jsonify({"row": row}), 200
except Exception as e:
current_app.logger.exception("list_or_get_order_item error")
return jsonify({"error": str(e)}), 500
finally:
try: cur.close()
except: pass
try: cnx.close()
except: pass
# -------------------------
# Delete order item
# -------------------------
@dashboard_bp.route("/order-item/<int:id>", methods=["DELETE"])
@basic_auth_required
def delete_order_item(id):
admin_id = getattr(request, "admin_id", None)
try:
id_int = int(id)
except (ValueError, TypeError):
return jsonify({"error": "id must be integer"}), 400
cnx = get_db()
cur = cnx.cursor(dictionary=True)
try:
cur.execute("SELECT admin_id FROM order_items WHERE id = %s", (id_int,))
row = cur.fetchone()
if not row:
return jsonify({"error": "order item not found"}), 404
if int(row["admin_id"]) != int(admin_id):
return jsonify({"error": "not authorized to delete this order item"}), 403
cur2 = cnx.cursor()
try:
cur2.execute("DELETE FROM order_items WHERE id = %s", (id_int,))
cnx.commit()
finally:
try: cur2.close()
except: pass
return jsonify({"message": "order item deleted", "id": id_int}), 200
except Exception:
cnx.rollback()
current_app.logger.exception("Error deleting order_item id=%s", id_int)
return jsonify({"error": "internal server error"}), 500
finally:
try: cur.close()
except: pass
try: cnx.close()
except: pass
# -------------------------
# Unsafe demo (intentional SQL injection vulnerability)
# -------------------------
@dashboard_bp.route("/order-item/unsafe", methods=["GET"])
@basic_auth_required
def get_order_item_unsafe():
"""
UNSAFE VERSION - DO NOT USE IN PRODUCTION
- No validation
- Uses raw f-string SQL (SQL Injection vulnerability demo)
"""
id_param = request.args.get("id", "").strip()
if id_param == "":
return jsonify({'error': 'missing id parameter'}), 400
# Completely unsafe SQL (demo only - VULNERABLE TO SQL INJECTION)
query = f"SELECT id, admin_id, order_id, product_name, quantity, price, total, status, created_at FROM order_items WHERE id = {id_param}"
cnx = get_db()
cur = cnx.cursor(dictionary=True)
try:
cur.execute(query) # SQL Injection works here
rows = cur.fetchall()
print(f"Executed unsafe query: {rows}")
except Exception as e:
return jsonify({'error': str(e), 'query': query}), 400
finally:
cur.close()
cnx.close()
return jsonify({
"executed_sql": query,
"rows": rows
}), 200