Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Manage accounts from the users directory, not the Admin tab

ea21f923d1fabe3a633e3dc4713ca18d211dbfcb · Keenan Nemetz · 4d ago · parent f11854fe

Manage accounts from the users directory, not the Admin tab

The Accounts tab rendered every account in the roster, which costs more as
an instance grows and duplicates a directory that is already paginated,
searchable, filterable, and sortable. The tab now keeps the invite and bot
forms and points at /u, and an instance admin gets a Manage action on each
directory row that opens the account page.

The Manage column is presentation only: the account page still checks the
instance-admin role, so a non-admin who types its URL gets the usual 404.

Refs #198 (01M6MK0NNZ2XR1D5TQKNWYT4HN).

server/admin_users_test.go +41−5

@@ -17,12 +17,21 @@ func TestAdminUsersLifecycle(t *testing.T) {
17 17 }
18 18 sess, cookie := f.browserSession(t, f.ownerID)
19 19
20 // The admin tab creates accounts; it does not list the roster, which is now
21 // the paginated users directory.
20 22 resp, body := f.browserGet(t, "/admin?tab=accounts", cookie)
21 if resp.StatusCode != 200 || !strings.Contains(body, "Invite a person") || !strings.Contains(body, "keenan") {
22 t.Fatalf("users page = %d:\n%s", resp.StatusCode, body)
23 if resp.StatusCode != 200 || !strings.Contains(body, "Invite a person") ||
24 strings.Contains(body, `<a href="/users/keenan">Manage</a>`) {
25 t.Fatalf("accounts tab = %d:\n%s", resp.StatusCode, body)
23 26 }
24 27 if !strings.Contains(body, `href="/admin?tab=accounts" aria-current="page"`) {
25 t.Fatalf("users page is missing the active top-nav link:\n%s", body)
28 t.Fatalf("accounts tab is missing the active top-nav link:\n%s", body)
29 }
30 resp, body = f.browserGet(t, "/u", cookie)
31 if resp.StatusCode != 200 ||
32 !strings.Contains(body, `<a class="people-name" href="/u/keenan">keenan</a>`) ||
33 !strings.Contains(body, `<a href="/users/keenan">Manage</a>`) {
34 t.Fatalf("users directory = %d:\n%s", resp.StatusCode, body)
26 35 }
27 36 if _, home := f.browserGet(t, "/", cookie); !strings.Contains(home, `href="/admin"`) {
28 37 t.Fatal("the top navigation does not link to Users for an instance admin")
@@ -193,8 +202,9 @@ func TestAdminCreatesBotAccount(t *testing.T) {
193 202 if !acct.Bot() || acct.Role != access.Contribute || len(acct.Keys) != 1 || acct.Keys[0].Line != botKey {
194 203 t.Fatalf("created bot = %+v", acct)
195 204 }
196 if _, list := f.browserGet(t, "/admin?tab=accounts", cookie); !strings.Contains(list, `<a href="/users/ci-runner">ci-runner</a> <span class="state">bot</span>`) {
197 t.Fatalf("the account list does not mark the bot:\n%s", list)
205 if _, list := f.browserGet(t, "/u", cookie); !strings.Contains(list, `<a class="people-name" href="/u/ci-runner">ci-runner</a> <span class="state">bot</span>`) ||
206 !strings.Contains(list, `<a href="/users/ci-runner">Manage</a>`) {
207 t.Fatalf("the users directory does not mark the bot or offer Manage:\n%s", list)
198 208 }
199 209
200 210 // The page that manages the bot says what it is and offers only actions a bot
@@ -263,3 +273,29 @@ func TestAdminUsersRequiresInstanceAdmin(t *testing.T) {
263 273 t.Fatalf("non-admin users page = %d, want 404", resp.StatusCode)
264 274 }
265 275 }
276
277 // Managing an account is an admin action, so the directory only grows the Manage
278 // column for an instance admin. Everyone else sees the same public roster.
279 func TestUsersDirectoryHidesManageFromNonAdmins(t *testing.T) {
280 t.Parallel()
281 f := browserFixture(t)
282 _, cookie := f.browserSession(t, f.ownerID)
283
284 resp, body := f.browserGet(t, "/u", cookie)
285 if resp.StatusCode != 200 {
286 t.Fatalf("GET /u = %d:\n%s", resp.StatusCode, body)
287 }
288 for _, gone := range []string{"<th>Manage</th>", `href="/users/keenan">Manage</a>`} {
289 if strings.Contains(body, gone) {
290 t.Fatalf("a non-admin sees %q in the users directory:\n%s", gone, body)
291 }
292 }
293
294 if _, err := f.srv.Identity.SetAdmin(f.ownerID, true, "test"); err != nil {
295 t.Fatal(err)
296 }
297 if _, body := f.browserGet(t, "/u", cookie); !strings.Contains(body, "<th>Manage</th>") ||
298 !strings.Contains(body, `href="/users/keenan">Manage</a>`) {
299 t.Fatalf("an instance admin has no Manage action in the users directory:\n%s", body)
300 }
301 }