— Add "Remember me" checkbox on login (#65)

This commit is contained in:
Luc Didry 2024-11-27 11:00:40 +01:00
parent 91a9b27106
commit 0563cf185a
No known key found for this signature in database
GPG key ID: EA868E12D0257E3C
5 changed files with 64 additions and 9 deletions

View file

@ -8,6 +8,7 @@
- ✨ — The HTTP method used by checks is now configurable
- ♻️ — Refactor some agent code
- 💄 — Filter form on domains list (#66)
- ✨ — Add "Remember me" checkbox on login (#65)
## 0.5.0

View file

@ -14,9 +14,19 @@ general:
# Can be "production", "dev", "test".
# If not present, default value is "production"
env: "production"
# to get a good string for cookie_secret, run:
# To get a good string for cookie_secret, run:
# openssl rand -hex 32
cookie_secret: "foo_bar_baz"
# Session duration
# Use m for minutes, h for hours, d for days
# w for weeks, mo for months, y for years
# If not present, default value is "7d"
session_duration: "7d"
# Session opened with "Remember me" checked
# If not present, the "Remember me" feature is not available
# remember_me_duration: "1mo"
# Default delay for checks.
# Can be superseeded in domain configuration.
# For ex., to run checks every minute:

View file

@ -175,16 +175,31 @@ class DbSettings(BaseModel):
class General(BaseModel):
"""Frequency for the checks and alerts"""
cookie_secret: str
frequency: int
db: DbSettings
env: Environment = "production"
cookie_secret: str
session_duration: int = 10080 # 7 days
remember_me_duration: Optional[int] = None
frequency: int
root_path: str = ""
alerts: Alert
mail: Optional[Mail] = None
gotify: Optional[List[GotifyUrl]] = None
apprise: Optional[Dict[str, List[str]]] = None
@field_validator("session_duration", mode="before")
def parse_session_duration(cls, value):
"""Convert the configured session duration to minutes"""
return string_to_duration(value, "minutes")
@field_validator("remember_me_duration", mode="before")
def parse_remember_me_duration(cls, value):
"""Convert the configured session duration with remember me feature to minutes"""
if value:
return string_to_duration(value, "minutes")
return None
@field_validator("frequency", mode="before")
def parse_frequency(cls, value):
"""Convert the configured frequency to minutes"""

View file

@ -28,7 +28,11 @@ SEVERITY_LEVELS = {"ok": 1, "warning": 2, "critical": 3, "unknown": 4}
@route.get("/login")
async def login_view(request: Request, msg: str | None = None):
async def login_view(
request: Request,
msg: str | None = None,
config: Config = Depends(get_config),
):
token = request.cookies.get("access-token")
if token is not None and token != "":
manager = request.app.state.manager
@ -44,7 +48,14 @@ async def login_view(request: Request, msg: str | None = None):
else:
msg = None
return templates.TemplateResponse("login.html", {"request": request, "msg": msg})
return templates.TemplateResponse(
"login.html",
{
"request": request,
"msg": msg,
"remember": config.general.remember_me_duration,
},
)
@route.post("/login")
@ -52,6 +63,8 @@ async def post_login(
request: Request,
db: Session = Depends(get_db),
data: OAuth2PasswordRequestForm = Depends(),
rememberme: Annotated[str | None, Form()] = None,
config: Config = Depends(get_config),
):
username = data.username
user = await queries.get_user(db, username)
@ -70,14 +83,22 @@ async def post_login(
db.commit()
manager = request.app.state.manager
token = manager.create_access_token(
data={"sub": username}, expires=timedelta(days=7)
)
session_duration = config.general.session_duration
if config.general.remember_me_duration is not None and rememberme == "on":
session_duration = config.general.remember_me_duration
delta = timedelta(minutes=session_duration)
token = manager.create_access_token(data={"sub": username}, expires=delta)
response = RedirectResponse(
request.url_for("get_severity_counts_view"),
status_code=status.HTTP_303_SEE_OTHER,
)
manager.set_cookie(response, token)
response.set_cookie(
key=manager.cookie_name,
value=token,
httponly=True,
samesite="strict",
expires=int(delta.total_seconds()),
)
return response

View file

@ -16,6 +16,14 @@
name="password"
type="password"
form="login">
{% if remember is not none %}
<label>
<input type="checkbox"
name="rememberme"
form="login">
Remember me
</label>
{% endif %}
<form id="login"
method="post"
action="{{ url_for('post_login') }}">