Compare commits

...

2 Commits

Author SHA1 Message Date
David Rodenkirchen
704184d6f9 add forgot password page 2024-08-26 23:31:27 +02:00
David Rodenkirchen
4a6b09f41c improve error messages 2024-08-26 22:22:03 +02:00
6 changed files with 131 additions and 19 deletions

Binary file not shown.

View File

@ -104,7 +104,7 @@ if __name__ == "__main__":
Page( Page(
name="ForgotPassword", name="ForgotPassword",
page_url="forgot-password", page_url="forgot-password",
build=lambda: pages.PlaceholderPage(placeholder_name="Passwort vergessen"), build=pages.ForgotPasswordPage,
), ),
Page( Page(
name="EditProfile", name="EditProfile",

View File

@ -50,6 +50,7 @@ class EditProfilePage(Component):
try: try:
new_pfp = await self.session.file_chooser(file_extensions=("png", "jpg", "jpeg"), multiple=False) new_pfp = await self.session.file_chooser(file_extensions=("png", "jpg", "jpeg"), multiple=False)
except NoFileSelectedError: except NoFileSelectedError:
await self.display_save_result_animation(False, optional_text="Keine Datei ausgewählt!")
return return
if new_pfp.size_in_bytes > 2 * 1_000_000: if new_pfp.size_in_bytes > 2 * 1_000_000:
@ -86,12 +87,12 @@ class EditProfilePage(Component):
async def on_save_pressed(self) -> None: async def on_save_pressed(self) -> None:
if not all((self.email_input.is_valid, self.birthday_input.is_valid)): if not all((self.email_input.is_valid, self.birthday_input.is_valid)):
await self.display_save_result_animation(False) await self.display_save_result_animation(False, optional_text="Ungültige Werte!")
return return
if len(self.new_pw_1_input.text.strip()) > 0: if len(self.new_pw_1_input.text.strip()) > 0:
if self.new_pw_1_input.text.strip() != self.new_pw_2_input.text.strip(): if self.new_pw_1_input.text.strip() != self.new_pw_2_input.text.strip():
await self.display_save_result_animation(False) await self.display_save_result_animation(False, optional_text="Passwörter nicht gleich!")
return return
user: User = self.session[UserService].get_user(self.session[SessionStorage].user_id) user: User = self.session[UserService].get_user(self.session[SessionStorage].user_id)

View File

@ -0,0 +1,111 @@
from hashlib import sha256
from random import choices
from email_validator import validate_email, EmailNotValidError
from rio import Column, Component, event, Text, TextStyle, TextInput, TextInputChangeEvent, Button
from src.ez_lan_manager import ConfigurationService, UserService, MailingService
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager.pages import BasePage
class ForgotPasswordPage(Component):
def on_email_changed(self, change_event: TextInputChangeEvent) -> None:
try:
validate_email(change_event.text, check_deliverability=False)
self.email_input.is_valid = True
self.submit_button.is_sensitive = True
except EmailNotValidError:
self.email_input.is_valid = False
self.submit_button.is_sensitive = False
async def on_submit_button_pressed(self) -> None:
self.submit_button.is_loading = True
await self.submit_button.force_refresh()
lan_info = self.session[ConfigurationService].get_lan_info()
user_service = self.session[UserService]
mailing_service = self.session[MailingService]
user = user_service.get_user(self.email_input.text.strip())
if user is not None:
new_password = "".join(choices(user_service.ALLOWED_USER_NAME_SYMBOLS, k=16))
user.user_password = sha256(new_password.encode(encoding="utf-8")).hexdigest()
user_service.update_user(user)
await mailing_service.send_email(
subject=f"Dein neues Passwort für {lan_info.name}",
body=f"Du hast für den EZ-LAN Manager der {lan_info.name} ein neues Passwort angefragt. "
f"Und hier ist es schon:\n\n{new_password}\n\nSolltest du kein neues Passwort angefordert haben, "
f"ignoriere diese E-Mail.\n\nLiebe Grüße\nDein {lan_info.name} - Team",
receiver=self.email_input.text.strip()
)
self.submit_button.is_loading = False
self.email_input.text = ""
self.info_text.text = "Falls für diese E-Mail ein Konto besteht, " \
"bekommst du in den nächsten Minuten ein neues Passwort zugeschickt. " \
"Bitte prüfe dein Spam-Postfach.",
@event.on_populate
async def on_populate(self) -> None:
await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Passwort vergessen")
def build(self) -> Component:
self.email_input = TextInput(
label="E-Mail Adresse",
text="",
margin_left=1,
margin_right=1,
margin_bottom=1,
grow_x=True,
on_change=self.on_email_changed
)
self.submit_button = Button(
content=Text(
"Neues Passwort anfordern",
style=TextStyle(fill=self.session.theme.background_color, font_size=0.9),
align_x=0.5
),
grow_x=True,
margin_top=2,
margin_left=1,
margin_right=1,
margin_bottom=1,
shape="rectangle",
style="minor",
color=self.session.theme.secondary_color,
on_press=self.on_submit_button_pressed,
is_sensitive=False
)
self.info_text = Text(
text="",
style=TextStyle(
fill=self.session.theme.background_color,
font_size=1
),
margin_top=2,
margin_left=1,
margin_right=1,
margin_bottom=2,
wrap=True
)
return BasePage(
content=Column(
MainViewContentBox(
content=Column(
Text(
"Passwort vergessen",
style=TextStyle(
fill=self.session.theme.background_color,
font_size=1.2
),
margin_top=2,
margin_bottom=2,
align_x=0.5
),
self.email_input,
self.submit_button,
self.info_text
)
),
align_y=0,
)
)

View File

@ -4,3 +4,4 @@ from .PlaceholderPage import PlaceholderPage
from .Logout import LogoutPage from .Logout import LogoutPage
from .Account import AccountPage from .Account import AccountPage
from .EditProfile import EditProfilePage from .EditProfile import EditProfilePage
from .ForgotPassword import ForgotPasswordPage

View File

@ -1,7 +1,6 @@
import logging import logging
from email.mime.multipart import MIMEMultipart from email.message import EmailMessage
from email.mime.text import MIMEText import aiosmtplib
from smtplib import SMTP
from src.ez_lan_manager.types.ConfigurationTypes import MailingServiceConfiguration from src.ez_lan_manager.types.ConfigurationTypes import MailingServiceConfiguration
@ -11,20 +10,20 @@ class MailingService:
def __init__(self, configuration: MailingServiceConfiguration): def __init__(self, configuration: MailingServiceConfiguration):
self._config = configuration self._config = configuration
def send_email(self, subject: str, body: str, receiver: str) -> None: async def send_email(self, subject: str, body: str, receiver: str) -> None:
# ToDo: Check with Rio/FastAPI if this needs to be ASYNC
try: try:
msg = MIMEMultipart() message = EmailMessage()
msg['From'] = self._config.sender message["From"] = self._config.sender
msg['To'] = receiver message["To"] = receiver
msg['Subject'] = subject message["Subject"] = subject
message.set_content(body)
msg.attach(MIMEText(body, 'plain'))
with SMTP(self._config.smtp_server, self._config.smtp_port) as server:
server.starttls()
server.login(self._config.username, self._config.password)
server.sendmail(self._config.sender, receiver, msg.as_string())
await aiosmtplib.send(
message,
hostname=self._config.smtp_server,
port=self._config.smtp_port,
username=self._config.username,
password=self._config.password
)
except Exception as e: except Exception as e:
logger.error(f"Failed to send email: {e}") logger.error(f"Failed to send email: {e}")