add login

This commit is contained in:
David Rodenkirchen 2024-08-25 22:31:29 +02:00
parent 8a47e95c2a
commit e6b7f4ca85
8 changed files with 216 additions and 60 deletions

View File

@ -2,10 +2,11 @@ import logging
from pathlib import Path
from rio import App, Theme, Color, Font, Page
from rio import App, Theme, Color, Font, Page, Session
from from_root import from_root
from src.ez_lan_manager import pages, init_services
from src.ez_lan_manager.types.SessionStorage import SessionStorage
logger = logging.getLogger(__name__.split(".")[-1])
@ -27,6 +28,10 @@ if __name__ == "__main__":
services = init_services()
lan_info = services[2].get_lan_info()
async def on_session_start(session: Session) -> None:
await session.set_title(lan_info.name)
session.attach(SessionStorage())
app = App(
name="EZ LAN Manager",
pages=[
@ -89,12 +94,22 @@ if __name__ == "__main__":
name="Imprint",
page_url="imprint",
build=lambda: pages.PlaceholderPage(placeholder_name="Impressum & DSGVO"),
),
Page(
name="Register",
page_url="register",
build=lambda: pages.PlaceholderPage(placeholder_name="Registrierung"),
),
Page(
name="ForgotPassword",
page_url="forgot-password",
build=lambda: pages.PlaceholderPage(placeholder_name="Passwort vergessen"),
)
],
theme=theme,
assets_dir=Path(__file__).parent / "assets",
default_attachments=services,
on_session_start= lambda s: s.set_title(lan_info.name),
on_session_start=on_session_start,
icon=from_root("src/ez_lan_manager/assets/img/favicon.png"),
meta_tags={
"robots": "INDEX,FOLLOW",

View File

@ -1,40 +1,26 @@
from rio import *
from src.ez_lan_manager import ConfigurationService
from src.ez_lan_manager.components.DesktopNavigationButton import DesktopNavigationButton
from src.ez_lan_manager.components.LoginBox import LoginBox
class DesktopNavigationButton(Component):
STYLE = TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9)
label: str
target_url: str
open_new_tab: bool = False
def build(self) -> Component:
return Link(
content=Button(
content=Text(self.label, style=self.STYLE),
shape="rectangle",
style="minor",
color="secondary",
grow_x=True,
margin_left=0.6,
margin_right=0.6,
margin_top=0.6
),
target_url=self.target_url,
open_in_new_tab=self.open_new_tab
)
from src.ez_lan_manager.components.UserInfoBox import UserInfoBox
from src.ez_lan_manager.types.SessionStorage import SessionStorage
class DesktopNavigation(Component):
async def refresh_cb(self) -> None:
self.box = self.login_box if self.session[SessionStorage].user_id is None else self.user_info_box
await self.force_refresh()
def build(self) -> Component:
self.user_info_box = UserInfoBox()
self.login_box = LoginBox(self.refresh_cb)
self.box = self.login_box if self.session[SessionStorage].user_id is None else self.user_info_box
lan_info = self.session[ConfigurationService].get_lan_info()
return Card(
Column(
Text(lan_info.name, align_x=0.5, margin_top=0.3, style=TextStyle(fill=self.session.theme.hud_color, font_size=2.5)),
Text(f"Edition {lan_info.iteration}", align_x=0.5, style=TextStyle(fill=self.session.theme.hud_color, font_size=1.2), margin_top=0.3, margin_bottom=2),
LoginBox(),
self.box,
DesktopNavigationButton("News", "./news"),
Spacer(min_height=1),
DesktopNavigationButton(f"Über {lan_info.name} {lan_info.iteration}", "./overview"),

View File

@ -0,0 +1,24 @@
from rio import Component, TextStyle, Color, Link, Button, Text
class DesktopNavigationButton(Component):
STYLE = TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9)
label: str
target_url: str
open_new_tab: bool = False
def build(self) -> Component:
return Link(
content=Button(
content=Text(self.label, style=self.STYLE),
shape="rectangle",
style="minor",
color="secondary",
grow_x=True,
margin_left=0.6,
margin_right=0.6,
margin_top=0.6
),
target_url=self.target_url,
open_in_new_tab=self.open_new_tab
)

View File

@ -1,47 +1,78 @@
from rio import Component, Card, Column, Text, Row, Rectangle, Button, TextStyle, Color, Spacer, TextInput
from typing import Callable
from rio import Component, Column, Text, Row, Rectangle, Button, TextStyle, Color, Spacer, TextInput
from src.ez_lan_manager import UserService
from src.ez_lan_manager.types.SessionStorage import SessionStorage
class LoginBox(Component):
TEXT_STYLE = TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9)
refresh_cb: Callable
async def _on_login_pressed(self) -> None:
self.login_button.is_loading = True
user_name = self.user_name_input.text.lower()
if self.session[UserService].is_login_valid(user_name, self.password_input.text):
self.session[SessionStorage].user_id = self.session[UserService].get_user(user_name).user_id
self.user_name_input.is_valid = True
self.password_input.is_valid = True
self.login_button.is_loading = False
await self.refresh_cb()
else:
self.user_name_input.is_valid = False
self.password_input.is_valid = False
self.login_button.is_loading = False
def build(self) -> Component:
self.user_name_input = TextInput(
text="",
label="Benutzername",
accessibility_label = "Benutzername",
min_height=0.5,
on_confirm=lambda _: self._on_login_pressed()
)
self.password_input = TextInput(
text="",
label="Passwort",
accessibility_label="Passwort",
is_secret=True,
on_confirm=lambda _: self._on_login_pressed()
)
self.login_button = Button(
Text("LOGIN", style=self.TEXT_STYLE, justify="center"),
shape="rectangle",
style="minor",
color="secondary",
margin_bottom=0.4,
on_press=self._on_login_pressed
)
self.register_button = Button(
Text("REG", style=self.TEXT_STYLE, justify="center"),
shape="rectangle",
style="minor",
color="secondary",
on_press=lambda: self.session.navigate_to("./register")
)
self.forgot_password_button = Button(
Text("LST PWD",style=self.TEXT_STYLE, justify="center"),
shape="rectangle",
style="minor",
color="secondary",
on_press=lambda: self.session.navigate_to("./forgot-password")
)
return Rectangle(
content=Column(
TextInput(
text="",
label="Benutzername",
accessibility_label = "Benutzername",
min_height=0.5
),
TextInput(
text="",
label="Passwort",
accessibility_label="Passwort",
is_secret=True
),
self.user_name_input,
self.password_input,
Column(
Row(
Button(
Text("LOGIN", style=self.TEXT_STYLE, justify="center"),
shape="rectangle",
style="minor",
color="secondary",
margin_bottom=0.4
)
self.login_button
),
Row(
Button(
Text("REG", style=self.TEXT_STYLE, justify="center"),
shape="rectangle",
style="minor",
color="secondary"
),
self.register_button,
Spacer(),
Button(
Text("LST PWD",style=self.TEXT_STYLE, justify="center"),
shape="rectangle",
style="minor",
color="secondary"
),
self.forgot_password_button,
proportions=(49, 2, 49)
)
),

View File

@ -0,0 +1,63 @@
from random import choice
from rio import Component, Card, Column, Text, Row, Rectangle, Button, TextStyle, Color, Spacer, TextInput, Link
from src.ez_lan_manager import UserService, AccountingService
from src.ez_lan_manager.components.UserInfoBoxButton import UserInfoBoxButton
from src.ez_lan_manager.types.SessionStorage import SessionStorage
class StatusButton(Component):
STYLE = TextStyle(fill=Color.from_hex("121212"), font_size=0.5)
label: str
target_url: str
enabled: bool
def build(self) -> Component:
return Link(
content=Button(
content=Text(self.label, style=self.STYLE, justify="center"),
shape="rectangle",
style="major",
color="success" if self.enabled else "danger",
grow_x=True,
margin_left=0.6,
margin_right=0.6,
margin_top=0.6
),
target_url=self.target_url,
align_y=0.5,
grow_y=False
)
class UserInfoBox(Component):
@staticmethod
def get_greeting() -> str:
return choice(["Grüße", "Hallo", "Willkommen", "Moin", "Ahoi"])
def build(self) -> Component:
user = self.session[UserService].get_user(self.session[SessionStorage].user_id)
if user is None: # Noone logged in
return Text("")
a_s = self.session[AccountingService]
return Rectangle(
content=Column(
Text(f"{self.get_greeting()},", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9), justify="center"),
Text(f"{user.user_name}", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=1.2), justify="center"),
Row(
StatusButton(label="TICKET", target_url="", enabled=True),
StatusButton(label="SITZPLATZ", target_url="", enabled=False),
proportions=(50, 50),
grow_y=False
),
UserInfoBoxButton("Profil bearbeiten", "./edit-profile"),
UserInfoBoxButton(f"Guthaben: {a_s.make_euro_string_from_int(a_s.get_balance(user.user_id))}", "./account"),
UserInfoBoxButton("Ausloggen", "./logout")
),
fill=Color.TRANSPARENT,
min_height=8,
min_width=12,
align_x=0.5,
margin_top=0.3,
margin_bottom=2
)

View File

@ -0,0 +1,24 @@
from rio import Component, TextStyle, Color, Link, Button, Text
class UserInfoBoxButton(Component):
STYLE = TextStyle(fill=Color.from_hex("02dac5"), font_size=0.6)
label: str
target_url: str
open_new_tab: bool = False
def build(self) -> Component:
return Link(
content=Button(
content=Text(self.label, style=self.STYLE),
shape="rectangle",
style="minor",
color="secondary",
grow_x=True,
margin_left=0.6,
margin_right=0.6,
margin_top=0.6
),
target_url=self.target_url,
open_in_new_tab=self.open_new_tab
)

View File

@ -16,9 +16,12 @@ class UserService:
def __init__(self, db_service: DatabaseService) -> None:
self._db_service = db_service
def get_user(self, accessor: Union[str, int]) -> Optional[User]:
def get_user(self, accessor: Optional[Union[str, int]]) -> Optional[User]:
if accessor is None:
return
if isinstance(accessor, int):
return self._db_service.get_user_by_id(accessor)
accessor = accessor.lower()
if "@" in accessor:
return self._db_service.get_user_by_mail(accessor)
return self._db_service.get_user_by_name(accessor)
@ -34,6 +37,8 @@ class UserService:
if disallowed_char:
raise NameNotAllowedError(disallowed_char)
user_name = user_name.lower()
hashed_pw = sha256(password_clear_text.encode(encoding="utf-8")).hexdigest()
return self._db_service.create_user(user_name, user_mail, hashed_pw)
@ -41,6 +46,7 @@ class UserService:
disallowed_char = self._check_for_disallowed_char(user.user_name)
if disallowed_char:
raise NameNotAllowedError(disallowed_char)
user.user_name = user.user_name.lower()
return self._db_service.update_user(user)
def is_login_valid(self, user_name_or_mail: str, password_clear_text: str) -> bool:

View File

@ -0,0 +1,7 @@
from dataclasses import dataclass
from typing import Optional
@dataclass(frozen=False)
class SessionStorage:
user_id: Optional[int] = None # DEBUG: Put user ID here to skip login