Skip to content

Restrict hard deletion of versioned concepts (solves https://github.com/OpenConceptLab/ocl_issues/issues/2570)#879

Open
filiperochalopes wants to merge 2 commits into
OpenConceptLab:masterfrom
filiperochalopes:feature/delete-concept-from-head-without-admin-role
Open

Restrict hard deletion of versioned concepts (solves https://github.com/OpenConceptLab/ocl_issues/issues/2570)#879
filiperochalopes wants to merge 2 commits into
OpenConceptLab:masterfrom
filiperochalopes:feature/delete-concept-from-head-without-admin-role

Conversation

@filiperochalopes

Copy link
Copy Markdown
Contributor

Summary

Allows users with write permission on a source to hard delete concepts that have never belonged to a draft or released source version.

This removes the administrator dependency that previously limited unpublished concept management from CIEL Lab.

Authorization decisions

  • User source owners may delete eligible concepts.
  • Organization members may delete eligible concepts from organization sources.
  • Usuário autenticado exclui conceito de source com acesso público de edição.
  • Users without write access and anonymous users remain blocked.
  • Administrators retain unrestricted hard delete access.
  • async=true, db=true, and individual version deletion remain administrator-only.

Eligibility rule

A non-administrator may hard delete a concept only when none of its current or historical rows is associated with a source version other than HEAD.

Draft and released versions both make the concept ineligible. Blocked requests return 409 Conflict.

Complete history deletion

The endpoint deletes the versioned concept root. Its version rows reference that root using on_delete=CASCADE, so the operation removes every HEAD-only concept version and its dependent names, descriptions, and associations.

A regression test creates four edits, producing six database rows including the root and initial version, and verifies that all six are removed.

Integrity considerations

  • All source-scoped concept rows are locked with select_for_update.
  • Eligibility validation and deletion occur in one transaction.
  • Blocked deletion preserves concepts, versions, names, descriptions, mappings, and source-version associations.
  • Source concept counts are updated after successful deletion.

Tests

  • 16 new authorization, history, cascade, and integrity tests: passing.
  • 4 existing delete and retire regression tests: passing.
  • 20 relevant tests total: passing.
  • Pylint: 10.00/10.
  • Python compilation and git diff --check: passing.

Files changed

  • core/concepts/constants.py
  • core/concepts/models.py
  • core/concepts/views.py
  • core/integration_tests/tests_concepts.py

No database migration or public schema change is required.

* Add CONCEPT_HARD_DELETE_REQUIRES_HEAD_ONLY to enforce deletion policy

* Implement belongs_to_non_head_source_version to detect snapshotted concepts

* Prevent non-admin hard deletes on non-HEAD concepts in view logic

* Add integration tests to validate deletion protection and access control
@filiperochalopes filiperochalopes changed the title feat(concepts): restrict hard deletion of versioned concepts Restrict hard deletion of versioned concepts (solves https://github.com/OpenConceptLab/ocl_issues/issues/2570) Jun 12, 2026
@filiperochalopes

Copy link
Copy Markdown
Contributor Author

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f5bc09a683

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread core/concepts/views.py
Comment on lines +409 to +412
locked_concepts = list(Concept.objects.select_for_update().filter(
parent_id=concept.parent_id,
versioned_object_id=concept.versioned_object_id,
))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Block deletion while source-version seeding is pending

When a source version has just been created through the normal asynchronous path, persist_new_version returns before seed_children_to_new_version calls Source.seed_concepts, so the new version temporarily has no concept associations. During that window this lock and the subsequent belongs_to_non_head_source_version() check see no non-HEAD membership and allow an editor to delete the concept; the seeding task then either omits the deleted concept or fails if it already selected the concept ID before the cascade. Source.seed_concepts in core/sources/models.py does not acquire these row locks, so locking only the concept rows here does not synchronize the operations and can corrupt a draft or released snapshot.

Useful? React with 👍 / 👎.

…eding

* Queue seed tasks with transaction.on_commit to ensure consistency

* Detect pending source version seeds to block unsafe deletions

* Update view logic to prevent hard deletes when seeds are pending

* Add tests to validate deletion blocking and normal behavior
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.

1 participant