ipn/ipnlocal: populate Groups field in profileFromView

This populates UserProfile.Groups in the WhoIs response from the
local backend with the groups of the corresponding user in the
netmap.

This allows tsnet apps to see (and e.g. forward) which groups a
user making a request belongs to - as long as the tsnet app runs
on a node that been granted the tailscale.com/visible-groups
capability via node attributes. If that's not the case or the
user doesn't belong to any groups allow-listed via the node
attribute, Groups won't be populated.

Updates tailscale/corp#31529

Signed-off-by: Gesa Stupperich <gesa@tailscale.com>
This commit is contained in:
Gesa Stupperich
2026-03-19 21:46:55 +00:00
committed by GitHub
parent ac19bd5e7a
commit ca9aa20255
2 changed files with 14 additions and 8 deletions
+1
View File
@@ -1473,6 +1473,7 @@ func profileFromView(v tailcfg.UserProfileView) tailcfg.UserProfile {
LoginName: v.LoginName(),
DisplayName: v.DisplayName(),
ProfilePicURL: v.ProfilePicURL(),
Groups: v.Groups().AsSlice(),
}
}
return tailcfg.UserProfile{}
+13 -8
View File
@@ -2070,19 +2070,21 @@ func TestWhoIs(t *testing.T) {
}).View(),
20: (&tailcfg.UserProfile{
DisplayName: "Peer",
Groups: []string{"group:foo"},
}).View(),
},
})
tests := []struct {
q string
want tailcfg.NodeID // 0 means want ok=false
wantName string
q string
want tailcfg.NodeID // 0 means want ok=false
wantName string
wantGroups []string
}{
{"100.101.102.103:0", 1, "Myself"},
{"100.101.102.103:123", 1, "Myself"},
{"100.200.200.200:0", 2, "Peer"},
{"100.200.200.200:123", 2, "Peer"},
{"100.4.0.4:404", 0, ""},
{"100.101.102.103:0", 1, "Myself", nil},
{"100.101.102.103:123", 1, "Myself", nil},
{"100.200.200.200:0", 2, "Peer", []string{"group:foo"}},
{"100.200.200.200:123", 2, "Peer", []string{"group:foo"}},
{"100.4.0.4:404", 0, "", nil},
}
for _, tt := range tests {
t.Run(tt.q, func(t *testing.T) {
@@ -2097,6 +2099,9 @@ func TestWhoIs(t *testing.T) {
if up.DisplayName != tt.wantName {
t.Errorf("got name %q; want %q", up.DisplayName, tt.wantName)
}
if !slices.Equal(up.Groups, tt.wantGroups) {
t.Errorf("got groups %q; want %q", up.Groups, tt.wantGroups)
}
})
}
}