"""Applications management router""" from fastapi import APIRouter, Depends, HTTPException, status, Query from typing import List from bson import ObjectId from datetime import datetime from app.models.application import Application, ApplicationCreate, ApplicationUpdate, ApplicationPublic from app.models.user import User, UserRole from app.routers.auth import get_current_user from app.utils.database import get_database from app.utils.security import generate_client_id, generate_client_secret router = APIRouter(prefix="/applications", tags=["Applications"]) def require_admin(current_user: User = Depends(get_current_user)) -> User: """Require admin role""" if current_user.role not in [UserRole.SYSTEM_ADMIN, UserRole.GROUP_ADMIN]: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions" ) return current_user @router.get("/", response_model=List[Application]) async def get_applications( skip: int = Query(0, ge=0), limit: int = Query(10, ge=1, le=100), current_user: User = Depends(get_current_user) ): """Get applications (own applications for users, all for admins)""" db = get_database() query = {} if current_user.role not in [UserRole.SYSTEM_ADMIN, UserRole.GROUP_ADMIN]: query = {"created_by": str(current_user.id)} apps = await db.applications.find(query).skip(skip).limit(limit).to_list(limit) return [Application(**app) for app in apps] @router.post("/", response_model=Application) async def create_application( app_data: ApplicationCreate, current_user: User = Depends(get_current_user) ): """Create a new application""" db = get_database() # Generate client credentials client_id = generate_client_id() client_secret = generate_client_secret() # Check if client_id already exists (unlikely but possible) existing_app = await db.applications.find_one({"client_id": client_id}) if existing_app: # Regenerate if collision client_id = generate_client_id() # Prepare application document app_doc = { **app_data.model_dump(), "client_id": client_id, "client_secret": client_secret, "created_by": str(current_user.id), "created_at": datetime.utcnow(), "updated_at": datetime.utcnow() } # Insert application result = await db.applications.insert_one(app_doc) app_doc["_id"] = result.inserted_id return Application(**app_doc) @router.get("/public/{client_id}", response_model=ApplicationPublic) async def get_application_public(client_id: str): """Get public application information (for OAuth flow)""" db = get_database() app = await db.applications.find_one({"client_id": client_id, "is_active": True}) if not app: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Application not found" ) return ApplicationPublic(**app) @router.get("/{app_id}", response_model=Application) async def get_application(app_id: str, current_user: User = Depends(get_current_user)): """Get application by ID""" db = get_database() try: app = await db.applications.find_one({"_id": ObjectId(app_id)}) if not app: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Application not found" ) # Check permissions if str(app["created_by"]) != str(current_user.id) and current_user.role not in [UserRole.SYSTEM_ADMIN, UserRole.GROUP_ADMIN]: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions" ) return Application(**app) except Exception as e: if isinstance(e, HTTPException): raise raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid application ID" ) @router.put("/{app_id}", response_model=Application) async def update_application( app_id: str, app_update: ApplicationUpdate, current_user: User = Depends(get_current_user) ): """Update application""" db = get_database() try: # Get existing application app = await db.applications.find_one({"_id": ObjectId(app_id)}) if not app: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Application not found" ) # Check permissions if str(app["created_by"]) != str(current_user.id) and current_user.role not in [UserRole.SYSTEM_ADMIN, UserRole.GROUP_ADMIN]: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions" ) # Prepare update data update_data = app_update.model_dump(exclude_unset=True) update_data["updated_at"] = datetime.utcnow() # Update application result = await db.applications.update_one( {"_id": ObjectId(app_id)}, {"$set": update_data} ) # Return updated application app = await db.applications.find_one({"_id": ObjectId(app_id)}) return Application(**app) except Exception as e: if isinstance(e, HTTPException): raise raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=str(e) ) @router.post("/{app_id}/regenerate-secret", response_model=Application) async def regenerate_client_secret( app_id: str, current_user: User = Depends(get_current_user) ): """Regenerate client secret""" db = get_database() try: # Get existing application app = await db.applications.find_one({"_id": ObjectId(app_id)}) if not app: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Application not found" ) # Check permissions if str(app["created_by"]) != str(current_user.id) and current_user.role not in [UserRole.SYSTEM_ADMIN, UserRole.GROUP_ADMIN]: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions" ) # Generate new secret new_secret = generate_client_secret() # Update application await db.applications.update_one( {"_id": ObjectId(app_id)}, {"$set": { "client_secret": new_secret, "updated_at": datetime.utcnow() }} ) # Return updated application app = await db.applications.find_one({"_id": ObjectId(app_id)}) return Application(**app) except Exception as e: if isinstance(e, HTTPException): raise raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=str(e) ) @router.delete("/{app_id}") async def delete_application( app_id: str, current_user: User = Depends(get_current_user) ): """Delete application""" db = get_database() try: # Get existing application app = await db.applications.find_one({"_id": ObjectId(app_id)}) if not app: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Application not found" ) # Check permissions if str(app["created_by"]) != str(current_user.id) and current_user.role != UserRole.SYSTEM_ADMIN: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions" ) # Delete application result = await db.applications.delete_one({"_id": ObjectId(app_id)}) return {"message": "Application deleted successfully"} except Exception as e: if isinstance(e, HTTPException): raise raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid application ID" )