all: upgrade to Go 1.26rc2 and modernize codebase
Some checks failed
Build / build-nix (push) Has been cancelled
Build / build-cross (GOARCH=amd64 GOOS=darwin) (push) Has been cancelled
Build / build-cross (GOARCH=amd64 GOOS=linux) (push) Has been cancelled
Build / build-cross (GOARCH=arm64 GOOS=darwin) (push) Has been cancelled
Build / build-cross (GOARCH=arm64 GOOS=linux) (push) Has been cancelled
Check Generated Files / check-generated (push) Has been cancelled
NixOS Module Tests / nix-module-check (push) Has been cancelled
Tests / test (push) Has been cancelled
Close inactive issues / close-issues (push) Has been cancelled

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

@@ -6,8 +6,7 @@ body:
- type: checkboxes
attributes:
label: Is this a support request?
description:
This issue tracker is for bugs and feature requests only. If you need
description: This issue tracker is for bugs and feature requests only. If you need
help, please use ask in our Discord community
options:
- label: This is not a support request
@@ -15,8 +14,7 @@ body:
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description:
Please search to see if an issue already exists for the bug you
description: Please search to see if an issue already exists for the bug you
encountered.
options:
- label: I have searched the existing issues

View File

@@ -45,6 +45,16 @@ linters:
Import "github.com/juanfont/headscale/hscontrol/util/zlog/zf" and use
constants like zf.NodeID, zf.UserName, etc. Add new constants to
hscontrol/util/zlog/zf/fields.go if needed.
# Forbid ptr.To - use Go 1.26 new(expr) instead
- pattern: 'ptr\.To\('
msg: >-
ptr.To is forbidden. Use Go 1.26's new(expr) syntax instead.
Example: ptr.To(value) → new(value)
# Forbid tsaddr.SortPrefixes - use slices.SortFunc with netip.Prefix.Compare
- pattern: 'tsaddr\.SortPrefixes'
msg: >-
tsaddr.SortPrefixes is forbidden. Use Go 1.26's netip.Prefix.Compare instead.
Example: slices.SortFunc(prefixes, netip.Prefix.Compare)
analyze-types: true
gocritic:
disabled-checks:

View File

@@ -2,7 +2,7 @@
version: 2
before:
hooks:
- go mod tidy -compat=1.25
- go mod tidy -compat=1.26
- go mod vendor
release:

View File

@@ -43,26 +43,12 @@ repos:
entry: prettier --write --list-different
language: system
exclude: ^docs/
types_or:
[
javascript,
jsx,
ts,
tsx,
yaml,
json,
toml,
html,
css,
scss,
sass,
markdown,
]
types_or: [javascript, jsx, ts, tsx, yaml, json, toml, html, css, scss, sass, markdown]
# golangci-lint for Go code quality
- id: golangci-lint
name: golangci-lint
entry: nix develop --command golangci-lint run --new-from-rev=HEAD~1 --timeout=5m --fix
entry: golangci-lint run --new-from-rev=HEAD~1 --timeout=5m --fix
language: system
types: [go]
pass_filenames: false

View File

@@ -1,6 +1,6 @@
# For testing purposes only
FROM golang:alpine AS build-env
FROM golang:1.26rc2-alpine AS build-env
WORKDIR /go/src

View File

@@ -2,7 +2,7 @@
# and are in no way endorsed by Headscale's maintainers as an
# official nor supported release or distribution.
FROM docker.io/golang:1.25-trixie AS builder
FROM docker.io/golang:1.26rc2-trixie AS builder
ARG VERSION=dev
ENV GOPATH /go
WORKDIR /go/src/headscale

View File

@@ -4,7 +4,7 @@
# This Dockerfile is more or less lifted from tailscale/tailscale
# to ensure a similar build process when testing the HEAD of tailscale.
FROM golang:1.25-alpine AS build-env
FROM golang:1.26rc2-alpine AS build-env
WORKDIR /go/src

View File

@@ -23,8 +23,7 @@ var serveCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
app, err := newHeadscaleServerWithConfig()
if err != nil {
var squibbleErr squibble.ValidationError
if errors.As(err, &squibbleErr) {
if squibbleErr, ok := errors.AsType[squibble.ValidationError](err); ok {
fmt.Printf("SQLite schema failed to validate:\n")
fmt.Println(squibbleErr.Diff)
}

View File

@@ -76,7 +76,7 @@ func detectGoVersion() string {
content, err := os.ReadFile(goModPath)
if err != nil {
return "1.25"
return "1.26rc2"
}
lines := splitLines(string(content))
@@ -91,7 +91,7 @@ func detectGoVersion() string {
}
}
return "1.25"
return "1.26rc2"
}
// splitLines splits a string into lines without using strings.Split.

View File

@@ -26,7 +26,7 @@
overlays.default = _: prev:
let
pkgs = nixpkgs.legacyPackages.${prev.stdenv.hostPlatform.system};
buildGo = pkgs.buildGo125Module;
buildGo = pkgs.buildGo126Module;
vendorHash = "sha256-9BvphYDAxzwooyVokI3l+q1wRuRsWn/qM+NpWUgqJH0=";
in
{
@@ -94,14 +94,46 @@
subPackages = [ "." ];
};
# Upstream does not override buildGoModule properly,
# importing a specific module, so comment out for now.
# golangci-lint = prev.golangci-lint.override {
# buildGoModule = buildGo;
# };
# golangci-lint-langserver = prev.golangci-lint.override {
# buildGoModule = buildGo;
# };
# Build golangci-lint with Go 1.26 (upstream uses hardcoded Go version)
golangci-lint = buildGo rec {
pname = "golangci-lint";
version = "2.8.0";
src = pkgs.fetchFromGitHub {
owner = "golangci";
repo = "golangci-lint";
rev = "v${version}";
hash = "sha256-w6MAOirj8rPHYbKrW4gJeemXCS64fNtteV6IioqIQTQ=";
};
vendorHash = "sha256-/Vqo/yrmGh6XipELQ9NDtlMEO2a654XykmvnMs0BdrI=";
subPackages = [ "cmd/golangci-lint" ];
nativeBuildInputs = [ pkgs.installShellFiles ];
ldflags = [
"-s"
"-w"
"-X main.version=${version}"
"-X main.commit=v${version}"
"-X main.date=1970-01-01T00:00:00Z"
];
postInstall = ''
for shell in bash zsh fish; do
HOME=$TMPDIR $out/bin/golangci-lint completion $shell > golangci-lint.$shell
installShellCompletion golangci-lint.$shell
done
'';
meta = {
description = "Fast linters runner for Go";
homepage = "https://golangci-lint.run/";
changelog = "https://github.com/golangci/golangci-lint/blob/v${version}/CHANGELOG.md";
mainProgram = "golangci-lint";
};
};
# The package uses buildGo125Module, not the convention.
# goreleaser = prev.goreleaser.override {
@@ -132,7 +164,7 @@
overlays = [ self.overlays.default ];
inherit system;
};
buildDeps = with pkgs; [ git go_1_25 gnumake ];
buildDeps = with pkgs; [ git go_1_26 gnumake ];
devDeps = with pkgs;
buildDeps
++ [

2
go.mod
View File

@@ -1,6 +1,6 @@
module github.com/juanfont/headscale
go 1.25.5
go 1.26rc2
require (
github.com/arl/statsviz v0.8.0

View File

@@ -16,7 +16,6 @@ import (
"gorm.io/gorm"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/types/ptr"
)
type AuthProvider interface {
@@ -113,8 +112,7 @@ func (h *Headscale) handleRegister(
resp, err := h.handleRegisterWithAuthKey(req, machineKey)
if err != nil {
// Preserve HTTPError types so they can be handled properly by the HTTP layer
var httpErr HTTPError
if errors.As(err, &httpErr) {
if httpErr, ok := errors.AsType[HTTPError](err); ok {
return nil, httpErr
}
@@ -315,7 +313,7 @@ func (h *Headscale) reqToNewRegisterResponse(
MachineKey: machineKey,
NodeKey: req.NodeKey,
Hostinfo: hostinfo,
LastSeen: ptr.To(time.Now()),
LastSeen: new(time.Now()),
},
)
@@ -344,8 +342,7 @@ func (h *Headscale) handleRegisterWithAuthKey(
return nil, NewHTTPError(http.StatusUnauthorized, "invalid pre auth key", nil)
}
var perr types.PAKError
if errors.As(err, &perr) {
if perr, ok := errors.AsType[types.PAKError](err); ok {
return nil, NewHTTPError(http.StatusUnauthorized, perr.Error(), nil)
}
@@ -443,7 +440,7 @@ func (h *Headscale) handleRegisterInteractive(
MachineKey: machineKey,
NodeKey: req.NodeKey,
Hostinfo: hostinfo,
LastSeen: ptr.To(time.Now()),
LastSeen: new(time.Now()),
},
)

View File

@@ -24,7 +24,6 @@ import (
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/gorm/schema"
"tailscale.com/net/tsaddr"
"zgo.at/zcache/v2"
)
@@ -172,7 +171,7 @@ AND auth_key_id NOT IN (
}
for nodeID, routes := range nodeRoutes {
tsaddr.SortPrefixes(routes)
slices.SortFunc(routes, netip.Prefix.Compare)
routes = slices.Compact(routes)
data, _ := json.Marshal(routes)

View File

@@ -13,7 +13,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"tailscale.com/net/tsaddr"
"tailscale.com/types/ptr"
)
var mpp = func(pref string) *netip.Prefix {
@@ -491,8 +490,8 @@ func TestIPAllocatorNextNoReservedIPs(t *testing.T) {
alloc, err := NewIPAllocator(
db,
ptr.To(tsaddr.CGNATRange()),
ptr.To(tsaddr.TailscaleULARange()),
new(tsaddr.CGNATRange()),
new(tsaddr.TailscaleULARange()),
types.IPAllocationStrategySequential,
)
if err != nil {
@@ -500,17 +499,17 @@ func TestIPAllocatorNextNoReservedIPs(t *testing.T) {
}
// Validate that we do not give out 100.100.100.100
nextQuad100, err := alloc.next(na("100.100.100.99"), ptr.To(tsaddr.CGNATRange()))
nextQuad100, err := alloc.next(na("100.100.100.99"), new(tsaddr.CGNATRange()))
require.NoError(t, err)
assert.Equal(t, na("100.100.100.101"), *nextQuad100)
// Validate that we do not give out fd7a:115c:a1e0::53
nextQuad100v6, err := alloc.next(na("fd7a:115c:a1e0::52"), ptr.To(tsaddr.TailscaleULARange()))
nextQuad100v6, err := alloc.next(na("fd7a:115c:a1e0::52"), new(tsaddr.TailscaleULARange()))
require.NoError(t, err)
assert.Equal(t, na("fd7a:115c:a1e0::54"), *nextQuad100v6)
// Validate that we do not give out fd7a:115c:a1e0::53
nextChrome, err := alloc.next(na("100.115.91.255"), ptr.To(tsaddr.CGNATRange()))
nextChrome, err := alloc.next(na("100.115.91.255"), new(tsaddr.CGNATRange()))
t.Logf("chrome: %s", nextChrome.String())
require.NoError(t, err)
assert.Equal(t, na("100.115.94.0"), *nextChrome)

View File

@@ -21,7 +21,6 @@ import (
"gorm.io/gorm"
"tailscale.com/net/tsaddr"
"tailscale.com/types/key"
"tailscale.com/types/ptr"
)
const (
@@ -694,7 +693,7 @@ func (hsdb *HSDatabase) CreateNodeForTest(user *types.User, hostname ...string)
Hostname: nodeName,
UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID),
AuthKeyID: new(pak.ID),
}
err = hsdb.DB.Save(node).Error

View File

@@ -22,7 +22,6 @@ import (
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/types/ptr"
)
func TestGetNode(t *testing.T) {
@@ -115,7 +114,7 @@ func TestExpireNode(t *testing.T) {
Hostname: "testnode",
UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID),
AuthKeyID: new(pak.ID),
Expiry: &time.Time{},
}
db.DB.Save(node)
@@ -159,7 +158,7 @@ func TestSetTags(t *testing.T) {
Hostname: "testnode",
UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID),
AuthKeyID: new(pak.ID),
}
trx := db.DB.Save(node)
@@ -444,7 +443,7 @@ func TestAutoApproveRoutes(t *testing.T) {
Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tt.routes,
},
IPv4: ptr.To(netip.MustParseAddr("100.64.0.1")),
IPv4: new(netip.MustParseAddr("100.64.0.1")),
}
err = adb.DB.Save(&node).Error
@@ -461,7 +460,7 @@ func TestAutoApproveRoutes(t *testing.T) {
RoutableIPs: tt.routes,
},
Tags: []string{"tag:exit"},
IPv4: ptr.To(netip.MustParseAddr("100.64.0.2")),
IPv4: new(netip.MustParseAddr("100.64.0.2")),
}
err = adb.DB.Save(&nodeTagged).Error
@@ -660,7 +659,7 @@ func TestListEphemeralNodes(t *testing.T) {
Hostname: "test",
UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID),
AuthKeyID: new(pak.ID),
}
nodeEph := types.Node{
@@ -670,7 +669,7 @@ func TestListEphemeralNodes(t *testing.T) {
Hostname: "ephemeral",
UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pakEph.ID),
AuthKeyID: new(pakEph.ID),
}
err = db.DB.Save(&node).Error
@@ -763,8 +762,8 @@ func TestNodeNaming(t *testing.T) {
return err
}
_, _ = RegisterNodeForTest(tx, nodeInvalidHostname, ptr.To(mpp("100.64.0.66/32").Addr()), nil)
_, err = RegisterNodeForTest(tx, nodeShortHostname, ptr.To(mpp("100.64.0.67/32").Addr()), nil)
_, _ = RegisterNodeForTest(tx, nodeInvalidHostname, new(mpp("100.64.0.66/32").Addr()), nil)
_, err = RegisterNodeForTest(tx, nodeShortHostname, new(mpp("100.64.0.67/32").Addr()), nil)
return err
})

View File

@@ -11,7 +11,6 @@ import (
"github.com/juanfont/headscale/hscontrol/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"tailscale.com/types/ptr"
)
func TestCreatePreAuthKey(t *testing.T) {
@@ -24,7 +23,7 @@ func TestCreatePreAuthKey(t *testing.T) {
test: func(t *testing.T, db *HSDatabase) {
t.Helper()
_, err := db.CreatePreAuthKey(ptr.To(types.UserID(12345)), true, false, nil, nil)
_, err := db.CreatePreAuthKey(new(types.UserID(12345)), true, false, nil, nil)
assert.Error(t, err)
},
},
@@ -127,7 +126,7 @@ func TestCannotDeleteAssignedPreAuthKey(t *testing.T) {
Hostname: "testest",
UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(key.ID),
AuthKeyID: new(key.ID),
}
db.DB.Save(&node)

View File

@@ -8,7 +8,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
"tailscale.com/types/ptr"
)
func TestCreateAndDestroyUser(t *testing.T) {
@@ -79,7 +78,7 @@ func TestDestroyUserErrors(t *testing.T) {
Hostname: "testnode",
UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID),
AuthKeyID: new(pak.ID),
}
trx := db.DB.Save(&node)
require.NoError(t, trx.Error)

View File

@@ -387,7 +387,7 @@ func (api headscaleV1APIServer) SetApprovedRoutes(
newApproved = append(newApproved, prefix)
}
}
tsaddr.SortPrefixes(newApproved)
slices.SortFunc(newApproved, netip.Prefix.Compare)
newApproved = slices.Compact(newApproved)
node, nodeChange, err := api.h.state.SetApprovedRoutes(types.NodeID(request.GetNodeId()), newApproved)

View File

@@ -36,8 +36,7 @@ const (
// httpError logs an error and sends an HTTP error response with the given.
func httpError(w http.ResponseWriter, err error) {
var herr HTTPError
if errors.As(err, &herr) {
if herr, ok := errors.AsType[HTTPError](err); ok {
http.Error(w, herr.Msg, herr.Code)
log.Error().Err(herr.Err).Int("code", herr.Code).Msgf("user msg: %s", herr.Msg)
} else {

View File

@@ -16,7 +16,6 @@ import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
// LockFreeBatcher errors.
@@ -151,7 +150,7 @@ func (b *LockFreeBatcher) RemoveNode(id types.NodeID, c chan<- *tailcfg.MapRespo
// No active connections - keep the node entry alive for rapid reconnections
// The node will get a fresh full map when it reconnects
nlog.Debug().Caller().Msg("node disconnected from batcher, keeping entry for rapid reconnection")
b.connected.Store(id, ptr.To(time.Now()))
b.connected.Store(id, new(time.Now()))
return false
}

View File

@@ -10,7 +10,6 @@ import (
"github.com/juanfont/headscale/hscontrol/types"
"tailscale.com/tailcfg"
"tailscale.com/types/dnstype"
"tailscale.com/types/ptr"
)
var iap = func(ipStr string) *netip.Addr {
@@ -47,7 +46,7 @@ func TestDNSConfigMapResponse(t *testing.T) {
mach := func(hostname, username string, userid uint) *types.Node {
return &types.Node{
Hostname: hostname,
UserID: ptr.To(userid),
UserID: new(userid),
User: &types.User{
Name: username,
},

View File

@@ -13,7 +13,6 @@ import (
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/types/ptr"
)
func TestTailNode(t *testing.T) {
@@ -98,7 +97,7 @@ func TestTailNode(t *testing.T) {
IPv4: iap("100.64.0.1"),
Hostname: "mini",
GivenName: "mini",
UserID: ptr.To(uint(0)),
UserID: new(uint(0)),
User: &types.User{
Name: "mini",
},
@@ -140,8 +139,8 @@ func TestTailNode(t *testing.T) {
Addresses: []netip.Prefix{netip.MustParsePrefix("100.64.0.1/32")},
AllowedIPs: []netip.Prefix{
tsaddr.AllIPv4(),
netip.MustParsePrefix("192.168.0.0/24"),
netip.MustParsePrefix("100.64.0.1/32"),
netip.MustParsePrefix("192.168.0.0/24"),
tsaddr.AllIPv6(),
},
PrimaryRoutes: []netip.Prefix{

View File

@@ -265,8 +265,7 @@ func (ns *noiseServer) NoiseRegistrationHandler(
resp, err = ns.headscale.handleRegister(req.Context(), regReq, ns.conn.Peer())
if err != nil {
var httpErr HTTPError
if errors.As(err, &httpErr) {
if httpErr, ok := errors.AsType[HTTPError](err); ok {
resp = &tailcfg.RegisterResponse{
Error: httpErr.Msg,
}

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{

View File

@@ -240,7 +240,7 @@ func (pr *PrimaryRoutes) PrimaryRoutes(id types.NodeID) []netip.Prefix {
}
}
tsaddr.SortPrefixes(routes)
slices.SortFunc(routes, netip.Prefix.Compare)
return routes
}
@@ -300,7 +300,7 @@ func (pr *PrimaryRoutes) DebugJSON() DebugRoutes {
// Populate available routes
for nodeID, routes := range pr.routes {
prefixes := routes.Slice()
tsaddr.SortPrefixes(prefixes)
slices.SortFunc(prefixes, netip.Prefix.Compare)
debug.AvailableRoutes[nodeID] = prefixes
}

View File

@@ -8,7 +8,6 @@ import (
"github.com/juanfont/headscale/hscontrol/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"tailscale.com/types/ptr"
)
// TestEphemeralNodeDeleteWithConcurrentUpdate tests the race condition where UpdateNode and DeleteNode
@@ -55,7 +54,7 @@ func TestEphemeralNodeDeleteWithConcurrentUpdate(t *testing.T) {
go func() {
updatedNode, updateOk = store.UpdateNode(node.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now())
n.LastSeen = new(time.Now())
})
done <- true
@@ -114,7 +113,7 @@ func TestUpdateNodeReturnsInvalidWhenDeletedInSameBatch(t *testing.T) {
// Start UpdateNode in goroutine - it will queue and wait for batch
go func() {
node, ok := store.UpdateNode(node.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now())
n.LastSeen = new(time.Now())
})
resultChan <- struct {
node types.NodeView
@@ -165,7 +164,7 @@ func TestPersistNodeToDBPreventsRaceCondition(t *testing.T) {
// Simulate UpdateNode being called
updatedNode, ok := store.UpdateNode(node.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now())
n.LastSeen = new(time.Now())
})
require.True(t, ok, "UpdateNode should succeed")
require.True(t, updatedNode.Valid(), "UpdateNode should return valid node")
@@ -234,7 +233,7 @@ func TestEphemeralNodeLogoutRaceCondition(t *testing.T) {
// Goroutine 1: UpdateNode (simulates UpdateNodeFromMapRequest)
go func() {
updatedNode, updateOk = store.UpdateNode(ephemeralNode.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now())
n.LastSeen = new(time.Now())
})
done <- true
@@ -310,7 +309,7 @@ func TestUpdateNodeFromMapRequestEphemeralLogoutSequence(t *testing.T) {
go func() {
node, ok := store.UpdateNode(ephemeralNode.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now())
n.LastSeen = new(time.Now())
endpoint := netip.MustParseAddrPort("10.0.0.1:41641")
n.Endpoints = []netip.AddrPort{endpoint}
})
@@ -380,7 +379,7 @@ func TestUpdateNodeDeletedInSameBatchReturnsInvalid(t *testing.T) {
go func() {
updatedNode, ok := store.UpdateNode(node.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now())
n.LastSeen = new(time.Now())
})
updateDone <- struct {
node types.NodeView
@@ -435,7 +434,7 @@ func TestPersistNodeToDBChecksNodeStoreBeforePersist(t *testing.T) {
// UpdateNode returns a node
updatedNode, ok := store.UpdateNode(ephemeralNode.ID, func(n *types.Node) {
n.LastSeen = ptr.To(time.Now())
n.LastSeen = new(time.Now())
})
require.True(t, ok, "UpdateNode should succeed")
require.True(t, updatedNode.Valid(), "updated node should be valid")

View File

@@ -13,7 +13,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"tailscale.com/types/key"
"tailscale.com/types/ptr"
)
func TestSnapshotFromNodes(t *testing.T) {
@@ -175,7 +174,7 @@ func createTestNode(nodeID types.NodeID, userID uint, username, hostname string)
DiscoKey: discoKey.Public(),
Hostname: hostname,
GivenName: hostname,
UserID: ptr.To(userID),
UserID: new(userID),
User: &types.User{
Name: username,
DisplayName: username,
@@ -883,7 +882,7 @@ func createConcurrentTestNode(id types.NodeID, hostname string) types.Node {
Hostname: hostname,
MachineKey: machineKey.Public(),
NodeKey: nodeKey.Public(),
UserID: ptr.To(uint(1)),
UserID: new(uint(1)),
User: &types.User{
Name: "concurrent-test-user",
},

View File

@@ -27,10 +27,8 @@ import (
"github.com/rs/zerolog/log"
"golang.org/x/sync/errgroup"
"gorm.io/gorm"
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/types/ptr"
"tailscale.com/types/views"
zcache "zgo.at/zcache/v2"
)
@@ -135,7 +133,7 @@ func NewState(cfg *types.Config) (*State, error) {
// On startup, all nodes should be marked as offline until they reconnect
// This ensures we don't have stale online status from previous runs
for _, node := range nodes {
node.IsOnline = ptr.To(false)
node.IsOnline = new(false)
}
users, err := db.ListUsers()
@@ -479,7 +477,7 @@ func (s *State) Connect(id types.NodeID) []change.Change {
// the NodeStore already reflects the correct online status for full map generation.
// now := time.Now()
node, ok := s.nodeStore.UpdateNode(id, func(n *types.Node) {
n.IsOnline = ptr.To(true)
n.IsOnline = new(true)
// n.LastSeen = ptr.To(now)
})
if !ok {
@@ -507,9 +505,9 @@ func (s *State) Disconnect(id types.NodeID) ([]change.Change, error) {
now := time.Now()
node, ok := s.nodeStore.UpdateNode(id, func(n *types.Node) {
n.LastSeen = ptr.To(now)
n.LastSeen = new(now)
// NodeStore is the source of truth for all node state including online status.
n.IsOnline = ptr.To(false)
n.IsOnline = new(false)
})
if !ok {
@@ -805,7 +803,7 @@ func (s *State) BackfillNodeIPs() ([]string, error) {
// Preserve online status and NetInfo when refreshing from database
existingNode, exists := s.nodeStore.GetNode(node.ID)
if exists && existingNode.Valid() {
node.IsOnline = ptr.To(existingNode.IsOnline().Get())
node.IsOnline = new(existingNode.IsOnline().Get())
// TODO(kradalby): We should ensure we use the same hostinfo and node merge semantics
// when a node re-registers as we do when it sends a map request (UpdateNodeFromMapRequest).
@@ -1183,8 +1181,8 @@ func (s *State) applyAuthNodeUpdate(params authNodeUpdateParams) (types.NodeView
)
node.Endpoints = params.RegEntry.Node.Endpoints
node.IsOnline = ptr.To(false)
node.LastSeen = ptr.To(time.Now())
node.IsOnline = new(false)
node.LastSeen = new(time.Now())
// Set RegisterMethod - for conversion this is the new method,
// for reauth we preserve the existing one from regEntry
@@ -1286,7 +1284,7 @@ func (s *State) createAndSaveNewNode(params newNodeParams) (types.NodeView, erro
DiscoKey: params.DiscoKey,
Hostinfo: params.Hostinfo,
Endpoints: params.Endpoints,
LastSeen: ptr.To(time.Now()),
LastSeen: new(time.Now()),
RegisterMethod: params.RegisterMethod,
Expiry: params.Expiry,
}
@@ -1822,8 +1820,8 @@ func (s *State) HandleNodeFromPreAuthKey(
// Only update AuthKey reference
node.AuthKey = pak
node.AuthKeyID = &pak.ID
node.IsOnline = ptr.To(false)
node.LastSeen = ptr.To(time.Now())
node.IsOnline = new(false)
node.LastSeen = new(time.Now())
// Tagged nodes keep their existing expiry (disabled).
// User-owned nodes update expiry from the client request.
@@ -2281,8 +2279,8 @@ func routesChanged(oldNode types.NodeView, newHI *tailcfg.Hostinfo) bool {
newRoutes = []netip.Prefix{}
}
tsaddr.SortPrefixes(oldRoutes)
tsaddr.SortPrefixes(newRoutes)
slices.SortFunc(oldRoutes, netip.Prefix.Compare)
slices.SortFunc(newRoutes, netip.Prefix.Compare)
return !slices.Equal(oldRoutes, newRoutes)
}

View File

@@ -333,7 +333,7 @@ func NodeOnline(nodeID types.NodeID) Change {
PeerPatches: []*tailcfg.PeerChange{
{
NodeID: nodeID.NodeID(),
Online: ptrTo(true),
Online: new(true),
},
},
}
@@ -346,7 +346,7 @@ func NodeOffline(nodeID types.NodeID) Change {
PeerPatches: []*tailcfg.PeerChange{
{
NodeID: nodeID.NodeID(),
Online: ptrTo(false),
Online: new(false),
},
},
}
@@ -367,7 +367,7 @@ func KeyExpiry(nodeID types.NodeID, expiry *time.Time) Change {
// ptrTo returns a pointer to the given value.
func ptrTo[T any](v T) *T {
return &v
return new(v)
}
// High-level change constructors

View File

@@ -16,8 +16,8 @@ func TestChange_FieldSync(t *testing.T) {
typ := reflect.TypeFor[Change]()
boolCount := 0
for i := range typ.NumField() {
if typ.Field(i).Type.Kind() == reflect.Bool {
for field := range typ.Fields() {
if field.Type.Kind() == reflect.Bool {
boolCount++
}
}

View File

@@ -407,8 +407,7 @@ func LoadConfig(path string, isFile bool) error {
err := viper.ReadInConfig()
if err != nil {
var configFileNotFoundError viper.ConfigFileNotFoundError
if errors.As(err, &configFileNotFoundError) {
if _, ok := errors.AsType[viper.ConfigFileNotFoundError](err); ok {
log.Warn().Msg("no config file found, using defaults")
return nil
}

View File

@@ -1104,7 +1104,7 @@ func (nv NodeView) TailNode(
primaryRoutes := primaryRouteFunc(nv.ID())
allowedIPs := slices.Concat(nv.Prefixes(), primaryRoutes, nv.ExitRoutes())
tsaddr.SortPrefixes(allowedIPs)
slices.SortFunc(allowedIPs, netip.Prefix.Compare)
capMap := tailcfg.NodeCapMap{
tailcfg.CapabilityAdmin: []tailcfg.RawMessage{},

View File

@@ -6,7 +6,6 @@ import (
"github.com/juanfont/headscale/hscontrol/util"
"github.com/stretchr/testify/assert"
"gorm.io/gorm"
"tailscale.com/types/ptr"
)
// TestNodeIsTagged tests the IsTagged() method for determining if a node is tagged.
@@ -69,7 +68,7 @@ func TestNodeIsTagged(t *testing.T) {
{
name: "node with user and no tags - not tagged",
node: Node{
UserID: ptr.To(uint(42)),
UserID: new(uint(42)),
Tags: []string{},
},
want: false,
@@ -112,7 +111,7 @@ func TestNodeViewIsTagged(t *testing.T) {
{
name: "user-owned node",
node: Node{
UserID: ptr.To(uint(1)),
UserID: new(uint(1)),
},
want: false,
},
@@ -223,7 +222,7 @@ func TestNodeTagsImmutableAfterRegistration(t *testing.T) {
// Test that a user-owned node is not tagged
userNode := Node{
ID: 2,
UserID: ptr.To(uint(42)),
UserID: new(uint(42)),
Tags: []string{},
RegisterMethod: util.RegisterMethodOIDC,
}
@@ -243,7 +242,7 @@ func TestNodeOwnershipModel(t *testing.T) {
name: "tagged node has tags, UserID is informational",
node: Node{
ID: 1,
UserID: ptr.To(uint(5)), // "created by" user 5
UserID: new(uint(5)), // "created by" user 5
Tags: []string{"tag:server"},
},
wantIsTagged: true,
@@ -253,7 +252,7 @@ func TestNodeOwnershipModel(t *testing.T) {
name: "user-owned node has no tags",
node: Node{
ID: 2,
UserID: ptr.To(uint(5)),
UserID: new(uint(5)),
Tags: []string{},
},
wantIsTagged: false,
@@ -265,7 +264,7 @@ func TestNodeOwnershipModel(t *testing.T) {
name: "node with only authkey tags - not tagged (tags should be copied)",
node: Node{
ID: 3,
UserID: ptr.To(uint(5)), // "created by" user 5
UserID: new(uint(5)), // "created by" user 5
AuthKey: &PreAuthKey{
Tags: []string{"tag:database"},
},

View File

@@ -110,9 +110,7 @@ func TestCanUsePreAuthKey(t *testing.T) {
if err == nil {
t.Errorf("expected error but got none")
} else {
var httpErr PAKError
ok := errors.As(err, &httpErr)
httpErr, ok := errors.AsType[PAKError](err)
if !ok {
t.Errorf("expected HTTPError but got %T", err)
} else {

View File

@@ -10,7 +10,7 @@ import (
)
var PrefixComparer = cmp.Comparer(func(x, y netip.Prefix) bool {
return x == y
return x.Compare(y) == 0
})
var IPComparer = cmp.Comparer(func(x, y netip.Addr) bool {

View File

@@ -20,7 +20,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
var veryLargeDestination = []policyv2.AliasWithPorts{
@@ -1284,9 +1283,9 @@ func TestACLAutogroupMember(t *testing.T) {
ACLs: []policyv2.ACL{
{
Action: "accept",
Sources: []policyv2.Alias{ptr.To(policyv2.AutoGroupMember)},
Sources: []policyv2.Alias{new(policyv2.AutoGroupMember)},
Destinations: []policyv2.AliasWithPorts{
aliasWithPorts(ptr.To(policyv2.AutoGroupMember), tailcfg.PortRangeAny),
aliasWithPorts(new(policyv2.AutoGroupMember), tailcfg.PortRangeAny),
},
},
},
@@ -1372,9 +1371,9 @@ func TestACLAutogroupTagged(t *testing.T) {
ACLs: []policyv2.ACL{
{
Action: "accept",
Sources: []policyv2.Alias{ptr.To(policyv2.AutoGroupTagged)},
Sources: []policyv2.Alias{new(policyv2.AutoGroupTagged)},
Destinations: []policyv2.AliasWithPorts{
aliasWithPorts(ptr.To(policyv2.AutoGroupTagged), tailcfg.PortRangeAny),
aliasWithPorts(new(policyv2.AutoGroupTagged), tailcfg.PortRangeAny),
},
},
},
@@ -1657,9 +1656,9 @@ func TestACLAutogroupSelf(t *testing.T) {
ACLs: []policyv2.ACL{
{
Action: "accept",
Sources: []policyv2.Alias{ptr.To(policyv2.AutoGroupMember)},
Sources: []policyv2.Alias{new(policyv2.AutoGroupMember)},
Destinations: []policyv2.AliasWithPorts{
aliasWithPorts(ptr.To(policyv2.AutoGroupSelf), tailcfg.PortRangeAny),
aliasWithPorts(new(policyv2.AutoGroupSelf), tailcfg.PortRangeAny),
},
},
{
@@ -1957,9 +1956,9 @@ func TestACLPolicyPropagationOverTime(t *testing.T) {
ACLs: []policyv2.ACL{
{
Action: "accept",
Sources: []policyv2.Alias{ptr.To(policyv2.AutoGroupMember)},
Sources: []policyv2.Alias{new(policyv2.AutoGroupMember)},
Destinations: []policyv2.AliasWithPorts{
aliasWithPorts(ptr.To(policyv2.AutoGroupSelf), tailcfg.PortRangeAny),
aliasWithPorts(new(policyv2.AutoGroupSelf), tailcfg.PortRangeAny),
},
},
},

View File

@@ -17,7 +17,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
func TestAuthKeyLogoutAndReloginSameUser(t *testing.T) {
@@ -634,7 +633,7 @@ func TestAuthKeyLogoutAndReloginRoutesPreserved(t *testing.T) {
},
AutoApprovers: policyv2.AutoApproverPolicy{
Routes: map[netip.Prefix]policyv2.AutoApprovers{
netip.MustParsePrefix(advertiseRoute): {ptr.To(policyv2.Username(user + "@test.no"))},
netip.MustParsePrefix(advertiseRoute): {new(policyv2.Username(user + "@test.no"))},
},
},
},

View File

@@ -27,7 +27,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
const (
@@ -906,32 +905,32 @@ func wildcard() policyv2.Alias {
// usernamep returns a pointer to a Username as an Alias for policy v2 configurations.
// Used in ACL rules to reference specific users in network access policies.
func usernamep(name string) policyv2.Alias {
return ptr.To(policyv2.Username(name))
return new(policyv2.Username(name))
}
// hostp returns a pointer to a Host as an Alias for policy v2 configurations.
// Used in ACL rules to reference specific hosts in network access policies.
func hostp(name string) policyv2.Alias {
return ptr.To(policyv2.Host(name))
return new(policyv2.Host(name))
}
// groupp returns a pointer to a Group as an Alias for policy v2 configurations.
// Used in ACL rules to reference user groups in network access policies.
func groupp(name string) policyv2.Alias {
return ptr.To(policyv2.Group(name))
return new(policyv2.Group(name))
}
// tagp returns a pointer to a Tag as an Alias for policy v2 configurations.
// Used in ACL rules to reference node tags in network access policies.
func tagp(name string) policyv2.Alias {
return ptr.To(policyv2.Tag(name))
return new(policyv2.Tag(name))
}
// prefixp returns a pointer to a Prefix from a CIDR string for policy v2 configurations.
// Converts CIDR notation to policy prefix format for network range specifications.
func prefixp(cidr string) policyv2.Alias {
prefix := netip.MustParsePrefix(cidr)
return ptr.To(policyv2.Prefix(prefix))
return new(policyv2.Prefix(prefix))
}
// aliasWithPorts creates an AliasWithPorts structure from an alias and port ranges.
@@ -947,7 +946,7 @@ func aliasWithPorts(alias policyv2.Alias, ports ...tailcfg.PortRange) policyv2.A
// usernameOwner returns a Username as an Owner for use in TagOwners policies.
// Specifies which users can assign and manage specific tags in ACL configurations.
func usernameOwner(name string) policyv2.Owner {
return ptr.To(policyv2.Username(name))
return new(policyv2.Username(name))
}
// groupOwner returns a Group as an Owner for use in TagOwners policies.
@@ -955,25 +954,25 @@ func usernameOwner(name string) policyv2.Owner {
//
//nolint:unused
func groupOwner(name string) policyv2.Owner {
return ptr.To(policyv2.Group(name))
return new(policyv2.Group(name))
}
// usernameApprover returns a Username as an AutoApprover for subnet route policies.
// Specifies which users can automatically approve subnet route advertisements.
func usernameApprover(name string) policyv2.AutoApprover {
return ptr.To(policyv2.Username(name))
return new(policyv2.Username(name))
}
// groupApprover returns a Group as an AutoApprover for subnet route policies.
// Specifies which groups can automatically approve subnet route advertisements.
func groupApprover(name string) policyv2.AutoApprover {
return ptr.To(policyv2.Group(name))
return new(policyv2.Group(name))
}
// tagApprover returns a Tag as an AutoApprover for subnet route policies.
// Specifies which tagged nodes can automatically approve subnet route advertisements.
func tagApprover(name string) policyv2.AutoApprover {
return ptr.To(policyv2.Tag(name))
return new(policyv2.Tag(name))
}
// oidcMockUser creates a MockUser for OIDC authentication testing.

View File

@@ -13,7 +13,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
func isSSHNoAccessStdError(stderr string) bool {
@@ -85,8 +84,8 @@ func TestSSHOneUserToAll(t *testing.T) {
// Use autogroup:member and autogroup:tagged instead of wildcard
// since wildcard (*) is no longer supported for SSH destinations
Destinations: policyv2.SSHDstAliases{
ptr.To(policyv2.AutoGroupMember),
ptr.To(policyv2.AutoGroupTagged),
new(policyv2.AutoGroupMember),
new(policyv2.AutoGroupTagged),
},
Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")},
},
@@ -160,7 +159,7 @@ func TestSSHMultipleUsersAllToAll(t *testing.T) {
// Username destinations (e.g., "user1@") now require the source
// to be that exact same user only. For group-to-group SSH access,
// use autogroup:self instead.
Destinations: policyv2.SSHDstAliases{ptr.To(policyv2.AutoGroupSelf)},
Destinations: policyv2.SSHDstAliases{new(policyv2.AutoGroupSelf)},
Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")},
},
},
@@ -285,7 +284,7 @@ func TestSSHIsBlockedInACL(t *testing.T) {
{
Action: "accept",
Sources: policyv2.SSHSrcAliases{groupp("group:integration-test")},
Destinations: policyv2.SSHDstAliases{ptr.To(policyv2.AutoGroupSelf)},
Destinations: policyv2.SSHDstAliases{new(policyv2.AutoGroupSelf)},
Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")},
},
},
@@ -340,13 +339,13 @@ func TestSSHUserOnlyIsolation(t *testing.T) {
{
Action: "accept",
Sources: policyv2.SSHSrcAliases{groupp("group:ssh1")},
Destinations: policyv2.SSHDstAliases{ptr.To(policyv2.AutoGroupSelf)},
Destinations: policyv2.SSHDstAliases{new(policyv2.AutoGroupSelf)},
Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")},
},
{
Action: "accept",
Sources: policyv2.SSHSrcAliases{groupp("group:ssh2")},
Destinations: policyv2.SSHDstAliases{ptr.To(policyv2.AutoGroupSelf)},
Destinations: policyv2.SSHDstAliases{new(policyv2.AutoGroupSelf)},
Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")},
},
},
@@ -522,10 +521,10 @@ func TestSSHAutogroupSelf(t *testing.T) {
{
Action: "accept",
Sources: policyv2.SSHSrcAliases{
ptr.To(policyv2.AutoGroupMember),
new(policyv2.AutoGroupMember),
},
Destinations: policyv2.SSHDstAliases{
ptr.To(policyv2.AutoGroupSelf),
new(policyv2.AutoGroupSelf),
},
Users: []policyv2.SSHUser{policyv2.SSHUser("ssh-it-user")},
},

View File

@@ -13,7 +13,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
const tagTestUser = "taguser"
@@ -30,9 +29,9 @@ const tagTestUser = "taguser"
func tagsTestPolicy() *policyv2.Policy {
return &policyv2.Policy{
TagOwners: policyv2.TagOwners{
"tag:valid-owned": policyv2.Owners{ptr.To(policyv2.Username(tagTestUser + "@"))},
"tag:second": policyv2.Owners{ptr.To(policyv2.Username(tagTestUser + "@"))},
"tag:valid-unowned": policyv2.Owners{ptr.To(policyv2.Username("other-user@"))},
"tag:valid-owned": policyv2.Owners{new(policyv2.Username(tagTestUser + "@"))},
"tag:second": policyv2.Owners{new(policyv2.Username(tagTestUser + "@"))},
"tag:valid-unowned": policyv2.Owners{new(policyv2.Username("other-user@"))},
// Note: tag:nonexistent deliberately NOT defined
},
ACLs: []policyv2.ACL{