Hide public org member tab

This commit is contained in:
Jonathon Leight
2026-07-03 18:52:57 -04:00
parent f992bbfad7
commit f52e3e4d5a
3 changed files with 59 additions and 4 deletions
+49
View File
@@ -0,0 +1,49 @@
package core
import (
"strings"
"testing"
)
// TestPublicOrgPageHidesMembersTab: the public org page never shows the Members
// tab — membership isn't public (only the admin list is), and the tab would 404
// on the root host anyway. It must stay hidden even for a signed-in member
// viewing the page via the identity beacon.
func TestPublicOrgPageHidesMembersTab(t *testing.T) {
t.Parallel()
st, ctx, ts, h := splitServer(t)
u, err := st.CreateUser(ctx, "orgmember", "")
if err != nil {
t.Fatal(err)
}
org, err := st.CreateOrg(ctx, "Tabs Org", u.ID) // creator is an admin member
if err != nil {
t.Fatal(err)
}
membersLink := "/orgs/" + org.Slug + "/members"
anon := readBody(t, do(t, ts, h.root, "/orgs/"+org.Slug))
if strings.Contains(anon, membersLink) {
t.Fatal("anonymous public org page shows a Members tab")
}
// Drop a root identity cookie for the member (as a fresh sign-in would), then
// view the public page as that member.
loginID, _ := st.CreateLogin(ctx, u.ID)
code, _ := st.CreateAuthCode(ctx, u.ID, loginID, "/")
beacon := do(t, ts, h.root, "/session/beacon?code="+code)
beacon.Body.Close()
sess := cookieByName(beacon, "meshtender_session")
if sess == nil {
t.Fatal("beacon set no identity cookie")
}
member := readBody(t, do(t, ts, h.root, "/orgs/"+org.Slug, sess))
if strings.Contains(member, membersLink) {
t.Fatal("member viewing the public org page still sees the Members tab")
}
// The tabs that ARE public should still be there.
if !strings.Contains(member, "/orgs/"+org.Slug+"/repeaters") {
t.Fatal("public org page missing the Repeaters tab")
}
}
+5 -2
View File
@@ -170,8 +170,11 @@ func (s *Handlers) renderOrgPublic(w http.ResponseWriter, r *http.Request, org *
}
uid := s.Auth.CurrentUserID(r.Context())
s.Render(w, r, "org_public.html", map[string]any{
"Org": org,
"Nav": s.OrgNavFor(r.Context(), org.ID, org.Slug, "home", isMember, isAdmin),
"Org": org,
// The public view never exposes the Members tab — membership isn't public,
// only the admin list is — so build the nav as a non-member regardless of who
// is viewing (a member previews the public page via ?view=public).
"Nav": s.OrgNavFor(r.Context(), org.ID, org.Slug, "home", false, isAdmin),
"Admins": admins,
"MemberCount": memberCount,
"RepeaterCount": repeaterCount,
+5 -2
View File
@@ -85,8 +85,11 @@ func (s *Handlers) renderOrgPublic(w http.ResponseWriter, r *http.Request, org *
}
uid := s.Auth.CurrentUserID(r.Context())
s.Render(w, r, "org_public.html", map[string]any{
"Org": org,
"Nav": s.OrgNavFor(r.Context(), org.ID, org.Slug, "home", isMember, isAdmin),
"Org": org,
// The public view never exposes the Members tab — membership isn't public,
// only the admin list is — so build the nav as a non-member regardless of who
// is viewing (the tab also 404s on the root host, which has no members route).
"Nav": s.OrgNavFor(r.Context(), org.ID, org.Slug, "home", false, isAdmin),
"Admins": admins,
"MemberCount": memberCount,
"RepeaterCount": repeaterCount,