mdb-engine 0.1.6__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.
- mdb_engine/README.md +144 -0
- mdb_engine/__init__.py +37 -0
- mdb_engine/auth/README.md +631 -0
- mdb_engine/auth/__init__.py +128 -0
- mdb_engine/auth/casbin_factory.py +199 -0
- mdb_engine/auth/casbin_models.py +46 -0
- mdb_engine/auth/config_defaults.py +71 -0
- mdb_engine/auth/config_helpers.py +213 -0
- mdb_engine/auth/cookie_utils.py +158 -0
- mdb_engine/auth/decorators.py +350 -0
- mdb_engine/auth/dependencies.py +747 -0
- mdb_engine/auth/helpers.py +64 -0
- mdb_engine/auth/integration.py +578 -0
- mdb_engine/auth/jwt.py +225 -0
- mdb_engine/auth/middleware.py +241 -0
- mdb_engine/auth/oso_factory.py +323 -0
- mdb_engine/auth/provider.py +570 -0
- mdb_engine/auth/restrictions.py +271 -0
- mdb_engine/auth/session_manager.py +477 -0
- mdb_engine/auth/token_lifecycle.py +213 -0
- mdb_engine/auth/token_store.py +289 -0
- mdb_engine/auth/users.py +1516 -0
- mdb_engine/auth/utils.py +614 -0
- mdb_engine/cli/__init__.py +13 -0
- mdb_engine/cli/commands/__init__.py +7 -0
- mdb_engine/cli/commands/generate.py +105 -0
- mdb_engine/cli/commands/migrate.py +83 -0
- mdb_engine/cli/commands/show.py +70 -0
- mdb_engine/cli/commands/validate.py +63 -0
- mdb_engine/cli/main.py +41 -0
- mdb_engine/cli/utils.py +92 -0
- mdb_engine/config.py +217 -0
- mdb_engine/constants.py +160 -0
- mdb_engine/core/README.md +542 -0
- mdb_engine/core/__init__.py +42 -0
- mdb_engine/core/app_registration.py +392 -0
- mdb_engine/core/connection.py +243 -0
- mdb_engine/core/engine.py +749 -0
- mdb_engine/core/index_management.py +162 -0
- mdb_engine/core/manifest.py +2793 -0
- mdb_engine/core/seeding.py +179 -0
- mdb_engine/core/service_initialization.py +355 -0
- mdb_engine/core/types.py +413 -0
- mdb_engine/database/README.md +522 -0
- mdb_engine/database/__init__.py +31 -0
- mdb_engine/database/abstraction.py +635 -0
- mdb_engine/database/connection.py +387 -0
- mdb_engine/database/scoped_wrapper.py +1721 -0
- mdb_engine/embeddings/README.md +184 -0
- mdb_engine/embeddings/__init__.py +62 -0
- mdb_engine/embeddings/dependencies.py +193 -0
- mdb_engine/embeddings/service.py +759 -0
- mdb_engine/exceptions.py +167 -0
- mdb_engine/indexes/README.md +651 -0
- mdb_engine/indexes/__init__.py +21 -0
- mdb_engine/indexes/helpers.py +145 -0
- mdb_engine/indexes/manager.py +895 -0
- mdb_engine/memory/README.md +451 -0
- mdb_engine/memory/__init__.py +30 -0
- mdb_engine/memory/service.py +1285 -0
- mdb_engine/observability/README.md +515 -0
- mdb_engine/observability/__init__.py +42 -0
- mdb_engine/observability/health.py +296 -0
- mdb_engine/observability/logging.py +161 -0
- mdb_engine/observability/metrics.py +297 -0
- mdb_engine/routing/README.md +462 -0
- mdb_engine/routing/__init__.py +73 -0
- mdb_engine/routing/websockets.py +813 -0
- mdb_engine/utils/__init__.py +7 -0
- mdb_engine-0.1.6.dist-info/METADATA +213 -0
- mdb_engine-0.1.6.dist-info/RECORD +75 -0
- mdb_engine-0.1.6.dist-info/WHEEL +5 -0
- mdb_engine-0.1.6.dist-info/entry_points.txt +2 -0
- mdb_engine-0.1.6.dist-info/licenses/LICENSE +661 -0
- mdb_engine-0.1.6.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Authentication and Authorization Module
|
|
3
|
+
|
|
4
|
+
Provides authentication, authorization, and access control for the MongoDB Engine.
|
|
5
|
+
|
|
6
|
+
This module is part of MDB_ENGINE - MongoDB Engine.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# Casbin Factory
|
|
10
|
+
from .casbin_factory import (create_casbin_enforcer, get_casbin_model,
|
|
11
|
+
initialize_casbin_from_manifest)
|
|
12
|
+
# Cookie utilities
|
|
13
|
+
from .cookie_utils import (clear_auth_cookies, get_secure_cookie_settings,
|
|
14
|
+
set_auth_cookies)
|
|
15
|
+
# Decorators
|
|
16
|
+
from .decorators import (auto_token_setup, rate_limit_auth, require_auth,
|
|
17
|
+
token_security)
|
|
18
|
+
from .dependencies import (SECRET_KEY, _validate_next_url, get_authz_provider,
|
|
19
|
+
get_current_user, get_current_user_from_request,
|
|
20
|
+
get_current_user_or_redirect, get_refresh_token,
|
|
21
|
+
get_session_manager, get_token_blacklist,
|
|
22
|
+
refresh_access_token, require_admin,
|
|
23
|
+
require_admin_or_developer, require_permission)
|
|
24
|
+
from .helpers import initialize_token_management
|
|
25
|
+
# Integration
|
|
26
|
+
from .integration import get_auth_config, setup_auth_from_manifest
|
|
27
|
+
from .jwt import (decode_jwt_token, encode_jwt_token, extract_token_metadata,
|
|
28
|
+
generate_token_pair)
|
|
29
|
+
# Middleware
|
|
30
|
+
from .middleware import SecurityMiddleware, create_security_middleware
|
|
31
|
+
from .provider import (AUTHZ_CACHE_TTL, AuthorizationProvider, CasbinAdapter,
|
|
32
|
+
OsoAdapter)
|
|
33
|
+
from .restrictions import block_demo_users, is_demo_user, require_non_demo_user
|
|
34
|
+
from .session_manager import SessionManager
|
|
35
|
+
from .token_lifecycle import (get_time_until_expiry, get_token_age,
|
|
36
|
+
get_token_expiry_time, get_token_info,
|
|
37
|
+
is_token_expiring_soon, should_refresh_token,
|
|
38
|
+
validate_token_version)
|
|
39
|
+
# Token management
|
|
40
|
+
from .token_store import TokenBlacklist
|
|
41
|
+
from .users import (authenticate_app_user, create_app_session, create_app_user,
|
|
42
|
+
ensure_demo_users_exist, ensure_demo_users_for_actor,
|
|
43
|
+
get_app_user, get_app_user_role,
|
|
44
|
+
get_or_create_anonymous_user, get_or_create_demo_user,
|
|
45
|
+
get_or_create_demo_user_for_request,
|
|
46
|
+
sync_app_user_to_casbin)
|
|
47
|
+
# Utilities
|
|
48
|
+
from .utils import (get_device_info, login_user, logout_user, register_user,
|
|
49
|
+
validate_password_strength)
|
|
50
|
+
|
|
51
|
+
__all__ = [
|
|
52
|
+
# Provider
|
|
53
|
+
"AuthorizationProvider",
|
|
54
|
+
"CasbinAdapter",
|
|
55
|
+
"OsoAdapter",
|
|
56
|
+
"AUTHZ_CACHE_TTL",
|
|
57
|
+
# JWT
|
|
58
|
+
"decode_jwt_token",
|
|
59
|
+
"encode_jwt_token",
|
|
60
|
+
"generate_token_pair",
|
|
61
|
+
"extract_token_metadata",
|
|
62
|
+
# Dependencies
|
|
63
|
+
"SECRET_KEY",
|
|
64
|
+
"get_authz_provider",
|
|
65
|
+
"get_current_user",
|
|
66
|
+
"get_current_user_from_request",
|
|
67
|
+
"require_admin",
|
|
68
|
+
"require_admin_or_developer",
|
|
69
|
+
"get_current_user_or_redirect",
|
|
70
|
+
"require_permission",
|
|
71
|
+
"_validate_next_url",
|
|
72
|
+
"get_token_blacklist",
|
|
73
|
+
"get_session_manager",
|
|
74
|
+
"get_refresh_token",
|
|
75
|
+
"refresh_access_token",
|
|
76
|
+
# App-level user management
|
|
77
|
+
"get_app_user",
|
|
78
|
+
"create_app_session",
|
|
79
|
+
"authenticate_app_user",
|
|
80
|
+
"create_app_user",
|
|
81
|
+
"get_or_create_anonymous_user",
|
|
82
|
+
"ensure_demo_users_exist",
|
|
83
|
+
"get_or_create_demo_user_for_request",
|
|
84
|
+
"get_or_create_demo_user",
|
|
85
|
+
"ensure_demo_users_for_actor",
|
|
86
|
+
"sync_app_user_to_casbin",
|
|
87
|
+
"get_app_user_role",
|
|
88
|
+
# Restrictions
|
|
89
|
+
"is_demo_user",
|
|
90
|
+
"require_non_demo_user",
|
|
91
|
+
"block_demo_users",
|
|
92
|
+
# Token Management
|
|
93
|
+
"TokenBlacklist",
|
|
94
|
+
"SessionManager",
|
|
95
|
+
"get_token_expiry_time",
|
|
96
|
+
"is_token_expiring_soon",
|
|
97
|
+
"should_refresh_token",
|
|
98
|
+
"get_token_age",
|
|
99
|
+
"get_time_until_expiry",
|
|
100
|
+
"validate_token_version",
|
|
101
|
+
"get_token_info",
|
|
102
|
+
"initialize_token_management",
|
|
103
|
+
# Utilities
|
|
104
|
+
"login_user",
|
|
105
|
+
"register_user",
|
|
106
|
+
"logout_user",
|
|
107
|
+
"validate_password_strength",
|
|
108
|
+
"get_device_info",
|
|
109
|
+
# Decorators
|
|
110
|
+
"require_auth",
|
|
111
|
+
"token_security",
|
|
112
|
+
"rate_limit_auth",
|
|
113
|
+
"auto_token_setup",
|
|
114
|
+
# Cookie utilities
|
|
115
|
+
"get_secure_cookie_settings",
|
|
116
|
+
"set_auth_cookies",
|
|
117
|
+
"clear_auth_cookies",
|
|
118
|
+
# Middleware
|
|
119
|
+
"SecurityMiddleware",
|
|
120
|
+
"create_security_middleware",
|
|
121
|
+
# Integration
|
|
122
|
+
"get_auth_config",
|
|
123
|
+
"setup_auth_from_manifest",
|
|
124
|
+
# Casbin Factory
|
|
125
|
+
"get_casbin_model",
|
|
126
|
+
"create_casbin_enforcer",
|
|
127
|
+
"initialize_casbin_from_manifest",
|
|
128
|
+
]
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Casbin Provider Factory
|
|
3
|
+
|
|
4
|
+
Provides helper functions to auto-initialize Casbin authorization provider
|
|
5
|
+
from manifest configuration.
|
|
6
|
+
|
|
7
|
+
This module is part of MDB_ENGINE - MongoDB Engine.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional
|
|
15
|
+
|
|
16
|
+
from .casbin_models import DEFAULT_RBAC_MODEL, SIMPLE_ACL_MODEL
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
import casbin
|
|
20
|
+
|
|
21
|
+
from .provider import CasbinAdapter
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_casbin_model(model_type: str = "rbac") -> str:
|
|
27
|
+
"""
|
|
28
|
+
Get Casbin model string by type or path.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
model_type: Model type ("rbac", "acl") or path to model file
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Casbin model string
|
|
35
|
+
"""
|
|
36
|
+
if model_type == "rbac":
|
|
37
|
+
return DEFAULT_RBAC_MODEL
|
|
38
|
+
elif model_type == "acl":
|
|
39
|
+
return SIMPLE_ACL_MODEL
|
|
40
|
+
else:
|
|
41
|
+
# Assume it's a file path
|
|
42
|
+
model_path = Path(model_type)
|
|
43
|
+
if model_path.exists():
|
|
44
|
+
return model_path.read_text()
|
|
45
|
+
else:
|
|
46
|
+
logger.warning(
|
|
47
|
+
f"Casbin model file not found: {model_type}, using default RBAC model"
|
|
48
|
+
)
|
|
49
|
+
return DEFAULT_RBAC_MODEL
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
async def create_casbin_enforcer(
|
|
53
|
+
db,
|
|
54
|
+
model: str = "rbac",
|
|
55
|
+
policies_collection: str = "casbin_policies",
|
|
56
|
+
default_roles: Optional[list] = None,
|
|
57
|
+
) -> casbin.AsyncEnforcer:
|
|
58
|
+
"""
|
|
59
|
+
Create a Casbin AsyncEnforcer with MongoDB adapter.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
db: MongoDB database instance (Motor AsyncIOMotorDatabase)
|
|
63
|
+
model: Casbin model type ("rbac", "acl") or path to model file
|
|
64
|
+
policies_collection: MongoDB collection name for policies
|
|
65
|
+
default_roles: List of default roles to create (optional)
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Configured Casbin AsyncEnforcer instance
|
|
69
|
+
|
|
70
|
+
Raises:
|
|
71
|
+
ImportError: If casbin or casbin-motor-adapter is not installed
|
|
72
|
+
"""
|
|
73
|
+
try:
|
|
74
|
+
import casbin
|
|
75
|
+
from casbin_motor_adapter import MotorAdapter
|
|
76
|
+
except ImportError as e:
|
|
77
|
+
raise ImportError(
|
|
78
|
+
"Casbin dependencies not installed. Install with: pip install mdb-engine[casbin]"
|
|
79
|
+
) from e
|
|
80
|
+
|
|
81
|
+
# Get model string
|
|
82
|
+
model_str = get_casbin_model(model)
|
|
83
|
+
|
|
84
|
+
# Create MongoDB adapter
|
|
85
|
+
adapter = MotorAdapter(db, policies_collection)
|
|
86
|
+
|
|
87
|
+
# Create enforcer with model and adapter
|
|
88
|
+
enforcer = casbin.AsyncEnforcer()
|
|
89
|
+
await enforcer.set_model(casbin.new_model_from_string(model_str))
|
|
90
|
+
enforcer.set_adapter(adapter)
|
|
91
|
+
|
|
92
|
+
# Load policies from database
|
|
93
|
+
await enforcer.load_policy()
|
|
94
|
+
|
|
95
|
+
# Create default roles if specified
|
|
96
|
+
if default_roles:
|
|
97
|
+
await _create_default_roles(enforcer, default_roles)
|
|
98
|
+
|
|
99
|
+
logger.info(
|
|
100
|
+
f"Casbin enforcer created with model '{model}' and "
|
|
101
|
+
f"policies collection '{policies_collection}'"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
return enforcer
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
async def _create_default_roles(enforcer: "casbin.AsyncEnforcer", roles: list) -> None:
|
|
108
|
+
"""
|
|
109
|
+
Create default roles in Casbin (as grouping rules).
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
enforcer: Casbin AsyncEnforcer instance
|
|
113
|
+
roles: List of role names to create
|
|
114
|
+
"""
|
|
115
|
+
for role in roles:
|
|
116
|
+
# Create a grouping rule: role -> role (self-reference for role existence)
|
|
117
|
+
# This ensures the role exists in the system
|
|
118
|
+
# Actual user-role assignments will be added when users are created
|
|
119
|
+
try:
|
|
120
|
+
# Check if role already exists
|
|
121
|
+
existing = await enforcer.get_roles_for_user(role)
|
|
122
|
+
if not existing:
|
|
123
|
+
# Add role as a self-grouping rule to ensure it exists
|
|
124
|
+
# This is a common pattern to "register" roles
|
|
125
|
+
await enforcer.add_grouping_policy(role, role)
|
|
126
|
+
logger.debug(f"Created default Casbin role: {role}")
|
|
127
|
+
except (AttributeError, TypeError, ValueError, RuntimeError) as e:
|
|
128
|
+
logger.warning(f"Error creating default role '{role}': {e}")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
async def initialize_casbin_from_manifest(
|
|
132
|
+
engine, app_slug: str, auth_config: Dict[str, Any]
|
|
133
|
+
) -> Optional["CasbinAdapter"]:
|
|
134
|
+
"""
|
|
135
|
+
Initialize Casbin provider from manifest configuration.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
engine: MongoDBEngine instance
|
|
139
|
+
app_slug: App slug identifier
|
|
140
|
+
auth_config: Auth configuration dict from manifest (contains auth_policy)
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
CasbinAdapter instance if successfully created, None otherwise
|
|
144
|
+
"""
|
|
145
|
+
try:
|
|
146
|
+
from .provider import CasbinAdapter
|
|
147
|
+
|
|
148
|
+
auth_policy = auth_config.get("auth_policy", {})
|
|
149
|
+
provider = auth_policy.get("provider", "casbin")
|
|
150
|
+
|
|
151
|
+
# Only proceed if provider is casbin
|
|
152
|
+
if provider != "casbin":
|
|
153
|
+
return None
|
|
154
|
+
|
|
155
|
+
# Get authorization config
|
|
156
|
+
authorization = auth_policy.get("authorization", {})
|
|
157
|
+
model = authorization.get("model", "rbac")
|
|
158
|
+
policies_collection = authorization.get(
|
|
159
|
+
"policies_collection", "casbin_policies"
|
|
160
|
+
)
|
|
161
|
+
default_roles = authorization.get("default_roles", [])
|
|
162
|
+
|
|
163
|
+
# Get database from engine
|
|
164
|
+
db = engine.get_database()
|
|
165
|
+
|
|
166
|
+
# Create enforcer
|
|
167
|
+
enforcer = await create_casbin_enforcer(
|
|
168
|
+
db=db,
|
|
169
|
+
model=model,
|
|
170
|
+
policies_collection=policies_collection,
|
|
171
|
+
default_roles=default_roles,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# Create adapter
|
|
175
|
+
adapter = CasbinAdapter(enforcer)
|
|
176
|
+
|
|
177
|
+
logger.info(f"Casbin provider initialized for app '{app_slug}'")
|
|
178
|
+
|
|
179
|
+
return adapter
|
|
180
|
+
|
|
181
|
+
except ImportError as e:
|
|
182
|
+
logger.warning(
|
|
183
|
+
f"Casbin not available for app '{app_slug}': {e}. "
|
|
184
|
+
"Install with: pip install mdb-engine[casbin]"
|
|
185
|
+
)
|
|
186
|
+
return None
|
|
187
|
+
except (
|
|
188
|
+
ImportError,
|
|
189
|
+
AttributeError,
|
|
190
|
+
TypeError,
|
|
191
|
+
ValueError,
|
|
192
|
+
RuntimeError,
|
|
193
|
+
KeyError,
|
|
194
|
+
) as e:
|
|
195
|
+
logger.error(
|
|
196
|
+
f"Error initializing Casbin provider for app '{app_slug}': {e}",
|
|
197
|
+
exc_info=True,
|
|
198
|
+
)
|
|
199
|
+
return None
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Default Casbin Models
|
|
3
|
+
|
|
4
|
+
Provides default Casbin model configurations for authorization.
|
|
5
|
+
|
|
6
|
+
This module is part of MDB_ENGINE - MongoDB Engine.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# Default RBAC (Role-Based Access Control) model
|
|
10
|
+
# This model supports:
|
|
11
|
+
# - Subject (user/role) -> Object (resource) -> Action (permission)
|
|
12
|
+
# - Role inheritance via g (grouping) rules
|
|
13
|
+
# - Policy effect: allow if any policy matches
|
|
14
|
+
|
|
15
|
+
DEFAULT_RBAC_MODEL = """
|
|
16
|
+
[request_definition]
|
|
17
|
+
r = sub, obj, act
|
|
18
|
+
|
|
19
|
+
[policy_definition]
|
|
20
|
+
p = sub, obj, act
|
|
21
|
+
|
|
22
|
+
[role_definition]
|
|
23
|
+
g = _, _
|
|
24
|
+
|
|
25
|
+
[policy_effect]
|
|
26
|
+
e = some(where (p.eft == allow))
|
|
27
|
+
|
|
28
|
+
[matchers]
|
|
29
|
+
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
# Alternative: Simple ACL model (no roles)
|
|
33
|
+
# Use this if you don't need role-based access control
|
|
34
|
+
SIMPLE_ACL_MODEL = """
|
|
35
|
+
[request_definition]
|
|
36
|
+
r = sub, obj, act
|
|
37
|
+
|
|
38
|
+
[policy_definition]
|
|
39
|
+
p = sub, obj, act
|
|
40
|
+
|
|
41
|
+
[policy_effect]
|
|
42
|
+
e = some(where (p.eft == allow))
|
|
43
|
+
|
|
44
|
+
[matchers]
|
|
45
|
+
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
|
|
46
|
+
"""
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Authentication Configuration Defaults
|
|
3
|
+
|
|
4
|
+
Centralized default values for all authentication and security configurations.
|
|
5
|
+
This module provides a single source of truth for all config defaults.
|
|
6
|
+
|
|
7
|
+
This module is part of MDB_ENGINE - MongoDB Engine.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Any, Dict
|
|
11
|
+
|
|
12
|
+
SECURITY_CONFIG_DEFAULTS: Dict[str, Any] = {
|
|
13
|
+
"password_policy": {
|
|
14
|
+
"allow_plain_text": False,
|
|
15
|
+
"min_length": 8,
|
|
16
|
+
"require_uppercase": True,
|
|
17
|
+
"require_lowercase": True,
|
|
18
|
+
"require_numbers": True,
|
|
19
|
+
"require_special": False,
|
|
20
|
+
},
|
|
21
|
+
"session_fingerprinting": {
|
|
22
|
+
"enabled": True,
|
|
23
|
+
"validate_on_login": True,
|
|
24
|
+
"validate_on_refresh": True,
|
|
25
|
+
"validate_on_request": False,
|
|
26
|
+
"strict_mode": False,
|
|
27
|
+
},
|
|
28
|
+
"account_lockout": {
|
|
29
|
+
"enabled": True,
|
|
30
|
+
"max_failed_attempts": 5,
|
|
31
|
+
"lockout_duration_seconds": 900,
|
|
32
|
+
"reset_on_success": True,
|
|
33
|
+
},
|
|
34
|
+
"ip_validation": {"enabled": False, "strict": False, "allow_ip_change": True},
|
|
35
|
+
"token_fingerprinting": {"enabled": True, "bind_to_device": True},
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
TOKEN_MANAGEMENT_DEFAULTS: Dict[str, Any] = {
|
|
39
|
+
"enabled": True,
|
|
40
|
+
"access_token_ttl": 900,
|
|
41
|
+
"refresh_token_ttl": 604800,
|
|
42
|
+
"token_rotation": True,
|
|
43
|
+
"max_sessions_per_user": 10,
|
|
44
|
+
"session_inactivity_timeout": 1800,
|
|
45
|
+
"auto_setup": True,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
CORS_DEFAULTS: Dict[str, Any] = {
|
|
49
|
+
"enabled": False,
|
|
50
|
+
"allow_origins": ["*"],
|
|
51
|
+
"allow_credentials": False,
|
|
52
|
+
"allow_methods": ["GET", "POST", "PUT", "DELETE", "PATCH"],
|
|
53
|
+
"allow_headers": ["*"],
|
|
54
|
+
"max_age": 3600,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
OBSERVABILITY_DEFAULTS: Dict[str, Any] = {
|
|
58
|
+
"health_checks": {"enabled": True, "endpoint": "/health", "interval_seconds": 30},
|
|
59
|
+
"metrics": {
|
|
60
|
+
"enabled": True,
|
|
61
|
+
"collect_operation_metrics": True,
|
|
62
|
+
"collect_performance_metrics": True,
|
|
63
|
+
"custom_metrics": [],
|
|
64
|
+
},
|
|
65
|
+
"logging": {
|
|
66
|
+
"level": "INFO",
|
|
67
|
+
"format": "json",
|
|
68
|
+
"include_request_id": True,
|
|
69
|
+
"log_sensitive_data": False,
|
|
70
|
+
},
|
|
71
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Authentication Config Helper Utilities
|
|
3
|
+
|
|
4
|
+
Type-safe utility functions for accessing and merging authentication configurations
|
|
5
|
+
from manifest.json and app.state.
|
|
6
|
+
|
|
7
|
+
This module is part of MDB_ENGINE - MongoDB Engine.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from typing import Any, Dict
|
|
12
|
+
|
|
13
|
+
from fastapi import Request
|
|
14
|
+
|
|
15
|
+
from .config_defaults import (CORS_DEFAULTS, OBSERVABILITY_DEFAULTS,
|
|
16
|
+
SECURITY_CONFIG_DEFAULTS,
|
|
17
|
+
TOKEN_MANAGEMENT_DEFAULTS)
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def merge_config_with_defaults(
|
|
23
|
+
user_config: Dict[str, Any], defaults: Dict[str, Any]
|
|
24
|
+
) -> Dict[str, Any]:
|
|
25
|
+
"""
|
|
26
|
+
Deep merge user config with defaults.
|
|
27
|
+
|
|
28
|
+
User config values take precedence over defaults. Nested dictionaries
|
|
29
|
+
are merged recursively.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
user_config: User-provided configuration (from manifest)
|
|
33
|
+
defaults: Default configuration values
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Merged configuration dictionary
|
|
37
|
+
"""
|
|
38
|
+
if not user_config:
|
|
39
|
+
return defaults.copy()
|
|
40
|
+
|
|
41
|
+
if not defaults:
|
|
42
|
+
return user_config.copy()
|
|
43
|
+
|
|
44
|
+
merged = defaults.copy()
|
|
45
|
+
|
|
46
|
+
for key, value in user_config.items():
|
|
47
|
+
if key in merged and isinstance(merged[key], dict) and isinstance(value, dict):
|
|
48
|
+
merged[key] = merge_config_with_defaults(value, merged[key])
|
|
49
|
+
else:
|
|
50
|
+
merged[key] = value
|
|
51
|
+
|
|
52
|
+
return merged
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def get_security_config(request: Request) -> Dict[str, Any]:
|
|
56
|
+
"""
|
|
57
|
+
Get security configuration from app.state with defaults merged.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
request: FastAPI Request object
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Security configuration dictionary with defaults applied
|
|
64
|
+
"""
|
|
65
|
+
try:
|
|
66
|
+
security_config = getattr(request.app.state, "security_config", None)
|
|
67
|
+
if security_config:
|
|
68
|
+
return merge_config_with_defaults(security_config, SECURITY_CONFIG_DEFAULTS)
|
|
69
|
+
return SECURITY_CONFIG_DEFAULTS.copy()
|
|
70
|
+
except (AttributeError, TypeError, KeyError) as e:
|
|
71
|
+
logger.warning(f"Error getting security config: {e}, using defaults")
|
|
72
|
+
return SECURITY_CONFIG_DEFAULTS.copy()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_password_policy(request: Request) -> Dict[str, Any]:
|
|
76
|
+
"""
|
|
77
|
+
Get password policy configuration with defaults merged.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
request: FastAPI Request object
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Password policy configuration dictionary
|
|
84
|
+
"""
|
|
85
|
+
security_config = get_security_config(request)
|
|
86
|
+
return security_config.get(
|
|
87
|
+
"password_policy", SECURITY_CONFIG_DEFAULTS["password_policy"].copy()
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def get_session_fingerprinting_config(request: Request) -> Dict[str, Any]:
|
|
92
|
+
"""
|
|
93
|
+
Get session fingerprinting configuration with defaults merged.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
request: FastAPI Request object
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Session fingerprinting configuration dictionary
|
|
100
|
+
"""
|
|
101
|
+
security_config = get_security_config(request)
|
|
102
|
+
return security_config.get(
|
|
103
|
+
"session_fingerprinting",
|
|
104
|
+
SECURITY_CONFIG_DEFAULTS["session_fingerprinting"].copy(),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_account_lockout_config(request: Request) -> Dict[str, Any]:
|
|
109
|
+
"""
|
|
110
|
+
Get account lockout configuration with defaults merged.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
request: FastAPI Request object
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Account lockout configuration dictionary
|
|
117
|
+
"""
|
|
118
|
+
security_config = get_security_config(request)
|
|
119
|
+
return security_config.get(
|
|
120
|
+
"account_lockout", SECURITY_CONFIG_DEFAULTS["account_lockout"].copy()
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def get_ip_validation_config(request: Request) -> Dict[str, Any]:
|
|
125
|
+
"""
|
|
126
|
+
Get IP validation configuration with defaults merged.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
request: FastAPI Request object
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
IP validation configuration dictionary
|
|
133
|
+
"""
|
|
134
|
+
security_config = get_security_config(request)
|
|
135
|
+
return security_config.get(
|
|
136
|
+
"ip_validation", SECURITY_CONFIG_DEFAULTS["ip_validation"].copy()
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def get_token_fingerprinting_config(request: Request) -> Dict[str, Any]:
|
|
141
|
+
"""
|
|
142
|
+
Get token fingerprinting configuration with defaults merged.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
request: FastAPI Request object
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Token fingerprinting configuration dictionary
|
|
149
|
+
"""
|
|
150
|
+
security_config = get_security_config(request)
|
|
151
|
+
return security_config.get(
|
|
152
|
+
"token_fingerprinting", SECURITY_CONFIG_DEFAULTS["token_fingerprinting"].copy()
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def get_token_management_config(request: Request) -> Dict[str, Any]:
|
|
157
|
+
"""
|
|
158
|
+
Get token management configuration from app.state with defaults merged.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
request: FastAPI Request object
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Token management configuration dictionary with defaults applied
|
|
165
|
+
"""
|
|
166
|
+
try:
|
|
167
|
+
token_config = getattr(request.app.state, "token_management_config", None)
|
|
168
|
+
if token_config:
|
|
169
|
+
return merge_config_with_defaults(token_config, TOKEN_MANAGEMENT_DEFAULTS)
|
|
170
|
+
return TOKEN_MANAGEMENT_DEFAULTS.copy()
|
|
171
|
+
except (AttributeError, TypeError, KeyError) as e:
|
|
172
|
+
logger.warning(f"Error getting token management config: {e}, using defaults")
|
|
173
|
+
return TOKEN_MANAGEMENT_DEFAULTS.copy()
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def get_cors_config(request: Request) -> Dict[str, Any]:
|
|
177
|
+
"""
|
|
178
|
+
Get CORS configuration from app.state with defaults merged.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
request: FastAPI Request object
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
CORS configuration dictionary with defaults applied
|
|
185
|
+
"""
|
|
186
|
+
try:
|
|
187
|
+
cors_config = getattr(request.app.state, "cors_config", None)
|
|
188
|
+
if cors_config:
|
|
189
|
+
return merge_config_with_defaults(cors_config, CORS_DEFAULTS)
|
|
190
|
+
return CORS_DEFAULTS.copy()
|
|
191
|
+
except (AttributeError, TypeError, KeyError) as e:
|
|
192
|
+
logger.warning(f"Error getting CORS config: {e}, using defaults")
|
|
193
|
+
return CORS_DEFAULTS.copy()
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def get_observability_config(request: Request) -> Dict[str, Any]:
|
|
197
|
+
"""
|
|
198
|
+
Get observability configuration from app.state with defaults merged.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
request: FastAPI Request object
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
Observability configuration dictionary with defaults applied
|
|
205
|
+
"""
|
|
206
|
+
try:
|
|
207
|
+
obs_config = getattr(request.app.state, "observability_config", None)
|
|
208
|
+
if obs_config:
|
|
209
|
+
return merge_config_with_defaults(obs_config, OBSERVABILITY_DEFAULTS)
|
|
210
|
+
return OBSERVABILITY_DEFAULTS.copy()
|
|
211
|
+
except (AttributeError, TypeError, KeyError) as e:
|
|
212
|
+
logger.warning(f"Error getting observability config: {e}, using defaults")
|
|
213
|
+
return OBSERVABILITY_DEFAULTS.copy()
|