filterUserGrantsForCaller narrows collGrants/itemGrants — the TARGET user's grants, already loaded by the caller — down to what the CALLER can see. Only meaningful when caller != target; handleListUserGrants skips calling this for self-queries (a user can always see their own grants). BUG-1928: han
(r *http.Request, workspaceID string, collGrants []models.CollectionGrant, itemGrants []models.ItemGrant)
| 2691 | // call (state-agnostic; no deleted_at filter) rather than N per-grant |
| 2692 | // lookups. |
| 2693 | func (s *Server) filterUserGrantsForCaller(r *http.Request, workspaceID string, collGrants []models.CollectionGrant, itemGrants []models.ItemGrant) ([]models.CollectionGrant, []models.ItemGrant, error) { |
| 2694 | fullCollIDs, grantedItemIDs, err := s.guestResourceFilter(r, workspaceID) |
| 2695 | if err != nil { |
| 2696 | return nil, nil, err |
| 2697 | } |
| 2698 | if fullCollIDs == nil && grantedItemIDs == nil { |
| 2699 | // Unrestricted caller (admin/cookie session, or a member with |
| 2700 | // full collection access) — no filtering, and no further store |
| 2701 | // calls needed. |
| 2702 | return collGrants, itemGrants, nil |
| 2703 | } |
| 2704 | |
| 2705 | filteredColl := make([]models.CollectionGrant, 0, len(collGrants)) |
| 2706 | for _, g := range collGrants { |
| 2707 | if isCollectionVisible(g.CollectionID, fullCollIDs) { |
| 2708 | filteredColl = append(filteredColl, g) |
| 2709 | } |
| 2710 | } |
| 2711 | |
| 2712 | filteredItem := make([]models.ItemGrant, 0, len(itemGrants)) |
| 2713 | if len(itemGrants) > 0 { |
| 2714 | itemIDs := make([]string, len(itemGrants)) |
| 2715 | for i, g := range itemGrants { |
| 2716 | itemIDs[i] = g.ItemID |
| 2717 | } |
| 2718 | refs, err := s.store.GetItemCollectionRefs(workspaceID, itemIDs) |
| 2719 | if err != nil { |
| 2720 | return nil, nil, err |
| 2721 | } |
| 2722 | collByItem := make(map[string]string, len(refs)) |
| 2723 | for _, ref := range refs { |
| 2724 | collByItem[ref.ID] = ref.CollectionID |
| 2725 | } |
| 2726 | for _, g := range itemGrants { |
| 2727 | collID, ok := collByItem[g.ItemID] |
| 2728 | if !ok { |
| 2729 | // item_grants.item_id is ON DELETE CASCADE, so a grant |
| 2730 | // row can't outlive its item — this should be |
| 2731 | // unreachable. Exclude defensively rather than show a |
| 2732 | // grant with no resolvable parent. |
| 2733 | continue |
| 2734 | } |
| 2735 | item := &models.Item{ID: g.ItemID, CollectionID: collID} |
| 2736 | if s.isItemVisibleToGuest(r, workspaceID, item, fullCollIDs, grantedItemIDs) { |
| 2737 | filteredItem = append(filteredItem, g) |
| 2738 | } |
| 2739 | } |
| 2740 | } |
| 2741 | |
| 2742 | return filteredColl, filteredItem, nil |
| 2743 | } |
| 2744 | |
| 2745 | // requireEditPermission checks if the user has edit access to the given item. |
| 2746 | // For regular members (editor/owner), this uses the standard role check. |
no test coverage detected