Harden auth and persist report schedules
This commit is contained in:
@@ -15,7 +15,13 @@ from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.database import get_db
|
||||
from app.core.auth import issue_access_token
|
||||
from app.core.auth import (
|
||||
AuthContext,
|
||||
ensure_website_scope_matches,
|
||||
get_auth_context,
|
||||
issue_access_token,
|
||||
require_website_auth,
|
||||
)
|
||||
from app.models.user import User
|
||||
from app.models.website import Website
|
||||
from app.schemas.wordpress import (
|
||||
@@ -44,6 +50,16 @@ logger = logging.getLogger(__name__)
|
||||
router = APIRouter(prefix="/wordpress", tags=["wordpress"])
|
||||
|
||||
|
||||
def _api_role_from_wordpress_roles(roles: list[str]) -> str:
|
||||
"""Map WordPress roles to API roles used by route authorization."""
|
||||
normalized_roles = {str(role).strip().lower() for role in roles}
|
||||
if normalized_roles & {"super_admin", "system_admin"}:
|
||||
return "system_admin"
|
||||
if normalized_roles & {"administrator", "admin"}:
|
||||
return "admin"
|
||||
return "student"
|
||||
|
||||
|
||||
def get_website_id_from_header(
|
||||
x_website_id: Optional[str] = Header(None, alias="X-Website-ID"),
|
||||
) -> int:
|
||||
@@ -132,7 +148,7 @@ async def sync_users_endpoint(
|
||||
Raises:
|
||||
HTTPException: If website not found, token invalid, or API error
|
||||
"""
|
||||
enforce_rate_limit(
|
||||
await enforce_rate_limit(
|
||||
request,
|
||||
scope="wordpress.sync_users",
|
||||
max_requests=20,
|
||||
@@ -230,7 +246,7 @@ async def verify_session_endpoint(
|
||||
Raises:
|
||||
HTTPException: If website not found or API error
|
||||
"""
|
||||
enforce_rate_limit(
|
||||
await enforce_rate_limit(
|
||||
http_request,
|
||||
scope="wordpress.verify_session",
|
||||
max_requests=60,
|
||||
@@ -273,7 +289,7 @@ async def verify_session_endpoint(
|
||||
},
|
||||
access_token=issue_access_token(
|
||||
website_id=request.website_id,
|
||||
role="student",
|
||||
role=_api_role_from_wordpress_roles(wp_user_info.roles),
|
||||
wp_user_id=request.wp_user_id,
|
||||
expires_in_seconds=3600 * 24,
|
||||
),
|
||||
@@ -310,6 +326,7 @@ async def verify_session_endpoint(
|
||||
async def get_website_users(
|
||||
website_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
auth: AuthContext = Depends(get_auth_context),
|
||||
page: int = 1,
|
||||
page_size: int = 50,
|
||||
) -> UserListResponse:
|
||||
@@ -328,6 +345,9 @@ async def get_website_users(
|
||||
Raises:
|
||||
HTTPException: If website not found
|
||||
"""
|
||||
auth_website_id = require_website_auth(auth, allowed_roles={"admin", "system_admin"})
|
||||
ensure_website_scope_matches(auth_website_id, website_id)
|
||||
|
||||
# Validate website exists
|
||||
await get_valid_website(website_id, db)
|
||||
|
||||
@@ -374,6 +394,7 @@ async def get_user_endpoint(
|
||||
website_id: int,
|
||||
wp_user_id: str,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
auth: AuthContext = Depends(get_auth_context),
|
||||
) -> WordPressUserResponse:
|
||||
"""
|
||||
Get a specific user by WordPress user ID.
|
||||
@@ -389,6 +410,16 @@ async def get_user_endpoint(
|
||||
Raises:
|
||||
HTTPException: If website or user not found
|
||||
"""
|
||||
auth_website_id = require_website_auth(
|
||||
auth, allowed_roles={"student", "admin", "system_admin"}
|
||||
)
|
||||
ensure_website_scope_matches(auth_website_id, website_id)
|
||||
if auth.role == "student" and auth.wp_user_id != wp_user_id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="User does not belong to this authenticated user",
|
||||
)
|
||||
|
||||
# Validate website exists
|
||||
await get_valid_website(website_id, db)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user