Skip to content

security(spp_change_request_v2): post-submit content freeze covers only field_mapping types — custom-strategy details stay writable [follow-up to #264] #403

Description

@gonzalesedwin1123

Follow-up from PR #264 (batch 2), surfaced while re-validating the CR cluster against the restored Add Member / Change HoH flows. Decision by Edwin 2026-08-10: file as a follow-up rather than widen #264.

Finding

_protected_content_fields() (spp_change_request_v2/models/change_request_detail_base.py) extends the post-submit freeze beyond field_to_modify only when the request type uses the field_mapping apply strategy:

protected = {"field_to_modify"}
cr_type = change_request.request_type_id
if cr_type.apply_strategy == "field_mapping":
    protected |= {m.source_field for m in cr_type.apply_mapping_ids if m.source_field}
return protected

Of the 11 change-request types shipped in data/default_types.xml, only two qualify:

Strategy Types
field_mapping — fully frozen edit_individual, edit_group
custom — only field_to_modify frozen add_member, change_hoh, remove_member, transfer_member, exit_registrant, update_id, create_group, split_household, merge_registrants

So on a submitted or approved custom-strategy request, the detail fields the apply strategy actually reads remain writable over RPC. For the restored Add Member that is given_name, family_name, birthdate, gender_id, relationship_id, id_number, phone — and strategies/add_member.py reads exactly those to create a new res.partner and its group membership. For Change HoH it is new_head_id, new_head_membership_id, previous_head_new_role_id.

Net effect: a requester can have one thing approved and a different thing applied, for 9 of 11 types.

Not a regression

This gap existed before the #382/#871 revert too — the redesigned details were also custom-strategy. What changed is blast radius: post-revert, Add Member's apply creates a person from the unfrozen fields rather than linking an existing one. #264 is a strict improvement and should merge as-is; this issue records what it does not cover.

Suggested fix

Give _protected_content_fields() a per-detail-model override hook so a custom-strategy detail can declare its own frozen set, then implement it for the highest-value details first — spp.cr.detail.add_member and spp.cr.detail.change_hoh.

This is design work, not a patch: each custom strategy has to be read to determine which fields actually feed the apply, and apply-output fields (created_individual_id and similar) must stay writable so strategies can record their results post-approval.

Notes

Related: #264, #346.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions