quantum-framework 0.9.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.
- quantum/__init__.py +1 -0
- quantum/cli/__init__.py +2 -0
- quantum/cli/commands/__init__.py +15 -0
- quantum/cli/commands/build.py +417 -0
- quantum/cli/commands/dev.py +185 -0
- quantum/cli/commands/docs.py +329 -0
- quantum/cli/commands/lint.py +523 -0
- quantum/cli/commands/migrate.py +527 -0
- quantum/cli/commands/new.py +622 -0
- quantum/cli/commands/serve.py +190 -0
- quantum/cli/commands/test.py +193 -0
- quantum/cli/deploy.py +810 -0
- quantum/cli/hot_reload.py +951 -0
- quantum/cli/jobs.py +356 -0
- quantum/cli/mq.py +582 -0
- quantum/cli/pkg.py +390 -0
- quantum/cli/runner.py +547 -0
- quantum/cli/server_process.py +159 -0
- quantum/cli/utils.py +334 -0
- quantum/compiler/__init__.py +30 -0
- quantum/compiler/base_generator.py +367 -0
- quantum/compiler/cli.py +295 -0
- quantum/compiler/expression_transformer.py +444 -0
- quantum/compiler/javascript/__init__.py +10 -0
- quantum/compiler/javascript/generator.py +659 -0
- quantum/compiler/optimizer.py +270 -0
- quantum/compiler/python/__init__.py +10 -0
- quantum/compiler/python/generator.py +883 -0
- quantum/compiler/python/runtime.py +863 -0
- quantum/compiler/transpiler.py +330 -0
- quantum/core/__init__.py +3 -0
- quantum/core/ast_nodes.py +2611 -0
- quantum/core/expression_diagnostics.py +87 -0
- quantum/core/expression_stdlib.py +148 -0
- quantum/core/expressions.py +591 -0
- quantum/core/features/agents/src/__init__.py +30 -0
- quantum/core/features/agents/src/ast_node.py +540 -0
- quantum/core/features/conditionals/src/__init__.py +8 -0
- quantum/core/features/conditionals/src/ast_node.py +68 -0
- quantum/core/features/data_fetching/src/__init__.py +21 -0
- quantum/core/features/data_fetching/src/ast_node.py +312 -0
- quantum/core/features/data_fetching/src/desktop_adapter.py +351 -0
- quantum/core/features/data_fetching/src/html_adapter.py +474 -0
- quantum/core/features/data_fetching/src/parser.py +225 -0
- quantum/core/features/data_import/src/__init__.py +0 -0
- quantum/core/features/data_import/src/ast_node.py +291 -0
- quantum/core/features/data_import/src/runtime.py +538 -0
- quantum/core/features/dump/src/__init__.py +12 -0
- quantum/core/features/dump/src/ast_node.py +106 -0
- quantum/core/features/dump/src/parser.py +61 -0
- quantum/core/features/dump/src/runtime.py +246 -0
- quantum/core/features/functions/src/__init__.py +8 -0
- quantum/core/features/functions/src/ast_node.py +149 -0
- quantum/core/features/game_engine_2d/src/__init__.py +19 -0
- quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
- quantum/core/features/game_engine_2d/src/parser.py +983 -0
- quantum/core/features/invocation/src/__init__.py +0 -0
- quantum/core/features/invocation/src/ast_node.py +146 -0
- quantum/core/features/invocation/src/runtime.py +327 -0
- quantum/core/features/knowledge_base/src/__init__.py +6 -0
- quantum/core/features/knowledge_base/src/ast_node.py +113 -0
- quantum/core/features/knowledge_base/src/parser.py +82 -0
- quantum/core/features/logging/src/__init__.py +12 -0
- quantum/core/features/logging/src/ast_node.py +111 -0
- quantum/core/features/logging/src/parser.py +50 -0
- quantum/core/features/logging/src/runtime.py +190 -0
- quantum/core/features/loops/src/__init__.py +8 -0
- quantum/core/features/loops/src/ast_node.py +60 -0
- quantum/core/features/query/src/__init__.py +0 -0
- quantum/core/features/query/src/database_service.py +322 -0
- quantum/core/features/query/src/query_validators.py +20 -0
- quantum/core/features/state_management/src/__init__.py +11 -0
- quantum/core/features/state_management/src/ast_node.py +228 -0
- quantum/core/features/terminal_engine/src/__init__.py +21 -0
- quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
- quantum/core/features/terminal_engine/src/parser.py +361 -0
- quantum/core/features/testing_engine/src/__init__.py +41 -0
- quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
- quantum/core/features/testing_engine/src/parser.py +604 -0
- quantum/core/features/theming/src/__init__.py +48 -0
- quantum/core/features/theming/src/ast_node.py +137 -0
- quantum/core/features/theming/src/presets.py +405 -0
- quantum/core/features/ui_engine/src/__init__.py +20 -0
- quantum/core/features/ui_engine/src/ast_nodes.py +1854 -0
- quantum/core/features/ui_engine/src/parser.py +1106 -0
- quantum/core/features/websocket/src/__init__.py +24 -0
- quantum/core/features/websocket/src/ast_node.py +247 -0
- quantum/core/html_compat.py +299 -0
- quantum/core/parser.py +1235 -0
- quantum/core/parser_registry.py +213 -0
- quantum/core/parsers/__init__.py +76 -0
- quantum/core/parsers/ai/__init__.py +12 -0
- quantum/core/parsers/ai/agent_parser.py +106 -0
- quantum/core/parsers/ai/knowledge_parser.py +86 -0
- quantum/core/parsers/ai/llm_parser.py +78 -0
- quantum/core/parsers/ai/team_parser.py +88 -0
- quantum/core/parsers/base.py +322 -0
- quantum/core/parsers/composition/__init__.py +10 -0
- quantum/core/parsers/composition/import_parser.py +50 -0
- quantum/core/parsers/composition/slot_parser.py +48 -0
- quantum/core/parsers/control_flow/__init__.py +11 -0
- quantum/core/parsers/control_flow/if_parser.py +68 -0
- quantum/core/parsers/control_flow/loop_parser.py +105 -0
- quantum/core/parsers/control_flow/set_parser.py +89 -0
- quantum/core/parsers/data/__init__.py +12 -0
- quantum/core/parsers/data/data_parser.py +217 -0
- quantum/core/parsers/data/invoke_parser.py +116 -0
- quantum/core/parsers/data/query_parser.py +188 -0
- quantum/core/parsers/data/transaction_parser.py +77 -0
- quantum/core/parsers/events/__init__.py +9 -0
- quantum/core/parsers/events/dispatch_event_parser.py +44 -0
- quantum/core/parsers/forms/__init__.py +11 -0
- quantum/core/parsers/forms/action_parser.py +54 -0
- quantum/core/parsers/forms/flash_parser.py +34 -0
- quantum/core/parsers/forms/redirect_parser.py +33 -0
- quantum/core/parsers/functions/__init__.py +11 -0
- quantum/core/parsers/functions/function_parser.py +137 -0
- quantum/core/parsers/functions/param_parser.py +66 -0
- quantum/core/parsers/functions/return_parser.py +31 -0
- quantum/core/parsers/html/__init__.py +10 -0
- quantum/core/parsers/html/component_call_parser.py +130 -0
- quantum/core/parsers/html/html_parser.py +115 -0
- quantum/core/parsers/jobs/__init__.py +11 -0
- quantum/core/parsers/jobs/job_parser.py +71 -0
- quantum/core/parsers/jobs/schedule_parser.py +62 -0
- quantum/core/parsers/jobs/thread_parser.py +57 -0
- quantum/core/parsers/messaging/__init__.py +17 -0
- quantum/core/parsers/messaging/message_ack_parser.py +30 -0
- quantum/core/parsers/messaging/message_nack_parser.py +30 -0
- quantum/core/parsers/messaging/message_parser.py +114 -0
- quantum/core/parsers/messaging/queue_parser.py +61 -0
- quantum/core/parsers/messaging/websocket_parser.py +121 -0
- quantum/core/parsers/persistence/__init__.py +9 -0
- quantum/core/parsers/persistence/persist_parser.py +64 -0
- quantum/core/parsers/routing/__init__.py +9 -0
- quantum/core/parsers/routing/route_parser.py +41 -0
- quantum/core/parsers/scripting/__init__.py +12 -0
- quantum/core/parsers/scripting/pyclass_parser.py +62 -0
- quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
- quantum/core/parsers/scripting/pyimport_parser.py +52 -0
- quantum/core/parsers/scripting/python_parser.py +49 -0
- quantum/core/parsers/services/__init__.py +12 -0
- quantum/core/parsers/services/dump_parser.py +52 -0
- quantum/core/parsers/services/file_parser.py +48 -0
- quantum/core/parsers/services/log_parser.py +46 -0
- quantum/core/parsers/services/mail_parser.py +65 -0
- quantum/core/tiers.py +82 -0
- quantum/packages/__init__.py +28 -0
- quantum/packages/manager.py +413 -0
- quantum/packages/manifest.py +351 -0
- quantum/packages/registry.py +399 -0
- quantum/packages/resolver.py +336 -0
- quantum/plugins/__init__.py +33 -0
- quantum/plugins/hooks.py +329 -0
- quantum/plugins/loader.py +479 -0
- quantum/plugins/manifest.py +336 -0
- quantum/plugins/registry.py +371 -0
- quantum/runtime/__init__.py +28 -0
- quantum/runtime/action_handler.py +443 -0
- quantum/runtime/adapters/__init__.py +88 -0
- quantum/runtime/adapters/memory_adapter.py +690 -0
- quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
- quantum/runtime/adapters/redis_adapter.py +582 -0
- quantum/runtime/adapters/sqlite_adapter.py +414 -0
- quantum/runtime/agent_service.py +1133 -0
- quantum/runtime/api_server.py +86 -0
- quantum/runtime/ast_cache.py +506 -0
- quantum/runtime/auth_service.py +267 -0
- quantum/runtime/component.py +990 -0
- quantum/runtime/component_composer.py +319 -0
- quantum/runtime/component_resolver.py +174 -0
- quantum/runtime/database_service.py +598 -0
- quantum/runtime/email_service.py +162 -0
- quantum/runtime/error_handler.py +295 -0
- quantum/runtime/execution_context.py +286 -0
- quantum/runtime/executor_registry.py +171 -0
- quantum/runtime/executors/__init__.py +71 -0
- quantum/runtime/executors/ai/__init__.py +12 -0
- quantum/runtime/executors/ai/agent_executor.py +217 -0
- quantum/runtime/executors/ai/knowledge_executor.py +114 -0
- quantum/runtime/executors/ai/llm_executor.py +153 -0
- quantum/runtime/executors/ai/team_executor.py +171 -0
- quantum/runtime/executors/base.py +262 -0
- quantum/runtime/executors/control_flow/__init__.py +11 -0
- quantum/runtime/executors/control_flow/if_executor.py +93 -0
- quantum/runtime/executors/control_flow/loop_executor.py +307 -0
- quantum/runtime/executors/control_flow/set_executor.py +412 -0
- quantum/runtime/executors/data/__init__.py +12 -0
- quantum/runtime/executors/data/data_executor.py +145 -0
- quantum/runtime/executors/data/invoke_executor.py +176 -0
- quantum/runtime/executors/data/query_executor.py +256 -0
- quantum/runtime/executors/data/transaction_executor.py +91 -0
- quantum/runtime/executors/jobs/__init__.py +11 -0
- quantum/runtime/executors/jobs/job_executor.py +190 -0
- quantum/runtime/executors/jobs/schedule_executor.py +132 -0
- quantum/runtime/executors/jobs/thread_executor.py +127 -0
- quantum/runtime/executors/messaging/__init__.py +17 -0
- quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
- quantum/runtime/executors/messaging/message_executor.py +174 -0
- quantum/runtime/executors/messaging/queue_executor.py +103 -0
- quantum/runtime/executors/messaging/websocket_executor.py +197 -0
- quantum/runtime/executors/scripting/__init__.py +11 -0
- quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
- quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
- quantum/runtime/executors/scripting/python_executor.py +249 -0
- quantum/runtime/executors/services/__init__.py +12 -0
- quantum/runtime/executors/services/dump_executor.py +72 -0
- quantum/runtime/executors/services/file_executor.py +89 -0
- quantum/runtime/executors/services/log_executor.py +77 -0
- quantum/runtime/executors/services/mail_executor.py +81 -0
- quantum/runtime/expression_cache.py +498 -0
- quantum/runtime/file_upload_service.py +326 -0
- quantum/runtime/function_registry.py +118 -0
- quantum/runtime/game_builder.py +166 -0
- quantum/runtime/game_code_generator.py +2371 -0
- quantum/runtime/game_templates.py +2006 -0
- quantum/runtime/godot_code_generator.py +4681 -0
- quantum/runtime/godot_templates.py +1449 -0
- quantum/runtime/job_executor.py +1599 -0
- quantum/runtime/knowledge_service.py +500 -0
- quantum/runtime/llm_cache.py +100 -0
- quantum/runtime/llm_providers.py +704 -0
- quantum/runtime/llm_service.py +287 -0
- quantum/runtime/logging_setup.py +140 -0
- quantum/runtime/message_broker.py +364 -0
- quantum/runtime/message_queue_service.py +571 -0
- quantum/runtime/param_validation.py +184 -0
- quantum/runtime/pypy_compat.py +315 -0
- quantum/runtime/python_bridge.py +698 -0
- quantum/runtime/query_validators.py +304 -0
- quantum/runtime/renderer.py +733 -0
- quantum/runtime/service_container.py +444 -0
- quantum/runtime/terminal_builder.py +76 -0
- quantum/runtime/terminal_code_generator.py +607 -0
- quantum/runtime/terminal_templates.py +243 -0
- quantum/runtime/testing_builder.py +77 -0
- quantum/runtime/testing_code_generator.py +833 -0
- quantum/runtime/testing_templates.py +85 -0
- quantum/runtime/ui_builder.py +188 -0
- quantum/runtime/ui_desktop_adapter.py +1730 -0
- quantum/runtime/ui_desktop_templates.py +307 -0
- quantum/runtime/ui_html_adapter.py +2691 -0
- quantum/runtime/ui_html_templates.py +2297 -0
- quantum/runtime/ui_mobile_adapter.py +1832 -0
- quantum/runtime/ui_mobile_templates.py +1003 -0
- quantum/runtime/ui_textual_adapter.py +1866 -0
- quantum/runtime/ui_textual_templates.py +45 -0
- quantum/runtime/ui_tokens.py +465 -0
- quantum/runtime/ui_validator.py +365 -0
- quantum/runtime/validators.py +256 -0
- quantum/runtime/web_server.py +1766 -0
- quantum/runtime/websocket_adapter.py +501 -0
- quantum/runtime/websocket_service.py +585 -0
- quantum/runtime/websocket_transport.py +289 -0
- quantum/runtime/wsgi.py +101 -0
- quantum/utils/__init__.py +1 -0
- quantum_framework-0.9.0.dist-info/METADATA +244 -0
- quantum_framework-0.9.0.dist-info/RECORD +262 -0
- quantum_framework-0.9.0.dist-info/WHEEL +5 -0
- quantum_framework-0.9.0.dist-info/entry_points.txt +2 -0
- quantum_framework-0.9.0.dist-info/licenses/LICENSE +21 -0
- quantum_framework-0.9.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Phase G: Authentication & Security Service
|
|
3
|
+
|
|
4
|
+
Provides password hashing, authentication, and RBAC functionality.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import bcrypt
|
|
8
|
+
from typing import Optional, List, Dict, Any
|
|
9
|
+
from datetime import datetime, timedelta
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AuthService:
|
|
13
|
+
"""Authentication and security service for Quantum framework"""
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def hash_password(password: str) -> str:
|
|
17
|
+
"""
|
|
18
|
+
Hash a password using bcrypt
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
password: Plain text password
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Hashed password (bcrypt hash)
|
|
25
|
+
"""
|
|
26
|
+
if not password:
|
|
27
|
+
raise ValueError("Password cannot be empty")
|
|
28
|
+
|
|
29
|
+
# Generate salt and hash password
|
|
30
|
+
salt = bcrypt.gensalt()
|
|
31
|
+
hashed = bcrypt.hashpw(password.encode('utf-8'), salt)
|
|
32
|
+
|
|
33
|
+
return hashed.decode('utf-8')
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def verify_password(password: str, hashed: str) -> bool:
|
|
37
|
+
"""
|
|
38
|
+
Verify a password against a bcrypt hash
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
password: Plain text password to verify
|
|
42
|
+
hashed: Bcrypt hash to check against
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
True if password matches, False otherwise
|
|
46
|
+
"""
|
|
47
|
+
if not password or not hashed:
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
return bcrypt.checkpw(password.encode('utf-8'), hashed.encode('utf-8'))
|
|
52
|
+
except Exception:
|
|
53
|
+
return False
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def is_authenticated(session_data: Dict[str, Any]) -> bool:
|
|
57
|
+
"""
|
|
58
|
+
Check if user is authenticated based on session data
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
session_data: Session dictionary
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
True if authenticated, False otherwise
|
|
65
|
+
"""
|
|
66
|
+
return AuthService._is_true(session_data.get('authenticated'))
|
|
67
|
+
|
|
68
|
+
# The set of values that count as "yes". Deliberately CLOSED: anything not
|
|
69
|
+
# listed is false, so this stays fail-closed. In particular '' , None, 0,
|
|
70
|
+
# 'false', 'no' and any unrecognised string are NOT authenticated.
|
|
71
|
+
_TRUTHY = frozenset({'true', '1', 'yes', 'on'})
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def _is_true(value: Any) -> bool:
|
|
75
|
+
"""Whether a session flag means yes.
|
|
76
|
+
|
|
77
|
+
This used to be `value is True`, requiring the Python boolean. But a
|
|
78
|
+
.q attribute is a string: the framework's own components/login.q line
|
|
79
|
+
19 writes
|
|
80
|
+
|
|
81
|
+
<q:set name="session.authenticated" value="true" />
|
|
82
|
+
|
|
83
|
+
which stores the STRING "true" unless the author remembered
|
|
84
|
+
type="boolean". "true" is not True, so is_authenticated returned False
|
|
85
|
+
for a user who had just logged in successfully, and require_auth
|
|
86
|
+
bounced them back to /login forever.
|
|
87
|
+
|
|
88
|
+
Same root cause as the q:set arithmetic bug: the engine stores strings
|
|
89
|
+
and the consumer demanded a strict type. Accepting the written forms
|
|
90
|
+
fixes the login without widening what counts as authenticated —
|
|
91
|
+
the list is closed and everything else is false.
|
|
92
|
+
"""
|
|
93
|
+
if isinstance(value, bool):
|
|
94
|
+
return value
|
|
95
|
+
if isinstance(value, str):
|
|
96
|
+
return value.strip().lower() in AuthService._TRUTHY
|
|
97
|
+
return value is True
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def get_user_id(session_data: Dict[str, Any]) -> Optional[int]:
|
|
101
|
+
"""
|
|
102
|
+
Get authenticated user ID from session
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
session_data: Session dictionary
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
User ID if authenticated, None otherwise
|
|
109
|
+
"""
|
|
110
|
+
if not AuthService.is_authenticated(session_data):
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
return session_data.get('userId')
|
|
114
|
+
|
|
115
|
+
@staticmethod
|
|
116
|
+
def get_user_role(session_data: Dict[str, Any]) -> Optional[str]:
|
|
117
|
+
"""
|
|
118
|
+
Get authenticated user role from session
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
session_data: Session dictionary
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
User role if authenticated, None otherwise
|
|
125
|
+
"""
|
|
126
|
+
if not AuthService.is_authenticated(session_data):
|
|
127
|
+
return None
|
|
128
|
+
|
|
129
|
+
return session_data.get('userRole')
|
|
130
|
+
|
|
131
|
+
@staticmethod
|
|
132
|
+
def has_permission(session_data: Dict[str, Any], required_permission: str) -> bool:
|
|
133
|
+
"""
|
|
134
|
+
Check if user has a specific permission
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
session_data: Session dictionary
|
|
138
|
+
required_permission: Permission to check (e.g., "admin", "user", "editor")
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
True if user has permission, False otherwise
|
|
142
|
+
"""
|
|
143
|
+
if not AuthService.is_authenticated(session_data):
|
|
144
|
+
return False
|
|
145
|
+
|
|
146
|
+
user_permissions = session_data.get('permissions', [])
|
|
147
|
+
|
|
148
|
+
# If permissions is a string, convert to list
|
|
149
|
+
if isinstance(user_permissions, str):
|
|
150
|
+
user_permissions = [user_permissions]
|
|
151
|
+
|
|
152
|
+
return required_permission in user_permissions
|
|
153
|
+
|
|
154
|
+
@staticmethod
|
|
155
|
+
def has_role(session_data: Dict[str, Any], required_role: str) -> bool:
|
|
156
|
+
"""
|
|
157
|
+
Check if user has a specific role
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
session_data: Session dictionary
|
|
161
|
+
required_role: Role to check (e.g., "admin", "user", "editor")
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
True if user has role, False otherwise
|
|
165
|
+
"""
|
|
166
|
+
if not AuthService.is_authenticated(session_data):
|
|
167
|
+
return False
|
|
168
|
+
|
|
169
|
+
user_role = session_data.get('userRole', '')
|
|
170
|
+
|
|
171
|
+
# Support multiple roles separated by comma
|
|
172
|
+
if ',' in required_role:
|
|
173
|
+
required_roles = [r.strip() for r in required_role.split(',')]
|
|
174
|
+
return user_role in required_roles
|
|
175
|
+
|
|
176
|
+
return user_role == required_role
|
|
177
|
+
|
|
178
|
+
@staticmethod
|
|
179
|
+
def login(session_data: Dict[str, Any], user_id: int, user_name: str,
|
|
180
|
+
user_role: str = "user", permissions: List[str] = None,
|
|
181
|
+
remember_me: bool = False) -> Dict[str, Any]:
|
|
182
|
+
"""
|
|
183
|
+
Login user and set session data
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
session_data: Session dictionary to update
|
|
187
|
+
user_id: User ID
|
|
188
|
+
user_name: User name
|
|
189
|
+
user_role: User role (default: "user")
|
|
190
|
+
permissions: List of permissions (optional)
|
|
191
|
+
remember_me: Whether to remember user (extended session)
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
Updated session data
|
|
195
|
+
"""
|
|
196
|
+
session_data['authenticated'] = True
|
|
197
|
+
session_data['userId'] = user_id
|
|
198
|
+
session_data['userName'] = user_name
|
|
199
|
+
session_data['userRole'] = user_role
|
|
200
|
+
session_data['loginTime'] = datetime.now().isoformat()
|
|
201
|
+
|
|
202
|
+
if permissions:
|
|
203
|
+
session_data['permissions'] = permissions
|
|
204
|
+
|
|
205
|
+
if remember_me:
|
|
206
|
+
# Set extended session expiry (30 days)
|
|
207
|
+
session_data['rememberMe'] = True
|
|
208
|
+
session_data['sessionExpiry'] = (datetime.now() + timedelta(days=30)).isoformat()
|
|
209
|
+
else:
|
|
210
|
+
# Default session expiry (24 hours)
|
|
211
|
+
session_data['sessionExpiry'] = (datetime.now() + timedelta(hours=24)).isoformat()
|
|
212
|
+
|
|
213
|
+
return session_data
|
|
214
|
+
|
|
215
|
+
@staticmethod
|
|
216
|
+
def logout(session_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
217
|
+
"""
|
|
218
|
+
Logout user and clear session data
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
session_data: Session dictionary to clear
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Cleared session data
|
|
225
|
+
"""
|
|
226
|
+
# Clear all authentication-related session data
|
|
227
|
+
keys_to_remove = [
|
|
228
|
+
'authenticated', 'userId', 'userName', 'userRole',
|
|
229
|
+
'loginTime', 'permissions', 'rememberMe', 'sessionExpiry'
|
|
230
|
+
]
|
|
231
|
+
|
|
232
|
+
for key in keys_to_remove:
|
|
233
|
+
session_data.pop(key, None)
|
|
234
|
+
|
|
235
|
+
session_data['logoutTime'] = datetime.now().isoformat()
|
|
236
|
+
|
|
237
|
+
return session_data
|
|
238
|
+
|
|
239
|
+
@staticmethod
|
|
240
|
+
def is_session_expired(session_data: Dict[str, Any]) -> bool:
|
|
241
|
+
"""
|
|
242
|
+
Check if session has expired
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
session_data: Session dictionary
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
True if session expired, False otherwise
|
|
249
|
+
"""
|
|
250
|
+
if not AuthService.is_authenticated(session_data):
|
|
251
|
+
return True
|
|
252
|
+
|
|
253
|
+
expiry_str = session_data.get('sessionExpiry')
|
|
254
|
+
if not expiry_str:
|
|
255
|
+
# No expiry set - consider expired for safety
|
|
256
|
+
return True
|
|
257
|
+
|
|
258
|
+
try:
|
|
259
|
+
expiry_time = datetime.fromisoformat(expiry_str)
|
|
260
|
+
return datetime.now() > expiry_time
|
|
261
|
+
except Exception:
|
|
262
|
+
return True
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
class AuthorizationError(Exception):
|
|
266
|
+
"""Raised when user is not authorized to access a resource"""
|
|
267
|
+
pass
|