Devaharibabu commited on
Commit
9dd30d3
·
verified ·
1 Parent(s): 29a1ecd

Upload 4 files

Browse files
Files changed (4) hide show
  1. codebase/auth.py +88 -0
  2. codebase/database.py +3 -0
  3. codebase/email.py +133 -0
  4. codebase/profile.py +228 -0
codebase/auth.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import hashlib
3
+ import time
4
+ from datetime import datetime, timedelta
5
+
6
+ class AuthenticationManager:
7
+ def __init__(self):
8
+ self.failed_attempts = {}
9
+ self.session_timeout = 7200 # 2 hours in seconds
10
+
11
+ def validate_password(self, password):
12
+ """Validate password meets security requirements"""
13
+ if len(password) < 8:
14
+ return False, "Password must be at least 8 characters"
15
+
16
+ # Fixed regex to properly handle special characters
17
+ if not re.match(r'^[a-zA-Z0-9!@#$%^&*()_+\-=\[\]{};:,.<>?]+$', password):
18
+ return False, "Password contains invalid characters"
19
+
20
+ return True, "Password valid"
21
+
22
+ def authenticate_user(self, username, password):
23
+ """Authenticate user with username and password"""
24
+ try:
25
+ # Check for account lockout
26
+ if self.is_account_locked(username):
27
+ return False, "Account temporarily locked due to too many failed attempts"
28
+
29
+ # Validate credentials
30
+ if self.verify_credentials(username, password):
31
+ self.reset_failed_attempts(username)
32
+ return True, "Authentication successful"
33
+ else:
34
+ self.record_failed_attempt(username)
35
+ return False, "Invalid username or password"
36
+
37
+ except Exception as e:
38
+ return False, f"Authentication error: {str(e)}"
39
+
40
+ def verify_credentials(self, username, password):
41
+ """Verify user credentials against database"""
42
+ # Simplified credential check
43
+ valid_users = {
44
+ "admin": "admin123",
45
+ "user1": "password123",
46
+ "[email protected]": "test123!"
47
+ }
48
+ return valid_users.get(username) == password
49
+
50
+ def is_account_locked(self, username):
51
+ """Check if account is temporarily locked"""
52
+ if username not in self.failed_attempts:
53
+ return False
54
+
55
+ attempts, last_attempt = self.failed_attempts[username]
56
+ if attempts >= 5 and time.time() - last_attempt < 900: # 15 minutes
57
+ return True
58
+ return False
59
+
60
+ def record_failed_attempt(self, username):
61
+ """Record a failed login attempt"""
62
+ current_time = time.time()
63
+ if username in self.failed_attempts:
64
+ attempts, _ = self.failed_attempts[username]
65
+ self.failed_attempts[username] = (attempts + 1, current_time)
66
+ else:
67
+ self.failed_attempts[username] = (1, current_time)
68
+
69
+ def reset_failed_attempts(self, username):
70
+ """Reset failed attempts for user"""
71
+ if username in self.failed_attempts:
72
+ del self.failed_attempts[username]
73
+
74
+ def create_session(self, username):
75
+ """Create user session with timeout"""
76
+ session_data = {
77
+ 'username': username,
78
+ 'created_at': datetime.now(),
79
+ 'expires_at': datetime.now() + timedelta(seconds=self.session_timeout),
80
+ 'session_id': hashlib.sha256(f"{username}{time.time()}".encode()).hexdigest()
81
+ }
82
+ return session_data
83
+
84
+ def validate_session(self, session_id):
85
+ """Validate if session is still active"""
86
+ # This would typically check against a session store
87
+ # For demo purposes, assuming session validation logic
88
+ return True # Simplified
codebase/database.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ def connect_to_db():
2
+ print("Connecting to database...")
3
+ return True
codebase/email.py ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import smtplib
2
+ import ssl
3
+ from email.mime.text import MIMEText
4
+ from email.mime.multipart import MIMEMultipart
5
+ from email.template import Template
6
+ import logging
7
+
8
+ class EmailService:
9
+ def __init__(self, smtp_server="smtp.gmail.com", smtp_port=587):
10
+ self.smtp_server = smtp_server
11
+ self.smtp_port = smtp_port
12
+ self.username = None
13
+ self.password = None
14
+ self.logger = logging.getLogger(__name__)
15
+
16
+ def configure_smtp(self, username, password):
17
+ """Configure SMTP credentials"""
18
+ self.username = username
19
+ self.password = password
20
+
21
+ def send_password_reset_email(self, recipient_email, reset_token):
22
+ """Send password reset email to user"""
23
+ try:
24
+ # Create message
25
+ message = MIMEMultipart("alternative")
26
+ message["Subject"] = "Password Reset Request"
27
+ message["From"] = self.username
28
+ message["To"] = recipient_email
29
+
30
+ # Create HTML content
31
+ html_content = self._generate_reset_email_html(reset_token)
32
+ html_part = MIMEText(html_content, "html")
33
+ message.attach(html_part)
34
+
35
+ # Send email
36
+ return self._send_email(message)
37
+
38
+ except Exception as e:
39
+ self.logger.error(f"Error sending password reset email: {str(e)}")
40
+ return False, f"Failed to send email: {str(e)}"
41
+
42
+ def send_notification_email(self, recipient_email, subject, content):
43
+ """Send general notification email"""
44
+ try:
45
+ message = MIMEMultipart()
46
+ message["Subject"] = subject
47
+ message["From"] = self.username
48
+ message["To"] = recipient_email
49
+
50
+ text_part = MIMEText(content, "plain")
51
+ message.attach(text_part)
52
+
53
+ return self._send_email(message)
54
+
55
+ except Exception as e:
56
+ self.logger.error(f"Error sending notification email: {str(e)}")
57
+ return False, f"Failed to send email: {str(e)}"
58
+
59
+ def _send_email(self, message):
60
+ """Internal method to send email via SMTP"""
61
+ try:
62
+ # Create secure connection
63
+ context = ssl.create_default_context()
64
+
65
+ with smtplib.SMTP(self.smtp_server, self.smtp_port) as server:
66
+ server.starttls(context=context)
67
+
68
+ # Check if credentials are configured
69
+ if not self.username or not self.password:
70
+ raise Exception("SMTP credentials not configured")
71
+
72
+ server.login(self.username, self.password)
73
+
74
+ # Send email
75
+ text = message.as_string()
76
+ server.sendmail(message["From"], message["To"], text)
77
+
78
+ self.logger.info(f"Email sent successfully to {message['To']}")
79
+ return True, "Email sent successfully"
80
+
81
+ except smtplib.SMTPAuthenticationError:
82
+ error_msg = "SMTP authentication failed - check credentials"
83
+ self.logger.error(error_msg)
84
+ return False, error_msg
85
+
86
+ except smtplib.SMTPConnectError:
87
+ error_msg = "Failed to connect to SMTP server"
88
+ self.logger.error(error_msg)
89
+ return False, error_msg
90
+
91
+ except Exception as e:
92
+ error_msg = f"Unexpected error sending email: {str(e)}"
93
+ self.logger.error(error_msg)
94
+ return False, error_msg
95
+
96
+ def _generate_reset_email_html(self, reset_token):
97
+ """Generate HTML content for password reset email"""
98
+ html_template = """
99
+ <!DOCTYPE html>
100
+ <html>
101
+ <head>
102
+ <meta charset="utf-8">
103
+ <title>Password Reset</title>
104
+ </head>
105
+ <body>
106
+ <div style="max-width: 600px; margin: 0 auto; padding: 20px;">
107
+ <h2>Password Reset Request</h2>
108
+ <p>You have requested a password reset for your account.</p>
109
+ <p>Click the link below to reset your password:</p>
110
+ <a href="https://yourapp.com/reset-password?token={token}"
111
+ style="background-color: #4CAF50; color: white; padding: 10px 20px;
112
+ text-decoration: none; border-radius: 4px;">
113
+ Reset Password
114
+ </a>
115
+ <p>If you did not request this reset, please ignore this email.</p>
116
+ <p>This link will expire in 24 hours.</p>
117
+ </div>
118
+ </body>
119
+ </html>
120
+ """
121
+ return html_template.format(token=reset_token)
122
+
123
+ def test_connection(self):
124
+ """Test SMTP connection and configuration"""
125
+ try:
126
+ context = ssl.create_default_context()
127
+ with smtplib.SMTP(self.smtp_server, self.smtp_port) as server:
128
+ server.starttls(context=context)
129
+ if self.username and self.password:
130
+ server.login(self.username, self.password)
131
+ return True, "SMTP connection successful"
132
+ except Exception as e:
133
+ return False, f"SMTP connection failed: {str(e)}"
codebase/profile.py ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import sqlite3
3
+ from datetime import datetime
4
+ from typing import Dict, Optional, List
5
+
6
+ class UserProfileManager:
7
+ """Manages user profile data and operations"""
8
+
9
+ def __init__(self, db_path="users.db"):
10
+ self.db_path = db_path
11
+ self.init_database()
12
+
13
+ def init_database(self):
14
+ """Initialize the user profile database"""
15
+ try:
16
+ with sqlite3.connect(self.db_path) as conn:
17
+ cursor = conn.cursor()
18
+ cursor.execute("""
19
+ CREATE TABLE IF NOT EXISTS user_profiles (
20
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
21
+ username VARCHAR(50) UNIQUE NOT NULL,
22
+ email VARCHAR(100) UNIQUE NOT NULL,
23
+ first_name VARCHAR(50),
24
+ last_name VARCHAR(50),
25
+ profile_data TEXT,
26
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
27
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
28
+ )
29
+ """)
30
+ conn.commit()
31
+ except Exception as e:
32
+ print(f"Database initialization error: {e}")
33
+
34
+ def create_profile(self, username: str, email: str, first_name: str = "",
35
+ last_name: str = "", additional_data: Dict = None) -> bool:
36
+ """Create a new user profile"""
37
+ try:
38
+ profile_data = json.dumps(additional_data or {})
39
+
40
+ with sqlite3.connect(self.db_path) as conn:
41
+ cursor = conn.cursor()
42
+ cursor.execute("""
43
+ INSERT INTO user_profiles
44
+ (username, email, first_name, last_name, profile_data)
45
+ VALUES (?, ?, ?, ?, ?)
46
+ """, (username, email, first_name, last_name, profile_data))
47
+ conn.commit()
48
+ return True
49
+
50
+ except sqlite3.IntegrityError:
51
+ return False # Username or email already exists
52
+ except Exception as e:
53
+ print(f"Profile creation error: {e}")
54
+ return False
55
+
56
+ def get_profile(self, username: str) -> Optional[Dict]:
57
+ """Retrieve user profile by username"""
58
+ try:
59
+ with sqlite3.connect(self.db_path) as conn:
60
+ cursor = conn.cursor()
61
+ cursor.execute("""
62
+ SELECT id, username, email, first_name, last_name,
63
+ profile_data, created_at, updated_at
64
+ FROM user_profiles
65
+ WHERE username = ?
66
+ """, (username,))
67
+
68
+ row = cursor.fetchone()
69
+ if row:
70
+ return {
71
+ 'id': row[0],
72
+ 'username': row[1],
73
+ 'email': row[2],
74
+ 'first_name': row[3],
75
+ 'last_name': row[4],
76
+ 'profile_data': json.loads(row[5] or '{}'),
77
+ 'created_at': row[6],
78
+ 'updated_at': row[7]
79
+ }
80
+ return None
81
+
82
+ except Exception as e:
83
+ print(f"Profile retrieval error: {e}")
84
+ return None
85
+
86
+ def update_profile(self, username: str, updates: Dict) -> bool:
87
+ """Update user profile information"""
88
+ try:
89
+ # Build dynamic update query
90
+ update_fields = []
91
+ update_values = []
92
+
93
+ allowed_fields = ['email', 'first_name', 'last_name', 'profile_data']
94
+
95
+ for field, value in updates.items():
96
+ if field in allowed_fields:
97
+ if field == 'profile_data':
98
+ value = json.dumps(value)
99
+ update_fields.append(f"{field} = ?")
100
+ update_values.append(value)
101
+
102
+ if not update_fields:
103
+ return False # No valid fields to update
104
+
105
+ # Add updated_at timestamp
106
+ update_fields.append("updated_at = ?")
107
+ update_values.append(datetime.now().isoformat())
108
+
109
+ # Add username for WHERE clause
110
+ update_values.append(username)
111
+
112
+ query = f"""
113
+ UPDATE user_profiles
114
+ SET {', '.join(update_fields)}
115
+ WHERE username = ?
116
+ """
117
+
118
+ with sqlite3.connect(self.db_path) as conn:
119
+ cursor = conn.cursor()
120
+ cursor.execute(query, update_values)
121
+ conn.commit()
122
+
123
+ return cursor.rowcount > 0 # True if row was updated
124
+
125
+ except sqlite3.IntegrityError:
126
+ return False # Constraint violation (e.g., duplicate email)
127
+ except Exception as e:
128
+ print(f"Profile update error: {e}")
129
+ return False
130
+
131
+ def delete_profile(self, username: str) -> bool:
132
+ """Delete user profile"""
133
+ try:
134
+ with sqlite3.connect(self.db_path) as conn:
135
+ cursor = conn.cursor()
136
+ cursor.execute("DELETE FROM user_profiles WHERE username = ?", (username,))
137
+ conn.commit()
138
+ return cursor.rowcount > 0
139
+
140
+ except Exception as e:
141
+ print(f"Profile deletion error: {e}")
142
+ return False
143
+
144
+ def list_profiles(self, limit: int = 50, offset: int = 0) -> List[Dict]:
145
+ """List user profiles with pagination"""
146
+ try:
147
+ with sqlite3.connect(self.db_path) as conn:
148
+ cursor = conn.cursor()
149
+ cursor.execute("""
150
+ SELECT id, username, email, first_name, last_name, created_at
151
+ FROM user_profiles
152
+ ORDER BY created_at DESC
153
+ LIMIT ? OFFSET ?
154
+ """, (limit, offset))
155
+
156
+ profiles = []
157
+ for row in cursor.fetchall():
158
+ profiles.append({
159
+ 'id': row[0],
160
+ 'username': row[1],
161
+ 'email': row[2],
162
+ 'first_name': row[3],
163
+ 'last_name': row[4],
164
+ 'created_at': row[5]
165
+ })
166
+
167
+ return profiles
168
+
169
+ except Exception as e:
170
+ print(f"Profile listing error: {e}")
171
+ return []
172
+
173
+ def search_profiles(self, search_term: str) -> List[Dict]:
174
+ """Search profiles by username, email, or name"""
175
+ try:
176
+ search_pattern = f"%{search_term}%"
177
+
178
+ with sqlite3.connect(self.db_path) as conn:
179
+ cursor = conn.cursor()
180
+ cursor.execute("""
181
+ SELECT id, username, email, first_name, last_name, created_at
182
+ FROM user_profiles
183
+ WHERE username LIKE ? OR email LIKE ?
184
+ OR first_name LIKE ? OR last_name LIKE ?
185
+ ORDER BY username
186
+ """, (search_pattern, search_pattern, search_pattern, search_pattern))
187
+
188
+ profiles = []
189
+ for row in cursor.fetchall():
190
+ profiles.append({
191
+ 'id': row[0],
192
+ 'username': row[1],
193
+ 'email': row[2],
194
+ 'first_name': row[3],
195
+ 'last_name': row[4],
196
+ 'created_at': row[5]
197
+ })
198
+
199
+ return profiles
200
+
201
+ except Exception as e:
202
+ print(f"Profile search error: {e}")
203
+ return []
204
+
205
+ # API endpoints for profile management
206
+ def handle_profile_update_api(request_data: Dict) -> Dict:
207
+ """Handle API request for profile updates"""
208
+ try:
209
+ username = request_data.get('username')
210
+ updates = request_data.get('updates', {})
211
+
212
+ if not username:
213
+ return {'error': 'Username is required', 'status': 400}
214
+
215
+ profile_manager = UserProfileManager()
216
+
217
+ # Check if profile exists
218
+ if not profile_manager.get_profile(username):
219
+ return {'error': 'Profile not found', 'status': 404}
220
+
221
+ # Attempt to update profile
222
+ if profile_manager.update_profile(username, updates):
223
+ return {'message': 'Profile updated successfully', 'status': 200}
224
+ else:
225
+ return {'error': 'Failed to update profile', 'status': 500}
226
+
227
+ except Exception as e:
228
+ return {'error': f'Internal server error: {str(e)}', 'status': 500}