Restrict hard deletion of versioned concepts (solves https://github.com/OpenConceptLab/ocl_issues/issues/2570)#879
Conversation
* 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
|
This solves OpenConceptLab/ocl_issues#2570 @snyaggarwal @jamlung-ri |
There was a problem hiding this comment.
💡 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".
| locked_concepts = list(Concept.objects.select_for_update().filter( | ||
| parent_id=concept.parent_id, | ||
| versioned_object_id=concept.versioned_object_id, | ||
| )) |
There was a problem hiding this comment.
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
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
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
select_for_update.Tests
10.00/10.git diff --check: passing.Files changed
core/concepts/constants.pycore/concepts/models.pycore/concepts/views.pycore/integration_tests/tests_concepts.pyNo database migration or public schema change is required.