1
0
mirror of https://github.com/juanfont/headscale.git synced 2025-06-24 01:16:14 +02:00

types/node: make user pointer

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
Kristoffer Dalby 2025-05-21 17:00:28 +02:00
parent b87bb35004
commit d3005b1d4f
No known key found for this signature in database
15 changed files with 123 additions and 108 deletions

View File

@ -193,8 +193,8 @@ func (h *Headscale) handleRegisterWithAuthKey(
nodeToRegister := types.Node{ nodeToRegister := types.Node{
Hostname: regReq.Hostinfo.Hostname, Hostname: regReq.Hostinfo.Hostname,
UserID: pak.User.ID, UserID: ptr.To(pak.User.ID),
User: pak.User, User: ptr.To(pak.User),
MachineKey: machineKey, MachineKey: machineKey,
NodeKey: regReq.NodeKey, NodeKey: regReq.NodeKey,
Hostinfo: regReq.Hostinfo, Hostinfo: regReq.Hostinfo,
@ -204,7 +204,7 @@ func (h *Headscale) handleRegisterWithAuthKey(
// TODO(kradalby): This should not be set on the node, // TODO(kradalby): This should not be set on the node,
// they should be looked up through the key, which is // they should be looked up through the key, which is
// attached to the node. // attached to the node.
ForcedTags: pak.Proto().GetAclTags(), Tags: pak.Proto().GetAclTags(),
AuthKey: pak, AuthKey: pak,
AuthKeyID: &pak.ID, AuthKeyID: &pak.ID,
} }

View File

@ -96,7 +96,7 @@ func TestIPAllocatorSequential(t *testing.T) {
db.DB.Save(&user) db.DB.Save(&user)
db.DB.Save(&types.Node{ db.DB.Save(&types.Node{
User: user, User: &user,
IPv4: nap("100.64.0.1"), IPv4: nap("100.64.0.1"),
IPv6: nap("fd7a:115c:a1e0::1"), IPv6: nap("fd7a:115c:a1e0::1"),
}) })
@ -124,7 +124,7 @@ func TestIPAllocatorSequential(t *testing.T) {
db.DB.Save(&user) db.DB.Save(&user)
db.DB.Save(&types.Node{ db.DB.Save(&types.Node{
User: user, User: &user,
IPv4: nap("100.64.0.2"), IPv4: nap("100.64.0.2"),
IPv6: nap("fd7a:115c:a1e0::2"), IPv6: nap("fd7a:115c:a1e0::2"),
}) })
@ -314,7 +314,7 @@ func TestBackfillIPAddresses(t *testing.T) {
db.DB.Save(&user) db.DB.Save(&user)
db.DB.Save(&types.Node{ db.DB.Save(&types.Node{
User: user, User: &user,
IPv4: nap("100.64.0.1"), IPv4: nap("100.64.0.1"),
}) })
@ -339,7 +339,7 @@ func TestBackfillIPAddresses(t *testing.T) {
db.DB.Save(&user) db.DB.Save(&user)
db.DB.Save(&types.Node{ db.DB.Save(&types.Node{
User: user, User: &user,
IPv6: nap("fd7a:115c:a1e0::1"), IPv6: nap("fd7a:115c:a1e0::1"),
}) })
@ -364,7 +364,7 @@ func TestBackfillIPAddresses(t *testing.T) {
db.DB.Save(&user) db.DB.Save(&user)
db.DB.Save(&types.Node{ db.DB.Save(&types.Node{
User: user, User: &user,
IPv4: nap("100.64.0.1"), IPv4: nap("100.64.0.1"),
IPv6: nap("fd7a:115c:a1e0::1"), IPv6: nap("fd7a:115c:a1e0::1"),
}) })
@ -388,7 +388,7 @@ func TestBackfillIPAddresses(t *testing.T) {
db.DB.Save(&user) db.DB.Save(&user)
db.DB.Save(&types.Node{ db.DB.Save(&types.Node{
User: user, User: &user,
IPv4: nap("100.64.0.1"), IPv4: nap("100.64.0.1"),
IPv6: nap("fd7a:115c:a1e0::1"), IPv6: nap("fd7a:115c:a1e0::1"),
}) })
@ -412,19 +412,19 @@ func TestBackfillIPAddresses(t *testing.T) {
db.DB.Save(&user) db.DB.Save(&user)
db.DB.Save(&types.Node{ db.DB.Save(&types.Node{
User: user, User: &user,
IPv4: nap("100.64.0.1"), IPv4: nap("100.64.0.1"),
}) })
db.DB.Save(&types.Node{ db.DB.Save(&types.Node{
User: user, User: &user,
IPv4: nap("100.64.0.2"), IPv4: nap("100.64.0.2"),
}) })
db.DB.Save(&types.Node{ db.DB.Save(&types.Node{
User: user, User: &user,
IPv4: nap("100.64.0.3"), IPv4: nap("100.64.0.3"),
}) })
db.DB.Save(&types.Node{ db.DB.Save(&types.Node{
User: user, User: &user,
IPv4: nap("100.64.0.4"), IPv4: nap("100.64.0.4"),
}) })

View File

@ -203,7 +203,7 @@ func SetTags(
) error { ) error {
if len(tags) == 0 { if len(tags) == 0 {
// if no tags are provided, we remove all forced tags // 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) return fmt.Errorf("removing tags: %w", err)
} }
@ -217,7 +217,7 @@ func SetTags(
return err 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) return fmt.Errorf("updating tags: %w", err)
} }
@ -376,12 +376,13 @@ func (hsdb *HSDatabase) HandleNodeFromAuthPath(
// Why not always? // Why not always?
// Registration of expired node with different user // Registration of expired node with different user
if reg.Node.ID != 0 && if reg.Node.ID != 0 &&
reg.Node.UserID != user.ID { reg.Node.UserID != nil &&
*reg.Node.UserID != user.ID {
return nil, ErrDifferentRegisteredUser return nil, ErrDifferentRegisteredUser
} }
reg.Node.UserID = user.ID reg.Node.UserID = &user.ID
reg.Node.User = *user reg.Node.User = user
reg.Node.RegisterMethod = registrationMethod reg.Node.RegisterMethod = registrationMethod
if nodeExpiry != nil { 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("node", node.Hostname).
Str("machine_key", node.MachineKey.ShortString()). Str("machine_key", node.MachineKey.ShortString()).
Str("node_key", node.NodeKey.ShortString()). Str("node_key", node.NodeKey.ShortString()).
Str("user", node.User.Username()).
Msg("Registering node") Msg("Registering node")
// If the a new node is registered with the same machine key, to the same user, // 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("node", node.Hostname).
Str("machine_key", node.MachineKey.ShortString()). Str("machine_key", node.MachineKey.ShortString()).
Str("node_key", node.NodeKey.ShortString()). Str("node_key", node.NodeKey.ShortString()).
Str("user", node.User.Username()).
Msg("Node authorized again") Msg("Node authorized again")
return &node, nil return &node, nil

View File

@ -43,7 +43,7 @@ func (s *Suite) TestGetNode(c *check.C) {
MachineKey: machineKey.Public(), MachineKey: machineKey.Public(),
NodeKey: nodeKey.Public(), NodeKey: nodeKey.Public(),
Hostname: "testnode", Hostname: "testnode",
UserID: user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID), AuthKeyID: ptr.To(pak.ID),
} }
@ -72,7 +72,7 @@ func (s *Suite) TestGetNodeByID(c *check.C) {
MachineKey: machineKey.Public(), MachineKey: machineKey.Public(),
NodeKey: nodeKey.Public(), NodeKey: nodeKey.Public(),
Hostname: "testnode", Hostname: "testnode",
UserID: user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID), AuthKeyID: ptr.To(pak.ID),
} }
@ -95,7 +95,7 @@ func (s *Suite) TestHardDeleteNode(c *check.C) {
MachineKey: machineKey.Public(), MachineKey: machineKey.Public(),
NodeKey: nodeKey.Public(), NodeKey: nodeKey.Public(),
Hostname: "testnode3", Hostname: "testnode3",
UserID: user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
} }
trx := db.DB.Save(&node) trx := db.DB.Save(&node)
@ -127,7 +127,7 @@ func (s *Suite) TestListPeers(c *check.C) {
MachineKey: machineKey.Public(), MachineKey: machineKey.Public(),
NodeKey: nodeKey.Public(), NodeKey: nodeKey.Public(),
Hostname: "testnode" + strconv.Itoa(index), Hostname: "testnode" + strconv.Itoa(index),
UserID: user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID), AuthKeyID: ptr.To(pak.ID),
} }
@ -165,7 +165,7 @@ func (s *Suite) TestExpireNode(c *check.C) {
MachineKey: machineKey.Public(), MachineKey: machineKey.Public(),
NodeKey: nodeKey.Public(), NodeKey: nodeKey.Public(),
Hostname: "testnode", Hostname: "testnode",
UserID: user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID), AuthKeyID: ptr.To(pak.ID),
Expiry: &time.Time{}, Expiry: &time.Time{},
@ -206,7 +206,7 @@ func (s *Suite) TestSetTags(c *check.C) {
MachineKey: machineKey.Public(), MachineKey: machineKey.Public(),
NodeKey: nodeKey.Public(), NodeKey: nodeKey.Public(),
Hostname: "testnode", Hostname: "testnode",
UserID: user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID), AuthKeyID: ptr.To(pak.ID),
} }
@ -220,7 +220,7 @@ func (s *Suite) TestSetTags(c *check.C) {
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
node, err = db.getNode(types.UserID(user.ID), "testnode") node, err = db.getNode(types.UserID(user.ID), "testnode")
c.Assert(err, check.IsNil) 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 // assign duplicate tags, expect no errors but no doubles in DB
eTags := []string{"tag:bar", "tag:test", "tag:unknown", "tag:test"} 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") node, err = db.getNode(types.UserID(user.ID), "testnode")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert( c.Assert(
node.ForcedTags, node.Tags,
check.DeepEquals, check.DeepEquals,
[]string{"tag:bar", "tag:test", "tag:unknown"}, []string{"tag:bar", "tag:test", "tag:unknown"},
) )
@ -239,7 +239,7 @@ func (s *Suite) TestSetTags(c *check.C) {
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
node, err = db.getNode(types.UserID(user.ID), "testnode") node, err = db.getNode(types.UserID(user.ID), "testnode")
c.Assert(err, check.IsNil) 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) { func TestHeadscale_generateGivenName(t *testing.T) {
@ -451,7 +451,7 @@ func TestAutoApproveRoutes(t *testing.T) {
MachineKey: key.NewMachine().Public(), MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(), NodeKey: key.NewNode().Public(),
Hostname: "testnode", Hostname: "testnode",
UserID: user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
Hostinfo: &tailcfg.Hostinfo{ Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tt.routes, RoutableIPs: tt.routes,
@ -467,12 +467,12 @@ func TestAutoApproveRoutes(t *testing.T) {
MachineKey: key.NewMachine().Public(), MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(), NodeKey: key.NewNode().Public(),
Hostname: "taggednode", Hostname: "taggednode",
UserID: taggedUser.ID, UserID: &taggedUser.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
Hostinfo: &tailcfg.Hostinfo{ Hostinfo: &tailcfg.Hostinfo{
RoutableIPs: tt.routes, RoutableIPs: tt.routes,
}, },
ForcedTags: []string{"tag:exit"}, Tags: []string{"tag:exit"},
IPv4: ptr.To(netip.MustParseAddr("100.64.0.2")), IPv4: ptr.To(netip.MustParseAddr("100.64.0.2")),
} }
@ -612,7 +612,7 @@ func TestListEphemeralNodes(t *testing.T) {
MachineKey: key.NewMachine().Public(), MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(), NodeKey: key.NewNode().Public(),
Hostname: "test", Hostname: "test",
UserID: user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID), AuthKeyID: ptr.To(pak.ID),
} }
@ -622,7 +622,7 @@ func TestListEphemeralNodes(t *testing.T) {
MachineKey: key.NewMachine().Public(), MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(), NodeKey: key.NewNode().Public(),
Hostname: "ephemeral", Hostname: "ephemeral",
UserID: user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pakEph.ID), AuthKeyID: ptr.To(pakEph.ID),
} }
@ -665,7 +665,7 @@ func TestRenameNode(t *testing.T) {
MachineKey: key.NewMachine().Public(), MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(), NodeKey: key.NewNode().Public(),
Hostname: "test", Hostname: "test",
UserID: user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@ -675,7 +675,7 @@ func TestRenameNode(t *testing.T) {
MachineKey: key.NewMachine().Public(), MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(), NodeKey: key.NewNode().Public(),
Hostname: "test", Hostname: "test",
UserID: user2.ID, UserID: &user2.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@ -765,7 +765,7 @@ func TestListPeers(t *testing.T) {
MachineKey: key.NewMachine().Public(), MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(), NodeKey: key.NewNode().Public(),
Hostname: "test1", Hostname: "test1",
UserID: user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@ -775,7 +775,7 @@ func TestListPeers(t *testing.T) {
MachineKey: key.NewMachine().Public(), MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(), NodeKey: key.NewNode().Public(),
Hostname: "test2", Hostname: "test2",
UserID: user2.ID, UserID: &user2.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@ -849,7 +849,7 @@ func TestListNodes(t *testing.T) {
MachineKey: key.NewMachine().Public(), MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(), NodeKey: key.NewNode().Public(),
Hostname: "test1", Hostname: "test1",
UserID: user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }
@ -859,7 +859,7 @@ func TestListNodes(t *testing.T) {
MachineKey: key.NewMachine().Public(), MachineKey: key.NewMachine().Public(),
NodeKey: key.NewNode().Public(), NodeKey: key.NewNode().Public(),
Hostname: "test2", Hostname: "test2",
UserID: user2.ID, UserID: &user2.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},
} }

View File

@ -74,7 +74,7 @@ func TestCannotDeleteAssignedPreAuthKey(t *testing.T) {
node := types.Node{ node := types.Node{
ID: 0, ID: 0,
Hostname: "testest", Hostname: "testest",
UserID: user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(key.ID), AuthKeyID: ptr.To(key.ID),
} }

View File

@ -7,6 +7,7 @@ import (
"github.com/juanfont/headscale/hscontrol/types" "github.com/juanfont/headscale/hscontrol/types"
"github.com/juanfont/headscale/hscontrol/util" "github.com/juanfont/headscale/hscontrol/util"
"gorm.io/gorm" "gorm.io/gorm"
"tailscale.com/types/ptr"
) )
var ( var (
@ -192,7 +193,7 @@ func (hsdb *HSDatabase) GetUserByName(name string) (*types.User, error) {
// ListNodesByUser gets all the nodes in a given user. // ListNodesByUser gets all the nodes in a given user.
func ListNodesByUser(tx *gorm.DB, uid types.UserID) (types.Nodes, error) { func ListNodesByUser(tx *gorm.DB, uid types.UserID) (types.Nodes, error) {
nodes := types.Nodes{} 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 return nil, err
} }
@ -211,7 +212,7 @@ func AssignNodeToUser(tx *gorm.DB, node *types.Node, uid types.UserID) error {
if err != nil { if err != nil {
return err return err
} }
node.User = *user node.User = user
if result := tx.Save(&node); result.Error != nil { if result := tx.Save(&node); result.Error != nil {
return result.Error return result.Error
} }

View File

@ -52,7 +52,7 @@ func (s *Suite) TestDestroyUserErrors(c *check.C) {
node := types.Node{ node := types.Node{
ID: 0, ID: 0,
Hostname: "testnode", Hostname: "testnode",
UserID: user.ID, UserID: &user.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID), AuthKeyID: ptr.To(pak.ID),
} }
@ -110,17 +110,17 @@ func (s *Suite) TestSetMachineUser(c *check.C) {
node := types.Node{ node := types.Node{
ID: 0, ID: 0,
Hostname: "testnode", Hostname: "testnode",
UserID: oldUser.ID, UserID: &oldUser.ID,
RegisterMethod: util.RegisterMethodAuthKey, RegisterMethod: util.RegisterMethodAuthKey,
AuthKeyID: ptr.To(pak.ID), AuthKeyID: ptr.To(pak.ID),
} }
trx := db.DB.Save(&node) trx := db.DB.Save(&node)
c.Assert(trx.Error, check.IsNil) 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)) err = db.AssignNodeToUser(&node, types.UserID(newUser.ID))
c.Assert(err, check.IsNil) 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) c.Assert(node.User.Name, check.Equals, newUser.Name)
err = db.AssignNodeToUser(&node, 9584849) err = db.AssignNodeToUser(&node, 9584849)
@ -128,6 +128,6 @@ func (s *Suite) TestSetMachineUser(c *check.C) {
err = db.AssignNodeToUser(&node, types.UserID(newUser.ID)) err = db.AssignNodeToUser(&node, types.UserID(newUser.ID))
c.Assert(err, check.IsNil) 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) c.Assert(node.User.Name, check.Equals, newUser.Name)
} }

View File

@ -552,7 +552,7 @@ func nodesToProto(polMan policy.PolicyManager, isLikelyConnected *xsync.MapOf[ty
tags = append(tags, tag) 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()...)) resp.SubnetRoutes = util.PrefixesToString(append(pr.PrimaryRoutes(node.ID), node.ExitRoutes()...))
response[index] = resp response[index] = resp
} }
@ -819,7 +819,7 @@ func (api headscaleV1APIServer) DebugCreateNode(
NodeKey: key.NewNode().Public(), NodeKey: key.NewNode().Public(),
MachineKey: key.NewMachine().Public(), MachineKey: key.NewMachine().Public(),
Hostname: request.GetName(), Hostname: request.GetName(),
User: *user, User: user,
Expiry: &time.Time{}, Expiry: &time.Time{},
LastSeen: &time.Time{}, LastSeen: &time.Time{},

View File

@ -104,12 +104,17 @@ func generateUserProfiles(
) []tailcfg.UserProfile { ) []tailcfg.UserProfile {
userMap := make(map[uint]*types.User) userMap := make(map[uint]*types.User)
ids := make([]uint, 0, len(userMap)) ids := make([]uint, 0, len(userMap))
userMap[node.User.ID] = &node.User var tagged bool
if node.IsUserOwned() {
userMap[node.User.ID] = node.User
ids = append(ids, node.User.ID) ids = append(ids, node.User.ID)
for _, peer := range peers { for _, peer := range peers {
userMap[peer.User.ID] = &peer.User userMap[peer.User.ID] = peer.User
ids = append(ids, peer.User.ID) ids = append(ids, peer.User.ID)
} }
} else {
tagged = true
}
slices.Sort(ids) slices.Sort(ids)
ids = slices.Compact(ids) ids = slices.Compact(ids)
@ -120,6 +125,10 @@ func generateUserProfiles(
} }
} }
if tagged {
profiles = append(profiles, types.TaggedDevices.TailscaleUserProfile())
}
return profiles return profiles
} }

View File

@ -53,8 +53,8 @@ func TestDNSConfigMapResponse(t *testing.T) {
mach := func(hostname, username string, userid uint) *types.Node { mach := func(hostname, username string, userid uint) *types.Node {
return &types.Node{ return &types.Node{
Hostname: hostname, Hostname: hostname,
UserID: userid, UserID: &userid,
User: types.User{ User: &types.User{
Name: username, Name: username,
}, },
} }
@ -131,9 +131,9 @@ func Test_fullMapResponse(t *testing.T) {
IPv4: iap("100.64.0.1"), IPv4: iap("100.64.0.1"),
Hostname: "mini", Hostname: "mini",
GivenName: "mini", GivenName: "mini",
UserID: user1.ID, UserID: &user1.ID,
User: user1, User: &user1,
ForcedTags: []string{}, Tags: []string{},
AuthKey: &types.PreAuthKey{}, AuthKey: &types.PreAuthKey{},
LastSeen: &lastSeen, LastSeen: &lastSeen,
Expiry: &expire, Expiry: &expire,
@ -208,9 +208,9 @@ func Test_fullMapResponse(t *testing.T) {
IPv4: iap("100.64.0.2"), IPv4: iap("100.64.0.2"),
Hostname: "peer1", Hostname: "peer1",
GivenName: "peer1", GivenName: "peer1",
UserID: user2.ID, UserID: &user2.ID,
User: user2, User: &user2,
ForcedTags: []string{}, Tags: []string{},
LastSeen: &lastSeen, LastSeen: &lastSeen,
Expiry: &expire, Expiry: &expire,
Hostinfo: &tailcfg.Hostinfo{}, Hostinfo: &tailcfg.Hostinfo{},

View File

@ -6,7 +6,6 @@ import (
"github.com/juanfont/headscale/hscontrol/policy" "github.com/juanfont/headscale/hscontrol/policy"
"github.com/juanfont/headscale/hscontrol/types" "github.com/juanfont/headscale/hscontrol/types"
"github.com/samber/lo"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
) )
@ -72,14 +71,6 @@ func tailNode(
return nil, fmt.Errorf("tailNode, failed to create FQDN: %s", err) 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) routes := primaryRouteFunc(node.ID)
allowed := append(node.Prefixes(), routes...) allowed := append(node.Prefixes(), routes...)
allowed = append(allowed, node.ExitRoutes()...) allowed = append(allowed, node.ExitRoutes()...)
@ -91,8 +82,6 @@ func tailNode(
Name: hostname, Name: hostname,
Cap: capVer, Cap: capVer,
User: tailcfg.UserID(node.UserID),
Key: node.NodeKey, Key: node.NodeKey,
KeyExpiry: keyExpiry.UTC(), KeyExpiry: keyExpiry.UTC(),
@ -109,12 +98,20 @@ func tailNode(
Online: node.IsOnline, Online: node.IsOnline,
Tags: tags, Tags: node.Tags,
MachineAuthorized: !node.IsExpired(), MachineAuthorized: !node.IsExpired(),
Expired: 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{ tNode.CapMap = tailcfg.NodeCapMap{
tailcfg.CapabilityFileSharing: []tailcfg.RawMessage{}, tailcfg.CapabilityFileSharing: []tailcfg.RawMessage{},
tailcfg.CapabilityAdmin: []tailcfg.RawMessage{}, tailcfg.CapabilityAdmin: []tailcfg.RawMessage{},

View File

@ -15,6 +15,7 @@ import (
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/key" "tailscale.com/types/key"
"tailscale.com/types/ptr"
) )
func TestTailNode(t *testing.T) { func TestTailNode(t *testing.T) {
@ -70,7 +71,6 @@ func TestTailNode(t *testing.T) {
HomeDERP: 0, HomeDERP: 0,
LegacyDERPString: "127.3.3.40:0", LegacyDERPString: "127.3.3.40:0",
Hostinfo: hiview(tailcfg.Hostinfo{}), Hostinfo: hiview(tailcfg.Hostinfo{}),
Tags: []string{},
MachineAuthorized: true, MachineAuthorized: true,
CapMap: tailcfg.NodeCapMap{ CapMap: tailcfg.NodeCapMap{
@ -97,11 +97,10 @@ func TestTailNode(t *testing.T) {
IPv4: iap("100.64.0.1"), IPv4: iap("100.64.0.1"),
Hostname: "mini", Hostname: "mini",
GivenName: "mini", GivenName: "mini",
UserID: 0, UserID: ptr.To(uint(0)),
User: types.User{ User: &types.User{
Name: "mini", Name: "mini",
}, },
ForcedTags: []string{},
AuthKey: &types.PreAuthKey{}, AuthKey: &types.PreAuthKey{},
LastSeen: &lastSeen, LastSeen: &lastSeen,
Expiry: &expire, Expiry: &expire,
@ -156,8 +155,6 @@ func TestTailNode(t *testing.T) {
}), }),
Created: created, Created: created,
Tags: []string{},
LastSeen: &lastSeen, LastSeen: &lastSeen,
MachineAuthorized: true, MachineAuthorized: true,
@ -184,7 +181,6 @@ func TestTailNode(t *testing.T) {
HomeDERP: 0, HomeDERP: 0,
LegacyDERPString: "127.3.3.40:0", LegacyDERPString: "127.3.3.40:0",
Hostinfo: hiview(tailcfg.Hostinfo{}), Hostinfo: hiview(tailcfg.Hostinfo{}),
Tags: []string{},
MachineAuthorized: true, MachineAuthorized: true,
CapMap: tailcfg.NodeCapMap{ CapMap: tailcfg.NodeCapMap{

View File

@ -567,7 +567,9 @@ func (nodes Nodes) DebugString() string {
func (node Node) DebugString() string { func (node Node) DebugString() string {
var sb strings.Builder var sb strings.Builder
fmt.Fprintf(&sb, "%s(%s):\n", node.Hostname, node.ID) fmt.Fprintf(&sb, "%s(%s):\n", node.Hostname, node.ID)
if node.IsUserOwned() {
fmt.Fprintf(&sb, "\tUser: %s (%d, %q)\n", node.User.Display(), node.User.ID, node.User.Username()) 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, "\tTags: %v\n", node.Tags)
fmt.Fprintf(&sb, "\tIPs: %v\n", node.IPs()) fmt.Fprintf(&sb, "\tIPs: %v\n", node.IPs())
fmt.Fprintf(&sb, "\tApprovedRoutes: %v\n", node.ApprovedRoutes) fmt.Fprintf(&sb, "\tApprovedRoutes: %v\n", node.ApprovedRoutes)

View File

@ -2,11 +2,12 @@ package types
import ( import (
"fmt" "fmt"
"github.com/juanfont/headscale/hscontrol/policy/matcher"
"net/netip" "net/netip"
"strings" "strings"
"testing" "testing"
"github.com/juanfont/headscale/hscontrol/policy/matcher"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts" "github.com/google/go-cmp/cmp/cmpopts"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1" v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
@ -139,7 +140,7 @@ func TestNodeFQDN(t *testing.T) {
name: "no-dnsconfig-with-username", name: "no-dnsconfig-with-username",
node: Node{ node: Node{
GivenName: "test", GivenName: "test",
User: User{ User: &User{
Name: "user", Name: "user",
}, },
}, },
@ -150,7 +151,7 @@ func TestNodeFQDN(t *testing.T) {
name: "all-set", name: "all-set",
node: Node{ node: Node{
GivenName: "test", GivenName: "test",
User: User{ User: &User{
Name: "user", Name: "user",
}, },
}, },
@ -160,7 +161,7 @@ func TestNodeFQDN(t *testing.T) {
{ {
name: "no-given-name", name: "no-given-name",
node: Node{ node: Node{
User: User{ User: &User{
Name: "user", Name: "user",
}, },
}, },
@ -179,7 +180,7 @@ func TestNodeFQDN(t *testing.T) {
name: "no-dnsconfig", name: "no-dnsconfig",
node: Node{ node: Node{
GivenName: "test", GivenName: "test",
User: User{ User: &User{
Name: "user", Name: "user",
}, },
}, },

View File

@ -18,6 +18,16 @@ import (
"tailscale.com/tailcfg" "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 UserID uint64
type Users []User type Users []User