microblog.pub/app/config.py

116 lines
2.9 KiB
Python
Raw Normal View History

2022-06-22 20:11:22 +02:00
import os
2022-07-11 09:34:06 +02:00
import secrets
2022-07-04 21:45:23 +02:00
import subprocess
2022-06-22 20:11:22 +02:00
from pathlib import Path
import bcrypt
2022-07-11 09:34:06 +02:00
import itsdangerous
2022-06-22 20:11:22 +02:00
import pydantic
import tomli
from fastapi import Form
from fastapi import HTTPException
from fastapi import Request
from itsdangerous import TimedSerializer
2022-07-11 09:34:06 +02:00
from itsdangerous import URLSafeTimedSerializer
from loguru import logger
2022-06-22 20:11:22 +02:00
2022-06-27 20:55:44 +02:00
from app.utils.emoji import _load_emojis
2022-06-22 20:11:22 +02:00
ROOT_DIR = Path().parent.resolve()
2022-07-07 20:37:16 +02:00
_CONFIG_FILE = os.getenv("MICROBLOGPUB_CONFIG_FILE", "profile.toml")
2022-06-22 20:11:22 +02:00
2022-07-04 21:45:23 +02:00
VERSION_COMMIT = (
subprocess.check_output(["git", "rev-parse", "--short=8", "HEAD"])
.split()[0]
.decode()
)
VERSION = f"2.0.0+{VERSION_COMMIT}"
2022-06-22 20:11:22 +02:00
USER_AGENT = f"microblogpub/{VERSION}"
AP_CONTENT_TYPE = "application/activity+json"
class Config(pydantic.BaseModel):
domain: str
username: str
admin_password: bytes
name: str
summary: str
https: bool
icon_url: str
secret: str
debug: bool = False
# Config items to make tests easier
2022-06-29 20:43:17 +02:00
sqlalchemy_database: str | None = None
2022-06-22 20:11:22 +02:00
key_path: str | None = None
def load_config() -> Config:
try:
return Config.parse_obj(
tomli.loads((ROOT_DIR / "data" / _CONFIG_FILE).read_text())
)
except FileNotFoundError:
2022-06-22 20:48:48 +02:00
raise ValueError(
f"Please run the configuration wizard, {_CONFIG_FILE} is missing"
)
2022-06-22 20:11:22 +02:00
def is_activitypub_requested(req: Request) -> bool:
accept_value = req.headers.get("accept")
if not accept_value:
return False
for val in {
"application/ld+json",
"application/activity+json",
}:
if accept_value.startswith(val):
return True
return False
def verify_password(pwd: str) -> bool:
return bcrypt.checkpw(pwd.encode(), CONFIG.admin_password)
CONFIG = load_config()
DOMAIN = CONFIG.domain
_SCHEME = "https" if CONFIG.https else "http"
ID = f"{_SCHEME}://{DOMAIN}"
USERNAME = CONFIG.username
BASE_URL = ID
DEBUG = CONFIG.debug
2022-06-29 20:43:17 +02:00
DB_PATH = CONFIG.sqlalchemy_database or ROOT_DIR / "data" / "microblogpub.db"
SQLALCHEMY_DATABASE_URL = f"sqlite:///{DB_PATH}"
2022-06-22 20:11:22 +02:00
KEY_PATH = (
(ROOT_DIR / CONFIG.key_path) if CONFIG.key_path else ROOT_DIR / "data" / "key.pem"
)
2022-06-27 20:55:44 +02:00
EMOJIS = "😺 😸 😹 😻 😼 😽 🙀 😿 😾"
# Emoji template for the FE
EMOJI_TPL = '<img src="/static/twemoji/{filename}.svg" alt="{raw}" class="emoji">'
_load_emojis(ROOT_DIR, BASE_URL)
2022-06-22 20:11:22 +02:00
session_serializer = TimedSerializer(CONFIG.secret, salt="microblogpub.login")
2022-07-11 09:34:06 +02:00
csrf_serializer = URLSafeTimedSerializer(
secrets.token_bytes(32),
salt=ID,
2022-06-22 20:11:22 +02:00
)
def generate_csrf_token() -> str:
2022-07-11 09:34:06 +02:00
return csrf_serializer.dumps(secrets.token_hex(16)) # type: ignore
2022-06-22 20:11:22 +02:00
def verify_csrf_token(csrf_token: str = Form()) -> None:
2022-07-11 09:34:06 +02:00
try:
csrf_serializer.loads(csrf_token, max_age=600)
except (itsdangerous.BadData, itsdangerous.SignatureExpired):
logger.exception("Failed to verify CSRF token")
2022-06-22 20:11:22 +02:00
raise HTTPException(status_code=403, detail="CSRF error")
return None