Address Gadfly review on #16: RemoveShare choke point + dedup
Build image / build-and-push (push) Successful in 5s
Build image / build-and-push (push) Successful in 5s
- RemoveShare now routes through requireGardenRole(roleViewer) — the standard authorization choke point (masks existence for non-participants) — then applies the owner-or-self rule on top (a participant removing someone else's share is now ErrForbidden, not ErrNotFound). No more bespoke GetGarden+manual check. - Add domain.RoleOwner constant; gardenRole.String() and the tests use it instead of the bare "owner" literal. - UpdatePlanting fetches the plant once and only re-checks visibility when the plant id actually CHANGES (new ≠ old), so a no-op plantId resend can't break a shared editor editing a plop that uses the owner's private plant. - Bound ListSharesForGarden with a LIMIT backstop. Skipped: AddShare email-existence disclosure (the issue explicitly accepts it as inherent to email-based sharing), 404-on-missing-share (correct DELETE semantics), and the join-scan/test-helper dedup nits. GOWORK=off go build/vet/test ./internal/... green. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01JdQpdYYsTgtkJBxbcpAszi
This commit is contained in:
@@ -121,17 +121,19 @@ func (s *Service) UpdatePlanting(ctx context.Context, actorID, plantingID int64,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
originalPlantID := pl.PlantID
|
||||
applyPlantingPatch(pl, patch)
|
||||
// The plant must be visible to the actor only when they're CHANGING it — an
|
||||
// existing plop may reference a plant the actor can't see (e.g. a shared
|
||||
// editor working in the owner's garden with the owner's private plant), and
|
||||
// editing its radius/label/date must still work.
|
||||
if patch.PlantID != nil {
|
||||
if _, err := s.visiblePlant(ctx, actorID, pl.PlantID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Fetch the plop's plant for the derived count. Only when the actor is
|
||||
// actually CHANGING the plant (new id ≠ old) is the new plant gated on
|
||||
// visibility — an existing plop may reference a plant the actor can't see
|
||||
// (e.g. a shared editor in the owner's garden using the owner's private
|
||||
// plant), and a no-op plantId resend must not break editing it.
|
||||
var plant *domain.Plant
|
||||
if pl.PlantID != originalPlantID {
|
||||
plant, err = s.visiblePlant(ctx, actorID, pl.PlantID)
|
||||
} else {
|
||||
plant, err = s.store.GetPlant(ctx, pl.PlantID)
|
||||
}
|
||||
plant, err := s.store.GetPlant(ctx, pl.PlantID) // for the derived count
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user