Navada25 commited on
Commit
a85cab8
·
verified ·
1 Parent(s): e6f46ee

Update auth_manager.py with stock analysis features

Browse files
Files changed (1) hide show
  1. auth_manager.py +309 -309
auth_manager.py CHANGED
@@ -1,310 +1,310 @@
1
- """
2
- Authentication Manager Module for NAVADA
3
- Handles user authentication, session management, and conversation storage.
4
- """
5
-
6
- import hashlib
7
- import secrets
8
- import sqlite3
9
- import json
10
- from datetime import datetime, timedelta
11
- from typing import Dict, Any, Optional, List
12
- import logging
13
-
14
- logger = logging.getLogger(__name__)
15
-
16
- class AuthManager:
17
- """Simple authentication manager with SQLite backend."""
18
-
19
- def __init__(self, db_path: str = "navada_auth.db"):
20
- self.db_path = db_path
21
- self.init_database()
22
-
23
- def init_database(self):
24
- """Initialize database tables."""
25
- try:
26
- # Use timeout to prevent hanging
27
- with sqlite3.connect(self.db_path, timeout=5.0) as conn:
28
- cursor = conn.cursor()
29
-
30
- # Set pragmas for better performance
31
- cursor.execute("PRAGMA journal_mode=WAL")
32
- cursor.execute("PRAGMA synchronous=NORMAL")
33
-
34
- # Users table
35
- cursor.execute('''
36
- CREATE TABLE IF NOT EXISTS users (
37
- user_id TEXT PRIMARY KEY,
38
- username TEXT UNIQUE NOT NULL,
39
- email TEXT UNIQUE,
40
- password_hash TEXT NOT NULL,
41
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
42
- )
43
- ''')
44
-
45
- # Sessions table
46
- cursor.execute('''
47
- CREATE TABLE IF NOT EXISTS sessions (
48
- session_token TEXT PRIMARY KEY,
49
- user_id TEXT NOT NULL,
50
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
51
- expires_at TIMESTAMP,
52
- FOREIGN KEY (user_id) REFERENCES users(user_id)
53
- )
54
- ''')
55
-
56
- # Conversations table
57
- cursor.execute('''
58
- CREATE TABLE IF NOT EXISTS conversations (
59
- conversation_id TEXT PRIMARY KEY,
60
- user_id TEXT NOT NULL,
61
- thread_id TEXT,
62
- mode TEXT,
63
- messages TEXT,
64
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
65
- FOREIGN KEY (user_id) REFERENCES users(user_id)
66
- )
67
- ''')
68
-
69
- # User actions log table
70
- cursor.execute('''
71
- CREATE TABLE IF NOT EXISTS user_actions (
72
- action_id INTEGER PRIMARY KEY AUTOINCREMENT,
73
- user_id TEXT NOT NULL,
74
- action_type TEXT,
75
- details TEXT,
76
- timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
77
- FOREIGN KEY (user_id) REFERENCES users(user_id)
78
- )
79
- ''')
80
-
81
- conn.commit()
82
- logger.info("Database tables initialized successfully")
83
- except sqlite3.OperationalError as e:
84
- if "database is locked" in str(e):
85
- logger.error("Database is locked - another process may be using it")
86
- else:
87
- logger.error(f"Database operational error: {e}")
88
- except Exception as e:
89
- logger.error(f"Database initialization error: {e}")
90
-
91
- def hash_password(self, password: str) -> str:
92
- """Hash a password using SHA256."""
93
- return hashlib.sha256(password.encode()).hexdigest()
94
-
95
- def generate_token(self) -> str:
96
- """Generate a secure random token."""
97
- return secrets.token_urlsafe(32)
98
-
99
- def register_user(self, username: str, password: str, email: Optional[str] = None) -> Dict[str, Any]:
100
- """Register a new user."""
101
- try:
102
- with sqlite3.connect(self.db_path, timeout=5.0) as conn:
103
- cursor = conn.cursor()
104
-
105
- # Check if user exists
106
- cursor.execute("SELECT username FROM users WHERE username = ?", (username,))
107
- if cursor.fetchone():
108
- return {"success": False, "message": "Username already exists"}
109
-
110
- # Create user
111
- user_id = secrets.token_urlsafe(16)
112
- password_hash = self.hash_password(password)
113
-
114
- cursor.execute('''
115
- INSERT INTO users (user_id, username, email, password_hash)
116
- VALUES (?, ?, ?, ?)
117
- ''', (user_id, username, email, password_hash))
118
-
119
- conn.commit()
120
-
121
- return {
122
- "success": True,
123
- "message": "User registered successfully",
124
- "user_id": user_id
125
- }
126
- except Exception as e:
127
- logger.error(f"Registration error: {e}")
128
- return {"success": False, "message": str(e)}
129
-
130
- def authenticate_user(self, username: str, password: str) -> Dict[str, Any]:
131
- """Authenticate a user and create a session."""
132
- try:
133
- with sqlite3.connect(self.db_path, timeout=5.0) as conn:
134
- cursor = conn.cursor()
135
-
136
- # Get user
137
- password_hash = self.hash_password(password)
138
- cursor.execute('''
139
- SELECT user_id, username FROM users
140
- WHERE username = ? AND password_hash = ?
141
- ''', (username, password_hash))
142
-
143
- user = cursor.fetchone()
144
- if not user:
145
- return {"success": False, "message": "Invalid credentials"}
146
-
147
- user_id, username = user
148
-
149
- # Create session
150
- session_token = self.generate_token()
151
- auth_token = self.generate_token()
152
- expires_at = datetime.now() + timedelta(hours=24)
153
-
154
- cursor.execute('''
155
- INSERT INTO sessions (session_token, user_id, expires_at)
156
- VALUES (?, ?, ?)
157
- ''', (session_token, user_id, expires_at))
158
-
159
- conn.commit()
160
-
161
- return {
162
- "success": True,
163
- "message": "Login successful",
164
- "user_id": user_id,
165
- "username": username,
166
- "session_token": session_token,
167
- "auth_token": auth_token
168
- }
169
- except Exception as e:
170
- logger.error(f"Authentication error: {e}")
171
- return {"success": False, "message": str(e)}
172
-
173
- def validate_session(self, session_token: str) -> Dict[str, Any]:
174
- """Validate a session token."""
175
- try:
176
- with sqlite3.connect(self.db_path, timeout=5.0) as conn:
177
- cursor = conn.cursor()
178
-
179
- cursor.execute('''
180
- SELECT s.user_id, s.expires_at, u.username
181
- FROM sessions s
182
- JOIN users u ON s.user_id = u.user_id
183
- WHERE s.session_token = ?
184
- ''', (session_token,))
185
-
186
- session = cursor.fetchone()
187
- if not session:
188
- return {"valid": False, "message": "Invalid session"}
189
-
190
- user_id, expires_at, username = session
191
-
192
- # Check expiration
193
- if datetime.fromisoformat(expires_at) < datetime.now():
194
- return {"valid": False, "message": "Session expired"}
195
-
196
- return {
197
- "valid": True,
198
- "user_id": user_id,
199
- "username": username
200
- }
201
- except Exception as e:
202
- logger.error(f"Session validation error: {e}")
203
- return {"valid": False, "message": str(e)}
204
-
205
- def logout_user(self, session_token: str) -> Dict[str, Any]:
206
- """Logout user by removing session."""
207
- try:
208
- with sqlite3.connect(self.db_path, timeout=5.0) as conn:
209
- cursor = conn.cursor()
210
- cursor.execute("DELETE FROM sessions WHERE session_token = ?", (session_token,))
211
- conn.commit()
212
- return {"success": True, "message": "Logged out successfully"}
213
- except Exception as e:
214
- logger.error(f"Logout error: {e}")
215
- return {"success": False, "message": str(e)}
216
-
217
- def save_full_conversation(self, user_id: str, thread_id: str, mode: str, messages: List[Dict]) -> Dict[str, Any]:
218
- """Save a conversation to database."""
219
- try:
220
- with sqlite3.connect(self.db_path, timeout=5.0) as conn:
221
- cursor = conn.cursor()
222
-
223
- conversation_id = secrets.token_urlsafe(16)
224
- messages_json = json.dumps(messages)
225
-
226
- cursor.execute('''
227
- INSERT INTO conversations (conversation_id, user_id, thread_id, mode, messages)
228
- VALUES (?, ?, ?, ?, ?)
229
- ''', (conversation_id, user_id, thread_id, mode, messages_json))
230
-
231
- conn.commit()
232
-
233
- return {"success": True, "conversation_id": conversation_id}
234
- except Exception as e:
235
- logger.error(f"Save conversation error: {e}")
236
- return {"success": False, "message": str(e)}
237
-
238
- def get_user_conversations(self, user_id: str, limit: int = 10) -> List[Dict[str, Any]]:
239
- """Get user's recent conversations."""
240
- try:
241
- with sqlite3.connect(self.db_path, timeout=5.0) as conn:
242
- cursor = conn.cursor()
243
-
244
- cursor.execute('''
245
- SELECT conversation_id, thread_id, mode, messages, created_at
246
- FROM conversations
247
- WHERE user_id = ?
248
- ORDER BY created_at DESC
249
- LIMIT ?
250
- ''', (user_id, limit))
251
-
252
- conversations = []
253
- for row in cursor.fetchall():
254
- conv_id, thread_id, mode, messages_json, created_at = row
255
- conversations.append({
256
- "conversation_id": conv_id,
257
- "thread_id": thread_id,
258
- "mode": mode,
259
- "messages": json.loads(messages_json) if messages_json else [],
260
- "created_at": created_at
261
- })
262
-
263
- return conversations
264
- except Exception as e:
265
- logger.error(f"Get conversations error: {e}")
266
- return []
267
-
268
- def log_user_action(self, user_id: str, action_type: str, details: Optional[Dict] = None) -> Dict[str, Any]:
269
- """Log a user action."""
270
- try:
271
- with sqlite3.connect(self.db_path, timeout=5.0) as conn:
272
- cursor = conn.cursor()
273
-
274
- details_json = json.dumps(details) if details else None
275
-
276
- cursor.execute('''
277
- INSERT INTO user_actions (user_id, action_type, details)
278
- VALUES (?, ?, ?)
279
- ''', (user_id, action_type, details_json))
280
-
281
- conn.commit()
282
-
283
- return {"success": True}
284
- except Exception as e:
285
- logger.error(f"Log action error: {e}")
286
- return {"success": False, "message": str(e)}
287
-
288
- def save_conversation(self, user_id: str, chainlit_session_id: str, role: str, content: str, persona_mode: str = None, metadata: Optional[Dict] = None) -> Dict[str, Any]:
289
- """Save individual conversation messages with chainlit session tracking."""
290
- try:
291
- # Use log_user_action for individual message logging
292
- details = {
293
- "chainlit_session_id": chainlit_session_id,
294
- "role": role,
295
- "content": content,
296
- "persona_mode": persona_mode,
297
- "metadata": metadata or {}
298
- }
299
-
300
- return self.log_user_action(
301
- user_id=user_id,
302
- action_type=f"message_{role}",
303
- details=details
304
- )
305
- except Exception as e:
306
- logger.error(f"Save conversation message error: {e}")
307
- return {"success": False, "message": str(e)}
308
-
309
- # Create singleton instance
310
  auth_manager = AuthManager()
 
1
+ """
2
+ Authentication Manager Module for NAVADA
3
+ Handles user authentication, session management, and conversation storage.
4
+ """
5
+
6
+ import hashlib
7
+ import secrets
8
+ import sqlite3
9
+ import json
10
+ from datetime import datetime, timedelta
11
+ from typing import Dict, Any, Optional, List
12
+ import logging
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ class AuthManager:
17
+ """Simple authentication manager with SQLite backend."""
18
+
19
+ def __init__(self, db_path: str = "navada_auth.db"):
20
+ self.db_path = db_path
21
+ self.init_database()
22
+
23
+ def init_database(self):
24
+ """Initialize database tables."""
25
+ try:
26
+ # Use timeout to prevent hanging
27
+ with sqlite3.connect(self.db_path, timeout=5.0) as conn:
28
+ cursor = conn.cursor()
29
+
30
+ # Set pragmas for better performance
31
+ cursor.execute("PRAGMA journal_mode=WAL")
32
+ cursor.execute("PRAGMA synchronous=NORMAL")
33
+
34
+ # Users table
35
+ cursor.execute('''
36
+ CREATE TABLE IF NOT EXISTS users (
37
+ user_id TEXT PRIMARY KEY,
38
+ username TEXT UNIQUE NOT NULL,
39
+ email TEXT UNIQUE,
40
+ password_hash TEXT NOT NULL,
41
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
42
+ )
43
+ ''')
44
+
45
+ # Sessions table
46
+ cursor.execute('''
47
+ CREATE TABLE IF NOT EXISTS sessions (
48
+ session_token TEXT PRIMARY KEY,
49
+ user_id TEXT NOT NULL,
50
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
51
+ expires_at TIMESTAMP,
52
+ FOREIGN KEY (user_id) REFERENCES users(user_id)
53
+ )
54
+ ''')
55
+
56
+ # Conversations table
57
+ cursor.execute('''
58
+ CREATE TABLE IF NOT EXISTS conversations (
59
+ conversation_id TEXT PRIMARY KEY,
60
+ user_id TEXT NOT NULL,
61
+ thread_id TEXT,
62
+ mode TEXT,
63
+ messages TEXT,
64
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
65
+ FOREIGN KEY (user_id) REFERENCES users(user_id)
66
+ )
67
+ ''')
68
+
69
+ # User actions log table
70
+ cursor.execute('''
71
+ CREATE TABLE IF NOT EXISTS user_actions (
72
+ action_id INTEGER PRIMARY KEY AUTOINCREMENT,
73
+ user_id TEXT NOT NULL,
74
+ action_type TEXT,
75
+ details TEXT,
76
+ timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
77
+ FOREIGN KEY (user_id) REFERENCES users(user_id)
78
+ )
79
+ ''')
80
+
81
+ conn.commit()
82
+ logger.info("Database tables initialized successfully")
83
+ except sqlite3.OperationalError as e:
84
+ if "database is locked" in str(e):
85
+ logger.error("Database is locked - another process may be using it")
86
+ else:
87
+ logger.error(f"Database operational error: {e}")
88
+ except Exception as e:
89
+ logger.error(f"Database initialization error: {e}")
90
+
91
+ def hash_password(self, password: str) -> str:
92
+ """Hash a password using SHA256."""
93
+ return hashlib.sha256(password.encode()).hexdigest()
94
+
95
+ def generate_token(self) -> str:
96
+ """Generate a secure random token."""
97
+ return secrets.token_urlsafe(32)
98
+
99
+ def register_user(self, username: str, password: str, email: Optional[str] = None) -> Dict[str, Any]:
100
+ """Register a new user."""
101
+ try:
102
+ with sqlite3.connect(self.db_path, timeout=5.0) as conn:
103
+ cursor = conn.cursor()
104
+
105
+ # Check if user exists
106
+ cursor.execute("SELECT username FROM users WHERE username = ?", (username,))
107
+ if cursor.fetchone():
108
+ return {"success": False, "message": "Username already exists"}
109
+
110
+ # Create user
111
+ user_id = secrets.token_urlsafe(16)
112
+ password_hash = self.hash_password(password)
113
+
114
+ cursor.execute('''
115
+ INSERT INTO users (user_id, username, email, password_hash)
116
+ VALUES (?, ?, ?, ?)
117
+ ''', (user_id, username, email, password_hash))
118
+
119
+ conn.commit()
120
+
121
+ return {
122
+ "success": True,
123
+ "message": "User registered successfully",
124
+ "user_id": user_id
125
+ }
126
+ except Exception as e:
127
+ logger.error(f"Registration error: {e}")
128
+ return {"success": False, "message": str(e)}
129
+
130
+ def authenticate_user(self, username: str, password: str) -> Dict[str, Any]:
131
+ """Authenticate a user and create a session."""
132
+ try:
133
+ with sqlite3.connect(self.db_path, timeout=5.0) as conn:
134
+ cursor = conn.cursor()
135
+
136
+ # Get user
137
+ password_hash = self.hash_password(password)
138
+ cursor.execute('''
139
+ SELECT user_id, username FROM users
140
+ WHERE username = ? AND password_hash = ?
141
+ ''', (username, password_hash))
142
+
143
+ user = cursor.fetchone()
144
+ if not user:
145
+ return {"success": False, "message": "Invalid credentials"}
146
+
147
+ user_id, username = user
148
+
149
+ # Create session
150
+ session_token = self.generate_token()
151
+ auth_token = self.generate_token()
152
+ expires_at = datetime.now() + timedelta(hours=24)
153
+
154
+ cursor.execute('''
155
+ INSERT INTO sessions (session_token, user_id, expires_at)
156
+ VALUES (?, ?, ?)
157
+ ''', (session_token, user_id, expires_at))
158
+
159
+ conn.commit()
160
+
161
+ return {
162
+ "success": True,
163
+ "message": "Login successful",
164
+ "user_id": user_id,
165
+ "username": username,
166
+ "session_token": session_token,
167
+ "auth_token": auth_token
168
+ }
169
+ except Exception as e:
170
+ logger.error(f"Authentication error: {e}")
171
+ return {"success": False, "message": str(e)}
172
+
173
+ def validate_session(self, session_token: str) -> Dict[str, Any]:
174
+ """Validate a session token."""
175
+ try:
176
+ with sqlite3.connect(self.db_path, timeout=5.0) as conn:
177
+ cursor = conn.cursor()
178
+
179
+ cursor.execute('''
180
+ SELECT s.user_id, s.expires_at, u.username
181
+ FROM sessions s
182
+ JOIN users u ON s.user_id = u.user_id
183
+ WHERE s.session_token = ?
184
+ ''', (session_token,))
185
+
186
+ session = cursor.fetchone()
187
+ if not session:
188
+ return {"valid": False, "message": "Invalid session"}
189
+
190
+ user_id, expires_at, username = session
191
+
192
+ # Check expiration
193
+ if datetime.fromisoformat(expires_at) < datetime.now():
194
+ return {"valid": False, "message": "Session expired"}
195
+
196
+ return {
197
+ "valid": True,
198
+ "user_id": user_id,
199
+ "username": username
200
+ }
201
+ except Exception as e:
202
+ logger.error(f"Session validation error: {e}")
203
+ return {"valid": False, "message": str(e)}
204
+
205
+ def logout_user(self, session_token: str) -> Dict[str, Any]:
206
+ """Logout user by removing session."""
207
+ try:
208
+ with sqlite3.connect(self.db_path, timeout=5.0) as conn:
209
+ cursor = conn.cursor()
210
+ cursor.execute("DELETE FROM sessions WHERE session_token = ?", (session_token,))
211
+ conn.commit()
212
+ return {"success": True, "message": "Logged out successfully"}
213
+ except Exception as e:
214
+ logger.error(f"Logout error: {e}")
215
+ return {"success": False, "message": str(e)}
216
+
217
+ def save_full_conversation(self, user_id: str, thread_id: str, mode: str, messages: List[Dict]) -> Dict[str, Any]:
218
+ """Save a conversation to database."""
219
+ try:
220
+ with sqlite3.connect(self.db_path, timeout=5.0) as conn:
221
+ cursor = conn.cursor()
222
+
223
+ conversation_id = secrets.token_urlsafe(16)
224
+ messages_json = json.dumps(messages)
225
+
226
+ cursor.execute('''
227
+ INSERT INTO conversations (conversation_id, user_id, thread_id, mode, messages)
228
+ VALUES (?, ?, ?, ?, ?)
229
+ ''', (conversation_id, user_id, thread_id, mode, messages_json))
230
+
231
+ conn.commit()
232
+
233
+ return {"success": True, "conversation_id": conversation_id}
234
+ except Exception as e:
235
+ logger.error(f"Save conversation error: {e}")
236
+ return {"success": False, "message": str(e)}
237
+
238
+ def get_user_conversations(self, user_id: str, limit: int = 10) -> List[Dict[str, Any]]:
239
+ """Get user's recent conversations."""
240
+ try:
241
+ with sqlite3.connect(self.db_path, timeout=5.0) as conn:
242
+ cursor = conn.cursor()
243
+
244
+ cursor.execute('''
245
+ SELECT conversation_id, thread_id, mode, messages, created_at
246
+ FROM conversations
247
+ WHERE user_id = ?
248
+ ORDER BY created_at DESC
249
+ LIMIT ?
250
+ ''', (user_id, limit))
251
+
252
+ conversations = []
253
+ for row in cursor.fetchall():
254
+ conv_id, thread_id, mode, messages_json, created_at = row
255
+ conversations.append({
256
+ "conversation_id": conv_id,
257
+ "thread_id": thread_id,
258
+ "mode": mode,
259
+ "messages": json.loads(messages_json) if messages_json else [],
260
+ "created_at": created_at
261
+ })
262
+
263
+ return conversations
264
+ except Exception as e:
265
+ logger.error(f"Get conversations error: {e}")
266
+ return []
267
+
268
+ def log_user_action(self, user_id: str, action_type: str, details: Optional[Dict] = None) -> Dict[str, Any]:
269
+ """Log a user action."""
270
+ try:
271
+ with sqlite3.connect(self.db_path, timeout=5.0) as conn:
272
+ cursor = conn.cursor()
273
+
274
+ details_json = json.dumps(details) if details else None
275
+
276
+ cursor.execute('''
277
+ INSERT INTO user_actions (user_id, action_type, details)
278
+ VALUES (?, ?, ?)
279
+ ''', (user_id, action_type, details_json))
280
+
281
+ conn.commit()
282
+
283
+ return {"success": True}
284
+ except Exception as e:
285
+ logger.error(f"Log action error: {e}")
286
+ return {"success": False, "message": str(e)}
287
+
288
+ def save_conversation(self, user_id: str, chainlit_session_id: str, role: str, content: str, persona_mode: str = None, metadata: Optional[Dict] = None) -> Dict[str, Any]:
289
+ """Save individual conversation messages with chainlit session tracking."""
290
+ try:
291
+ # Use log_user_action for individual message logging
292
+ details = {
293
+ "chainlit_session_id": chainlit_session_id,
294
+ "role": role,
295
+ "content": content,
296
+ "persona_mode": persona_mode,
297
+ "metadata": metadata or {}
298
+ }
299
+
300
+ return self.log_user_action(
301
+ user_id=user_id,
302
+ action_type=f"message_{role}",
303
+ details=details
304
+ )
305
+ except Exception as e:
306
+ logger.error(f"Save conversation message error: {e}")
307
+ return {"success": False, "message": str(e)}
308
+
309
+ # Create singleton instance
310
  auth_manager = AuthManager()