Public: anyone can read this; the crew can change it.

An app's private destinations on its settings page are §5 rows in a setting list, with an outline Grant #3842

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/3825-an-app-s-private-destinations-on-its-set → main 3 commits fork

  • Merged. scout/3825-an-app-s-private-destinations-on-its-set was merged into main as 655bb2a.

Review

Sign in to review this change.
2 files changed +45 −37
src/web/pages/settings.ts +26 −37
@@ -1185,42 +1185,31 @@ export async function appDomainDelete(ctx: Ctx): Promise<Response> {
11851185 function appEgressView(base: string, egress: EgressView): Html {
11861186 const at = (destination: string) =>
11871187 `${base}/settings/app/egress/${encodeURIComponent(destination)}`;
1188 const rows = egress.rows.map(
1189 (r) =>
1190 html`<li>
1191 <div class="row">
1192 <span class="row-lead">${icon("globe")}</span>
1193 <span class="row-main">
1194 <span class="row-title"
1195 ><span class="mono">${r.destination}</span> ${r.granted
1196 ? badge("granted", { tone: "success" })
1197 : badge("not granted", { tone: "attn" })}
1198 ${r.asked ? null : badge("no longer asked")}</span
1199 >
1200 <span class="row-meta"
1201 >${r.granted && r.grantedAt !== null
1202 ? html`Granted by ${r.grantedBy ?? "someone"}
1203 ${time(r.grantedAt)}`
1204 : "Nothing reaches it until a site admin grants it."}</span
1205 >
1206 </span>
1207 ${when(
1208 egress.canChange,
1209 () =>
1210 html`<span class="row-trail">
1211 ${r.granted
1212 ? confirmAction({
1213 action: `${at(r.destination)}/revoke`,
1214 verb: "Revoke",
1215 size: "sm",
1216 })
1217 : html`<form method="post" action="${at(r.destination)}/grant">
1218 ${btn("Grant", { size: "sm" })}
1219 </form>`}
1220 </span>`,
1221 )}
1222 </div>
1223 </li>`,
1188 const rows = egress.rows.map((r) =>
1189 row({
1190 lead: icon("globe"),
1191 title: html`<span class="mono">${r.destination}</span> ${r.granted
1192 ? badge("granted", { tone: "success" })
1193 : badge("not granted", { tone: "attn" })}
1194 ${r.asked ? null : badge("no longer asked")}`,
1195 meta:
1196 r.granted && r.grantedAt !== null
1197 ? html`Granted by ${r.grantedBy ?? "someone"} ${time(r.grantedAt)}`
1198 : html`Nothing reaches it until a site admin grants it.`,
1199 trail: egress.canChange
1200 ? html`<div class="row-fields">
1201 ${r.granted
1202 ? confirmAction({
1203 action: `${at(r.destination)}/revoke`,
1204 verb: "Revoke",
1205 size: "sm",
1206 })
1207 : html`<form method="post" action="${at(r.destination)}/grant">
1208 ${btn("Grant", { size: "sm", variant: "outline" })}
1209 </form>`}
1210 </div>`
1211 : html``,
1212 }),
12241213 );
12251214 const changes = egress.changes.map(
12261215 (c) =>
@@ -1241,7 +1230,7 @@ function appEgressView(base: string, egress: EgressView): Html {
12411230 : " Only a site admin grants or revokes."}
12421231 </p>
12431232 </div>
1244 <ul class="list">
1233 <ul class="list setting-list">
12451234 ${rows}
12461235 </ul>
12471236 ${when(
test/app-egress-grants.test.ts +19
@@ -187,6 +187,25 @@ describe("private destinations asked for", () => {
187187 expect(cyPage).not.toContain(">Grant<");
188188 });
189189
190 test("the section is a setting list: an outline Grant for a site admin, no buttons for anyone else", async () => {
191 const section = async (cookie: string) => {
192 const page = await (await web(cookie, "/cy/arm/settings/app")).text();
193 const from = page.indexOf("Private destinations asked for");
194 return page.slice(from, page.indexOf("</ul>", from));
195 };
196 const admin = await section(adaCookie);
197 expect(admin).toContain('<ul class="list setting-list">');
198 expect(admin).toContain('class="row-fields"');
199 expect(admin).toMatch(/<button[^>]*data-variant="outline"[^>]*>\s*Grant\s*<\/button>/);
200 expect(admin).toContain("Revoke");
201 const owner = await section(cyCookie);
202 expect(owner).toContain('<ul class="list setting-list">');
203 expect(owner).toContain(CAMERA);
204 expect(owner).not.toContain("Grant<");
205 expect(owner).not.toContain("Revoke");
206 expect(owner).not.toContain("/settings/app/egress/");
207 });
208
190209 test("the page grants too, and refuses a foreign origin", async () => {
191210 const path = `/cy/arm/settings/app/egress/${encodeURIComponent(CAMERA)}/grant`;
192211 await web(adaCookie, path, "https://evil.example");

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs