all: upgrade to Go 1.26rc2 and modernize codebase

This commit upgrades the codebase from Go 1.25.5 to Go 1.26rc2 and
adopts new language features.

Toolchain updates:
- go.mod: go 1.25.5 → go 1.26rc2
- flake.nix: buildGo125Module → buildGo126Module, go_1_25 → go_1_26
- flake.nix: build golangci-lint from source with Go 1.26
- Dockerfile.integration: golang:1.25-trixie → golang:1.26rc2-trixie
- Dockerfile.tailscale-HEAD: golang:1.25-alpine → golang:1.26rc2-alpine
- Dockerfile.derper: golang:alpine → golang:1.26rc2-alpine
- .goreleaser.yml: go mod tidy -compat=1.25 → -compat=1.26
- cmd/hi/run.go: fallback Go version 1.25 → 1.26rc2
- .pre-commit-config.yaml: simplify golangci-lint hook entry

Code modernization using Go 1.26 features:
- Replace tsaddr.SortPrefixes with slices.SortFunc + netip.Prefix.Compare
- Replace ptr.To(x) with new(x) syntax
- Replace errors.As with errors.AsType[T]

Lint rule updates:
- Add forbidigo rules to prevent regression to old patterns
This commit is contained in:
Kristoffer Dalby 2026-02-06 21:39:35 +00:00
parent 20dff82f95
commit 0f6d312ada
50 changed files with 508 additions and 521 deletions

View file

@ -9,7 +9,6 @@ import (
"github.com/juanfont/headscale/hscontrol/util"
"github.com/rs/zerolog/log"
"github.com/samber/lo"
"tailscale.com/net/tsaddr"
"tailscale.com/types/views"
)
@ -111,7 +110,7 @@ func ApproveRoutesWithPolicy(pm PolicyManager, nv types.NodeView, currentApprove
}
// Sort and deduplicate
tsaddr.SortPrefixes(newApproved)
slices.SortFunc(newApproved, netip.Prefix.Compare)
newApproved = slices.Compact(newApproved)
newApproved = lo.Filter(newApproved, func(route netip.Prefix, index int) bool {
return route.IsValid()
@ -120,7 +119,7 @@ func ApproveRoutesWithPolicy(pm PolicyManager, nv types.NodeView, currentApprove
// Sort the current approved for comparison
sortedCurrent := make([]netip.Prefix, len(currentApproved))
copy(sortedCurrent, currentApproved)
tsaddr.SortPrefixes(sortedCurrent)
slices.SortFunc(sortedCurrent, netip.Prefix.Compare)
// Only update if the routes actually changed
if !slices.Equal(sortedCurrent, newApproved) {

View file

@ -3,6 +3,7 @@ package policy
import (
"fmt"
"net/netip"
"slices"
"testing"
policyv2 "github.com/juanfont/headscale/hscontrol/policy/v2"
@ -11,9 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
"tailscale.com/net/tsaddr"
"tailscale.com/types/key"
"tailscale.com/types/ptr"
"tailscale.com/types/views"
)
@ -33,10 +32,10 @@ func TestApproveRoutesWithPolicy_NeverRemovesApprovedRoutes(t *testing.T) {
MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(),
Hostname: "test-node",
UserID: ptr.To(user1.ID),
User: ptr.To(user1),
UserID: new(user1.ID),
User: new(user1),
RegisterMethod: util.RegisterMethodAuthKey,
IPv4: ptr.To(netip.MustParseAddr("100.64.0.1")),
IPv4: new(netip.MustParseAddr("100.64.0.1")),
Tags: []string{"tag:test"},
}
@ -45,10 +44,10 @@ func TestApproveRoutesWithPolicy_NeverRemovesApprovedRoutes(t *testing.T) {
MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(),
Hostname: "other-node",
UserID: ptr.To(user2.ID),
User: ptr.To(user2),
UserID: new(user2.ID),
User: new(user2),
RegisterMethod: util.RegisterMethodAuthKey,
IPv4: ptr.To(netip.MustParseAddr("100.64.0.2")),
IPv4: new(netip.MustParseAddr("100.64.0.2")),
}
// Create a policy that auto-approves specific routes
@ -195,7 +194,7 @@ func TestApproveRoutesWithPolicy_NeverRemovesApprovedRoutes(t *testing.T) {
assert.Equal(t, tt.wantChanged, gotChanged, "changed flag mismatch: %s", tt.description)
// Sort for comparison since ApproveRoutesWithPolicy sorts the results
tsaddr.SortPrefixes(tt.wantApproved)
slices.SortFunc(tt.wantApproved, netip.Prefix.Compare)
assert.Equal(t, tt.wantApproved, gotApproved, "approved routes mismatch: %s", tt.description)
// Verify that all previously approved routes are still present
@ -305,10 +304,10 @@ func TestApproveRoutesWithPolicy_NilAndEmptyCases(t *testing.T) {
MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(),
Hostname: "testnode",
UserID: ptr.To(user.ID),
User: ptr.To(user),
UserID: new(user.ID),
User: new(user),
RegisterMethod: util.RegisterMethodAuthKey,
IPv4: ptr.To(netip.MustParseAddr("100.64.0.1")),
IPv4: new(netip.MustParseAddr("100.64.0.1")),
ApprovedRoutes: tt.currentApproved,
}
nodes := types.Nodes{&node}
@ -334,7 +333,7 @@ func TestApproveRoutesWithPolicy_NilAndEmptyCases(t *testing.T) {
if tt.wantApproved == nil {
assert.Nil(t, gotApproved, "expected nil approved routes")
} else {
tsaddr.SortPrefixes(tt.wantApproved)
slices.SortFunc(tt.wantApproved, netip.Prefix.Compare)
assert.Equal(t, tt.wantApproved, gotApproved, "approved routes mismatch")
}
})

View file

@ -13,7 +13,6 @@ import (
"gorm.io/gorm"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/types/ptr"
)
func TestApproveRoutesWithPolicy_NeverRemovesRoutes(t *testing.T) {
@ -92,8 +91,8 @@ func TestApproveRoutesWithPolicy_NeverRemovesRoutes(t *testing.T) {
announcedRoutes: []netip.Prefix{}, // No routes announced anymore
nodeUser: "test",
wantApproved: []netip.Prefix{
netip.MustParsePrefix("172.16.0.0/16"),
netip.MustParsePrefix("10.0.0.0/24"),
netip.MustParsePrefix("172.16.0.0/16"),
netip.MustParsePrefix("192.168.0.0/24"),
},
wantChanged: false,
@ -124,8 +123,8 @@ func TestApproveRoutesWithPolicy_NeverRemovesRoutes(t *testing.T) {
nodeUser: "test",
nodeTags: []string{"tag:approved"},
wantApproved: []netip.Prefix{
netip.MustParsePrefix("172.16.0.0/16"), // New tag-approved
netip.MustParsePrefix("10.0.0.0/24"), // Previous approval preserved
netip.MustParsePrefix("172.16.0.0/16"), // New tag-approved
},
wantChanged: true,
},
@ -168,13 +167,13 @@ func TestApproveRoutesWithPolicy_NeverRemovesRoutes(t *testing.T) {
MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(),
Hostname: tt.nodeHostname,
UserID: ptr.To(user.ID),
User: ptr.To(user),
UserID: new(user.ID),
User: new(user),
RegisterMethod: util.RegisterMethodAuthKey,
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tt.announcedRoutes,
},
IPv4: ptr.To(netip.MustParseAddr("100.64.0.1")),
IPv4: new(netip.MustParseAddr("100.64.0.1")),
ApprovedRoutes: tt.currentApproved,
Tags: tt.nodeTags,
}
@ -294,13 +293,13 @@ func TestApproveRoutesWithPolicy_EdgeCases(t *testing.T) {
MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(),
Hostname: "testnode",
UserID: ptr.To(user.ID),
User: ptr.To(user),
UserID: new(user.ID),
User: new(user),
RegisterMethod: util.RegisterMethodAuthKey,
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tt.announcedRoutes,
},
IPv4: ptr.To(netip.MustParseAddr("100.64.0.1")),
IPv4: new(netip.MustParseAddr("100.64.0.1")),
ApprovedRoutes: tt.currentApproved,
}
nodes := types.Nodes{&node}
@ -343,13 +342,13 @@ func TestApproveRoutesWithPolicy_NilPolicyManagerCase(t *testing.T) {
MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(),
Hostname: "testnode",
UserID: ptr.To(user.ID),
User: ptr.To(user),
UserID: new(user.ID),
User: new(user),
RegisterMethod: util.RegisterMethodAuthKey,
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: announcedRoutes,
},
IPv4: ptr.To(netip.MustParseAddr("100.64.0.1")),
IPv4: new(netip.MustParseAddr("100.64.0.1")),
ApprovedRoutes: currentApproved,
}

View file

@ -14,7 +14,6 @@ import (
"github.com/stretchr/testify/require"
"gorm.io/gorm"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
var ap = func(ipStr string) *netip.Addr {
@ -1084,21 +1083,21 @@ func TestSSHPolicyRules(t *testing.T) {
nodeUser1 := types.Node{
Hostname: "user1-device",
IPv4: ap("100.64.0.1"),
UserID: ptr.To(uint(1)),
User: ptr.To(users[0]),
UserID: new(uint(1)),
User: new(users[0]),
}
nodeUser2 := types.Node{
Hostname: "user2-device",
IPv4: ap("100.64.0.2"),
UserID: ptr.To(uint(2)),
User: ptr.To(users[1]),
UserID: new(uint(2)),
User: new(users[1]),
}
taggedClient := types.Node{
Hostname: "tagged-client",
IPv4: ap("100.64.0.4"),
UserID: ptr.To(uint(2)),
User: ptr.To(users[1]),
UserID: new(uint(2)),
User: new(users[1]),
Tags: []string{"tag:client"},
}
@ -1106,8 +1105,8 @@ func TestSSHPolicyRules(t *testing.T) {
nodeTaggedServer := types.Node{
Hostname: "tagged-server",
IPv4: ap("100.64.0.5"),
UserID: ptr.To(uint(1)),
User: ptr.To(users[0]),
UserID: new(uint(1)),
User: new(users[0]),
Tags: []string{"tag:server"},
}

View file

@ -17,7 +17,6 @@ import (
"gorm.io/gorm"
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
"tailscale.com/util/must"
)
@ -145,13 +144,13 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0:ab12:4843:2222:6273:2221"),
User: ptr.To(users[0]),
User: new(users[0]),
},
peers: types.Nodes{
&types.Node{
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0:ab12:4843:2222:6273:2222"),
User: ptr.To(users[0]),
User: new(users[0]),
},
},
want: []tailcfg.FilterRule{},
@ -192,7 +191,7 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]),
User: new(users[1]),
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: []netip.Prefix{
netip.MustParsePrefix("10.33.0.0/16"),
@ -203,7 +202,7 @@ func TestReduceFilterRules(t *testing.T) {
&types.Node{
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[1]),
User: new(users[1]),
},
},
want: []tailcfg.FilterRule{
@ -274,19 +273,19 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]),
User: new(users[1]),
},
peers: types.Nodes{
&types.Node{
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[2]),
User: new(users[2]),
},
// "internal" exit node
&types.Node{
IPv4: ap("100.64.0.100"),
IPv6: ap("fd7a:115c:a1e0::100"),
User: ptr.To(users[3]),
User: new(users[3]),
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tsaddr.ExitRoutes(),
},
@ -335,7 +334,7 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{
IPv4: ap("100.64.0.100"),
IPv6: ap("fd7a:115c:a1e0::100"),
User: ptr.To(users[3]),
User: new(users[3]),
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tsaddr.ExitRoutes(),
},
@ -344,12 +343,12 @@ func TestReduceFilterRules(t *testing.T) {
&types.Node{
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[2]),
User: new(users[2]),
},
&types.Node{
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]),
User: new(users[1]),
},
},
want: []tailcfg.FilterRule{
@ -442,7 +441,7 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{
IPv4: ap("100.64.0.100"),
IPv6: ap("fd7a:115c:a1e0::100"),
User: ptr.To(users[3]),
User: new(users[3]),
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tsaddr.ExitRoutes(),
},
@ -451,12 +450,12 @@ func TestReduceFilterRules(t *testing.T) {
&types.Node{
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[2]),
User: new(users[2]),
},
&types.Node{
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]),
User: new(users[1]),
},
},
want: []tailcfg.FilterRule{
@ -549,7 +548,7 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{
IPv4: ap("100.64.0.100"),
IPv6: ap("fd7a:115c:a1e0::100"),
User: ptr.To(users[3]),
User: new(users[3]),
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: []netip.Prefix{netip.MustParsePrefix("8.0.0.0/16"), netip.MustParsePrefix("16.0.0.0/16")},
},
@ -558,12 +557,12 @@ func TestReduceFilterRules(t *testing.T) {
&types.Node{
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[2]),
User: new(users[2]),
},
&types.Node{
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]),
User: new(users[1]),
},
},
want: []tailcfg.FilterRule{
@ -634,7 +633,7 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{
IPv4: ap("100.64.0.100"),
IPv6: ap("fd7a:115c:a1e0::100"),
User: ptr.To(users[3]),
User: new(users[3]),
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: []netip.Prefix{netip.MustParsePrefix("8.0.0.0/8"), netip.MustParsePrefix("16.0.0.0/8")},
},
@ -643,12 +642,12 @@ func TestReduceFilterRules(t *testing.T) {
&types.Node{
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[2]),
User: new(users[2]),
},
&types.Node{
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]),
User: new(users[1]),
},
},
want: []tailcfg.FilterRule{
@ -711,7 +710,7 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{
IPv4: ap("100.64.0.100"),
IPv6: ap("fd7a:115c:a1e0::100"),
User: ptr.To(users[3]),
User: new(users[3]),
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: []netip.Prefix{netip.MustParsePrefix("172.16.0.0/24")},
},
@ -721,7 +720,7 @@ func TestReduceFilterRules(t *testing.T) {
&types.Node{
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]),
User: new(users[1]),
},
},
want: []tailcfg.FilterRule{
@ -778,13 +777,13 @@ func TestReduceFilterRules(t *testing.T) {
node: &types.Node{
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[3]),
User: new(users[3]),
},
peers: types.Nodes{
&types.Node{
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[1]),
User: new(users[1]),
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: []netip.Prefix{p("172.16.0.0/24"), p("10.10.11.0/24"), p("10.10.12.0/24")},
},

View file

@ -10,7 +10,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
"tailscale.com/types/ptr"
)
func TestNodeCanApproveRoute(t *testing.T) {
@ -25,24 +24,24 @@ func TestNodeCanApproveRoute(t *testing.T) {
ID: 1,
Hostname: "user1-device",
IPv4: ap("100.64.0.1"),
UserID: ptr.To(uint(1)),
User: ptr.To(users[0]),
UserID: new(uint(1)),
User: new(users[0]),
}
exitNode := types.Node{
ID: 2,
Hostname: "user2-device",
IPv4: ap("100.64.0.2"),
UserID: ptr.To(uint(2)),
User: ptr.To(users[1]),
UserID: new(uint(2)),
User: new(users[1]),
}
taggedNode := types.Node{
ID: 3,
Hostname: "tagged-server",
IPv4: ap("100.64.0.3"),
UserID: ptr.To(uint(3)),
User: ptr.To(users[2]),
UserID: new(uint(3)),
User: new(users[2]),
Tags: []string{"tag:router"},
}
@ -50,8 +49,8 @@ func TestNodeCanApproveRoute(t *testing.T) {
ID: 4,
Hostname: "multi-tag-node",
IPv4: ap("100.64.0.4"),
UserID: ptr.To(uint(2)),
User: ptr.To(users[1]),
UserID: new(uint(2)),
User: new(users[1]),
Tags: []string{"tag:router", "tag:server"},
}

View file

@ -15,7 +15,6 @@ import (
"github.com/stretchr/testify/require"
"gorm.io/gorm"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
// aliasWithPorts creates an AliasWithPorts structure from an alias and ports.
@ -411,15 +410,15 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
nodeTaggedServer := types.Node{
Hostname: "tagged-server",
IPv4: createAddr("100.64.0.1"),
UserID: ptr.To(users[0].ID),
User: ptr.To(users[0]),
UserID: new(users[0].ID),
User: new(users[0]),
Tags: []string{"tag:server"},
}
nodeTaggedDB := types.Node{
Hostname: "tagged-db",
IPv4: createAddr("100.64.0.2"),
UserID: ptr.To(users[1].ID),
User: ptr.To(users[1]),
UserID: new(users[1].ID),
User: new(users[1]),
Tags: []string{"tag:database"},
}
// Add untagged node for user2 - this will be the SSH source
@ -427,8 +426,8 @@ func TestCompileSSHPolicy_UserMapping(t *testing.T) {
nodeUser2Untagged := types.Node{
Hostname: "user2-device",
IPv4: createAddr("100.64.0.3"),
UserID: ptr.To(users[1].ID),
User: ptr.To(users[1]),
UserID: new(users[1].ID),
User: new(users[1]),
}
nodes := types.Nodes{&nodeTaggedServer, &nodeTaggedDB, &nodeUser2Untagged}
@ -658,15 +657,15 @@ func TestCompileSSHPolicy_CheckAction(t *testing.T) {
nodeTaggedServer := types.Node{
Hostname: "tagged-server",
IPv4: createAddr("100.64.0.1"),
UserID: ptr.To(users[0].ID),
User: ptr.To(users[0]),
UserID: new(users[0].ID),
User: new(users[0]),
Tags: []string{"tag:server"},
}
nodeUser2 := types.Node{
Hostname: "user2-device",
IPv4: createAddr("100.64.0.2"),
UserID: ptr.To(users[1].ID),
User: ptr.To(users[1]),
UserID: new(users[1].ID),
User: new(users[1]),
}
nodes := types.Nodes{&nodeTaggedServer, &nodeUser2}
@ -723,15 +722,15 @@ func TestSSHIntegrationReproduction(t *testing.T) {
node1 := &types.Node{
Hostname: "user1-node",
IPv4: createAddr("100.64.0.1"),
UserID: ptr.To(users[0].ID),
User: ptr.To(users[0]),
UserID: new(users[0].ID),
User: new(users[0]),
}
node2 := &types.Node{
Hostname: "user2-node",
IPv4: createAddr("100.64.0.2"),
UserID: ptr.To(users[1].ID),
User: ptr.To(users[1]),
UserID: new(users[1].ID),
User: new(users[1]),
}
nodes := types.Nodes{node1, node2}
@ -848,19 +847,19 @@ func TestCompileFilterRulesForNodeWithAutogroupSelf(t *testing.T) {
nodes := types.Nodes{
{
User: ptr.To(users[0]),
User: new(users[0]),
IPv4: ap("100.64.0.1"),
},
{
User: ptr.To(users[0]),
User: new(users[0]),
IPv4: ap("100.64.0.2"),
},
{
User: ptr.To(users[1]),
User: new(users[1]),
IPv4: ap("100.64.0.3"),
},
{
User: ptr.To(users[1]),
User: new(users[1]),
IPv4: ap("100.64.0.4"),
},
// Tagged device for user1
@ -983,11 +982,11 @@ func TestTagUserMutualExclusivity(t *testing.T) {
nodes := types.Nodes{
// User-owned nodes
{
User: ptr.To(users[0]),
User: new(users[0]),
IPv4: ap("100.64.0.1"),
},
{
User: ptr.To(users[1]),
User: new(users[1]),
IPv4: ap("100.64.0.2"),
},
// Tagged nodes
@ -1005,8 +1004,8 @@ func TestTagUserMutualExclusivity(t *testing.T) {
policy := &Policy{
TagOwners: TagOwners{
Tag("tag:server"): Owners{ptr.To(Username("user1@"))},
Tag("tag:database"): Owners{ptr.To(Username("user2@"))},
Tag("tag:server"): Owners{new(Username("user1@"))},
Tag("tag:database"): Owners{new(Username("user2@"))},
},
ACLs: []ACL{
// Rule 1: user1 (user-owned) should NOT be able to reach tagged nodes
@ -1101,11 +1100,11 @@ func TestAutogroupTagged(t *testing.T) {
nodes := types.Nodes{
// User-owned nodes (not tagged)
{
User: ptr.To(users[0]),
User: new(users[0]),
IPv4: ap("100.64.0.1"),
},
{
User: ptr.To(users[1]),
User: new(users[1]),
IPv4: ap("100.64.0.2"),
},
// Tagged nodes
@ -1128,10 +1127,10 @@ func TestAutogroupTagged(t *testing.T) {
policy := &Policy{
TagOwners: TagOwners{
Tag("tag:server"): Owners{ptr.To(Username("user1@"))},
Tag("tag:database"): Owners{ptr.To(Username("user2@"))},
Tag("tag:web"): Owners{ptr.To(Username("user1@"))},
Tag("tag:prod"): Owners{ptr.To(Username("user1@"))},
Tag("tag:server"): Owners{new(Username("user1@"))},
Tag("tag:database"): Owners{new(Username("user2@"))},
Tag("tag:web"): Owners{new(Username("user1@"))},
Tag("tag:prod"): Owners{new(Username("user1@"))},
},
ACLs: []ACL{
// Rule: autogroup:tagged can reach user-owned nodes
@ -1252,10 +1251,10 @@ func TestAutogroupSelfWithSpecificUserSource(t *testing.T) {
}
nodes := types.Nodes{
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1")},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4")},
{User: new(users[0]), IPv4: ap("100.64.0.1")},
{User: new(users[0]), IPv4: ap("100.64.0.2")},
{User: new(users[1]), IPv4: ap("100.64.0.3")},
{User: new(users[1]), IPv4: ap("100.64.0.4")},
}
policy := &Policy{
@ -1320,11 +1319,11 @@ func TestAutogroupSelfWithGroupSource(t *testing.T) {
}
nodes := types.Nodes{
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1")},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4")},
{User: ptr.To(users[2]), IPv4: ap("100.64.0.5")},
{User: new(users[0]), IPv4: ap("100.64.0.1")},
{User: new(users[0]), IPv4: ap("100.64.0.2")},
{User: new(users[1]), IPv4: ap("100.64.0.3")},
{User: new(users[1]), IPv4: ap("100.64.0.4")},
{User: new(users[2]), IPv4: ap("100.64.0.5")},
}
policy := &Policy{
@ -1389,13 +1388,13 @@ func TestSSHWithAutogroupSelfInDestination(t *testing.T) {
nodes := types.Nodes{
// User1's nodes
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1"), Hostname: "user1-node1"},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2"), Hostname: "user1-node2"},
{User: new(users[0]), IPv4: ap("100.64.0.1"), Hostname: "user1-node1"},
{User: new(users[0]), IPv4: ap("100.64.0.2"), Hostname: "user1-node2"},
// User2's nodes
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3"), Hostname: "user2-node1"},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4"), Hostname: "user2-node2"},
{User: new(users[1]), IPv4: ap("100.64.0.3"), Hostname: "user2-node1"},
{User: new(users[1]), IPv4: ap("100.64.0.4"), Hostname: "user2-node2"},
// Tagged node for user1 (should be excluded)
{User: ptr.To(users[0]), IPv4: ap("100.64.0.5"), Hostname: "user1-tagged", Tags: []string{"tag:server"}},
{User: new(users[0]), IPv4: ap("100.64.0.5"), Hostname: "user1-tagged", Tags: []string{"tag:server"}},
}
policy := &Policy{
@ -1470,10 +1469,10 @@ func TestSSHWithAutogroupSelfAndSpecificUser(t *testing.T) {
}
nodes := types.Nodes{
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1")},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4")},
{User: new(users[0]), IPv4: ap("100.64.0.1")},
{User: new(users[0]), IPv4: ap("100.64.0.2")},
{User: new(users[1]), IPv4: ap("100.64.0.3")},
{User: new(users[1]), IPv4: ap("100.64.0.4")},
}
policy := &Policy{
@ -1526,11 +1525,11 @@ func TestSSHWithAutogroupSelfAndGroup(t *testing.T) {
}
nodes := types.Nodes{
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1")},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3")},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4")},
{User: ptr.To(users[2]), IPv4: ap("100.64.0.5")},
{User: new(users[0]), IPv4: ap("100.64.0.1")},
{User: new(users[0]), IPv4: ap("100.64.0.2")},
{User: new(users[1]), IPv4: ap("100.64.0.3")},
{User: new(users[1]), IPv4: ap("100.64.0.4")},
{User: new(users[2]), IPv4: ap("100.64.0.5")},
}
policy := &Policy{
@ -1585,10 +1584,10 @@ func TestSSHWithAutogroupSelfExcludesTaggedDevices(t *testing.T) {
}
nodes := types.Nodes{
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1"), Hostname: "untagged1"},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2"), Hostname: "untagged2"},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.3"), Hostname: "tagged1", Tags: []string{"tag:server"}},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.4"), Hostname: "tagged2", Tags: []string{"tag:web"}},
{User: new(users[0]), IPv4: ap("100.64.0.1"), Hostname: "untagged1"},
{User: new(users[0]), IPv4: ap("100.64.0.2"), Hostname: "untagged2"},
{User: new(users[0]), IPv4: ap("100.64.0.3"), Hostname: "tagged1", Tags: []string{"tag:server"}},
{User: new(users[0]), IPv4: ap("100.64.0.4"), Hostname: "tagged2", Tags: []string{"tag:web"}},
}
policy := &Policy{
@ -1647,10 +1646,10 @@ func TestSSHWithAutogroupSelfAndMixedDestinations(t *testing.T) {
}
nodes := types.Nodes{
{User: ptr.To(users[0]), IPv4: ap("100.64.0.1"), Hostname: "user1-device"},
{User: ptr.To(users[0]), IPv4: ap("100.64.0.2"), Hostname: "user1-device2"},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.3"), Hostname: "user2-device"},
{User: ptr.To(users[1]), IPv4: ap("100.64.0.4"), Hostname: "user2-router", Tags: []string{"tag:router"}},
{User: new(users[0]), IPv4: ap("100.64.0.1"), Hostname: "user1-device"},
{User: new(users[0]), IPv4: ap("100.64.0.2"), Hostname: "user1-device2"},
{User: new(users[1]), IPv4: ap("100.64.0.3"), Hostname: "user2-device"},
{User: new(users[1]), IPv4: ap("100.64.0.4"), Hostname: "user2-router", Tags: []string{"tag:router"}},
}
policy := &Policy{
@ -1722,11 +1721,11 @@ func TestAutogroupSelfWithNonExistentUserInGroup(t *testing.T) {
nodes := types.Nodes{
// superadmin's device
{ID: 1, User: ptr.To(users[0]), IPv4: ap("100.64.0.1"), Hostname: "superadmin-device"},
{ID: 1, User: new(users[0]), IPv4: ap("100.64.0.1"), Hostname: "superadmin-device"},
// admin's device
{ID: 2, User: ptr.To(users[1]), IPv4: ap("100.64.0.2"), Hostname: "admin-device"},
{ID: 2, User: new(users[1]), IPv4: ap("100.64.0.2"), Hostname: "admin-device"},
// direction's device
{ID: 3, User: ptr.To(users[2]), IPv4: ap("100.64.0.3"), Hostname: "direction-device"},
{ID: 3, User: new(users[2]), IPv4: ap("100.64.0.3"), Hostname: "direction-device"},
// tagged servers
{ID: 4, IPv4: ap("100.64.0.10"), Hostname: "common-server", Tags: []string{"tag:common"}},
{ID: 5, IPv4: ap("100.64.0.11"), Hostname: "tech-server", Tags: []string{"tag:tech"}},

View file

@ -11,7 +11,6 @@ import (
"github.com/stretchr/testify/require"
"gorm.io/gorm"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
func node(name, ipv4, ipv6 string, user types.User) *types.Node {
@ -20,8 +19,8 @@ func node(name, ipv4, ipv6 string, user types.User) *types.Node {
Hostname: name,
IPv4: ap(ipv4),
IPv6: ap(ipv6),
User: ptr.To(user),
UserID: ptr.To(user.ID),
User: new(user),
UserID: new(user.ID),
}
}
@ -463,8 +462,8 @@ func TestAutogroupSelfWithOtherRules(t *testing.T) {
Hostname: "test-1-device",
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[0]),
UserID: ptr.To(users[0].ID),
User: new(users[0]),
UserID: new(users[0].ID),
Hostinfo: &tailcfg.Hostinfo{},
}
@ -474,8 +473,8 @@ func TestAutogroupSelfWithOtherRules(t *testing.T) {
Hostname: "test-2-router",
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[1]),
UserID: ptr.To(users[1].ID),
User: new(users[1]),
UserID: new(users[1].ID),
Tags: []string{"tag:node-router"},
Hostinfo: &tailcfg.Hostinfo{},
}
@ -543,8 +542,8 @@ func TestAutogroupSelfPolicyUpdateTriggersMapResponse(t *testing.T) {
Hostname: "test-1-device",
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[0]),
UserID: ptr.To(users[0].ID),
User: new(users[0]),
UserID: new(users[0].ID),
Hostinfo: &tailcfg.Hostinfo{},
}
@ -553,8 +552,8 @@ func TestAutogroupSelfPolicyUpdateTriggersMapResponse(t *testing.T) {
Hostname: "test-2-device",
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[1]),
UserID: ptr.To(users[1].ID),
User: new(users[1]),
UserID: new(users[1].ID),
Hostinfo: &tailcfg.Hostinfo{},
}
@ -653,8 +652,8 @@ func TestTagPropagationToPeerMap(t *testing.T) {
Hostname: "user1-node",
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[0]),
UserID: ptr.To(users[0].ID),
User: new(users[0]),
UserID: new(users[0].ID),
Tags: []string{"tag:web", "tag:internal"},
}
@ -664,8 +663,8 @@ func TestTagPropagationToPeerMap(t *testing.T) {
Hostname: "user2-node",
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[1]),
UserID: ptr.To(users[1].ID),
User: new(users[1]),
UserID: new(users[1].ID),
}
initialNodes := types.Nodes{user1Node, user2Node}
@ -692,8 +691,8 @@ func TestTagPropagationToPeerMap(t *testing.T) {
Hostname: "user1-node",
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[0]),
UserID: ptr.To(users[0].ID),
User: new(users[0]),
UserID: new(users[0].ID),
Tags: []string{"tag:internal"}, // tag:web removed!
}
@ -755,8 +754,8 @@ func TestAutogroupSelfWithAdminOverride(t *testing.T) {
Hostname: "admin-device",
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[0]),
UserID: ptr.To(users[0].ID),
User: new(users[0]),
UserID: new(users[0].ID),
Hostinfo: &tailcfg.Hostinfo{},
}
@ -766,8 +765,8 @@ func TestAutogroupSelfWithAdminOverride(t *testing.T) {
Hostname: "user1-server",
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[1]),
UserID: ptr.To(users[1].ID),
User: new(users[1]),
UserID: new(users[1].ID),
Tags: []string{"tag:server"},
Hostinfo: &tailcfg.Hostinfo{},
}
@ -838,8 +837,8 @@ func TestAutogroupSelfSymmetricVisibility(t *testing.T) {
Hostname: "device-a",
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
User: ptr.To(users[0]),
UserID: ptr.To(users[0].ID),
User: new(users[0]),
UserID: new(users[0].ID),
Hostinfo: &tailcfg.Hostinfo{},
}
@ -849,8 +848,8 @@ func TestAutogroupSelfSymmetricVisibility(t *testing.T) {
Hostname: "device-b",
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
User: ptr.To(users[1]),
UserID: ptr.To(users[1].ID),
User: new(users[1]),
UserID: new(users[1].ID),
Tags: []string{"tag:web"},
Hostinfo: &tailcfg.Hostinfo{},
}
@ -928,8 +927,8 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
superadminDevice := &types.Node{
ID: 1,
Hostname: "superadmin-laptop",
User: ptr.To(users[0]),
UserID: ptr.To(users[0].ID),
User: new(users[0]),
UserID: new(users[0].ID),
IPv4: ap("100.64.0.1"),
Hostinfo: &tailcfg.Hostinfo{},
}
@ -937,8 +936,8 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
adminDevice := &types.Node{
ID: 2,
Hostname: "admin-laptop",
User: ptr.To(users[1]),
UserID: ptr.To(users[1].ID),
User: new(users[1]),
UserID: new(users[1].ID),
IPv4: ap("100.64.0.2"),
Hostinfo: &tailcfg.Hostinfo{},
}
@ -946,8 +945,8 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
directionDevice := &types.Node{
ID: 3,
Hostname: "direction-laptop",
User: ptr.To(users[2]),
UserID: ptr.To(users[2].ID),
User: new(users[2]),
UserID: new(users[2].ID),
IPv4: ap("100.64.0.3"),
Hostinfo: &tailcfg.Hostinfo{},
}
@ -955,8 +954,8 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
commonServer := &types.Node{
ID: 4,
Hostname: "common-server",
User: ptr.To(users[3]),
UserID: ptr.To(users[3].ID),
User: new(users[3]),
UserID: new(users[3].ID),
IPv4: ap("100.64.0.4"),
Tags: []string{"tag:common"},
Hostinfo: &tailcfg.Hostinfo{},
@ -965,8 +964,8 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
techServer := &types.Node{
ID: 5,
Hostname: "tech-server",
User: ptr.To(users[3]),
UserID: ptr.To(users[3].ID),
User: new(users[3]),
UserID: new(users[3].ID),
IPv4: ap("100.64.0.5"),
Tags: []string{"tag:tech"},
Hostinfo: &tailcfg.Hostinfo{},
@ -975,8 +974,8 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
privilegedServer := &types.Node{
ID: 6,
Hostname: "privileged-server",
User: ptr.To(users[3]),
UserID: ptr.To(users[3].ID),
User: new(users[3]),
UserID: new(users[3].ID),
IPv4: ap("100.64.0.6"),
Tags: []string{"tag:privileged"},
Hostinfo: &tailcfg.Hostinfo{},
@ -1088,8 +1087,8 @@ func TestEmptyFilterNodesStillVisible(t *testing.T) {
adminDevice := &types.Node{
ID: 1,
Hostname: "admin-laptop",
User: ptr.To(users[0]),
UserID: ptr.To(users[0].ID),
User: new(users[0]),
UserID: new(users[0].ID),
IPv4: ap("100.64.0.1"),
Hostinfo: &tailcfg.Hostinfo{},
}
@ -1099,8 +1098,8 @@ func TestEmptyFilterNodesStillVisible(t *testing.T) {
taggedServer := &types.Node{
ID: 2,
Hostname: "server",
User: ptr.To(users[1]),
UserID: ptr.To(users[1].ID),
User: new(users[1]),
UserID: new(users[1].ID),
IPv4: ap("100.64.0.2"),
Tags: []string{"tag:server"},
Hostinfo: &tailcfg.Hostinfo{},
@ -1157,8 +1156,8 @@ func TestAutogroupSelfCombinedWithTags(t *testing.T) {
adminLaptop := &types.Node{
ID: 1,
Hostname: "admin-laptop",
User: ptr.To(users[0]),
UserID: ptr.To(users[0].ID),
User: new(users[0]),
UserID: new(users[0].ID),
IPv4: ap("100.64.0.1"),
Hostinfo: &tailcfg.Hostinfo{},
}
@ -1166,8 +1165,8 @@ func TestAutogroupSelfCombinedWithTags(t *testing.T) {
adminPhone := &types.Node{
ID: 2,
Hostname: "admin-phone",
User: ptr.To(users[0]),
UserID: ptr.To(users[0].ID),
User: new(users[0]),
UserID: new(users[0].ID),
IPv4: ap("100.64.0.2"),
Hostinfo: &tailcfg.Hostinfo{},
}
@ -1176,8 +1175,8 @@ func TestAutogroupSelfCombinedWithTags(t *testing.T) {
webServer := &types.Node{
ID: 3,
Hostname: "web-server",
User: ptr.To(users[1]),
UserID: ptr.To(users[1].ID),
User: new(users[1]),
UserID: new(users[1].ID),
IPv4: ap("100.64.0.3"),
Tags: []string{"tag:web"},
Hostinfo: &tailcfg.Hostinfo{},
@ -1252,8 +1251,8 @@ func TestIssue2990SameUserTaggedDevice(t *testing.T) {
node1 := &types.Node{
ID: 1,
Hostname: "node1",
User: ptr.To(users[0]),
UserID: ptr.To(users[0].ID),
User: new(users[0]),
UserID: new(users[0].ID),
IPv4: ap("100.64.0.1"),
IPv6: ap("fd7a:115c:a1e0::1"),
Hostinfo: &tailcfg.Hostinfo{},
@ -1263,8 +1262,8 @@ func TestIssue2990SameUserTaggedDevice(t *testing.T) {
node2 := &types.Node{
ID: 2,
Hostname: "node2",
User: ptr.To(users[0]),
UserID: ptr.To(users[0].ID),
User: new(users[0]),
UserID: new(users[0].ID),
IPv4: ap("100.64.0.2"),
IPv6: ap("fd7a:115c:a1e0::2"),
Tags: []string{"tag:admin"},

View file

@ -16,7 +16,6 @@ import (
"go4.org/netipx"
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
"tailscale.com/types/views"
"tailscale.com/util/multierr"
"tailscale.com/util/slicesx"
@ -762,17 +761,17 @@ func parseAlias(vs string) (Alias, error) {
case isWildcard(vs):
return Wildcard, nil
case isUser(vs):
return ptr.To(Username(vs)), nil
return new(Username(vs)), nil
case isGroup(vs):
return ptr.To(Group(vs)), nil
return new(Group(vs)), nil
case isTag(vs):
return ptr.To(Tag(vs)), nil
return new(Tag(vs)), nil
case isAutoGroup(vs):
return ptr.To(AutoGroup(vs)), nil
return new(AutoGroup(vs)), nil
}
if isHost(vs) {
return ptr.To(Host(vs)), nil
return new(Host(vs)), nil
}
return nil, fmt.Errorf("%w: %q", ErrInvalidAlias, vs)
@ -933,11 +932,11 @@ func (aa AutoApprovers) MarshalJSON() ([]byte, error) {
func parseAutoApprover(s string) (AutoApprover, error) {
switch {
case isUser(s):
return ptr.To(Username(s)), nil
return new(Username(s)), nil
case isGroup(s):
return ptr.To(Group(s)), nil
return new(Group(s)), nil
case isTag(s):
return ptr.To(Tag(s)), nil
return new(Tag(s)), nil
}
return nil, fmt.Errorf("%w: %q", ErrInvalidAutoApprover, s)
@ -1027,11 +1026,11 @@ func (o Owners) MarshalJSON() ([]byte, error) {
func parseOwner(s string) (Owner, error) {
switch {
case isUser(s):
return ptr.To(Username(s)), nil
return new(Username(s)), nil
case isGroup(s):
return ptr.To(Group(s)), nil
return new(Group(s)), nil
case isTag(s):
return ptr.To(Tag(s)), nil
return new(Tag(s)), nil
}
return nil, fmt.Errorf("%w: %q", ErrInvalidOwner, s)
@ -2299,8 +2298,7 @@ func unmarshalPolicy(b []byte) (*Policy, error) {
ast.Standardize()
if err = json.Unmarshal(ast.Pack(), &policy, policyJSONOpts...); err != nil { //nolint:noinlineerr
var serr *json.SemanticError
if errors.As(err, &serr) && errors.Is(serr.Err, json.ErrUnknownName) {
if serr, ok := errors.AsType[*json.SemanticError](err); ok && errors.Is(serr.Err, json.ErrUnknownName) {
ptr := serr.JSONPointer
name := ptr.LastToken()

View file

@ -19,7 +19,6 @@ import (
"gorm.io/gorm"
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
// TestUnmarshalPolicy tests the unmarshalling of JSON into Policy objects and the marshalling
@ -53,11 +52,11 @@ func TestMarshalJSON(t *testing.T) {
Action: "accept",
Protocol: "tcp",
Sources: Aliases{
ptr.To(Username("user@example.com")),
new(Username("user@example.com")),
},
Destinations: []AliasWithPorts{
{
Alias: ptr.To(Username("other@example.com")),
Alias: new(Username("other@example.com")),
Ports: []tailcfg.PortRange{{First: 80, Last: 80}},
},
},
@ -254,11 +253,11 @@ func TestUnmarshalPolicy(t *testing.T) {
Action: "accept",
Protocol: "tcp",
Sources: Aliases{
ptr.To(Username("testuser@headscale.net")),
new(Username("testuser@headscale.net")),
},
Destinations: []AliasWithPorts{
{
Alias: ptr.To(Username("otheruser@headscale.net")),
Alias: new(Username("otheruser@headscale.net")),
Ports: []tailcfg.PortRange{{First: 80, Last: 80}},
},
},
@ -547,7 +546,7 @@ func TestUnmarshalPolicy(t *testing.T) {
},
Destinations: []AliasWithPorts{
{
Alias: ptr.To(AutoGroup("autogroup:internet")),
Alias: new(AutoGroup("autogroup:internet")),
Ports: []tailcfg.PortRange{tailcfg.PortRangeAny},
},
},
@ -684,8 +683,8 @@ func TestUnmarshalPolicy(t *testing.T) {
`,
want: &Policy{
TagOwners: TagOwners{
Tag("tag:web"): Owners{ptr.To(Username("admin@example.com"))},
Tag("tag:server"): Owners{ptr.To(Username("admin@example.com"))},
Tag("tag:web"): Owners{new(Username("admin@example.com"))},
Tag("tag:server"): Owners{new(Username("admin@example.com"))},
},
SSHs: []SSH{
{
@ -1157,7 +1156,7 @@ func TestUnmarshalPolicy(t *testing.T) {
},
Destinations: []AliasWithPorts{
{
Alias: ptr.To(AutoGroup("autogroup:internet")),
Alias: new(AutoGroup("autogroup:internet")),
Ports: []tailcfg.PortRange{tailcfg.PortRangeAny},
},
},
@ -1494,7 +1493,7 @@ func TestUnmarshalPolicy(t *testing.T) {
want: &Policy{
TagOwners: TagOwners{
Tag("tag:bigbrother"): {},
Tag("tag:smallbrother"): {ptr.To(Tag("tag:bigbrother"))},
Tag("tag:smallbrother"): {new(Tag("tag:bigbrother"))},
},
ACLs: []ACL{
{
@ -1505,7 +1504,7 @@ func TestUnmarshalPolicy(t *testing.T) {
},
Destinations: []AliasWithPorts{
{
Alias: ptr.To(Tag("tag:smallbrother")),
Alias: new(Tag("tag:smallbrother")),
Ports: []tailcfg.PortRange{{First: 9000, Last: 9000}},
},
},
@ -1829,14 +1828,14 @@ func TestUnmarshalPolicy(t *testing.T) {
}
}
func gp(s string) *Group { return ptr.To(Group(s)) }
func up(s string) *Username { return ptr.To(Username(s)) }
func hp(s string) *Host { return ptr.To(Host(s)) }
func tp(s string) *Tag { return ptr.To(Tag(s)) }
func agp(s string) *AutoGroup { return ptr.To(AutoGroup(s)) }
func gp(s string) *Group { return new(Group(s)) }
func up(s string) *Username { return new(Username(s)) }
func hp(s string) *Host { return new(Host(s)) }
func tp(s string) *Tag { return new(Tag(s)) }
func agp(s string) *AutoGroup { return new(AutoGroup(s)) }
func mp(pref string) netip.Prefix { return netip.MustParsePrefix(pref) }
func ap(addr string) *netip.Addr { return ptr.To(netip.MustParseAddr(addr)) }
func pp(pref string) *Prefix { return ptr.To(Prefix(mp(pref))) }
func ap(addr string) *netip.Addr { return new(netip.MustParseAddr(addr)) }
func pp(pref string) *Prefix { return new(Prefix(mp(pref))) }
func p(pref string) Prefix { return Prefix(mp(pref)) }
func TestResolvePolicy(t *testing.T) {
@ -1882,31 +1881,31 @@ func TestResolvePolicy(t *testing.T) {
},
{
name: "username",
toResolve: ptr.To(Username("testuser@")),
toResolve: new(Username("testuser@")),
nodes: types.Nodes{
// Not matching other user
{
User: ptr.To(notme),
User: new(notme),
IPv4: ap("100.100.101.1"),
},
// Not matching forced tags
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:anything"},
IPv4: ap("100.100.101.2"),
},
// not matching because it's tagged (tags copied from AuthKey)
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"alsotagged"},
IPv4: ap("100.100.101.3"),
},
{
User: ptr.To(testuser),
User: new(testuser),
IPv4: ap("100.100.101.103"),
},
{
User: ptr.To(testuser),
User: new(testuser),
IPv4: ap("100.100.101.104"),
},
},
@ -1914,31 +1913,31 @@ func TestResolvePolicy(t *testing.T) {
},
{
name: "group",
toResolve: ptr.To(Group("group:testgroup")),
toResolve: new(Group("group:testgroup")),
nodes: types.Nodes{
// Not matching other user
{
User: ptr.To(notme),
User: new(notme),
IPv4: ap("100.100.101.4"),
},
// Not matching forced tags
{
User: ptr.To(groupuser),
User: new(groupuser),
Tags: []string{"tag:anything"},
IPv4: ap("100.100.101.5"),
},
// not matching because it's tagged (tags copied from AuthKey)
{
User: ptr.To(groupuser),
User: new(groupuser),
Tags: []string{"tag:alsotagged"},
IPv4: ap("100.100.101.6"),
},
{
User: ptr.To(groupuser),
User: new(groupuser),
IPv4: ap("100.100.101.203"),
},
{
User: ptr.To(groupuser),
User: new(groupuser),
IPv4: ap("100.100.101.204"),
},
},
@ -1956,7 +1955,7 @@ func TestResolvePolicy(t *testing.T) {
nodes: types.Nodes{
// Not matching other user
{
User: ptr.To(notme),
User: new(notme),
IPv4: ap("100.100.101.9"),
},
// Not matching forced tags
@ -1992,7 +1991,7 @@ func TestResolvePolicy(t *testing.T) {
pol: &Policy{
TagOwners: TagOwners{
Tag("tag:bigbrother"): {},
Tag("tag:smallbrother"): {ptr.To(Tag("tag:bigbrother"))},
Tag("tag:smallbrother"): {new(Tag("tag:bigbrother"))},
},
},
nodes: types.Nodes{
@ -2015,7 +2014,7 @@ func TestResolvePolicy(t *testing.T) {
pol: &Policy{
TagOwners: TagOwners{
Tag("tag:bigbrother"): {},
Tag("tag:smallbrother"): {ptr.To(Tag("tag:bigbrother"))},
Tag("tag:smallbrother"): {new(Tag("tag:bigbrother"))},
},
},
nodes: types.Nodes{
@ -2050,14 +2049,14 @@ func TestResolvePolicy(t *testing.T) {
},
{
name: "multiple-groups",
toResolve: ptr.To(Group("group:testgroup")),
toResolve: new(Group("group:testgroup")),
nodes: types.Nodes{
{
User: ptr.To(groupuser1),
User: new(groupuser1),
IPv4: ap("100.100.101.203"),
},
{
User: ptr.To(groupuser2),
User: new(groupuser2),
IPv4: ap("100.100.101.204"),
},
},
@ -2075,10 +2074,10 @@ func TestResolvePolicy(t *testing.T) {
},
{
name: "invalid-username",
toResolve: ptr.To(Username("invaliduser@")),
toResolve: new(Username("invaliduser@")),
nodes: types.Nodes{
{
User: ptr.To(testuser),
User: new(testuser),
IPv4: ap("100.100.101.103"),
},
},
@ -2106,47 +2105,47 @@ func TestResolvePolicy(t *testing.T) {
},
{
name: "autogroup-member-comprehensive",
toResolve: ptr.To(AutoGroupMember),
toResolve: new(AutoGroupMember),
nodes: types.Nodes{
// Node with no tags (should be included - is a member)
{
User: ptr.To(testuser),
User: new(testuser),
IPv4: ap("100.100.101.1"),
},
// Node with single tag (should be excluded - tagged nodes are not members)
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:test"},
IPv4: ap("100.100.101.2"),
},
// Node with multiple tags, all defined in policy (should be excluded)
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:test", "tag:other"},
IPv4: ap("100.100.101.3"),
},
// Node with tag not defined in policy (should be excluded - still tagged)
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:undefined"},
IPv4: ap("100.100.101.4"),
},
// Node with mixed tags - some defined, some not (should be excluded)
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:test", "tag:undefined"},
IPv4: ap("100.100.101.5"),
},
// Another untagged node from different user (should be included)
{
User: ptr.To(testuser2),
User: new(testuser2),
IPv4: ap("100.100.101.6"),
},
},
pol: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("testuser@"))},
Tag("tag:other"): Owners{ptr.To(Username("testuser@"))},
Tag("tag:test"): Owners{new(Username("testuser@"))},
Tag("tag:other"): Owners{new(Username("testuser@"))},
},
},
want: []netip.Prefix{
@ -2156,54 +2155,54 @@ func TestResolvePolicy(t *testing.T) {
},
{
name: "autogroup-tagged",
toResolve: ptr.To(AutoGroupTagged),
toResolve: new(AutoGroupTagged),
nodes: types.Nodes{
// Node with no tags (should be excluded - not tagged)
{
User: ptr.To(testuser),
User: new(testuser),
IPv4: ap("100.100.101.1"),
},
// Node with single tag defined in policy (should be included)
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:test"},
IPv4: ap("100.100.101.2"),
},
// Node with multiple tags, all defined in policy (should be included)
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:test", "tag:other"},
IPv4: ap("100.100.101.3"),
},
// Node with tag not defined in policy (should be included - still tagged)
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:undefined"},
IPv4: ap("100.100.101.4"),
},
// Node with mixed tags - some defined, some not (should be included)
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:test", "tag:undefined"},
IPv4: ap("100.100.101.5"),
},
// Another untagged node from different user (should be excluded)
{
User: ptr.To(testuser2),
User: new(testuser2),
IPv4: ap("100.100.101.6"),
},
// Tagged node from different user (should be included)
{
User: ptr.To(testuser2),
User: new(testuser2),
Tags: []string{"tag:server"},
IPv4: ap("100.100.101.7"),
},
},
pol: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("testuser@"))},
Tag("tag:other"): Owners{ptr.To(Username("testuser@"))},
Tag("tag:server"): Owners{ptr.To(Username("testuser2@"))},
Tag("tag:test"): Owners{new(Username("testuser@"))},
Tag("tag:other"): Owners{new(Username("testuser@"))},
Tag("tag:server"): Owners{new(Username("testuser2@"))},
},
},
want: []netip.Prefix{
@ -2214,37 +2213,37 @@ func TestResolvePolicy(t *testing.T) {
},
{
name: "autogroup-self",
toResolve: ptr.To(AutoGroupSelf),
toResolve: new(AutoGroupSelf),
nodes: types.Nodes{
{
User: ptr.To(testuser),
User: new(testuser),
IPv4: ap("100.100.101.1"),
},
{
User: ptr.To(testuser2),
User: new(testuser2),
IPv4: ap("100.100.101.2"),
},
{
User: ptr.To(testuser),
User: new(testuser),
Tags: []string{"tag:test"},
IPv4: ap("100.100.101.3"),
},
{
User: ptr.To(testuser2),
User: new(testuser2),
Tags: []string{"tag:test"},
IPv4: ap("100.100.101.4"),
},
},
pol: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("testuser@"))},
Tag("tag:test"): Owners{new(Username("testuser@"))},
},
},
wantErr: "autogroup:self requires per-node resolution",
},
{
name: "autogroup-invalid",
toResolve: ptr.To(AutoGroup("autogroup:invalid")),
toResolve: new(AutoGroup("autogroup:invalid")),
wantErr: "unknown autogroup",
},
}
@ -2323,7 +2322,7 @@ func TestResolveAutoApprovers(t *testing.T) {
policy: &Policy{
AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Username("user1@"))},
mp("10.0.0.0/24"): {new(Username("user1@"))},
},
},
},
@ -2338,8 +2337,8 @@ func TestResolveAutoApprovers(t *testing.T) {
policy: &Policy{
AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Username("user1@"))},
mp("10.0.1.0/24"): {ptr.To(Username("user2@"))},
mp("10.0.0.0/24"): {new(Username("user1@"))},
mp("10.0.1.0/24"): {new(Username("user2@"))},
},
},
},
@ -2354,7 +2353,7 @@ func TestResolveAutoApprovers(t *testing.T) {
name: "exit-node",
policy: &Policy{
AutoApprovers: AutoApproverPolicy{
ExitNode: AutoApprovers{ptr.To(Username("user1@"))},
ExitNode: AutoApprovers{new(Username("user1@"))},
},
},
want: map[netip.Prefix]*netipx.IPSet{},
@ -2369,7 +2368,7 @@ func TestResolveAutoApprovers(t *testing.T) {
},
AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Group("group:testgroup"))},
mp("10.0.0.0/24"): {new(Group("group:testgroup"))},
},
},
},
@ -2384,20 +2383,20 @@ func TestResolveAutoApprovers(t *testing.T) {
policy: &Policy{
TagOwners: TagOwners{
"tag:testtag": Owners{
ptr.To(Username("user1@")),
ptr.To(Username("user2@")),
new(Username("user1@")),
new(Username("user2@")),
},
"tag:exittest": Owners{
ptr.To(Group("group:exitgroup")),
new(Group("group:exitgroup")),
},
},
Groups: Groups{
"group:exitgroup": Usernames{"user2@"},
},
AutoApprovers: AutoApproverPolicy{
ExitNode: AutoApprovers{ptr.To(Tag("tag:exittest"))},
ExitNode: AutoApprovers{new(Tag("tag:exittest"))},
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.1.0/24"): {ptr.To(Tag("tag:testtag"))},
mp("10.0.1.0/24"): {new(Tag("tag:testtag"))},
},
},
},
@ -2415,10 +2414,10 @@ func TestResolveAutoApprovers(t *testing.T) {
},
AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Group("group:testgroup"))},
mp("10.0.1.0/24"): {ptr.To(Username("user3@"))},
mp("10.0.0.0/24"): {new(Group("group:testgroup"))},
mp("10.0.1.0/24"): {new(Username("user3@"))},
},
ExitNode: AutoApprovers{ptr.To(Username("user1@"))},
ExitNode: AutoApprovers{new(Username("user1@"))},
},
},
want: map[netip.Prefix]*netipx.IPSet{
@ -2639,7 +2638,7 @@ func TestNodeCanApproveRoute(t *testing.T) {
policy: &Policy{
AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Username("user1@"))},
mp("10.0.0.0/24"): {new(Username("user1@"))},
},
},
},
@ -2652,8 +2651,8 @@ func TestNodeCanApproveRoute(t *testing.T) {
policy: &Policy{
AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Username("user1@"))},
mp("10.0.1.0/24"): {ptr.To(Username("user2@"))},
mp("10.0.0.0/24"): {new(Username("user1@"))},
mp("10.0.1.0/24"): {new(Username("user2@"))},
},
},
},
@ -2665,7 +2664,7 @@ func TestNodeCanApproveRoute(t *testing.T) {
name: "exit-node-approval",
policy: &Policy{
AutoApprovers: AutoApproverPolicy{
ExitNode: AutoApprovers{ptr.To(Username("user1@"))},
ExitNode: AutoApprovers{new(Username("user1@"))},
},
},
node: nodes[0],
@ -2680,7 +2679,7 @@ func TestNodeCanApproveRoute(t *testing.T) {
},
AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Group("group:testgroup"))},
mp("10.0.0.0/24"): {new(Group("group:testgroup"))},
},
},
},
@ -2696,10 +2695,10 @@ func TestNodeCanApproveRoute(t *testing.T) {
},
AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Group("group:testgroup"))},
mp("10.0.1.0/24"): {ptr.To(Username("user3@"))},
mp("10.0.0.0/24"): {new(Group("group:testgroup"))},
mp("10.0.1.0/24"): {new(Username("user3@"))},
},
ExitNode: AutoApprovers{ptr.To(Username("user1@"))},
ExitNode: AutoApprovers{new(Username("user1@"))},
},
},
node: nodes[0],
@ -2711,7 +2710,7 @@ func TestNodeCanApproveRoute(t *testing.T) {
policy: &Policy{
AutoApprovers: AutoApproverPolicy{
Routes: map[netip.Prefix]AutoApprovers{
mp("10.0.0.0/24"): {ptr.To(Username("user2@"))},
mp("10.0.0.0/24"): {new(Username("user2@"))},
},
},
},
@ -2769,7 +2768,7 @@ func TestResolveTagOwners(t *testing.T) {
name: "single-tag-owner",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user1@"))},
Tag("tag:test"): Owners{new(Username("user1@"))},
},
},
want: map[Tag]*netipx.IPSet{
@ -2781,7 +2780,7 @@ func TestResolveTagOwners(t *testing.T) {
name: "multiple-tag-owners",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user1@")), ptr.To(Username("user2@"))},
Tag("tag:test"): Owners{new(Username("user1@")), new(Username("user2@"))},
},
},
want: map[Tag]*netipx.IPSet{
@ -2796,7 +2795,7 @@ func TestResolveTagOwners(t *testing.T) {
"group:testgroup": Usernames{"user1@", "user2@"},
},
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Group("group:testgroup"))},
Tag("tag:test"): Owners{new(Group("group:testgroup"))},
},
},
want: map[Tag]*netipx.IPSet{
@ -2808,8 +2807,8 @@ func TestResolveTagOwners(t *testing.T) {
name: "tag-owns-tag",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:bigbrother"): Owners{ptr.To(Username("user1@"))},
Tag("tag:smallbrother"): Owners{ptr.To(Tag("tag:bigbrother"))},
Tag("tag:bigbrother"): Owners{new(Username("user1@"))},
Tag("tag:smallbrother"): Owners{new(Tag("tag:bigbrother"))},
},
},
want: map[Tag]*netipx.IPSet{
@ -2871,7 +2870,7 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "single-tag-owner",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user1@"))},
Tag("tag:test"): Owners{new(Username("user1@"))},
},
},
node: nodes[0],
@ -2882,7 +2881,7 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "multiple-tag-owners",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user1@")), ptr.To(Username("user2@"))},
Tag("tag:test"): Owners{new(Username("user1@")), new(Username("user2@"))},
},
},
node: nodes[1],
@ -2896,7 +2895,7 @@ func TestNodeCanHaveTag(t *testing.T) {
"group:testgroup": Usernames{"user1@", "user2@"},
},
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Group("group:testgroup"))},
Tag("tag:test"): Owners{new(Group("group:testgroup"))},
},
},
node: nodes[1],
@ -2910,7 +2909,7 @@ func TestNodeCanHaveTag(t *testing.T) {
"group:testgroup": Usernames{"invalid"},
},
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Group("group:testgroup"))},
Tag("tag:test"): Owners{new(Group("group:testgroup"))},
},
},
node: nodes[0],
@ -2922,7 +2921,7 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "node-cannot-have-tag",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user2@"))},
Tag("tag:test"): Owners{new(Username("user2@"))},
},
},
node: nodes[0],
@ -2933,7 +2932,7 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "node-with-unauthorized-tag-different-user",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:prod"): Owners{ptr.To(Username("user1@"))},
Tag("tag:prod"): Owners{new(Username("user1@"))},
},
},
node: nodes[2], // user3's node
@ -2944,8 +2943,8 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "node-with-multiple-tags-one-unauthorized",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:web"): Owners{ptr.To(Username("user1@"))},
Tag("tag:database"): Owners{ptr.To(Username("user2@"))},
Tag("tag:web"): Owners{new(Username("user1@"))},
Tag("tag:database"): Owners{new(Username("user2@"))},
},
},
node: nodes[0], // user1's node
@ -2965,7 +2964,7 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "tag-not-in-tagowners",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:prod"): Owners{ptr.To(Username("user1@"))},
Tag("tag:prod"): Owners{new(Username("user1@"))},
},
},
node: nodes[0],
@ -2978,13 +2977,13 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "node-without-ip-user-owns-tag",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user1@"))},
Tag("tag:test"): Owners{new(Username("user1@"))},
},
},
node: &types.Node{
// No IPv4 or IPv6 - simulates new node registration
User: &users[0],
UserID: ptr.To(users[0].ID),
UserID: new(users[0].ID),
},
tag: "tag:test",
want: true, // Should succeed via user-based fallback
@ -2993,13 +2992,13 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "node-without-ip-user-does-not-own-tag",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user2@"))},
Tag("tag:test"): Owners{new(Username("user2@"))},
},
},
node: &types.Node{
// No IPv4 or IPv6 - simulates new node registration
User: &users[0], // user1, but tag owned by user2
UserID: ptr.To(users[0].ID),
UserID: new(users[0].ID),
},
tag: "tag:test",
want: false, // user1 does not own tag:test
@ -3011,13 +3010,13 @@ func TestNodeCanHaveTag(t *testing.T) {
"group:admins": Usernames{"user1@", "user2@"},
},
TagOwners: TagOwners{
Tag("tag:admin"): Owners{ptr.To(Group("group:admins"))},
Tag("tag:admin"): Owners{new(Group("group:admins"))},
},
},
node: &types.Node{
// No IPv4 or IPv6 - simulates new node registration
User: &users[1], // user2 is in group:admins
UserID: ptr.To(users[1].ID),
UserID: new(users[1].ID),
},
tag: "tag:admin",
want: true, // Should succeed via group membership
@ -3029,13 +3028,13 @@ func TestNodeCanHaveTag(t *testing.T) {
"group:admins": Usernames{"user1@"},
},
TagOwners: TagOwners{
Tag("tag:admin"): Owners{ptr.To(Group("group:admins"))},
Tag("tag:admin"): Owners{new(Group("group:admins"))},
},
},
node: &types.Node{
// No IPv4 or IPv6 - simulates new node registration
User: &users[1], // user2 is NOT in group:admins
UserID: ptr.To(users[1].ID),
UserID: new(users[1].ID),
},
tag: "tag:admin",
want: false, // user2 is not in group:admins
@ -3044,7 +3043,7 @@ func TestNodeCanHaveTag(t *testing.T) {
name: "node-without-ip-no-user",
policy: &Policy{
TagOwners: TagOwners{
Tag("tag:test"): Owners{ptr.To(Username("user1@"))},
Tag("tag:test"): Owners{new(Username("user1@"))},
},
},
node: &types.Node{
@ -3061,14 +3060,14 @@ func TestNodeCanHaveTag(t *testing.T) {
},
TagOwners: TagOwners{
Tag("tag:server"): Owners{
ptr.To(Username("user1@")),
ptr.To(Group("group:ops")),
new(Username("user1@")),
new(Group("group:ops")),
},
},
},
node: &types.Node{
User: &users[0], // user1 directly owns the tag
UserID: ptr.To(users[0].ID),
UserID: new(users[0].ID),
},
tag: "tag:server",
want: true,
@ -3081,14 +3080,14 @@ func TestNodeCanHaveTag(t *testing.T) {
},
TagOwners: TagOwners{
Tag("tag:server"): Owners{
ptr.To(Username("user1@")),
ptr.To(Group("group:ops")),
new(Username("user1@")),
new(Group("group:ops")),
},
},
},
node: &types.Node{
User: &users[2], // user3 is in group:ops
UserID: ptr.To(users[2].ID),
UserID: new(users[2].ID),
},
tag: "tag:server",
want: true,
@ -3134,14 +3133,14 @@ func TestUserMatchesOwner(t *testing.T) {
name: "username-match",
policy: &Policy{},
user: users[0],
owner: ptr.To(Username("user1@")),
owner: new(Username("user1@")),
want: true,
},
{
name: "username-no-match",
policy: &Policy{},
user: users[0],
owner: ptr.To(Username("user2@")),
owner: new(Username("user2@")),
want: false,
},
{
@ -3152,7 +3151,7 @@ func TestUserMatchesOwner(t *testing.T) {
},
},
user: users[1], // user2 is in group:admins
owner: ptr.To(Group("group:admins")),
owner: new(Group("group:admins")),
want: true,
},
{
@ -3163,7 +3162,7 @@ func TestUserMatchesOwner(t *testing.T) {
},
},
user: users[1], // user2 is NOT in group:admins
owner: ptr.To(Group("group:admins")),
owner: new(Group("group:admins")),
want: false,
},
{
@ -3172,7 +3171,7 @@ func TestUserMatchesOwner(t *testing.T) {
Groups: Groups{},
},
user: users[0],
owner: ptr.To(Group("group:undefined")),
owner: new(Group("group:undefined")),
want: false,
},
{
@ -3517,20 +3516,20 @@ func TestFlattenTagOwners(t *testing.T) {
{
name: "tag-owns-tag",
input: TagOwners{
Tag("tag:bigbrother"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:smallbrother"): Owners{ptr.To(Tag("tag:bigbrother"))},
Tag("tag:bigbrother"): Owners{new(Group("group:user1"))},
Tag("tag:smallbrother"): Owners{new(Tag("tag:bigbrother"))},
},
want: TagOwners{
Tag("tag:bigbrother"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:smallbrother"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:bigbrother"): Owners{new(Group("group:user1"))},
Tag("tag:smallbrother"): Owners{new(Group("group:user1"))},
},
wantErr: "",
},
{
name: "circular-reference",
input: TagOwners{
Tag("tag:a"): Owners{ptr.To(Tag("tag:b"))},
Tag("tag:b"): Owners{ptr.To(Tag("tag:a"))},
Tag("tag:a"): Owners{new(Tag("tag:b"))},
Tag("tag:b"): Owners{new(Tag("tag:a"))},
},
want: nil,
wantErr: "circular reference detected: tag:a -> tag:b",
@ -3538,83 +3537,83 @@ func TestFlattenTagOwners(t *testing.T) {
{
name: "mixed-owners",
input: TagOwners{
Tag("tag:x"): Owners{ptr.To(Username("user1@")), ptr.To(Tag("tag:y"))},
Tag("tag:y"): Owners{ptr.To(Username("user2@"))},
Tag("tag:x"): Owners{new(Username("user1@")), new(Tag("tag:y"))},
Tag("tag:y"): Owners{new(Username("user2@"))},
},
want: TagOwners{
Tag("tag:x"): Owners{ptr.To(Username("user1@")), ptr.To(Username("user2@"))},
Tag("tag:y"): Owners{ptr.To(Username("user2@"))},
Tag("tag:x"): Owners{new(Username("user1@")), new(Username("user2@"))},
Tag("tag:y"): Owners{new(Username("user2@"))},
},
wantErr: "",
},
{
name: "mixed-dupe-owners",
input: TagOwners{
Tag("tag:x"): Owners{ptr.To(Username("user1@")), ptr.To(Tag("tag:y"))},
Tag("tag:y"): Owners{ptr.To(Username("user1@"))},
Tag("tag:x"): Owners{new(Username("user1@")), new(Tag("tag:y"))},
Tag("tag:y"): Owners{new(Username("user1@"))},
},
want: TagOwners{
Tag("tag:x"): Owners{ptr.To(Username("user1@"))},
Tag("tag:y"): Owners{ptr.To(Username("user1@"))},
Tag("tag:x"): Owners{new(Username("user1@"))},
Tag("tag:y"): Owners{new(Username("user1@"))},
},
wantErr: "",
},
{
name: "no-tag-owners",
input: TagOwners{
Tag("tag:solo"): Owners{ptr.To(Username("user1@"))},
Tag("tag:solo"): Owners{new(Username("user1@"))},
},
want: TagOwners{
Tag("tag:solo"): Owners{ptr.To(Username("user1@"))},
Tag("tag:solo"): Owners{new(Username("user1@"))},
},
wantErr: "",
},
{
name: "tag-long-owner-chain",
input: TagOwners{
Tag("tag:a"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:b"): Owners{ptr.To(Tag("tag:a"))},
Tag("tag:c"): Owners{ptr.To(Tag("tag:b"))},
Tag("tag:d"): Owners{ptr.To(Tag("tag:c"))},
Tag("tag:e"): Owners{ptr.To(Tag("tag:d"))},
Tag("tag:f"): Owners{ptr.To(Tag("tag:e"))},
Tag("tag:g"): Owners{ptr.To(Tag("tag:f"))},
Tag("tag:a"): Owners{new(Group("group:user1"))},
Tag("tag:b"): Owners{new(Tag("tag:a"))},
Tag("tag:c"): Owners{new(Tag("tag:b"))},
Tag("tag:d"): Owners{new(Tag("tag:c"))},
Tag("tag:e"): Owners{new(Tag("tag:d"))},
Tag("tag:f"): Owners{new(Tag("tag:e"))},
Tag("tag:g"): Owners{new(Tag("tag:f"))},
},
want: TagOwners{
Tag("tag:a"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:b"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:c"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:d"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:e"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:f"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:g"): Owners{ptr.To(Group("group:user1"))},
Tag("tag:a"): Owners{new(Group("group:user1"))},
Tag("tag:b"): Owners{new(Group("group:user1"))},
Tag("tag:c"): Owners{new(Group("group:user1"))},
Tag("tag:d"): Owners{new(Group("group:user1"))},
Tag("tag:e"): Owners{new(Group("group:user1"))},
Tag("tag:f"): Owners{new(Group("group:user1"))},
Tag("tag:g"): Owners{new(Group("group:user1"))},
},
wantErr: "",
},
{
name: "tag-long-circular-chain",
input: TagOwners{
Tag("tag:a"): Owners{ptr.To(Tag("tag:g"))},
Tag("tag:b"): Owners{ptr.To(Tag("tag:a"))},
Tag("tag:c"): Owners{ptr.To(Tag("tag:b"))},
Tag("tag:d"): Owners{ptr.To(Tag("tag:c"))},
Tag("tag:e"): Owners{ptr.To(Tag("tag:d"))},
Tag("tag:f"): Owners{ptr.To(Tag("tag:e"))},
Tag("tag:g"): Owners{ptr.To(Tag("tag:f"))},
Tag("tag:a"): Owners{new(Tag("tag:g"))},
Tag("tag:b"): Owners{new(Tag("tag:a"))},
Tag("tag:c"): Owners{new(Tag("tag:b"))},
Tag("tag:d"): Owners{new(Tag("tag:c"))},
Tag("tag:e"): Owners{new(Tag("tag:d"))},
Tag("tag:f"): Owners{new(Tag("tag:e"))},
Tag("tag:g"): Owners{new(Tag("tag:f"))},
},
wantErr: "circular reference detected: tag:a -> tag:b -> tag:c -> tag:d -> tag:e -> tag:f -> tag:g",
},
{
name: "undefined-tag-reference",
input: TagOwners{
Tag("tag:a"): Owners{ptr.To(Tag("tag:nonexistent"))},
Tag("tag:a"): Owners{new(Tag("tag:nonexistent"))},
},
wantErr: `tag "tag:a" references undefined tag "tag:nonexistent"`,
},
{
name: "tag-with-empty-owners-is-valid",
input: TagOwners{
Tag("tag:a"): Owners{ptr.To(Tag("tag:b"))},
Tag("tag:a"): Owners{new(Tag("tag:b"))},
Tag("tag:b"): Owners{}, // empty owners but exists
},
want: TagOwners{