mashrur950 commited on
Commit
79f305b
·
1 Parent(s): f474e49

Enhance API key handling and user authentication

Browse files

- Improved API key extraction from requests using context variables.
- Updated user authentication to validate API keys and handle development mode securely.
- Added cleanup scripts for correcting bad user_id records in the database.
- Implemented verification for user_id fixes to ensure data integrity.

Files changed (6) hide show
  1. chat/tools.py +156 -52
  2. cleanup_bad_user_ids.py +124 -0
  3. database/api_keys.py +5 -1
  4. fix_user_id.py +53 -0
  5. server.py +89 -49
  6. verify_fix.py +51 -0
chat/tools.py CHANGED
@@ -1340,10 +1340,14 @@ def handle_create_order(tool_input: dict, user_id: str = None) -> dict:
1340
  Returns:
1341
  Order creation result
1342
  """
1343
- # Authentication check - allow dev mode
1344
  if not user_id:
1345
  # Development mode - use default user
1346
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
1347
  user_id = "dev-user"
1348
  else:
1349
  return {
@@ -1476,9 +1480,13 @@ def handle_create_driver(tool_input: dict, user_id: str = None) -> dict:
1476
  Returns:
1477
  Driver creation result
1478
  """
1479
- # Authentication check - allow dev mode
1480
  if not user_id:
1481
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
1482
  user_id = "dev-user"
1483
  else:
1484
  return {
@@ -1603,9 +1611,13 @@ def handle_update_order(tool_input: dict, user_id: str = None) -> dict:
1603
  Returns:
1604
  Update result
1605
  """
1606
- # Authentication check - allow dev mode
1607
  if not user_id:
1608
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
1609
  user_id = "dev-user"
1610
  else:
1611
  return {
@@ -1829,9 +1841,13 @@ def handle_delete_all_orders(tool_input: dict, user_id: str = None) -> dict:
1829
  Returns:
1830
  Deletion result with count
1831
  """
1832
- # Authentication check - allow dev mode
1833
  if not user_id:
1834
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
1835
  user_id = "dev-user"
1836
  else:
1837
  return {
@@ -1914,9 +1930,13 @@ def handle_delete_order(tool_input: dict, user_id: str = None) -> dict:
1914
  Returns:
1915
  Deletion result
1916
  """
1917
- # Authentication check - allow dev mode
1918
  if not user_id:
1919
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
1920
  user_id = "dev-user"
1921
  else:
1922
  return {
@@ -2017,9 +2037,13 @@ def handle_update_driver(tool_input: dict, user_id: str = None) -> dict:
2017
  Returns:
2018
  Update result
2019
  """
2020
- # Authentication check - allow dev mode
2021
  if not user_id:
2022
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
2023
  user_id = "dev-user"
2024
  else:
2025
  return {
@@ -2166,9 +2190,13 @@ def handle_delete_all_drivers(tool_input: dict, user_id: str = None) -> dict:
2166
  Returns:
2167
  Deletion result with count
2168
  """
2169
- # Authentication check - allow dev mode
2170
  if not user_id:
2171
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
2172
  user_id = "dev-user"
2173
  else:
2174
  return {
@@ -2256,9 +2284,13 @@ def handle_delete_driver(tool_input: dict, user_id: str = None) -> dict:
2256
  Returns:
2257
  Deletion result
2258
  """
2259
- # Authentication check - allow dev mode
2260
  if not user_id:
2261
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
2262
  user_id = "dev-user"
2263
  else:
2264
  return {
@@ -2384,9 +2416,13 @@ def handle_count_orders(tool_input: dict, user_id: str = None) -> dict:
2384
  Returns:
2385
  Order count result with breakdown (only user's orders)
2386
  """
2387
- # Authentication check - allow dev mode
2388
  if not user_id:
2389
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
2390
  user_id = "dev-user"
2391
  else:
2392
  return {
@@ -2493,10 +2529,14 @@ def handle_fetch_orders(tool_input: dict, user_id: str = None) -> dict:
2493
  Returns:
2494
  List of orders matching criteria (only user's orders)
2495
  """
2496
- # Authentication check - allow dev mode
2497
  if not user_id:
2498
  # Development mode - use default user
2499
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
2500
  user_id = "dev-user"
2501
  else:
2502
  return {
@@ -2647,9 +2687,13 @@ def handle_get_order_details(tool_input: dict, user_id: str = None) -> dict:
2647
  Returns:
2648
  Complete order details (only if owned by user)
2649
  """
2650
- # Authentication check - allow dev mode
2651
  if not user_id:
2652
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
2653
  user_id = "dev-user"
2654
  else:
2655
  return {
@@ -2768,9 +2812,13 @@ def handle_search_orders(tool_input: dict, user_id: str = None) -> dict:
2768
  Returns:
2769
  List of matching orders
2770
  """
2771
- # Authentication check - allow dev mode
2772
  if not user_id:
2773
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
2774
  user_id = "dev-user"
2775
  else:
2776
  return {
@@ -2857,9 +2905,13 @@ def handle_get_incomplete_orders(tool_input: dict, user_id: str = None) -> dict:
2857
  Returns:
2858
  List of incomplete orders (pending, assigned, in_transit)
2859
  """
2860
- # Authentication check - allow dev mode
2861
  if not user_id:
2862
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
2863
  user_id = "dev-user"
2864
  else:
2865
  return {
@@ -2938,9 +2990,13 @@ def handle_count_drivers(tool_input: dict, user_id: str = None) -> dict:
2938
  Returns:
2939
  Driver count result with breakdown
2940
  """
2941
- # Authentication check - allow dev mode
2942
  if not user_id:
2943
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
2944
  user_id = "dev-user"
2945
  else:
2946
  return {
@@ -3023,9 +3079,13 @@ def handle_fetch_drivers(tool_input: dict, user_id: str = None) -> dict:
3023
  Returns:
3024
  List of drivers matching criteria (only user's drivers)
3025
  """
3026
- # Authentication check - allow dev mode
3027
  if not user_id:
3028
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
3029
  user_id = "dev-user"
3030
  else:
3031
  return {
@@ -3148,9 +3208,13 @@ def handle_get_driver_details(tool_input: dict, user_id: str = None) -> dict:
3148
  Returns:
3149
  Complete driver details
3150
  """
3151
- # Authentication check - allow dev mode
3152
  if not user_id:
3153
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
3154
  user_id = "dev-user"
3155
  else:
3156
  return {
@@ -3267,9 +3331,13 @@ def handle_search_drivers(tool_input: dict, user_id: str = None) -> dict:
3267
  Returns:
3268
  List of matching drivers
3269
  """
3270
- # Authentication check - allow dev mode
3271
  if not user_id:
3272
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
3273
  user_id = "dev-user"
3274
  else:
3275
  return {
@@ -3357,9 +3425,13 @@ def handle_get_available_drivers(tool_input: dict, user_id: str = None) -> dict:
3357
  Returns:
3358
  List of available drivers (active or offline)
3359
  """
3360
- # Authentication check - allow dev mode
3361
  if not user_id:
3362
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
3363
  user_id = "dev-user"
3364
  else:
3365
  return {
@@ -3461,9 +3533,13 @@ def handle_create_assignment(tool_input: dict, user_id: str = None) -> dict:
3461
  Returns:
3462
  Assignment creation result with route data
3463
  """
3464
- # Authentication check - allow dev mode
3465
  if not user_id:
3466
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
3467
  user_id = "dev-user"
3468
  else:
3469
  return {
@@ -3737,9 +3813,13 @@ def handle_auto_assign_order(tool_input: dict, user_id: str = None) -> dict:
3737
  Returns:
3738
  Assignment details with selected driver info and distance
3739
  """
3740
- # Authentication check - allow dev mode
3741
  if not user_id:
3742
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
3743
  user_id = "dev-user"
3744
  else:
3745
  return {
@@ -3955,9 +4035,13 @@ def handle_intelligent_assign_order(tool_input: dict, user_id: str = None) -> di
3955
  Returns:
3956
  Assignment details with AI reasoning and selected driver info
3957
  """
3958
- # Authentication check - allow dev mode
3959
  if not user_id:
3960
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
3961
  user_id = "dev-user"
3962
  else:
3963
  return {
@@ -4285,9 +4369,13 @@ def handle_get_assignment_details(tool_input: dict, user_id: str = None) -> dict
4285
  Returns:
4286
  Assignment details or list of assignments
4287
  """
4288
- # Authentication check - allow dev mode
4289
  if not user_id:
4290
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
4291
  user_id = "dev-user"
4292
  else:
4293
  return {
@@ -4442,9 +4530,13 @@ def handle_update_assignment(tool_input: dict, user_id: str = None) -> dict:
4442
  Returns:
4443
  Update result
4444
  """
4445
- # Authentication check - allow dev mode
4446
  if not user_id:
4447
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
4448
  user_id = "dev-user"
4449
  else:
4450
  return {
@@ -4638,9 +4730,13 @@ def handle_unassign_order(tool_input: dict, user_id: str = None) -> dict:
4638
  Returns:
4639
  Unassignment result
4640
  """
4641
- # Authentication check - allow dev mode
4642
  if not user_id:
4643
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
4644
  user_id = "dev-user"
4645
  else:
4646
  return {
@@ -4781,9 +4877,13 @@ def handle_complete_delivery(tool_input: dict, user_id: str = None) -> dict:
4781
  Returns:
4782
  Completion result
4783
  """
4784
- # Authentication check - allow dev mode
4785
  if not user_id:
4786
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
4787
  user_id = "dev-user"
4788
  else:
4789
  return {
@@ -5016,9 +5116,13 @@ def handle_fail_delivery(tool_input: dict, user_id: str = None) -> dict:
5016
  Returns:
5017
  Failure recording result
5018
  """
5019
- # Authentication check - allow dev mode
5020
  if not user_id:
5021
- if os.getenv("SKIP_AUTH") == "true":
 
 
 
 
5022
  user_id = "dev-user"
5023
  else:
5024
  return {
 
1340
  Returns:
1341
  Order creation result
1342
  """
1343
+ # Authentication check - allow dev mode (only in non-production environments)
1344
  if not user_id:
1345
  # Development mode - use default user
1346
+ # SECURITY: Only allow SKIP_AUTH in development environments
1347
+ env = os.getenv("ENV", "production").lower()
1348
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
1349
+
1350
+ if skip_auth and env != "production":
1351
  user_id = "dev-user"
1352
  else:
1353
  return {
 
1480
  Returns:
1481
  Driver creation result
1482
  """
1483
+ # Authentication check - allow dev mode (only in non-production environments)
1484
  if not user_id:
1485
+ # SECURITY: Only allow SKIP_AUTH in development environments
1486
+ env = os.getenv("ENV", "production").lower()
1487
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
1488
+
1489
+ if skip_auth and env != "production":
1490
  user_id = "dev-user"
1491
  else:
1492
  return {
 
1611
  Returns:
1612
  Update result
1613
  """
1614
+ # Authentication check - allow dev mode (only in non-production environments)
1615
  if not user_id:
1616
+ # SECURITY: Only allow SKIP_AUTH in development environments
1617
+ env = os.getenv("ENV", "production").lower()
1618
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
1619
+
1620
+ if skip_auth and env != "production":
1621
  user_id = "dev-user"
1622
  else:
1623
  return {
 
1841
  Returns:
1842
  Deletion result with count
1843
  """
1844
+ # Authentication check - allow dev mode (only in non-production environments)
1845
  if not user_id:
1846
+ # SECURITY: Only allow SKIP_AUTH in development environments
1847
+ env = os.getenv("ENV", "production").lower()
1848
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
1849
+
1850
+ if skip_auth and env != "production":
1851
  user_id = "dev-user"
1852
  else:
1853
  return {
 
1930
  Returns:
1931
  Deletion result
1932
  """
1933
+ # Authentication check - allow dev mode (only in non-production environments)
1934
  if not user_id:
1935
+ # SECURITY: Only allow SKIP_AUTH in development environments
1936
+ env = os.getenv("ENV", "production").lower()
1937
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
1938
+
1939
+ if skip_auth and env != "production":
1940
  user_id = "dev-user"
1941
  else:
1942
  return {
 
2037
  Returns:
2038
  Update result
2039
  """
2040
+ # Authentication check - allow dev mode (only in non-production environments)
2041
  if not user_id:
2042
+ # SECURITY: Only allow SKIP_AUTH in development environments
2043
+ env = os.getenv("ENV", "production").lower()
2044
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
2045
+
2046
+ if skip_auth and env != "production":
2047
  user_id = "dev-user"
2048
  else:
2049
  return {
 
2190
  Returns:
2191
  Deletion result with count
2192
  """
2193
+ # Authentication check - allow dev mode (only in non-production environments)
2194
  if not user_id:
2195
+ # SECURITY: Only allow SKIP_AUTH in development environments
2196
+ env = os.getenv("ENV", "production").lower()
2197
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
2198
+
2199
+ if skip_auth and env != "production":
2200
  user_id = "dev-user"
2201
  else:
2202
  return {
 
2284
  Returns:
2285
  Deletion result
2286
  """
2287
+ # Authentication check - allow dev mode (only in non-production environments)
2288
  if not user_id:
2289
+ # SECURITY: Only allow SKIP_AUTH in development environments
2290
+ env = os.getenv("ENV", "production").lower()
2291
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
2292
+
2293
+ if skip_auth and env != "production":
2294
  user_id = "dev-user"
2295
  else:
2296
  return {
 
2416
  Returns:
2417
  Order count result with breakdown (only user's orders)
2418
  """
2419
+ # Authentication check - allow dev mode (only in non-production environments)
2420
  if not user_id:
2421
+ # SECURITY: Only allow SKIP_AUTH in development environments
2422
+ env = os.getenv("ENV", "production").lower()
2423
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
2424
+
2425
+ if skip_auth and env != "production":
2426
  user_id = "dev-user"
2427
  else:
2428
  return {
 
2529
  Returns:
2530
  List of orders matching criteria (only user's orders)
2531
  """
2532
+ # Authentication check - allow dev mode (only in non-production environments)
2533
  if not user_id:
2534
  # Development mode - use default user
2535
+ # SECURITY: Only allow SKIP_AUTH in development environments
2536
+ env = os.getenv("ENV", "production").lower()
2537
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
2538
+
2539
+ if skip_auth and env != "production":
2540
  user_id = "dev-user"
2541
  else:
2542
  return {
 
2687
  Returns:
2688
  Complete order details (only if owned by user)
2689
  """
2690
+ # Authentication check - allow dev mode (only in non-production environments)
2691
  if not user_id:
2692
+ # SECURITY: Only allow SKIP_AUTH in development environments
2693
+ env = os.getenv("ENV", "production").lower()
2694
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
2695
+
2696
+ if skip_auth and env != "production":
2697
  user_id = "dev-user"
2698
  else:
2699
  return {
 
2812
  Returns:
2813
  List of matching orders
2814
  """
2815
+ # Authentication check - allow dev mode (only in non-production environments)
2816
  if not user_id:
2817
+ # SECURITY: Only allow SKIP_AUTH in development environments
2818
+ env = os.getenv("ENV", "production").lower()
2819
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
2820
+
2821
+ if skip_auth and env != "production":
2822
  user_id = "dev-user"
2823
  else:
2824
  return {
 
2905
  Returns:
2906
  List of incomplete orders (pending, assigned, in_transit)
2907
  """
2908
+ # Authentication check - allow dev mode (only in non-production environments)
2909
  if not user_id:
2910
+ # SECURITY: Only allow SKIP_AUTH in development environments
2911
+ env = os.getenv("ENV", "production").lower()
2912
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
2913
+
2914
+ if skip_auth and env != "production":
2915
  user_id = "dev-user"
2916
  else:
2917
  return {
 
2990
  Returns:
2991
  Driver count result with breakdown
2992
  """
2993
+ # Authentication check - allow dev mode (only in non-production environments)
2994
  if not user_id:
2995
+ # SECURITY: Only allow SKIP_AUTH in development environments
2996
+ env = os.getenv("ENV", "production").lower()
2997
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
2998
+
2999
+ if skip_auth and env != "production":
3000
  user_id = "dev-user"
3001
  else:
3002
  return {
 
3079
  Returns:
3080
  List of drivers matching criteria (only user's drivers)
3081
  """
3082
+ # Authentication check - allow dev mode (only in non-production environments)
3083
  if not user_id:
3084
+ # SECURITY: Only allow SKIP_AUTH in development environments
3085
+ env = os.getenv("ENV", "production").lower()
3086
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
3087
+
3088
+ if skip_auth and env != "production":
3089
  user_id = "dev-user"
3090
  else:
3091
  return {
 
3208
  Returns:
3209
  Complete driver details
3210
  """
3211
+ # Authentication check - allow dev mode (only in non-production environments)
3212
  if not user_id:
3213
+ # SECURITY: Only allow SKIP_AUTH in development environments
3214
+ env = os.getenv("ENV", "production").lower()
3215
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
3216
+
3217
+ if skip_auth and env != "production":
3218
  user_id = "dev-user"
3219
  else:
3220
  return {
 
3331
  Returns:
3332
  List of matching drivers
3333
  """
3334
+ # Authentication check - allow dev mode (only in non-production environments)
3335
  if not user_id:
3336
+ # SECURITY: Only allow SKIP_AUTH in development environments
3337
+ env = os.getenv("ENV", "production").lower()
3338
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
3339
+
3340
+ if skip_auth and env != "production":
3341
  user_id = "dev-user"
3342
  else:
3343
  return {
 
3425
  Returns:
3426
  List of available drivers (active or offline)
3427
  """
3428
+ # Authentication check - allow dev mode (only in non-production environments)
3429
  if not user_id:
3430
+ # SECURITY: Only allow SKIP_AUTH in development environments
3431
+ env = os.getenv("ENV", "production").lower()
3432
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
3433
+
3434
+ if skip_auth and env != "production":
3435
  user_id = "dev-user"
3436
  else:
3437
  return {
 
3533
  Returns:
3534
  Assignment creation result with route data
3535
  """
3536
+ # Authentication check - allow dev mode (only in non-production environments)
3537
  if not user_id:
3538
+ # SECURITY: Only allow SKIP_AUTH in development environments
3539
+ env = os.getenv("ENV", "production").lower()
3540
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
3541
+
3542
+ if skip_auth and env != "production":
3543
  user_id = "dev-user"
3544
  else:
3545
  return {
 
3813
  Returns:
3814
  Assignment details with selected driver info and distance
3815
  """
3816
+ # Authentication check - allow dev mode (only in non-production environments)
3817
  if not user_id:
3818
+ # SECURITY: Only allow SKIP_AUTH in development environments
3819
+ env = os.getenv("ENV", "production").lower()
3820
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
3821
+
3822
+ if skip_auth and env != "production":
3823
  user_id = "dev-user"
3824
  else:
3825
  return {
 
4035
  Returns:
4036
  Assignment details with AI reasoning and selected driver info
4037
  """
4038
+ # Authentication check - allow dev mode (only in non-production environments)
4039
  if not user_id:
4040
+ # SECURITY: Only allow SKIP_AUTH in development environments
4041
+ env = os.getenv("ENV", "production").lower()
4042
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
4043
+
4044
+ if skip_auth and env != "production":
4045
  user_id = "dev-user"
4046
  else:
4047
  return {
 
4369
  Returns:
4370
  Assignment details or list of assignments
4371
  """
4372
+ # Authentication check - allow dev mode (only in non-production environments)
4373
  if not user_id:
4374
+ # SECURITY: Only allow SKIP_AUTH in development environments
4375
+ env = os.getenv("ENV", "production").lower()
4376
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
4377
+
4378
+ if skip_auth and env != "production":
4379
  user_id = "dev-user"
4380
  else:
4381
  return {
 
4530
  Returns:
4531
  Update result
4532
  """
4533
+ # Authentication check - allow dev mode (only in non-production environments)
4534
  if not user_id:
4535
+ # SECURITY: Only allow SKIP_AUTH in development environments
4536
+ env = os.getenv("ENV", "production").lower()
4537
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
4538
+
4539
+ if skip_auth and env != "production":
4540
  user_id = "dev-user"
4541
  else:
4542
  return {
 
4730
  Returns:
4731
  Unassignment result
4732
  """
4733
+ # Authentication check - allow dev mode (only in non-production environments)
4734
  if not user_id:
4735
+ # SECURITY: Only allow SKIP_AUTH in development environments
4736
+ env = os.getenv("ENV", "production").lower()
4737
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
4738
+
4739
+ if skip_auth and env != "production":
4740
  user_id = "dev-user"
4741
  else:
4742
  return {
 
4877
  Returns:
4878
  Completion result
4879
  """
4880
+ # Authentication check - allow dev mode (only in non-production environments)
4881
  if not user_id:
4882
+ # SECURITY: Only allow SKIP_AUTH in development environments
4883
+ env = os.getenv("ENV", "production").lower()
4884
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
4885
+
4886
+ if skip_auth and env != "production":
4887
  user_id = "dev-user"
4888
  else:
4889
  return {
 
5116
  Returns:
5117
  Failure recording result
5118
  """
5119
+ # Authentication check - allow dev mode (only in non-production environments)
5120
  if not user_id:
5121
+ # SECURITY: Only allow SKIP_AUTH in development environments
5122
+ env = os.getenv("ENV", "production").lower()
5123
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
5124
+
5125
+ if skip_auth and env != "production":
5126
  user_id = "dev-user"
5127
  else:
5128
  return {
cleanup_bad_user_ids.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Cleanup script for records with incorrect user_id='user_id'
3
+ This fixes data created before the verify_api_key() bug was fixed
4
+ """
5
+
6
+ from database.connection import get_db_connection
7
+ from database.api_keys import list_api_keys
8
+ import sys
9
+
10
+ def check_bad_records():
11
+ """Check how many records have user_id='user_id'"""
12
+ conn = get_db_connection()
13
+ cursor = conn.cursor()
14
+
15
+ print("\n=== Checking for records with incorrect user_id='user_id' ===\n")
16
+
17
+ tables = ['orders', 'drivers', 'assignments']
18
+ total_bad = 0
19
+ details = {}
20
+
21
+ for table in tables:
22
+ try:
23
+ cursor.execute(f"SELECT COUNT(*) as count FROM {table} WHERE user_id = %s", ('user_id',))
24
+ result = cursor.fetchone()
25
+ count = result['count'] if result else 0
26
+
27
+ if count > 0:
28
+ print(f" {table}: {count} records with user_id='user_id'")
29
+ total_bad += count
30
+ details[table] = count
31
+ else:
32
+ print(f" {table}: No bad records found")
33
+ details[table] = 0
34
+ except Exception as e:
35
+ print(f" {table}: Error checking - {e}")
36
+ details[table] = 0
37
+
38
+ cursor.close()
39
+ conn.close()
40
+
41
+ print(f"\nTotal bad records: {total_bad}\n")
42
+ return total_bad, details
43
+
44
+ def get_first_api_key_user():
45
+ """Get the first API key user (likely the correct one)"""
46
+ try:
47
+ keys = list_api_keys()
48
+ if keys and len(keys) > 0:
49
+ return keys[0]['user_id']
50
+ except Exception as e:
51
+ print(f"Error fetching API keys: {e}")
52
+ return None
53
+
54
+ def cleanup_bad_records(correct_user_id: str):
55
+ """
56
+ Update records with user_id='user_id' to the correct user_id
57
+
58
+ Args:
59
+ correct_user_id: The actual user_id to assign (e.g., 'user_75d23af433e0')
60
+ """
61
+ conn = get_db_connection()
62
+ cursor = conn.cursor()
63
+
64
+ print(f"\n=== Updating records to user_id='{correct_user_id}' ===\n")
65
+
66
+ tables = ['orders', 'drivers', 'assignments']
67
+ total_updated = 0
68
+
69
+ for table in tables:
70
+ try:
71
+ cursor.execute(
72
+ f"UPDATE {table} SET user_id = %s WHERE user_id = %s",
73
+ (correct_user_id, 'user_id')
74
+ )
75
+ count = cursor.rowcount
76
+
77
+ if count > 0:
78
+ print(f" {table}: Updated {count} records")
79
+ total_updated += count
80
+ else:
81
+ print(f" {table}: No records to update")
82
+ except Exception as e:
83
+ print(f" {table}: Error updating - {e}")
84
+ conn.rollback()
85
+ cursor.close()
86
+ conn.close()
87
+ return 0
88
+
89
+ conn.commit()
90
+ cursor.close()
91
+ conn.close()
92
+
93
+ print(f"\nTotal records updated: {total_updated}\n")
94
+ return total_updated
95
+
96
+ if __name__ == "__main__":
97
+ print("=== FleetMind User ID Cleanup Utility ===")
98
+
99
+ # First, check how many bad records exist
100
+ bad_count, details = check_bad_records()
101
+
102
+ if bad_count == 0:
103
+ print("No cleanup needed - all records have correct user_id values!")
104
+ sys.exit(0)
105
+
106
+ # Get the correct user_id from API keys table
107
+ correct_user_id = get_first_api_key_user()
108
+
109
+ if not correct_user_id:
110
+ print("\nERROR: Could not determine correct user_id from API keys table")
111
+ print("Please run this script with the user_id as argument:")
112
+ print(" python cleanup_bad_user_ids.py <user_id>")
113
+ sys.exit(1)
114
+
115
+ print(f"\nFound API key user: {correct_user_id}")
116
+
117
+ # Auto-cleanup
118
+ print(f"\nProceeding with automatic cleanup...")
119
+ updated = cleanup_bad_records(correct_user_id)
120
+
121
+ if updated > 0:
122
+ print(f"\nSUCCESS: Cleanup complete! Updated {updated} records.")
123
+ else:
124
+ print("\nNo updates were made.")
database/api_keys.py CHANGED
@@ -163,7 +163,11 @@ def verify_api_key(api_key: str) -> Optional[Dict[str, str]]:
163
  if not result:
164
  return None
165
 
166
- user_id, email, name, is_active = result
 
 
 
 
167
 
168
  if not is_active:
169
  return None
 
163
  if not result:
164
  return None
165
 
166
+ # Access RealDictRow fields by key (not tuple unpacking!)
167
+ user_id = result['user_id']
168
+ email = result['email']
169
+ name = result['name']
170
+ is_active = result['is_active']
171
 
172
  if not is_active:
173
  return None
fix_user_id.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Quick fix for the bad user_id record"""
2
+
3
+ from database.connection import get_db_connection
4
+
5
+ def fix_bad_user_id():
6
+ conn = get_db_connection()
7
+ cursor = conn.cursor()
8
+
9
+ # Get the correct user_id from api_keys table
10
+ cursor.execute("SELECT user_id FROM api_keys WHERE is_active = true LIMIT 1")
11
+ result = cursor.fetchone()
12
+
13
+ if not result:
14
+ print("ERROR: No active API key found")
15
+ cursor.close()
16
+ conn.close()
17
+ return
18
+
19
+ correct_user_id = result['user_id']
20
+ print(f"Found active user: {correct_user_id}")
21
+
22
+ # Update orders with bad user_id
23
+ cursor.execute(
24
+ "UPDATE orders SET user_id = %s WHERE user_id = %s",
25
+ (correct_user_id, 'user_id')
26
+ )
27
+ orders_updated = cursor.rowcount
28
+ print(f"Updated {orders_updated} orders")
29
+
30
+ # Update drivers with bad user_id
31
+ cursor.execute(
32
+ "UPDATE drivers SET user_id = %s WHERE user_id = %s",
33
+ (correct_user_id, 'user_id')
34
+ )
35
+ drivers_updated = cursor.rowcount
36
+ print(f"Updated {drivers_updated} drivers")
37
+
38
+ # Update assignments with bad user_id
39
+ cursor.execute(
40
+ "UPDATE assignments SET user_id = %s WHERE user_id = %s",
41
+ (correct_user_id, 'user_id')
42
+ )
43
+ assignments_updated = cursor.rowcount
44
+ print(f"Updated {assignments_updated} assignments")
45
+
46
+ conn.commit()
47
+ cursor.close()
48
+ conn.close()
49
+
50
+ print(f"\nTotal records fixed: {orders_updated + drivers_updated + assignments_updated}")
51
+
52
+ if __name__ == "__main__":
53
+ fix_bad_user_id()
server.py CHANGED
@@ -13,6 +13,7 @@ import logging
13
  from pathlib import Path
14
  from typing import Literal
15
  from datetime import datetime
 
16
 
17
  # Add project root to path
18
  sys.path.insert(0, str(Path(__file__).parent))
@@ -37,6 +38,38 @@ logging.basicConfig(
37
  )
38
  logger = logging.getLogger(__name__)
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  # ============================================================================
41
  # MCP SERVER INITIALIZATION
42
  # ============================================================================
@@ -63,67 +96,50 @@ except Exception as e:
63
 
64
  def get_authenticated_user():
65
  """
66
- Extract and verify user via API Key authentication
67
-
68
- Supports 3 authentication methods (in order):
69
- 1. Request context (from FastMCP request, if available)
70
- 2. Server environment variable FLEETMIND_API_KEY
71
- 3. Development Mode (SKIP_AUTH=true)
72
 
73
  Returns:
74
  User info dict with user_id, email, scopes, name or None if not authenticated
75
  """
76
  try:
77
- api_key = None
78
-
79
- # METHOD 1: Try to get API key from request context (SSE/HTTP)
80
- # This is populated by FastMCP from query params or headers
81
- try:
82
- from fastmcp.server.dependencies import get_request_context
83
- context = get_request_context()
84
- if context and hasattr(context, 'query_params'):
85
- api_key = context.query_params.get('api_key')
86
- if api_key:
87
- logger.debug("API key found in query params")
88
- elif context and hasattr(context, 'headers'):
89
- # Try Authorization header
90
- auth_header = context.headers.get('authorization', '')
91
- if auth_header.startswith('Bearer '):
92
- api_key = auth_header[7:]
93
- logger.debug("API key found in Authorization header")
94
- except (ImportError, RuntimeError, AttributeError) as e:
95
- logger.debug(f"No request context available: {e}")
96
- pass
97
-
98
- # METHOD 2: Try server environment variable (for local development)
99
  if not api_key:
100
  api_key = os.getenv("FLEETMIND_API_KEY")
101
  if api_key:
102
- logger.debug("API key found in server environment")
103
 
104
- # Verify the API key if we found one
105
  if api_key:
106
  from database.api_keys import verify_api_key
107
  user_info = verify_api_key(api_key)
108
  if user_info:
109
- logger.info(f"✅ API Key auth: {user_info['email']} (user_id: {user_info['user_id']})")
110
  return user_info
111
  else:
112
- logger.warning(f"❌ Invalid API key provided")
113
  return None
114
 
115
  # METHOD 3: Development bypass mode (local testing only)
116
- if os.getenv("SKIP_AUTH") == "true":
117
- logger.debug("SKIP_AUTH enabled - using development user")
118
- return {
119
- 'user_id': 'dev-user',
120
- 'email': 'dev@fleetmind.local',
121
- 'scopes': ['admin'],
122
- 'name': 'Development User'
123
- }
 
 
 
 
 
 
 
 
124
 
125
- # No authentication provided
126
- logger.debug("No API key or SKIP_AUTH - authentication required")
127
  return None
128
 
129
  except Exception as e:
@@ -138,7 +154,7 @@ def get_authenticated_user():
138
  def get_orders_resource() -> str:
139
  """
140
  Real-time orders dataset for AI context.
141
- Returns all orders from the last 30 days.
142
 
143
  Returns:
144
  JSON string containing orders array with key fields:
@@ -146,16 +162,27 @@ def get_orders_resource() -> str:
146
  - status, priority, created_at, assigned_driver_id
147
  """
148
  try:
 
 
 
 
 
 
 
 
 
 
149
  query = """
150
  SELECT order_id, customer_name, delivery_address,
151
  status, priority, created_at, assigned_driver_id
152
  FROM orders
153
- WHERE created_at > NOW() - INTERVAL '30 days'
 
154
  ORDER BY created_at DESC
155
  LIMIT 1000
156
  """
157
- orders = execute_query(query)
158
- logger.info(f"Resource orders://all - Retrieved {len(orders) if orders else 0} orders")
159
  return json.dumps(orders, default=str, indent=2)
160
  except Exception as e:
161
  logger.error(f"Resource orders://all failed: {e}")
@@ -166,7 +193,7 @@ def get_orders_resource() -> str:
166
  def get_drivers_resource() -> str:
167
  """
168
  Real-time drivers dataset for AI context.
169
- Returns all drivers with current locations and status.
170
 
171
  Returns:
172
  JSON string containing drivers array with key fields:
@@ -174,14 +201,25 @@ def get_drivers_resource() -> str:
174
  - current_lat, current_lng, last_location_update
175
  """
176
  try:
 
 
 
 
 
 
 
 
 
 
177
  query = """
178
  SELECT driver_id, name, status, vehicle_type, vehicle_plate,
179
  current_lat, current_lng, last_location_update
180
  FROM drivers
 
181
  ORDER BY name ASC
182
  """
183
- drivers = execute_query(query)
184
- logger.info(f"Resource drivers://all - Retrieved {len(drivers) if drivers else 0} drivers")
185
  return json.dumps(drivers, default=str, indent=2)
186
  except Exception as e:
187
  logger.error(f"Resource drivers://all failed: {e}")
@@ -1830,5 +1868,7 @@ if __name__ == "__main__":
1830
  logger.info("Tools: 27 tools registered (19 core + 6 assignment + 2 bulk delete)")
1831
  logger.info("Resources: 2 resources available")
1832
  logger.info("Prompts: 3 workflow templates")
 
 
1833
  logger.info("Starting MCP server...")
1834
  mcp.run()
 
13
  from pathlib import Path
14
  from typing import Literal
15
  from datetime import datetime
16
+ from contextvars import ContextVar
17
 
18
  # Add project root to path
19
  sys.path.insert(0, str(Path(__file__).parent))
 
38
  )
39
  logger = logging.getLogger(__name__)
40
 
41
+ # ============================================================================
42
+ # API KEY EXTRACTION FROM REQUEST
43
+ # ============================================================================
44
+
45
+ # Store API key per request using context variable
46
+ _current_api_key: ContextVar[str] = ContextVar('api_key', default=None)
47
+
48
+ def get_api_key_from_context() -> str:
49
+ """
50
+ Get API key from the current request context.
51
+ This is set by our custom dependencies.
52
+ """
53
+ return _current_api_key.get(None)
54
+
55
+ def extract_api_key_from_request():
56
+ """
57
+ Extract API key from HTTP request and store in context variable.
58
+ Called at the start of each tool execution.
59
+ """
60
+ try:
61
+ from fastmcp.server.dependencies import get_http_request
62
+ request = get_http_request()
63
+ api_key = request.query_params.get('api_key')
64
+ if api_key:
65
+ _current_api_key.set(api_key)
66
+ logger.debug(f"API key extracted from request: {api_key[:10]}...")
67
+ return api_key
68
+ except RuntimeError:
69
+ # No HTTP request available (e.g., stdio transport)
70
+ logger.debug("No HTTP request available for API key extraction")
71
+ return None
72
+
73
  # ============================================================================
74
  # MCP SERVER INITIALIZATION
75
  # ============================================================================
 
96
 
97
  def get_authenticated_user():
98
  """
99
+ Get authenticated user by validating API key from request context.
 
 
 
 
 
100
 
101
  Returns:
102
  User info dict with user_id, email, scopes, name or None if not authenticated
103
  """
104
  try:
105
+ # METHOD 1: Extract API key from current HTTP request
106
+ api_key = extract_api_key_from_request()
107
+
108
+ # METHOD 2: Fallback to environment variable for testing
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  if not api_key:
110
  api_key = os.getenv("FLEETMIND_API_KEY")
111
  if api_key:
112
+ logger.debug("Using API key from environment")
113
 
114
+ # Validate the API key
115
  if api_key:
116
  from database.api_keys import verify_api_key
117
  user_info = verify_api_key(api_key)
118
  if user_info:
119
+ logger.info(f"✅ Authenticated: {user_info['email']} (user_id: {user_info['user_id']})")
120
  return user_info
121
  else:
122
+ logger.warning(f"❌ Invalid API key: {api_key[:10]}...")
123
  return None
124
 
125
  # METHOD 3: Development bypass mode (local testing only)
126
+ # SECURITY: Only allow SKIP_AUTH in development environments
127
+ env = os.getenv("ENV", "production").lower()
128
+ skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
129
+
130
+ if skip_auth:
131
+ if env == "production":
132
+ logger.error("⚠️ SKIP_AUTH is enabled but ENV=production - DENYING access for security")
133
+ return None
134
+ else:
135
+ logger.warning(f"⚠️ SKIP_AUTH enabled in {env} environment - using development user")
136
+ return {
137
+ 'user_id': 'dev-user',
138
+ 'email': 'dev@fleetmind.local',
139
+ 'scopes': ['admin'],
140
+ 'name': 'Development User'
141
+ }
142
 
 
 
143
  return None
144
 
145
  except Exception as e:
 
154
  def get_orders_resource() -> str:
155
  """
156
  Real-time orders dataset for AI context.
157
+ Returns authenticated user's orders from the last 30 days.
158
 
159
  Returns:
160
  JSON string containing orders array with key fields:
 
162
  - status, priority, created_at, assigned_driver_id
163
  """
164
  try:
165
+ # Authenticate user
166
+ user = get_authenticated_user()
167
+ if not user:
168
+ logger.warning("Resource orders://all - Authentication required")
169
+ return json.dumps({
170
+ "error": "Authentication required",
171
+ "message": "Please provide a valid API key to access orders"
172
+ })
173
+
174
+ # Query only this user's orders
175
  query = """
176
  SELECT order_id, customer_name, delivery_address,
177
  status, priority, created_at, assigned_driver_id
178
  FROM orders
179
+ WHERE user_id = %s
180
+ AND created_at > NOW() - INTERVAL '30 days'
181
  ORDER BY created_at DESC
182
  LIMIT 1000
183
  """
184
+ orders = execute_query(query, (user['user_id'],))
185
+ logger.info(f"Resource orders://all - User {user['user_id']} retrieved {len(orders) if orders else 0} orders")
186
  return json.dumps(orders, default=str, indent=2)
187
  except Exception as e:
188
  logger.error(f"Resource orders://all failed: {e}")
 
193
  def get_drivers_resource() -> str:
194
  """
195
  Real-time drivers dataset for AI context.
196
+ Returns authenticated user's drivers with current locations and status.
197
 
198
  Returns:
199
  JSON string containing drivers array with key fields:
 
201
  - current_lat, current_lng, last_location_update
202
  """
203
  try:
204
+ # Authenticate user
205
+ user = get_authenticated_user()
206
+ if not user:
207
+ logger.warning("Resource drivers://all - Authentication required")
208
+ return json.dumps({
209
+ "error": "Authentication required",
210
+ "message": "Please provide a valid API key to access drivers"
211
+ })
212
+
213
+ # Query only this user's drivers
214
  query = """
215
  SELECT driver_id, name, status, vehicle_type, vehicle_plate,
216
  current_lat, current_lng, last_location_update
217
  FROM drivers
218
+ WHERE user_id = %s
219
  ORDER BY name ASC
220
  """
221
+ drivers = execute_query(query, (user['user_id'],))
222
+ logger.info(f"Resource drivers://all - User {user['user_id']} retrieved {len(drivers) if drivers else 0} drivers")
223
  return json.dumps(drivers, default=str, indent=2)
224
  except Exception as e:
225
  logger.error(f"Resource drivers://all failed: {e}")
 
1868
  logger.info("Tools: 27 tools registered (19 core + 6 assignment + 2 bulk delete)")
1869
  logger.info("Resources: 2 resources available")
1870
  logger.info("Prompts: 3 workflow templates")
1871
+ logger.info("Authentication: Multi-tenant API key via URL query params")
1872
+ logger.info("Usage: Connect with ?api_key=YOUR_KEY in the SSE URL")
1873
  logger.info("Starting MCP server...")
1874
  mcp.run()
verify_fix.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Verify the user_id fix"""
2
+
3
+ from database.connection import get_db_connection
4
+
5
+ def verify_order_fix():
6
+ conn = get_db_connection()
7
+ cursor = conn.cursor()
8
+
9
+ # Check the specific order mentioned by user
10
+ order_id = "ORD-202511192234450754"
11
+
12
+ cursor.execute(
13
+ "SELECT order_id, user_id, customer_name, status FROM orders WHERE order_id = %s",
14
+ (order_id,)
15
+ )
16
+
17
+ result = cursor.fetchone()
18
+
19
+ if result:
20
+ print(f"\nOrder: {result['order_id']}")
21
+ print(f"User ID: {result['user_id']}")
22
+ print(f"Customer: {result['customer_name']}")
23
+ print(f"Status: {result['status']}")
24
+
25
+ if result['user_id'] == 'user_id':
26
+ print("\n[ERROR] Still has bad user_id value!")
27
+ elif result['user_id'].startswith('user_'):
28
+ print("\n[SUCCESS] User ID is now correct!")
29
+ else:
30
+ print(f"\n[WARNING] Unexpected user_id format: {result['user_id']}")
31
+ else:
32
+ print(f"\n[NOT FOUND] Order {order_id} not found")
33
+
34
+ # Check if any records still have bad user_id
35
+ print("\n--- Checking for remaining bad records ---")
36
+
37
+ for table in ['orders', 'drivers', 'assignments']:
38
+ cursor.execute(f"SELECT COUNT(*) as count FROM {table} WHERE user_id = %s", ('user_id',))
39
+ result = cursor.fetchone()
40
+ count = result['count'] if result else 0
41
+
42
+ if count > 0:
43
+ print(f"{table}: {count} records still have user_id='user_id'")
44
+ else:
45
+ print(f"{table}: All clean!")
46
+
47
+ cursor.close()
48
+ conn.close()
49
+
50
+ if __name__ == "__main__":
51
+ verify_order_fix()