Add logout and fixed user specific requirements retrieval

This commit is contained in:
gulimabr
2025-11-30 19:24:06 -03:00
parent eb70598cab
commit 67efbfd317
4 changed files with 148 additions and 27 deletions

View File

@@ -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
)

View File

@@ -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()

View File

@@ -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(