|
35 | 35 |
|
36 | 36 | LOGIN_TOKEN_LENGTH = 32 |
37 | 37 | LOGIN_TOKEN_TIMEOUT_SECONDS = 10 * 60 |
| 38 | +EMAIL_CONFIRMATION_TIMEOUT_SECONDS = 10 * 60 |
38 | 39 | ACCOUNT_DELETE_TOKEN_TIMEOUT_SECONDS = 10 * 60 |
39 | | -USER_DELETE_TOKEN_PREFIX = "user_delete_token" |
| 40 | +USER_DELETE_TOKEN_PREFIX = "user_delete_token" |
40 | 41 |
|
41 | 42 | def main(request): |
42 | 43 | return render(request,"main.html") |
@@ -113,6 +114,11 @@ def data(request): |
113 | 114 | def Confirmationlogin(request): |
114 | 115 | return render(request,'confirmation_login.html') |
115 | 116 |
|
| 117 | +def login_user(request, user): |
| 118 | + login(request, user, backend='django.contrib.auth.backends.ModelBackend') |
| 119 | + #user.last_login = now # this should be set by Django's UserManager |
| 120 | + user.save() |
| 121 | + |
116 | 122 | @require_GET |
117 | 123 | def authenticate_via_magic_link(request: HttpRequest, token: str): |
118 | 124 | email = cache.get(token) |
@@ -145,7 +151,7 @@ def authenticate_via_magic_link(request: HttpRequest, token: str): |
145 | 151 | user = User.objects.create_user(username=email, email=email) |
146 | 152 | is_new = True |
147 | 153 |
|
148 | | - login(request, user, backend='django.contrib.auth.backends.ModelBackend') |
| 154 | + login_user(request, user) |
149 | 155 |
|
150 | 156 | cache.delete(token) |
151 | 157 | return render(request, "confirmation_login.html", { |
@@ -209,47 +215,112 @@ def delete_account(request): |
209 | 215 | messages.info(request, 'Your account has been successfully deleted.') |
210 | 216 | return render(request, 'deleteaccount.html') |
211 | 217 |
|
| 218 | +@login_required |
212 | 219 | def change_useremail(request): |
213 | 220 | email_new = request.POST.get('email_new', False) |
214 | 221 | currentuser = request.user |
215 | 222 | email_old = currentuser.email |
216 | | - logger.info('User requests to change email from %s to %s', email_old, email_new) |
217 | 223 |
|
218 | | - if is_email_blocked(email): |
219 | | - logger.warning('Attempted login with blocked email: %s', email) |
| 224 | + if is_email_blocked(email_new): |
| 225 | + logger.warning('Attempted login with blocked email: %s', email_new) |
220 | 226 | return render(request, "error.html", { |
221 | 227 | 'error': { |
222 | 228 | 'class': 'danger', |
223 | 229 | 'title': 'Login failed!', |
224 | 230 | 'text': 'You attempted to change your email to an address that is blocked. Please contact support for assistance.' |
225 | 231 | } |
226 | 232 | }) |
227 | | - |
228 | | - if email_new: |
229 | | - currentuser.email = email_new |
230 | | - currentuser.username = email_new |
231 | | - currentuser.save() |
232 | | - #send email |
233 | | - subject = 'Change Email' |
234 | | - link = get_login_link(request, email_new) |
235 | | - message =f"""Hello {email_new}, |
236 | | -
|
237 | | -You requested to change your email address from {email_old} to {email_new}. |
| 233 | + messages.error(request, "Invalid email change request.") |
| 234 | + return render(request, 'changeuser.html') |
| 235 | + |
| 236 | + if not email_new or email_new == email_old: |
| 237 | + messages.error(request, "Invalid email change request.") |
| 238 | + return render(request, 'changeuser.html') |
| 239 | + |
| 240 | + if User.objects.filter(email=email_new).exists(): |
| 241 | + messages.error(request, "This email is already in use.") |
| 242 | + return render(request, 'changeuser.html') |
| 243 | + |
| 244 | + token = secrets.token_urlsafe(32) |
| 245 | + cache.set( |
| 246 | + f"email_confirmation_{email_new}", |
| 247 | + {"token": token, "old_email": request.user.email}, |
| 248 | + timeout=EMAIL_CONFIRMATION_TIMEOUT_SECONDS, |
| 249 | + ) |
| 250 | + |
| 251 | + confirm_url = request.build_absolute_uri( |
| 252 | + reverse("optimap:confirm-email-change", args=[token, email_new]) |
| 253 | + ) |
| 254 | + |
| 255 | + subject = 'Confirm Your Email Change' |
| 256 | + message = f"""Hello, |
| 257 | +
|
| 258 | +You requested to change your email from {email_old} to {email_new}. |
238 | 259 | Please confirm the new email by clicking on this link: |
239 | 260 |
|
240 | | -{link} |
| 261 | +{confirm_url} |
| 262 | +
|
| 263 | +This link will expire in 10 minutes. |
241 | 264 |
|
242 | 265 | Thank you for using OPTIMAP! |
243 | 266 | """ |
244 | | - send_mail( |
245 | | - subject, |
246 | | - message, |
247 | | - from_email = settings.EMAIL_HOST_USER, |
248 | | - recipient_list=[email_new] |
249 | | - ) |
250 | | - logout(request) |
| 267 | + send_mail(subject, message, settings.EMAIL_HOST_USER, [email_new]) |
| 268 | + messages.info(request, "A confirmation email has been sent.") |
| 269 | + logout(request) |
| 270 | + |
| 271 | + return render(request, 'changeuser.html') |
| 272 | + |
| 273 | +def confirm_email_change(request, token, email_new): |
| 274 | + cached_data = cache.get(f"email_confirmation_{email_new}") |
| 275 | + |
| 276 | + if not cached_data: |
| 277 | + messages.error(request, "Invalid or expired confirmation link.") |
| 278 | + return HttpResponseRedirect("/") |
| 279 | + |
| 280 | + if isinstance(cached_data, str): |
| 281 | + messages.error(request, "Cache error: Expected dictionary, got string.") |
| 282 | + return HttpResponseRedirect("/") |
| 283 | + |
| 284 | + stored_token = cached_data.get("token") |
| 285 | + old_email = cached_data.get("old_email") |
| 286 | + |
| 287 | + if stored_token != token: |
| 288 | + messages.error(request, "Invalid or expired confirmation link.") |
| 289 | + return HttpResponseRedirect("/") |
| 290 | + |
| 291 | + user = User.objects.filter(email=old_email).first() |
| 292 | + |
| 293 | + if not user: |
| 294 | + messages.error(request, "User not found.") |
| 295 | + return HttpResponseRedirect("/") |
| 296 | + |
| 297 | + user.email = email_new |
| 298 | + user.username = email_new |
| 299 | + user.save() |
| 300 | + |
| 301 | + contactURL = f"{settings.BASE_URL}/contact" |
| 302 | + notify_subject = 'Your OPTIMAP Email Was Changed' |
| 303 | + notify_message = f"""Hello, |
| 304 | +
|
| 305 | +Your email associated with OPTIMAP was changed from {old_email} to {email_new}. |
| 306 | +If you did NOT request this change, please contact us immediately at {contactURL}. |
| 307 | +
|
| 308 | +Thank you for using OPTIMAP! |
| 309 | +""" |
| 310 | + |
| 311 | + send_mail( |
| 312 | + notify_subject, |
| 313 | + notify_message, |
| 314 | + from_email=settings.EMAIL_HOST_USER, |
| 315 | + recipient_list=[old_email] |
| 316 | + ) |
| 317 | + |
| 318 | + cache.delete(f"email_confirmation_{email_new}") |
| 319 | + |
| 320 | + login_user(request, user) |
251 | 321 |
|
252 | | - return render(request,'changeuser.html') |
| 322 | + messages.success(request, "Your email has been successfully updated!") |
| 323 | + return redirect("/usersettings/") |
253 | 324 |
|
254 | 325 | def get_login_link(request, email): |
255 | 326 | token = secrets.token_urlsafe(nbytes = LOGIN_TOKEN_LENGTH) |
|
0 commit comments