anoderb commited on
Commit
de3e22b
·
1 Parent(s): 61839a2

fix: auto-discover MySQL users table column names for login authentication

Browse files
Files changed (1) hide show
  1. 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
- # Note: Laravel default users table contains id, name, email, password, role, etc.
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
- query = text("SELECT id, name, email, password, role FROM users WHERE email = :email LIMIT 1")
320
- res = conn.execute(query, {"email": email}).fetchone()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  except Exception as e:
322
- # Fallback in case columns are slightly different, e.g., 'nama' instead of 'name'
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