Go · 2499 bytes Raw Blame History
1 // SPDX-License-Identifier: AGPL-3.0-or-later
2
3 package policy
4
5 import "fmt"
6
7 // VisibilityPredicate returns a SQL WHERE-clause fragment plus its
8 // bind args that filters rows from a `repos` table reference (or a
9 // table that joins to repos via a column named like
10 // `repo_id`/`r.id`) to those visible to the actor.
11 //
12 // The fragment is parameterised against the supplied placeholder
13 // offset so callers can splice it into queries that already bind
14 // other parameters. Returns:
15 //
16 // clause — SQL fragment ready to drop after `WHERE` or `AND`.
17 // args — the values for the placeholders inside `clause`, in
18 // order from `$<startPlaceholder>` upward.
19 //
20 // `tableAlias` is the alias the caller used for the `repos` table
21 // (e.g. "r" for `FROM repos r`). The fragment references
22 // `<tableAlias>.visibility`, `<tableAlias>.owner_user_id`,
23 // `<tableAlias>.deleted_at`, and `<tableAlias>.id` as needed.
24 //
25 // Visibility rules (mirror policy.Can step 4–6):
26 //
27 // - Soft-deleted repos are always excluded.
28 // - Public repos visible to anyone.
29 // - Private repos visible only to: owner, or any user with a
30 // row in `repo_collaborators` (any role).
31 //
32 // Site-admin special-cased: when actor.IsSiteAdmin, only the
33 // soft-delete filter applies (admins can read everything).
34 //
35 // This is the single source of truth for "what repos can this
36 // viewer see in a list query". S28 search composes it; future
37 // listing endpoints (trending, activity feed) reuse it.
38 func VisibilityPredicate(actor Actor, tableAlias string, startPlaceholder int) (clause string, args []any) {
39 if tableAlias == "" {
40 tableAlias = "r"
41 }
42
43 // Always exclude soft-deleted.
44 base := fmt.Sprintf("%s.deleted_at IS NULL", tableAlias)
45
46 if actor.IsSiteAdmin {
47 // Admins see everything that isn't soft-deleted.
48 return base, nil
49 }
50
51 if actor.IsAnonymous {
52 // Public only.
53 return fmt.Sprintf(
54 "%s AND %s.visibility = 'public'",
55 base, tableAlias,
56 ), nil
57 }
58
59 // Logged-in: public OR (owner) OR (collab row exists).
60 // Two placeholders consumed: actor.UserID twice (owner check +
61 // collab subquery).
62 p1 := startPlaceholder
63 p2 := startPlaceholder + 1
64 clause = fmt.Sprintf(
65 "%s AND ("+
66 "%s.visibility = 'public' "+
67 "OR %s.owner_user_id = $%d "+
68 "OR EXISTS (SELECT 1 FROM repo_collaborators c "+
69 "WHERE c.repo_id = %s.id AND c.user_id = $%d)"+
70 ")",
71 base, tableAlias, tableAlias, p1, tableAlias, p2,
72 )
73 args = []any{actor.UserID, actor.UserID}
74 return clause, args
75 }
76