2021-12-07 15:39:18 +00:00
|
|
|
import logging
|
2020-08-19 14:20:57 +00:00
|
|
|
from functools import wraps
|
2022-12-11 21:49:32 +00:00
|
|
|
from urllib.parse import urlsplit
|
|
|
|
from urllib.parse import urlunsplit
|
2021-12-20 22:57:27 +00:00
|
|
|
|
|
|
|
from flask import abort
|
|
|
|
from flask import current_app
|
2023-08-13 20:08:28 +00:00
|
|
|
from flask import g
|
2023-03-09 16:41:26 +00:00
|
|
|
from flask import request
|
2021-12-20 22:57:27 +00:00
|
|
|
from flask import session
|
2023-06-28 15:56:49 +00:00
|
|
|
from werkzeug.routing import BaseConverter
|
2020-08-19 14:20:57 +00:00
|
|
|
|
2024-03-15 18:58:06 +00:00
|
|
|
from canaille.app import models
|
|
|
|
from canaille.app.i18n import gettext as _
|
|
|
|
from canaille.app.themes import render_template
|
|
|
|
|
2020-08-19 14:20:57 +00:00
|
|
|
|
|
|
|
def current_user():
|
2023-08-13 20:08:28 +00:00
|
|
|
if "user" in g:
|
|
|
|
return g.user
|
|
|
|
|
2023-02-05 18:08:25 +00:00
|
|
|
for user_id in session.get("user_id", [])[::-1]:
|
2024-04-09 13:45:47 +00:00
|
|
|
user = models.User.get(user_id)
|
2022-11-01 11:25:21 +00:00
|
|
|
if user and (
|
|
|
|
not current_app.backend.has_account_lockability() or not user.locked
|
|
|
|
):
|
2023-08-13 20:08:28 +00:00
|
|
|
g.user = user
|
|
|
|
return g.user
|
2020-11-25 16:41:03 +00:00
|
|
|
|
2023-02-05 18:08:25 +00:00
|
|
|
session["user_id"].remove(user_id)
|
2020-11-25 16:41:03 +00:00
|
|
|
|
2024-04-09 09:56:51 +00:00
|
|
|
if "user_id" in session and not session["user_id"]:
|
|
|
|
del session["user_id"]
|
|
|
|
|
2022-12-10 19:47:47 +00:00
|
|
|
return None
|
2020-08-19 14:20:57 +00:00
|
|
|
|
|
|
|
|
2023-08-23 12:56:56 +00:00
|
|
|
def login_user(user):
|
2023-12-28 17:31:57 +00:00
|
|
|
"""Opens a session for the user."""
|
2023-08-23 12:56:56 +00:00
|
|
|
g.user = user
|
|
|
|
try:
|
|
|
|
previous = (
|
|
|
|
session["user_id"]
|
|
|
|
if isinstance(session["user_id"], list)
|
|
|
|
else [session["user_id"]]
|
|
|
|
)
|
|
|
|
session["user_id"] = previous + [user.id]
|
|
|
|
except KeyError:
|
|
|
|
session["user_id"] = [user.id]
|
|
|
|
|
|
|
|
|
|
|
|
def logout_user():
|
2023-12-28 17:31:57 +00:00
|
|
|
"""Closes the user session."""
|
2023-08-23 12:56:56 +00:00
|
|
|
try:
|
|
|
|
session["user_id"].pop()
|
|
|
|
del g.user
|
|
|
|
if not session["user_id"]:
|
|
|
|
del session["user_id"]
|
|
|
|
except (IndexError, KeyError):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2020-08-19 14:20:57 +00:00
|
|
|
def user_needed():
|
|
|
|
def wrapper(view_function):
|
|
|
|
@wraps(view_function)
|
|
|
|
def decorator(*args, **kwargs):
|
2020-08-27 08:50:50 +00:00
|
|
|
user = current_user()
|
|
|
|
if not user:
|
2020-08-19 14:20:57 +00:00
|
|
|
abort(403)
|
2020-08-27 08:50:50 +00:00
|
|
|
return view_function(*args, user=user, **kwargs)
|
2020-08-19 14:20:57 +00:00
|
|
|
|
|
|
|
return decorator
|
|
|
|
|
|
|
|
return wrapper
|
|
|
|
|
|
|
|
|
2021-12-02 17:23:14 +00:00
|
|
|
def permissions_needed(*args):
|
|
|
|
permissions = set(args)
|
2020-11-02 11:13:03 +00:00
|
|
|
|
2020-08-19 14:20:57 +00:00
|
|
|
def wrapper(view_function):
|
|
|
|
@wraps(view_function)
|
|
|
|
def decorator(*args, **kwargs):
|
|
|
|
user = current_user()
|
2024-04-07 13:21:32 +00:00
|
|
|
if not user or not user.can(*permissions):
|
2020-08-19 14:20:57 +00:00
|
|
|
abort(403)
|
2020-10-29 10:09:31 +00:00
|
|
|
return view_function(*args, user=user, **kwargs)
|
2020-08-19 14:20:57 +00:00
|
|
|
|
|
|
|
return decorator
|
|
|
|
|
|
|
|
return wrapper
|
2021-12-07 15:39:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
def smtp_needed():
|
|
|
|
def wrapper(view_function):
|
|
|
|
@wraps(view_function)
|
|
|
|
def decorator(*args, **kwargs):
|
2023-12-18 17:06:03 +00:00
|
|
|
if "SMTP" in current_app.config["CANAILLE"]:
|
2021-12-07 15:39:18 +00:00
|
|
|
return view_function(*args, **kwargs)
|
|
|
|
|
|
|
|
message = _("No SMTP server has been configured")
|
|
|
|
logging.warning(message)
|
|
|
|
return (
|
|
|
|
render_template(
|
|
|
|
"error.html",
|
2023-06-20 12:22:15 +00:00
|
|
|
error_code=500,
|
2021-12-07 15:39:18 +00:00
|
|
|
icon="tools",
|
|
|
|
description=message,
|
|
|
|
),
|
|
|
|
500,
|
|
|
|
)
|
|
|
|
|
|
|
|
return decorator
|
|
|
|
|
|
|
|
return wrapper
|
2022-02-19 16:53:05 +00:00
|
|
|
|
|
|
|
|
2022-12-11 21:49:32 +00:00
|
|
|
def set_parameter_in_url_query(url, **kwargs):
|
|
|
|
split = list(urlsplit(url))
|
|
|
|
pairs = split[3].split("&")
|
|
|
|
parameters = {pair.split("=")[0]: pair.split("=")[1] for pair in pairs if pair}
|
|
|
|
parameters = {**parameters, **kwargs}
|
|
|
|
split[3] = "&".join(f"{key}={value}" for key, value in parameters.items())
|
|
|
|
return urlunsplit(split)
|
2023-03-09 16:41:26 +00:00
|
|
|
|
|
|
|
|
2023-03-30 21:14:39 +00:00
|
|
|
def request_is_htmx():
|
2023-06-23 14:26:38 +00:00
|
|
|
return request.headers.get("HX-Request", False) and not request.headers.get(
|
|
|
|
"HX-Boosted", False
|
|
|
|
)
|
2023-03-30 21:14:39 +00:00
|
|
|
|
|
|
|
|
2023-03-09 16:41:26 +00:00
|
|
|
def render_htmx_template(template, htmx_template=None, **kwargs):
|
|
|
|
template = (
|
2023-03-30 21:14:39 +00:00
|
|
|
(htmx_template or f"partial/{template}") if request_is_htmx() else template
|
2023-03-09 16:41:26 +00:00
|
|
|
)
|
|
|
|
return render_template(template, **kwargs)
|
2023-06-28 15:56:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
def model_converter(model):
|
|
|
|
class ModelConverter(BaseConverter):
|
2023-06-29 10:15:12 +00:00
|
|
|
def __init__(self, *args, required=True, **kwargs):
|
|
|
|
self.required = required
|
|
|
|
super().__init__(self, *args, **kwargs)
|
|
|
|
|
2023-06-28 15:56:49 +00:00
|
|
|
def to_url(self, instance):
|
|
|
|
return instance.identifier
|
|
|
|
|
|
|
|
def to_python(self, identifier):
|
|
|
|
current_app.backend.setup()
|
|
|
|
instance = model.get(identifier)
|
2023-06-29 10:15:12 +00:00
|
|
|
if self.required and not instance:
|
2023-06-28 15:56:49 +00:00
|
|
|
abort(404)
|
|
|
|
|
|
|
|
return instance
|
|
|
|
|
|
|
|
return ModelConverter
|