mdb-engine 0.1.6__py3-none-any.whl → 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. mdb_engine/__init__.py +104 -11
  2. mdb_engine/auth/ARCHITECTURE.md +112 -0
  3. mdb_engine/auth/README.md +648 -11
  4. mdb_engine/auth/__init__.py +136 -29
  5. mdb_engine/auth/audit.py +592 -0
  6. mdb_engine/auth/base.py +252 -0
  7. mdb_engine/auth/casbin_factory.py +264 -69
  8. mdb_engine/auth/config_helpers.py +7 -6
  9. mdb_engine/auth/cookie_utils.py +3 -7
  10. mdb_engine/auth/csrf.py +373 -0
  11. mdb_engine/auth/decorators.py +3 -10
  12. mdb_engine/auth/dependencies.py +47 -50
  13. mdb_engine/auth/helpers.py +3 -3
  14. mdb_engine/auth/integration.py +53 -80
  15. mdb_engine/auth/jwt.py +2 -6
  16. mdb_engine/auth/middleware.py +77 -34
  17. mdb_engine/auth/oso_factory.py +18 -38
  18. mdb_engine/auth/provider.py +270 -171
  19. mdb_engine/auth/rate_limiter.py +504 -0
  20. mdb_engine/auth/restrictions.py +8 -24
  21. mdb_engine/auth/session_manager.py +14 -29
  22. mdb_engine/auth/shared_middleware.py +600 -0
  23. mdb_engine/auth/shared_users.py +759 -0
  24. mdb_engine/auth/token_store.py +14 -28
  25. mdb_engine/auth/users.py +54 -113
  26. mdb_engine/auth/utils.py +213 -15
  27. mdb_engine/cli/commands/generate.py +545 -9
  28. mdb_engine/cli/commands/validate.py +3 -7
  29. mdb_engine/cli/utils.py +3 -3
  30. mdb_engine/config.py +7 -21
  31. mdb_engine/constants.py +65 -0
  32. mdb_engine/core/README.md +117 -6
  33. mdb_engine/core/__init__.py +39 -7
  34. mdb_engine/core/app_registration.py +22 -41
  35. mdb_engine/core/app_secrets.py +290 -0
  36. mdb_engine/core/connection.py +18 -9
  37. mdb_engine/core/encryption.py +223 -0
  38. mdb_engine/core/engine.py +1057 -93
  39. mdb_engine/core/index_management.py +12 -16
  40. mdb_engine/core/manifest.py +459 -150
  41. mdb_engine/core/ray_integration.py +435 -0
  42. mdb_engine/core/seeding.py +10 -18
  43. mdb_engine/core/service_initialization.py +12 -23
  44. mdb_engine/core/types.py +2 -5
  45. mdb_engine/database/README.md +140 -17
  46. mdb_engine/database/__init__.py +17 -6
  47. mdb_engine/database/abstraction.py +25 -37
  48. mdb_engine/database/connection.py +11 -18
  49. mdb_engine/database/query_validator.py +367 -0
  50. mdb_engine/database/resource_limiter.py +204 -0
  51. mdb_engine/database/scoped_wrapper.py +713 -196
  52. mdb_engine/dependencies.py +426 -0
  53. mdb_engine/di/__init__.py +34 -0
  54. mdb_engine/di/container.py +248 -0
  55. mdb_engine/di/providers.py +205 -0
  56. mdb_engine/di/scopes.py +139 -0
  57. mdb_engine/embeddings/README.md +54 -24
  58. mdb_engine/embeddings/__init__.py +31 -24
  59. mdb_engine/embeddings/dependencies.py +37 -154
  60. mdb_engine/embeddings/service.py +11 -25
  61. mdb_engine/exceptions.py +92 -0
  62. mdb_engine/indexes/README.md +30 -13
  63. mdb_engine/indexes/__init__.py +1 -0
  64. mdb_engine/indexes/helpers.py +1 -1
  65. mdb_engine/indexes/manager.py +50 -114
  66. mdb_engine/memory/README.md +2 -2
  67. mdb_engine/memory/__init__.py +1 -2
  68. mdb_engine/memory/service.py +30 -87
  69. mdb_engine/observability/README.md +4 -2
  70. mdb_engine/observability/__init__.py +26 -9
  71. mdb_engine/observability/health.py +8 -9
  72. mdb_engine/observability/metrics.py +32 -12
  73. mdb_engine/repositories/__init__.py +34 -0
  74. mdb_engine/repositories/base.py +325 -0
  75. mdb_engine/repositories/mongo.py +233 -0
  76. mdb_engine/repositories/unit_of_work.py +166 -0
  77. mdb_engine/routing/README.md +1 -1
  78. mdb_engine/routing/__init__.py +1 -3
  79. mdb_engine/routing/websockets.py +25 -60
  80. mdb_engine-0.2.0.dist-info/METADATA +313 -0
  81. mdb_engine-0.2.0.dist-info/RECORD +96 -0
  82. mdb_engine-0.1.6.dist-info/METADATA +0 -213
  83. mdb_engine-0.1.6.dist-info/RECORD +0 -75
  84. {mdb_engine-0.1.6.dist-info → mdb_engine-0.2.0.dist-info}/WHEEL +0 -0
  85. {mdb_engine-0.1.6.dist-info → mdb_engine-0.2.0.dist-info}/entry_points.txt +0 -0
  86. {mdb_engine-0.1.6.dist-info → mdb_engine-0.2.0.dist-info}/licenses/LICENSE +0 -0
  87. {mdb_engine-0.1.6.dist-info → mdb_engine-0.2.0.dist-info}/top_level.txt +0 -0
@@ -11,8 +11,11 @@ from datetime import datetime, timedelta
11
11
  from typing import Optional
12
12
 
13
13
  try:
14
- from pymongo.errors import (ConnectionFailure, OperationFailure,
15
- ServerSelectionTimeoutError)
14
+ from pymongo.errors import (
15
+ ConnectionFailure,
16
+ OperationFailure,
17
+ ServerSelectionTimeoutError,
18
+ )
16
19
  except ImportError:
17
20
  ConnectionFailure = Exception
18
21
  OperationFailure = Exception
@@ -54,9 +57,7 @@ class TokenBlacklist:
54
57
 
55
58
  try:
56
59
  # Create unique index on jti
57
- await self.collection.create_index(
58
- "jti", unique=True, name="jti_unique_idx"
59
- )
60
+ await self.collection.create_index("jti", unique=True, name="jti_unique_idx")
60
61
 
61
62
  # Create TTL index on expires_at (auto-delete expired entries)
62
63
  await self.collection.create_index(
@@ -110,9 +111,7 @@ class TokenBlacklist:
110
111
  }
111
112
 
112
113
  # Use upsert to handle duplicate jti gracefully
113
- await self.collection.update_one(
114
- {"jti": jti}, {"$set": blacklist_entry}, upsert=True
115
- )
114
+ await self.collection.update_one({"jti": jti}, {"$set": blacklist_entry}, upsert=True)
116
115
 
117
116
  logger.debug(f"Token {jti} added to blacklist (reason: {reason})")
118
117
  return True
@@ -158,15 +157,11 @@ class TokenBlacklist:
158
157
  ValueError,
159
158
  TypeError,
160
159
  ) as e:
161
- logger.error(
162
- f"Error checking token revocation status for {jti}: {e}", exc_info=True
163
- )
160
+ logger.error(f"Error checking token revocation status for {jti}: {e}", exc_info=True)
164
161
  # On error, assume not revoked (fail open for availability)
165
162
  return False
166
163
 
167
- async def revoke_all_user_tokens(
168
- self, user_id: str, reason: Optional[str] = None
169
- ) -> int:
164
+ async def revoke_all_user_tokens(self, user_id: str, reason: Optional[str] = None) -> int:
170
165
  """
171
166
  Revoke all tokens for a specific user.
172
167
 
@@ -189,8 +184,7 @@ class TokenBlacklist:
189
184
  "user_id": user_id,
190
185
  "revoked_at": datetime.utcnow(),
191
186
  "reason": reason or "logout_all",
192
- "expires_at": datetime.utcnow()
193
- + timedelta(days=30), # Keep for 30 days
187
+ "expires_at": datetime.utcnow() + timedelta(days=30), # Keep for 30 days
194
188
  }
195
189
 
196
190
  # Use upsert to update or create marker
@@ -213,9 +207,7 @@ class TokenBlacklist:
213
207
  ValueError,
214
208
  TypeError,
215
209
  ) as e:
216
- logger.error(
217
- f"Error revoking all tokens for user {user_id}: {e}", exc_info=True
218
- )
210
+ logger.error(f"Error revoking all tokens for user {user_id}: {e}", exc_info=True)
219
211
  return 0
220
212
 
221
213
  async def is_user_revoked(self, user_id: str) -> bool:
@@ -231,9 +223,7 @@ class TokenBlacklist:
231
223
  try:
232
224
  await self.ensure_indexes()
233
225
 
234
- marker = await self.collection.find_one(
235
- {"user_id": user_id, "type": "user_revocation"}
236
- )
226
+ marker = await self.collection.find_one({"user_id": user_id, "type": "user_revocation"})
237
227
 
238
228
  if marker:
239
229
  # Check if marker hasn't expired
@@ -269,9 +259,7 @@ class TokenBlacklist:
269
259
  Number of entries deleted
270
260
  """
271
261
  try:
272
- result = await self.collection.delete_many(
273
- {"expires_at": {"$lt": datetime.utcnow()}}
274
- )
262
+ result = await self.collection.delete_many({"expires_at": {"$lt": datetime.utcnow()}})
275
263
  deleted_count = result.deleted_count
276
264
  if deleted_count > 0:
277
265
  logger.info(f"Cleared {deleted_count} expired blacklist entries")
@@ -283,7 +271,5 @@ class TokenBlacklist:
283
271
  ValueError,
284
272
  TypeError,
285
273
  ) as e:
286
- logger.error(
287
- f"Error clearing expired blacklist entries: {e}", exc_info=True
288
- )
274
+ logger.error(f"Error clearing expired blacklist entries: {e}", exc_info=True)
289
275
  return 0
mdb_engine/auth/users.py CHANGED
@@ -22,9 +22,15 @@ from fastapi import Request
22
22
  from fastapi.responses import Response
23
23
 
24
24
  try:
25
- from pymongo.errors import (ConnectionFailure, OperationFailure,
26
- ServerSelectionTimeoutError)
25
+ from bson.errors import InvalidId as BsonInvalidId
26
+ from pymongo.errors import (
27
+ ConnectionFailure,
28
+ OperationFailure,
29
+ PyMongoError,
30
+ ServerSelectionTimeoutError,
31
+ )
27
32
  except ImportError:
33
+ BsonInvalidId = ValueError # Fallback if bson not available
28
34
  ConnectionFailure = Exception
29
35
  OperationFailure = Exception
30
36
  ServerSelectionTimeoutError = Exception
@@ -90,11 +96,8 @@ def _convert_user_id_to_objectid(user_id: Any) -> Tuple[Any, Optional[str]]:
90
96
  # If it's not a string or ObjectId, try to convert (for backward compatibility)
91
97
  try:
92
98
  return ObjectId(user_id), None
93
- except (TypeError, ValueError) as e:
99
+ except (TypeError, ValueError, BsonInvalidId) as e:
94
100
  return None, f"Invalid user_id format: {user_id}: {e}"
95
- except Exception:
96
- logger.exception("Unexpected error converting user_id to ObjectId")
97
- raise
98
101
 
99
102
  return user_id, None
100
103
 
@@ -154,9 +157,7 @@ async def get_app_user(
154
157
  db,
155
158
  config: Optional[Dict[str, Any]] = None,
156
159
  allow_demo_fallback: bool = False,
157
- get_app_config_func: Optional[
158
- Callable[[Request, str, Dict], Awaitable[Dict]]
159
- ] = None,
160
+ get_app_config_func: Optional[Callable[[Request, str, Dict], Awaitable[Dict]]] = None,
160
161
  ) -> Optional[Dict[str, Any]]:
161
162
  """
162
163
  Get app-level user from session cookie.
@@ -279,9 +280,7 @@ async def _try_demo_mode(
279
280
  f"(MONGO_URI={MONGO_URI}, DB_NAME={DB_NAME})"
280
281
  )
281
282
 
282
- demo_user = await get_or_create_demo_user(
283
- db, slug_id, config, MONGO_URI, DB_NAME
284
- )
283
+ demo_user = await get_or_create_demo_user(db, slug_id, config, MONGO_URI, DB_NAME)
285
284
 
286
285
  if not demo_user:
287
286
  logger.warning(
@@ -321,9 +320,7 @@ async def create_app_session(
321
320
  user_id: str,
322
321
  config: Optional[Dict[str, Any]] = None,
323
322
  response: Optional[Response] = None,
324
- get_app_config_func: Optional[
325
- Callable[[Request, str, Dict], Awaitable[Dict]]
326
- ] = None,
323
+ get_app_config_func: Optional[Callable[[Request, str, Dict], Awaitable[Dict]]] = None,
327
324
  ) -> str:
328
325
  """
329
326
  Create a app-specific session token and set cookie.
@@ -387,9 +384,7 @@ async def create_app_session(
387
384
  cookie_name = f"{session_cookie_name}_{slug_id}"
388
385
 
389
386
  # Determine secure cookie setting
390
- should_use_secure = (
391
- request.url.scheme == "https" or os.getenv("G_NOME_ENV") == "production"
392
- )
387
+ should_use_secure = request.url.scheme == "https" or os.getenv("G_NOME_ENV") == "production"
393
388
 
394
389
  response.set_cookie(
395
390
  key=cookie_name,
@@ -436,12 +431,9 @@ async def authenticate_app_user(
436
431
  from bson.objectid import ObjectId
437
432
 
438
433
  query["store_id"] = ObjectId(store_id)
439
- except (TypeError, ValueError) as e:
434
+ except (TypeError, ValueError, BsonInvalidId) as e:
440
435
  logger.warning(f"Invalid store_id format: {store_id}: {e}")
441
436
  return None
442
- except Exception:
443
- logger.exception("Unexpected error converting store_id to ObjectId")
444
- raise
445
437
 
446
438
  # Find user
447
439
  # Use getattr to access collection (works with ScopedMongoWrapper and AppDB)
@@ -479,10 +471,12 @@ async def authenticate_app_user(
479
471
  except (ValueError, TypeError):
480
472
  logger.exception("Validation error authenticating app user")
481
473
  return None
482
- except Exception:
483
- logger.exception("Unexpected error authenticating app user")
484
- # Re-raise unexpected errors for debugging
485
- raise
474
+ except PyMongoError:
475
+ logger.exception("Database error authenticating app user")
476
+ return None
477
+ except (AttributeError, KeyError):
478
+ logger.exception("Attribute access error authenticating app user")
479
+ return None
486
480
 
487
481
 
488
482
  async def create_app_user(
@@ -513,12 +507,7 @@ async def create_app_user(
513
507
  from bson.objectid import ObjectId
514
508
 
515
509
  # Validate email format
516
- if (
517
- not email
518
- or not isinstance(email, str)
519
- or "@" not in email
520
- or "." not in email
521
- ):
510
+ if not email or not isinstance(email, str) or "@" not in email or "." not in email:
522
511
  logger.warning(f"Invalid email format: {email}")
523
512
  return None
524
513
 
@@ -545,13 +534,9 @@ async def create_app_user(
545
534
  # Always hash password (plain text support removed for security)
546
535
  try:
547
536
  password_hash = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt())
548
- except ValueError:
537
+ except (ValueError, TypeError, UnicodeEncodeError):
549
538
  logger.exception("Error encoding password for hashing")
550
539
  return None
551
- except Exception:
552
- logger.exception("Unexpected error hashing password")
553
- # Re-raise unexpected errors
554
- raise
555
540
 
556
541
  # Create user document
557
542
  user_doc = {
@@ -573,13 +558,15 @@ async def create_app_user(
573
558
 
574
559
  return user_doc
575
560
 
576
- except (ValueError, TypeError):
561
+ except (ValueError, TypeError, BsonInvalidId):
577
562
  logger.exception("Validation error creating app user")
578
563
  return None
579
- except Exception:
580
- logger.exception("Unexpected error creating app user")
581
- # Re-raise unexpected errors for debugging
582
- raise
564
+ except PyMongoError:
565
+ logger.exception("Database error creating app user")
566
+ return None
567
+ except (AttributeError, KeyError):
568
+ logger.exception("Attribute access error creating app user")
569
+ return None
583
570
 
584
571
 
585
572
  async def get_or_create_anonymous_user(
@@ -587,9 +574,7 @@ async def get_or_create_anonymous_user(
587
574
  slug_id: str,
588
575
  db,
589
576
  config: Optional[Dict[str, Any]] = None,
590
- get_app_config_func: Optional[
591
- Callable[[Request, str, Dict], Awaitable[Dict]]
592
- ] = None,
577
+ get_app_config_func: Optional[Callable[[Request, str, Dict], Awaitable[Dict]]] = None,
593
578
  ) -> Optional[Dict[str, Any]]:
594
579
  """
595
580
  Get or create an anonymous user for anonymous_session strategy.
@@ -655,9 +640,7 @@ async def get_or_create_anonymous_user(
655
640
  return user
656
641
 
657
642
 
658
- async def get_platform_demo_user(
659
- mongo_uri: str, db_name: str
660
- ) -> Optional[Dict[str, Any]]:
643
+ async def get_platform_demo_user(mongo_uri: str, db_name: str) -> Optional[Dict[str, Any]]:
661
644
  """
662
645
  Get platform demo user information from top-level database.
663
646
 
@@ -669,8 +652,7 @@ async def get_platform_demo_user(
669
652
  Dict with demo user info (email, password from config, user_id) or None if not available
670
653
  """
671
654
  try:
672
- from ..config import (DEMO_EMAIL_DEFAULT, DEMO_ENABLED,
673
- DEMO_PASSWORD_DEFAULT)
655
+ from ..config import DEMO_EMAIL_DEFAULT, DEMO_ENABLED, DEMO_PASSWORD_DEFAULT
674
656
 
675
657
  if not DEMO_ENABLED or not DEMO_EMAIL_DEFAULT:
676
658
  return None
@@ -716,14 +698,10 @@ async def _link_platform_demo_user(
716
698
  import datetime
717
699
 
718
700
  try:
719
- logger.debug(
720
- f"ensure_demo_users_exist: Auto-linking platform demo user for '{slug_id}'"
721
- )
701
+ logger.debug(f"ensure_demo_users_exist: Auto-linking platform demo user for '{slug_id}'")
722
702
  platform_demo = await get_platform_demo_user(mongo_uri, db_name)
723
703
  if not platform_demo:
724
- logger.warning(
725
- f"ensure_demo_users_exist: Platform demo user not found for '{slug_id}'"
726
- )
704
+ logger.warning(f"ensure_demo_users_exist: Platform demo user not found for '{slug_id}'")
727
705
  return None
728
706
 
729
707
  # Check if app demo user already exists for platform demo
@@ -737,13 +715,10 @@ async def _link_platform_demo_user(
737
715
  platform_password = platform_demo.get("password", "demo123")
738
716
  password_hash = None
739
717
  try:
740
- password_hash = bcrypt.hashpw(
741
- platform_password.encode("utf-8"), bcrypt.gensalt()
742
- )
718
+ password_hash = bcrypt.hashpw(platform_password.encode("utf-8"), bcrypt.gensalt())
743
719
  except (ValueError, TypeError, AttributeError) as e:
744
720
  logger.error(
745
- f"Error hashing password for platform demo user "
746
- f"{platform_demo['email']}: {e}",
721
+ f"Error hashing password for platform demo user " f"{platform_demo['email']}: {e}",
747
722
  exc_info=True,
748
723
  )
749
724
  return None
@@ -793,9 +768,7 @@ def _validate_demo_user_config(
793
768
 
794
769
  extra_data = demo_user_config.get("extra_data", {})
795
770
  if not isinstance(extra_data, dict):
796
- logger.warning(
797
- f"Invalid extra_data for demo user config (not a dict): {extra_data}"
798
- )
771
+ logger.warning(f"Invalid extra_data for demo user config (not a dict): {extra_data}")
799
772
  extra_data = {}
800
773
 
801
774
  return {
@@ -825,14 +798,10 @@ async def _resolve_demo_user_email_password(
825
798
  if not password:
826
799
  password = platform_demo["password"]
827
800
  else:
828
- logger.warning(
829
- f"No email specified and platform demo not available for {slug_id}"
830
- )
801
+ logger.warning(f"No email specified and platform demo not available for {slug_id}")
831
802
  return None, None
832
803
  else:
833
- logger.warning(
834
- f"No email specified and cannot access platform demo for {slug_id}"
835
- )
804
+ logger.warning(f"No email specified and cannot access platform demo for {slug_id}")
836
805
  return None, None
837
806
 
838
807
  # Validate email format
@@ -877,9 +846,7 @@ async def _create_demo_user_from_config(
877
846
  try:
878
847
  password_hash = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt())
879
848
  except (ValueError, TypeError, AttributeError) as e:
880
- logger.error(
881
- f"Error hashing password for demo user {email}: {e}", exc_info=True
882
- )
849
+ logger.error(f"Error hashing password for demo user {email}: {e}", exc_info=True)
883
850
  return None
884
851
 
885
852
  # Create user document
@@ -945,9 +912,7 @@ async def _create_demo_user_from_config(
945
912
 
946
913
  user_doc["_id"] = result.inserted_id if not custom_id else custom_id
947
914
  user_doc["app_user_id"] = str(user_doc["_id"])
948
- logger.info(
949
- f"Created demo user {email} for {slug_id} with _id={user_doc['_id']}"
950
- )
915
+ logger.info(f"Created demo user {email} for {slug_id} with _id={user_doc['_id']}")
951
916
  return user_doc
952
917
  except (
953
918
  OperationFailure,
@@ -1030,9 +995,7 @@ async def ensure_demo_users_exist(
1030
995
  for demo_user_config in demo_users_config:
1031
996
  try:
1032
997
  # Validate config structure
1033
- validated_config, error = _validate_demo_user_config(
1034
- demo_user_config, slug_id
1035
- )
998
+ validated_config, error = _validate_demo_user_config(demo_user_config, slug_id)
1036
999
  if not validated_config:
1037
1000
  if error:
1038
1001
  logger.warning(error)
@@ -1096,9 +1059,7 @@ async def get_or_create_demo_user_for_request(
1096
1059
  slug_id: str,
1097
1060
  db,
1098
1061
  config: Optional[Dict[str, Any]] = None,
1099
- get_app_config_func: Optional[
1100
- Callable[[Request, str, Dict], Awaitable[Dict]]
1101
- ] = None,
1062
+ get_app_config_func: Optional[Callable[[Request, str, Dict], Awaitable[Dict]]] = None,
1102
1063
  ) -> Optional[Dict[str, Any]]:
1103
1064
  """
1104
1065
  Get or create a demo user for the current request context.
@@ -1146,9 +1107,7 @@ async def get_or_create_demo_user_for_request(
1146
1107
  # Check if app demo user exists
1147
1108
  # Use getattr to access collection (works with ScopedMongoWrapper and AppDB)
1148
1109
  collection = getattr(db, collection_name)
1149
- app_demo = await collection.find_one(
1150
- {"email": DEMO_EMAIL_DEFAULT}
1151
- )
1110
+ app_demo = await collection.find_one({"email": DEMO_EMAIL_DEFAULT})
1152
1111
 
1153
1112
  if app_demo:
1154
1113
  app_demo["app_user_id"] = str(app_demo["_id"])
@@ -1158,15 +1117,11 @@ async def get_or_create_demo_user_for_request(
1158
1117
  try:
1159
1118
  from ..config import DB_NAME, MONGO_URI
1160
1119
 
1161
- await ensure_demo_users_exist(
1162
- db, slug_id, config, MONGO_URI, DB_NAME
1163
- )
1120
+ await ensure_demo_users_exist(db, slug_id, config, MONGO_URI, DB_NAME)
1164
1121
  # Use getattr to access collection (works with
1165
1122
  # ScopedMongoWrapper and AppDB)
1166
1123
  collection = getattr(db, collection_name)
1167
- app_demo = await collection.find_one(
1168
- {"email": DEMO_EMAIL_DEFAULT}
1169
- )
1124
+ app_demo = await collection.find_one({"email": DEMO_EMAIL_DEFAULT})
1170
1125
  if app_demo:
1171
1126
  app_demo["app_user_id"] = str(app_demo["_id"])
1172
1127
  return app_demo
@@ -1232,9 +1187,7 @@ async def get_or_create_demo_user(
1232
1187
  f"db_name={'provided' if db_name else 'not provided'})"
1233
1188
  )
1234
1189
 
1235
- demo_users = await ensure_demo_users_exist(
1236
- db, slug_id, config, mongo_uri, db_name
1237
- )
1190
+ demo_users = await ensure_demo_users_exist(db, slug_id, config, mongo_uri, db_name)
1238
1191
 
1239
1192
  if demo_users and len(demo_users) > 0:
1240
1193
  # Return the first demo user (usually the primary one)
@@ -1366,12 +1319,10 @@ async def ensure_demo_users_for_actor(
1366
1319
  return []
1367
1320
 
1368
1321
  try:
1369
- with open(manifest_path, "r") as f:
1322
+ with open(manifest_path) as f:
1370
1323
  config = json.load(f)
1371
1324
  except json.JSONDecodeError as e:
1372
- logger.error(
1373
- f"Invalid JSON in manifest.json for {slug_id}: {e}", exc_info=True
1374
- )
1325
+ logger.error(f"Invalid JSON in manifest.json for {slug_id}: {e}", exc_info=True)
1375
1326
  return []
1376
1327
 
1377
1328
  # Ensure demo users exist
@@ -1395,9 +1346,7 @@ async def ensure_demo_users_for_actor(
1395
1346
  KeyError,
1396
1347
  PermissionError,
1397
1348
  ) as e:
1398
- logger.error(
1399
- f"Error ensuring demo users for actor {slug_id}: {e}", exc_info=True
1400
- )
1349
+ logger.error(f"Error ensuring demo users for actor {slug_id}: {e}", exc_info=True)
1401
1350
  return []
1402
1351
 
1403
1352
 
@@ -1425,9 +1374,7 @@ async def sync_app_user_to_casbin(
1425
1374
  try:
1426
1375
  # Check if provider is CasbinAdapter
1427
1376
  if not hasattr(authz_provider, "_enforcer"):
1428
- logger.debug(
1429
- "sync_app_user_to_casbin: Provider is not CasbinAdapter, skipping"
1430
- )
1377
+ logger.debug("sync_app_user_to_casbin: Provider is not CasbinAdapter, skipping")
1431
1378
  return False
1432
1379
 
1433
1380
  enforcer = authz_provider._enforcer
@@ -1448,9 +1395,7 @@ async def sync_app_user_to_casbin(
1448
1395
  # Check if role assignment already exists
1449
1396
  existing_roles = await enforcer.get_roles_for_user(subject)
1450
1397
  if role in existing_roles:
1451
- logger.debug(
1452
- f"sync_app_user_to_casbin: User {subject} already has role {role}"
1453
- )
1398
+ logger.debug(f"sync_app_user_to_casbin: User {subject} already has role {role}")
1454
1399
  return True
1455
1400
 
1456
1401
  # Add grouping policy: user -> role
@@ -1479,15 +1424,11 @@ async def sync_app_user_to_casbin(
1479
1424
  ConnectionError,
1480
1425
  KeyError,
1481
1426
  ) as e:
1482
- logger.error(
1483
- f"sync_app_user_to_casbin: Error syncing user to Casbin: {e}", exc_info=True
1484
- )
1427
+ logger.error(f"sync_app_user_to_casbin: Error syncing user to Casbin: {e}", exc_info=True)
1485
1428
  return False
1486
1429
 
1487
1430
 
1488
- def get_app_user_role(
1489
- user: Dict[str, Any], config: Optional[Dict[str, Any]] = None
1490
- ) -> str:
1431
+ def get_app_user_role(user: Dict[str, Any], config: Optional[Dict[str, Any]] = None) -> str:
1491
1432
  """
1492
1433
  Determine Casbin role for app-level user.
1493
1434