openvpn-endpoint-server/server/app/api/endpoints.py

232 lines
7.2 KiB
Python

"""Endpoint management API routes."""
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from ..database import get_db
from ..models.endpoint import Endpoint, ApplicationTemplate
from ..models.gateway import Gateway
from ..models.user import User, UserRole
from ..models.access import UserGatewayAccess, UserEndpointAccess
from ..schemas.endpoint import (
EndpointCreate, EndpointUpdate, EndpointResponse,
ApplicationTemplateResponse
)
from .deps import get_current_user, require_admin
router = APIRouter()
def user_has_gateway_access(db: Session, user: User, gateway_id: int) -> bool:
"""Check if user has access to gateway."""
if user.role == UserRole.SUPER_ADMIN:
return True
if user.role == UserRole.ADMIN:
gateway = db.query(Gateway).filter(Gateway.id == gateway_id).first()
return gateway and gateway.tenant_id == user.tenant_id
access = db.query(UserGatewayAccess).filter(
UserGatewayAccess.user_id == user.id,
UserGatewayAccess.gateway_id == gateway_id
).first()
return access is not None
@router.get("/templates", response_model=list[ApplicationTemplateResponse])
def list_application_templates(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""List available application templates."""
templates = db.query(ApplicationTemplate).all()
return templates
@router.get("/gateway/{gateway_id}", response_model=list[EndpointResponse])
def list_endpoints_for_gateway(
gateway_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""List endpoints for a gateway."""
if not user_has_gateway_access(db, current_user, gateway_id):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="No access to this gateway"
)
endpoints = db.query(Endpoint).filter(Endpoint.gateway_id == gateway_id).all()
return endpoints
@router.post("/gateway/{gateway_id}", response_model=EndpointResponse, status_code=status.HTTP_201_CREATED)
def create_endpoint(
gateway_id: int,
endpoint_data: EndpointCreate,
db: Session = Depends(get_db),
current_user: User = Depends(require_admin)
):
"""Create a new endpoint for a gateway."""
gateway = db.query(Gateway).filter(Gateway.id == gateway_id).first()
if not gateway:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Gateway not found"
)
# Check tenant access
if current_user.role != UserRole.SUPER_ADMIN:
if gateway.tenant_id != current_user.tenant_id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Cannot add endpoints to gateways from other tenants"
)
endpoint = Endpoint(
**endpoint_data.model_dump(),
gateway_id=gateway_id
)
db.add(endpoint)
db.commit()
db.refresh(endpoint)
return endpoint
@router.get("/{endpoint_id}", response_model=EndpointResponse)
def get_endpoint(
endpoint_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""Get endpoint by ID."""
endpoint = db.query(Endpoint).filter(Endpoint.id == endpoint_id).first()
if not endpoint:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Endpoint not found"
)
if not user_has_gateway_access(db, current_user, endpoint.gateway_id):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="No access to this endpoint"
)
return endpoint
@router.put("/{endpoint_id}", response_model=EndpointResponse)
def update_endpoint(
endpoint_id: int,
endpoint_data: EndpointUpdate,
db: Session = Depends(get_db),
current_user: User = Depends(require_admin)
):
"""Update endpoint."""
endpoint = db.query(Endpoint).filter(Endpoint.id == endpoint_id).first()
if not endpoint:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Endpoint not found"
)
gateway = db.query(Gateway).filter(Gateway.id == endpoint.gateway_id).first()
# Check tenant access
if current_user.role != UserRole.SUPER_ADMIN:
if gateway.tenant_id != current_user.tenant_id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Cannot modify endpoints from other tenants"
)
update_data = endpoint_data.model_dump(exclude_unset=True)
for field, value in update_data.items():
setattr(endpoint, field, value)
db.commit()
db.refresh(endpoint)
return endpoint
@router.delete("/{endpoint_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_endpoint(
endpoint_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(require_admin)
):
"""Delete endpoint."""
endpoint = db.query(Endpoint).filter(Endpoint.id == endpoint_id).first()
if not endpoint:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Endpoint not found"
)
gateway = db.query(Gateway).filter(Gateway.id == endpoint.gateway_id).first()
# Check tenant access
if current_user.role != UserRole.SUPER_ADMIN:
if gateway.tenant_id != current_user.tenant_id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Cannot delete endpoints from other tenants"
)
db.delete(endpoint)
db.commit()
@router.post("/{endpoint_id}/access/{user_id}", status_code=status.HTTP_201_CREATED)
def grant_endpoint_access(
endpoint_id: int,
user_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(require_admin)
):
"""Grant user access to specific endpoint (fine-grained control)."""
endpoint = db.query(Endpoint).filter(Endpoint.id == endpoint_id).first()
if not endpoint:
raise HTTPException(status_code=404, detail="Endpoint not found")
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
existing = db.query(UserEndpointAccess).filter(
UserEndpointAccess.endpoint_id == endpoint_id,
UserEndpointAccess.user_id == user_id
).first()
if existing:
raise HTTPException(status_code=400, detail="Access already granted")
access = UserEndpointAccess(
user_id=user_id,
endpoint_id=endpoint_id,
granted_by_id=current_user.id
)
db.add(access)
db.commit()
return {"message": "Access granted"}
@router.delete("/{endpoint_id}/access/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
def revoke_endpoint_access(
endpoint_id: int,
user_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(require_admin)
):
"""Revoke user access to endpoint."""
access = db.query(UserEndpointAccess).filter(
UserEndpointAccess.endpoint_id == endpoint_id,
UserEndpointAccess.user_id == user_id
).first()
if not access:
raise HTTPException(status_code=404, detail="Access not found")
db.delete(access)
db.commit()