feat(demo): hidden /auth/demo login + require_human guard
Whitelisted emails enter the shared demo user via /auth/demo (lazily created, no OAuth token/scope), rate-limited per IP and answering uniformly so it can't be hammered as an enumeration oracle. Add a require_human dependency that blocks the demo account from quota-spending sync endpoints and the OAuth upgrade flow, and surface is_demo on /api/me.
This commit is contained in:
parent
796eecb3ac
commit
feb41f4514
3 changed files with 86 additions and 8 deletions
|
|
@ -77,6 +77,7 @@ def get_me(
|
|||
"display_name": user.display_name,
|
||||
"avatar_url": user.avatar_url,
|
||||
"role": user.role,
|
||||
"is_demo": user.is_demo,
|
||||
"can_read": has_read_scope(user),
|
||||
"can_write": has_write_scope(user),
|
||||
"pending_invites": pending_invites,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from sqlalchemy import and_, case, func, select, update
|
|||
from sqlalchemy.orm import Session
|
||||
|
||||
from app import quota, state
|
||||
from app.auth import current_user, has_read_scope
|
||||
from app.auth import current_user, has_read_scope, require_human
|
||||
from app.db import get_db
|
||||
from app.models import Channel, Subscription, User, Video
|
||||
from app.sync.runner import (
|
||||
|
|
@ -31,7 +31,7 @@ def _user_channels(db: Session, user: User) -> list[Channel]:
|
|||
|
||||
@router.post("/subscriptions")
|
||||
def sync_subscriptions(
|
||||
user: User = Depends(current_user), db: Session = Depends(get_db)
|
||||
user: User = Depends(require_human), db: Session = Depends(get_db)
|
||||
) -> dict:
|
||||
if not has_read_scope(user):
|
||||
# No YouTube read grant yet — the onboarding wizard hasn't been completed.
|
||||
|
|
@ -49,7 +49,7 @@ def sync_subscriptions(
|
|||
|
||||
@router.post("/rss")
|
||||
def sync_rss(
|
||||
user: User = Depends(current_user), db: Session = Depends(get_db)
|
||||
user: User = Depends(require_human), db: Session = Depends(get_db)
|
||||
) -> dict:
|
||||
new = run_rss_poll(db, _user_channels(db, user))
|
||||
return {"new_videos": new}
|
||||
|
|
@ -57,7 +57,7 @@ def sync_rss(
|
|||
|
||||
@router.post("/backfill")
|
||||
def sync_backfill(
|
||||
user: User = Depends(current_user),
|
||||
user: User = Depends(require_human),
|
||||
db: Session = Depends(get_db),
|
||||
max_channels: int = 25,
|
||||
) -> dict:
|
||||
|
|
@ -70,7 +70,7 @@ def sync_backfill(
|
|||
|
||||
@router.post("/enrich")
|
||||
def sync_enrich(
|
||||
user: User = Depends(current_user), db: Session = Depends(get_db)
|
||||
user: User = Depends(require_human), db: Session = Depends(get_db)
|
||||
) -> dict:
|
||||
before = quota.units_used_today(db)
|
||||
with quota.attribute(user.id, "enrich"):
|
||||
|
|
@ -175,7 +175,7 @@ def my_status(
|
|||
|
||||
@router.post("/deep-all")
|
||||
def deep_all(
|
||||
user: User = Depends(current_user),
|
||||
user: User = Depends(require_human),
|
||||
db: Session = Depends(get_db),
|
||||
on: bool = True,
|
||||
) -> dict:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue