From d3005b1d4f62c3d7d2bf27e92ef11d59d55a9a4f Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 21 May 2025 17:00:28 +0200 Subject: [PATCH] types/node: make user pointer Signed-off-by: Kristoffer Dalby --- hscontrol/auth.go | 10 ++++---- hscontrol/db/ip_test.go | 20 +++++++-------- hscontrol/db/node.go | 13 +++++----- hscontrol/db/node_test.go | 42 +++++++++++++++---------------- hscontrol/db/preauth_keys_test.go | 2 +- hscontrol/db/users.go | 5 ++-- hscontrol/db/users_test.go | 10 ++++---- hscontrol/grpcv1.go | 4 +-- hscontrol/mapper/mapper.go | 19 ++++++++++---- hscontrol/mapper/mapper_test.go | 42 +++++++++++++++---------------- hscontrol/mapper/tail.go | 21 +++++++--------- hscontrol/mapper/tail_test.go | 16 +++++------- hscontrol/types/node.go | 4 ++- hscontrol/types/node_test.go | 11 ++++---- hscontrol/types/users.go | 12 ++++++++- 15 files changed, 123 insertions(+), 108 deletions(-) diff --git a/hscontrol/auth.go b/hscontrol/auth.go index 941b51b2..49964173 100644 --- a/hscontrol/auth.go +++ b/hscontrol/auth.go @@ -193,8 +193,8 @@ func (h *Headscale) handleRegisterWithAuthKey( nodeToRegister := types.Node{ Hostname: regReq.Hostinfo.Hostname, - UserID: pak.User.ID, - User: pak.User, + UserID: ptr.To(pak.User.ID), + User: ptr.To(pak.User), MachineKey: machineKey, NodeKey: regReq.NodeKey, Hostinfo: regReq.Hostinfo, @@ -204,9 +204,9 @@ func (h *Headscale) handleRegisterWithAuthKey( // TODO(kradalby): This should not be set on the node, // they should be looked up through the key, which is // attached to the node. - ForcedTags: pak.Proto().GetAclTags(), - AuthKey: pak, - AuthKeyID: &pak.ID, + Tags: pak.Proto().GetAclTags(), + AuthKey: pak, + AuthKeyID: &pak.ID, } if !regReq.Expiry.IsZero() { diff --git a/hscontrol/db/ip_test.go b/hscontrol/db/ip_test.go index f558cdf7..73115876 100644 --- a/hscontrol/db/ip_test.go +++ b/hscontrol/db/ip_test.go @@ -96,7 +96,7 @@ func TestIPAllocatorSequential(t *testing.T) { db.DB.Save(&user) db.DB.Save(&types.Node{ - User: user, + User: &user, IPv4: nap("100.64.0.1"), IPv6: nap("fd7a:115c:a1e0::1"), }) @@ -124,7 +124,7 @@ func TestIPAllocatorSequential(t *testing.T) { db.DB.Save(&user) db.DB.Save(&types.Node{ - User: user, + User: &user, IPv4: nap("100.64.0.2"), IPv6: nap("fd7a:115c:a1e0::2"), }) @@ -314,7 +314,7 @@ func TestBackfillIPAddresses(t *testing.T) { db.DB.Save(&user) db.DB.Save(&types.Node{ - User: user, + User: &user, IPv4: nap("100.64.0.1"), }) @@ -339,7 +339,7 @@ func TestBackfillIPAddresses(t *testing.T) { db.DB.Save(&user) db.DB.Save(&types.Node{ - User: user, + User: &user, IPv6: nap("fd7a:115c:a1e0::1"), }) @@ -364,7 +364,7 @@ func TestBackfillIPAddresses(t *testing.T) { db.DB.Save(&user) db.DB.Save(&types.Node{ - User: user, + User: &user, IPv4: nap("100.64.0.1"), IPv6: nap("fd7a:115c:a1e0::1"), }) @@ -388,7 +388,7 @@ func TestBackfillIPAddresses(t *testing.T) { db.DB.Save(&user) db.DB.Save(&types.Node{ - User: user, + User: &user, IPv4: nap("100.64.0.1"), IPv6: nap("fd7a:115c:a1e0::1"), }) @@ -412,19 +412,19 @@ func TestBackfillIPAddresses(t *testing.T) { db.DB.Save(&user) db.DB.Save(&types.Node{ - User: user, + User: &user, IPv4: nap("100.64.0.1"), }) db.DB.Save(&types.Node{ - User: user, + User: &user, IPv4: nap("100.64.0.2"), }) db.DB.Save(&types.Node{ - User: user, + User: &user, IPv4: nap("100.64.0.3"), }) db.DB.Save(&types.Node{ - User: user, + User: &user, IPv4: nap("100.64.0.4"), }) diff --git a/hscontrol/db/node.go b/hscontrol/db/node.go index c91687da..9b372f50 100644 --- a/hscontrol/db/node.go +++ b/hscontrol/db/node.go @@ -203,7 +203,7 @@ func SetTags( ) error { if len(tags) == 0 { // if no tags are provided, we remove all forced tags - if err := tx.Model(&types.Node{}).Where("id = ?", nodeID).Update("forced_tags", "[]").Error; err != nil { + if err := tx.Model(&types.Node{}).Where("id = ?", nodeID).Update("tags", "[]").Error; err != nil { return fmt.Errorf("removing tags: %w", err) } @@ -217,7 +217,7 @@ func SetTags( return err } - if err := tx.Model(&types.Node{}).Where("id = ?", nodeID).Update("forced_tags", string(b)).Error; err != nil { + if err := tx.Model(&types.Node{}).Where("id = ?", nodeID).Update("tags", string(b)).Error; err != nil { return fmt.Errorf("updating tags: %w", err) } @@ -376,12 +376,13 @@ func (hsdb *HSDatabase) HandleNodeFromAuthPath( // Why not always? // Registration of expired node with different user if reg.Node.ID != 0 && - reg.Node.UserID != user.ID { + reg.Node.UserID != nil && + *reg.Node.UserID != user.ID { return nil, ErrDifferentRegisteredUser } - reg.Node.UserID = user.ID - reg.Node.User = *user + reg.Node.UserID = &user.ID + reg.Node.User = user reg.Node.RegisterMethod = registrationMethod if nodeExpiry != nil { @@ -435,7 +436,6 @@ func RegisterNode(tx *gorm.DB, node types.Node, ipv4 *netip.Addr, ipv6 *netip.Ad Str("node", node.Hostname). Str("machine_key", node.MachineKey.ShortString()). Str("node_key", node.NodeKey.ShortString()). - Str("user", node.User.Username()). Msg("Registering node") // If the a new node is registered with the same machine key, to the same user, @@ -463,7 +463,6 @@ func RegisterNode(tx *gorm.DB, node types.Node, ipv4 *netip.Addr, ipv6 *netip.Ad Str("node", node.Hostname). Str("machine_key", node.MachineKey.ShortString()). Str("node_key", node.NodeKey.ShortString()). - Str("user", node.User.Username()). Msg("Node authorized again") return &node, nil diff --git a/hscontrol/db/node_test.go b/hscontrol/db/node_test.go index 56c967f1..4f4871d0 100644 --- a/hscontrol/db/node_test.go +++ b/hscontrol/db/node_test.go @@ -43,7 +43,7 @@ func (s *Suite) TestGetNode(c *check.C) { MachineKey: machineKey.Public(), NodeKey: nodeKey.Public(), Hostname: "testnode", - UserID: user.ID, + UserID: &user.ID, RegisterMethod: util.RegisterMethodAuthKey, AuthKeyID: ptr.To(pak.ID), } @@ -72,7 +72,7 @@ func (s *Suite) TestGetNodeByID(c *check.C) { MachineKey: machineKey.Public(), NodeKey: nodeKey.Public(), Hostname: "testnode", - UserID: user.ID, + UserID: &user.ID, RegisterMethod: util.RegisterMethodAuthKey, AuthKeyID: ptr.To(pak.ID), } @@ -95,7 +95,7 @@ func (s *Suite) TestHardDeleteNode(c *check.C) { MachineKey: machineKey.Public(), NodeKey: nodeKey.Public(), Hostname: "testnode3", - UserID: user.ID, + UserID: &user.ID, RegisterMethod: util.RegisterMethodAuthKey, } trx := db.DB.Save(&node) @@ -127,7 +127,7 @@ func (s *Suite) TestListPeers(c *check.C) { MachineKey: machineKey.Public(), NodeKey: nodeKey.Public(), Hostname: "testnode" + strconv.Itoa(index), - UserID: user.ID, + UserID: &user.ID, RegisterMethod: util.RegisterMethodAuthKey, AuthKeyID: ptr.To(pak.ID), } @@ -165,7 +165,7 @@ func (s *Suite) TestExpireNode(c *check.C) { MachineKey: machineKey.Public(), NodeKey: nodeKey.Public(), Hostname: "testnode", - UserID: user.ID, + UserID: &user.ID, RegisterMethod: util.RegisterMethodAuthKey, AuthKeyID: ptr.To(pak.ID), Expiry: &time.Time{}, @@ -206,7 +206,7 @@ func (s *Suite) TestSetTags(c *check.C) { MachineKey: machineKey.Public(), NodeKey: nodeKey.Public(), Hostname: "testnode", - UserID: user.ID, + UserID: &user.ID, RegisterMethod: util.RegisterMethodAuthKey, AuthKeyID: ptr.To(pak.ID), } @@ -220,7 +220,7 @@ func (s *Suite) TestSetTags(c *check.C) { c.Assert(err, check.IsNil) node, err = db.getNode(types.UserID(user.ID), "testnode") c.Assert(err, check.IsNil) - c.Assert(node.ForcedTags, check.DeepEquals, sTags) + c.Assert(node.Tags, check.DeepEquals, sTags) // assign duplicate tags, expect no errors but no doubles in DB eTags := []string{"tag:bar", "tag:test", "tag:unknown", "tag:test"} @@ -229,7 +229,7 @@ func (s *Suite) TestSetTags(c *check.C) { node, err = db.getNode(types.UserID(user.ID), "testnode") c.Assert(err, check.IsNil) c.Assert( - node.ForcedTags, + node.Tags, check.DeepEquals, []string{"tag:bar", "tag:test", "tag:unknown"}, ) @@ -239,7 +239,7 @@ func (s *Suite) TestSetTags(c *check.C) { c.Assert(err, check.IsNil) node, err = db.getNode(types.UserID(user.ID), "testnode") c.Assert(err, check.IsNil) - c.Assert(node.ForcedTags, check.DeepEquals, []string{}) + c.Assert(node.Tags, check.DeepEquals, []string{}) } func TestHeadscale_generateGivenName(t *testing.T) { @@ -451,7 +451,7 @@ func TestAutoApproveRoutes(t *testing.T) { MachineKey: key.NewMachine().Public(), NodeKey: key.NewNode().Public(), Hostname: "testnode", - UserID: user.ID, + UserID: &user.ID, RegisterMethod: util.RegisterMethodAuthKey, Hostinfo: &tailcfg.Hostinfo{ RoutableIPs: tt.routes, @@ -467,13 +467,13 @@ func TestAutoApproveRoutes(t *testing.T) { MachineKey: key.NewMachine().Public(), NodeKey: key.NewNode().Public(), Hostname: "taggednode", - UserID: taggedUser.ID, + UserID: &taggedUser.ID, RegisterMethod: util.RegisterMethodAuthKey, Hostinfo: &tailcfg.Hostinfo{ RoutableIPs: tt.routes, }, - ForcedTags: []string{"tag:exit"}, - IPv4: ptr.To(netip.MustParseAddr("100.64.0.2")), + Tags: []string{"tag:exit"}, + IPv4: ptr.To(netip.MustParseAddr("100.64.0.2")), } err = adb.DB.Save(&nodeTagged).Error @@ -612,7 +612,7 @@ func TestListEphemeralNodes(t *testing.T) { MachineKey: key.NewMachine().Public(), NodeKey: key.NewNode().Public(), Hostname: "test", - UserID: user.ID, + UserID: &user.ID, RegisterMethod: util.RegisterMethodAuthKey, AuthKeyID: ptr.To(pak.ID), } @@ -622,7 +622,7 @@ func TestListEphemeralNodes(t *testing.T) { MachineKey: key.NewMachine().Public(), NodeKey: key.NewNode().Public(), Hostname: "ephemeral", - UserID: user.ID, + UserID: &user.ID, RegisterMethod: util.RegisterMethodAuthKey, AuthKeyID: ptr.To(pakEph.ID), } @@ -665,7 +665,7 @@ func TestRenameNode(t *testing.T) { MachineKey: key.NewMachine().Public(), NodeKey: key.NewNode().Public(), Hostname: "test", - UserID: user.ID, + UserID: &user.ID, RegisterMethod: util.RegisterMethodAuthKey, Hostinfo: &tailcfg.Hostinfo{}, } @@ -675,7 +675,7 @@ func TestRenameNode(t *testing.T) { MachineKey: key.NewMachine().Public(), NodeKey: key.NewNode().Public(), Hostname: "test", - UserID: user2.ID, + UserID: &user2.ID, RegisterMethod: util.RegisterMethodAuthKey, Hostinfo: &tailcfg.Hostinfo{}, } @@ -765,7 +765,7 @@ func TestListPeers(t *testing.T) { MachineKey: key.NewMachine().Public(), NodeKey: key.NewNode().Public(), Hostname: "test1", - UserID: user.ID, + UserID: &user.ID, RegisterMethod: util.RegisterMethodAuthKey, Hostinfo: &tailcfg.Hostinfo{}, } @@ -775,7 +775,7 @@ func TestListPeers(t *testing.T) { MachineKey: key.NewMachine().Public(), NodeKey: key.NewNode().Public(), Hostname: "test2", - UserID: user2.ID, + UserID: &user2.ID, RegisterMethod: util.RegisterMethodAuthKey, Hostinfo: &tailcfg.Hostinfo{}, } @@ -849,7 +849,7 @@ func TestListNodes(t *testing.T) { MachineKey: key.NewMachine().Public(), NodeKey: key.NewNode().Public(), Hostname: "test1", - UserID: user.ID, + UserID: &user.ID, RegisterMethod: util.RegisterMethodAuthKey, Hostinfo: &tailcfg.Hostinfo{}, } @@ -859,7 +859,7 @@ func TestListNodes(t *testing.T) { MachineKey: key.NewMachine().Public(), NodeKey: key.NewNode().Public(), Hostname: "test2", - UserID: user2.ID, + UserID: &user2.ID, RegisterMethod: util.RegisterMethodAuthKey, Hostinfo: &tailcfg.Hostinfo{}, } diff --git a/hscontrol/db/preauth_keys_test.go b/hscontrol/db/preauth_keys_test.go index 5ace968a..a90cf41f 100644 --- a/hscontrol/db/preauth_keys_test.go +++ b/hscontrol/db/preauth_keys_test.go @@ -74,7 +74,7 @@ func TestCannotDeleteAssignedPreAuthKey(t *testing.T) { node := types.Node{ ID: 0, Hostname: "testest", - UserID: user.ID, + UserID: &user.ID, RegisterMethod: util.RegisterMethodAuthKey, AuthKeyID: ptr.To(key.ID), } diff --git a/hscontrol/db/users.go b/hscontrol/db/users.go index d7f31e5b..cdc6fc32 100644 --- a/hscontrol/db/users.go +++ b/hscontrol/db/users.go @@ -7,6 +7,7 @@ import ( "github.com/juanfont/headscale/hscontrol/types" "github.com/juanfont/headscale/hscontrol/util" "gorm.io/gorm" + "tailscale.com/types/ptr" ) var ( @@ -192,7 +193,7 @@ func (hsdb *HSDatabase) GetUserByName(name string) (*types.User, error) { // ListNodesByUser gets all the nodes in a given user. func ListNodesByUser(tx *gorm.DB, uid types.UserID) (types.Nodes, error) { nodes := types.Nodes{} - if err := tx.Preload("AuthKey").Preload("AuthKey.User").Preload("User").Where(&types.Node{UserID: uint(uid)}).Find(&nodes).Error; err != nil { + if err := tx.Preload("AuthKey").Preload("AuthKey.User").Preload("User").Where(&types.Node{UserID: ptr.To(uint(uid))}).Find(&nodes).Error; err != nil { return nil, err } @@ -211,7 +212,7 @@ func AssignNodeToUser(tx *gorm.DB, node *types.Node, uid types.UserID) error { if err != nil { return err } - node.User = *user + node.User = user if result := tx.Save(&node); result.Error != nil { return result.Error } diff --git a/hscontrol/db/users_test.go b/hscontrol/db/users_test.go index 6cec2d5a..6bbb77c0 100644 --- a/hscontrol/db/users_test.go +++ b/hscontrol/db/users_test.go @@ -52,7 +52,7 @@ func (s *Suite) TestDestroyUserErrors(c *check.C) { node := types.Node{ ID: 0, Hostname: "testnode", - UserID: user.ID, + UserID: &user.ID, RegisterMethod: util.RegisterMethodAuthKey, AuthKeyID: ptr.To(pak.ID), } @@ -110,17 +110,17 @@ func (s *Suite) TestSetMachineUser(c *check.C) { node := types.Node{ ID: 0, Hostname: "testnode", - UserID: oldUser.ID, + UserID: &oldUser.ID, RegisterMethod: util.RegisterMethodAuthKey, AuthKeyID: ptr.To(pak.ID), } trx := db.DB.Save(&node) c.Assert(trx.Error, check.IsNil) - c.Assert(node.UserID, check.Equals, oldUser.ID) + c.Assert(*node.UserID, check.Equals, oldUser.ID) err = db.AssignNodeToUser(&node, types.UserID(newUser.ID)) c.Assert(err, check.IsNil) - c.Assert(node.UserID, check.Equals, newUser.ID) + c.Assert(*node.UserID, check.Equals, newUser.ID) c.Assert(node.User.Name, check.Equals, newUser.Name) err = db.AssignNodeToUser(&node, 9584849) @@ -128,6 +128,6 @@ func (s *Suite) TestSetMachineUser(c *check.C) { err = db.AssignNodeToUser(&node, types.UserID(newUser.ID)) c.Assert(err, check.IsNil) - c.Assert(node.UserID, check.Equals, newUser.ID) + c.Assert(*node.UserID, check.Equals, newUser.ID) c.Assert(node.User.Name, check.Equals, newUser.Name) } diff --git a/hscontrol/grpcv1.go b/hscontrol/grpcv1.go index 8b516c3e..fce992ba 100644 --- a/hscontrol/grpcv1.go +++ b/hscontrol/grpcv1.go @@ -552,7 +552,7 @@ func nodesToProto(polMan policy.PolicyManager, isLikelyConnected *xsync.MapOf[ty tags = append(tags, tag) } } - resp.ValidTags = lo.Uniq(append(tags, node.ForcedTags...)) + resp.ValidTags = lo.Uniq(append(tags, node.Tags...)) resp.SubnetRoutes = util.PrefixesToString(append(pr.PrimaryRoutes(node.ID), node.ExitRoutes()...)) response[index] = resp } @@ -819,7 +819,7 @@ func (api headscaleV1APIServer) DebugCreateNode( NodeKey: key.NewNode().Public(), MachineKey: key.NewMachine().Public(), Hostname: request.GetName(), - User: *user, + User: user, Expiry: &time.Time{}, LastSeen: &time.Time{}, diff --git a/hscontrol/mapper/mapper.go b/hscontrol/mapper/mapper.go index d7deb0a5..e84b7397 100644 --- a/hscontrol/mapper/mapper.go +++ b/hscontrol/mapper/mapper.go @@ -104,11 +104,16 @@ func generateUserProfiles( ) []tailcfg.UserProfile { userMap := make(map[uint]*types.User) ids := make([]uint, 0, len(userMap)) - userMap[node.User.ID] = &node.User - ids = append(ids, node.User.ID) - for _, peer := range peers { - userMap[peer.User.ID] = &peer.User - ids = append(ids, peer.User.ID) + var tagged bool + if node.IsUserOwned() { + userMap[node.User.ID] = node.User + ids = append(ids, node.User.ID) + for _, peer := range peers { + userMap[peer.User.ID] = peer.User + ids = append(ids, peer.User.ID) + } + } else { + tagged = true } slices.Sort(ids) @@ -120,6 +125,10 @@ func generateUserProfiles( } } + if tagged { + profiles = append(profiles, types.TaggedDevices.TailscaleUserProfile()) + } + return profiles } diff --git a/hscontrol/mapper/mapper_test.go b/hscontrol/mapper/mapper_test.go index 8d2c60bb..eb76c03a 100644 --- a/hscontrol/mapper/mapper_test.go +++ b/hscontrol/mapper/mapper_test.go @@ -53,8 +53,8 @@ func TestDNSConfigMapResponse(t *testing.T) { mach := func(hostname, username string, userid uint) *types.Node { return &types.Node{ Hostname: hostname, - UserID: userid, - User: types.User{ + UserID: &userid, + User: &types.User{ Name: username, }, } @@ -128,15 +128,15 @@ func Test_fullMapResponse(t *testing.T) { DiscoKey: mustDK( "discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", ), - IPv4: iap("100.64.0.1"), - Hostname: "mini", - GivenName: "mini", - UserID: user1.ID, - User: user1, - ForcedTags: []string{}, - AuthKey: &types.PreAuthKey{}, - LastSeen: &lastSeen, - Expiry: &expire, + IPv4: iap("100.64.0.1"), + Hostname: "mini", + GivenName: "mini", + UserID: &user1.ID, + User: &user1, + Tags: []string{}, + AuthKey: &types.PreAuthKey{}, + LastSeen: &lastSeen, + Expiry: &expire, Hostinfo: &tailcfg.Hostinfo{ RoutableIPs: []netip.Prefix{ tsaddr.AllIPv4(), @@ -205,16 +205,16 @@ func Test_fullMapResponse(t *testing.T) { DiscoKey: mustDK( "discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084", ), - IPv4: iap("100.64.0.2"), - Hostname: "peer1", - GivenName: "peer1", - UserID: user2.ID, - User: user2, - ForcedTags: []string{}, - LastSeen: &lastSeen, - Expiry: &expire, - Hostinfo: &tailcfg.Hostinfo{}, - CreatedAt: created, + IPv4: iap("100.64.0.2"), + Hostname: "peer1", + GivenName: "peer1", + UserID: &user2.ID, + User: &user2, + Tags: []string{}, + LastSeen: &lastSeen, + Expiry: &expire, + Hostinfo: &tailcfg.Hostinfo{}, + CreatedAt: created, } tailPeer1 := &tailcfg.Node{ diff --git a/hscontrol/mapper/tail.go b/hscontrol/mapper/tail.go index eae70e96..60e26ee1 100644 --- a/hscontrol/mapper/tail.go +++ b/hscontrol/mapper/tail.go @@ -6,7 +6,6 @@ import ( "github.com/juanfont/headscale/hscontrol/policy" "github.com/juanfont/headscale/hscontrol/types" - "github.com/samber/lo" "tailscale.com/net/tsaddr" "tailscale.com/tailcfg" ) @@ -72,14 +71,6 @@ func tailNode( return nil, fmt.Errorf("tailNode, failed to create FQDN: %s", err) } - var tags []string - for _, tag := range node.RequestTags() { - if polMan.NodeCanHaveTag(node, tag) { - tags = append(tags, tag) - } - } - tags = lo.Uniq(append(tags, node.ForcedTags...)) - routes := primaryRouteFunc(node.ID) allowed := append(node.Prefixes(), routes...) allowed = append(allowed, node.ExitRoutes()...) @@ -91,8 +82,6 @@ func tailNode( Name: hostname, Cap: capVer, - User: tailcfg.UserID(node.UserID), - Key: node.NodeKey, KeyExpiry: keyExpiry.UTC(), @@ -109,12 +98,20 @@ func tailNode( Online: node.IsOnline, - Tags: tags, + Tags: node.Tags, MachineAuthorized: !node.IsExpired(), Expired: node.IsExpired(), } + if node.IsUserOwned() { + tNode.User = tailcfg.UserID(*node.UserID) + } + + if node.IsTagged() { + tNode.User = tailcfg.UserID(types.TaggedDevices.ID) + } + tNode.CapMap = tailcfg.NodeCapMap{ tailcfg.CapabilityFileSharing: []tailcfg.RawMessage{}, tailcfg.CapabilityAdmin: []tailcfg.RawMessage{}, diff --git a/hscontrol/mapper/tail_test.go b/hscontrol/mapper/tail_test.go index cacc4930..9fc19c14 100644 --- a/hscontrol/mapper/tail_test.go +++ b/hscontrol/mapper/tail_test.go @@ -15,6 +15,7 @@ import ( "tailscale.com/net/tsaddr" "tailscale.com/tailcfg" "tailscale.com/types/key" + "tailscale.com/types/ptr" ) func TestTailNode(t *testing.T) { @@ -70,7 +71,6 @@ func TestTailNode(t *testing.T) { HomeDERP: 0, LegacyDERPString: "127.3.3.40:0", Hostinfo: hiview(tailcfg.Hostinfo{}), - Tags: []string{}, MachineAuthorized: true, CapMap: tailcfg.NodeCapMap{ @@ -97,14 +97,13 @@ func TestTailNode(t *testing.T) { IPv4: iap("100.64.0.1"), Hostname: "mini", GivenName: "mini", - UserID: 0, - User: types.User{ + UserID: ptr.To(uint(0)), + User: &types.User{ Name: "mini", }, - ForcedTags: []string{}, - AuthKey: &types.PreAuthKey{}, - LastSeen: &lastSeen, - Expiry: &expire, + AuthKey: &types.PreAuthKey{}, + LastSeen: &lastSeen, + Expiry: &expire, Hostinfo: &tailcfg.Hostinfo{ RoutableIPs: []netip.Prefix{ tsaddr.AllIPv4(), @@ -156,8 +155,6 @@ func TestTailNode(t *testing.T) { }), Created: created, - Tags: []string{}, - LastSeen: &lastSeen, MachineAuthorized: true, @@ -184,7 +181,6 @@ func TestTailNode(t *testing.T) { HomeDERP: 0, LegacyDERPString: "127.3.3.40:0", Hostinfo: hiview(tailcfg.Hostinfo{}), - Tags: []string{}, MachineAuthorized: true, CapMap: tailcfg.NodeCapMap{ diff --git a/hscontrol/types/node.go b/hscontrol/types/node.go index b7c8b2ce..91162f94 100644 --- a/hscontrol/types/node.go +++ b/hscontrol/types/node.go @@ -567,7 +567,9 @@ func (nodes Nodes) DebugString() string { func (node Node) DebugString() string { var sb strings.Builder fmt.Fprintf(&sb, "%s(%s):\n", node.Hostname, node.ID) - fmt.Fprintf(&sb, "\tUser: %s (%d, %q)\n", node.User.Display(), node.User.ID, node.User.Username()) + if node.IsUserOwned() { + fmt.Fprintf(&sb, "\tUser: %s (%d, %q)\n", node.User.Display(), node.User.ID, node.User.Username()) + } fmt.Fprintf(&sb, "\tTags: %v\n", node.Tags) fmt.Fprintf(&sb, "\tIPs: %v\n", node.IPs()) fmt.Fprintf(&sb, "\tApprovedRoutes: %v\n", node.ApprovedRoutes) diff --git a/hscontrol/types/node_test.go b/hscontrol/types/node_test.go index c7261587..12c3bdbb 100644 --- a/hscontrol/types/node_test.go +++ b/hscontrol/types/node_test.go @@ -2,11 +2,12 @@ package types import ( "fmt" - "github.com/juanfont/headscale/hscontrol/policy/matcher" "net/netip" "strings" "testing" + "github.com/juanfont/headscale/hscontrol/policy/matcher" + "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" v1 "github.com/juanfont/headscale/gen/go/headscale/v1" @@ -139,7 +140,7 @@ func TestNodeFQDN(t *testing.T) { name: "no-dnsconfig-with-username", node: Node{ GivenName: "test", - User: User{ + User: &User{ Name: "user", }, }, @@ -150,7 +151,7 @@ func TestNodeFQDN(t *testing.T) { name: "all-set", node: Node{ GivenName: "test", - User: User{ + User: &User{ Name: "user", }, }, @@ -160,7 +161,7 @@ func TestNodeFQDN(t *testing.T) { { name: "no-given-name", node: Node{ - User: User{ + User: &User{ Name: "user", }, }, @@ -179,7 +180,7 @@ func TestNodeFQDN(t *testing.T) { name: "no-dnsconfig", node: Node{ GivenName: "test", - User: User{ + User: &User{ Name: "user", }, }, diff --git a/hscontrol/types/users.go b/hscontrol/types/users.go index 6cd2c41a..c3982cbe 100644 --- a/hscontrol/types/users.go +++ b/hscontrol/types/users.go @@ -18,6 +18,16 @@ import ( "tailscale.com/tailcfg" ) +// TaggedDevices is a special user that is used to +// populate the tagged devices in the Tailscale MapResponse. +var TaggedDevices = User{ + // This ID is arbitrarily chosen, it is naively high to avoid + // and conflicts with other IDs. + Model: gorm.Model{ID: 2147455555}, + Name: "tagged-devices", + DisplayName: "Tagged Devices", +} + type UserID uint64 type Users []User @@ -273,7 +283,7 @@ func CleanIdentifier(identifier string) string { cleanParts = append(cleanParts, part) } } - + if len(cleanParts) == 0 { u.Path = "" } else {