add forward auth header reading
All checks were successful
ci/woodpecker/push/build Pipeline was successful
All checks were successful
ci/woodpecker/push/build Pipeline was successful
This commit is contained in:
parent
b18572ee56
commit
4e89158d96
4 changed files with 99 additions and 13 deletions
105
src/main.py
105
src/main.py
|
@ -12,7 +12,7 @@ from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.responses import FileResponse, JSONResponse, RedirectResponse
|
from fastapi.responses import FileResponse, JSONResponse, RedirectResponse
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from sqlmodel import Session, and_, select
|
from sqlmodel import Session, and_, or_, select
|
||||||
|
|
||||||
import models
|
import models
|
||||||
from process import AudioProcessor
|
from process import AudioProcessor
|
||||||
|
@ -30,7 +30,21 @@ def get_session() -> Generator[Session, Any, None]:
|
||||||
yield session
|
yield session
|
||||||
|
|
||||||
|
|
||||||
|
def handle_user_auth(request: Request) -> tuple[str, str]:
|
||||||
|
if (
|
||||||
|
settings.forward_auth_name_header is None
|
||||||
|
or settings.forward_auth_uid_header is None
|
||||||
|
):
|
||||||
|
return ("default", "Admin")
|
||||||
|
|
||||||
|
return (
|
||||||
|
request.headers.get(settings.forward_auth_uid_header, "default"),
|
||||||
|
request.headers.get(settings.forward_auth_name_header, "Admin"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
SessionDep = Annotated[Session, Depends(get_session)]
|
SessionDep = Annotated[Session, Depends(get_session)]
|
||||||
|
AuthDep = Annotated[tuple[str, str], Depends(handle_user_auth)]
|
||||||
|
|
||||||
|
|
||||||
log = structlog.get_logger()
|
log = structlog.get_logger()
|
||||||
|
@ -46,13 +60,23 @@ audio_processor.start_processing()
|
||||||
|
|
||||||
|
|
||||||
@app.get("/admin")
|
@app.get("/admin")
|
||||||
def admin_list_podcasts(session: SessionDep, request: Request):
|
def admin_list_podcasts(session: SessionDep, request: Request, user: AuthDep):
|
||||||
podcasts = session.exec(select(models.Podcast)).all()
|
podcasts = session.exec(
|
||||||
|
select(models.Podcast).where(
|
||||||
|
or_(
|
||||||
|
models.Podcast.owner_id == user[0],
|
||||||
|
models.Podcast.owner_id == None,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).all()
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request=request,
|
request=request,
|
||||||
name="admin_feeds.html.j2",
|
name="admin_feeds.html.j2",
|
||||||
context={"podcasts": podcasts},
|
context={
|
||||||
|
"podcasts": podcasts,
|
||||||
|
"user_name": user[1],
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,6 +92,7 @@ def admin_create_podcast(request: Request):
|
||||||
def admin_create_podcast_post(
|
def admin_create_podcast_post(
|
||||||
session: SessionDep,
|
session: SessionDep,
|
||||||
request: Request,
|
request: Request,
|
||||||
|
user: AuthDep,
|
||||||
name: Annotated[str, Form()],
|
name: Annotated[str, Form()],
|
||||||
):
|
):
|
||||||
if name.strip() == "":
|
if name.strip() == "":
|
||||||
|
@ -81,7 +106,7 @@ def admin_create_podcast_post(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
podcast = models.Podcast(name=name, description=name)
|
podcast = models.Podcast(name=name, description=name, owner_id=user[0])
|
||||||
|
|
||||||
session.add(podcast)
|
session.add(podcast)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
@ -90,9 +115,19 @@ def admin_create_podcast_post(
|
||||||
|
|
||||||
|
|
||||||
@app.get("/admin/{podcast_id}")
|
@app.get("/admin/{podcast_id}")
|
||||||
def admin_list_podcast(session: SessionDep, request: Request, podcast_id: str):
|
def admin_list_podcast(
|
||||||
|
session: SessionDep, request: Request, podcast_id: str, user: AuthDep
|
||||||
|
):
|
||||||
podcast = session.exec(
|
podcast = session.exec(
|
||||||
select(models.Podcast).where(models.Podcast.id == podcast_id)
|
select(models.Podcast).where(
|
||||||
|
and_(
|
||||||
|
models.Podcast.id == podcast_id,
|
||||||
|
or_(
|
||||||
|
models.Podcast.owner_id == user[0],
|
||||||
|
models.Podcast.owner_id == None,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
if podcast is None:
|
if podcast is None:
|
||||||
|
@ -151,7 +186,11 @@ def finish_processing(
|
||||||
|
|
||||||
@app.post("/admin/{podcast_id}/upload")
|
@app.post("/admin/{podcast_id}/upload")
|
||||||
async def admin_upload_episode(
|
async def admin_upload_episode(
|
||||||
session: SessionDep, request: Request, podcast_id: str, file: UploadFile
|
session: SessionDep,
|
||||||
|
request: Request,
|
||||||
|
podcast_id: str,
|
||||||
|
file: UploadFile,
|
||||||
|
user: AuthDep,
|
||||||
):
|
):
|
||||||
file_id = request.headers.get("uploader-file-id")
|
file_id = request.headers.get("uploader-file-id")
|
||||||
chunks_total = int(request.headers.get("uploader-chunks-total"))
|
chunks_total = int(request.headers.get("uploader-chunks-total"))
|
||||||
|
@ -164,7 +203,15 @@ async def admin_upload_episode(
|
||||||
file_id = "".join(c for c in file_id if c.isalnum()).strip()
|
file_id = "".join(c for c in file_id if c.isalnum()).strip()
|
||||||
|
|
||||||
podcast = session.exec(
|
podcast = session.exec(
|
||||||
select(models.Podcast).where(models.Podcast.id == podcast_id)
|
select(models.Podcast).where(
|
||||||
|
and_(
|
||||||
|
models.Podcast.id == podcast_id,
|
||||||
|
or_(
|
||||||
|
models.Podcast.owner_id == user[0],
|
||||||
|
models.Podcast.owner_id == None,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
if podcast is None:
|
if podcast is None:
|
||||||
|
@ -216,12 +263,17 @@ def admin_delete_episode(
|
||||||
request: Request,
|
request: Request,
|
||||||
podcast_id: str,
|
podcast_id: str,
|
||||||
episode_id: str,
|
episode_id: str,
|
||||||
|
user: AuthDep,
|
||||||
):
|
):
|
||||||
episode = session.exec(
|
episode = session.exec(
|
||||||
select(models.PodcastEpisode).where(
|
select(models.PodcastEpisode).where(
|
||||||
and_(
|
and_(
|
||||||
models.PodcastEpisode.id == episode_id,
|
models.PodcastEpisode.id == episode_id,
|
||||||
models.PodcastEpisode.podcast_id == podcast_id,
|
models.PodcastEpisode.podcast_id == podcast_id,
|
||||||
|
or_(
|
||||||
|
models.Podcast.owner_id == user[0],
|
||||||
|
models.Podcast.owner_id == None,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).first()
|
).first()
|
||||||
|
@ -250,12 +302,17 @@ def admin_edit_episode(
|
||||||
request: Request,
|
request: Request,
|
||||||
podcast_id: str,
|
podcast_id: str,
|
||||||
episode_id: str,
|
episode_id: str,
|
||||||
|
user: AuthDep,
|
||||||
):
|
):
|
||||||
episode = session.exec(
|
episode = session.exec(
|
||||||
select(models.PodcastEpisode).where(
|
select(models.PodcastEpisode).where(
|
||||||
and_(
|
and_(
|
||||||
models.PodcastEpisode.id == episode_id,
|
models.PodcastEpisode.id == episode_id,
|
||||||
models.PodcastEpisode.podcast_id == podcast_id,
|
models.PodcastEpisode.podcast_id == podcast_id,
|
||||||
|
or_(
|
||||||
|
models.Podcast.owner_id == user[0],
|
||||||
|
models.Podcast.owner_id == None,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).first()
|
).first()
|
||||||
|
@ -284,6 +341,7 @@ def admin_edit_episode_post(
|
||||||
request: Request,
|
request: Request,
|
||||||
podcast_id: str,
|
podcast_id: str,
|
||||||
episode_id: str,
|
episode_id: str,
|
||||||
|
user: AuthDep,
|
||||||
name: Annotated[str, Form()],
|
name: Annotated[str, Form()],
|
||||||
description: Annotated[str, Form()],
|
description: Annotated[str, Form()],
|
||||||
):
|
):
|
||||||
|
@ -292,6 +350,10 @@ def admin_edit_episode_post(
|
||||||
and_(
|
and_(
|
||||||
models.PodcastEpisode.id == episode_id,
|
models.PodcastEpisode.id == episode_id,
|
||||||
models.PodcastEpisode.podcast_id == podcast_id,
|
models.PodcastEpisode.podcast_id == podcast_id,
|
||||||
|
or_(
|
||||||
|
models.Podcast.owner_id == user[0],
|
||||||
|
models.Podcast.owner_id == None,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).first()
|
).first()
|
||||||
|
@ -322,9 +384,19 @@ def admin_edit_episode_post(
|
||||||
|
|
||||||
|
|
||||||
@app.get("/admin/{podcast_id}/edit")
|
@app.get("/admin/{podcast_id}/edit")
|
||||||
def admin_edit_podcast(session: SessionDep, request: Request, podcast_id: str):
|
def admin_edit_podcast(
|
||||||
|
session: SessionDep, request: Request, user: AuthDep, podcast_id: str
|
||||||
|
):
|
||||||
podcast = session.exec(
|
podcast = session.exec(
|
||||||
select(models.Podcast).where(models.Podcast.id == podcast_id)
|
select(models.Podcast).where(
|
||||||
|
and_(
|
||||||
|
models.Podcast.id == podcast_id,
|
||||||
|
or_(
|
||||||
|
models.Podcast.owner_id == user[0],
|
||||||
|
models.Podcast.owner_id == None,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
if podcast is None:
|
if podcast is None:
|
||||||
|
@ -350,12 +422,21 @@ def admin_edit_podcast_post(
|
||||||
session: SessionDep,
|
session: SessionDep,
|
||||||
request: Request,
|
request: Request,
|
||||||
podcast_id: str,
|
podcast_id: str,
|
||||||
|
user: AuthDep,
|
||||||
name: Annotated[str, Form()],
|
name: Annotated[str, Form()],
|
||||||
description: Annotated[str, Form()],
|
description: Annotated[str, Form()],
|
||||||
image: Optional[UploadFile] = None,
|
image: Optional[UploadFile] = None,
|
||||||
):
|
):
|
||||||
podcast = session.exec(
|
podcast = session.exec(
|
||||||
select(models.Podcast).where(models.Podcast.id == podcast_id)
|
select(models.Podcast).where(
|
||||||
|
and_(
|
||||||
|
models.Podcast.id == podcast_id,
|
||||||
|
or_(
|
||||||
|
models.Podcast.owner_id == user[0],
|
||||||
|
models.Podcast.owner_id == None,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
if podcast is None:
|
if podcast is None:
|
||||||
|
|
|
@ -15,6 +15,7 @@ class Podcast(SQLModel, table=True):
|
||||||
description: str
|
description: str
|
||||||
explicit: bool = Field(default=True)
|
explicit: bool = Field(default=True)
|
||||||
image_filename: Optional[str] = Field(default=None)
|
image_filename: Optional[str] = Field(default=None)
|
||||||
|
owner_id: Optional[str] = Field(default=None)
|
||||||
|
|
||||||
episodes: list["PodcastEpisode"] = Relationship(back_populates="podcast")
|
episodes: list["PodcastEpisode"] = Relationship(back_populates="podcast")
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
@ -7,6 +8,8 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
directory: Path = Field(default=Path.cwd() / "data")
|
directory: Path = Field(default=Path.cwd() / "data")
|
||||||
uploads_directory: Path = Field(default=Path.cwd() / "uploads")
|
uploads_directory: Path = Field(default=Path.cwd() / "uploads")
|
||||||
|
forward_auth_name_header: Optional[str] = Field(default=None)
|
||||||
|
forward_auth_uid_header: Optional[str] = Field(default=None)
|
||||||
|
|
||||||
model_config = SettingsConfigDict(env_nested_delimiter="__", env_prefix="PG_")
|
model_config = SettingsConfigDict(env_nested_delimiter="__", env_prefix="PG_")
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
{% extends 'admin_layout.html.j2' %}
|
{% extends 'admin_layout.html.j2' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
<h1>Podcasts</h1>
|
<h1>Hello {{ user_name }}!</h1>
|
||||||
|
<h2>My Podcasts</h2>
|
||||||
<p>
|
<p>
|
||||||
<b>Actions:</b> <a href="/admin/new">Create</a>
|
<b>Actions:</b> <a href="/admin/new">Create</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
Loading…
Reference in a new issue