#5002: the orphan recovery goes both ways
#4806 gave a site admin one write on a help desk group nobody is left to run, and #4926 made the Person field of that form a people search. Neither closed the other half: `storeAgent()` was gated on requireManagerAccessOrOrphanRecovery(), `destroyAgent()` on plain requireManagerAccess(). So the rescue could appoint and never un-appoint — the row it had just written sat in the rescuer's own Agents list marked "Not yours to manage", with no Remove control and a 403 behind the DELETE. A `manager` press was worse: the write ends the orphan state, so the group left `orphanedGroupIds` and the admin could not remove the row, appoint anybody else, or appoint themselves. The only person who could undo it was the person wrongly appointed — #4806's frozen group, reached through #4806's cure. destroyAgent() now asks requireRemoveAccess(): a manager of the group, or a site admin while the group is STILL orphaned (symmetric with the write and strictly narrower — on such a group they could appoint themselves and then remove anything anyway), or a site admin undoing a row their own rescue wrote. That last arm needs provenance, hence `recovered_by`: "the group would be orphaned without this row" cannot tell a mistaken appointment from the sole working manager of a healthy group, and admitting the second would let any admin strip a manager and seize the desk (#4710). The stamp is cleared the moment a manager of the group writes the row themselves. `removableAgentIds` is that gate in prop form, so the Remove button and the DELETE cannot disagree, and the confirmation says when a removal hands the group back to having nobody. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,13 @@ class TicketingAgentAccess extends Model
|
||||
{
|
||||
protected $table = 'ticketing_agent_access';
|
||||
|
||||
protected $fillable = ['user_id', 'group_id', 'role'];
|
||||
/**
|
||||
* `recovered_by` is #5002: the site admin who wrote this row as an orphan
|
||||
* recovery (#4806), and may therefore take it back. Null on every row
|
||||
* written by an ordinary manager — see the migration for why this is not
|
||||
* a general-purpose `created_by`.
|
||||
*/
|
||||
protected $fillable = ['user_id', 'group_id', 'role', 'recovered_by'];
|
||||
|
||||
public function group(): BelongsTo
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user