tenseleyflow/shithub / 81e12ae

Browse files

web: migrate UserActor callsites to viewer.PolicyActor (SR2 C1/C2)

18 handler sites switched from policy.UserActor(viewer.ID,
viewer.Username, viewer.IsSuspended, false) to viewer.PolicyActor().
Includes profile, search, and repo (issues/PRs/lifecycle/social/
fork/labels/repo home) surfaces.

Pre-migration these all hardcoded IsSiteAdmin=false and ignored
viewer.ImpersonatedUserID. Post-migration impersonation is a
construct-time concern and admin-read-private gets a 200 instead
of 404.

PAT-bearing API handlers (api/checks.go, api/stars.go) and the
SSH/HTTPS-git paths keep plain UserActor() — those gates reject
suspended at credential check, and impersonation is impossible via
non-cookie auth, so the false literals are correct by construction.
Authored by espadonne
SHA
81e12ae5f3fcf5f2e13ad48cc26c3c368cdd4797
Parents
c9d4831
Tree
44e89fd

11 changed files

StatusFile+-
M internal/web/handlers/profile/org_profile.go 1 5
M internal/web/handlers/profile/repositories_tab.go 1 1
M internal/web/handlers/profile/stars_tab.go 1 1
M internal/web/handlers/repo/fork.go 3 3
M internal/web/handlers/repo/issues.go 3 3
M internal/web/handlers/repo/labels_milestones.go 2 2
M internal/web/handlers/repo/lifecycle.go 2 2
M internal/web/handlers/repo/pulls.go 3 3
M internal/web/handlers/repo/repo.go 1 1
M internal/web/handlers/repo/social.go 1 1
M internal/web/handlers/search/search.go 1 1
internal/web/handlers/profile/org_profile.gomodified
@@ -152,11 +152,7 @@ func (h *Handlers) orgProfileRepos(ctx context.Context, orgID int64, viewer midd
152152
 	}
153153
 	actor := policy.AnonymousActor()
154154
 	if !viewer.IsAnonymous() {
155
-		actor = policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, viewer.IsSiteAdmin)
156
-		if viewer.ImpersonatedUserID != 0 {
157
-			actor.Impersonating = true
158
-			actor.ImpersonateWriteOK = viewer.ImpersonateWriteOK
159
-		}
155
+		actor = viewer.PolicyActor()
160156
 	}
161157
 	deps := policy.Deps{Pool: h.d.Pool}
162158
 
internal/web/handlers/profile/repositories_tab.gomodified
@@ -32,7 +32,7 @@ func (h *Handlers) serveRepositoriesTab(w http.ResponseWriter, r *http.Request,
3232
 
3333
 	actor := policy.AnonymousActor()
3434
 	if !viewer.IsAnonymous() {
35
-		actor = policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, false)
35
+		actor = viewer.PolicyActor()
3636
 	}
3737
 	deps := policy.Deps{Pool: h.d.Pool}
3838
 
internal/web/handlers/profile/stars_tab.gomodified
@@ -48,7 +48,7 @@ func (h *Handlers) serveStarsTab(w http.ResponseWriter, r *http.Request, user us
4848
 
4949
 	actor := policy.AnonymousActor()
5050
 	if !viewer.IsAnonymous() {
51
-		actor = policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, false)
51
+		actor = viewer.PolicyActor()
5252
 	}
5353
 	deps := policy.Deps{Pool: h.d.Pool}
5454
 
internal/web/handlers/repo/fork.gomodified
@@ -63,7 +63,7 @@ func (h *Handlers) repoFork(w http.ResponseWriter, r *http.Request) {
6363
 		h.d.Render.HTTPError(w, r, http.StatusNotFound, "")
6464
 		return
6565
 	}
66
-	actor := policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, false)
66
+	actor := viewer.PolicyActor()
6767
 	repoRef := policy.NewRepoRefFromRepo(source)
6868
 	if dec := policy.Can(r.Context(), policy.Deps{Pool: h.d.Pool}, actor, policy.ActionForkCreate, repoRef); !dec.Allow {
6969
 		h.d.Render.HTTPError(w, r, policy.Maybe404(dec, repoRef, actor), "")
@@ -122,7 +122,7 @@ func (h *Handlers) repoSync(w http.ResponseWriter, r *http.Request) {
122122
 		h.d.Render.HTTPError(w, r, http.StatusNotFound, "")
123123
 		return
124124
 	}
125
-	actor := policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, false)
125
+	actor := viewer.PolicyActor()
126126
 	repoRef := policy.NewRepoRefFromRepo(row)
127127
 	if dec := policy.Can(r.Context(), policy.Deps{Pool: h.d.Pool}, actor, policy.ActionRepoWrite, repoRef); !dec.Allow {
128128
 		h.d.Render.HTTPError(w, r, policy.Maybe404(dec, repoRef, actor), "")
@@ -217,7 +217,7 @@ func actorFor(viewer middleware.CurrentUser) policy.Actor {
217217
 	if viewer.IsAnonymous() {
218218
 		return policy.AnonymousActor()
219219
 	}
220
-	return policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, false)
220
+	return viewer.PolicyActor()
221221
 }
222222
 
223223
 // handleForkError maps the orchestrator's typed errors to status
internal/web/handlers/repo/issues.gomodified
@@ -298,7 +298,7 @@ func (h *Handlers) issueView(w http.ResponseWriter, r *http.Request) {
298298
 		authorName = usernameFor(issue.AuthorUserID.Int64)
299299
 	}
300300
 	viewer := middleware.CurrentUserFromContext(r.Context())
301
-	actor := policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, false)
301
+	actor := viewer.PolicyActor()
302302
 	pdeps := policy.Deps{Pool: h.d.Pool}
303303
 	repoRef := policy.NewRepoRefFromRepo(row)
304304
 	stateRef := issueStateRepoRef(row, issue)
@@ -552,7 +552,7 @@ func (h *Handlers) issueComment(w http.ResponseWriter, r *http.Request) {
552552
 	// posters). We resolve the *real* role via the policy package — owner
553553
 	// is implicit admin, and any explicit collaborator with role >= triage
554554
 	// passes. Read fails (DB miss, unknown role) fail closed via RoleNone.
555
-	actor := policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, false)
555
+	actor := viewer.PolicyActor()
556556
 	isCollab := policy.HasRoleAtLeast(r.Context(), policy.Deps{Pool: h.d.Pool}, actor, policy.NewRepoRefFromRepo(row), policy.RoleTriage)
557557
 
558558
 	var commentID int64
@@ -609,7 +609,7 @@ func (h *Handlers) issueSetState(w http.ResponseWriter, r *http.Request) {
609609
 		return
610610
 	}
611611
 	viewer := middleware.CurrentUserFromContext(r.Context())
612
-	actor := policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, false)
612
+	actor := viewer.PolicyActor()
613613
 	stateRef := issueStateRepoRef(row, issue)
614614
 	if dec := policy.Can(r.Context(), policy.Deps{Pool: h.d.Pool}, actor, policy.ActionIssueClose, stateRef); !dec.Allow {
615615
 		h.d.Render.HTTPError(w, r, policy.Maybe404(dec, stateRef, actor), "")
internal/web/handlers/repo/labels_milestones.gomodified
@@ -25,7 +25,7 @@ func (h *Handlers) labelsList(w http.ResponseWriter, r *http.Request) {
2525
 	}
2626
 	labels, _ := h.iq.ListLabels(r.Context(), h.d.Pool, row.ID)
2727
 	viewer := middleware.CurrentUserFromContext(r.Context())
28
-	actor := policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, false)
28
+	actor := viewer.PolicyActor()
2929
 	canManage := policy.Can(r.Context(), policy.Deps{Pool: h.d.Pool}, actor, policy.ActionIssueLabel, policy.NewRepoRefFromRepo(row)).Allow
3030
 	w.Header().Set("Content-Type", "text/html; charset=utf-8")
3131
 	_ = h.d.Render.RenderPage(w, r, "repo/labels", map[string]any{
@@ -128,7 +128,7 @@ func (h *Handlers) milestonesList(w http.ResponseWriter, r *http.Request) {
128128
 	}
129129
 	ms, _ := h.iq.ListMilestones(r.Context(), h.d.Pool, row.ID)
130130
 	viewer := middleware.CurrentUserFromContext(r.Context())
131
-	actor := policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, false)
131
+	actor := viewer.PolicyActor()
132132
 	canManage := policy.Can(r.Context(), policy.Deps{Pool: h.d.Pool}, actor, policy.ActionIssueLabel, policy.NewRepoRefFromRepo(row)).Allow
133133
 	w.Header().Set("Content-Type", "text/html; charset=utf-8")
134134
 	_ = h.d.Render.RenderPage(w, r, "repo/milestones", map[string]any{
internal/web/handlers/repo/lifecycle.gomodified
@@ -105,7 +105,7 @@ func (h *Handlers) loadRepoAndAuthorize(w http.ResponseWriter, r *http.Request,
105105
 		return reposdb.Repo{}, usersdb.User{}, false
106106
 	}
107107
 	viewer := middleware.CurrentUserFromContext(r.Context())
108
-	actor := policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, false)
108
+	actor := viewer.PolicyActor()
109109
 	repoRef := policy.NewRepoRefFromRepo(row)
110110
 	dec := policy.Can(r.Context(), policy.Deps{Pool: h.d.Pool}, actor, action, repoRef)
111111
 	if !dec.Allow {
@@ -293,7 +293,7 @@ func (h *Handlers) transferCancel(w http.ResponseWriter, r *http.Request) {
293293
 		return
294294
 	}
295295
 	viewer := middleware.CurrentUserFromContext(r.Context())
296
-	actor := policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, false)
296
+	actor := viewer.PolicyActor()
297297
 	repoRef := policy.NewRepoRefFromRepo(repo)
298298
 	if dec := policy.Can(r.Context(), policy.Deps{Pool: h.d.Pool}, actor, policy.ActionRepoAdmin, repoRef); !dec.Allow {
299299
 		h.d.Render.HTTPError(w, r, policy.Maybe404(dec, repoRef, actor), "")
internal/web/handlers/repo/pulls.gomodified
@@ -343,7 +343,7 @@ func (h *Handlers) renderPullPage(w http.ResponseWriter, r *http.Request, tab st
343343
 		"ActiveSubnav": "pulls",
344344
 	}
345345
 	viewer := middleware.CurrentUserFromContext(r.Context())
346
-	actor := policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, false)
346
+	actor := viewer.PolicyActor()
347347
 	pdeps := policy.Deps{Pool: h.d.Pool}
348348
 	repoRef := policy.NewRepoRefFromRepo(row)
349349
 	stateRef := repoRef
@@ -491,7 +491,7 @@ func (h *Handlers) pullView(w http.ResponseWriter, r *http.Request) {
491491
 		reqs = append(reqs, rr)
492492
 	}
493493
 	viewer := middleware.CurrentUserFromContext(r.Context())
494
-	actor := policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, false)
494
+	actor := viewer.PolicyActor()
495495
 	pdeps := policy.Deps{Pool: h.d.Pool}
496496
 	repoRef := policy.NewRepoRefFromRepo(row)
497497
 	canCommentAction := policy.Can(r.Context(), pdeps, actor, policy.ActionIssueComment, repoRef).Allow
@@ -777,7 +777,7 @@ func (h *Handlers) pullSetState(w http.ResponseWriter, r *http.Request) {
777777
 		return
778778
 	}
779779
 	viewer := middleware.CurrentUserFromContext(r.Context())
780
-	actor := policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, false)
780
+	actor := viewer.PolicyActor()
781781
 	repoRef := policy.NewRepoRefFromRepo(row)
782782
 	if pr.IAuthorUserID.Valid {
783783
 		repoRef.AuthorUserID = pr.IAuthorUserID.Int64
internal/web/handlers/repo/repo.gomodified
@@ -450,7 +450,7 @@ func (h *Handlers) lookupRepoForViewer(ctx context.Context, ownerName, repoName
450450
 	if viewer.IsAnonymous() {
451451
 		actor = policy.AnonymousActor()
452452
 	} else {
453
-		actor = policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, false)
453
+		actor = viewer.PolicyActor()
454454
 	}
455455
 	// ActionRepoRead deny on a private repo with a non-collab viewer is
456456
 	// indistinguishable from "doesn't exist" — Maybe404 returns 404 in
internal/web/handlers/repo/social.gomodified
@@ -194,7 +194,7 @@ func (h *Handlers) authorizeSocialAction(w http.ResponseWriter, r *http.Request,
194194
 		return repoRow{}, "", false
195195
 	}
196196
 	viewer := middleware.CurrentUserFromContext(r.Context())
197
-	actor := policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, false)
197
+	actor := viewer.PolicyActor()
198198
 	repoRef := policy.NewRepoRefFromRepo(row)
199199
 	if dec := policy.Can(r.Context(), policy.Deps{Pool: h.d.Pool}, actor, action, repoRef); !dec.Allow {
200200
 		h.d.Render.HTTPError(w, r, policy.Maybe404(dec, repoRef, actor), "")
internal/web/handlers/search/search.gomodified
@@ -58,7 +58,7 @@ func (h *Handlers) actor(r *http.Request) policy.Actor {
5858
 	if viewer.IsAnonymous() {
5959
 		return policy.AnonymousActor()
6060
 	}
61
-	return policy.UserActor(viewer.ID, viewer.Username, viewer.IsSuspended, false)
61
+	return viewer.PolicyActor()
6262
 }
6363
 
6464
 // results renders the full /search page with type tabs.