Add logout and fixed user specific requirements retrieval
This commit is contained in:
@@ -126,13 +126,25 @@ class AuthController:
|
||||
@staticmethod
|
||||
def logout() -> JSONResponse:
|
||||
"""
|
||||
Logout the user by clearing the authentication cookie.
|
||||
Logout the user by clearing the authentication cookie and returning
|
||||
the Keycloak logout URL for full session termination.
|
||||
|
||||
Returns:
|
||||
JSONResponse: Success message with cookie cleared.
|
||||
JSONResponse: Contains the Keycloak logout URL and clears the cookie.
|
||||
"""
|
||||
# Build Keycloak logout URL
|
||||
keycloak_logout_url = (
|
||||
f"{settings.keycloak_external_url}realms/{settings.keycloak_realm}"
|
||||
f"/protocol/openid-connect/logout"
|
||||
f"?client_id={settings.keycloak_client_id}"
|
||||
f"&post_logout_redirect_uri={settings.frontend_url}"
|
||||
)
|
||||
|
||||
response = JSONResponse(
|
||||
content={"message": "Successfully logged out"},
|
||||
content={
|
||||
"message": "Successfully logged out",
|
||||
"logout_url": keycloak_logout_url
|
||||
},
|
||||
status_code=status.HTTP_200_OK
|
||||
)
|
||||
|
||||
|
||||
@@ -266,34 +266,52 @@ def _build_requirement_response(req) -> RequirementResponse:
|
||||
|
||||
@app.get("/api/requirements", response_model=List[RequirementResponse])
|
||||
async def get_requirements(
|
||||
request: Request,
|
||||
group_id: Optional[int] = None,
|
||||
tag_id: Optional[int] = None,
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Get all requirements, optionally filtered by group or tag.
|
||||
Get all requirements for the authenticated user, optionally filtered by group or tag.
|
||||
|
||||
Args:
|
||||
group_id: Optional group ID to filter by
|
||||
tag_id: Optional tag ID to filter by
|
||||
|
||||
Returns:
|
||||
List of all requirements with their related data.
|
||||
List of requirements owned by the authenticated user.
|
||||
"""
|
||||
# Get the current user from cookie
|
||||
user_info = AuthController.get_current_user(request)
|
||||
|
||||
# Get the user's database ID
|
||||
from src.repositories import UserRepository
|
||||
user_repo = UserRepository(db)
|
||||
user = await user_repo.get_by_sub(user_info.sub)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="User not found in database"
|
||||
)
|
||||
|
||||
req_repo = RequirementRepository(db)
|
||||
|
||||
if group_id:
|
||||
requirements = await req_repo.get_by_group_id(group_id)
|
||||
requirements = await req_repo.get_by_group_id(group_id, user_id=user.id)
|
||||
elif tag_id:
|
||||
requirements = await req_repo.get_by_tag_id(tag_id)
|
||||
requirements = await req_repo.get_by_tag_id(tag_id, user_id=user.id)
|
||||
else:
|
||||
requirements = await req_repo.get_all()
|
||||
requirements = await req_repo.get_by_user_id(user.id)
|
||||
|
||||
return [_build_requirement_response(req) for req in requirements]
|
||||
|
||||
|
||||
@app.get("/api/requirements/{requirement_id}", response_model=RequirementResponse)
|
||||
async def get_requirement(requirement_id: int, db: AsyncSession = Depends(get_db)):
|
||||
async def get_requirement(
|
||||
requirement_id: int,
|
||||
request: Request,
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Get a specific requirement by ID.
|
||||
|
||||
@@ -301,8 +319,21 @@ async def get_requirement(requirement_id: int, db: AsyncSession = Depends(get_db
|
||||
requirement_id: The requirement ID
|
||||
|
||||
Returns:
|
||||
The requirement if found.
|
||||
The requirement if found and owned by the authenticated user.
|
||||
"""
|
||||
# Get the current user from cookie
|
||||
user_info = AuthController.get_current_user(request)
|
||||
|
||||
# Get the user's database ID
|
||||
from src.repositories import UserRepository
|
||||
user_repo = UserRepository(db)
|
||||
user = await user_repo.get_by_sub(user_info.sub)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="User not found in database"
|
||||
)
|
||||
|
||||
req_repo = RequirementRepository(db)
|
||||
requirement = await req_repo.get_by_id(requirement_id)
|
||||
if not requirement:
|
||||
@@ -310,6 +341,14 @@ async def get_requirement(requirement_id: int, db: AsyncSession = Depends(get_db
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Requirement with id {requirement_id} not found"
|
||||
)
|
||||
|
||||
# Verify user owns this requirement
|
||||
if requirement.user_id != user.id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="You do not have permission to access this requirement"
|
||||
)
|
||||
|
||||
return _build_requirement_response(requirement)
|
||||
|
||||
|
||||
@@ -386,6 +425,21 @@ async def update_requirement(
|
||||
)
|
||||
|
||||
req_repo = RequirementRepository(db)
|
||||
|
||||
# First check if requirement exists and user owns it
|
||||
existing_req = await req_repo.get_by_id(requirement_id)
|
||||
if not existing_req:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Requirement with id {requirement_id} not found"
|
||||
)
|
||||
|
||||
if existing_req.user_id != user.id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="You do not have permission to update this requirement"
|
||||
)
|
||||
|
||||
requirement = await req_repo.update(
|
||||
requirement_id=requirement_id,
|
||||
editor_id=user.id,
|
||||
@@ -396,12 +450,6 @@ async def update_requirement(
|
||||
group_ids=req_data.group_ids,
|
||||
)
|
||||
|
||||
if not requirement:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Requirement with id {requirement_id} not found"
|
||||
)
|
||||
|
||||
await db.commit()
|
||||
return _build_requirement_response(requirement)
|
||||
|
||||
@@ -418,16 +466,34 @@ async def delete_requirement(
|
||||
Args:
|
||||
requirement_id: The requirement ID to delete
|
||||
"""
|
||||
# Verify user is authenticated
|
||||
AuthController.get_current_user(request)
|
||||
# Get the current user from cookie
|
||||
user_info = AuthController.get_current_user(request)
|
||||
|
||||
# Get the user's database ID
|
||||
from src.repositories import UserRepository
|
||||
user_repo = UserRepository(db)
|
||||
user = await user_repo.get_by_sub(user_info.sub)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="User not found in database"
|
||||
)
|
||||
|
||||
req_repo = RequirementRepository(db)
|
||||
deleted = await req_repo.delete(requirement_id)
|
||||
|
||||
if not deleted:
|
||||
# First check if requirement exists and user owns it
|
||||
existing_req = await req_repo.get_by_id(requirement_id)
|
||||
if not existing_req:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Requirement with id {requirement_id} not found"
|
||||
)
|
||||
|
||||
if existing_req.user_id != user.id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="You do not have permission to delete this requirement"
|
||||
)
|
||||
|
||||
await req_repo.delete(requirement_id)
|
||||
await db.commit()
|
||||
|
||||
@@ -36,6 +36,29 @@ class RequirementRepository:
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
|
||||
async def get_by_user_id(self, user_id: int) -> List[Requirement]:
|
||||
"""
|
||||
Get all requirements for a specific user.
|
||||
|
||||
Args:
|
||||
user_id: The user's database ID
|
||||
|
||||
Returns:
|
||||
List of requirements owned by the user
|
||||
"""
|
||||
result = await self.session.execute(
|
||||
select(Requirement)
|
||||
.options(
|
||||
selectinload(Requirement.tag),
|
||||
selectinload(Requirement.priority),
|
||||
selectinload(Requirement.groups),
|
||||
selectinload(Requirement.validations).selectinload(Validation.status),
|
||||
)
|
||||
.where(Requirement.user_id == user_id)
|
||||
.order_by(Requirement.created_at.desc())
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
|
||||
async def get_by_id(self, requirement_id: int) -> Optional[Requirement]:
|
||||
"""
|
||||
Get a requirement by ID with its related data.
|
||||
@@ -60,17 +83,18 @@ class RequirementRepository:
|
||||
)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
async def get_by_group_id(self, group_id: int) -> List[Requirement]:
|
||||
async def get_by_group_id(self, group_id: int, user_id: Optional[int] = None) -> List[Requirement]:
|
||||
"""
|
||||
Get all requirements belonging to a specific group.
|
||||
|
||||
Args:
|
||||
group_id: The group ID
|
||||
user_id: Optional user ID to filter by
|
||||
|
||||
Returns:
|
||||
List of requirements in the group
|
||||
"""
|
||||
result = await self.session.execute(
|
||||
query = (
|
||||
select(Requirement)
|
||||
.options(
|
||||
selectinload(Requirement.tag),
|
||||
@@ -80,21 +104,27 @@ class RequirementRepository:
|
||||
)
|
||||
.join(Requirement.groups)
|
||||
.where(Group.id == group_id)
|
||||
.order_by(Requirement.created_at.desc())
|
||||
)
|
||||
|
||||
if user_id is not None:
|
||||
query = query.where(Requirement.user_id == user_id)
|
||||
|
||||
query = query.order_by(Requirement.created_at.desc())
|
||||
result = await self.session.execute(query)
|
||||
return list(result.scalars().all())
|
||||
|
||||
async def get_by_tag_id(self, tag_id: int) -> List[Requirement]:
|
||||
async def get_by_tag_id(self, tag_id: int, user_id: Optional[int] = None) -> List[Requirement]:
|
||||
"""
|
||||
Get all requirements with a specific tag.
|
||||
|
||||
Args:
|
||||
tag_id: The tag ID
|
||||
user_id: Optional user ID to filter by
|
||||
|
||||
Returns:
|
||||
List of requirements with the tag
|
||||
"""
|
||||
result = await self.session.execute(
|
||||
query = (
|
||||
select(Requirement)
|
||||
.options(
|
||||
selectinload(Requirement.tag),
|
||||
@@ -103,8 +133,13 @@ class RequirementRepository:
|
||||
selectinload(Requirement.validations).selectinload(Validation.status),
|
||||
)
|
||||
.where(Requirement.tag_id == tag_id)
|
||||
.order_by(Requirement.created_at.desc())
|
||||
)
|
||||
|
||||
if user_id is not None:
|
||||
query = query.where(Requirement.user_id == user_id)
|
||||
|
||||
query = query.order_by(Requirement.created_at.desc())
|
||||
result = await self.session.execute(query)
|
||||
return list(result.scalars().all())
|
||||
|
||||
async def create(
|
||||
|
||||
Reference in New Issue
Block a user