Spaces:
Sleeping
Sleeping
anoderb commited on
Commit ·
de3e22b
1
Parent(s): 61839a2
fix: auto-discover MySQL users table column names for login authentication
Browse files- backend/app/database.py +49 -15
backend/app/database.py
CHANGED
|
@@ -308,23 +308,57 @@ def verify_user_mysql(email: str, password_input: str) -> dict:
|
|
| 308 |
"""
|
| 309 |
Verify user credential using MySQL 'users' table.
|
| 310 |
Checks email, bcrypt password hash (Laravel format), and validates super_admin/admin role.
|
|
|
|
| 311 |
Returns user dict on success, raises Exception otherwise.
|
| 312 |
"""
|
| 313 |
with engine.connect() as conn:
|
| 314 |
-
#
|
| 315 |
-
# Let's select id, name, email, password, and role.
|
| 316 |
-
# Sometimes Laravel uses 'name' instead of 'nama', let's handle both or fallbacks.
|
| 317 |
-
# Let's inspect the users table columns by running a query or write it robustly.
|
| 318 |
try:
|
| 319 |
-
|
| 320 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 321 |
except Exception as e:
|
| 322 |
-
|
| 323 |
-
try:
|
| 324 |
-
query = text("SELECT id, nama as name, email, password, role FROM users WHERE email = :email LIMIT 1")
|
| 325 |
-
res = conn.execute(query, {"email": email}).fetchone()
|
| 326 |
-
except Exception as e2:
|
| 327 |
-
raise Exception(f"Database error querying users table: {str(e2)}")
|
| 328 |
|
| 329 |
if not res:
|
| 330 |
raise Exception("Email tidak terdaftar")
|
|
@@ -334,7 +368,7 @@ def verify_user_mysql(email: str, password_input: str) -> dict:
|
|
| 334 |
if not hashed_password:
|
| 335 |
raise Exception("Password hash tidak ditemukan di database")
|
| 336 |
|
| 337 |
-
# Verify password using bcrypt
|
| 338 |
try:
|
| 339 |
# Laravel uses $2y$ prefix, python bcrypt expects $2b$ or $2a$
|
| 340 |
compat_hash = hashed_password
|
|
@@ -351,8 +385,8 @@ def verify_user_mysql(email: str, password_input: str) -> dict:
|
|
| 351 |
if not is_valid:
|
| 352 |
raise Exception("Password salah")
|
| 353 |
|
| 354 |
-
# Check role - must be admin or super_admin
|
| 355 |
-
role = user_data.get("role", "").lower()
|
| 356 |
if role not in ["admin", "super_admin", "superadmin", "super-admin"]:
|
| 357 |
raise Exception(f"Akses ditolak: Role '{role}' tidak memiliki izin administrator")
|
| 358 |
|
|
|
|
| 308 |
"""
|
| 309 |
Verify user credential using MySQL 'users' table.
|
| 310 |
Checks email, bcrypt password hash (Laravel format), and validates super_admin/admin role.
|
| 311 |
+
Auto-discovers actual column names from INFORMATION_SCHEMA to handle varying schemas.
|
| 312 |
Returns user dict on success, raises Exception otherwise.
|
| 313 |
"""
|
| 314 |
with engine.connect() as conn:
|
| 315 |
+
# Step 1: Discover actual column names in the 'users' table
|
|
|
|
|
|
|
|
|
|
| 316 |
try:
|
| 317 |
+
col_query = text("""
|
| 318 |
+
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
|
| 319 |
+
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users'
|
| 320 |
+
""")
|
| 321 |
+
columns = [row[0].lower() for row in conn.execute(col_query).fetchall()]
|
| 322 |
+
except Exception as e:
|
| 323 |
+
raise Exception(f"Gagal membaca skema tabel users: {str(e)}")
|
| 324 |
+
|
| 325 |
+
if not columns:
|
| 326 |
+
raise Exception("Tabel 'users' tidak ditemukan di database")
|
| 327 |
+
|
| 328 |
+
# Step 2: Map logical fields to actual column names
|
| 329 |
+
# Name column: could be 'name', 'nama', 'full_name', 'username'
|
| 330 |
+
name_col = None
|
| 331 |
+
for candidate in ['name', 'nama', 'full_name', 'username', 'nama_lengkap']:
|
| 332 |
+
if candidate in columns:
|
| 333 |
+
name_col = candidate
|
| 334 |
+
break
|
| 335 |
+
if not name_col:
|
| 336 |
+
name_col = 'email' # fallback to email as display name
|
| 337 |
+
|
| 338 |
+
# Password column: could be 'password', 'kata_sandi', 'sandi', 'passwd'
|
| 339 |
+
password_col = None
|
| 340 |
+
for candidate in ['password', 'kata_sandi', 'sandi', 'passwd', 'pass']:
|
| 341 |
+
if candidate in columns:
|
| 342 |
+
password_col = candidate
|
| 343 |
+
break
|
| 344 |
+
if not password_col:
|
| 345 |
+
raise Exception(f"Kolom password tidak ditemukan di tabel users. Kolom yang ada: {', '.join(columns)}")
|
| 346 |
+
|
| 347 |
+
# Role column: could be 'role', 'roles', 'user_role', 'level', 'tipe'
|
| 348 |
+
role_col = None
|
| 349 |
+
for candidate in ['role', 'roles', 'user_role', 'level', 'tipe', 'type']:
|
| 350 |
+
if candidate in columns:
|
| 351 |
+
role_col = candidate
|
| 352 |
+
break
|
| 353 |
+
if not role_col:
|
| 354 |
+
raise Exception(f"Kolom role tidak ditemukan di tabel users. Kolom yang ada: {', '.join(columns)}")
|
| 355 |
+
|
| 356 |
+
# Step 3: Build and execute the query
|
| 357 |
+
try:
|
| 358 |
+
sql = f"SELECT id, `{name_col}` as name, email, `{password_col}` as password, `{role_col}` as role FROM users WHERE email = :email LIMIT 1"
|
| 359 |
+
res = conn.execute(text(sql), {"email": email}).fetchone()
|
| 360 |
except Exception as e:
|
| 361 |
+
raise Exception(f"Database error querying users table: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 362 |
|
| 363 |
if not res:
|
| 364 |
raise Exception("Email tidak terdaftar")
|
|
|
|
| 368 |
if not hashed_password:
|
| 369 |
raise Exception("Password hash tidak ditemukan di database")
|
| 370 |
|
| 371 |
+
# Step 4: Verify password using bcrypt
|
| 372 |
try:
|
| 373 |
# Laravel uses $2y$ prefix, python bcrypt expects $2b$ or $2a$
|
| 374 |
compat_hash = hashed_password
|
|
|
|
| 385 |
if not is_valid:
|
| 386 |
raise Exception("Password salah")
|
| 387 |
|
| 388 |
+
# Step 5: Check role - must be admin or super_admin
|
| 389 |
+
role = str(user_data.get("role", "")).lower().strip()
|
| 390 |
if role not in ["admin", "super_admin", "superadmin", "super-admin"]:
|
| 391 |
raise Exception(f"Akses ditolak: Role '{role}' tidak memiliki izin administrator")
|
| 392 |
|