Skip to content

#94 feat(usermanagement): add secure self-delete endpoint with confirmation and test coverage - #97

Merged
luaraggio merged 4 commits into
mainfrom
94-endpoint-delete
Mar 14, 2026
Merged

luaraggio merged 4 commits into
mainfrom
94-endpoint-delete

Conversation

@dwbessa

@dwbessa dwbessa commented Mar 12, 2026

Copy link
Copy Markdown
Collaborator

Context

This PR introduces secure account deletion support in the usermanagement-service.
The endpoint allows full user removal, but only when:

  1. The authenticated user is deleting their own account.
  2. The request body contains the exact confirmation phrase: "Yes, delete my user".

What changed

1) Feature implementation (commit: 03621e9)

Added a new protected DELETE flow in usermanagement-service:

  • New endpoint: DELETE /users/{user_id}
  • Ownership enforcement: JWT sub must match user_id
  • Confirmation enforcement: exact phrase required in request payload
  • Domain command created for delete workflow
  • Service and contracts updated to expose delete operation
  • New domain error for invalid confirmation text
  • Exception handler mapping updated for:
    • invalid confirmation
    • unauthorized action
    • user not found

Files included:

  • backend/usermanagement-service/src/controller/user.py
  • backend/usermanagement-service/src/core/exception_handlers.py
  • backend/usermanagement-service/src/domain/contracts.py
  • backend/usermanagement-service/src/domain/exceptions.py
  • backend/usermanagement-service/src/domain/schemas/init.py
  • backend/usermanagement-service/src/domain/schemas/user.py
  • backend/usermanagement-service/src/domain/services/commands/init.py
  • backend/usermanagement-service/src/domain/services/commands/delete_user_profile.py
  • backend/usermanagement-service/src/domain/services/user.py

2) Test coverage (commit: 6fd1a06)

Added unit and integration tests covering the full delete behavior:

  • Unit tests:
    • successful deletion
    • invalid confirmation phrase
    • user not found
    • service-level delete execution
  • Integration tests:
    • 204 when deletion succeeds
    • 403 when trying to delete another user
    • 400 when confirmation text is invalid
  • Test infrastructure update:
    • Mock session now supports delete tracking

Files included:

  • backend/usermanagement-service/tests/conftest.py
  • backend/usermanagement-service/tests/integration/test_user_delete.py
  • backend/usermanagement-service/tests/unit/test_delete_user_profile.py

API behavior

Endpoint

DELETE /users/{user_id}

Request body

{
  "confirmation_text": "Yes, delete my user"
}

Responses

  • 204 No Content: user deleted successfully
  • 400 Bad Request: invalid delete confirmation phrase
  • 403 Forbidden: authenticated user is not the target user
  • 404 Not Found: target user does not exist

Validation and security rules

  • Deletion is self-service only (no cross-user deletion).
  • Confirmation text is strict and case-sensitive.
  • Deletion is persisted in database only after all validations pass.

Testing

Executed targeted tests for this feature and all passed:

  • tests/unit/test_delete_user_profile.py
  • tests/integration/test_user_delete.py

@dwbessa dwbessa linked an issue Mar 12, 2026 that may be closed by this pull request
@dwbessa
dwbessa requested a review from luaraggio March 12, 2026 01:06

@luaraggio luaraggio left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testes realizados

Registro e login de usuários:

  • criação usuários de teste (delete_test, user1, user2, integrity_test)
  • login funciona antes da deleção

Deleção em si:

  • endpoint testado manualmente
  • somente o próprio usuário pode se deletar (tentativa de deletar outro usuário retorna 403 Forbidden)
  • confirmação de deleção obrigatória: "Yes, delete my user" (texto exato). Diferente disso, dá erro
  • deleção sem o texto correto retorna 400 Invalid Delete Confirmation
  • usuário deletado não consegue mais logar (INVALID_CREDENTIALS)

Integridade entre serviços:

  • usermanagement-service: usuário realmente removido do banco
  • tournament-service: token antigo não funciona e o usuário deletado não acessa dados

Outros:

  • tentativa de deletar um usuário errado
  • um usuário tentando deletar o outro
  • logs OK
  • confirmação de deleção incorreta → 400 Invalid Delete Confirmation
  • login com usuário deletado → INVALID_CREDENTIALS
  • uso de token antigo para acessar outros serviços → falha de autenticação

**** testes unitários e de integração rodaram 100% também

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a secure self-service account deletion capability to the usermanagement-service, enforcing both ownership (JWT sub must match user_id) and an explicit confirmation phrase before permanently deleting the user record.

Changes:

  • Introduces DELETE /users/{user_id} with ownership enforcement and confirmation-text validation.
  • Adds a domain command (DeleteUserProfileCommand) plus service/contract/schema updates to support deletion.
  • Expands exception mapping and adds unit + integration tests for the delete flow.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
backend/usermanagement-service/src/controller/user.py Adds the DELETE /users/{user_id} endpoint and ownership check via JWT subject.
backend/usermanagement-service/src/core/exception_handlers.py Maps new/previous domain errors to correct HTTP status codes and error types.
backend/usermanagement-service/src/domain/contracts.py Extends the user service contract with delete_user_profile.
backend/usermanagement-service/src/domain/exceptions.py Adds InvalidDeleteConfirmationError.
backend/usermanagement-service/src/domain/schemas/user.py Adds DeleteUserRequest schema for confirmation payload.
backend/usermanagement-service/src/domain/schemas/init.py Exposes DeleteUserRequest from the schema package.
backend/usermanagement-service/src/domain/services/commands/delete_user_profile.py Implements the delete command with phrase validation + DB delete/commit.
backend/usermanagement-service/src/domain/services/commands/init.py Exports the new delete command.
backend/usermanagement-service/src/domain/services/user.py Adds UserService.delete_user_profile to execute the delete command.
backend/usermanagement-service/tests/conftest.py Extends MockSession to track deletes for unit tests.
backend/usermanagement-service/tests/unit/test_delete_user_profile.py Unit tests for the delete command and service method.
backend/usermanagement-service/tests/integration/test_user_delete.py Integration tests for success/403/400 delete scenarios.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +11 to +35
REQUIRED_DELETE_CONFIRMATION = "Yes, delete my user"


class DeleteUserProfileCommand(Command):
"""Command to delete a user profile with explicit confirmation."""

def __init__(self, session: AsyncSession, user_id: str, confirmation_text: str):
self.session = session
self.user_id = user_id
self.confirmation_text = confirmation_text

async def execute(self) -> None:
self._validate_confirmation_phrase()
user = await self._get_user()

await self.session.delete(user)
await self.session.commit()

logger.info("User profile deleted", user_id=self.user_id)

def _validate_confirmation_phrase(self) -> None:
if self.confirmation_text != REQUIRED_DELETE_CONFIRMATION:
raise InvalidDeleteConfirmationError(
f"Invalid confirmation text. Required: '{REQUIRED_DELETE_CONFIRMATION}'"
)
Comment on lines +12 to +18
@pytest.mark.asyncio
class TestUserDelete:
"""Integration tests for /users/{user_id} delete endpoint."""

async def test_delete_user_success_with_valid_confirmation(
self, client: AsyncClient, db_session: AsyncSession
):
@luaraggio

Copy link
Copy Markdown
Collaborator

Só não sei se é uma boa expor a frase de deleção.. já tinha pensado nisso e Copilot deu uma reforçada

@dwbessa

dwbessa commented Mar 13, 2026

Copy link
Copy Markdown
Collaborator Author

Só não sei se é uma boa expor a frase de deleção.. já tinha pensado nisso e Copilot deu uma reforçada

Em que sentido você fala sobre essa exposição da frase?
Porque vai ser uma frase conhecida por todos que tentarem deletar, minha ideia é ela aparecer no front pro usuário escrever manualmente igual é aqui no github quando você deleta um repositório que você tem que escrever "/" sabe? Não vai ser uma "frase secreta"
Mas não sei se entendi direito mesmo.

Sobre o copilot:
No comentário que ele fala que tá duplicado eu não vi aonde tá a duplicação kkkkk, colocou numa constante e seguiu a vida, não?
Agora sobre a frase estar hard-codada nos testes unitários isso eu posso adaptar, mas não vejo ganho real
@luaraggio

@luaraggio
luaraggio merged commit 5d8c72b into main Mar 14, 2026
7 checks passed
@luaraggio
luaraggio deleted the 94-endpoint-delete branch March 14, 2026 22:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Backend] - endpoint de remoção de usuario

3 participants