Skip to content

VPR-59 [4/4] CMS: delegated block editing via per-block edit permissions#255

Open
rlorenzo wants to merge 22 commits into
VPR-59-cms-r3-frontendfrom
feature/cms-delegated-editing
Open

VPR-59 [4/4] CMS: delegated block editing via per-block edit permissions#255
rlorenzo wants to merge 22 commits into
VPR-59-cms-r3-frontendfrom
feature/cms-delegated-editing

Conversation

@rlorenzo

@rlorenzo rlorenzo commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Slice 4 of the CMS migration stack (stacks on #253; the diff shows only this feature). Adds delegated block editing: admins attach RAPS permissions to a block as its edit permissions, and holders of any of them can edit that block's content and attached files — without SVMSecure.CMS.ManageContentBlocks and without access to anything else.

⚠️ Manual schema change required (before deploying to each environment)

CREATE TABLE dbo.ContentBlockToEditPermission (
    ContentBlockEditPermissionID int IDENTITY(1,1) NOT NULL
        CONSTRAINT PK_ContentBlockToEditPermission PRIMARY KEY,
    ContentBlockID int NOT NULL
        CONSTRAINT FK_ContentBlockToEditPermissions_ContentBlock REFERENCES dbo.ContentBlock (ContentBlockId),
    permission varchar(500) NOT NULL
);

CREATE INDEX IX_ContentBlockToEditPermission_ContentBlockID
    ON dbo.ContentBlockToEditPermission (ContentBlockId) INCLUDE (permission);

Apply to dev → TEST → PROD ahead of the corresponding deploy (same manual-DDL process as VPR-143). Additive only; invisible to legacy ColdFusion (which reads only the view-permission table), so coexistence is unaffected.

Semantics

  • Edit access to block B = ManageContentBlocks or any of B's edit permissions (ANY-OF).
  • An empty edit list means manager-only — delegation is explicit (deliberately not the view list's "empty ⇒ all SVMSecure" rule).
  • Delegated editors CAN: edit content, attach existing managed files (capped, minimal-DTO search), upload new files into the block's folder with the block's view permissions (existing staged-upload flow), detach files, view/load version history, save (history attribution + 409 stale-edit guard unchanged).
  • Delegated editors CANNOT: rename (title or friendly name), change system/section/page/order/public access, change either permission list, delete/restore, reach the manage list/history pages, or touch the file store beyond the block-scoped operations above.
  • Soft-deleted blocks are not editable by delegates.

Implementation

  • New ContentBlockToEditPermission entity/table + CanEditAsync authorization (single-resolve permission HashSet, fail-closed).
  • Content-only PATCH gains attachment deltas; server-side field whitelisting is the enforcement boundary (the delegated DTO simply has no settings fields).
  • New content-scoped endpoints so the editor has ONE code path for all users: GET /editable, GET /attachable-files, per-block files/check-name, POST {id}/files, rollback-only DELETE {id}/files/{guid} (owner + folder + not-attached-elsewhere constrained).
  • Editor renders a capability mode: read-only settings summary for delegates; managers additionally get the "Edit access" selector. Hub gains a "Blocks you can edit" card for delegates.
  • Public fn endpoint DTO unchanged (no edit-permission disclosure).
  • Tests: authorization matrix, field-scoping, attachment deltas, rollback-delete constraints, editable/attachable filtering, capability-mode rendering, hub card, PATCH conflict flow.

Admins attach RAPS permissions to a content block as its EDIT
permissions; holders of any of them can edit that block without
SVMSecure.CMS.ManageContentBlocks.

- New ContentBlockToEditPermission entity/table (schema applied
  manually per environment; DDL in the PR description). An empty
  edit list means manager-only: delegation is explicit, deliberately
  not the view list "empty means all SVMSecure" rule
- CanEditAsync (manager OR edit-permission intersection, fail-closed,
  single-resolve HashSet) gates block load, history, diff, and the
  content-only PATCH, which now also carries attachment deltas; the
  delegated DTO has no settings fields, so field whitelisting is
  server-side by construction
- Content-scoped file endpoints (attachable search capped at 25 with
  a minimal DTO, per-block check-name/upload, rollback-only delete
  constrained to uploader+folder+not-attached-elsewhere) give the
  editor one code path for managers and delegates alike
- Editor renders a capability mode: read-only settings summary for
  delegates, full editor plus a new "Edit access" selector for
  managers; the hub gains a "Blocks you can edit" card; the editor
  route relies on server enforcement
- Delegates can view and load version history (restoring is a content
  save); soft-deleted blocks are not delegate-editable; the anonymous
  fn endpoint discloses nothing new
- 48 new tests (authorization matrix, field scoping, attachment
  deltas, rollback constraints, capability-mode rendering, hub card)

Copilot AI 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.

Pull request overview

Adds “delegated block editing” to the CMS migration stack by introducing per-block edit-permission lists (separate from view permissions) and a content-scoped editing/file workflow that allows non-managers to edit only content + attachments for blocks they’re delegated.

Changes:

  • Introduces ContentBlockToEditPermission (entity + EF mapping) and edit-authorization in CmsContentBlockService (CanEditAsync, editable-block listing, attachable-file search, rollback-delete eligibility).
  • Expands the CMS content API with content-scoped endpoints for delegated editors (editable blocks list, attachable-files search, block-scoped file check-name/upload/rollback-delete) and adds attachment deltas to the content-only PATCH.
  • Updates the Vue CMS editor and hub to support “capability mode” (manager vs delegated UI) and adds/updates frontend + backend tests for delegated scenarios.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
web/Models/VIPER/ContentBlockToEditPermission.cs New EF entity representing a block → edit-permission row.
web/Models/VIPER/ContentBlock.cs Adds navigation collection for edit-permission rows.
web/Classes/SQLContext/VIPERContext.cs Registers DbSet + model configuration for the new table.
web/Areas/CMS/Services/CmsContentBlockService.cs Core authorization + delegated editing behaviors (content-only update with attachment deltas, editable listing, attachable-file search, rollback-delete checks).
web/Areas/CMS/Models/DTOs/ContentBlockDto.cs Adds EditPermissions to block DTO; adds minimal CmsAttachableFileDto.
web/Areas/CMS/Models/CmsContentBlockMapper.cs Maps edit-permission list into DTO.
web/Areas/CMS/Models/CMSBlockAddEdit.cs Adds EditPermissions to create/update request model.
web/Areas/CMS/Controllers/CMSContentController.cs Widens selected endpoints to SVMSecure + CanEditAsync gate; adds content-scoped endpoints for delegated file attach/upload/rollback-delete.
VueApp/src/CMS/types/index.ts Adds editPermissions, plus types for editable blocks and attachable file search results.
VueApp/src/CMS/router/routes.ts Broadens content-block edit route permission to SVMSecure (server is source of truth).
VueApp/src/CMS/pages/ContentBlockEdit.vue Implements manager vs delegated capability UI; uses PATCH for delegated saves; switches attach/search and rollback paths to content-scoped APIs.
VueApp/src/CMS/pages/CmsHome.vue Adds “Blocks you can edit” hub card (delegated editors).
VueApp/src/CMS/components/PermissionSelector.vue Makes hint configurable to reuse for edit-permissions selector.
VueApp/src/CMS/components/InlineFileUpload.vue Adds block-scoped upload mode (check-name/upload/rollback-delete) for delegated editors.
VueApp/src/CMS/tests/inline-file-upload.test.ts Tests block-scoped uploader behavior (no folder/permission fields; no use-existing; POST overwrite).
VueApp/src/CMS/tests/content-block-edit-save.test.ts Updates save payload expectations; tests edit-access selector; updates rollback URL expectations; updates attachable-files behavior.
VueApp/src/CMS/tests/content-block-edit-delegated.test.ts New delegated-mode editor tests (read-only settings, PATCH save, conflict handling).
VueApp/src/CMS/tests/cms-home.test.ts New tests for delegated editable-blocks hub card behavior.
test/CMS/CMSContentControllerTests.cs Extends controller tests for CanEditAsync gating + content-scoped file ops.
test/CMS/CmsContentBlockServiceTests.cs Adds service-level tests for delegated authorization, attachment deltas, editable listing, attachable search, and rollback-delete eligibility.

Comment thread VueApp/src/CMS/pages/ContentBlockEdit.vue Outdated
Comment thread web/Areas/CMS/Controllers/CMSContentController.cs
Comment thread VueApp/src/CMS/pages/ContentBlockEdit.vue Outdated
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ed2020bf-169f-4364-9b44-b55f00d1c6a5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/cms-delegated-editing

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI 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.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 3 comments.

Comment thread web/Areas/CMS/Controllers/CMSContentController.cs Outdated
Comment thread web/Classes/SQLContext/VIPERContext.cs
Comment thread web/Areas/CMS/Services/CmsContentBlockService.cs
rlorenzo added 2 commits July 3, 2026 12:24
- the attach-by-search picker returned any active file by name, so
  delegated editors could see names/guids of files they cannot
  download; results now pass the same rules as downloads (public,
  unrestricted, permission match, person grant)
- CheckBlockFileName reads just the section path instead of loading
  and sanitizing the whole block, and returns 400 (not 404) for a
  block with no section path, matching UploadBlockFile

Copilot AI 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.

Pull request overview

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

Comment thread web/Areas/CMS/Services/CmsContentBlockService.cs
Comment thread web/Classes/SQLContext/VIPERContext.cs
rlorenzo added 2 commits July 3, 2026 12:54
…PATCH

The delegated-editor PATCH validated only that file GUIDs exist, so a
guessed GUID could attach a restricted file and leak its name through
the attachment list. Newly-added files now pass the same rules as
downloads; files already on the block are exempt so a delegate can
resend a manager-attached set.

Copilot AI 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.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.

Comment thread web/Areas/CMS/Services/CmsContentBlockService.cs
rlorenzo added 2 commits July 3, 2026 13:55
The endpoint and the hub card both document delegated-matches-only
with managers getting an empty list, but a manager whose permissions
intersected an edit list still got results.

Copilot AI 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.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.

Comment thread VueApp/src/CMS/router/routes.ts
The route required a plain SVMSecure meta permission, but the CMS
store only loads SVMSecure.CMS-prefixed permissions, so the guard
redirected every user - managers included - away from the editor.
The route now relies on requireLogin plus server-side 403s, per its
documented design.

Copilot AI 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.

Pull request overview

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

Comment thread web/Areas/CMS/Services/CmsContentBlockService.cs
Comment thread web/Areas/CMS/Services/CmsContentBlockService.cs
…attachments

- bound the name-match candidate pool (200) before materializing so a
  broad two-character term cannot pull the whole file store into memory
- the attach guard now requires active files, matching the search; a
  known GUID could otherwise resurrect a soft-deleted file into an
  active block

Copilot AI 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.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated no new comments.

Copilot AI 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.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated no new comments.

@rlorenzo rlorenzo changed the title [4/4] CMS: delegated block editing via per-block edit permissions VPR-59 [4/4] CMS: delegated block editing via per-block edit permissions Jul 6, 2026
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.

2 participants