- Affected version
- 2.3
Setting secondary_group_ids via the REST API does not merge groups from xf_user_group_change into the rebuilt cache. Groups added by internal systems (moderator promotions, user upgrades, automatic promotions) are silently removed from xf_user_group_relation and the cached secondary_group_ids column, even though the xf_user_group_change records remain intact.
Steps to reproduce
1. Have a user who is a moderator (entry in xf_moderator, EXAMPLE: group 4 added via xf_user_group_change)
2. Confirm GET /api/users/{id}/ returns group 4 in secondary_group_ids
3. POST /api/users/{id}/ with secondary_group_ids[] set to the same list minus group 4
4. Group 4 is removed from the user's effective groups despite the xf_user_group_change record still existing
Expected behavior
The POST should rebuild the user's effective secondary groups by merging the provided direct groups with groups from xf_user_group_change, the same way the cache is originally built. The xf_user_group_change record should take precedence — if the moderator system says the user should be in group 4, that group should persist regardless of what secondary_group_ids[] is set to.
Actual behavior
The POST sets secondary_group_ids to exactly what was provided, ignoring xf_user_group_change entirely. The change records survive in the database but have no effect until something else triggers a proper rebuild.
Impact
Any API consumer that reads secondary_group_ids (which returns the combined list), modifies it, and writes it back can silently strip system-managed groups. The GET response gives no indication which groups are direct vs change-managed, so the consumer has no way to avoid this without independently querying xf_user_group_change. This affects groups from all xf_user_group_change sources: moderator promotions, user upgrades, and automatic user group promotions.
Steps to reproduce
1. Have a user who is a moderator (entry in xf_moderator, EXAMPLE: group 4 added via xf_user_group_change)
2. Confirm GET /api/users/{id}/ returns group 4 in secondary_group_ids
3. POST /api/users/{id}/ with secondary_group_ids[] set to the same list minus group 4
4. Group 4 is removed from the user's effective groups despite the xf_user_group_change record still existing
Expected behavior
The POST should rebuild the user's effective secondary groups by merging the provided direct groups with groups from xf_user_group_change, the same way the cache is originally built. The xf_user_group_change record should take precedence — if the moderator system says the user should be in group 4, that group should persist regardless of what secondary_group_ids[] is set to.
Actual behavior
The POST sets secondary_group_ids to exactly what was provided, ignoring xf_user_group_change entirely. The change records survive in the database but have no effect until something else triggers a proper rebuild.
Impact
Any API consumer that reads secondary_group_ids (which returns the combined list), modifies it, and writes it back can silently strip system-managed groups. The GET response gives no indication which groups are direct vs change-managed, so the consumer has no way to avoid this without independently querying xf_user_group_change. This affects groups from all xf_user_group_change sources: moderator promotions, user upgrades, and automatic user group promotions.