mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-31 16:11:03 +01:00 
			
		
		
		
	Rename [Nn]amespace -> [Uu]ser in go code
Use gopls, ag and perl to rename all occurances of Namespace Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
		
							parent
							
								
									bafb6791d3
								
							
						
					
					
						commit
						e3a2593344
					
				
							
								
								
									
										36
									
								
								acls.go
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								acls.go
									
									
									
									
									
								
							| @ -424,7 +424,7 @@ func parseProtocol(protocol string) ([]int, bool, error) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // expandalias has an input of either | // expandalias has an input of either | ||||||
| // - a namespace | // - a user | ||||||
| // - a group | // - a group | ||||||
| // - a tag | // - a tag | ||||||
| // and transform these in IPAddresses. | // and transform these in IPAddresses. | ||||||
| @ -444,12 +444,12 @@ func expandAlias( | |||||||
| 		Msg("Expanding") | 		Msg("Expanding") | ||||||
| 
 | 
 | ||||||
| 	if strings.HasPrefix(alias, "group:") { | 	if strings.HasPrefix(alias, "group:") { | ||||||
| 		namespaces, err := expandGroup(aclPolicy, alias, stripEmailDomain) | 		users, err := expandGroup(aclPolicy, alias, stripEmailDomain) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return ips, err | 			return ips, err | ||||||
| 		} | 		} | ||||||
| 		for _, n := range namespaces { | 		for _, n := range users { | ||||||
| 			nodes := filterMachinesByNamespace(machines, n) | 			nodes := filterMachinesByUser(machines, n) | ||||||
| 			for _, node := range nodes { | 			for _, node := range nodes { | ||||||
| 				ips = append(ips, node.IPAddresses.ToStringSlice()...) | 				ips = append(ips, node.IPAddresses.ToStringSlice()...) | ||||||
| 			} | 			} | ||||||
| @ -485,8 +485,8 @@ func expandAlias( | |||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// filter out machines per tag owner | 		// filter out machines per tag owner | ||||||
| 		for _, namespace := range owners { | 		for _, user := range owners { | ||||||
| 			machines := filterMachinesByNamespace(machines, namespace) | 			machines := filterMachinesByUser(machines, user) | ||||||
| 			for _, machine := range machines { | 			for _, machine := range machines { | ||||||
| 				hi := machine.GetHostInfo() | 				hi := machine.GetHostInfo() | ||||||
| 				if contains(hi.RequestTags, alias) { | 				if contains(hi.RequestTags, alias) { | ||||||
| @ -498,8 +498,8 @@ func expandAlias( | |||||||
| 		return ips, nil | 		return ips, nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// if alias is a namespace | 	// if alias is a user | ||||||
| 	nodes := filterMachinesByNamespace(machines, alias) | 	nodes := filterMachinesByUser(machines, alias) | ||||||
| 	nodes = excludeCorrectlyTaggedNodes(aclPolicy, nodes, alias, stripEmailDomain) | 	nodes = excludeCorrectlyTaggedNodes(aclPolicy, nodes, alias, stripEmailDomain) | ||||||
| 
 | 
 | ||||||
| 	for _, n := range nodes { | 	for _, n := range nodes { | ||||||
| @ -532,20 +532,20 @@ func expandAlias( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // excludeCorrectlyTaggedNodes will remove from the list of input nodes the ones | // excludeCorrectlyTaggedNodes will remove from the list of input nodes the ones | ||||||
| // that are correctly tagged since they should not be listed as being in the namespace | // that are correctly tagged since they should not be listed as being in the user | ||||||
| // we assume in this function that we only have nodes from 1 namespace. | // we assume in this function that we only have nodes from 1 user. | ||||||
| func excludeCorrectlyTaggedNodes( | func excludeCorrectlyTaggedNodes( | ||||||
| 	aclPolicy ACLPolicy, | 	aclPolicy ACLPolicy, | ||||||
| 	nodes []Machine, | 	nodes []Machine, | ||||||
| 	namespace string, | 	user string, | ||||||
| 	stripEmailDomain bool, | 	stripEmailDomain bool, | ||||||
| ) []Machine { | ) []Machine { | ||||||
| 	out := []Machine{} | 	out := []Machine{} | ||||||
| 	tags := []string{} | 	tags := []string{} | ||||||
| 	for tag := range aclPolicy.TagOwners { | 	for tag := range aclPolicy.TagOwners { | ||||||
| 		owners, _ := expandTagOwners(aclPolicy, namespace, stripEmailDomain) | 		owners, _ := expandTagOwners(aclPolicy, user, stripEmailDomain) | ||||||
| 		ns := append(owners, namespace) | 		ns := append(owners, user) | ||||||
| 		if contains(ns, namespace) { | 		if contains(ns, user) { | ||||||
| 			tags = append(tags, tag) | 			tags = append(tags, tag) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @ -619,10 +619,10 @@ func expandPorts(portsStr string, needsWildcard bool) (*[]tailcfg.PortRange, err | |||||||
| 	return &ports, nil | 	return &ports, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func filterMachinesByNamespace(machines []Machine, namespace string) []Machine { | func filterMachinesByUser(machines []Machine, user string) []Machine { | ||||||
| 	out := []Machine{} | 	out := []Machine{} | ||||||
| 	for _, machine := range machines { | 	for _, machine := range machines { | ||||||
| 		if machine.Namespace.Name == namespace { | 		if machine.User.Name == user { | ||||||
| 			out = append(out, machine) | 			out = append(out, machine) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @ -630,7 +630,7 @@ func filterMachinesByNamespace(machines []Machine, namespace string) []Machine { | |||||||
| 	return out | 	return out | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // expandTagOwners will return a list of namespace. An owner can be either a namespace or a group | // expandTagOwners will return a list of user. An owner can be either a user or a group | ||||||
| // a group cannot be composed of groups. | // a group cannot be composed of groups. | ||||||
| func expandTagOwners( | func expandTagOwners( | ||||||
| 	aclPolicy ACLPolicy, | 	aclPolicy ACLPolicy, | ||||||
| @ -661,7 +661,7 @@ func expandTagOwners( | |||||||
| 	return owners, nil | 	return owners, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // expandGroup will return the list of namespace inside the group | // expandGroup will return the list of user inside the group | ||||||
| // after some validation. | // after some validation. | ||||||
| func expandGroup( | func expandGroup( | ||||||
| 	aclPolicy ACLPolicy, | 	aclPolicy ACLPolicy, | ||||||
|  | |||||||
							
								
								
									
										212
									
								
								acls_test.go
									
									
									
									
									
								
							
							
						
						
									
										212
									
								
								acls_test.go
									
									
									
									
									
								
							| @ -77,10 +77,10 @@ func (s *Suite) TestInvalidAction(c *check.C) { | |||||||
| func (s *Suite) TestSshRules(c *check.C) { | func (s *Suite) TestSshRules(c *check.C) { | ||||||
| 	envknob.Setenv("HEADSCALE_EXPERIMENTAL_FEATURE_SSH", "1") | 	envknob.Setenv("HEADSCALE_EXPERIMENTAL_FEATURE_SSH", "1") | ||||||
| 
 | 
 | ||||||
| 	namespace, err := app.CreateNamespace("user1") | 	user, err := app.CreateUser("user1") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine("user1", "testmachine") | 	_, err = app.GetMachine("user1", "testmachine") | ||||||
| @ -98,7 +98,7 @@ func (s *Suite) TestSshRules(c *check.C) { | |||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testmachine", | 		Hostname:       "testmachine", | ||||||
| 		IPAddresses:    MachineAddresses{netip.MustParseAddr("100.64.0.1")}, | 		IPAddresses:    MachineAddresses{netip.MustParseAddr("100.64.0.1")}, | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 		HostInfo:       HostInfo(hostInfo), | 		HostInfo:       HostInfo(hostInfo), | ||||||
| @ -187,10 +187,10 @@ func (s *Suite) TestInvalidTagOwners(c *check.C) { | |||||||
| // match properly the IP's of the related hosts. The owner is valid and the tag is also valid. | // match properly the IP's of the related hosts. The owner is valid and the tag is also valid. | ||||||
| // the tag is matched in the Sources section. | // the tag is matched in the Sources section. | ||||||
| func (s *Suite) TestValidExpandTagOwnersInSources(c *check.C) { | func (s *Suite) TestValidExpandTagOwnersInSources(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("user1") | 	user, err := app.CreateUser("user1") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine("user1", "testmachine") | 	_, err = app.GetMachine("user1", "testmachine") | ||||||
| @ -208,7 +208,7 @@ func (s *Suite) TestValidExpandTagOwnersInSources(c *check.C) { | |||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testmachine", | 		Hostname:       "testmachine", | ||||||
| 		IPAddresses:    MachineAddresses{netip.MustParseAddr("100.64.0.1")}, | 		IPAddresses:    MachineAddresses{netip.MustParseAddr("100.64.0.1")}, | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 		HostInfo:       HostInfo(hostInfo), | 		HostInfo:       HostInfo(hostInfo), | ||||||
| @ -237,10 +237,10 @@ func (s *Suite) TestValidExpandTagOwnersInSources(c *check.C) { | |||||||
| // match properly the IP's of the related hosts. The owner is valid and the tag is also valid. | // match properly the IP's of the related hosts. The owner is valid and the tag is also valid. | ||||||
| // the tag is matched in the Destinations section. | // the tag is matched in the Destinations section. | ||||||
| func (s *Suite) TestValidExpandTagOwnersInDestinations(c *check.C) { | func (s *Suite) TestValidExpandTagOwnersInDestinations(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("user1") | 	user, err := app.CreateUser("user1") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine("user1", "testmachine") | 	_, err = app.GetMachine("user1", "testmachine") | ||||||
| @ -258,7 +258,7 @@ func (s *Suite) TestValidExpandTagOwnersInDestinations(c *check.C) { | |||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testmachine", | 		Hostname:       "testmachine", | ||||||
| 		IPAddresses:    MachineAddresses{netip.MustParseAddr("100.64.0.1")}, | 		IPAddresses:    MachineAddresses{netip.MustParseAddr("100.64.0.1")}, | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 		HostInfo:       HostInfo(hostInfo), | 		HostInfo:       HostInfo(hostInfo), | ||||||
| @ -284,13 +284,13 @@ func (s *Suite) TestValidExpandTagOwnersInDestinations(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // need a test with: | // need a test with: | ||||||
| // tag on a host that isn't owned by a tag owners. So the namespace | // tag on a host that isn't owned by a tag owners. So the user | ||||||
| // of the host should be valid. | // of the host should be valid. | ||||||
| func (s *Suite) TestInvalidTagValidNamespace(c *check.C) { | func (s *Suite) TestInvalidTagValidUser(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("user1") | 	user, err := app.CreateUser("user1") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine("user1", "testmachine") | 	_, err = app.GetMachine("user1", "testmachine") | ||||||
| @ -308,7 +308,7 @@ func (s *Suite) TestInvalidTagValidNamespace(c *check.C) { | |||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testmachine", | 		Hostname:       "testmachine", | ||||||
| 		IPAddresses:    MachineAddresses{netip.MustParseAddr("100.64.0.1")}, | 		IPAddresses:    MachineAddresses{netip.MustParseAddr("100.64.0.1")}, | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 		HostInfo:       HostInfo(hostInfo), | 		HostInfo:       HostInfo(hostInfo), | ||||||
| @ -333,13 +333,13 @@ func (s *Suite) TestInvalidTagValidNamespace(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // tag on a host is owned by a tag owner, the tag is valid. | // tag on a host is owned by a tag owner, the tag is valid. | ||||||
| // an ACL rule is matching the tag to a namespace. It should not be valid since the | // an ACL rule is matching the tag to a user. It should not be valid since the | ||||||
| // host should be tied to the tag now. | // host should be tied to the tag now. | ||||||
| func (s *Suite) TestValidTagInvalidNamespace(c *check.C) { | func (s *Suite) TestValidTagInvalidUser(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("user1") | 	user, err := app.CreateUser("user1") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine("user1", "webserver") | 	_, err = app.GetMachine("user1", "webserver") | ||||||
| @ -357,7 +357,7 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) { | |||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "webserver", | 		Hostname:       "webserver", | ||||||
| 		IPAddresses:    MachineAddresses{netip.MustParseAddr("100.64.0.1")}, | 		IPAddresses:    MachineAddresses{netip.MustParseAddr("100.64.0.1")}, | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 		HostInfo:       HostInfo(hostInfo), | 		HostInfo:       HostInfo(hostInfo), | ||||||
| @ -376,7 +376,7 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) { | |||||||
| 		DiscoKey:       "faab", | 		DiscoKey:       "faab", | ||||||
| 		Hostname:       "user", | 		Hostname:       "user", | ||||||
| 		IPAddresses:    MachineAddresses{netip.MustParseAddr("100.64.0.2")}, | 		IPAddresses:    MachineAddresses{netip.MustParseAddr("100.64.0.2")}, | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 		HostInfo:       HostInfo(hostInfo2), | 		HostInfo:       HostInfo(hostInfo2), | ||||||
| @ -467,14 +467,14 @@ func (s *Suite) TestPortWildcardYAML(c *check.C) { | |||||||
| 	c.Assert(rules[0].SrcIPs[0], check.Equals, "*") | 	c.Assert(rules[0].SrcIPs[0], check.Equals, "*") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestPortNamespace(c *check.C) { | func (s *Suite) TestPortUser(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("testnamespace") | 	user, err := app.CreateUser("testuser") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine("testnamespace", "testmachine") | 	_, err = app.GetMachine("testuser", "testmachine") | ||||||
| 	c.Assert(err, check.NotNil) | 	c.Assert(err, check.NotNil) | ||||||
| 	ips, _ := app.getAvailableIPs() | 	ips, _ := app.getAvailableIPs() | ||||||
| 	machine := Machine{ | 	machine := Machine{ | ||||||
| @ -483,7 +483,7 @@ func (s *Suite) TestPortNamespace(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testmachine", | 		Hostname:       "testmachine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		IPAddresses:    ips, | 		IPAddresses:    ips, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| @ -491,7 +491,7 @@ func (s *Suite) TestPortNamespace(c *check.C) { | |||||||
| 	app.db.Save(&machine) | 	app.db.Save(&machine) | ||||||
| 
 | 
 | ||||||
| 	err = app.LoadACLPolicy( | 	err = app.LoadACLPolicy( | ||||||
| 		"./tests/acls/acl_policy_basic_namespace_as_user.hujson", | 		"./tests/acls/acl_policy_basic_user_as_user.hujson", | ||||||
| 	) | 	) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| @ -513,13 +513,13 @@ func (s *Suite) TestPortNamespace(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestPortGroup(c *check.C) { | func (s *Suite) TestPortGroup(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("testnamespace") | 	user, err := app.CreateUser("testuser") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine("testnamespace", "testmachine") | 	_, err = app.GetMachine("testuser", "testmachine") | ||||||
| 	c.Assert(err, check.NotNil) | 	c.Assert(err, check.NotNil) | ||||||
| 	ips, _ := app.getAvailableIPs() | 	ips, _ := app.getAvailableIPs() | ||||||
| 	machine := Machine{ | 	machine := Machine{ | ||||||
| @ -528,7 +528,7 @@ func (s *Suite) TestPortGroup(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testmachine", | 		Hostname:       "testmachine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		IPAddresses:    ips, | 		IPAddresses:    ips, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| @ -689,7 +689,7 @@ func Test_expandTagOwners(t *testing.T) { | |||||||
| 			wantErr: false, | 			wantErr: false, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "expand with namespace and group", | 			name: "expand with user and group", | ||||||
| 			args: args{ | 			args: args{ | ||||||
| 				aclPolicy: ACLPolicy{ | 				aclPolicy: ACLPolicy{ | ||||||
| 					Groups:    Groups{"group:foo": []string{"user1", "user2"}}, | 					Groups:    Groups{"group:foo": []string{"user1", "user2"}}, | ||||||
| @ -843,10 +843,10 @@ func Test_expandPorts(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func Test_listMachinesInNamespace(t *testing.T) { | func Test_listMachinesInUser(t *testing.T) { | ||||||
| 	type args struct { | 	type args struct { | ||||||
| 		machines  []Machine | 		machines  []Machine | ||||||
| 		namespace string | 		user string | ||||||
| 	} | 	} | ||||||
| 	tests := []struct { | 	tests := []struct { | ||||||
| 		name string | 		name string | ||||||
| @ -854,54 +854,54 @@ func Test_listMachinesInNamespace(t *testing.T) { | |||||||
| 		want []Machine | 		want []Machine | ||||||
| 	}{ | 	}{ | ||||||
| 		{ | 		{ | ||||||
| 			name: "1 machine in namespace", | 			name: "1 machine in user", | ||||||
| 			args: args{ | 			args: args{ | ||||||
| 				machines: []Machine{ | 				machines: []Machine{ | ||||||
| 					{Namespace: Namespace{Name: "joe"}}, | 					{User: User{Name: "joe"}}, | ||||||
| 				}, | 				}, | ||||||
| 				namespace: "joe", | 				user: "joe", | ||||||
| 			}, | 			}, | ||||||
| 			want: []Machine{ | 			want: []Machine{ | ||||||
| 				{Namespace: Namespace{Name: "joe"}}, | 				{User: User{Name: "joe"}}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "3 machines, 2 in namespace", | 			name: "3 machines, 2 in user", | ||||||
| 			args: args{ | 			args: args{ | ||||||
| 				machines: []Machine{ | 				machines: []Machine{ | ||||||
| 					{ID: 1, Namespace: Namespace{Name: "joe"}}, | 					{ID: 1, User: User{Name: "joe"}}, | ||||||
| 					{ID: 2, Namespace: Namespace{Name: "marc"}}, | 					{ID: 2, User: User{Name: "marc"}}, | ||||||
| 					{ID: 3, Namespace: Namespace{Name: "marc"}}, | 					{ID: 3, User: User{Name: "marc"}}, | ||||||
| 				}, | 				}, | ||||||
| 				namespace: "marc", | 				user: "marc", | ||||||
| 			}, | 			}, | ||||||
| 			want: []Machine{ | 			want: []Machine{ | ||||||
| 				{ID: 2, Namespace: Namespace{Name: "marc"}}, | 				{ID: 2, User: User{Name: "marc"}}, | ||||||
| 				{ID: 3, Namespace: Namespace{Name: "marc"}}, | 				{ID: 3, User: User{Name: "marc"}}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "5 machines, 0 in namespace", | 			name: "5 machines, 0 in user", | ||||||
| 			args: args{ | 			args: args{ | ||||||
| 				machines: []Machine{ | 				machines: []Machine{ | ||||||
| 					{ID: 1, Namespace: Namespace{Name: "joe"}}, | 					{ID: 1, User: User{Name: "joe"}}, | ||||||
| 					{ID: 2, Namespace: Namespace{Name: "marc"}}, | 					{ID: 2, User: User{Name: "marc"}}, | ||||||
| 					{ID: 3, Namespace: Namespace{Name: "marc"}}, | 					{ID: 3, User: User{Name: "marc"}}, | ||||||
| 					{ID: 4, Namespace: Namespace{Name: "marc"}}, | 					{ID: 4, User: User{Name: "marc"}}, | ||||||
| 					{ID: 5, Namespace: Namespace{Name: "marc"}}, | 					{ID: 5, User: User{Name: "marc"}}, | ||||||
| 				}, | 				}, | ||||||
| 				namespace: "mickael", | 				user: "mickael", | ||||||
| 			}, | 			}, | ||||||
| 			want: []Machine{}, | 			want: []Machine{}, | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| 	for _, test := range tests { | 	for _, test := range tests { | ||||||
| 		t.Run(test.name, func(t *testing.T) { | 		t.Run(test.name, func(t *testing.T) { | ||||||
| 			if got := filterMachinesByNamespace(test.args.machines, test.args.namespace); !reflect.DeepEqual( | 			if got := filterMachinesByUser(test.args.machines, test.args.user); !reflect.DeepEqual( | ||||||
| 				got, | 				got, | ||||||
| 				test.want, | 				test.want, | ||||||
| 			) { | 			) { | ||||||
| 				t.Errorf("listMachinesInNamespace() = %v, want %v", got, test.want) | 				t.Errorf("listMachinesInUser() = %v, want %v", got, test.want) | ||||||
| 			} | 			} | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| @ -947,25 +947,25 @@ func Test_expandAlias(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.1"), | 							netip.MustParseAddr("100.64.0.1"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.2"), | 							netip.MustParseAddr("100.64.0.2"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.3"), | 							netip.MustParseAddr("100.64.0.3"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "marc"}, | 						User: User{Name: "marc"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.4"), | 							netip.MustParseAddr("100.64.0.4"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "mickael"}, | 						User: User{Name: "mickael"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				aclPolicy: ACLPolicy{ | 				aclPolicy: ACLPolicy{ | ||||||
| @ -985,25 +985,25 @@ func Test_expandAlias(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.1"), | 							netip.MustParseAddr("100.64.0.1"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.2"), | 							netip.MustParseAddr("100.64.0.2"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.3"), | 							netip.MustParseAddr("100.64.0.3"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "marc"}, | 						User: User{Name: "marc"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.4"), | 							netip.MustParseAddr("100.64.0.4"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "mickael"}, | 						User: User{Name: "mickael"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				aclPolicy: ACLPolicy{ | 				aclPolicy: ACLPolicy{ | ||||||
| @ -1071,7 +1071,7 @@ func Test_expandAlias(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.1"), | 							netip.MustParseAddr("100.64.0.1"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 						HostInfo: HostInfo{ | 						HostInfo: HostInfo{ | ||||||
| 							OS:          "centos", | 							OS:          "centos", | ||||||
| 							Hostname:    "foo", | 							Hostname:    "foo", | ||||||
| @ -1082,7 +1082,7 @@ func Test_expandAlias(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.2"), | 							netip.MustParseAddr("100.64.0.2"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 						HostInfo: HostInfo{ | 						HostInfo: HostInfo{ | ||||||
| 							OS:          "centos", | 							OS:          "centos", | ||||||
| 							Hostname:    "foo", | 							Hostname:    "foo", | ||||||
| @ -1093,13 +1093,13 @@ func Test_expandAlias(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.3"), | 							netip.MustParseAddr("100.64.0.3"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "marc"}, | 						User: User{Name: "marc"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.4"), | 							netip.MustParseAddr("100.64.0.4"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				aclPolicy: ACLPolicy{ | 				aclPolicy: ACLPolicy{ | ||||||
| @ -1119,25 +1119,25 @@ func Test_expandAlias(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.1"), | 							netip.MustParseAddr("100.64.0.1"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.2"), | 							netip.MustParseAddr("100.64.0.2"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.3"), | 							netip.MustParseAddr("100.64.0.3"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "marc"}, | 						User: User{Name: "marc"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.4"), | 							netip.MustParseAddr("100.64.0.4"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "mickael"}, | 						User: User{Name: "mickael"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				aclPolicy: ACLPolicy{ | 				aclPolicy: ACLPolicy{ | ||||||
| @ -1160,27 +1160,27 @@ func Test_expandAlias(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.1"), | 							netip.MustParseAddr("100.64.0.1"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace:  Namespace{Name: "joe"}, | 						User:  User{Name: "joe"}, | ||||||
| 						ForcedTags: []string{"tag:hr-webserver"}, | 						ForcedTags: []string{"tag:hr-webserver"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.2"), | 							netip.MustParseAddr("100.64.0.2"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace:  Namespace{Name: "joe"}, | 						User:  User{Name: "joe"}, | ||||||
| 						ForcedTags: []string{"tag:hr-webserver"}, | 						ForcedTags: []string{"tag:hr-webserver"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.3"), | 							netip.MustParseAddr("100.64.0.3"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "marc"}, | 						User: User{Name: "marc"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.4"), | 							netip.MustParseAddr("100.64.0.4"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "mickael"}, | 						User: User{Name: "mickael"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				aclPolicy:        ACLPolicy{}, | 				aclPolicy:        ACLPolicy{}, | ||||||
| @ -1198,14 +1198,14 @@ func Test_expandAlias(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.1"), | 							netip.MustParseAddr("100.64.0.1"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace:  Namespace{Name: "joe"}, | 						User:  User{Name: "joe"}, | ||||||
| 						ForcedTags: []string{"tag:hr-webserver"}, | 						ForcedTags: []string{"tag:hr-webserver"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.2"), | 							netip.MustParseAddr("100.64.0.2"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 						HostInfo: HostInfo{ | 						HostInfo: HostInfo{ | ||||||
| 							OS:          "centos", | 							OS:          "centos", | ||||||
| 							Hostname:    "foo", | 							Hostname:    "foo", | ||||||
| @ -1216,13 +1216,13 @@ func Test_expandAlias(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.3"), | 							netip.MustParseAddr("100.64.0.3"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "marc"}, | 						User: User{Name: "marc"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.4"), | 							netip.MustParseAddr("100.64.0.4"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "mickael"}, | 						User: User{Name: "mickael"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				aclPolicy: ACLPolicy{ | 				aclPolicy: ACLPolicy{ | ||||||
| @ -1236,7 +1236,7 @@ func Test_expandAlias(t *testing.T) { | |||||||
| 			wantErr: false, | 			wantErr: false, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "list host in namespace without correctly tagged servers", | 			name: "list host in user without correctly tagged servers", | ||||||
| 			args: args{ | 			args: args{ | ||||||
| 				alias: "joe", | 				alias: "joe", | ||||||
| 				machines: []Machine{ | 				machines: []Machine{ | ||||||
| @ -1244,7 +1244,7 @@ func Test_expandAlias(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.1"), | 							netip.MustParseAddr("100.64.0.1"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 						HostInfo: HostInfo{ | 						HostInfo: HostInfo{ | ||||||
| 							OS:          "centos", | 							OS:          "centos", | ||||||
| 							Hostname:    "foo", | 							Hostname:    "foo", | ||||||
| @ -1255,7 +1255,7 @@ func Test_expandAlias(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.2"), | 							netip.MustParseAddr("100.64.0.2"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 						HostInfo: HostInfo{ | 						HostInfo: HostInfo{ | ||||||
| 							OS:          "centos", | 							OS:          "centos", | ||||||
| 							Hostname:    "foo", | 							Hostname:    "foo", | ||||||
| @ -1266,13 +1266,13 @@ func Test_expandAlias(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.3"), | 							netip.MustParseAddr("100.64.0.3"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "marc"}, | 						User: User{Name: "marc"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.4"), | 							netip.MustParseAddr("100.64.0.4"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				aclPolicy: ACLPolicy{ | 				aclPolicy: ACLPolicy{ | ||||||
| @ -1308,7 +1308,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { | |||||||
| 	type args struct { | 	type args struct { | ||||||
| 		aclPolicy        ACLPolicy | 		aclPolicy        ACLPolicy | ||||||
| 		nodes            []Machine | 		nodes            []Machine | ||||||
| 		namespace        string | 		user        string | ||||||
| 		stripEmailDomain bool | 		stripEmailDomain bool | ||||||
| 	} | 	} | ||||||
| 	tests := []struct { | 	tests := []struct { | ||||||
| @ -1328,7 +1328,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.1"), | 							netip.MustParseAddr("100.64.0.1"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 						HostInfo: HostInfo{ | 						HostInfo: HostInfo{ | ||||||
| 							OS:          "centos", | 							OS:          "centos", | ||||||
| 							Hostname:    "foo", | 							Hostname:    "foo", | ||||||
| @ -1339,7 +1339,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.2"), | 							netip.MustParseAddr("100.64.0.2"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 						HostInfo: HostInfo{ | 						HostInfo: HostInfo{ | ||||||
| 							OS:          "centos", | 							OS:          "centos", | ||||||
| 							Hostname:    "foo", | 							Hostname:    "foo", | ||||||
| @ -1350,16 +1350,16 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.4"), | 							netip.MustParseAddr("100.64.0.4"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				namespace:        "joe", | 				user:        "joe", | ||||||
| 				stripEmailDomain: true, | 				stripEmailDomain: true, | ||||||
| 			}, | 			}, | ||||||
| 			want: []Machine{ | 			want: []Machine{ | ||||||
| 				{ | 				{ | ||||||
| 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.4")}, | 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.4")}, | ||||||
| 					Namespace:   Namespace{Name: "joe"}, | 					User:   User{Name: "joe"}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| @ -1379,7 +1379,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.1"), | 							netip.MustParseAddr("100.64.0.1"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 						HostInfo: HostInfo{ | 						HostInfo: HostInfo{ | ||||||
| 							OS:          "centos", | 							OS:          "centos", | ||||||
| 							Hostname:    "foo", | 							Hostname:    "foo", | ||||||
| @ -1390,7 +1390,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.2"), | 							netip.MustParseAddr("100.64.0.2"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 						HostInfo: HostInfo{ | 						HostInfo: HostInfo{ | ||||||
| 							OS:          "centos", | 							OS:          "centos", | ||||||
| 							Hostname:    "foo", | 							Hostname:    "foo", | ||||||
| @ -1401,16 +1401,16 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.4"), | 							netip.MustParseAddr("100.64.0.4"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				namespace:        "joe", | 				user:        "joe", | ||||||
| 				stripEmailDomain: true, | 				stripEmailDomain: true, | ||||||
| 			}, | 			}, | ||||||
| 			want: []Machine{ | 			want: []Machine{ | ||||||
| 				{ | 				{ | ||||||
| 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.4")}, | 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.4")}, | ||||||
| 					Namespace:   Namespace{Name: "joe"}, | 					User:   User{Name: "joe"}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| @ -1425,7 +1425,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.1"), | 							netip.MustParseAddr("100.64.0.1"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 						HostInfo: HostInfo{ | 						HostInfo: HostInfo{ | ||||||
| 							OS:          "centos", | 							OS:          "centos", | ||||||
| 							Hostname:    "foo", | 							Hostname:    "foo", | ||||||
| @ -1436,23 +1436,23 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.2"), | 							netip.MustParseAddr("100.64.0.2"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace:  Namespace{Name: "joe"}, | 						User:  User{Name: "joe"}, | ||||||
| 						ForcedTags: []string{"tag:accountant-webserver"}, | 						ForcedTags: []string{"tag:accountant-webserver"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.4"), | 							netip.MustParseAddr("100.64.0.4"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				namespace:        "joe", | 				user:        "joe", | ||||||
| 				stripEmailDomain: true, | 				stripEmailDomain: true, | ||||||
| 			}, | 			}, | ||||||
| 			want: []Machine{ | 			want: []Machine{ | ||||||
| 				{ | 				{ | ||||||
| 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.4")}, | 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.4")}, | ||||||
| 					Namespace:   Namespace{Name: "joe"}, | 					User:   User{Name: "joe"}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| @ -1467,7 +1467,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.1"), | 							netip.MustParseAddr("100.64.0.1"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 						HostInfo: HostInfo{ | 						HostInfo: HostInfo{ | ||||||
| 							OS:          "centos", | 							OS:          "centos", | ||||||
| 							Hostname:    "hr-web1", | 							Hostname:    "hr-web1", | ||||||
| @ -1478,7 +1478,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.2"), | 							netip.MustParseAddr("100.64.0.2"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 						HostInfo: HostInfo{ | 						HostInfo: HostInfo{ | ||||||
| 							OS:          "centos", | 							OS:          "centos", | ||||||
| 							Hostname:    "hr-web2", | 							Hostname:    "hr-web2", | ||||||
| @ -1489,10 +1489,10 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.4"), | 							netip.MustParseAddr("100.64.0.4"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				namespace:        "joe", | 				user:        "joe", | ||||||
| 				stripEmailDomain: true, | 				stripEmailDomain: true, | ||||||
| 			}, | 			}, | ||||||
| 			want: []Machine{ | 			want: []Machine{ | ||||||
| @ -1500,7 +1500,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { | |||||||
| 					IPAddresses: MachineAddresses{ | 					IPAddresses: MachineAddresses{ | ||||||
| 						netip.MustParseAddr("100.64.0.1"), | 						netip.MustParseAddr("100.64.0.1"), | ||||||
| 					}, | 					}, | ||||||
| 					Namespace: Namespace{Name: "joe"}, | 					User: User{Name: "joe"}, | ||||||
| 					HostInfo: HostInfo{ | 					HostInfo: HostInfo{ | ||||||
| 						OS:          "centos", | 						OS:          "centos", | ||||||
| 						Hostname:    "hr-web1", | 						Hostname:    "hr-web1", | ||||||
| @ -1511,7 +1511,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { | |||||||
| 					IPAddresses: MachineAddresses{ | 					IPAddresses: MachineAddresses{ | ||||||
| 						netip.MustParseAddr("100.64.0.2"), | 						netip.MustParseAddr("100.64.0.2"), | ||||||
| 					}, | 					}, | ||||||
| 					Namespace: Namespace{Name: "joe"}, | 					User: User{Name: "joe"}, | ||||||
| 					HostInfo: HostInfo{ | 					HostInfo: HostInfo{ | ||||||
| 						OS:          "centos", | 						OS:          "centos", | ||||||
| 						Hostname:    "hr-web2", | 						Hostname:    "hr-web2", | ||||||
| @ -1522,7 +1522,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { | |||||||
| 					IPAddresses: MachineAddresses{ | 					IPAddresses: MachineAddresses{ | ||||||
| 						netip.MustParseAddr("100.64.0.4"), | 						netip.MustParseAddr("100.64.0.4"), | ||||||
| 					}, | 					}, | ||||||
| 					Namespace: Namespace{Name: "joe"}, | 					User: User{Name: "joe"}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| @ -1532,7 +1532,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) { | |||||||
| 			got := excludeCorrectlyTaggedNodes( | 			got := excludeCorrectlyTaggedNodes( | ||||||
| 				test.args.aclPolicy, | 				test.args.aclPolicy, | ||||||
| 				test.args.nodes, | 				test.args.nodes, | ||||||
| 				test.args.namespace, | 				test.args.user, | ||||||
| 				test.args.stripEmailDomain, | 				test.args.stripEmailDomain, | ||||||
| 			) | 			) | ||||||
| 			if !reflect.DeepEqual(got, test.want) { | 			if !reflect.DeepEqual(got, test.want) { | ||||||
|  | |||||||
| @ -34,7 +34,7 @@ type Groups map[string][]string | |||||||
| // Hosts are alias for IP addresses or subnets. | // Hosts are alias for IP addresses or subnets. | ||||||
| type Hosts map[string]netip.Prefix | type Hosts map[string]netip.Prefix | ||||||
| 
 | 
 | ||||||
| // TagOwners specify what users (namespaces?) are allow to use certain tags. | // TagOwners specify what users (users?) are allow to use certain tags. | ||||||
| type TagOwners map[string][]string | type TagOwners map[string][]string | ||||||
| 
 | 
 | ||||||
| // ACLTest is not implemented, but should be use to check if a certain rule is allowed. | // ACLTest is not implemented, but should be use to check if a certain rule is allowed. | ||||||
| @ -44,7 +44,7 @@ type ACLTest struct { | |||||||
| 	Deny   []string `json:"deny,omitempty" yaml:"deny,omitempty"` | 	Deny   []string `json:"deny,omitempty" yaml:"deny,omitempty"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // AutoApprovers specify which users (namespaces?), groups or tags have their advertised routes | // AutoApprovers specify which users (users?), groups or tags have their advertised routes | ||||||
| // or exit node status automatically enabled. | // or exit node status automatically enabled. | ||||||
| type AutoApprovers struct { | type AutoApprovers struct { | ||||||
| 	Routes   map[string][]string `json:"routes"   yaml:"routes"` | 	Routes   map[string][]string `json:"routes"   yaml:"routes"` | ||||||
| @ -119,7 +119,7 @@ func (policy ACLPolicy) IsZero() bool { | |||||||
| 	return false | 	return false | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Returns the list of autoApproving namespaces, groups or tags for a given IPPrefix. | // Returns the list of autoApproving users, groups or tags for a given IPPrefix. | ||||||
| func (autoApprovers *AutoApprovers) GetRouteApprovers( | func (autoApprovers *AutoApprovers) GetRouteApprovers( | ||||||
| 	prefix netip.Prefix, | 	prefix netip.Prefix, | ||||||
| ) ([]string, error) { | ) ([]string, error) { | ||||||
|  | |||||||
| @ -29,7 +29,7 @@ type APIKey struct { | |||||||
| 	LastSeen   *time.Time | 	LastSeen   *time.Time | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // CreateAPIKey creates a new ApiKey in a namespace, and returns it. | // CreateAPIKey creates a new ApiKey in a user, and returns it. | ||||||
| func (h *Headscale) CreateAPIKey( | func (h *Headscale) CreateAPIKey( | ||||||
| 	expiration *time.Time, | 	expiration *time.Time, | ||||||
| ) (string, *APIKey, error) { | ) (string, *APIKey, error) { | ||||||
| @ -64,7 +64,7 @@ func (h *Headscale) CreateAPIKey( | |||||||
| 	return keyStr, &key, nil | 	return keyStr, &key, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ListAPIKeys returns the list of ApiKeys for a namespace. | // ListAPIKeys returns the list of ApiKeys for a user. | ||||||
| func (h *Headscale) ListAPIKeys() ([]APIKey, error) { | func (h *Headscale) ListAPIKeys() ([]APIKey, error) { | ||||||
| 	keys := []APIKey{} | 	keys := []APIKey{} | ||||||
| 	if err := h.db.Find(&keys).Error; err != nil { | 	if err := h.db.Find(&keys).Error; err != nil { | ||||||
|  | |||||||
							
								
								
									
										48
									
								
								app.go
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								app.go
									
									
									
									
									
								
							| @ -238,20 +238,20 @@ func (h *Headscale) failoverSubnetRoutes(milliSeconds int64) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (h *Headscale) expireEphemeralNodesWorker() { | func (h *Headscale) expireEphemeralNodesWorker() { | ||||||
| 	namespaces, err := h.ListNamespaces() | 	users, err := h.ListUsers() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error().Err(err).Msg("Error listing namespaces") | 		log.Error().Err(err).Msg("Error listing users") | ||||||
| 
 | 
 | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for _, namespace := range namespaces { | 	for _, user := range users { | ||||||
| 		machines, err := h.ListMachinesInNamespace(namespace.Name) | 		machines, err := h.ListMachinesByUser(user.Name) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Error(). | 			log.Error(). | ||||||
| 				Err(err). | 				Err(err). | ||||||
| 				Str("namespace", namespace.Name). | 				Str("user", user.Name). | ||||||
| 				Msg("Error listing machines in namespace") | 				Msg("Error listing machines in user") | ||||||
| 
 | 
 | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @ -283,20 +283,20 @@ func (h *Headscale) expireEphemeralNodesWorker() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (h *Headscale) expireExpiredMachinesWorker() { | func (h *Headscale) expireExpiredMachinesWorker() { | ||||||
| 	namespaces, err := h.ListNamespaces() | 	users, err := h.ListUsers() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error().Err(err).Msg("Error listing namespaces") | 		log.Error().Err(err).Msg("Error listing users") | ||||||
| 
 | 
 | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for _, namespace := range namespaces { | 	for _, user := range users { | ||||||
| 		machines, err := h.ListMachinesInNamespace(namespace.Name) | 		machines, err := h.ListMachinesByUser(user.Name) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Error(). | 			log.Error(). | ||||||
| 				Err(err). | 				Err(err). | ||||||
| 				Str("namespace", namespace.Name). | 				Str("user", user.Name). | ||||||
| 				Msg("Error listing machines in namespace") | 				Msg("Error listing machines in user") | ||||||
| 
 | 
 | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @ -304,7 +304,7 @@ func (h *Headscale) expireExpiredMachinesWorker() { | |||||||
| 		expiredFound := false | 		expiredFound := false | ||||||
| 		for index, machine := range machines { | 		for index, machine := range machines { | ||||||
| 			if machine.isExpired() && | 			if machine.isExpired() && | ||||||
| 				machine.Expiry.After(h.getLastStateChange(namespace)) { | 				machine.Expiry.After(h.getLastStateChange(user)) { | ||||||
| 				expiredFound = true | 				expiredFound = true | ||||||
| 
 | 
 | ||||||
| 				err := h.ExpireMachine(&machines[index]) | 				err := h.ExpireMachine(&machines[index]) | ||||||
| @ -908,31 +908,31 @@ func (h *Headscale) setLastStateChangeToNow() { | |||||||
| 
 | 
 | ||||||
| 	now := time.Now().UTC() | 	now := time.Now().UTC() | ||||||
| 
 | 
 | ||||||
| 	namespaces, err := h.ListNamespaces() | 	users, err := h.ListUsers() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error(). | 		log.Error(). | ||||||
| 			Caller(). | 			Caller(). | ||||||
| 			Err(err). | 			Err(err). | ||||||
| 			Msg("failed to fetch all namespaces, failing to update last changed state.") | 			Msg("failed to fetch all users, failing to update last changed state.") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for _, namespace := range namespaces { | 	for _, user := range users { | ||||||
| 		lastStateUpdate.WithLabelValues(namespace.Name, "headscale").Set(float64(now.Unix())) | 		lastStateUpdate.WithLabelValues(user.Name, "headscale").Set(float64(now.Unix())) | ||||||
| 		if h.lastStateChange == nil { | 		if h.lastStateChange == nil { | ||||||
| 			h.lastStateChange = xsync.NewMapOf[time.Time]() | 			h.lastStateChange = xsync.NewMapOf[time.Time]() | ||||||
| 		} | 		} | ||||||
| 		h.lastStateChange.Store(namespace.Name, now) | 		h.lastStateChange.Store(user.Name, now) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (h *Headscale) getLastStateChange(namespaces ...Namespace) time.Time { | func (h *Headscale) getLastStateChange(users ...User) time.Time { | ||||||
| 	times := []time.Time{} | 	times := []time.Time{} | ||||||
| 
 | 
 | ||||||
| 	// getLastStateChange takes a list of namespaces as a "filter", if no namespaces | 	// getLastStateChange takes a list of users as a "filter", if no users | ||||||
| 	// are past, then use the entier list of namespaces and look for the last update | 	// are past, then use the entier list of users and look for the last update | ||||||
| 	if len(namespaces) > 0 { | 	if len(users) > 0 { | ||||||
| 		for _, namespace := range namespaces { | 		for _, user := range users { | ||||||
| 			if lastChange, ok := h.lastStateChange.Load(namespace.Name); ok { | 			if lastChange, ok := h.lastStateChange.Load(user.Name); ok { | ||||||
| 				times = append(times, lastChange) | 				times = append(times, lastChange) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -77,7 +77,7 @@ func main() { | |||||||
| 		"TestCreateTailscale", | 		"TestCreateTailscale", | ||||||
| 		"TestEnablingRoutes", | 		"TestEnablingRoutes", | ||||||
| 		"TestHeadscale", | 		"TestHeadscale", | ||||||
| 		"TestNamespaceCommand", | 		"TestUserCommand", | ||||||
| 		"TestOIDCAuthenticationPingAll", | 		"TestOIDCAuthenticationPingAll", | ||||||
| 		"TestOIDCExpireNodes", | 		"TestOIDCExpireNodes", | ||||||
| 		"TestPingAllByHostname", | 		"TestPingAllByHostname", | ||||||
| @ -87,10 +87,10 @@ func main() { | |||||||
| 		"TestPreAuthKeyCommandWithoutExpiry", | 		"TestPreAuthKeyCommandWithoutExpiry", | ||||||
| 		"TestResolveMagicDNS", | 		"TestResolveMagicDNS", | ||||||
| 		"TestSSHIsBlockedInACL", | 		"TestSSHIsBlockedInACL", | ||||||
| 		"TestSSHMultipleNamespacesAllToAll", | 		"TestSSHMultipleUsersAllToAll", | ||||||
| 		"TestSSHNoSSHConfigured", | 		"TestSSHNoSSHConfigured", | ||||||
| 		"TestSSHOneNamespaceAllToAll", | 		"TestSSHOneUserAllToAll", | ||||||
| 		"TestSSNamespaceOnlyIsolation", | 		"TestSSUserOnlyIsolation", | ||||||
| 		"TestTaildrop", | 		"TestTaildrop", | ||||||
| 		"TestTailscaleNodesJoiningHeadcale", | 		"TestTailscaleNodesJoiningHeadcale", | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -27,8 +27,8 @@ func init() { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal().Err(err).Msg("") | 		log.Fatal().Err(err).Msg("") | ||||||
| 	} | 	} | ||||||
| 	createNodeCmd.Flags().StringP("namespace", "n", "", "Namespace") | 	createNodeCmd.Flags().StringP("user", "n", "", "User") | ||||||
| 	err = createNodeCmd.MarkFlagRequired("namespace") | 	err = createNodeCmd.MarkFlagRequired("user") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal().Err(err).Msg("") | 		log.Fatal().Err(err).Msg("") | ||||||
| 	} | 	} | ||||||
| @ -55,9 +55,9 @@ var createNodeCmd = &cobra.Command{ | |||||||
| 	Run: func(cmd *cobra.Command, args []string) { | 	Run: func(cmd *cobra.Command, args []string) { | ||||||
| 		output, _ := cmd.Flags().GetString("output") | 		output, _ := cmd.Flags().GetString("output") | ||||||
| 
 | 
 | ||||||
| 		namespace, err := cmd.Flags().GetString("namespace") | 		user, err := cmd.Flags().GetString("user") | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ErrorOutput(err, fmt.Sprintf("Error getting namespace: %s", err), output) | 			ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output) | ||||||
| 
 | 
 | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @ -112,7 +112,7 @@ var createNodeCmd = &cobra.Command{ | |||||||
| 		request := &v1.DebugCreateMachineRequest{ | 		request := &v1.DebugCreateMachineRequest{ | ||||||
| 			Key:       machineKey, | 			Key:       machineKey, | ||||||
| 			Name:      name, | 			Name:      name, | ||||||
| 			Namespace: namespace, | 			User: user, | ||||||
| 			Routes:    routes, | 			Routes:    routes, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -13,26 +13,26 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func init() { | func init() { | ||||||
| 	rootCmd.AddCommand(namespaceCmd) | 	rootCmd.AddCommand(userCmd) | ||||||
| 	namespaceCmd.AddCommand(createNamespaceCmd) | 	userCmd.AddCommand(createUserCmd) | ||||||
| 	namespaceCmd.AddCommand(listNamespacesCmd) | 	userCmd.AddCommand(listUsersCmd) | ||||||
| 	namespaceCmd.AddCommand(destroyNamespaceCmd) | 	userCmd.AddCommand(destroyUserCmd) | ||||||
| 	namespaceCmd.AddCommand(renameNamespaceCmd) | 	userCmd.AddCommand(renameUserCmd) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| 	errMissingParameter = headscale.Error("missing parameters") | 	errMissingParameter = headscale.Error("missing parameters") | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var namespaceCmd = &cobra.Command{ | var userCmd = &cobra.Command{ | ||||||
| 	Use:     "namespaces", | 	Use:     "users", | ||||||
| 	Short:   "Manage the namespaces of Headscale", | 	Short:   "Manage the users of Headscale", | ||||||
| 	Aliases: []string{"namespace", "ns", "user", "users"}, | 	Aliases: []string{"user", "ns", "user", "users"}, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var createNamespaceCmd = &cobra.Command{ | var createUserCmd = &cobra.Command{ | ||||||
| 	Use:     "create NAME", | 	Use:     "create NAME", | ||||||
| 	Short:   "Creates a new namespace", | 	Short:   "Creates a new user", | ||||||
| 	Aliases: []string{"c", "new"}, | 	Aliases: []string{"c", "new"}, | ||||||
| 	Args: func(cmd *cobra.Command, args []string) error { | 	Args: func(cmd *cobra.Command, args []string) error { | ||||||
| 		if len(args) < 1 { | 		if len(args) < 1 { | ||||||
| @ -44,7 +44,7 @@ var createNamespaceCmd = &cobra.Command{ | |||||||
| 	Run: func(cmd *cobra.Command, args []string) { | 	Run: func(cmd *cobra.Command, args []string) { | ||||||
| 		output, _ := cmd.Flags().GetString("output") | 		output, _ := cmd.Flags().GetString("output") | ||||||
| 
 | 
 | ||||||
| 		namespaceName := args[0] | 		userName := args[0] | ||||||
| 
 | 
 | ||||||
| 		ctx, client, conn, cancel := getHeadscaleCLIClient() | 		ctx, client, conn, cancel := getHeadscaleCLIClient() | ||||||
| 		defer cancel() | 		defer cancel() | ||||||
| @ -52,15 +52,15 @@ var createNamespaceCmd = &cobra.Command{ | |||||||
| 
 | 
 | ||||||
| 		log.Trace().Interface("client", client).Msg("Obtained gRPC client") | 		log.Trace().Interface("client", client).Msg("Obtained gRPC client") | ||||||
| 
 | 
 | ||||||
| 		request := &v1.CreateNamespaceRequest{Name: namespaceName} | 		request := &v1.CreateUserRequest{Name: userName} | ||||||
| 
 | 
 | ||||||
| 		log.Trace().Interface("request", request).Msg("Sending CreateNamespace request") | 		log.Trace().Interface("request", request).Msg("Sending CreateUser request") | ||||||
| 		response, err := client.CreateNamespace(ctx, request) | 		response, err := client.CreateUser(ctx, request) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ErrorOutput( | 			ErrorOutput( | ||||||
| 				err, | 				err, | ||||||
| 				fmt.Sprintf( | 				fmt.Sprintf( | ||||||
| 					"Cannot create namespace: %s", | 					"Cannot create user: %s", | ||||||
| 					status.Convert(err).Message(), | 					status.Convert(err).Message(), | ||||||
| 				), | 				), | ||||||
| 				output, | 				output, | ||||||
| @ -69,13 +69,13 @@ var createNamespaceCmd = &cobra.Command{ | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		SuccessOutput(response.Namespace, "Namespace created", output) | 		SuccessOutput(response.User, "User created", output) | ||||||
| 	}, | 	}, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var destroyNamespaceCmd = &cobra.Command{ | var destroyUserCmd = &cobra.Command{ | ||||||
| 	Use:     "destroy NAME", | 	Use:     "destroy NAME", | ||||||
| 	Short:   "Destroys a namespace", | 	Short:   "Destroys a user", | ||||||
| 	Aliases: []string{"delete"}, | 	Aliases: []string{"delete"}, | ||||||
| 	Args: func(cmd *cobra.Command, args []string) error { | 	Args: func(cmd *cobra.Command, args []string) error { | ||||||
| 		if len(args) < 1 { | 		if len(args) < 1 { | ||||||
| @ -87,17 +87,17 @@ var destroyNamespaceCmd = &cobra.Command{ | |||||||
| 	Run: func(cmd *cobra.Command, args []string) { | 	Run: func(cmd *cobra.Command, args []string) { | ||||||
| 		output, _ := cmd.Flags().GetString("output") | 		output, _ := cmd.Flags().GetString("output") | ||||||
| 
 | 
 | ||||||
| 		namespaceName := args[0] | 		userName := args[0] | ||||||
| 
 | 
 | ||||||
| 		request := &v1.GetNamespaceRequest{ | 		request := &v1.GetUserRequest{ | ||||||
| 			Name: namespaceName, | 			Name: userName, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		ctx, client, conn, cancel := getHeadscaleCLIClient() | 		ctx, client, conn, cancel := getHeadscaleCLIClient() | ||||||
| 		defer cancel() | 		defer cancel() | ||||||
| 		defer conn.Close() | 		defer conn.Close() | ||||||
| 
 | 
 | ||||||
| 		_, err := client.GetNamespace(ctx, request) | 		_, err := client.GetUser(ctx, request) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ErrorOutput( | 			ErrorOutput( | ||||||
| 				err, | 				err, | ||||||
| @ -113,8 +113,8 @@ var destroyNamespaceCmd = &cobra.Command{ | |||||||
| 		if !force { | 		if !force { | ||||||
| 			prompt := &survey.Confirm{ | 			prompt := &survey.Confirm{ | ||||||
| 				Message: fmt.Sprintf( | 				Message: fmt.Sprintf( | ||||||
| 					"Do you want to remove the namespace '%s' and any associated preauthkeys?", | 					"Do you want to remove the user '%s' and any associated preauthkeys?", | ||||||
| 					namespaceName, | 					userName, | ||||||
| 				), | 				), | ||||||
| 			} | 			} | ||||||
| 			err := survey.AskOne(prompt, &confirm) | 			err := survey.AskOne(prompt, &confirm) | ||||||
| @ -124,14 +124,14 @@ var destroyNamespaceCmd = &cobra.Command{ | |||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if confirm || force { | 		if confirm || force { | ||||||
| 			request := &v1.DeleteNamespaceRequest{Name: namespaceName} | 			request := &v1.DeleteUserRequest{Name: userName} | ||||||
| 
 | 
 | ||||||
| 			response, err := client.DeleteNamespace(ctx, request) | 			response, err := client.DeleteUser(ctx, request) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				ErrorOutput( | 				ErrorOutput( | ||||||
| 					err, | 					err, | ||||||
| 					fmt.Sprintf( | 					fmt.Sprintf( | ||||||
| 						"Cannot destroy namespace: %s", | 						"Cannot destroy user: %s", | ||||||
| 						status.Convert(err).Message(), | 						status.Convert(err).Message(), | ||||||
| 					), | 					), | ||||||
| 					output, | 					output, | ||||||
| @ -139,16 +139,16 @@ var destroyNamespaceCmd = &cobra.Command{ | |||||||
| 
 | 
 | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			SuccessOutput(response, "Namespace destroyed", output) | 			SuccessOutput(response, "User destroyed", output) | ||||||
| 		} else { | 		} else { | ||||||
| 			SuccessOutput(map[string]string{"Result": "Namespace not destroyed"}, "Namespace not destroyed", output) | 			SuccessOutput(map[string]string{"Result": "User not destroyed"}, "User not destroyed", output) | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var listNamespacesCmd = &cobra.Command{ | var listUsersCmd = &cobra.Command{ | ||||||
| 	Use:     "list", | 	Use:     "list", | ||||||
| 	Short:   "List all the namespaces", | 	Short:   "List all the users", | ||||||
| 	Aliases: []string{"ls", "show"}, | 	Aliases: []string{"ls", "show"}, | ||||||
| 	Run: func(cmd *cobra.Command, args []string) { | 	Run: func(cmd *cobra.Command, args []string) { | ||||||
| 		output, _ := cmd.Flags().GetString("output") | 		output, _ := cmd.Flags().GetString("output") | ||||||
| @ -157,13 +157,13 @@ var listNamespacesCmd = &cobra.Command{ | |||||||
| 		defer cancel() | 		defer cancel() | ||||||
| 		defer conn.Close() | 		defer conn.Close() | ||||||
| 
 | 
 | ||||||
| 		request := &v1.ListNamespacesRequest{} | 		request := &v1.ListUsersRequest{} | ||||||
| 
 | 
 | ||||||
| 		response, err := client.ListNamespaces(ctx, request) | 		response, err := client.ListUsers(ctx, request) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ErrorOutput( | 			ErrorOutput( | ||||||
| 				err, | 				err, | ||||||
| 				fmt.Sprintf("Cannot get namespaces: %s", status.Convert(err).Message()), | 				fmt.Sprintf("Cannot get users: %s", status.Convert(err).Message()), | ||||||
| 				output, | 				output, | ||||||
| 			) | 			) | ||||||
| 
 | 
 | ||||||
| @ -171,19 +171,19 @@ var listNamespacesCmd = &cobra.Command{ | |||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if output != "" { | 		if output != "" { | ||||||
| 			SuccessOutput(response.Namespaces, "", output) | 			SuccessOutput(response.Users, "", output) | ||||||
| 
 | 
 | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		tableData := pterm.TableData{{"ID", "Name", "Created"}} | 		tableData := pterm.TableData{{"ID", "Name", "Created"}} | ||||||
| 		for _, namespace := range response.GetNamespaces() { | 		for _, user := range response.GetUsers() { | ||||||
| 			tableData = append( | 			tableData = append( | ||||||
| 				tableData, | 				tableData, | ||||||
| 				[]string{ | 				[]string{ | ||||||
| 					namespace.GetId(), | 					user.GetId(), | ||||||
| 					namespace.GetName(), | 					user.GetName(), | ||||||
| 					namespace.GetCreatedAt().AsTime().Format("2006-01-02 15:04:05"), | 					user.GetCreatedAt().AsTime().Format("2006-01-02 15:04:05"), | ||||||
| 				}, | 				}, | ||||||
| 			) | 			) | ||||||
| 		} | 		} | ||||||
| @ -200,9 +200,9 @@ var listNamespacesCmd = &cobra.Command{ | |||||||
| 	}, | 	}, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var renameNamespaceCmd = &cobra.Command{ | var renameUserCmd = &cobra.Command{ | ||||||
| 	Use:     "rename OLD_NAME NEW_NAME", | 	Use:     "rename OLD_NAME NEW_NAME", | ||||||
| 	Short:   "Renames a namespace", | 	Short:   "Renames a user", | ||||||
| 	Aliases: []string{"mv"}, | 	Aliases: []string{"mv"}, | ||||||
| 	Args: func(cmd *cobra.Command, args []string) error { | 	Args: func(cmd *cobra.Command, args []string) error { | ||||||
| 		expectedArguments := 2 | 		expectedArguments := 2 | ||||||
| @ -219,17 +219,17 @@ var renameNamespaceCmd = &cobra.Command{ | |||||||
| 		defer cancel() | 		defer cancel() | ||||||
| 		defer conn.Close() | 		defer conn.Close() | ||||||
| 
 | 
 | ||||||
| 		request := &v1.RenameNamespaceRequest{ | 		request := &v1.RenameUserRequest{ | ||||||
| 			OldName: args[0], | 			OldName: args[0], | ||||||
| 			NewName: args[1], | 			NewName: args[1], | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		response, err := client.RenameNamespace(ctx, request) | 		response, err := client.RenameUser(ctx, request) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ErrorOutput( | 			ErrorOutput( | ||||||
| 				err, | 				err, | ||||||
| 				fmt.Sprintf( | 				fmt.Sprintf( | ||||||
| 					"Cannot rename namespace: %s", | 					"Cannot rename user: %s", | ||||||
| 					status.Convert(err).Message(), | 					status.Convert(err).Message(), | ||||||
| 				), | 				), | ||||||
| 				output, | 				output, | ||||||
| @ -238,6 +238,6 @@ var renameNamespaceCmd = &cobra.Command{ | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		SuccessOutput(response.Namespace, "Namespace renamed", output) | 		SuccessOutput(response.User, "User renamed", output) | ||||||
| 	}, | 	}, | ||||||
| } | } | ||||||
|  | |||||||
| @ -19,12 +19,12 @@ import ( | |||||||
| 
 | 
 | ||||||
| func init() { | func init() { | ||||||
| 	rootCmd.AddCommand(nodeCmd) | 	rootCmd.AddCommand(nodeCmd) | ||||||
| 	listNodesCmd.Flags().StringP("namespace", "n", "", "Filter by namespace") | 	listNodesCmd.Flags().StringP("user", "n", "", "Filter by user") | ||||||
| 	listNodesCmd.Flags().BoolP("tags", "t", false, "Show tags") | 	listNodesCmd.Flags().BoolP("tags", "t", false, "Show tags") | ||||||
| 	nodeCmd.AddCommand(listNodesCmd) | 	nodeCmd.AddCommand(listNodesCmd) | ||||||
| 
 | 
 | ||||||
| 	registerNodeCmd.Flags().StringP("namespace", "n", "", "Namespace") | 	registerNodeCmd.Flags().StringP("user", "n", "", "User") | ||||||
| 	err := registerNodeCmd.MarkFlagRequired("namespace") | 	err := registerNodeCmd.MarkFlagRequired("user") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatalf(err.Error()) | 		log.Fatalf(err.Error()) | ||||||
| 	} | 	} | ||||||
| @ -63,9 +63,9 @@ func init() { | |||||||
| 		log.Fatalf(err.Error()) | 		log.Fatalf(err.Error()) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	moveNodeCmd.Flags().StringP("namespace", "n", "", "New namespace") | 	moveNodeCmd.Flags().StringP("user", "n", "", "New user") | ||||||
| 
 | 
 | ||||||
| 	err = moveNodeCmd.MarkFlagRequired("namespace") | 	err = moveNodeCmd.MarkFlagRequired("user") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatalf(err.Error()) | 		log.Fatalf(err.Error()) | ||||||
| 	} | 	} | ||||||
| @ -93,9 +93,9 @@ var registerNodeCmd = &cobra.Command{ | |||||||
| 	Short: "Registers a machine to your network", | 	Short: "Registers a machine to your network", | ||||||
| 	Run: func(cmd *cobra.Command, args []string) { | 	Run: func(cmd *cobra.Command, args []string) { | ||||||
| 		output, _ := cmd.Flags().GetString("output") | 		output, _ := cmd.Flags().GetString("output") | ||||||
| 		namespace, err := cmd.Flags().GetString("namespace") | 		user, err := cmd.Flags().GetString("user") | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ErrorOutput(err, fmt.Sprintf("Error getting namespace: %s", err), output) | 			ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output) | ||||||
| 
 | 
 | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @ -117,7 +117,7 @@ var registerNodeCmd = &cobra.Command{ | |||||||
| 
 | 
 | ||||||
| 		request := &v1.RegisterMachineRequest{ | 		request := &v1.RegisterMachineRequest{ | ||||||
| 			Key:       machineKey, | 			Key:       machineKey, | ||||||
| 			Namespace: namespace, | 			User: user, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		response, err := client.RegisterMachine(ctx, request) | 		response, err := client.RegisterMachine(ctx, request) | ||||||
| @ -146,9 +146,9 @@ var listNodesCmd = &cobra.Command{ | |||||||
| 	Aliases: []string{"ls", "show"}, | 	Aliases: []string{"ls", "show"}, | ||||||
| 	Run: func(cmd *cobra.Command, args []string) { | 	Run: func(cmd *cobra.Command, args []string) { | ||||||
| 		output, _ := cmd.Flags().GetString("output") | 		output, _ := cmd.Flags().GetString("output") | ||||||
| 		namespace, err := cmd.Flags().GetString("namespace") | 		user, err := cmd.Flags().GetString("user") | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ErrorOutput(err, fmt.Sprintf("Error getting namespace: %s", err), output) | 			ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output) | ||||||
| 
 | 
 | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @ -164,7 +164,7 @@ var listNodesCmd = &cobra.Command{ | |||||||
| 		defer conn.Close() | 		defer conn.Close() | ||||||
| 
 | 
 | ||||||
| 		request := &v1.ListMachinesRequest{ | 		request := &v1.ListMachinesRequest{ | ||||||
| 			Namespace: namespace, | 			User: user, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		response, err := client.ListMachines(ctx, request) | 		response, err := client.ListMachines(ctx, request) | ||||||
| @ -184,7 +184,7 @@ var listNodesCmd = &cobra.Command{ | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		tableData, err := nodesToPtables(namespace, showTags, response.Machines) | 		tableData, err := nodesToPtables(user, showTags, response.Machines) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ErrorOutput(err, fmt.Sprintf("Error converting to table: %s", err), output) | 			ErrorOutput(err, fmt.Sprintf("Error converting to table: %s", err), output) | ||||||
| 
 | 
 | ||||||
| @ -388,7 +388,7 @@ var deleteNodeCmd = &cobra.Command{ | |||||||
| 
 | 
 | ||||||
| var moveNodeCmd = &cobra.Command{ | var moveNodeCmd = &cobra.Command{ | ||||||
| 	Use:     "move", | 	Use:     "move", | ||||||
| 	Short:   "Move node to another namespace", | 	Short:   "Move node to another user", | ||||||
| 	Aliases: []string{"mv"}, | 	Aliases: []string{"mv"}, | ||||||
| 	Run: func(cmd *cobra.Command, args []string) { | 	Run: func(cmd *cobra.Command, args []string) { | ||||||
| 		output, _ := cmd.Flags().GetString("output") | 		output, _ := cmd.Flags().GetString("output") | ||||||
| @ -404,11 +404,11 @@ var moveNodeCmd = &cobra.Command{ | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		namespace, err := cmd.Flags().GetString("namespace") | 		user, err := cmd.Flags().GetString("user") | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ErrorOutput( | 			ErrorOutput( | ||||||
| 				err, | 				err, | ||||||
| 				fmt.Sprintf("Error getting namespace: %s", err), | 				fmt.Sprintf("Error getting user: %s", err), | ||||||
| 				output, | 				output, | ||||||
| 			) | 			) | ||||||
| 
 | 
 | ||||||
| @ -439,7 +439,7 @@ var moveNodeCmd = &cobra.Command{ | |||||||
| 
 | 
 | ||||||
| 		moveRequest := &v1.MoveMachineRequest{ | 		moveRequest := &v1.MoveMachineRequest{ | ||||||
| 			MachineId: identifier, | 			MachineId: identifier, | ||||||
| 			Namespace: namespace, | 			User: user, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		moveResponse, err := client.MoveMachine(ctx, moveRequest) | 		moveResponse, err := client.MoveMachine(ctx, moveRequest) | ||||||
| @ -456,12 +456,12 @@ var moveNodeCmd = &cobra.Command{ | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		SuccessOutput(moveResponse.Machine, "Node moved to another namespace", output) | 		SuccessOutput(moveResponse.Machine, "Node moved to another user", output) | ||||||
| 	}, | 	}, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func nodesToPtables( | func nodesToPtables( | ||||||
| 	currentNamespace string, | 	currentUser string, | ||||||
| 	showTags bool, | 	showTags bool, | ||||||
| 	machines []*v1.Machine, | 	machines []*v1.Machine, | ||||||
| ) (pterm.TableData, error) { | ) (pterm.TableData, error) { | ||||||
| @ -471,7 +471,7 @@ func nodesToPtables( | |||||||
| 		"Name", | 		"Name", | ||||||
| 		"MachineKey", | 		"MachineKey", | ||||||
| 		"NodeKey", | 		"NodeKey", | ||||||
| 		"Namespace", | 		"User", | ||||||
| 		"IP addresses", | 		"IP addresses", | ||||||
| 		"Ephemeral", | 		"Ephemeral", | ||||||
| 		"Last seen", | 		"Last seen", | ||||||
| @ -560,12 +560,12 @@ func nodesToPtables( | |||||||
| 		} | 		} | ||||||
| 		validTags = strings.TrimLeft(validTags, ",") | 		validTags = strings.TrimLeft(validTags, ",") | ||||||
| 
 | 
 | ||||||
| 		var namespace string | 		var user string | ||||||
| 		if currentNamespace == "" || (currentNamespace == machine.Namespace.Name) { | 		if currentUser == "" || (currentUser == machine.User.Name) { | ||||||
| 			namespace = pterm.LightMagenta(machine.Namespace.Name) | 			user = pterm.LightMagenta(machine.User.Name) | ||||||
| 		} else { | 		} else { | ||||||
| 			// Shared into this namespace | 			// Shared into this user | ||||||
| 			namespace = pterm.LightYellow(machine.Namespace.Name) | 			user = pterm.LightYellow(machine.User.Name) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		var IPV4Address string | 		var IPV4Address string | ||||||
| @ -584,7 +584,7 @@ func nodesToPtables( | |||||||
| 			machine.GetGivenName(), | 			machine.GetGivenName(), | ||||||
| 			machineKey.ShortString(), | 			machineKey.ShortString(), | ||||||
| 			nodeKey.ShortString(), | 			nodeKey.ShortString(), | ||||||
| 			namespace, | 			user, | ||||||
| 			strings.Join([]string{IPV4Address, IPV6Address}, ", "), | 			strings.Join([]string{IPV4Address, IPV6Address}, ", "), | ||||||
| 			strconv.FormatBool(ephemeral), | 			strconv.FormatBool(ephemeral), | ||||||
| 			lastSeenTime, | 			lastSeenTime, | ||||||
|  | |||||||
| @ -20,8 +20,8 @@ const ( | |||||||
| 
 | 
 | ||||||
| func init() { | func init() { | ||||||
| 	rootCmd.AddCommand(preauthkeysCmd) | 	rootCmd.AddCommand(preauthkeysCmd) | ||||||
| 	preauthkeysCmd.PersistentFlags().StringP("namespace", "n", "", "Namespace") | 	preauthkeysCmd.PersistentFlags().StringP("user", "n", "", "User") | ||||||
| 	err := preauthkeysCmd.MarkPersistentFlagRequired("namespace") | 	err := preauthkeysCmd.MarkPersistentFlagRequired("user") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal().Err(err).Msg("") | 		log.Fatal().Err(err).Msg("") | ||||||
| 	} | 	} | ||||||
| @ -46,14 +46,14 @@ var preauthkeysCmd = &cobra.Command{ | |||||||
| 
 | 
 | ||||||
| var listPreAuthKeys = &cobra.Command{ | var listPreAuthKeys = &cobra.Command{ | ||||||
| 	Use:     "list", | 	Use:     "list", | ||||||
| 	Short:   "List the preauthkeys for this namespace", | 	Short:   "List the preauthkeys for this user", | ||||||
| 	Aliases: []string{"ls", "show"}, | 	Aliases: []string{"ls", "show"}, | ||||||
| 	Run: func(cmd *cobra.Command, args []string) { | 	Run: func(cmd *cobra.Command, args []string) { | ||||||
| 		output, _ := cmd.Flags().GetString("output") | 		output, _ := cmd.Flags().GetString("output") | ||||||
| 
 | 
 | ||||||
| 		namespace, err := cmd.Flags().GetString("namespace") | 		user, err := cmd.Flags().GetString("user") | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ErrorOutput(err, fmt.Sprintf("Error getting namespace: %s", err), output) | 			ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output) | ||||||
| 
 | 
 | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @ -63,7 +63,7 @@ var listPreAuthKeys = &cobra.Command{ | |||||||
| 		defer conn.Close() | 		defer conn.Close() | ||||||
| 
 | 
 | ||||||
| 		request := &v1.ListPreAuthKeysRequest{ | 		request := &v1.ListPreAuthKeysRequest{ | ||||||
| 			Namespace: namespace, | 			User: user, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		response, err := client.ListPreAuthKeys(ctx, request) | 		response, err := client.ListPreAuthKeys(ctx, request) | ||||||
| @ -143,14 +143,14 @@ var listPreAuthKeys = &cobra.Command{ | |||||||
| 
 | 
 | ||||||
| var createPreAuthKeyCmd = &cobra.Command{ | var createPreAuthKeyCmd = &cobra.Command{ | ||||||
| 	Use:     "create", | 	Use:     "create", | ||||||
| 	Short:   "Creates a new preauthkey in the specified namespace", | 	Short:   "Creates a new preauthkey in the specified user", | ||||||
| 	Aliases: []string{"c", "new"}, | 	Aliases: []string{"c", "new"}, | ||||||
| 	Run: func(cmd *cobra.Command, args []string) { | 	Run: func(cmd *cobra.Command, args []string) { | ||||||
| 		output, _ := cmd.Flags().GetString("output") | 		output, _ := cmd.Flags().GetString("output") | ||||||
| 
 | 
 | ||||||
| 		namespace, err := cmd.Flags().GetString("namespace") | 		user, err := cmd.Flags().GetString("user") | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ErrorOutput(err, fmt.Sprintf("Error getting namespace: %s", err), output) | 			ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output) | ||||||
| 
 | 
 | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @ -162,11 +162,11 @@ var createPreAuthKeyCmd = &cobra.Command{ | |||||||
| 		log.Trace(). | 		log.Trace(). | ||||||
| 			Bool("reusable", reusable). | 			Bool("reusable", reusable). | ||||||
| 			Bool("ephemeral", ephemeral). | 			Bool("ephemeral", ephemeral). | ||||||
| 			Str("namespace", namespace). | 			Str("user", user). | ||||||
| 			Msg("Preparing to create preauthkey") | 			Msg("Preparing to create preauthkey") | ||||||
| 
 | 
 | ||||||
| 		request := &v1.CreatePreAuthKeyRequest{ | 		request := &v1.CreatePreAuthKeyRequest{ | ||||||
| 			Namespace: namespace, | 			User: user, | ||||||
| 			Reusable:  reusable, | 			Reusable:  reusable, | ||||||
| 			Ephemeral: ephemeral, | 			Ephemeral: ephemeral, | ||||||
| 			AclTags:   tags, | 			AclTags:   tags, | ||||||
| @ -225,9 +225,9 @@ var expirePreAuthKeyCmd = &cobra.Command{ | |||||||
| 	}, | 	}, | ||||||
| 	Run: func(cmd *cobra.Command, args []string) { | 	Run: func(cmd *cobra.Command, args []string) { | ||||||
| 		output, _ := cmd.Flags().GetString("output") | 		output, _ := cmd.Flags().GetString("output") | ||||||
| 		namespace, err := cmd.Flags().GetString("namespace") | 		user, err := cmd.Flags().GetString("user") | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ErrorOutput(err, fmt.Sprintf("Error getting namespace: %s", err), output) | 			ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output) | ||||||
| 
 | 
 | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @ -237,7 +237,7 @@ var expirePreAuthKeyCmd = &cobra.Command{ | |||||||
| 		defer conn.Close() | 		defer conn.Close() | ||||||
| 
 | 
 | ||||||
| 		request := &v1.ExpirePreAuthKeyRequest{ | 		request := &v1.ExpirePreAuthKeyRequest{ | ||||||
| 			Namespace: namespace, | 			User: user, | ||||||
| 			Key:       args[0], | 			Key:       args[0], | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										14
									
								
								dns.go
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								dns.go
									
									
									
									
									
								
							| @ -190,23 +190,23 @@ func getMapResponseDNSConfig( | |||||||
| ) *tailcfg.DNSConfig { | ) *tailcfg.DNSConfig { | ||||||
| 	var dnsConfig *tailcfg.DNSConfig = dnsConfigOrig.Clone() | 	var dnsConfig *tailcfg.DNSConfig = dnsConfigOrig.Clone() | ||||||
| 	if dnsConfigOrig != nil && dnsConfigOrig.Proxied { // if MagicDNS is enabled | 	if dnsConfigOrig != nil && dnsConfigOrig.Proxied { // if MagicDNS is enabled | ||||||
| 		// Only inject the Search Domain of the current namespace - shared nodes should use their full FQDN | 		// Only inject the Search Domain of the current user - shared nodes should use their full FQDN | ||||||
| 		dnsConfig.Domains = append( | 		dnsConfig.Domains = append( | ||||||
| 			dnsConfig.Domains, | 			dnsConfig.Domains, | ||||||
| 			fmt.Sprintf( | 			fmt.Sprintf( | ||||||
| 				"%s.%s", | 				"%s.%s", | ||||||
| 				machine.Namespace.Name, | 				machine.User.Name, | ||||||
| 				baseDomain, | 				baseDomain, | ||||||
| 			), | 			), | ||||||
| 		) | 		) | ||||||
| 
 | 
 | ||||||
| 		namespaceSet := mapset.NewSet[Namespace]() | 		userSet := mapset.NewSet[User]() | ||||||
| 		namespaceSet.Add(machine.Namespace) | 		userSet.Add(machine.User) | ||||||
| 		for _, p := range peers { | 		for _, p := range peers { | ||||||
| 			namespaceSet.Add(p.Namespace) | 			userSet.Add(p.User) | ||||||
| 		} | 		} | ||||||
| 		for _, namespace := range namespaceSet.ToSlice() { | 		for _, user := range userSet.ToSlice() { | ||||||
| 			dnsRoute := fmt.Sprintf("%v.%v", namespace.Name, baseDomain) | 			dnsRoute := fmt.Sprintf("%v.%v", user.Name, baseDomain) | ||||||
| 			dnsConfig.Routes[dnsRoute] = nil | 			dnsConfig.Routes[dnsRoute] = nil | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
|  | |||||||
							
								
								
									
										82
									
								
								dns_test.go
									
									
									
									
									
								
							
							
						
						
									
										82
									
								
								dns_test.go
									
									
									
									
									
								
							| @ -112,17 +112,17 @@ func (s *Suite) TestMagicDNSRootDomainsIPv6SingleMultiple(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { | func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { | ||||||
| 	namespaceShared1, err := app.CreateNamespace("shared1") | 	userShared1, err := app.CreateUser("shared1") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	namespaceShared2, err := app.CreateNamespace("shared2") | 	userShared2, err := app.CreateUser("shared2") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	namespaceShared3, err := app.CreateNamespace("shared3") | 	userShared3, err := app.CreateUser("shared3") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	preAuthKeyInShared1, err := app.CreatePreAuthKey( | 	preAuthKeyInShared1, err := app.CreatePreAuthKey( | ||||||
| 		namespaceShared1.Name, | 		userShared1.Name, | ||||||
| 		false, | 		false, | ||||||
| 		false, | 		false, | ||||||
| 		nil, | 		nil, | ||||||
| @ -131,7 +131,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { | |||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	preAuthKeyInShared2, err := app.CreatePreAuthKey( | 	preAuthKeyInShared2, err := app.CreatePreAuthKey( | ||||||
| 		namespaceShared2.Name, | 		userShared2.Name, | ||||||
| 		false, | 		false, | ||||||
| 		false, | 		false, | ||||||
| 		nil, | 		nil, | ||||||
| @ -140,7 +140,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { | |||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	preAuthKeyInShared3, err := app.CreatePreAuthKey( | 	preAuthKeyInShared3, err := app.CreatePreAuthKey( | ||||||
| 		namespaceShared3.Name, | 		userShared3.Name, | ||||||
| 		false, | 		false, | ||||||
| 		false, | 		false, | ||||||
| 		nil, | 		nil, | ||||||
| @ -149,7 +149,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { | |||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	PreAuthKey2InShared1, err := app.CreatePreAuthKey( | 	PreAuthKey2InShared1, err := app.CreatePreAuthKey( | ||||||
| 		namespaceShared1.Name, | 		userShared1.Name, | ||||||
| 		false, | 		false, | ||||||
| 		false, | 		false, | ||||||
| 		nil, | 		nil, | ||||||
| @ -157,7 +157,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { | |||||||
| 	) | 	) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine(namespaceShared1.Name, "test_get_shared_nodes_1") | 	_, err = app.GetMachine(userShared1.Name, "test_get_shared_nodes_1") | ||||||
| 	c.Assert(err, check.NotNil) | 	c.Assert(err, check.NotNil) | ||||||
| 
 | 
 | ||||||
| 	machineInShared1 := &Machine{ | 	machineInShared1 := &Machine{ | ||||||
| @ -166,15 +166,15 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { | |||||||
| 		NodeKey:        "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", | 		NodeKey:        "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", | ||||||
| 		DiscoKey:       "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", | 		DiscoKey:       "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", | ||||||
| 		Hostname:       "test_get_shared_nodes_1", | 		Hostname:       "test_get_shared_nodes_1", | ||||||
| 		NamespaceID:    namespaceShared1.ID, | 		UserID:    userShared1.ID, | ||||||
| 		Namespace:      *namespaceShared1, | 		User:      *userShared1, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.1")}, | 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.1")}, | ||||||
| 		AuthKeyID:      uint(preAuthKeyInShared1.ID), | 		AuthKeyID:      uint(preAuthKeyInShared1.ID), | ||||||
| 	} | 	} | ||||||
| 	app.db.Save(machineInShared1) | 	app.db.Save(machineInShared1) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine(namespaceShared1.Name, machineInShared1.Hostname) | 	_, err = app.GetMachine(userShared1.Name, machineInShared1.Hostname) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	machineInShared2 := &Machine{ | 	machineInShared2 := &Machine{ | ||||||
| @ -183,15 +183,15 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { | |||||||
| 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | ||||||
| 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | ||||||
| 		Hostname:       "test_get_shared_nodes_2", | 		Hostname:       "test_get_shared_nodes_2", | ||||||
| 		NamespaceID:    namespaceShared2.ID, | 		UserID:    userShared2.ID, | ||||||
| 		Namespace:      *namespaceShared2, | 		User:      *userShared2, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.2")}, | 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.2")}, | ||||||
| 		AuthKeyID:      uint(preAuthKeyInShared2.ID), | 		AuthKeyID:      uint(preAuthKeyInShared2.ID), | ||||||
| 	} | 	} | ||||||
| 	app.db.Save(machineInShared2) | 	app.db.Save(machineInShared2) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine(namespaceShared2.Name, machineInShared2.Hostname) | 	_, err = app.GetMachine(userShared2.Name, machineInShared2.Hostname) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	machineInShared3 := &Machine{ | 	machineInShared3 := &Machine{ | ||||||
| @ -200,15 +200,15 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { | |||||||
| 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | ||||||
| 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | ||||||
| 		Hostname:       "test_get_shared_nodes_3", | 		Hostname:       "test_get_shared_nodes_3", | ||||||
| 		NamespaceID:    namespaceShared3.ID, | 		UserID:    userShared3.ID, | ||||||
| 		Namespace:      *namespaceShared3, | 		User:      *userShared3, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.3")}, | 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.3")}, | ||||||
| 		AuthKeyID:      uint(preAuthKeyInShared3.ID), | 		AuthKeyID:      uint(preAuthKeyInShared3.ID), | ||||||
| 	} | 	} | ||||||
| 	app.db.Save(machineInShared3) | 	app.db.Save(machineInShared3) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine(namespaceShared3.Name, machineInShared3.Hostname) | 	_, err = app.GetMachine(userShared3.Name, machineInShared3.Hostname) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	machine2InShared1 := &Machine{ | 	machine2InShared1 := &Machine{ | ||||||
| @ -217,8 +217,8 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { | |||||||
| 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | ||||||
| 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | ||||||
| 		Hostname:       "test_get_shared_nodes_4", | 		Hostname:       "test_get_shared_nodes_4", | ||||||
| 		NamespaceID:    namespaceShared1.ID, | 		UserID:    userShared1.ID, | ||||||
| 		Namespace:      *namespaceShared1, | 		User:      *userShared1, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.4")}, | 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.4")}, | ||||||
| 		AuthKeyID:      uint(PreAuthKey2InShared1.ID), | 		AuthKeyID:      uint(PreAuthKey2InShared1.ID), | ||||||
| @ -245,31 +245,31 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { | |||||||
| 
 | 
 | ||||||
| 	c.Assert(len(dnsConfig.Routes), check.Equals, 3) | 	c.Assert(len(dnsConfig.Routes), check.Equals, 3) | ||||||
| 
 | 
 | ||||||
| 	domainRouteShared1 := fmt.Sprintf("%s.%s", namespaceShared1.Name, baseDomain) | 	domainRouteShared1 := fmt.Sprintf("%s.%s", userShared1.Name, baseDomain) | ||||||
| 	_, ok := dnsConfig.Routes[domainRouteShared1] | 	_, ok := dnsConfig.Routes[domainRouteShared1] | ||||||
| 	c.Assert(ok, check.Equals, true) | 	c.Assert(ok, check.Equals, true) | ||||||
| 
 | 
 | ||||||
| 	domainRouteShared2 := fmt.Sprintf("%s.%s", namespaceShared2.Name, baseDomain) | 	domainRouteShared2 := fmt.Sprintf("%s.%s", userShared2.Name, baseDomain) | ||||||
| 	_, ok = dnsConfig.Routes[domainRouteShared2] | 	_, ok = dnsConfig.Routes[domainRouteShared2] | ||||||
| 	c.Assert(ok, check.Equals, true) | 	c.Assert(ok, check.Equals, true) | ||||||
| 
 | 
 | ||||||
| 	domainRouteShared3 := fmt.Sprintf("%s.%s", namespaceShared3.Name, baseDomain) | 	domainRouteShared3 := fmt.Sprintf("%s.%s", userShared3.Name, baseDomain) | ||||||
| 	_, ok = dnsConfig.Routes[domainRouteShared3] | 	_, ok = dnsConfig.Routes[domainRouteShared3] | ||||||
| 	c.Assert(ok, check.Equals, true) | 	c.Assert(ok, check.Equals, true) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { | func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { | ||||||
| 	namespaceShared1, err := app.CreateNamespace("shared1") | 	userShared1, err := app.CreateUser("shared1") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	namespaceShared2, err := app.CreateNamespace("shared2") | 	userShared2, err := app.CreateUser("shared2") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	namespaceShared3, err := app.CreateNamespace("shared3") | 	userShared3, err := app.CreateUser("shared3") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	preAuthKeyInShared1, err := app.CreatePreAuthKey( | 	preAuthKeyInShared1, err := app.CreatePreAuthKey( | ||||||
| 		namespaceShared1.Name, | 		userShared1.Name, | ||||||
| 		false, | 		false, | ||||||
| 		false, | 		false, | ||||||
| 		nil, | 		nil, | ||||||
| @ -278,7 +278,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { | |||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	preAuthKeyInShared2, err := app.CreatePreAuthKey( | 	preAuthKeyInShared2, err := app.CreatePreAuthKey( | ||||||
| 		namespaceShared2.Name, | 		userShared2.Name, | ||||||
| 		false, | 		false, | ||||||
| 		false, | 		false, | ||||||
| 		nil, | 		nil, | ||||||
| @ -287,7 +287,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { | |||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	preAuthKeyInShared3, err := app.CreatePreAuthKey( | 	preAuthKeyInShared3, err := app.CreatePreAuthKey( | ||||||
| 		namespaceShared3.Name, | 		userShared3.Name, | ||||||
| 		false, | 		false, | ||||||
| 		false, | 		false, | ||||||
| 		nil, | 		nil, | ||||||
| @ -296,7 +296,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { | |||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	preAuthKey2InShared1, err := app.CreatePreAuthKey( | 	preAuthKey2InShared1, err := app.CreatePreAuthKey( | ||||||
| 		namespaceShared1.Name, | 		userShared1.Name, | ||||||
| 		false, | 		false, | ||||||
| 		false, | 		false, | ||||||
| 		nil, | 		nil, | ||||||
| @ -304,7 +304,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { | |||||||
| 	) | 	) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine(namespaceShared1.Name, "test_get_shared_nodes_1") | 	_, err = app.GetMachine(userShared1.Name, "test_get_shared_nodes_1") | ||||||
| 	c.Assert(err, check.NotNil) | 	c.Assert(err, check.NotNil) | ||||||
| 
 | 
 | ||||||
| 	machineInShared1 := &Machine{ | 	machineInShared1 := &Machine{ | ||||||
| @ -313,15 +313,15 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { | |||||||
| 		NodeKey:        "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", | 		NodeKey:        "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", | ||||||
| 		DiscoKey:       "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", | 		DiscoKey:       "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", | ||||||
| 		Hostname:       "test_get_shared_nodes_1", | 		Hostname:       "test_get_shared_nodes_1", | ||||||
| 		NamespaceID:    namespaceShared1.ID, | 		UserID:    userShared1.ID, | ||||||
| 		Namespace:      *namespaceShared1, | 		User:      *userShared1, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.1")}, | 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.1")}, | ||||||
| 		AuthKeyID:      uint(preAuthKeyInShared1.ID), | 		AuthKeyID:      uint(preAuthKeyInShared1.ID), | ||||||
| 	} | 	} | ||||||
| 	app.db.Save(machineInShared1) | 	app.db.Save(machineInShared1) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine(namespaceShared1.Name, machineInShared1.Hostname) | 	_, err = app.GetMachine(userShared1.Name, machineInShared1.Hostname) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	machineInShared2 := &Machine{ | 	machineInShared2 := &Machine{ | ||||||
| @ -330,15 +330,15 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { | |||||||
| 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | ||||||
| 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | ||||||
| 		Hostname:       "test_get_shared_nodes_2", | 		Hostname:       "test_get_shared_nodes_2", | ||||||
| 		NamespaceID:    namespaceShared2.ID, | 		UserID:    userShared2.ID, | ||||||
| 		Namespace:      *namespaceShared2, | 		User:      *userShared2, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.2")}, | 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.2")}, | ||||||
| 		AuthKeyID:      uint(preAuthKeyInShared2.ID), | 		AuthKeyID:      uint(preAuthKeyInShared2.ID), | ||||||
| 	} | 	} | ||||||
| 	app.db.Save(machineInShared2) | 	app.db.Save(machineInShared2) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine(namespaceShared2.Name, machineInShared2.Hostname) | 	_, err = app.GetMachine(userShared2.Name, machineInShared2.Hostname) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	machineInShared3 := &Machine{ | 	machineInShared3 := &Machine{ | ||||||
| @ -347,15 +347,15 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { | |||||||
| 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | ||||||
| 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | ||||||
| 		Hostname:       "test_get_shared_nodes_3", | 		Hostname:       "test_get_shared_nodes_3", | ||||||
| 		NamespaceID:    namespaceShared3.ID, | 		UserID:    userShared3.ID, | ||||||
| 		Namespace:      *namespaceShared3, | 		User:      *userShared3, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.3")}, | 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.3")}, | ||||||
| 		AuthKeyID:      uint(preAuthKeyInShared3.ID), | 		AuthKeyID:      uint(preAuthKeyInShared3.ID), | ||||||
| 	} | 	} | ||||||
| 	app.db.Save(machineInShared3) | 	app.db.Save(machineInShared3) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine(namespaceShared3.Name, machineInShared3.Hostname) | 	_, err = app.GetMachine(userShared3.Name, machineInShared3.Hostname) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	machine2InShared1 := &Machine{ | 	machine2InShared1 := &Machine{ | ||||||
| @ -364,8 +364,8 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { | |||||||
| 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | ||||||
| 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | ||||||
| 		Hostname:       "test_get_shared_nodes_4", | 		Hostname:       "test_get_shared_nodes_4", | ||||||
| 		NamespaceID:    namespaceShared1.ID, | 		UserID:    userShared1.ID, | ||||||
| 		Namespace:      *namespaceShared1, | 		User:      *userShared1, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.4")}, | 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.4")}, | ||||||
| 		AuthKeyID:      uint(preAuthKey2InShared1.ID), | 		AuthKeyID:      uint(preAuthKey2InShared1.ID), | ||||||
|  | |||||||
							
								
								
									
										80
									
								
								grpcv1.go
									
									
									
									
									
								
							
							
						
						
									
										80
									
								
								grpcv1.go
									
									
									
									
									
								
							| @ -26,76 +26,76 @@ func newHeadscaleV1APIServer(h *Headscale) v1.HeadscaleServiceServer { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (api headscaleV1APIServer) GetNamespace( | func (api headscaleV1APIServer) GetUser( | ||||||
| 	ctx context.Context, | 	ctx context.Context, | ||||||
| 	request *v1.GetNamespaceRequest, | 	request *v1.GetUserRequest, | ||||||
| ) (*v1.GetNamespaceResponse, error) { | ) (*v1.GetUserResponse, error) { | ||||||
| 	namespace, err := api.h.GetNamespace(request.GetName()) | 	user, err := api.h.GetUser(request.GetName()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return &v1.GetNamespaceResponse{Namespace: namespace.toProto()}, nil | 	return &v1.GetUserResponse{User: user.toProto()}, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (api headscaleV1APIServer) CreateNamespace( | func (api headscaleV1APIServer) CreateUser( | ||||||
| 	ctx context.Context, | 	ctx context.Context, | ||||||
| 	request *v1.CreateNamespaceRequest, | 	request *v1.CreateUserRequest, | ||||||
| ) (*v1.CreateNamespaceResponse, error) { | ) (*v1.CreateUserResponse, error) { | ||||||
| 	namespace, err := api.h.CreateNamespace(request.GetName()) | 	user, err := api.h.CreateUser(request.GetName()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return &v1.CreateNamespaceResponse{Namespace: namespace.toProto()}, nil | 	return &v1.CreateUserResponse{User: user.toProto()}, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (api headscaleV1APIServer) RenameNamespace( | func (api headscaleV1APIServer) RenameUser( | ||||||
| 	ctx context.Context, | 	ctx context.Context, | ||||||
| 	request *v1.RenameNamespaceRequest, | 	request *v1.RenameUserRequest, | ||||||
| ) (*v1.RenameNamespaceResponse, error) { | ) (*v1.RenameUserResponse, error) { | ||||||
| 	err := api.h.RenameNamespace(request.GetOldName(), request.GetNewName()) | 	err := api.h.RenameUser(request.GetOldName(), request.GetNewName()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	namespace, err := api.h.GetNamespace(request.GetNewName()) | 	user, err := api.h.GetUser(request.GetNewName()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return &v1.RenameNamespaceResponse{Namespace: namespace.toProto()}, nil | 	return &v1.RenameUserResponse{User: user.toProto()}, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (api headscaleV1APIServer) DeleteNamespace( | func (api headscaleV1APIServer) DeleteUser( | ||||||
| 	ctx context.Context, | 	ctx context.Context, | ||||||
| 	request *v1.DeleteNamespaceRequest, | 	request *v1.DeleteUserRequest, | ||||||
| ) (*v1.DeleteNamespaceResponse, error) { | ) (*v1.DeleteUserResponse, error) { | ||||||
| 	err := api.h.DestroyNamespace(request.GetName()) | 	err := api.h.DestroyUser(request.GetName()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return &v1.DeleteNamespaceResponse{}, nil | 	return &v1.DeleteUserResponse{}, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (api headscaleV1APIServer) ListNamespaces( | func (api headscaleV1APIServer) ListUsers( | ||||||
| 	ctx context.Context, | 	ctx context.Context, | ||||||
| 	request *v1.ListNamespacesRequest, | 	request *v1.ListUsersRequest, | ||||||
| ) (*v1.ListNamespacesResponse, error) { | ) (*v1.ListUsersResponse, error) { | ||||||
| 	namespaces, err := api.h.ListNamespaces() | 	users, err := api.h.ListUsers() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	response := make([]*v1.Namespace, len(namespaces)) | 	response := make([]*v1.User, len(users)) | ||||||
| 	for index, namespace := range namespaces { | 	for index, user := range users { | ||||||
| 		response[index] = namespace.toProto() | 		response[index] = user.toProto() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	log.Trace().Caller().Interface("namespaces", response).Msg("") | 	log.Trace().Caller().Interface("users", response).Msg("") | ||||||
| 
 | 
 | ||||||
| 	return &v1.ListNamespacesResponse{Namespaces: response}, nil | 	return &v1.ListUsersResponse{Users: response}, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (api headscaleV1APIServer) CreatePreAuthKey( | func (api headscaleV1APIServer) CreatePreAuthKey( | ||||||
| @ -117,7 +117,7 @@ func (api headscaleV1APIServer) CreatePreAuthKey( | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	preAuthKey, err := api.h.CreatePreAuthKey( | 	preAuthKey, err := api.h.CreatePreAuthKey( | ||||||
| 		request.GetNamespace(), | 		request.GetUser(), | ||||||
| 		request.GetReusable(), | 		request.GetReusable(), | ||||||
| 		request.GetEphemeral(), | 		request.GetEphemeral(), | ||||||
| 		&expiration, | 		&expiration, | ||||||
| @ -134,7 +134,7 @@ func (api headscaleV1APIServer) ExpirePreAuthKey( | |||||||
| 	ctx context.Context, | 	ctx context.Context, | ||||||
| 	request *v1.ExpirePreAuthKeyRequest, | 	request *v1.ExpirePreAuthKeyRequest, | ||||||
| ) (*v1.ExpirePreAuthKeyResponse, error) { | ) (*v1.ExpirePreAuthKeyResponse, error) { | ||||||
| 	preAuthKey, err := api.h.GetPreAuthKey(request.GetNamespace(), request.Key) | 	preAuthKey, err := api.h.GetPreAuthKey(request.GetUser(), request.Key) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -151,7 +151,7 @@ func (api headscaleV1APIServer) ListPreAuthKeys( | |||||||
| 	ctx context.Context, | 	ctx context.Context, | ||||||
| 	request *v1.ListPreAuthKeysRequest, | 	request *v1.ListPreAuthKeysRequest, | ||||||
| ) (*v1.ListPreAuthKeysResponse, error) { | ) (*v1.ListPreAuthKeysResponse, error) { | ||||||
| 	preAuthKeys, err := api.h.ListPreAuthKeys(request.GetNamespace()) | 	preAuthKeys, err := api.h.ListPreAuthKeys(request.GetUser()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -169,13 +169,13 @@ func (api headscaleV1APIServer) RegisterMachine( | |||||||
| 	request *v1.RegisterMachineRequest, | 	request *v1.RegisterMachineRequest, | ||||||
| ) (*v1.RegisterMachineResponse, error) { | ) (*v1.RegisterMachineResponse, error) { | ||||||
| 	log.Trace(). | 	log.Trace(). | ||||||
| 		Str("namespace", request.GetNamespace()). | 		Str("user", request.GetUser()). | ||||||
| 		Str("node_key", request.GetKey()). | 		Str("node_key", request.GetKey()). | ||||||
| 		Msg("Registering machine") | 		Msg("Registering machine") | ||||||
| 
 | 
 | ||||||
| 	machine, err := api.h.RegisterMachineFromAuthCallback( | 	machine, err := api.h.RegisterMachineFromAuthCallback( | ||||||
| 		request.GetKey(), | 		request.GetKey(), | ||||||
| 		request.GetNamespace(), | 		request.GetUser(), | ||||||
| 		nil, | 		nil, | ||||||
| 		RegisterMethodCLI, | 		RegisterMethodCLI, | ||||||
| 	) | 	) | ||||||
| @ -313,8 +313,8 @@ func (api headscaleV1APIServer) ListMachines( | |||||||
| 	ctx context.Context, | 	ctx context.Context, | ||||||
| 	request *v1.ListMachinesRequest, | 	request *v1.ListMachinesRequest, | ||||||
| ) (*v1.ListMachinesResponse, error) { | ) (*v1.ListMachinesResponse, error) { | ||||||
| 	if request.GetNamespace() != "" { | 	if request.GetUser() != "" { | ||||||
| 		machines, err := api.h.ListMachinesInNamespace(request.GetNamespace()) | 		machines, err := api.h.ListMachinesByUser(request.GetUser()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
| @ -357,7 +357,7 @@ func (api headscaleV1APIServer) MoveMachine( | |||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = api.h.SetMachineNamespace(machine, request.GetNamespace()) | 	err = api.h.SetMachineUser(machine, request.GetUser()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -483,7 +483,7 @@ func (api headscaleV1APIServer) DebugCreateMachine( | |||||||
| 	ctx context.Context, | 	ctx context.Context, | ||||||
| 	request *v1.DebugCreateMachineRequest, | 	request *v1.DebugCreateMachineRequest, | ||||||
| ) (*v1.DebugCreateMachineResponse, error) { | ) (*v1.DebugCreateMachineResponse, error) { | ||||||
| 	namespace, err := api.h.GetNamespace(request.GetNamespace()) | 	user, err := api.h.GetUser(request.GetUser()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -514,7 +514,7 @@ func (api headscaleV1APIServer) DebugCreateMachine( | |||||||
| 		MachineKey: request.GetKey(), | 		MachineKey: request.GetKey(), | ||||||
| 		Hostname:   request.GetName(), | 		Hostname:   request.GetName(), | ||||||
| 		GivenName:  givenName, | 		GivenName:  givenName, | ||||||
| 		Namespace:  *namespace, | 		User:       *user, | ||||||
| 
 | 
 | ||||||
| 		Expiry:               &time.Time{}, | 		Expiry:               &time.Time{}, | ||||||
| 		LastSeen:             &time.Time{}, | 		LastSeen:             &time.Time{}, | ||||||
|  | |||||||
| @ -49,7 +49,7 @@ func TestOIDCAuthenticationPingAll(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	spec := map[string]int{ | 	spec := map[string]int{ | ||||||
| 		"namespace1": len(TailscaleVersions), | 		"user1": len(TailscaleVersions), | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	oidcConfig, err := scenario.runMockOIDC(defaultAccessTTL) | 	oidcConfig, err := scenario.runMockOIDC(defaultAccessTTL) | ||||||
| @ -116,7 +116,7 @@ func TestOIDCExpireNodes(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	spec := map[string]int{ | 	spec := map[string]int{ | ||||||
| 		"namespace1": len(TailscaleVersions), | 		"user1": len(TailscaleVersions), | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	oidcConfig, err := scenario.runMockOIDC(shortAccessTTL) | 	oidcConfig, err := scenario.runMockOIDC(shortAccessTTL) | ||||||
| @ -169,7 +169,7 @@ func TestOIDCExpireNodes(t *testing.T) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *AuthOIDCScenario) CreateHeadscaleEnv( | func (s *AuthOIDCScenario) CreateHeadscaleEnv( | ||||||
| 	namespaces map[string]int, | 	users map[string]int, | ||||||
| 	opts ...hsic.Option, | 	opts ...hsic.Option, | ||||||
| ) error { | ) error { | ||||||
| 	headscale, err := s.Headscale(opts...) | 	headscale, err := s.Headscale(opts...) | ||||||
| @ -182,19 +182,19 @@ func (s *AuthOIDCScenario) CreateHeadscaleEnv( | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for namespaceName, clientCount := range namespaces { | 	for userName, clientCount := range users { | ||||||
| 		log.Printf("creating namespace %s with %d clients", namespaceName, clientCount) | 		log.Printf("creating user %s with %d clients", userName, clientCount) | ||||||
| 		err = s.CreateNamespace(namespaceName) | 		err = s.CreateUser(userName) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		err = s.CreateTailscaleNodesInNamespace(namespaceName, "all", clientCount) | 		err = s.CreateTailscaleNodesInUser(userName, "all", clientCount) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		err = s.runTailscaleUp(namespaceName, headscale.GetEndpoint()) | 		err = s.runTailscaleUp(userName, headscale.GetEndpoint()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| @ -287,20 +287,20 @@ func (s *AuthOIDCScenario) runMockOIDC(accessTTL time.Duration) (*headscale.OIDC | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *AuthOIDCScenario) runTailscaleUp( | func (s *AuthOIDCScenario) runTailscaleUp( | ||||||
| 	namespaceStr, loginServer string, | 	userStr, loginServer string, | ||||||
| ) error { | ) error { | ||||||
| 	headscale, err := s.Headscale() | 	headscale, err := s.Headscale() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	log.Printf("running tailscale up for namespace %s", namespaceStr) | 	log.Printf("running tailscale up for user %s", userStr) | ||||||
| 	if namespace, ok := s.namespaces[namespaceStr]; ok { | 	if user, ok := s.users[userStr]; ok { | ||||||
| 		for _, client := range namespace.Clients { | 		for _, client := range user.Clients { | ||||||
| 			namespace.joinWaitGroup.Add(1) | 			user.joinWaitGroup.Add(1) | ||||||
| 
 | 
 | ||||||
| 			go func(c TailscaleClient) { | 			go func(c TailscaleClient) { | ||||||
| 				defer namespace.joinWaitGroup.Done() | 				defer user.joinWaitGroup.Done() | ||||||
| 
 | 
 | ||||||
| 				// TODO(juanfont): error handle this | 				// TODO(juanfont): error handle this | ||||||
| 				loginURL, err := c.UpWithLoginURL(loginServer) | 				loginURL, err := c.UpWithLoginURL(loginServer) | ||||||
| @ -347,12 +347,12 @@ func (s *AuthOIDCScenario) runTailscaleUp( | |||||||
| 			log.Printf("client %s is ready", client.Hostname()) | 			log.Printf("client %s is ready", client.Hostname()) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		namespace.joinWaitGroup.Wait() | 		user.joinWaitGroup.Wait() | ||||||
| 
 | 
 | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return fmt.Errorf("failed to up tailscale node: %w", errNoNamespaceAvailable) | 	return fmt.Errorf("failed to up tailscale node: %w", errNoUserAvailable) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func pingAll(t *testing.T, clients []TailscaleClient, ips []netip.Addr) int { | func pingAll(t *testing.T, clients []TailscaleClient, ips []netip.Addr) int { | ||||||
|  | |||||||
| @ -35,8 +35,8 @@ func TestAuthWebFlowAuthenticationPingAll(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	spec := map[string]int{ | 	spec := map[string]int{ | ||||||
| 		"namespace1": len(TailscaleVersions), | 		"user1": len(TailscaleVersions), | ||||||
| 		"namespace2": len(TailscaleVersions), | 		"user2": len(TailscaleVersions), | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = scenario.CreateHeadscaleEnv(spec, hsic.WithTestName("webauthping")) | 	err = scenario.CreateHeadscaleEnv(spec, hsic.WithTestName("webauthping")) | ||||||
| @ -93,8 +93,8 @@ func TestAuthWebFlowLogoutAndRelogin(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	spec := map[string]int{ | 	spec := map[string]int{ | ||||||
| 		"namespace1": len(TailscaleVersions), | 		"user1": len(TailscaleVersions), | ||||||
| 		"namespace2": len(TailscaleVersions), | 		"user2": len(TailscaleVersions), | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = scenario.CreateHeadscaleEnv(spec, hsic.WithTestName("weblogout")) | 	err = scenario.CreateHeadscaleEnv(spec, hsic.WithTestName("weblogout")) | ||||||
| @ -156,8 +156,8 @@ func TestAuthWebFlowLogoutAndRelogin(t *testing.T) { | |||||||
| 		t.Errorf("failed to get headscale server: %s", err) | 		t.Errorf("failed to get headscale server: %s", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for namespaceName := range spec { | 	for userName := range spec { | ||||||
| 		err = scenario.runTailscaleUp(namespaceName, headscale.GetEndpoint()) | 		err = scenario.runTailscaleUp(userName, headscale.GetEndpoint()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			t.Errorf("failed to run tailscale up: %s", err) | 			t.Errorf("failed to run tailscale up: %s", err) | ||||||
| 		} | 		} | ||||||
| @ -225,7 +225,7 @@ func TestAuthWebFlowLogoutAndRelogin(t *testing.T) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *AuthWebFlowScenario) CreateHeadscaleEnv( | func (s *AuthWebFlowScenario) CreateHeadscaleEnv( | ||||||
| 	namespaces map[string]int, | 	users map[string]int, | ||||||
| 	opts ...hsic.Option, | 	opts ...hsic.Option, | ||||||
| ) error { | ) error { | ||||||
| 	headscale, err := s.Headscale(opts...) | 	headscale, err := s.Headscale(opts...) | ||||||
| @ -238,19 +238,19 @@ func (s *AuthWebFlowScenario) CreateHeadscaleEnv( | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for namespaceName, clientCount := range namespaces { | 	for userName, clientCount := range users { | ||||||
| 		log.Printf("creating namespace %s with %d clients", namespaceName, clientCount) | 		log.Printf("creating user %s with %d clients", userName, clientCount) | ||||||
| 		err = s.CreateNamespace(namespaceName) | 		err = s.CreateUser(userName) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		err = s.CreateTailscaleNodesInNamespace(namespaceName, "all", clientCount) | 		err = s.CreateTailscaleNodesInUser(userName, "all", clientCount) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		err = s.runTailscaleUp(namespaceName, headscale.GetEndpoint()) | 		err = s.runTailscaleUp(userName, headscale.GetEndpoint()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| @ -260,15 +260,15 @@ func (s *AuthWebFlowScenario) CreateHeadscaleEnv( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *AuthWebFlowScenario) runTailscaleUp( | func (s *AuthWebFlowScenario) runTailscaleUp( | ||||||
| 	namespaceStr, loginServer string, | 	userStr, loginServer string, | ||||||
| ) error { | ) error { | ||||||
| 	log.Printf("running tailscale up for namespace %s", namespaceStr) | 	log.Printf("running tailscale up for user %s", userStr) | ||||||
| 	if namespace, ok := s.namespaces[namespaceStr]; ok { | 	if user, ok := s.users[userStr]; ok { | ||||||
| 		for _, client := range namespace.Clients { | 		for _, client := range user.Clients { | ||||||
| 			namespace.joinWaitGroup.Add(1) | 			user.joinWaitGroup.Add(1) | ||||||
| 
 | 
 | ||||||
| 			go func(c TailscaleClient) { | 			go func(c TailscaleClient) { | ||||||
| 				defer namespace.joinWaitGroup.Done() | 				defer user.joinWaitGroup.Done() | ||||||
| 
 | 
 | ||||||
| 				// TODO(juanfont): error handle this | 				// TODO(juanfont): error handle this | ||||||
| 				loginURL, err := c.UpWithLoginURL(loginServer) | 				loginURL, err := c.UpWithLoginURL(loginServer) | ||||||
| @ -276,7 +276,7 @@ func (s *AuthWebFlowScenario) runTailscaleUp( | |||||||
| 					log.Printf("failed to run tailscale up: %s", err) | 					log.Printf("failed to run tailscale up: %s", err) | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				err = s.runHeadscaleRegister(namespaceStr, loginURL) | 				err = s.runHeadscaleRegister(userStr, loginURL) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					log.Printf("failed to register client: %s", err) | 					log.Printf("failed to register client: %s", err) | ||||||
| 				} | 				} | ||||||
| @ -287,15 +287,15 @@ func (s *AuthWebFlowScenario) runTailscaleUp( | |||||||
| 				log.Printf("error waiting for client %s to be ready: %s", client.Hostname(), err) | 				log.Printf("error waiting for client %s to be ready: %s", client.Hostname(), err) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		namespace.joinWaitGroup.Wait() | 		user.joinWaitGroup.Wait() | ||||||
| 
 | 
 | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return fmt.Errorf("failed to up tailscale node: %w", errNoNamespaceAvailable) | 	return fmt.Errorf("failed to up tailscale node: %w", errNoUserAvailable) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *AuthWebFlowScenario) runHeadscaleRegister(namespaceStr string, loginURL *url.URL) error { | func (s *AuthWebFlowScenario) runHeadscaleRegister(userStr string, loginURL *url.URL) error { | ||||||
| 	headscale, err := s.Headscale() | 	headscale, err := s.Headscale() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| @ -335,7 +335,7 @@ func (s *AuthWebFlowScenario) runHeadscaleRegister(namespaceStr string, loginURL | |||||||
| 
 | 
 | ||||||
| 	if headscale, err := s.Headscale(); err == nil { | 	if headscale, err := s.Headscale(); err == nil { | ||||||
| 		_, err = headscale.Execute( | 		_, err = headscale.Execute( | ||||||
| 			[]string{"headscale", "-n", namespaceStr, "nodes", "register", "--key", key}, | 			[]string{"headscale", "-n", userStr, "nodes", "register", "--key", key}, | ||||||
| 		) | 		) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Printf("failed to register node: %s", err) | 			log.Printf("failed to register node: %s", err) | ||||||
|  | |||||||
| @ -28,7 +28,7 @@ func executeAndUnmarshal[T any](headscale ControlServer, command []string, resul | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestNamespaceCommand(t *testing.T) { | func TestUserCommand(t *testing.T) { | ||||||
| 	IntegrationSkip(t) | 	IntegrationSkip(t) | ||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
| 
 | 
 | ||||||
| @ -36,8 +36,8 @@ func TestNamespaceCommand(t *testing.T) { | |||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	spec := map[string]int{ | 	spec := map[string]int{ | ||||||
| 		"namespace1": 0, | 		"user1": 0, | ||||||
| 		"namespace2": 0, | 		"user2": 0, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins")) | 	err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins")) | ||||||
| @ -46,60 +46,60 @@ func TestNamespaceCommand(t *testing.T) { | |||||||
| 	headscale, err := scenario.Headscale() | 	headscale, err := scenario.Headscale() | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	var listNamespaces []v1.Namespace | 	var listUsers []v1.User | ||||||
| 	err = executeAndUnmarshal(headscale, | 	err = executeAndUnmarshal(headscale, | ||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"namespaces", | 			"users", | ||||||
| 			"list", | 			"list", | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| 		}, | 		}, | ||||||
| 		&listNamespaces, | 		&listUsers, | ||||||
| 	) | 	) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	result := []string{listNamespaces[0].Name, listNamespaces[1].Name} | 	result := []string{listUsers[0].Name, listUsers[1].Name} | ||||||
| 	sort.Strings(result) | 	sort.Strings(result) | ||||||
| 
 | 
 | ||||||
| 	assert.Equal( | 	assert.Equal( | ||||||
| 		t, | 		t, | ||||||
| 		[]string{"namespace1", "namespace2"}, | 		[]string{"user1", "user2"}, | ||||||
| 		result, | 		result, | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	_, err = headscale.Execute( | 	_, err = headscale.Execute( | ||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"namespaces", | 			"users", | ||||||
| 			"rename", | 			"rename", | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| 			"namespace2", | 			"user2", | ||||||
| 			"newname", | 			"newname", | ||||||
| 		}, | 		}, | ||||||
| 	) | 	) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	var listAfterRenameNamespaces []v1.Namespace | 	var listAfterRenameUsers []v1.User | ||||||
| 	err = executeAndUnmarshal(headscale, | 	err = executeAndUnmarshal(headscale, | ||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"namespaces", | 			"users", | ||||||
| 			"list", | 			"list", | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| 		}, | 		}, | ||||||
| 		&listAfterRenameNamespaces, | 		&listAfterRenameUsers, | ||||||
| 	) | 	) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	result = []string{listAfterRenameNamespaces[0].Name, listAfterRenameNamespaces[1].Name} | 	result = []string{listAfterRenameUsers[0].Name, listAfterRenameUsers[1].Name} | ||||||
| 	sort.Strings(result) | 	sort.Strings(result) | ||||||
| 
 | 
 | ||||||
| 	assert.Equal( | 	assert.Equal( | ||||||
| 		t, | 		t, | ||||||
| 		[]string{"namespace1", "newname"}, | 		[]string{"user1", "newname"}, | ||||||
| 		result, | 		result, | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| @ -111,14 +111,14 @@ func TestPreAuthKeyCommand(t *testing.T) { | |||||||
| 	IntegrationSkip(t) | 	IntegrationSkip(t) | ||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
| 
 | 
 | ||||||
| 	namespace := "preauthkeyspace" | 	user := "preauthkeyspace" | ||||||
| 	count := 3 | 	count := 3 | ||||||
| 
 | 
 | ||||||
| 	scenario, err := NewScenario() | 	scenario, err := NewScenario() | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	spec := map[string]int{ | 	spec := map[string]int{ | ||||||
| 		namespace: 0, | 		user: 0, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clipak")) | 	err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clipak")) | ||||||
| @ -137,8 +137,8 @@ func TestPreAuthKeyCommand(t *testing.T) { | |||||||
| 			[]string{ | 			[]string{ | ||||||
| 				"headscale", | 				"headscale", | ||||||
| 				"preauthkeys", | 				"preauthkeys", | ||||||
| 				"--namespace", | 				"--user", | ||||||
| 				namespace, | 				user, | ||||||
| 				"create", | 				"create", | ||||||
| 				"--reusable", | 				"--reusable", | ||||||
| 				"--expiration", | 				"--expiration", | ||||||
| @ -163,8 +163,8 @@ func TestPreAuthKeyCommand(t *testing.T) { | |||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"preauthkeys", | 			"preauthkeys", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			namespace, | 			user, | ||||||
| 			"list", | 			"list", | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| @ -216,8 +216,8 @@ func TestPreAuthKeyCommand(t *testing.T) { | |||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"preauthkeys", | 			"preauthkeys", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			namespace, | 			user, | ||||||
| 			"expire", | 			"expire", | ||||||
| 			listedPreAuthKeys[1].Key, | 			listedPreAuthKeys[1].Key, | ||||||
| 		}, | 		}, | ||||||
| @ -230,8 +230,8 @@ func TestPreAuthKeyCommand(t *testing.T) { | |||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"preauthkeys", | 			"preauthkeys", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			namespace, | 			user, | ||||||
| 			"list", | 			"list", | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| @ -252,13 +252,13 @@ func TestPreAuthKeyCommandWithoutExpiry(t *testing.T) { | |||||||
| 	IntegrationSkip(t) | 	IntegrationSkip(t) | ||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
| 
 | 
 | ||||||
| 	namespace := "pre-auth-key-without-exp-namespace" | 	user := "pre-auth-key-without-exp-user" | ||||||
| 
 | 
 | ||||||
| 	scenario, err := NewScenario() | 	scenario, err := NewScenario() | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	spec := map[string]int{ | 	spec := map[string]int{ | ||||||
| 		namespace: 0, | 		user: 0, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clipaknaexp")) | 	err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clipaknaexp")) | ||||||
| @ -273,8 +273,8 @@ func TestPreAuthKeyCommandWithoutExpiry(t *testing.T) { | |||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"preauthkeys", | 			"preauthkeys", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			namespace, | 			user, | ||||||
| 			"create", | 			"create", | ||||||
| 			"--reusable", | 			"--reusable", | ||||||
| 			"--output", | 			"--output", | ||||||
| @ -290,8 +290,8 @@ func TestPreAuthKeyCommandWithoutExpiry(t *testing.T) { | |||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"preauthkeys", | 			"preauthkeys", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			namespace, | 			user, | ||||||
| 			"list", | 			"list", | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| @ -317,13 +317,13 @@ func TestPreAuthKeyCommandReusableEphemeral(t *testing.T) { | |||||||
| 	IntegrationSkip(t) | 	IntegrationSkip(t) | ||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
| 
 | 
 | ||||||
| 	namespace := "pre-auth-key-reus-ephm-namespace" | 	user := "pre-auth-key-reus-ephm-user" | ||||||
| 
 | 
 | ||||||
| 	scenario, err := NewScenario() | 	scenario, err := NewScenario() | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	spec := map[string]int{ | 	spec := map[string]int{ | ||||||
| 		namespace: 0, | 		user: 0, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clipakresueeph")) | 	err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clipakresueeph")) | ||||||
| @ -338,8 +338,8 @@ func TestPreAuthKeyCommandReusableEphemeral(t *testing.T) { | |||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"preauthkeys", | 			"preauthkeys", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			namespace, | 			user, | ||||||
| 			"create", | 			"create", | ||||||
| 			"--reusable=true", | 			"--reusable=true", | ||||||
| 			"--output", | 			"--output", | ||||||
| @ -355,8 +355,8 @@ func TestPreAuthKeyCommandReusableEphemeral(t *testing.T) { | |||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"preauthkeys", | 			"preauthkeys", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			namespace, | 			user, | ||||||
| 			"create", | 			"create", | ||||||
| 			"--ephemeral=true", | 			"--ephemeral=true", | ||||||
| 			"--output", | 			"--output", | ||||||
| @ -375,8 +375,8 @@ func TestPreAuthKeyCommandReusableEphemeral(t *testing.T) { | |||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"preauthkeys", | 			"preauthkeys", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			namespace, | 			user, | ||||||
| 			"list", | 			"list", | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| @ -396,13 +396,13 @@ func TestEnablingRoutes(t *testing.T) { | |||||||
| 	IntegrationSkip(t) | 	IntegrationSkip(t) | ||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
| 
 | 
 | ||||||
| 	namespace := "enable-routing" | 	user := "enable-routing" | ||||||
| 
 | 
 | ||||||
| 	scenario, err := NewScenario() | 	scenario, err := NewScenario() | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	spec := map[string]int{ | 	spec := map[string]int{ | ||||||
| 		namespace: 3, | 		user: 3, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clienableroute")) | 	err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clienableroute")) | ||||||
|  | |||||||
| @ -10,9 +10,9 @@ type ControlServer interface { | |||||||
| 	GetHealthEndpoint() string | 	GetHealthEndpoint() string | ||||||
| 	GetEndpoint() string | 	GetEndpoint() string | ||||||
| 	WaitForReady() error | 	WaitForReady() error | ||||||
| 	CreateNamespace(namespace string) error | 	CreateUser(user string) error | ||||||
| 	CreateAuthKey(namespace string, reusable bool, ephemeral bool) (*v1.PreAuthKey, error) | 	CreateAuthKey(user string, reusable bool, ephemeral bool) (*v1.PreAuthKey, error) | ||||||
| 	ListMachinesInNamespace(namespace string) ([]*v1.Machine, error) | 	ListMachinesInUser(user string) ([]*v1.Machine, error) | ||||||
| 	GetCert() []byte | 	GetCert() []byte | ||||||
| 	GetHostname() string | 	GetHostname() string | ||||||
| 	GetIP() string | 	GetIP() string | ||||||
|  | |||||||
| @ -22,8 +22,8 @@ func TestPingAllByIP(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	spec := map[string]int{ | 	spec := map[string]int{ | ||||||
| 		"namespace1": len(TailscaleVersions), | 		"user1": len(TailscaleVersions), | ||||||
| 		"namespace2": len(TailscaleVersions), | 		"user2": len(TailscaleVersions), | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("pingallbyip")) | 	err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("pingallbyip")) | ||||||
| @ -77,8 +77,8 @@ func TestAuthKeyLogoutAndRelogin(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	spec := map[string]int{ | 	spec := map[string]int{ | ||||||
| 		"namespace1": len(TailscaleVersions), | 		"user1": len(TailscaleVersions), | ||||||
| 		"namespace2": len(TailscaleVersions), | 		"user2": len(TailscaleVersions), | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("pingallbyip")) | 	err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("pingallbyip")) | ||||||
| @ -121,15 +121,15 @@ func TestAuthKeyLogoutAndRelogin(t *testing.T) { | |||||||
| 		t.Errorf("failed to get headscale server: %s", err) | 		t.Errorf("failed to get headscale server: %s", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for namespaceName := range spec { | 	for userName := range spec { | ||||||
| 		key, err := scenario.CreatePreAuthKey(namespaceName, true, false) | 		key, err := scenario.CreatePreAuthKey(userName, true, false) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			t.Errorf("failed to create pre-auth key for namespace %s: %s", namespaceName, err) | 			t.Errorf("failed to create pre-auth key for user %s: %s", userName, err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		err = scenario.RunTailscaleUp(namespaceName, headscale.GetEndpoint(), key.GetKey()) | 		err = scenario.RunTailscaleUp(userName, headscale.GetEndpoint(), key.GetKey()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			t.Errorf("failed to run tailscale up for namespace %s: %s", namespaceName, err) | 			t.Errorf("failed to run tailscale up for user %s: %s", userName, err) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -207,8 +207,8 @@ func TestEphemeral(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	spec := map[string]int{ | 	spec := map[string]int{ | ||||||
| 		"namespace1": len(TailscaleVersions), | 		"user1": len(TailscaleVersions), | ||||||
| 		"namespace2": len(TailscaleVersions), | 		"user2": len(TailscaleVersions), | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	headscale, err := scenario.Headscale(hsic.WithTestName("ephemeral")) | 	headscale, err := scenario.Headscale(hsic.WithTestName("ephemeral")) | ||||||
| @ -216,25 +216,25 @@ func TestEphemeral(t *testing.T) { | |||||||
| 		t.Errorf("failed to create headscale environment: %s", err) | 		t.Errorf("failed to create headscale environment: %s", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for namespaceName, clientCount := range spec { | 	for userName, clientCount := range spec { | ||||||
| 		err = scenario.CreateNamespace(namespaceName) | 		err = scenario.CreateUser(userName) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			t.Errorf("failed to create namespace %s: %s", namespaceName, err) | 			t.Errorf("failed to create user %s: %s", userName, err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		err = scenario.CreateTailscaleNodesInNamespace(namespaceName, "all", clientCount, []tsic.Option{}...) | 		err = scenario.CreateTailscaleNodesInUser(userName, "all", clientCount, []tsic.Option{}...) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			t.Errorf("failed to create tailscale nodes in namespace %s: %s", namespaceName, err) | 			t.Errorf("failed to create tailscale nodes in user %s: %s", userName, err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		key, err := scenario.CreatePreAuthKey(namespaceName, true, true) | 		key, err := scenario.CreatePreAuthKey(userName, true, true) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			t.Errorf("failed to create pre-auth key for namespace %s: %s", namespaceName, err) | 			t.Errorf("failed to create pre-auth key for user %s: %s", userName, err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		err = scenario.RunTailscaleUp(namespaceName, headscale.GetEndpoint(), key.GetKey()) | 		err = scenario.RunTailscaleUp(userName, headscale.GetEndpoint(), key.GetKey()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			t.Errorf("failed to run tailscale up for namespace %s: %s", namespaceName, err) | 			t.Errorf("failed to run tailscale up for user %s: %s", userName, err) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -278,19 +278,19 @@ func TestEphemeral(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	t.Logf("all clients logged out") | 	t.Logf("all clients logged out") | ||||||
| 
 | 
 | ||||||
| 	for namespaceName := range spec { | 	for userName := range spec { | ||||||
| 		machines, err := headscale.ListMachinesInNamespace(namespaceName) | 		machines, err := headscale.ListMachinesInUser(userName) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Error(). | 			log.Error(). | ||||||
| 				Err(err). | 				Err(err). | ||||||
| 				Str("namespace", namespaceName). | 				Str("user", userName). | ||||||
| 				Msg("Error listing machines in namespace") | 				Msg("Error listing machines in user") | ||||||
| 
 | 
 | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if len(machines) != 0 { | 		if len(machines) != 0 { | ||||||
| 			t.Errorf("expected no machines, got %d in namespace %s", len(machines), namespaceName) | 			t.Errorf("expected no machines, got %d in user %s", len(machines), userName) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -311,8 +311,8 @@ func TestPingAllByHostname(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	spec := map[string]int{ | 	spec := map[string]int{ | ||||||
| 		// Omit 1.16.2 (-1) because it does not have the FQDN field | 		// Omit 1.16.2 (-1) because it does not have the FQDN field | ||||||
| 		"namespace3": len(TailscaleVersions) - 1, | 		"user3": len(TailscaleVersions) - 1, | ||||||
| 		"namespace4": len(TailscaleVersions) - 1, | 		"user4": len(TailscaleVersions) - 1, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("pingallbyname")) | 	err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("pingallbyname")) | ||||||
|  | |||||||
| @ -328,10 +328,10 @@ func (t *HeadscaleInContainer) WaitForReady() error { | |||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *HeadscaleInContainer) CreateNamespace( | func (t *HeadscaleInContainer) CreateUser( | ||||||
| 	namespace string, | 	user string, | ||||||
| ) error { | ) error { | ||||||
| 	command := []string{"headscale", "namespaces", "create", namespace} | 	command := []string{"headscale", "users", "create", user} | ||||||
| 
 | 
 | ||||||
| 	_, _, err := dockertestutil.ExecuteCommand( | 	_, _, err := dockertestutil.ExecuteCommand( | ||||||
| 		t.container, | 		t.container, | ||||||
| @ -346,14 +346,14 @@ func (t *HeadscaleInContainer) CreateNamespace( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *HeadscaleInContainer) CreateAuthKey( | func (t *HeadscaleInContainer) CreateAuthKey( | ||||||
| 	namespace string, | 	user string, | ||||||
| 	reusable bool, | 	reusable bool, | ||||||
| 	ephemeral bool, | 	ephemeral bool, | ||||||
| ) (*v1.PreAuthKey, error) { | ) (*v1.PreAuthKey, error) { | ||||||
| 	command := []string{ | 	command := []string{ | ||||||
| 		"headscale", | 		"headscale", | ||||||
| 		"--namespace", | 		"--user", | ||||||
| 		namespace, | 		user, | ||||||
| 		"preauthkeys", | 		"preauthkeys", | ||||||
| 		"create", | 		"create", | ||||||
| 		"--expiration", | 		"--expiration", | ||||||
| @ -388,10 +388,10 @@ func (t *HeadscaleInContainer) CreateAuthKey( | |||||||
| 	return &preAuthKey, nil | 	return &preAuthKey, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *HeadscaleInContainer) ListMachinesInNamespace( | func (t *HeadscaleInContainer) ListMachinesInUser( | ||||||
| 	namespace string, | 	user string, | ||||||
| ) ([]*v1.Machine, error) { | ) ([]*v1.Machine, error) { | ||||||
| 	command := []string{"headscale", "--namespace", namespace, "nodes", "list", "--output", "json"} | 	command := []string{"headscale", "--user", user, "nodes", "list", "--output", "json"} | ||||||
| 
 | 
 | ||||||
| 	result, _, err := dockertestutil.ExecuteCommand( | 	result, _, err := dockertestutil.ExecuteCommand( | ||||||
| 		t.container, | 		t.container, | ||||||
|  | |||||||
| @ -25,7 +25,7 @@ const ( | |||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	errNoHeadscaleAvailable = errors.New("no headscale available") | 	errNoHeadscaleAvailable = errors.New("no headscale available") | ||||||
| 	errNoNamespaceAvailable = errors.New("no namespace available") | 	errNoUserAvailable = errors.New("no user available") | ||||||
| 
 | 
 | ||||||
| 	// Tailscale started adding TS2021 support in CapabilityVersion>=28 (v1.24.0), but | 	// Tailscale started adding TS2021 support in CapabilityVersion>=28 (v1.24.0), but | ||||||
| 	// proper support in Headscale was only added for CapabilityVersion>=39 clients (v1.30.0). | 	// proper support in Headscale was only added for CapabilityVersion>=39 clients (v1.30.0). | ||||||
| @ -61,7 +61,7 @@ var ( | |||||||
| 	) | 	) | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type Namespace struct { | type User struct { | ||||||
| 	Clients map[string]TailscaleClient | 	Clients map[string]TailscaleClient | ||||||
| 
 | 
 | ||||||
| 	createWaitGroup sync.WaitGroup | 	createWaitGroup sync.WaitGroup | ||||||
| @ -75,7 +75,7 @@ type Scenario struct { | |||||||
| 	// use one. | 	// use one. | ||||||
| 	controlServers *xsync.MapOf[string, ControlServer] | 	controlServers *xsync.MapOf[string, ControlServer] | ||||||
| 
 | 
 | ||||||
| 	namespaces map[string]*Namespace | 	users map[string]*User | ||||||
| 
 | 
 | ||||||
| 	pool    *dockertest.Pool | 	pool    *dockertest.Pool | ||||||
| 	network *dockertest.Network | 	network *dockertest.Network | ||||||
| @ -116,7 +116,7 @@ func NewScenario() (*Scenario, error) { | |||||||
| 
 | 
 | ||||||
| 	return &Scenario{ | 	return &Scenario{ | ||||||
| 		controlServers: xsync.NewMapOf[ControlServer](), | 		controlServers: xsync.NewMapOf[ControlServer](), | ||||||
| 		namespaces:     make(map[string]*Namespace), | 		users:     make(map[string]*User), | ||||||
| 
 | 
 | ||||||
| 		pool:    pool, | 		pool:    pool, | ||||||
| 		network: network, | 		network: network, | ||||||
| @ -136,9 +136,9 @@ func (s *Scenario) Shutdown() error { | |||||||
| 		return true | 		return true | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	for namespaceName, namespace := range s.namespaces { | 	for userName, user := range s.users { | ||||||
| 		for _, client := range namespace.Clients { | 		for _, client := range user.Clients { | ||||||
| 			log.Printf("removing client %s in namespace %s", client.Hostname(), namespaceName) | 			log.Printf("removing client %s in user %s", client.Hostname(), userName) | ||||||
| 			err := client.Shutdown() | 			err := client.Shutdown() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return fmt.Errorf("failed to tear down client: %w", err) | 				return fmt.Errorf("failed to tear down client: %w", err) | ||||||
| @ -158,13 +158,13 @@ func (s *Scenario) Shutdown() error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Scenario) Namespaces() []string { | func (s *Scenario) Users() []string { | ||||||
| 	namespaces := make([]string, 0) | 	users := make([]string, 0) | ||||||
| 	for namespace := range s.namespaces { | 	for user := range s.users { | ||||||
| 		namespaces = append(namespaces, namespace) | 		users = append(users, user) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return namespaces | 	return users | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Headscale related stuff | /// Headscale related stuff | ||||||
| @ -194,45 +194,45 @@ func (s *Scenario) Headscale(opts ...hsic.Option) (ControlServer, error) { | |||||||
| 	return headscale, nil | 	return headscale, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Scenario) CreatePreAuthKey(namespace string, reusable bool, ephemeral bool) (*v1.PreAuthKey, error) { | func (s *Scenario) CreatePreAuthKey(user string, reusable bool, ephemeral bool) (*v1.PreAuthKey, error) { | ||||||
| 	if headscale, err := s.Headscale(); err == nil { | 	if headscale, err := s.Headscale(); err == nil { | ||||||
| 		key, err := headscale.CreateAuthKey(namespace, reusable, ephemeral) | 		key, err := headscale.CreateAuthKey(user, reusable, ephemeral) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, fmt.Errorf("failed to create namespace: %w", err) | 			return nil, fmt.Errorf("failed to create user: %w", err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		return key, nil | 		return key, nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return nil, fmt.Errorf("failed to create namespace: %w", errNoHeadscaleAvailable) | 	return nil, fmt.Errorf("failed to create user: %w", errNoHeadscaleAvailable) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Scenario) CreateNamespace(namespace string) error { | func (s *Scenario) CreateUser(user string) error { | ||||||
| 	if headscale, err := s.Headscale(); err == nil { | 	if headscale, err := s.Headscale(); err == nil { | ||||||
| 		err := headscale.CreateNamespace(namespace) | 		err := headscale.CreateUser(user) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return fmt.Errorf("failed to create namespace: %w", err) | 			return fmt.Errorf("failed to create user: %w", err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		s.namespaces[namespace] = &Namespace{ | 		s.users[user] = &User{ | ||||||
| 			Clients: make(map[string]TailscaleClient), | 			Clients: make(map[string]TailscaleClient), | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return fmt.Errorf("failed to create namespace: %w", errNoHeadscaleAvailable) | 	return fmt.Errorf("failed to create user: %w", errNoHeadscaleAvailable) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Client related stuff | /// Client related stuff | ||||||
| 
 | 
 | ||||||
| func (s *Scenario) CreateTailscaleNodesInNamespace( | func (s *Scenario) CreateTailscaleNodesInUser( | ||||||
| 	namespaceStr string, | 	userStr string, | ||||||
| 	requestedVersion string, | 	requestedVersion string, | ||||||
| 	count int, | 	count int, | ||||||
| 	opts ...tsic.Option, | 	opts ...tsic.Option, | ||||||
| ) error { | ) error { | ||||||
| 	if namespace, ok := s.namespaces[namespaceStr]; ok { | 	if user, ok := s.users[userStr]; ok { | ||||||
| 		for i := 0; i < count; i++ { | 		for i := 0; i < count; i++ { | ||||||
| 			version := requestedVersion | 			version := requestedVersion | ||||||
| 			if requestedVersion == "all" { | 			if requestedVersion == "all" { | ||||||
| @ -247,7 +247,7 @@ func (s *Scenario) CreateTailscaleNodesInNamespace( | |||||||
| 			cert := headscale.GetCert() | 			cert := headscale.GetCert() | ||||||
| 			hostname := headscale.GetHostname() | 			hostname := headscale.GetHostname() | ||||||
| 
 | 
 | ||||||
| 			namespace.createWaitGroup.Add(1) | 			user.createWaitGroup.Add(1) | ||||||
| 
 | 
 | ||||||
| 			opts = append(opts, | 			opts = append(opts, | ||||||
| 				tsic.WithHeadscaleTLS(cert), | 				tsic.WithHeadscaleTLS(cert), | ||||||
| @ -255,7 +255,7 @@ func (s *Scenario) CreateTailscaleNodesInNamespace( | |||||||
| 			) | 			) | ||||||
| 
 | 
 | ||||||
| 			go func() { | 			go func() { | ||||||
| 				defer namespace.createWaitGroup.Done() | 				defer user.createWaitGroup.Done() | ||||||
| 
 | 
 | ||||||
| 				// TODO(kradalby): error handle this | 				// TODO(kradalby): error handle this | ||||||
| 				tsClient, err := tsic.New( | 				tsClient, err := tsic.New( | ||||||
| @ -275,26 +275,26 @@ func (s *Scenario) CreateTailscaleNodesInNamespace( | |||||||
| 					log.Printf("failed to wait for tailscaled: %s", err) | 					log.Printf("failed to wait for tailscaled: %s", err) | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				namespace.Clients[tsClient.Hostname()] = tsClient | 				user.Clients[tsClient.Hostname()] = tsClient | ||||||
| 			}() | 			}() | ||||||
| 		} | 		} | ||||||
| 		namespace.createWaitGroup.Wait() | 		user.createWaitGroup.Wait() | ||||||
| 
 | 
 | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return fmt.Errorf("failed to add tailscale node: %w", errNoNamespaceAvailable) | 	return fmt.Errorf("failed to add tailscale node: %w", errNoUserAvailable) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Scenario) RunTailscaleUp( | func (s *Scenario) RunTailscaleUp( | ||||||
| 	namespaceStr, loginServer, authKey string, | 	userStr, loginServer, authKey string, | ||||||
| ) error { | ) error { | ||||||
| 	if namespace, ok := s.namespaces[namespaceStr]; ok { | 	if user, ok := s.users[userStr]; ok { | ||||||
| 		for _, client := range namespace.Clients { | 		for _, client := range user.Clients { | ||||||
| 			namespace.joinWaitGroup.Add(1) | 			user.joinWaitGroup.Add(1) | ||||||
| 
 | 
 | ||||||
| 			go func(c TailscaleClient) { | 			go func(c TailscaleClient) { | ||||||
| 				defer namespace.joinWaitGroup.Done() | 				defer user.joinWaitGroup.Done() | ||||||
| 
 | 
 | ||||||
| 				// TODO(kradalby): error handle this | 				// TODO(kradalby): error handle this | ||||||
| 				_ = c.Up(loginServer, authKey) | 				_ = c.Up(loginServer, authKey) | ||||||
| @ -306,19 +306,19 @@ func (s *Scenario) RunTailscaleUp( | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		namespace.joinWaitGroup.Wait() | 		user.joinWaitGroup.Wait() | ||||||
| 
 | 
 | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return fmt.Errorf("failed to up tailscale node: %w", errNoNamespaceAvailable) | 	return fmt.Errorf("failed to up tailscale node: %w", errNoUserAvailable) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Scenario) CountTailscale() int { | func (s *Scenario) CountTailscale() int { | ||||||
| 	count := 0 | 	count := 0 | ||||||
| 
 | 
 | ||||||
| 	for _, namespace := range s.namespaces { | 	for _, user := range s.users { | ||||||
| 		count += len(namespace.Clients) | 		count += len(user.Clients) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return count | 	return count | ||||||
| @ -327,18 +327,18 @@ func (s *Scenario) CountTailscale() int { | |||||||
| func (s *Scenario) WaitForTailscaleSync() error { | func (s *Scenario) WaitForTailscaleSync() error { | ||||||
| 	tsCount := s.CountTailscale() | 	tsCount := s.CountTailscale() | ||||||
| 
 | 
 | ||||||
| 	for _, namespace := range s.namespaces { | 	for _, user := range s.users { | ||||||
| 		for _, client := range namespace.Clients { | 		for _, client := range user.Clients { | ||||||
| 			namespace.syncWaitGroup.Add(1) | 			user.syncWaitGroup.Add(1) | ||||||
| 
 | 
 | ||||||
| 			go func(c TailscaleClient) { | 			go func(c TailscaleClient) { | ||||||
| 				defer namespace.syncWaitGroup.Done() | 				defer user.syncWaitGroup.Done() | ||||||
| 
 | 
 | ||||||
| 				// TODO(kradalby): error handle this | 				// TODO(kradalby): error handle this | ||||||
| 				_ = c.WaitForPeers(tsCount) | 				_ = c.WaitForPeers(tsCount) | ||||||
| 			}(client) | 			}(client) | ||||||
| 		} | 		} | ||||||
| 		namespace.syncWaitGroup.Wait() | 		user.syncWaitGroup.Wait() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return nil | ||||||
| @ -346,9 +346,9 @@ func (s *Scenario) WaitForTailscaleSync() error { | |||||||
| 
 | 
 | ||||||
| // CreateHeadscaleEnv is a conventient method returning a set up Headcale | // CreateHeadscaleEnv is a conventient method returning a set up Headcale | ||||||
| // test environment with nodes of all versions, joined to the server with X | // test environment with nodes of all versions, joined to the server with X | ||||||
| // namespaces. | // users. | ||||||
| func (s *Scenario) CreateHeadscaleEnv( | func (s *Scenario) CreateHeadscaleEnv( | ||||||
| 	namespaces map[string]int, | 	users map[string]int, | ||||||
| 	tsOpts []tsic.Option, | 	tsOpts []tsic.Option, | ||||||
| 	opts ...hsic.Option, | 	opts ...hsic.Option, | ||||||
| ) error { | ) error { | ||||||
| @ -357,23 +357,23 @@ func (s *Scenario) CreateHeadscaleEnv( | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for namespaceName, clientCount := range namespaces { | 	for userName, clientCount := range users { | ||||||
| 		err = s.CreateNamespace(namespaceName) | 		err = s.CreateUser(userName) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		err = s.CreateTailscaleNodesInNamespace(namespaceName, "all", clientCount, tsOpts...) | 		err = s.CreateTailscaleNodesInUser(userName, "all", clientCount, tsOpts...) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		key, err := s.CreatePreAuthKey(namespaceName, true, false) | 		key, err := s.CreatePreAuthKey(userName, true, false) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		err = s.RunTailscaleUp(namespaceName, headscale.GetEndpoint(), key.GetKey()) | 		err = s.RunTailscaleUp(userName, headscale.GetEndpoint(), key.GetKey()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| @ -382,9 +382,9 @@ func (s *Scenario) CreateHeadscaleEnv( | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Scenario) GetIPs(namespace string) ([]netip.Addr, error) { | func (s *Scenario) GetIPs(user string) ([]netip.Addr, error) { | ||||||
| 	var ips []netip.Addr | 	var ips []netip.Addr | ||||||
| 	if ns, ok := s.namespaces[namespace]; ok { | 	if ns, ok := s.users[user]; ok { | ||||||
| 		for _, client := range ns.Clients { | 		for _, client := range ns.Clients { | ||||||
| 			clientIps, err := client.IPs() | 			clientIps, err := client.IPs() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| @ -396,12 +396,12 @@ func (s *Scenario) GetIPs(namespace string) ([]netip.Addr, error) { | |||||||
| 		return ips, nil | 		return ips, nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return ips, fmt.Errorf("failed to get ips: %w", errNoNamespaceAvailable) | 	return ips, fmt.Errorf("failed to get ips: %w", errNoUserAvailable) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Scenario) GetClients(namespace string) ([]TailscaleClient, error) { | func (s *Scenario) GetClients(user string) ([]TailscaleClient, error) { | ||||||
| 	var clients []TailscaleClient | 	var clients []TailscaleClient | ||||||
| 	if ns, ok := s.namespaces[namespace]; ok { | 	if ns, ok := s.users[user]; ok { | ||||||
| 		for _, client := range ns.Clients { | 		for _, client := range ns.Clients { | ||||||
| 			clients = append(clients, client) | 			clients = append(clients, client) | ||||||
| 		} | 		} | ||||||
| @ -409,18 +409,18 @@ func (s *Scenario) GetClients(namespace string) ([]TailscaleClient, error) { | |||||||
| 		return clients, nil | 		return clients, nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return clients, fmt.Errorf("failed to get clients: %w", errNoNamespaceAvailable) | 	return clients, fmt.Errorf("failed to get clients: %w", errNoUserAvailable) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Scenario) ListTailscaleClients(namespaces ...string) ([]TailscaleClient, error) { | func (s *Scenario) ListTailscaleClients(users ...string) ([]TailscaleClient, error) { | ||||||
| 	var allClients []TailscaleClient | 	var allClients []TailscaleClient | ||||||
| 
 | 
 | ||||||
| 	if len(namespaces) == 0 { | 	if len(users) == 0 { | ||||||
| 		namespaces = s.Namespaces() | 		users = s.Users() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for _, namespace := range namespaces { | 	for _, user := range users { | ||||||
| 		clients, err := s.GetClients(namespace) | 		clients, err := s.GetClients(user) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
| @ -431,15 +431,15 @@ func (s *Scenario) ListTailscaleClients(namespaces ...string) ([]TailscaleClient | |||||||
| 	return allClients, nil | 	return allClients, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Scenario) ListTailscaleClientsIPs(namespaces ...string) ([]netip.Addr, error) { | func (s *Scenario) ListTailscaleClientsIPs(users ...string) ([]netip.Addr, error) { | ||||||
| 	var allIps []netip.Addr | 	var allIps []netip.Addr | ||||||
| 
 | 
 | ||||||
| 	if len(namespaces) == 0 { | 	if len(users) == 0 { | ||||||
| 		namespaces = s.Namespaces() | 		users = s.Users() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for _, namespace := range namespaces { | 	for _, user := range users { | ||||||
| 		ips, err := s.GetIPs(namespace) | 		ips, err := s.GetIPs(user) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
| @ -450,10 +450,10 @@ func (s *Scenario) ListTailscaleClientsIPs(namespaces ...string) ([]netip.Addr, | |||||||
| 	return allIps, nil | 	return allIps, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Scenario) ListTailscaleClientsFQDNs(namespaces ...string) ([]string, error) { | func (s *Scenario) ListTailscaleClientsFQDNs(users ...string) ([]string, error) { | ||||||
| 	allFQDNs := make([]string, 0) | 	allFQDNs := make([]string, 0) | ||||||
| 
 | 
 | ||||||
| 	clients, err := s.ListTailscaleClients(namespaces...) | 	clients, err := s.ListTailscaleClients(users...) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -471,17 +471,17 @@ func (s *Scenario) ListTailscaleClientsFQDNs(namespaces ...string) ([]string, er | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Scenario) WaitForTailscaleLogout() { | func (s *Scenario) WaitForTailscaleLogout() { | ||||||
| 	for _, namespace := range s.namespaces { | 	for _, user := range s.users { | ||||||
| 		for _, client := range namespace.Clients { | 		for _, client := range user.Clients { | ||||||
| 			namespace.syncWaitGroup.Add(1) | 			user.syncWaitGroup.Add(1) | ||||||
| 
 | 
 | ||||||
| 			go func(c TailscaleClient) { | 			go func(c TailscaleClient) { | ||||||
| 				defer namespace.syncWaitGroup.Done() | 				defer user.syncWaitGroup.Done() | ||||||
| 
 | 
 | ||||||
| 				// TODO(kradalby): error handle this | 				// TODO(kradalby): error handle this | ||||||
| 				_ = c.WaitForLogout() | 				_ = c.WaitForLogout() | ||||||
| 			}(client) | 			}(client) | ||||||
| 		} | 		} | ||||||
| 		namespace.syncWaitGroup.Wait() | 		user.syncWaitGroup.Wait() | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -31,7 +31,7 @@ func TestHeadscale(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	var err error | 	var err error | ||||||
| 
 | 
 | ||||||
| 	namespace := "test-space" | 	user := "test-space" | ||||||
| 
 | 
 | ||||||
| 	scenario, err := NewScenario() | 	scenario, err := NewScenario() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -50,19 +50,19 @@ func TestHeadscale(t *testing.T) { | |||||||
| 		} | 		} | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	t.Run("create-namespace", func(t *testing.T) { | 	t.Run("create-user", func(t *testing.T) { | ||||||
| 		err := scenario.CreateNamespace(namespace) | 		err := scenario.CreateUser(user) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			t.Errorf("failed to create namespace: %s", err) | 			t.Errorf("failed to create user: %s", err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if _, ok := scenario.namespaces[namespace]; !ok { | 		if _, ok := scenario.users[user]; !ok { | ||||||
| 			t.Errorf("namespace is not in scenario") | 			t.Errorf("user is not in scenario") | ||||||
| 		} | 		} | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	t.Run("create-auth-key", func(t *testing.T) { | 	t.Run("create-auth-key", func(t *testing.T) { | ||||||
| 		_, err := scenario.CreatePreAuthKey(namespace, true, false) | 		_, err := scenario.CreatePreAuthKey(user, true, false) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			t.Errorf("failed to create preauthkey: %s", err) | 			t.Errorf("failed to create preauthkey: %s", err) | ||||||
| 		} | 		} | ||||||
| @ -82,24 +82,24 @@ func TestCreateTailscale(t *testing.T) { | |||||||
| 	IntegrationSkip(t) | 	IntegrationSkip(t) | ||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
| 
 | 
 | ||||||
| 	namespace := "only-create-containers" | 	user := "only-create-containers" | ||||||
| 
 | 
 | ||||||
| 	scenario, err := NewScenario() | 	scenario, err := NewScenario() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("failed to create scenario: %s", err) | 		t.Errorf("failed to create scenario: %s", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	scenario.namespaces[namespace] = &Namespace{ | 	scenario.users[user] = &User{ | ||||||
| 		Clients: make(map[string]TailscaleClient), | 		Clients: make(map[string]TailscaleClient), | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	t.Run("create-tailscale", func(t *testing.T) { | 	t.Run("create-tailscale", func(t *testing.T) { | ||||||
| 		err := scenario.CreateTailscaleNodesInNamespace(namespace, "all", 3) | 		err := scenario.CreateTailscaleNodesInUser(user, "all", 3) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			t.Errorf("failed to add tailscale nodes: %s", err) | 			t.Errorf("failed to add tailscale nodes: %s", err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if clients := len(scenario.namespaces[namespace].Clients); clients != 3 { | 		if clients := len(scenario.users[user].Clients); clients != 3 { | ||||||
| 			t.Errorf("wrong number of tailscale clients: %d != %d", clients, 3) | 			t.Errorf("wrong number of tailscale clients: %d != %d", clients, 3) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| @ -122,7 +122,7 @@ func TestTailscaleNodesJoiningHeadcale(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	var err error | 	var err error | ||||||
| 
 | 
 | ||||||
| 	namespace := "join-node-test" | 	user := "join-node-test" | ||||||
| 
 | 
 | ||||||
| 	count := 1 | 	count := 1 | ||||||
| 
 | 
 | ||||||
| @ -143,30 +143,30 @@ func TestTailscaleNodesJoiningHeadcale(t *testing.T) { | |||||||
| 		} | 		} | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	t.Run("create-namespace", func(t *testing.T) { | 	t.Run("create-user", func(t *testing.T) { | ||||||
| 		err := scenario.CreateNamespace(namespace) | 		err := scenario.CreateUser(user) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			t.Errorf("failed to create namespace: %s", err) | 			t.Errorf("failed to create user: %s", err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if _, ok := scenario.namespaces[namespace]; !ok { | 		if _, ok := scenario.users[user]; !ok { | ||||||
| 			t.Errorf("namespace is not in scenario") | 			t.Errorf("user is not in scenario") | ||||||
| 		} | 		} | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	t.Run("create-tailscale", func(t *testing.T) { | 	t.Run("create-tailscale", func(t *testing.T) { | ||||||
| 		err := scenario.CreateTailscaleNodesInNamespace(namespace, "1.30.2", count) | 		err := scenario.CreateTailscaleNodesInUser(user, "1.30.2", count) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			t.Errorf("failed to add tailscale nodes: %s", err) | 			t.Errorf("failed to add tailscale nodes: %s", err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if clients := len(scenario.namespaces[namespace].Clients); clients != count { | 		if clients := len(scenario.users[user].Clients); clients != count { | ||||||
| 			t.Errorf("wrong number of tailscale clients: %d != %d", clients, count) | 			t.Errorf("wrong number of tailscale clients: %d != %d", clients, count) | ||||||
| 		} | 		} | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	t.Run("join-headscale", func(t *testing.T) { | 	t.Run("join-headscale", func(t *testing.T) { | ||||||
| 		key, err := scenario.CreatePreAuthKey(namespace, true, false) | 		key, err := scenario.CreatePreAuthKey(user, true, false) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			t.Errorf("failed to create preauthkey: %s", err) | 			t.Errorf("failed to create preauthkey: %s", err) | ||||||
| 		} | 		} | ||||||
| @ -177,7 +177,7 @@ func TestTailscaleNodesJoiningHeadcale(t *testing.T) { | |||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		err = scenario.RunTailscaleUp( | 		err = scenario.RunTailscaleUp( | ||||||
| 			namespace, | 			user, | ||||||
| 			headscale.GetEndpoint(), | 			headscale.GetEndpoint(), | ||||||
| 			key.GetKey(), | 			key.GetKey(), | ||||||
| 		) | 		) | ||||||
| @ -187,7 +187,7 @@ func TestTailscaleNodesJoiningHeadcale(t *testing.T) { | |||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	t.Run("get-ips", func(t *testing.T) { | 	t.Run("get-ips", func(t *testing.T) { | ||||||
| 		ips, err := scenario.GetIPs(namespace) | 		ips, err := scenario.GetIPs(user) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			t.Errorf("failed to get tailscale ips: %s", err) | 			t.Errorf("failed to get tailscale ips: %s", err) | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -41,7 +41,7 @@ var retry = func(times int, sleepInterval time.Duration, | |||||||
| 	return result, stderr, err | 	return result, stderr, err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestSSHOneNamespaceAllToAll(t *testing.T) { | func TestSSHOneUserAllToAll(t *testing.T) { | ||||||
| 	IntegrationSkip(t) | 	IntegrationSkip(t) | ||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
| 
 | 
 | ||||||
| @ -51,7 +51,7 @@ func TestSSHOneNamespaceAllToAll(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	spec := map[string]int{ | 	spec := map[string]int{ | ||||||
| 		"namespace1": len(TailscaleVersions) - 5, | 		"user1": len(TailscaleVersions) - 5, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = scenario.CreateHeadscaleEnv(spec, | 	err = scenario.CreateHeadscaleEnv(spec, | ||||||
| @ -59,7 +59,7 @@ func TestSSHOneNamespaceAllToAll(t *testing.T) { | |||||||
| 		hsic.WithACLPolicy( | 		hsic.WithACLPolicy( | ||||||
| 			&headscale.ACLPolicy{ | 			&headscale.ACLPolicy{ | ||||||
| 				Groups: map[string][]string{ | 				Groups: map[string][]string{ | ||||||
| 					"group:integration-test": {"namespace1"}, | 					"group:integration-test": {"user1"}, | ||||||
| 				}, | 				}, | ||||||
| 				ACLs: []headscale.ACL{ | 				ACLs: []headscale.ACL{ | ||||||
| 					{ | 					{ | ||||||
| @ -117,7 +117,7 @@ func TestSSHOneNamespaceAllToAll(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestSSHMultipleNamespacesAllToAll(t *testing.T) { | func TestSSHMultipleUsersAllToAll(t *testing.T) { | ||||||
| 	IntegrationSkip(t) | 	IntegrationSkip(t) | ||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
| 
 | 
 | ||||||
| @ -127,8 +127,8 @@ func TestSSHMultipleNamespacesAllToAll(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	spec := map[string]int{ | 	spec := map[string]int{ | ||||||
| 		"namespace1": len(TailscaleVersions) - 5, | 		"user1": len(TailscaleVersions) - 5, | ||||||
| 		"namespace2": len(TailscaleVersions) - 5, | 		"user2": len(TailscaleVersions) - 5, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = scenario.CreateHeadscaleEnv(spec, | 	err = scenario.CreateHeadscaleEnv(spec, | ||||||
| @ -136,7 +136,7 @@ func TestSSHMultipleNamespacesAllToAll(t *testing.T) { | |||||||
| 		hsic.WithACLPolicy( | 		hsic.WithACLPolicy( | ||||||
| 			&headscale.ACLPolicy{ | 			&headscale.ACLPolicy{ | ||||||
| 				Groups: map[string][]string{ | 				Groups: map[string][]string{ | ||||||
| 					"group:integration-test": {"namespace1", "namespace2"}, | 					"group:integration-test": {"user1", "user2"}, | ||||||
| 				}, | 				}, | ||||||
| 				ACLs: []headscale.ACL{ | 				ACLs: []headscale.ACL{ | ||||||
| 					{ | 					{ | ||||||
| @ -163,12 +163,12 @@ func TestSSHMultipleNamespacesAllToAll(t *testing.T) { | |||||||
| 		t.Errorf("failed to create headscale environment: %s", err) | 		t.Errorf("failed to create headscale environment: %s", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	nsOneClients, err := scenario.ListTailscaleClients("namespace1") | 	nsOneClients, err := scenario.ListTailscaleClients("user1") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("failed to get clients: %s", err) | 		t.Errorf("failed to get clients: %s", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	nsTwoClients, err := scenario.ListTailscaleClients("namespace2") | 	nsTwoClients, err := scenario.ListTailscaleClients("user2") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("failed to get clients: %s", err) | 		t.Errorf("failed to get clients: %s", err) | ||||||
| 	} | 	} | ||||||
| @ -183,7 +183,7 @@ func TestSSHMultipleNamespacesAllToAll(t *testing.T) { | |||||||
| 		t.Errorf("failed to get FQDNs: %s", err) | 		t.Errorf("failed to get FQDNs: %s", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	testInterNamespaceSSH := func(sourceClients []TailscaleClient, targetClients []TailscaleClient) { | 	testInterUserSSH := func(sourceClients []TailscaleClient, targetClients []TailscaleClient) { | ||||||
| 		for _, client := range sourceClients { | 		for _, client := range sourceClients { | ||||||
| 			for _, peer := range targetClients { | 			for _, peer := range targetClients { | ||||||
| 				assertSSHHostname(t, client, peer) | 				assertSSHHostname(t, client, peer) | ||||||
| @ -191,8 +191,8 @@ func TestSSHMultipleNamespacesAllToAll(t *testing.T) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	testInterNamespaceSSH(nsOneClients, nsTwoClients) | 	testInterUserSSH(nsOneClients, nsTwoClients) | ||||||
| 	testInterNamespaceSSH(nsTwoClients, nsOneClients) | 	testInterUserSSH(nsTwoClients, nsOneClients) | ||||||
| 
 | 
 | ||||||
| 	err = scenario.Shutdown() | 	err = scenario.Shutdown() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -210,7 +210,7 @@ func TestSSHNoSSHConfigured(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	spec := map[string]int{ | 	spec := map[string]int{ | ||||||
| 		"namespace1": len(TailscaleVersions) - 5, | 		"user1": len(TailscaleVersions) - 5, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = scenario.CreateHeadscaleEnv(spec, | 	err = scenario.CreateHeadscaleEnv(spec, | ||||||
| @ -218,7 +218,7 @@ func TestSSHNoSSHConfigured(t *testing.T) { | |||||||
| 		hsic.WithACLPolicy( | 		hsic.WithACLPolicy( | ||||||
| 			&headscale.ACLPolicy{ | 			&headscale.ACLPolicy{ | ||||||
| 				Groups: map[string][]string{ | 				Groups: map[string][]string{ | ||||||
| 					"group:integration-test": {"namespace1"}, | 					"group:integration-test": {"user1"}, | ||||||
| 				}, | 				}, | ||||||
| 				ACLs: []headscale.ACL{ | 				ACLs: []headscale.ACL{ | ||||||
| 					{ | 					{ | ||||||
| @ -280,7 +280,7 @@ func TestSSHIsBlockedInACL(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	spec := map[string]int{ | 	spec := map[string]int{ | ||||||
| 		"namespace1": len(TailscaleVersions) - 5, | 		"user1": len(TailscaleVersions) - 5, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = scenario.CreateHeadscaleEnv(spec, | 	err = scenario.CreateHeadscaleEnv(spec, | ||||||
| @ -288,7 +288,7 @@ func TestSSHIsBlockedInACL(t *testing.T) { | |||||||
| 		hsic.WithACLPolicy( | 		hsic.WithACLPolicy( | ||||||
| 			&headscale.ACLPolicy{ | 			&headscale.ACLPolicy{ | ||||||
| 				Groups: map[string][]string{ | 				Groups: map[string][]string{ | ||||||
| 					"group:integration-test": {"namespace1"}, | 					"group:integration-test": {"user1"}, | ||||||
| 				}, | 				}, | ||||||
| 				ACLs: []headscale.ACL{ | 				ACLs: []headscale.ACL{ | ||||||
| 					{ | 					{ | ||||||
| @ -347,7 +347,7 @@ func TestSSHIsBlockedInACL(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestSSNamespaceOnlyIsolation(t *testing.T) { | func TestSSUserOnlyIsolation(t *testing.T) { | ||||||
| 	IntegrationSkip(t) | 	IntegrationSkip(t) | ||||||
| 	t.Parallel() | 	t.Parallel() | ||||||
| 
 | 
 | ||||||
| @ -357,8 +357,8 @@ func TestSSNamespaceOnlyIsolation(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	spec := map[string]int{ | 	spec := map[string]int{ | ||||||
| 		"namespaceacl1": len(TailscaleVersions) - 5, | 		"useracl1": len(TailscaleVersions) - 5, | ||||||
| 		"namespaceacl2": len(TailscaleVersions) - 5, | 		"useracl2": len(TailscaleVersions) - 5, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = scenario.CreateHeadscaleEnv(spec, | 	err = scenario.CreateHeadscaleEnv(spec, | ||||||
| @ -366,8 +366,8 @@ func TestSSNamespaceOnlyIsolation(t *testing.T) { | |||||||
| 		hsic.WithACLPolicy( | 		hsic.WithACLPolicy( | ||||||
| 			&headscale.ACLPolicy{ | 			&headscale.ACLPolicy{ | ||||||
| 				Groups: map[string][]string{ | 				Groups: map[string][]string{ | ||||||
| 					"group:ssh1": {"namespaceacl1"}, | 					"group:ssh1": {"useracl1"}, | ||||||
| 					"group:ssh2": {"namespaceacl2"}, | 					"group:ssh2": {"useracl2"}, | ||||||
| 				}, | 				}, | ||||||
| 				ACLs: []headscale.ACL{ | 				ACLs: []headscale.ACL{ | ||||||
| 					{ | 					{ | ||||||
| @ -392,7 +392,7 @@ func TestSSNamespaceOnlyIsolation(t *testing.T) { | |||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		), | 		), | ||||||
| 		hsic.WithTestName("sshtwonamespaceaclblock"), | 		hsic.WithTestName("sshtwouseraclblock"), | ||||||
| 		hsic.WithConfigEnv(map[string]string{ | 		hsic.WithConfigEnv(map[string]string{ | ||||||
| 			"HEADSCALE_EXPERIMENTAL_FEATURE_SSH": "1", | 			"HEADSCALE_EXPERIMENTAL_FEATURE_SSH": "1", | ||||||
| 		}), | 		}), | ||||||
| @ -401,12 +401,12 @@ func TestSSNamespaceOnlyIsolation(t *testing.T) { | |||||||
| 		t.Errorf("failed to create headscale environment: %s", err) | 		t.Errorf("failed to create headscale environment: %s", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ssh1Clients, err := scenario.ListTailscaleClients("namespaceacl1") | 	ssh1Clients, err := scenario.ListTailscaleClients("useracl1") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("failed to get clients: %s", err) | 		t.Errorf("failed to get clients: %s", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ssh2Clients, err := scenario.ListTailscaleClients("namespaceacl2") | 	ssh2Clients, err := scenario.ListTailscaleClients("useracl2") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("failed to get clients: %s", err) | 		t.Errorf("failed to get clients: %s", err) | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -138,12 +138,12 @@ func (s *IntegrationCLITestSuite) HandleStats( | |||||||
| 	s.stats = stats | 	s.stats = stats | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *IntegrationCLITestSuite) createNamespace(name string) (*v1.Namespace, error) { | func (s *IntegrationCLITestSuite) createUser(name string) (*v1.User, error) { | ||||||
| 	result, _, err := ExecuteCommand( | 	result, _, err := ExecuteCommand( | ||||||
| 		&s.headscale, | 		&s.headscale, | ||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"namespaces", | 			"users", | ||||||
| 			"create", | 			"create", | ||||||
| 			name, | 			name, | ||||||
| 			"--output", | 			"--output", | ||||||
| @ -155,38 +155,38 @@ func (s *IntegrationCLITestSuite) createNamespace(name string) (*v1.Namespace, e | |||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var namespace v1.Namespace | 	var user v1.User | ||||||
| 	err = json.Unmarshal([]byte(result), &namespace) | 	err = json.Unmarshal([]byte(result), &user) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return &namespace, nil | 	return &user, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *IntegrationCLITestSuite) TestNamespaceCommand() { | func (s *IntegrationCLITestSuite) TestUserCommand() { | ||||||
| 	names := []string{"namespace1", "otherspace", "tasty"} | 	names := []string{"user1", "otherspace", "tasty"} | ||||||
| 	namespaces := make([]*v1.Namespace, len(names)) | 	users := make([]*v1.User, len(names)) | ||||||
| 
 | 
 | ||||||
| 	for index, namespaceName := range names { | 	for index, userName := range names { | ||||||
| 		namespace, err := s.createNamespace(namespaceName) | 		user, err := s.createUser(userName) | ||||||
| 		assert.Nil(s.T(), err) | 		assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 		namespaces[index] = namespace | 		users[index] = user | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	assert.Len(s.T(), namespaces, len(names)) | 	assert.Len(s.T(), users, len(names)) | ||||||
| 
 | 
 | ||||||
| 	assert.Equal(s.T(), names[0], namespaces[0].Name) | 	assert.Equal(s.T(), names[0], users[0].Name) | ||||||
| 	assert.Equal(s.T(), names[1], namespaces[1].Name) | 	assert.Equal(s.T(), names[1], users[1].Name) | ||||||
| 	assert.Equal(s.T(), names[2], namespaces[2].Name) | 	assert.Equal(s.T(), names[2], users[2].Name) | ||||||
| 
 | 
 | ||||||
| 	// Test list namespaces | 	// Test list users | ||||||
| 	listResult, _, err := ExecuteCommand( | 	listResult, _, err := ExecuteCommand( | ||||||
| 		&s.headscale, | 		&s.headscale, | ||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"namespaces", | 			"users", | ||||||
| 			"list", | 			"list", | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| @ -195,20 +195,20 @@ func (s *IntegrationCLITestSuite) TestNamespaceCommand() { | |||||||
| 	) | 	) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	var listedNamespaces []v1.Namespace | 	var listedUsers []v1.User | ||||||
| 	err = json.Unmarshal([]byte(listResult), &listedNamespaces) | 	err = json.Unmarshal([]byte(listResult), &listedUsers) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	assert.Equal(s.T(), names[0], listedNamespaces[0].Name) | 	assert.Equal(s.T(), names[0], listedUsers[0].Name) | ||||||
| 	assert.Equal(s.T(), names[1], listedNamespaces[1].Name) | 	assert.Equal(s.T(), names[1], listedUsers[1].Name) | ||||||
| 	assert.Equal(s.T(), names[2], listedNamespaces[2].Name) | 	assert.Equal(s.T(), names[2], listedUsers[2].Name) | ||||||
| 
 | 
 | ||||||
| 	// Test rename namespace | 	// Test rename user | ||||||
| 	renameResult, _, err := ExecuteCommand( | 	renameResult, _, err := ExecuteCommand( | ||||||
| 		&s.headscale, | 		&s.headscale, | ||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"namespaces", | 			"users", | ||||||
| 			"rename", | 			"rename", | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| @ -219,18 +219,18 @@ func (s *IntegrationCLITestSuite) TestNamespaceCommand() { | |||||||
| 	) | 	) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	var renamedNamespace v1.Namespace | 	var renamedUser v1.User | ||||||
| 	err = json.Unmarshal([]byte(renameResult), &renamedNamespace) | 	err = json.Unmarshal([]byte(renameResult), &renamedUser) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	assert.Equal(s.T(), renamedNamespace.Name, "newname") | 	assert.Equal(s.T(), renamedUser.Name, "newname") | ||||||
| 
 | 
 | ||||||
| 	// Test list after rename namespaces | 	// Test list after rename users | ||||||
| 	listAfterRenameResult, _, err := ExecuteCommand( | 	listAfterRenameResult, _, err := ExecuteCommand( | ||||||
| 		&s.headscale, | 		&s.headscale, | ||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"namespaces", | 			"users", | ||||||
| 			"list", | 			"list", | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| @ -239,19 +239,19 @@ func (s *IntegrationCLITestSuite) TestNamespaceCommand() { | |||||||
| 	) | 	) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	var listedAfterRenameNamespaces []v1.Namespace | 	var listedAfterRenameUsers []v1.User | ||||||
| 	err = json.Unmarshal([]byte(listAfterRenameResult), &listedAfterRenameNamespaces) | 	err = json.Unmarshal([]byte(listAfterRenameResult), &listedAfterRenameUsers) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	assert.Equal(s.T(), names[0], listedAfterRenameNamespaces[0].Name) | 	assert.Equal(s.T(), names[0], listedAfterRenameUsers[0].Name) | ||||||
| 	assert.Equal(s.T(), names[1], listedAfterRenameNamespaces[1].Name) | 	assert.Equal(s.T(), names[1], listedAfterRenameUsers[1].Name) | ||||||
| 	assert.Equal(s.T(), "newname", listedAfterRenameNamespaces[2].Name) | 	assert.Equal(s.T(), "newname", listedAfterRenameUsers[2].Name) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *IntegrationCLITestSuite) TestPreAuthKeyCommand() { | func (s *IntegrationCLITestSuite) TestPreAuthKeyCommand() { | ||||||
| 	count := 5 | 	count := 5 | ||||||
| 
 | 
 | ||||||
| 	namespace, err := s.createNamespace("pre-auth-key-namespace") | 	user, err := s.createUser("pre-auth-key-user") | ||||||
| 
 | 
 | ||||||
| 	keys := make([]*v1.PreAuthKey, count) | 	keys := make([]*v1.PreAuthKey, count) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| @ -262,8 +262,8 @@ func (s *IntegrationCLITestSuite) TestPreAuthKeyCommand() { | |||||||
| 			[]string{ | 			[]string{ | ||||||
| 				"headscale", | 				"headscale", | ||||||
| 				"preauthkeys", | 				"preauthkeys", | ||||||
| 				"--namespace", | 				"--user", | ||||||
| 				namespace.Name, | 				user.Name, | ||||||
| 				"create", | 				"create", | ||||||
| 				"--reusable", | 				"--reusable", | ||||||
| 				"--expiration", | 				"--expiration", | ||||||
| @ -292,8 +292,8 @@ func (s *IntegrationCLITestSuite) TestPreAuthKeyCommand() { | |||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"preauthkeys", | 			"preauthkeys", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			namespace.Name, | 			user.Name, | ||||||
| 			"list", | 			"list", | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| @ -357,8 +357,8 @@ func (s *IntegrationCLITestSuite) TestPreAuthKeyCommand() { | |||||||
| 			[]string{ | 			[]string{ | ||||||
| 				"headscale", | 				"headscale", | ||||||
| 				"preauthkeys", | 				"preauthkeys", | ||||||
| 				"--namespace", | 				"--user", | ||||||
| 				namespace.Name, | 				user.Name, | ||||||
| 				"expire", | 				"expire", | ||||||
| 				listedPreAuthKeys[i].Key, | 				listedPreAuthKeys[i].Key, | ||||||
| 			}, | 			}, | ||||||
| @ -373,8 +373,8 @@ func (s *IntegrationCLITestSuite) TestPreAuthKeyCommand() { | |||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"preauthkeys", | 			"preauthkeys", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			namespace.Name, | 			user.Name, | ||||||
| 			"list", | 			"list", | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| @ -410,7 +410,7 @@ func (s *IntegrationCLITestSuite) TestPreAuthKeyCommand() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *IntegrationCLITestSuite) TestPreAuthKeyCommandWithoutExpiry() { | func (s *IntegrationCLITestSuite) TestPreAuthKeyCommandWithoutExpiry() { | ||||||
| 	namespace, err := s.createNamespace("pre-auth-key-without-exp-namespace") | 	user, err := s.createUser("pre-auth-key-without-exp-user") | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	preAuthResult, _, err := ExecuteCommand( | 	preAuthResult, _, err := ExecuteCommand( | ||||||
| @ -418,8 +418,8 @@ func (s *IntegrationCLITestSuite) TestPreAuthKeyCommandWithoutExpiry() { | |||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"preauthkeys", | 			"preauthkeys", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			namespace.Name, | 			user.Name, | ||||||
| 			"create", | 			"create", | ||||||
| 			"--reusable", | 			"--reusable", | ||||||
| 			"--output", | 			"--output", | ||||||
| @ -439,8 +439,8 @@ func (s *IntegrationCLITestSuite) TestPreAuthKeyCommandWithoutExpiry() { | |||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"preauthkeys", | 			"preauthkeys", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			namespace.Name, | 			user.Name, | ||||||
| 			"list", | 			"list", | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| @ -463,7 +463,7 @@ func (s *IntegrationCLITestSuite) TestPreAuthKeyCommandWithoutExpiry() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *IntegrationCLITestSuite) TestPreAuthKeyCommandReusableEphemeral() { | func (s *IntegrationCLITestSuite) TestPreAuthKeyCommandReusableEphemeral() { | ||||||
| 	namespace, err := s.createNamespace("pre-auth-key-reus-ephm-namespace") | 	user, err := s.createUser("pre-auth-key-reus-ephm-user") | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	preAuthReusableResult, _, err := ExecuteCommand( | 	preAuthReusableResult, _, err := ExecuteCommand( | ||||||
| @ -471,8 +471,8 @@ func (s *IntegrationCLITestSuite) TestPreAuthKeyCommandReusableEphemeral() { | |||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"preauthkeys", | 			"preauthkeys", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			namespace.Name, | 			user.Name, | ||||||
| 			"create", | 			"create", | ||||||
| 			"--reusable=true", | 			"--reusable=true", | ||||||
| 			"--output", | 			"--output", | ||||||
| @ -494,8 +494,8 @@ func (s *IntegrationCLITestSuite) TestPreAuthKeyCommandReusableEphemeral() { | |||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"preauthkeys", | 			"preauthkeys", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			namespace.Name, | 			user.Name, | ||||||
| 			"create", | 			"create", | ||||||
| 			"--ephemeral=true", | 			"--ephemeral=true", | ||||||
| 			"--output", | 			"--output", | ||||||
| @ -518,8 +518,8 @@ func (s *IntegrationCLITestSuite) TestPreAuthKeyCommandReusableEphemeral() { | |||||||
| 	// 	[]string{ | 	// 	[]string{ | ||||||
| 	// 		"headscale", | 	// 		"headscale", | ||||||
| 	// 		"preauthkeys", | 	// 		"preauthkeys", | ||||||
| 	// 		"--namespace", | 	// 		"--user", | ||||||
| 	// 		namespace.Name, | 	// 		user.Name, | ||||||
| 	// 		"create", | 	// 		"create", | ||||||
| 	// 		"--ephemeral", | 	// 		"--ephemeral", | ||||||
| 	// 		"--reusable", | 	// 		"--reusable", | ||||||
| @ -536,8 +536,8 @@ func (s *IntegrationCLITestSuite) TestPreAuthKeyCommandReusableEphemeral() { | |||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"preauthkeys", | 			"preauthkeys", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			namespace.Name, | 			user.Name, | ||||||
| 			"list", | 			"list", | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| @ -554,7 +554,7 @@ func (s *IntegrationCLITestSuite) TestPreAuthKeyCommandReusableEphemeral() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *IntegrationCLITestSuite) TestNodeTagCommand() { | func (s *IntegrationCLITestSuite) TestNodeTagCommand() { | ||||||
| 	namespace, err := s.createNamespace("machine-namespace") | 	user, err := s.createUser("machine-user") | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	machineKeys := []string{ | 	machineKeys := []string{ | ||||||
| @ -573,8 +573,8 @@ func (s *IntegrationCLITestSuite) TestNodeTagCommand() { | |||||||
| 				"create-node", | 				"create-node", | ||||||
| 				"--name", | 				"--name", | ||||||
| 				fmt.Sprintf("machine-%d", index+1), | 				fmt.Sprintf("machine-%d", index+1), | ||||||
| 				"--namespace", | 				"--user", | ||||||
| 				namespace.Name, | 				user.Name, | ||||||
| 				"--key", | 				"--key", | ||||||
| 				machineKey, | 				machineKey, | ||||||
| 				"--output", | 				"--output", | ||||||
| @ -589,8 +589,8 @@ func (s *IntegrationCLITestSuite) TestNodeTagCommand() { | |||||||
| 			[]string{ | 			[]string{ | ||||||
| 				"headscale", | 				"headscale", | ||||||
| 				"nodes", | 				"nodes", | ||||||
| 				"--namespace", | 				"--user", | ||||||
| 				namespace.Name, | 				user.Name, | ||||||
| 				"register", | 				"register", | ||||||
| 				"--key", | 				"--key", | ||||||
| 				machineKey, | 				machineKey, | ||||||
| @ -683,10 +683,10 @@ func (s *IntegrationCLITestSuite) TestNodeTagCommand() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *IntegrationCLITestSuite) TestNodeCommand() { | func (s *IntegrationCLITestSuite) TestNodeCommand() { | ||||||
| 	namespace, err := s.createNamespace("machine-namespace") | 	user, err := s.createUser("machine-user") | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	secondNamespace, err := s.createNamespace("other-namespace") | 	secondUser, err := s.createUser("other-user") | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	// Randomly generated machine keys | 	// Randomly generated machine keys | ||||||
| @ -709,8 +709,8 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { | |||||||
| 				"create-node", | 				"create-node", | ||||||
| 				"--name", | 				"--name", | ||||||
| 				fmt.Sprintf("machine-%d", index+1), | 				fmt.Sprintf("machine-%d", index+1), | ||||||
| 				"--namespace", | 				"--user", | ||||||
| 				namespace.Name, | 				user.Name, | ||||||
| 				"--key", | 				"--key", | ||||||
| 				machineKey, | 				machineKey, | ||||||
| 				"--output", | 				"--output", | ||||||
| @ -725,8 +725,8 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { | |||||||
| 			[]string{ | 			[]string{ | ||||||
| 				"headscale", | 				"headscale", | ||||||
| 				"nodes", | 				"nodes", | ||||||
| 				"--namespace", | 				"--user", | ||||||
| 				namespace.Name, | 				user.Name, | ||||||
| 				"register", | 				"register", | ||||||
| 				"--key", | 				"--key", | ||||||
| 				machineKey, | 				machineKey, | ||||||
| @ -778,14 +778,14 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { | |||||||
| 	assert.Equal(s.T(), "machine-4", listAll[3].Name) | 	assert.Equal(s.T(), "machine-4", listAll[3].Name) | ||||||
| 	assert.Equal(s.T(), "machine-5", listAll[4].Name) | 	assert.Equal(s.T(), "machine-5", listAll[4].Name) | ||||||
| 
 | 
 | ||||||
| 	otherNamespaceMachineKeys := []string{ | 	otherUserMachineKeys := []string{ | ||||||
| 		"nodekey:b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e", | 		"nodekey:b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e", | ||||||
| 		"nodekey:dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584", | 		"nodekey:dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584", | ||||||
| 	} | 	} | ||||||
| 	otherNamespaceMachines := make([]*v1.Machine, len(otherNamespaceMachineKeys)) | 	otherUserMachines := make([]*v1.Machine, len(otherUserMachineKeys)) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	for index, machineKey := range otherNamespaceMachineKeys { | 	for index, machineKey := range otherUserMachineKeys { | ||||||
| 		_, _, err := ExecuteCommand( | 		_, _, err := ExecuteCommand( | ||||||
| 			&s.headscale, | 			&s.headscale, | ||||||
| 			[]string{ | 			[]string{ | ||||||
| @ -793,9 +793,9 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { | |||||||
| 				"debug", | 				"debug", | ||||||
| 				"create-node", | 				"create-node", | ||||||
| 				"--name", | 				"--name", | ||||||
| 				fmt.Sprintf("otherNamespace-machine-%d", index+1), | 				fmt.Sprintf("otherUser-machine-%d", index+1), | ||||||
| 				"--namespace", | 				"--user", | ||||||
| 				secondNamespace.Name, | 				secondUser.Name, | ||||||
| 				"--key", | 				"--key", | ||||||
| 				machineKey, | 				machineKey, | ||||||
| 				"--output", | 				"--output", | ||||||
| @ -810,8 +810,8 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { | |||||||
| 			[]string{ | 			[]string{ | ||||||
| 				"headscale", | 				"headscale", | ||||||
| 				"nodes", | 				"nodes", | ||||||
| 				"--namespace", | 				"--user", | ||||||
| 				secondNamespace.Name, | 				secondUser.Name, | ||||||
| 				"register", | 				"register", | ||||||
| 				"--key", | 				"--key", | ||||||
| 				machineKey, | 				machineKey, | ||||||
| @ -826,13 +826,13 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { | |||||||
| 		err = json.Unmarshal([]byte(machineResult), &machine) | 		err = json.Unmarshal([]byte(machineResult), &machine) | ||||||
| 		assert.Nil(s.T(), err) | 		assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 		otherNamespaceMachines[index] = &machine | 		otherUserMachines[index] = &machine | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	assert.Len(s.T(), otherNamespaceMachines, len(otherNamespaceMachineKeys)) | 	assert.Len(s.T(), otherUserMachines, len(otherUserMachineKeys)) | ||||||
| 
 | 
 | ||||||
| 	// Test list all nodes after added otherNamespace | 	// Test list all nodes after added otherUser | ||||||
| 	listAllWithotherNamespaceResult, _, err := ExecuteCommand( | 	listAllWithotherUserResult, _, err := ExecuteCommand( | ||||||
| 		&s.headscale, | 		&s.headscale, | ||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| @ -845,31 +845,31 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { | |||||||
| 	) | 	) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	var listAllWithotherNamespace []v1.Machine | 	var listAllWithotherUser []v1.Machine | ||||||
| 	err = json.Unmarshal( | 	err = json.Unmarshal( | ||||||
| 		[]byte(listAllWithotherNamespaceResult), | 		[]byte(listAllWithotherUserResult), | ||||||
| 		&listAllWithotherNamespace, | 		&listAllWithotherUser, | ||||||
| 	) | 	) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	// All nodes, machines + otherNamespace | 	// All nodes, machines + otherUser | ||||||
| 	assert.Len(s.T(), listAllWithotherNamespace, 7) | 	assert.Len(s.T(), listAllWithotherUser, 7) | ||||||
| 
 | 
 | ||||||
| 	assert.Equal(s.T(), uint64(6), listAllWithotherNamespace[5].Id) | 	assert.Equal(s.T(), uint64(6), listAllWithotherUser[5].Id) | ||||||
| 	assert.Equal(s.T(), uint64(7), listAllWithotherNamespace[6].Id) | 	assert.Equal(s.T(), uint64(7), listAllWithotherUser[6].Id) | ||||||
| 
 | 
 | ||||||
| 	assert.Equal(s.T(), "otherNamespace-machine-1", listAllWithotherNamespace[5].Name) | 	assert.Equal(s.T(), "otherUser-machine-1", listAllWithotherUser[5].Name) | ||||||
| 	assert.Equal(s.T(), "otherNamespace-machine-2", listAllWithotherNamespace[6].Name) | 	assert.Equal(s.T(), "otherUser-machine-2", listAllWithotherUser[6].Name) | ||||||
| 
 | 
 | ||||||
| 	// Test list all nodes after added otherNamespace | 	// Test list all nodes after added otherUser | ||||||
| 	listOnlyotherNamespaceMachineNamespaceResult, _, err := ExecuteCommand( | 	listOnlyotherUserMachineUserResult, _, err := ExecuteCommand( | ||||||
| 		&s.headscale, | 		&s.headscale, | ||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"nodes", | 			"nodes", | ||||||
| 			"list", | 			"list", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			secondNamespace.Name, | 			secondUser.Name, | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| 		}, | 		}, | ||||||
| @ -877,27 +877,27 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { | |||||||
| 	) | 	) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	var listOnlyotherNamespaceMachineNamespace []v1.Machine | 	var listOnlyotherUserMachineUser []v1.Machine | ||||||
| 	err = json.Unmarshal( | 	err = json.Unmarshal( | ||||||
| 		[]byte(listOnlyotherNamespaceMachineNamespaceResult), | 		[]byte(listOnlyotherUserMachineUserResult), | ||||||
| 		&listOnlyotherNamespaceMachineNamespace, | 		&listOnlyotherUserMachineUser, | ||||||
| 	) | 	) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	assert.Len(s.T(), listOnlyotherNamespaceMachineNamespace, 2) | 	assert.Len(s.T(), listOnlyotherUserMachineUser, 2) | ||||||
| 
 | 
 | ||||||
| 	assert.Equal(s.T(), uint64(6), listOnlyotherNamespaceMachineNamespace[0].Id) | 	assert.Equal(s.T(), uint64(6), listOnlyotherUserMachineUser[0].Id) | ||||||
| 	assert.Equal(s.T(), uint64(7), listOnlyotherNamespaceMachineNamespace[1].Id) | 	assert.Equal(s.T(), uint64(7), listOnlyotherUserMachineUser[1].Id) | ||||||
| 
 | 
 | ||||||
| 	assert.Equal( | 	assert.Equal( | ||||||
| 		s.T(), | 		s.T(), | ||||||
| 		"otherNamespace-machine-1", | 		"otherUser-machine-1", | ||||||
| 		listOnlyotherNamespaceMachineNamespace[0].Name, | 		listOnlyotherUserMachineUser[0].Name, | ||||||
| 	) | 	) | ||||||
| 	assert.Equal( | 	assert.Equal( | ||||||
| 		s.T(), | 		s.T(), | ||||||
| 		"otherNamespace-machine-2", | 		"otherUser-machine-2", | ||||||
| 		listOnlyotherNamespaceMachineNamespace[1].Name, | 		listOnlyotherUserMachineUser[1].Name, | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	// Delete a machines | 	// Delete a machines | ||||||
| @ -918,15 +918,15 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { | |||||||
| 	) | 	) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	// Test: list main namespace after machine is deleted | 	// Test: list main user after machine is deleted | ||||||
| 	listOnlyMachineNamespaceAfterDeleteResult, _, err := ExecuteCommand( | 	listOnlyMachineUserAfterDeleteResult, _, err := ExecuteCommand( | ||||||
| 		&s.headscale, | 		&s.headscale, | ||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"nodes", | 			"nodes", | ||||||
| 			"list", | 			"list", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			namespace.Name, | 			user.Name, | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| 		}, | 		}, | ||||||
| @ -934,18 +934,18 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() { | |||||||
| 	) | 	) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	var listOnlyMachineNamespaceAfterDelete []v1.Machine | 	var listOnlyMachineUserAfterDelete []v1.Machine | ||||||
| 	err = json.Unmarshal( | 	err = json.Unmarshal( | ||||||
| 		[]byte(listOnlyMachineNamespaceAfterDeleteResult), | 		[]byte(listOnlyMachineUserAfterDeleteResult), | ||||||
| 		&listOnlyMachineNamespaceAfterDelete, | 		&listOnlyMachineUserAfterDelete, | ||||||
| 	) | 	) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	assert.Len(s.T(), listOnlyMachineNamespaceAfterDelete, 4) | 	assert.Len(s.T(), listOnlyMachineUserAfterDelete, 4) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *IntegrationCLITestSuite) TestNodeExpireCommand() { | func (s *IntegrationCLITestSuite) TestNodeExpireCommand() { | ||||||
| 	namespace, err := s.createNamespace("machine-expire-namespace") | 	user, err := s.createUser("machine-expire-user") | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	// Randomly generated machine keys | 	// Randomly generated machine keys | ||||||
| @ -968,8 +968,8 @@ func (s *IntegrationCLITestSuite) TestNodeExpireCommand() { | |||||||
| 				"create-node", | 				"create-node", | ||||||
| 				"--name", | 				"--name", | ||||||
| 				fmt.Sprintf("machine-%d", index+1), | 				fmt.Sprintf("machine-%d", index+1), | ||||||
| 				"--namespace", | 				"--user", | ||||||
| 				namespace.Name, | 				user.Name, | ||||||
| 				"--key", | 				"--key", | ||||||
| 				machineKey, | 				machineKey, | ||||||
| 				"--output", | 				"--output", | ||||||
| @ -984,8 +984,8 @@ func (s *IntegrationCLITestSuite) TestNodeExpireCommand() { | |||||||
| 			[]string{ | 			[]string{ | ||||||
| 				"headscale", | 				"headscale", | ||||||
| 				"nodes", | 				"nodes", | ||||||
| 				"--namespace", | 				"--user", | ||||||
| 				namespace.Name, | 				user.Name, | ||||||
| 				"register", | 				"register", | ||||||
| 				"--key", | 				"--key", | ||||||
| 				machineKey, | 				machineKey, | ||||||
| @ -1072,7 +1072,7 @@ func (s *IntegrationCLITestSuite) TestNodeExpireCommand() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *IntegrationCLITestSuite) TestNodeRenameCommand() { | func (s *IntegrationCLITestSuite) TestNodeRenameCommand() { | ||||||
| 	namespace, err := s.createNamespace("machine-rename-command") | 	user, err := s.createUser("machine-rename-command") | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	// Randomly generated machine keys | 	// Randomly generated machine keys | ||||||
| @ -1095,8 +1095,8 @@ func (s *IntegrationCLITestSuite) TestNodeRenameCommand() { | |||||||
| 				"create-node", | 				"create-node", | ||||||
| 				"--name", | 				"--name", | ||||||
| 				fmt.Sprintf("machine-%d", index+1), | 				fmt.Sprintf("machine-%d", index+1), | ||||||
| 				"--namespace", | 				"--user", | ||||||
| 				namespace.Name, | 				user.Name, | ||||||
| 				"--key", | 				"--key", | ||||||
| 				machineKey, | 				machineKey, | ||||||
| 				"--output", | 				"--output", | ||||||
| @ -1111,8 +1111,8 @@ func (s *IntegrationCLITestSuite) TestNodeRenameCommand() { | |||||||
| 			[]string{ | 			[]string{ | ||||||
| 				"headscale", | 				"headscale", | ||||||
| 				"nodes", | 				"nodes", | ||||||
| 				"--namespace", | 				"--user", | ||||||
| 				namespace.Name, | 				user.Name, | ||||||
| 				"register", | 				"register", | ||||||
| 				"--key", | 				"--key", | ||||||
| 				machineKey, | 				machineKey, | ||||||
| @ -1389,9 +1389,9 @@ func (s *IntegrationCLITestSuite) TestApiKeyCommand() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *IntegrationCLITestSuite) TestNodeMoveCommand() { | func (s *IntegrationCLITestSuite) TestNodeMoveCommand() { | ||||||
| 	oldNamespace, err := s.createNamespace("old-namespace") | 	oldUser, err := s.createUser("old-user") | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 	newNamespace, err := s.createNamespace("new-namespace") | 	newUser, err := s.createUser("new-user") | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	// Randomly generated machine key | 	// Randomly generated machine key | ||||||
| @ -1405,8 +1405,8 @@ func (s *IntegrationCLITestSuite) TestNodeMoveCommand() { | |||||||
| 			"create-node", | 			"create-node", | ||||||
| 			"--name", | 			"--name", | ||||||
| 			"nomad-machine", | 			"nomad-machine", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			oldNamespace.Name, | 			oldUser.Name, | ||||||
| 			"--key", | 			"--key", | ||||||
| 			machineKey, | 			machineKey, | ||||||
| 			"--output", | 			"--output", | ||||||
| @ -1421,8 +1421,8 @@ func (s *IntegrationCLITestSuite) TestNodeMoveCommand() { | |||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"nodes", | 			"nodes", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			oldNamespace.Name, | 			oldUser.Name, | ||||||
| 			"register", | 			"register", | ||||||
| 			"--key", | 			"--key", | ||||||
| 			machineKey, | 			machineKey, | ||||||
| @ -1439,7 +1439,7 @@ func (s *IntegrationCLITestSuite) TestNodeMoveCommand() { | |||||||
| 
 | 
 | ||||||
| 	assert.Equal(s.T(), uint64(1), machine.Id) | 	assert.Equal(s.T(), uint64(1), machine.Id) | ||||||
| 	assert.Equal(s.T(), "nomad-machine", machine.Name) | 	assert.Equal(s.T(), "nomad-machine", machine.Name) | ||||||
| 	assert.Equal(s.T(), machine.Namespace.Name, oldNamespace.Name) | 	assert.Equal(s.T(), machine.User.Name, oldUser.Name) | ||||||
| 
 | 
 | ||||||
| 	machineId := fmt.Sprintf("%d", machine.Id) | 	machineId := fmt.Sprintf("%d", machine.Id) | ||||||
| 
 | 
 | ||||||
| @ -1451,8 +1451,8 @@ func (s *IntegrationCLITestSuite) TestNodeMoveCommand() { | |||||||
| 			"move", | 			"move", | ||||||
| 			"--identifier", | 			"--identifier", | ||||||
| 			machineId, | 			machineId, | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			newNamespace.Name, | 			newUser.Name, | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| 		}, | 		}, | ||||||
| @ -1463,7 +1463,7 @@ func (s *IntegrationCLITestSuite) TestNodeMoveCommand() { | |||||||
| 	err = json.Unmarshal([]byte(moveToNewNSResult), &machine) | 	err = json.Unmarshal([]byte(moveToNewNSResult), &machine) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	assert.Equal(s.T(), machine.Namespace, newNamespace) | 	assert.Equal(s.T(), machine.User, newUser) | ||||||
| 
 | 
 | ||||||
| 	listAllNodesResult, _, err := ExecuteCommand( | 	listAllNodesResult, _, err := ExecuteCommand( | ||||||
| 		&s.headscale, | 		&s.headscale, | ||||||
| @ -1485,8 +1485,8 @@ func (s *IntegrationCLITestSuite) TestNodeMoveCommand() { | |||||||
| 	assert.Len(s.T(), allNodes, 1) | 	assert.Len(s.T(), allNodes, 1) | ||||||
| 
 | 
 | ||||||
| 	assert.Equal(s.T(), allNodes[0].Id, machine.Id) | 	assert.Equal(s.T(), allNodes[0].Id, machine.Id) | ||||||
| 	assert.Equal(s.T(), allNodes[0].Namespace, machine.Namespace) | 	assert.Equal(s.T(), allNodes[0].User, machine.User) | ||||||
| 	assert.Equal(s.T(), allNodes[0].Namespace, newNamespace) | 	assert.Equal(s.T(), allNodes[0].User, newUser) | ||||||
| 
 | 
 | ||||||
| 	moveToNonExistingNSResult, _, err := ExecuteCommand( | 	moveToNonExistingNSResult, _, err := ExecuteCommand( | ||||||
| 		&s.headscale, | 		&s.headscale, | ||||||
| @ -1496,8 +1496,8 @@ func (s *IntegrationCLITestSuite) TestNodeMoveCommand() { | |||||||
| 			"move", | 			"move", | ||||||
| 			"--identifier", | 			"--identifier", | ||||||
| 			machineId, | 			machineId, | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			"non-existing-namespace", | 			"non-existing-user", | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| 		}, | 		}, | ||||||
| @ -1508,9 +1508,9 @@ func (s *IntegrationCLITestSuite) TestNodeMoveCommand() { | |||||||
| 	assert.Contains( | 	assert.Contains( | ||||||
| 		s.T(), | 		s.T(), | ||||||
| 		string(moveToNonExistingNSResult), | 		string(moveToNonExistingNSResult), | ||||||
| 		"Namespace not found", | 		"User not found", | ||||||
| 	) | 	) | ||||||
| 	assert.Equal(s.T(), machine.Namespace, newNamespace) | 	assert.Equal(s.T(), machine.User, newUser) | ||||||
| 
 | 
 | ||||||
| 	moveToOldNSResult, _, err := ExecuteCommand( | 	moveToOldNSResult, _, err := ExecuteCommand( | ||||||
| 		&s.headscale, | 		&s.headscale, | ||||||
| @ -1520,8 +1520,8 @@ func (s *IntegrationCLITestSuite) TestNodeMoveCommand() { | |||||||
| 			"move", | 			"move", | ||||||
| 			"--identifier", | 			"--identifier", | ||||||
| 			machineId, | 			machineId, | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			oldNamespace.Name, | 			oldUser.Name, | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| 		}, | 		}, | ||||||
| @ -1532,7 +1532,7 @@ func (s *IntegrationCLITestSuite) TestNodeMoveCommand() { | |||||||
| 	err = json.Unmarshal([]byte(moveToOldNSResult), &machine) | 	err = json.Unmarshal([]byte(moveToOldNSResult), &machine) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	assert.Equal(s.T(), machine.Namespace, oldNamespace) | 	assert.Equal(s.T(), machine.User, oldUser) | ||||||
| 
 | 
 | ||||||
| 	moveToSameNSResult, _, err := ExecuteCommand( | 	moveToSameNSResult, _, err := ExecuteCommand( | ||||||
| 		&s.headscale, | 		&s.headscale, | ||||||
| @ -1542,8 +1542,8 @@ func (s *IntegrationCLITestSuite) TestNodeMoveCommand() { | |||||||
| 			"move", | 			"move", | ||||||
| 			"--identifier", | 			"--identifier", | ||||||
| 			machineId, | 			machineId, | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			oldNamespace.Name, | 			oldUser.Name, | ||||||
| 			"--output", | 			"--output", | ||||||
| 			"json", | 			"json", | ||||||
| 		}, | 		}, | ||||||
| @ -1554,7 +1554,7 @@ func (s *IntegrationCLITestSuite) TestNodeMoveCommand() { | |||||||
| 	err = json.Unmarshal([]byte(moveToSameNSResult), &machine) | 	err = json.Unmarshal([]byte(moveToSameNSResult), &machine) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	assert.Equal(s.T(), machine.Namespace, oldNamespace) | 	assert.Equal(s.T(), machine.User, oldUser) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *IntegrationCLITestSuite) TestLoadConfigFromCommand() { | func (s *IntegrationCLITestSuite) TestLoadConfigFromCommand() { | ||||||
|  | |||||||
| @ -47,7 +47,7 @@ var ( | |||||||
| 	} | 	} | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type TestNamespace struct { | type TestUser struct { | ||||||
| 	count      int | 	count      int | ||||||
| 	tailscales map[string]dockertest.Resource | 	tailscales map[string]dockertest.Resource | ||||||
| } | } | ||||||
| @ -296,7 +296,7 @@ func getMagicFQDN( | |||||||
| 		hostnames[index] = fmt.Sprintf( | 		hostnames[index] = fmt.Sprintf( | ||||||
| 			"%s.%s.headscale.net", | 			"%s.%s.headscale.net", | ||||||
| 			listAll[index].GetGivenName(), | 			listAll[index].GetGivenName(), | ||||||
| 			listAll[index].GetNamespace().GetName(), | 			listAll[index].GetUser().GetName(), | ||||||
| 		) | 		) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -26,7 +26,7 @@ import ( | |||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| 	headscaleDerpHostname = "headscale-derp" | 	headscaleDerpHostname = "headscale-derp" | ||||||
| 	namespaceName         = "derpnamespace" | 	userName         = "derpuser" | ||||||
| 	totalContainers       = 3 | 	totalContainers       = 3 | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @ -199,22 +199,22 @@ func (s *IntegrationDERPTestSuite) SetupSuite() { | |||||||
| 	} | 	} | ||||||
| 	log.Println("headscale container is ready for embedded DERP tests") | 	log.Println("headscale container is ready for embedded DERP tests") | ||||||
| 
 | 
 | ||||||
| 	log.Printf("Creating headscale namespace: %s\n", namespaceName) | 	log.Printf("Creating headscale user: %s\n", userName) | ||||||
| 	result, _, err := ExecuteCommand( | 	result, _, err := ExecuteCommand( | ||||||
| 		&s.headscale, | 		&s.headscale, | ||||||
| 		[]string{"headscale", "namespaces", "create", namespaceName}, | 		[]string{"headscale", "users", "create", userName}, | ||||||
| 		[]string{}, | 		[]string{}, | ||||||
| 	) | 	) | ||||||
| 	log.Println("headscale create namespace result: ", result) | 	log.Println("headscale create user result: ", result) | ||||||
| 	assert.Nil(s.T(), err) | 	assert.Nil(s.T(), err) | ||||||
| 
 | 
 | ||||||
| 	log.Printf("Creating pre auth key for %s\n", namespaceName) | 	log.Printf("Creating pre auth key for %s\n", userName) | ||||||
| 	preAuthResult, _, err := ExecuteCommand( | 	preAuthResult, _, err := ExecuteCommand( | ||||||
| 		&s.headscale, | 		&s.headscale, | ||||||
| 		[]string{ | 		[]string{ | ||||||
| 			"headscale", | 			"headscale", | ||||||
| 			"--namespace", | 			"--user", | ||||||
| 			namespaceName, | 			userName, | ||||||
| 			"preauthkeys", | 			"preauthkeys", | ||||||
| 			"create", | 			"create", | ||||||
| 			"--reusable", | 			"--reusable", | ||||||
|  | |||||||
							
								
								
									
										72
									
								
								machine.go
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								machine.go
									
									
									
									
									
								
							| @ -27,8 +27,8 @@ const ( | |||||||
| 	) | 	) | ||||||
| 	ErrCouldNotConvertMachineInterface = Error("failed to convert machine interface") | 	ErrCouldNotConvertMachineInterface = Error("failed to convert machine interface") | ||||||
| 	ErrHostnameTooLong                 = Error("Hostname too long") | 	ErrHostnameTooLong                 = Error("Hostname too long") | ||||||
| 	ErrDifferentRegisteredNamespace    = Error( | 	ErrDifferentRegisteredUser    = Error( | ||||||
| 		"machine was previously registered with a different namespace", | 		"machine was previously registered with a different user", | ||||||
| 	) | 	) | ||||||
| 	MachineGivenNameHashLength = 8 | 	MachineGivenNameHashLength = 8 | ||||||
| 	MachineGivenNameTrimSize   = 2 | 	MachineGivenNameTrimSize   = 2 | ||||||
| @ -57,8 +57,8 @@ type Machine struct { | |||||||
| 	// GivenName is the name used in all DNS related | 	// GivenName is the name used in all DNS related | ||||||
| 	// parts of headscale. | 	// parts of headscale. | ||||||
| 	GivenName   string `gorm:"type:varchar(63);unique_index"` | 	GivenName   string `gorm:"type:varchar(63);unique_index"` | ||||||
| 	NamespaceID uint | 	UserID uint | ||||||
| 	Namespace   Namespace `gorm:"foreignKey:NamespaceID"` | 	User   User `gorm:"foreignKey:UserID"` | ||||||
| 
 | 
 | ||||||
| 	RegisterMethod string | 	RegisterMethod string | ||||||
| 
 | 
 | ||||||
| @ -192,7 +192,7 @@ func getFilteredByACLPeers( | |||||||
| 		Msg("Finding peers filtered by ACLs") | 		Msg("Finding peers filtered by ACLs") | ||||||
| 
 | 
 | ||||||
| 	peers := make(map[uint64]Machine) | 	peers := make(map[uint64]Machine) | ||||||
| 	// Aclfilter peers here. We are itering through machines in all namespaces and search through the computed aclRules | 	// Aclfilter peers here. We are itering through machines in all users and search through the computed aclRules | ||||||
| 	// for match between rule SrcIPs and DstPorts. If the rule is a match we allow the machine to be viewable. | 	// for match between rule SrcIPs and DstPorts. If the rule is a match we allow the machine to be viewable. | ||||||
| 	machineIPs := machine.IPAddresses.ToStringSlice() | 	machineIPs := machine.IPAddresses.ToStringSlice() | ||||||
| 	for _, peer := range machines { | 	for _, peer := range machines { | ||||||
| @ -270,7 +270,7 @@ func (h *Headscale) ListPeers(machine *Machine) (Machines, error) { | |||||||
| 		Msg("Finding direct peers") | 		Msg("Finding direct peers") | ||||||
| 
 | 
 | ||||||
| 	machines := Machines{} | 	machines := Machines{} | ||||||
| 	if err := h.db.Preload("AuthKey").Preload("AuthKey.Namespace").Preload("Namespace").Where("node_key <> ?", | 	if err := h.db.Preload("AuthKey").Preload("AuthKey.User").Preload("User").Where("node_key <> ?", | ||||||
| 		machine.NodeKey).Find(&machines).Error; err != nil { | 		machine.NodeKey).Find(&machines).Error; err != nil { | ||||||
| 		log.Error().Err(err).Msg("Error accessing db") | 		log.Error().Err(err).Msg("Error accessing db") | ||||||
| 
 | 
 | ||||||
| @ -292,7 +292,7 @@ func (h *Headscale) getPeers(machine *Machine) (Machines, error) { | |||||||
| 	var err error | 	var err error | ||||||
| 
 | 
 | ||||||
| 	// If ACLs rules are defined, filter visible host list with the ACLs | 	// If ACLs rules are defined, filter visible host list with the ACLs | ||||||
| 	// else use the classic namespace scope | 	// else use the classic user scope | ||||||
| 	if h.aclPolicy != nil { | 	if h.aclPolicy != nil { | ||||||
| 		var machines []Machine | 		var machines []Machine | ||||||
| 		machines, err = h.ListMachines() | 		machines, err = h.ListMachines() | ||||||
| @ -343,7 +343,7 @@ func (h *Headscale) getValidPeers(machine *Machine) (Machines, error) { | |||||||
| 
 | 
 | ||||||
| func (h *Headscale) ListMachines() ([]Machine, error) { | func (h *Headscale) ListMachines() ([]Machine, error) { | ||||||
| 	machines := []Machine{} | 	machines := []Machine{} | ||||||
| 	if err := h.db.Preload("AuthKey").Preload("AuthKey.Namespace").Preload("Namespace").Find(&machines).Error; err != nil { | 	if err := h.db.Preload("AuthKey").Preload("AuthKey.User").Preload("User").Find(&machines).Error; err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -352,16 +352,16 @@ func (h *Headscale) ListMachines() ([]Machine, error) { | |||||||
| 
 | 
 | ||||||
| func (h *Headscale) ListMachinesByGivenName(givenName string) ([]Machine, error) { | func (h *Headscale) ListMachinesByGivenName(givenName string) ([]Machine, error) { | ||||||
| 	machines := []Machine{} | 	machines := []Machine{} | ||||||
| 	if err := h.db.Preload("AuthKey").Preload("AuthKey.Namespace").Preload("Namespace").Find(&machines).Where("given_name = ?", givenName).Error; err != nil { | 	if err := h.db.Preload("AuthKey").Preload("AuthKey.User").Preload("User").Find(&machines).Where("given_name = ?", givenName).Error; err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return machines, nil | 	return machines, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetMachine finds a Machine by name and namespace and returns the Machine struct. | // GetMachine finds a Machine by name and user and returns the Machine struct. | ||||||
| func (h *Headscale) GetMachine(namespace string, name string) (*Machine, error) { | func (h *Headscale) GetMachine(user string, name string) (*Machine, error) { | ||||||
| 	machines, err := h.ListMachinesInNamespace(namespace) | 	machines, err := h.ListMachinesByUser(user) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -375,9 +375,9 @@ func (h *Headscale) GetMachine(namespace string, name string) (*Machine, error) | |||||||
| 	return nil, ErrMachineNotFound | 	return nil, ErrMachineNotFound | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetMachineByGivenName finds a Machine by given name and namespace and returns the Machine struct. | // GetMachineByGivenName finds a Machine by given name and user and returns the Machine struct. | ||||||
| func (h *Headscale) GetMachineByGivenName(namespace string, givenName string) (*Machine, error) { | func (h *Headscale) GetMachineByGivenName(user string, givenName string) (*Machine, error) { | ||||||
| 	machines, err := h.ListMachinesInNamespace(namespace) | 	machines, err := h.ListMachinesByUser(user) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -394,7 +394,7 @@ func (h *Headscale) GetMachineByGivenName(namespace string, givenName string) (* | |||||||
| // GetMachineByID finds a Machine by ID and returns the Machine struct. | // GetMachineByID finds a Machine by ID and returns the Machine struct. | ||||||
| func (h *Headscale) GetMachineByID(id uint64) (*Machine, error) { | func (h *Headscale) GetMachineByID(id uint64) (*Machine, error) { | ||||||
| 	m := Machine{} | 	m := Machine{} | ||||||
| 	if result := h.db.Preload("AuthKey").Preload("Namespace").Find(&Machine{ID: id}).First(&m); result.Error != nil { | 	if result := h.db.Preload("AuthKey").Preload("User").Find(&Machine{ID: id}).First(&m); result.Error != nil { | ||||||
| 		return nil, result.Error | 		return nil, result.Error | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -406,7 +406,7 @@ func (h *Headscale) GetMachineByMachineKey( | |||||||
| 	machineKey key.MachinePublic, | 	machineKey key.MachinePublic, | ||||||
| ) (*Machine, error) { | ) (*Machine, error) { | ||||||
| 	m := Machine{} | 	m := Machine{} | ||||||
| 	if result := h.db.Preload("AuthKey").Preload("Namespace").First(&m, "machine_key = ?", MachinePublicKeyStripPrefix(machineKey)); result.Error != nil { | 	if result := h.db.Preload("AuthKey").Preload("User").First(&m, "machine_key = ?", MachinePublicKeyStripPrefix(machineKey)); result.Error != nil { | ||||||
| 		return nil, result.Error | 		return nil, result.Error | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -418,7 +418,7 @@ func (h *Headscale) GetMachineByNodeKey( | |||||||
| 	nodeKey key.NodePublic, | 	nodeKey key.NodePublic, | ||||||
| ) (*Machine, error) { | ) (*Machine, error) { | ||||||
| 	machine := Machine{} | 	machine := Machine{} | ||||||
| 	if result := h.db.Preload("AuthKey").Preload("Namespace").First(&machine, "node_key = ?", | 	if result := h.db.Preload("AuthKey").Preload("User").First(&machine, "node_key = ?", | ||||||
| 		NodePublicKeyStripPrefix(nodeKey)); result.Error != nil { | 		NodePublicKeyStripPrefix(nodeKey)); result.Error != nil { | ||||||
| 		return nil, result.Error | 		return nil, result.Error | ||||||
| 	} | 	} | ||||||
| @ -431,7 +431,7 @@ func (h *Headscale) GetMachineByAnyKey( | |||||||
| 	machineKey key.MachinePublic, nodeKey key.NodePublic, oldNodeKey key.NodePublic, | 	machineKey key.MachinePublic, nodeKey key.NodePublic, oldNodeKey key.NodePublic, | ||||||
| ) (*Machine, error) { | ) (*Machine, error) { | ||||||
| 	machine := Machine{} | 	machine := Machine{} | ||||||
| 	if result := h.db.Preload("AuthKey").Preload("Namespace").First(&machine, "machine_key = ? OR node_key = ? OR node_key = ?", | 	if result := h.db.Preload("AuthKey").Preload("User").First(&machine, "machine_key = ? OR node_key = ? OR node_key = ?", | ||||||
| 		MachinePublicKeyStripPrefix(machineKey), | 		MachinePublicKeyStripPrefix(machineKey), | ||||||
| 		NodePublicKeyStripPrefix(nodeKey), | 		NodePublicKeyStripPrefix(nodeKey), | ||||||
| 		NodePublicKeyStripPrefix(oldNodeKey)); result.Error != nil { | 		NodePublicKeyStripPrefix(oldNodeKey)); result.Error != nil { | ||||||
| @ -570,9 +570,9 @@ func (h *Headscale) isOutdated(machine *Machine) bool { | |||||||
| 		return true | 		return true | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Get the last update from all headscale namespaces to compare with our nodes | 	// Get the last update from all headscale users to compare with our nodes | ||||||
| 	// last update. | 	// last update. | ||||||
| 	// TODO(kradalby): Only request updates from namespaces where we can talk to nodes | 	// TODO(kradalby): Only request updates from users where we can talk to nodes | ||||||
| 	// This would mostly be for a bit of performance, and can be calculated based on | 	// This would mostly be for a bit of performance, and can be calculated based on | ||||||
| 	// ACLs. | 	// ACLs. | ||||||
| 	lastChange := h.getLastStateChange() | 	lastChange := h.getLastStateChange() | ||||||
| @ -720,7 +720,7 @@ func (h *Headscale) toNode( | |||||||
| 		hostname = fmt.Sprintf( | 		hostname = fmt.Sprintf( | ||||||
| 			"%s.%s.%s", | 			"%s.%s.%s", | ||||||
| 			machine.GivenName, | 			machine.GivenName, | ||||||
| 			machine.Namespace.Name, | 			machine.User.Name, | ||||||
| 			baseDomain, | 			baseDomain, | ||||||
| 		) | 		) | ||||||
| 		if len(hostname) > maxHostnameLength { | 		if len(hostname) > maxHostnameLength { | ||||||
| @ -744,7 +744,7 @@ func (h *Headscale) toNode( | |||||||
| 			strconv.FormatUint(machine.ID, Base10), | 			strconv.FormatUint(machine.ID, Base10), | ||||||
| 		), // in headscale, unlike tailcontrol server, IDs are permanent | 		), // in headscale, unlike tailcontrol server, IDs are permanent | ||||||
| 		Name:          hostname, | 		Name:          hostname, | ||||||
| 		User:          tailcfg.UserID(machine.NamespaceID), | 		User:          tailcfg.UserID(machine.UserID), | ||||||
| 		Key:           nodeKey, | 		Key:           nodeKey, | ||||||
| 		KeyExpiry:     keyExpiry, | 		KeyExpiry:     keyExpiry, | ||||||
| 		Machine:       machineKey, | 		Machine:       machineKey, | ||||||
| @ -782,7 +782,7 @@ func (machine *Machine) toProto() *v1.Machine { | |||||||
| 		IpAddresses: machine.IPAddresses.ToStringSlice(), | 		IpAddresses: machine.IPAddresses.ToStringSlice(), | ||||||
| 		Name:        machine.Hostname, | 		Name:        machine.Hostname, | ||||||
| 		GivenName:   machine.GivenName, | 		GivenName:   machine.GivenName, | ||||||
| 		Namespace:   machine.Namespace.toProto(), | 		User:   machine.User.toProto(), | ||||||
| 		ForcedTags:  machine.ForcedTags, | 		ForcedTags:  machine.ForcedTags, | ||||||
| 		Online:      machine.isOnline(), | 		Online:      machine.isOnline(), | ||||||
| 
 | 
 | ||||||
| @ -837,7 +837,7 @@ func getTags( | |||||||
| 		} | 		} | ||||||
| 		var found bool | 		var found bool | ||||||
| 		for _, owner := range owners { | 		for _, owner := range owners { | ||||||
| 			if machine.Namespace.Name == owner { | 			if machine.User.Name == owner { | ||||||
| 				found = true | 				found = true | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @ -859,7 +859,7 @@ func getTags( | |||||||
| 
 | 
 | ||||||
| func (h *Headscale) RegisterMachineFromAuthCallback( | func (h *Headscale) RegisterMachineFromAuthCallback( | ||||||
| 	nodeKeyStr string, | 	nodeKeyStr string, | ||||||
| 	namespaceName string, | 	userName string, | ||||||
| 	machineExpiry *time.Time, | 	machineExpiry *time.Time, | ||||||
| 	registrationMethod string, | 	registrationMethod string, | ||||||
| ) (*Machine, error) { | ) (*Machine, error) { | ||||||
| @ -871,28 +871,28 @@ func (h *Headscale) RegisterMachineFromAuthCallback( | |||||||
| 
 | 
 | ||||||
| 	log.Debug(). | 	log.Debug(). | ||||||
| 		Str("nodeKey", nodeKey.ShortString()). | 		Str("nodeKey", nodeKey.ShortString()). | ||||||
| 		Str("namespaceName", namespaceName). | 		Str("userName", userName). | ||||||
| 		Str("registrationMethod", registrationMethod). | 		Str("registrationMethod", registrationMethod). | ||||||
| 		Str("expiresAt", fmt.Sprintf("%v", machineExpiry)). | 		Str("expiresAt", fmt.Sprintf("%v", machineExpiry)). | ||||||
| 		Msg("Registering machine from API/CLI or auth callback") | 		Msg("Registering machine from API/CLI or auth callback") | ||||||
| 
 | 
 | ||||||
| 	if machineInterface, ok := h.registrationCache.Get(NodePublicKeyStripPrefix(nodeKey)); ok { | 	if machineInterface, ok := h.registrationCache.Get(NodePublicKeyStripPrefix(nodeKey)); ok { | ||||||
| 		if registrationMachine, ok := machineInterface.(Machine); ok { | 		if registrationMachine, ok := machineInterface.(Machine); ok { | ||||||
| 			namespace, err := h.GetNamespace(namespaceName) | 			user, err := h.GetUser(userName) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return nil, fmt.Errorf( | 				return nil, fmt.Errorf( | ||||||
| 					"failed to find namespace in register machine from auth callback, %w", | 					"failed to find user in register machine from auth callback, %w", | ||||||
| 					err, | 					err, | ||||||
| 				) | 				) | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			// Registration of expired machine with different namespace | 			// Registration of expired machine with different user | ||||||
| 			if registrationMachine.ID != 0 && | 			if registrationMachine.ID != 0 && | ||||||
| 				registrationMachine.NamespaceID != namespace.ID { | 				registrationMachine.UserID != user.ID { | ||||||
| 				return nil, ErrDifferentRegisteredNamespace | 				return nil, ErrDifferentRegisteredUser | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			registrationMachine.NamespaceID = namespace.ID | 			registrationMachine.UserID = user.ID | ||||||
| 			registrationMachine.RegisterMethod = registrationMethod | 			registrationMachine.RegisterMethod = registrationMethod | ||||||
| 
 | 
 | ||||||
| 			if machineExpiry != nil { | 			if machineExpiry != nil { | ||||||
| @ -923,7 +923,7 @@ func (h *Headscale) RegisterMachine(machine Machine, | |||||||
| 		Str("machine", machine.Hostname). | 		Str("machine", machine.Hostname). | ||||||
| 		Str("machine_key", machine.MachineKey). | 		Str("machine_key", machine.MachineKey). | ||||||
| 		Str("node_key", machine.NodeKey). | 		Str("node_key", machine.NodeKey). | ||||||
| 		Str("namespace", machine.Namespace.Name). | 		Str("user", machine.User.Name). | ||||||
| 		Msg("Registering machine") | 		Msg("Registering machine") | ||||||
| 
 | 
 | ||||||
| 	// If the machine exists and we had already IPs for it, we just save it | 	// If the machine exists and we had already IPs for it, we just save it | ||||||
| @ -939,7 +939,7 @@ func (h *Headscale) RegisterMachine(machine Machine, | |||||||
| 			Str("machine", machine.Hostname). | 			Str("machine", machine.Hostname). | ||||||
| 			Str("machine_key", machine.MachineKey). | 			Str("machine_key", machine.MachineKey). | ||||||
| 			Str("node_key", machine.NodeKey). | 			Str("node_key", machine.NodeKey). | ||||||
| 			Str("namespace", machine.Namespace.Name). | 			Str("user", machine.User.Name). | ||||||
| 			Msg("Machine authorized again") | 			Msg("Machine authorized again") | ||||||
| 
 | 
 | ||||||
| 		return &machine, nil | 		return &machine, nil | ||||||
| @ -1137,7 +1137,7 @@ func (h *Headscale) EnableAutoApprovedRoutes(machine *Machine) error { | |||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		for _, approvedAlias := range routeApprovers { | 		for _, approvedAlias := range routeApprovers { | ||||||
| 			if approvedAlias == machine.Namespace.Name { | 			if approvedAlias == machine.User.Name { | ||||||
| 				approvedRoutes = append(approvedRoutes, advertisedRoute) | 				approvedRoutes = append(approvedRoutes, advertisedRoute) | ||||||
| 			} else { | 			} else { | ||||||
| 				approvedIps, err := expandAlias([]Machine{*machine}, *h.aclPolicy, approvedAlias, h.cfg.OIDC.StripEmaildomain) | 				approvedIps, err := expandAlias([]Machine{*machine}, *h.aclPolicy, approvedAlias, h.cfg.OIDC.StripEmaildomain) | ||||||
|  | |||||||
							
								
								
									
										174
									
								
								machine_test.go
									
									
									
									
									
								
							
							
						
						
									
										174
									
								
								machine_test.go
									
									
									
									
									
								
							| @ -15,10 +15,10 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestGetMachine(c *check.C) { | func (s *Suite) TestGetMachine(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test") | 	user, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine("test", "testmachine") | 	_, err = app.GetMachine("test", "testmachine") | ||||||
| @ -30,7 +30,7 @@ func (s *Suite) TestGetMachine(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testmachine", | 		Hostname:       "testmachine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 	} | 	} | ||||||
| @ -41,10 +41,10 @@ func (s *Suite) TestGetMachine(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestGetMachineByID(c *check.C) { | func (s *Suite) TestGetMachineByID(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test") | 	user, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachineByID(0) | 	_, err = app.GetMachineByID(0) | ||||||
| @ -56,7 +56,7 @@ func (s *Suite) TestGetMachineByID(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testmachine", | 		Hostname:       "testmachine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 	} | 	} | ||||||
| @ -67,10 +67,10 @@ func (s *Suite) TestGetMachineByID(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestGetMachineByNodeKey(c *check.C) { | func (s *Suite) TestGetMachineByNodeKey(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test") | 	user, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachineByID(0) | 	_, err = app.GetMachineByID(0) | ||||||
| @ -85,7 +85,7 @@ func (s *Suite) TestGetMachineByNodeKey(c *check.C) { | |||||||
| 		NodeKey:        NodePublicKeyStripPrefix(nodeKey.Public()), | 		NodeKey:        NodePublicKeyStripPrefix(nodeKey.Public()), | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testmachine", | 		Hostname:       "testmachine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 	} | 	} | ||||||
| @ -96,10 +96,10 @@ func (s *Suite) TestGetMachineByNodeKey(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestGetMachineByAnyNodeKey(c *check.C) { | func (s *Suite) TestGetMachineByAnyNodeKey(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test") | 	user, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachineByID(0) | 	_, err = app.GetMachineByID(0) | ||||||
| @ -116,7 +116,7 @@ func (s *Suite) TestGetMachineByAnyNodeKey(c *check.C) { | |||||||
| 		NodeKey:        NodePublicKeyStripPrefix(nodeKey.Public()), | 		NodeKey:        NodePublicKeyStripPrefix(nodeKey.Public()), | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testmachine", | 		Hostname:       "testmachine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 	} | 	} | ||||||
| @ -127,7 +127,7 @@ func (s *Suite) TestGetMachineByAnyNodeKey(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestDeleteMachine(c *check.C) { | func (s *Suite) TestDeleteMachine(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test") | 	user, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 	machine := Machine{ | 	machine := Machine{ | ||||||
| 		ID:             0, | 		ID:             0, | ||||||
| @ -135,7 +135,7 @@ func (s *Suite) TestDeleteMachine(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testmachine", | 		Hostname:       "testmachine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(1), | 		AuthKeyID:      uint(1), | ||||||
| 	} | 	} | ||||||
| @ -144,12 +144,12 @@ func (s *Suite) TestDeleteMachine(c *check.C) { | |||||||
| 	err = app.DeleteMachine(&machine) | 	err = app.DeleteMachine(&machine) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine(namespace.Name, "testmachine") | 	_, err = app.GetMachine(user.Name, "testmachine") | ||||||
| 	c.Assert(err, check.NotNil) | 	c.Assert(err, check.NotNil) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestHardDeleteMachine(c *check.C) { | func (s *Suite) TestHardDeleteMachine(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test") | 	user, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 	machine := Machine{ | 	machine := Machine{ | ||||||
| 		ID:             0, | 		ID:             0, | ||||||
| @ -157,7 +157,7 @@ func (s *Suite) TestHardDeleteMachine(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testmachine3", | 		Hostname:       "testmachine3", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(1), | 		AuthKeyID:      uint(1), | ||||||
| 	} | 	} | ||||||
| @ -166,15 +166,15 @@ func (s *Suite) TestHardDeleteMachine(c *check.C) { | |||||||
| 	err = app.HardDeleteMachine(&machine) | 	err = app.HardDeleteMachine(&machine) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine(namespace.Name, "testmachine3") | 	_, err = app.GetMachine(user.Name, "testmachine3") | ||||||
| 	c.Assert(err, check.NotNil) | 	c.Assert(err, check.NotNil) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestListPeers(c *check.C) { | func (s *Suite) TestListPeers(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test") | 	user, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachineByID(0) | 	_, err = app.GetMachineByID(0) | ||||||
| @ -187,7 +187,7 @@ func (s *Suite) TestListPeers(c *check.C) { | |||||||
| 			NodeKey:        "bar" + strconv.Itoa(index), | 			NodeKey:        "bar" + strconv.Itoa(index), | ||||||
| 			DiscoKey:       "faa" + strconv.Itoa(index), | 			DiscoKey:       "faa" + strconv.Itoa(index), | ||||||
| 			Hostname:       "testmachine" + strconv.Itoa(index), | 			Hostname:       "testmachine" + strconv.Itoa(index), | ||||||
| 			NamespaceID:    namespace.ID, | 			UserID:    user.ID, | ||||||
| 			RegisterMethod: RegisterMethodAuthKey, | 			RegisterMethod: RegisterMethodAuthKey, | ||||||
| 			AuthKeyID:      uint(pak.ID), | 			AuthKeyID:      uint(pak.ID), | ||||||
| 		} | 		} | ||||||
| @ -208,18 +208,18 @@ func (s *Suite) TestListPeers(c *check.C) { | |||||||
| 
 | 
 | ||||||
| func (s *Suite) TestGetACLFilteredPeers(c *check.C) { | func (s *Suite) TestGetACLFilteredPeers(c *check.C) { | ||||||
| 	type base struct { | 	type base struct { | ||||||
| 		namespace *Namespace | 		user *User | ||||||
| 		key       *PreAuthKey | 		key       *PreAuthKey | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	stor := make([]base, 0) | 	stor := make([]base, 0) | ||||||
| 
 | 
 | ||||||
| 	for _, name := range []string{"test", "admin"} { | 	for _, name := range []string{"test", "admin"} { | ||||||
| 		namespace, err := app.CreateNamespace(name) | 		user, err := app.CreateUser(name) | ||||||
| 		c.Assert(err, check.IsNil) | 		c.Assert(err, check.IsNil) | ||||||
| 		pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 		pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 		c.Assert(err, check.IsNil) | 		c.Assert(err, check.IsNil) | ||||||
| 		stor = append(stor, base{namespace, pak}) | 		stor = append(stor, base{user, pak}) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	_, err := app.GetMachineByID(0) | 	_, err := app.GetMachineByID(0) | ||||||
| @ -235,7 +235,7 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) { | |||||||
| 				netip.MustParseAddr(fmt.Sprintf("100.64.0.%v", strconv.Itoa(index+1))), | 				netip.MustParseAddr(fmt.Sprintf("100.64.0.%v", strconv.Itoa(index+1))), | ||||||
| 			}, | 			}, | ||||||
| 			Hostname:       "testmachine" + strconv.Itoa(index), | 			Hostname:       "testmachine" + strconv.Itoa(index), | ||||||
| 			NamespaceID:    stor[index%2].namespace.ID, | 			UserID:    stor[index%2].user.ID, | ||||||
| 			RegisterMethod: RegisterMethodAuthKey, | 			RegisterMethod: RegisterMethodAuthKey, | ||||||
| 			AuthKeyID:      uint(stor[index%2].key.ID), | 			AuthKeyID:      uint(stor[index%2].key.ID), | ||||||
| 		} | 		} | ||||||
| @ -267,11 +267,11 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) { | |||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	adminMachine, err := app.GetMachineByID(1) | 	adminMachine, err := app.GetMachineByID(1) | ||||||
| 	c.Logf("Machine(%v), namespace: %v", adminMachine.Hostname, adminMachine.Namespace) | 	c.Logf("Machine(%v), user: %v", adminMachine.Hostname, adminMachine.User) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	testMachine, err := app.GetMachineByID(2) | 	testMachine, err := app.GetMachineByID(2) | ||||||
| 	c.Logf("Machine(%v), namespace: %v", testMachine.Hostname, testMachine.Namespace) | 	c.Logf("Machine(%v), user: %v", testMachine.Hostname, testMachine.User) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	machines, err := app.ListMachines() | 	machines, err := app.ListMachines() | ||||||
| @ -294,10 +294,10 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestExpireMachine(c *check.C) { | func (s *Suite) TestExpireMachine(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test") | 	user, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine("test", "testmachine") | 	_, err = app.GetMachine("test", "testmachine") | ||||||
| @ -309,7 +309,7 @@ func (s *Suite) TestExpireMachine(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testmachine", | 		Hostname:       "testmachine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 		Expiry:         &time.Time{}, | 		Expiry:         &time.Time{}, | ||||||
| @ -350,13 +350,13 @@ func (s *Suite) TestSerdeAddressStrignSlice(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestGenerateGivenName(c *check.C) { | func (s *Suite) TestGenerateGivenName(c *check.C) { | ||||||
| 	namespace1, err := app.CreateNamespace("namespace-1") | 	user1, err := app.CreateUser("user-1") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace1.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user1.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine("namespace-1", "testmachine") | 	_, err = app.GetMachine("user-1", "testmachine") | ||||||
| 	c.Assert(err, check.NotNil) | 	c.Assert(err, check.NotNil) | ||||||
| 
 | 
 | ||||||
| 	machine := &Machine{ | 	machine := &Machine{ | ||||||
| @ -366,38 +366,38 @@ func (s *Suite) TestGenerateGivenName(c *check.C) { | |||||||
| 		DiscoKey:       "disco-key-1", | 		DiscoKey:       "disco-key-1", | ||||||
| 		Hostname:       "hostname-1", | 		Hostname:       "hostname-1", | ||||||
| 		GivenName:      "hostname-1", | 		GivenName:      "hostname-1", | ||||||
| 		NamespaceID:    namespace1.ID, | 		UserID:    user1.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 	} | 	} | ||||||
| 	app.db.Save(machine) | 	app.db.Save(machine) | ||||||
| 
 | 
 | ||||||
| 	givenName, err := app.GenerateGivenName("machine-key-2", "hostname-2") | 	givenName, err := app.GenerateGivenName("machine-key-2", "hostname-2") | ||||||
| 	comment := check.Commentf("Same namespace, unique machines, unique hostnames, no conflict") | 	comment := check.Commentf("Same user, unique machines, unique hostnames, no conflict") | ||||||
| 	c.Assert(err, check.IsNil, comment) | 	c.Assert(err, check.IsNil, comment) | ||||||
| 	c.Assert(givenName, check.Equals, "hostname-2", comment) | 	c.Assert(givenName, check.Equals, "hostname-2", comment) | ||||||
| 
 | 
 | ||||||
| 	givenName, err = app.GenerateGivenName("machine-key-1", "hostname-1") | 	givenName, err = app.GenerateGivenName("machine-key-1", "hostname-1") | ||||||
| 	comment = check.Commentf("Same namespace, same machine, same hostname, no conflict") | 	comment = check.Commentf("Same user, same machine, same hostname, no conflict") | ||||||
| 	c.Assert(err, check.IsNil, comment) | 	c.Assert(err, check.IsNil, comment) | ||||||
| 	c.Assert(givenName, check.Equals, "hostname-1", comment) | 	c.Assert(givenName, check.Equals, "hostname-1", comment) | ||||||
| 
 | 
 | ||||||
| 	givenName, err = app.GenerateGivenName("machine-key-2", "hostname-1") | 	givenName, err = app.GenerateGivenName("machine-key-2", "hostname-1") | ||||||
| 	comment = check.Commentf("Same namespace, unique machines, same hostname, conflict") | 	comment = check.Commentf("Same user, unique machines, same hostname, conflict") | ||||||
| 	c.Assert(err, check.IsNil, comment) | 	c.Assert(err, check.IsNil, comment) | ||||||
| 	c.Assert(givenName, check.Matches, fmt.Sprintf("^hostname-1-[a-z0-9]{%d}$", MachineGivenNameHashLength), comment) | 	c.Assert(givenName, check.Matches, fmt.Sprintf("^hostname-1-[a-z0-9]{%d}$", MachineGivenNameHashLength), comment) | ||||||
| 
 | 
 | ||||||
| 	givenName, err = app.GenerateGivenName("machine-key-2", "hostname-1") | 	givenName, err = app.GenerateGivenName("machine-key-2", "hostname-1") | ||||||
| 	comment = check.Commentf("Unique namespaces, unique machines, same hostname, conflict") | 	comment = check.Commentf("Unique users, unique machines, same hostname, conflict") | ||||||
| 	c.Assert(err, check.IsNil, comment) | 	c.Assert(err, check.IsNil, comment) | ||||||
| 	c.Assert(givenName, check.Matches, fmt.Sprintf("^hostname-1-[a-z0-9]{%d}$", MachineGivenNameHashLength), comment) | 	c.Assert(givenName, check.Matches, fmt.Sprintf("^hostname-1-[a-z0-9]{%d}$", MachineGivenNameHashLength), comment) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestSetTags(c *check.C) { | func (s *Suite) TestSetTags(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test") | 	user, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine("test", "testmachine") | 	_, err = app.GetMachine("test", "testmachine") | ||||||
| @ -409,7 +409,7 @@ func (s *Suite) TestSetTags(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testmachine", | 		Hostname:       "testmachine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 	} | 	} | ||||||
| @ -457,7 +457,7 @@ func Test_getTags(t *testing.T) { | |||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				machine: Machine{ | 				machine: Machine{ | ||||||
| 					Namespace: Namespace{ | 					User: User{ | ||||||
| 						Name: "joe", | 						Name: "joe", | ||||||
| 					}, | 					}, | ||||||
| 					HostInfo: HostInfo{ | 					HostInfo: HostInfo{ | ||||||
| @ -478,7 +478,7 @@ func Test_getTags(t *testing.T) { | |||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				machine: Machine{ | 				machine: Machine{ | ||||||
| 					Namespace: Namespace{ | 					User: User{ | ||||||
| 						Name: "joe", | 						Name: "joe", | ||||||
| 					}, | 					}, | ||||||
| 					HostInfo: HostInfo{ | 					HostInfo: HostInfo{ | ||||||
| @ -499,7 +499,7 @@ func Test_getTags(t *testing.T) { | |||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				machine: Machine{ | 				machine: Machine{ | ||||||
| 					Namespace: Namespace{ | 					User: User{ | ||||||
| 						Name: "joe", | 						Name: "joe", | ||||||
| 					}, | 					}, | ||||||
| 					HostInfo: HostInfo{ | 					HostInfo: HostInfo{ | ||||||
| @ -524,7 +524,7 @@ func Test_getTags(t *testing.T) { | |||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				machine: Machine{ | 				machine: Machine{ | ||||||
| 					Namespace: Namespace{ | 					User: User{ | ||||||
| 						Name: "joe", | 						Name: "joe", | ||||||
| 					}, | 					}, | ||||||
| 					HostInfo: HostInfo{ | 					HostInfo: HostInfo{ | ||||||
| @ -541,7 +541,7 @@ func Test_getTags(t *testing.T) { | |||||||
| 			args: args{ | 			args: args{ | ||||||
| 				aclPolicy: nil, | 				aclPolicy: nil, | ||||||
| 				machine: Machine{ | 				machine: Machine{ | ||||||
| 					Namespace: Namespace{ | 					User: User{ | ||||||
| 						Name: "joe", | 						Name: "joe", | ||||||
| 					}, | 					}, | ||||||
| 					HostInfo: HostInfo{ | 					HostInfo: HostInfo{ | ||||||
| @ -607,21 +607,21 @@ func Test_getFilteredByACLPeers(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.1"), | 							netip.MustParseAddr("100.64.0.1"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						ID: 2, | 						ID: 2, | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.2"), | 							netip.MustParseAddr("100.64.0.2"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "marc"}, | 						User: User{Name: "marc"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						ID: 3, | 						ID: 3, | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.3"), | 							netip.MustParseAddr("100.64.0.3"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "mickael"}, | 						User: User{Name: "mickael"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				rules: []tailcfg.FilterRule{ // list of all ACLRules registered | 				rules: []tailcfg.FilterRule{ // list of all ACLRules registered | ||||||
| @ -635,19 +635,19 @@ func Test_getFilteredByACLPeers(t *testing.T) { | |||||||
| 				machine: &Machine{ // current machine | 				machine: &Machine{ // current machine | ||||||
| 					ID:          1, | 					ID:          1, | ||||||
| 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.1")}, | 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.1")}, | ||||||
| 					Namespace:   Namespace{Name: "joe"}, | 					User:   User{Name: "joe"}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			want: Machines{ | 			want: Machines{ | ||||||
| 				{ | 				{ | ||||||
| 					ID:          2, | 					ID:          2, | ||||||
| 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.2")}, | 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.2")}, | ||||||
| 					Namespace:   Namespace{Name: "marc"}, | 					User:   User{Name: "marc"}, | ||||||
| 				}, | 				}, | ||||||
| 				{ | 				{ | ||||||
| 					ID:          3, | 					ID:          3, | ||||||
| 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.3")}, | 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.3")}, | ||||||
| 					Namespace:   Namespace{Name: "mickael"}, | 					User:   User{Name: "mickael"}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| @ -660,21 +660,21 @@ func Test_getFilteredByACLPeers(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.1"), | 							netip.MustParseAddr("100.64.0.1"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						ID: 2, | 						ID: 2, | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.2"), | 							netip.MustParseAddr("100.64.0.2"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "marc"}, | 						User: User{Name: "marc"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						ID: 3, | 						ID: 3, | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.3"), | 							netip.MustParseAddr("100.64.0.3"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "mickael"}, | 						User: User{Name: "mickael"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				rules: []tailcfg.FilterRule{ // list of all ACLRules registered | 				rules: []tailcfg.FilterRule{ // list of all ACLRules registered | ||||||
| @ -688,14 +688,14 @@ func Test_getFilteredByACLPeers(t *testing.T) { | |||||||
| 				machine: &Machine{ // current machine | 				machine: &Machine{ // current machine | ||||||
| 					ID:          1, | 					ID:          1, | ||||||
| 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.1")}, | 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.1")}, | ||||||
| 					Namespace:   Namespace{Name: "joe"}, | 					User:   User{Name: "joe"}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			want: Machines{ | 			want: Machines{ | ||||||
| 				{ | 				{ | ||||||
| 					ID:          2, | 					ID:          2, | ||||||
| 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.2")}, | 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.2")}, | ||||||
| 					Namespace:   Namespace{Name: "marc"}, | 					User:   User{Name: "marc"}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| @ -708,21 +708,21 @@ func Test_getFilteredByACLPeers(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.1"), | 							netip.MustParseAddr("100.64.0.1"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						ID: 2, | 						ID: 2, | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.2"), | 							netip.MustParseAddr("100.64.0.2"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "marc"}, | 						User: User{Name: "marc"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						ID: 3, | 						ID: 3, | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.3"), | 							netip.MustParseAddr("100.64.0.3"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "mickael"}, | 						User: User{Name: "mickael"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				rules: []tailcfg.FilterRule{ // list of all ACLRules registered | 				rules: []tailcfg.FilterRule{ // list of all ACLRules registered | ||||||
| @ -736,14 +736,14 @@ func Test_getFilteredByACLPeers(t *testing.T) { | |||||||
| 				machine: &Machine{ // current machine | 				machine: &Machine{ // current machine | ||||||
| 					ID:          2, | 					ID:          2, | ||||||
| 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.2")}, | 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.2")}, | ||||||
| 					Namespace:   Namespace{Name: "marc"}, | 					User:   User{Name: "marc"}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			want: Machines{ | 			want: Machines{ | ||||||
| 				{ | 				{ | ||||||
| 					ID:          3, | 					ID:          3, | ||||||
| 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.3")}, | 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.3")}, | ||||||
| 					Namespace:   Namespace{Name: "mickael"}, | 					User:   User{Name: "mickael"}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| @ -756,21 +756,21 @@ func Test_getFilteredByACLPeers(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.1"), | 							netip.MustParseAddr("100.64.0.1"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						ID: 2, | 						ID: 2, | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.2"), | 							netip.MustParseAddr("100.64.0.2"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "marc"}, | 						User: User{Name: "marc"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						ID: 3, | 						ID: 3, | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.3"), | 							netip.MustParseAddr("100.64.0.3"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "mickael"}, | 						User: User{Name: "mickael"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				rules: []tailcfg.FilterRule{ // list of all ACLRules registered | 				rules: []tailcfg.FilterRule{ // list of all ACLRules registered | ||||||
| @ -786,7 +786,7 @@ func Test_getFilteredByACLPeers(t *testing.T) { | |||||||
| 					IPAddresses: MachineAddresses{ | 					IPAddresses: MachineAddresses{ | ||||||
| 						netip.MustParseAddr("100.64.0.1"), | 						netip.MustParseAddr("100.64.0.1"), | ||||||
| 					}, | 					}, | ||||||
| 					Namespace: Namespace{Name: "joe"}, | 					User: User{Name: "joe"}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			want: Machines{ | 			want: Machines{ | ||||||
| @ -795,7 +795,7 @@ func Test_getFilteredByACLPeers(t *testing.T) { | |||||||
| 					IPAddresses: MachineAddresses{ | 					IPAddresses: MachineAddresses{ | ||||||
| 						netip.MustParseAddr("100.64.0.2"), | 						netip.MustParseAddr("100.64.0.2"), | ||||||
| 					}, | 					}, | ||||||
| 					Namespace: Namespace{Name: "marc"}, | 					User: User{Name: "marc"}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| @ -808,21 +808,21 @@ func Test_getFilteredByACLPeers(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.1"), | 							netip.MustParseAddr("100.64.0.1"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						ID: 2, | 						ID: 2, | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.2"), | 							netip.MustParseAddr("100.64.0.2"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "marc"}, | 						User: User{Name: "marc"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						ID: 3, | 						ID: 3, | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.3"), | 							netip.MustParseAddr("100.64.0.3"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "mickael"}, | 						User: User{Name: "mickael"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				rules: []tailcfg.FilterRule{ // list of all ACLRules registered | 				rules: []tailcfg.FilterRule{ // list of all ACLRules registered | ||||||
| @ -838,7 +838,7 @@ func Test_getFilteredByACLPeers(t *testing.T) { | |||||||
| 					IPAddresses: MachineAddresses{ | 					IPAddresses: MachineAddresses{ | ||||||
| 						netip.MustParseAddr("100.64.0.2"), | 						netip.MustParseAddr("100.64.0.2"), | ||||||
| 					}, | 					}, | ||||||
| 					Namespace: Namespace{Name: "marc"}, | 					User: User{Name: "marc"}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			want: Machines{ | 			want: Machines{ | ||||||
| @ -847,14 +847,14 @@ func Test_getFilteredByACLPeers(t *testing.T) { | |||||||
| 					IPAddresses: MachineAddresses{ | 					IPAddresses: MachineAddresses{ | ||||||
| 						netip.MustParseAddr("100.64.0.1"), | 						netip.MustParseAddr("100.64.0.1"), | ||||||
| 					}, | 					}, | ||||||
| 					Namespace: Namespace{Name: "joe"}, | 					User: User{Name: "joe"}, | ||||||
| 				}, | 				}, | ||||||
| 				{ | 				{ | ||||||
| 					ID: 3, | 					ID: 3, | ||||||
| 					IPAddresses: MachineAddresses{ | 					IPAddresses: MachineAddresses{ | ||||||
| 						netip.MustParseAddr("100.64.0.3"), | 						netip.MustParseAddr("100.64.0.3"), | ||||||
| 					}, | 					}, | ||||||
| 					Namespace: Namespace{Name: "mickael"}, | 					User: User{Name: "mickael"}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| @ -867,21 +867,21 @@ func Test_getFilteredByACLPeers(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.1"), | 							netip.MustParseAddr("100.64.0.1"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						ID: 2, | 						ID: 2, | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.2"), | 							netip.MustParseAddr("100.64.0.2"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "marc"}, | 						User: User{Name: "marc"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						ID: 3, | 						ID: 3, | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.3"), | 							netip.MustParseAddr("100.64.0.3"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "mickael"}, | 						User: User{Name: "mickael"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				rules: []tailcfg.FilterRule{ // list of all ACLRules registered | 				rules: []tailcfg.FilterRule{ // list of all ACLRules registered | ||||||
| @ -895,7 +895,7 @@ func Test_getFilteredByACLPeers(t *testing.T) { | |||||||
| 				machine: &Machine{ // current machine | 				machine: &Machine{ // current machine | ||||||
| 					ID:          2, | 					ID:          2, | ||||||
| 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.2")}, | 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.2")}, | ||||||
| 					Namespace:   Namespace{Name: "marc"}, | 					User:   User{Name: "marc"}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			want: Machines{ | 			want: Machines{ | ||||||
| @ -904,12 +904,12 @@ func Test_getFilteredByACLPeers(t *testing.T) { | |||||||
| 					IPAddresses: MachineAddresses{ | 					IPAddresses: MachineAddresses{ | ||||||
| 						netip.MustParseAddr("100.64.0.1"), | 						netip.MustParseAddr("100.64.0.1"), | ||||||
| 					}, | 					}, | ||||||
| 					Namespace: Namespace{Name: "joe"}, | 					User: User{Name: "joe"}, | ||||||
| 				}, | 				}, | ||||||
| 				{ | 				{ | ||||||
| 					ID:          3, | 					ID:          3, | ||||||
| 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.3")}, | 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.3")}, | ||||||
| 					Namespace:   Namespace{Name: "mickael"}, | 					User:   User{Name: "mickael"}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| @ -922,21 +922,21 @@ func Test_getFilteredByACLPeers(t *testing.T) { | |||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.1"), | 							netip.MustParseAddr("100.64.0.1"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "joe"}, | 						User: User{Name: "joe"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						ID: 2, | 						ID: 2, | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.2"), | 							netip.MustParseAddr("100.64.0.2"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "marc"}, | 						User: User{Name: "marc"}, | ||||||
| 					}, | 					}, | ||||||
| 					{ | 					{ | ||||||
| 						ID: 3, | 						ID: 3, | ||||||
| 						IPAddresses: MachineAddresses{ | 						IPAddresses: MachineAddresses{ | ||||||
| 							netip.MustParseAddr("100.64.0.3"), | 							netip.MustParseAddr("100.64.0.3"), | ||||||
| 						}, | 						}, | ||||||
| 						Namespace: Namespace{Name: "mickael"}, | 						User: User{Name: "mickael"}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| 				rules: []tailcfg.FilterRule{ // list of all ACLRules registered | 				rules: []tailcfg.FilterRule{ // list of all ACLRules registered | ||||||
| @ -944,7 +944,7 @@ func Test_getFilteredByACLPeers(t *testing.T) { | |||||||
| 				machine: &Machine{ // current machine | 				machine: &Machine{ // current machine | ||||||
| 					ID:          2, | 					ID:          2, | ||||||
| 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.2")}, | 					IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.2")}, | ||||||
| 					Namespace:   Namespace{Name: "marc"}, | 					User:   User{Name: "marc"}, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
| 			want: Machines{}, | 			want: Machines{}, | ||||||
| @ -1125,10 +1125,10 @@ func (s *Suite) TestAutoApproveRoutes(c *check.C) { | |||||||
| 	err := app.LoadACLPolicy("./tests/acls/acl_policy_autoapprovers.hujson") | 	err := app.LoadACLPolicy("./tests/acls/acl_policy_autoapprovers.hujson") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	namespace, err := app.CreateNamespace("test") | 	user, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	nodeKey := key.NewNode() | 	nodeKey := key.NewNode() | ||||||
| @ -1144,7 +1144,7 @@ func (s *Suite) TestAutoApproveRoutes(c *check.C) { | |||||||
| 		NodeKey:        NodePublicKeyStripPrefix(nodeKey.Public()), | 		NodeKey:        NodePublicKeyStripPrefix(nodeKey.Public()), | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "test", | 		Hostname:       "test", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 		HostInfo: HostInfo{ | 		HostInfo: HostInfo{ | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								metrics.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								metrics.go
									
									
									
									
									
								
							| @ -8,34 +8,34 @@ import ( | |||||||
| const prometheusNamespace = "headscale" | const prometheusNamespace = "headscale" | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	// This is a high cardinality metric (namespace x machines), we might want to make this | 	// This is a high cardinality metric (user x machines), we might want to make this | ||||||
| 	// configurable/opt-in in the future. | 	// configurable/opt-in in the future. | ||||||
| 	lastStateUpdate = promauto.NewGaugeVec(prometheus.GaugeOpts{ | 	lastStateUpdate = promauto.NewGaugeVec(prometheus.GaugeOpts{ | ||||||
| 		Namespace: prometheusNamespace, | 		Namespace: prometheusNamespace, | ||||||
| 		Name:      "last_update_seconds", | 		Name:      "last_update_seconds", | ||||||
| 		Help:      "Time stamp in unix time when a machine or headscale was updated", | 		Help:      "Time stamp in unix time when a machine or headscale was updated", | ||||||
| 	}, []string{"namespace", "machine"}) | 	}, []string{"user", "machine"}) | ||||||
| 
 | 
 | ||||||
| 	machineRegistrations = promauto.NewCounterVec(prometheus.CounterOpts{ | 	machineRegistrations = promauto.NewCounterVec(prometheus.CounterOpts{ | ||||||
| 		Namespace: prometheusNamespace, | 		Namespace: prometheusNamespace, | ||||||
| 		Name:      "machine_registrations_total", | 		Name:      "machine_registrations_total", | ||||||
| 		Help:      "The total amount of registered machine attempts", | 		Help:      "The total amount of registered machine attempts", | ||||||
| 	}, []string{"action", "auth", "status", "namespace"}) | 	}, []string{"action", "auth", "status", "user"}) | ||||||
| 
 | 
 | ||||||
| 	updateRequestsFromNode = promauto.NewCounterVec(prometheus.CounterOpts{ | 	updateRequestsFromNode = promauto.NewCounterVec(prometheus.CounterOpts{ | ||||||
| 		Namespace: prometheusNamespace, | 		Namespace: prometheusNamespace, | ||||||
| 		Name:      "update_request_from_node_total", | 		Name:      "update_request_from_node_total", | ||||||
| 		Help:      "The number of updates requested by a node/update function", | 		Help:      "The number of updates requested by a node/update function", | ||||||
| 	}, []string{"namespace", "machine", "state"}) | 	}, []string{"user", "machine", "state"}) | ||||||
| 	updateRequestsSentToNode = promauto.NewCounterVec(prometheus.CounterOpts{ | 	updateRequestsSentToNode = promauto.NewCounterVec(prometheus.CounterOpts{ | ||||||
| 		Namespace: prometheusNamespace, | 		Namespace: prometheusNamespace, | ||||||
| 		Name:      "update_request_sent_to_node_total", | 		Name:      "update_request_sent_to_node_total", | ||||||
| 		Help:      "The number of calls/messages issued on a specific nodes update channel", | 		Help:      "The number of calls/messages issued on a specific nodes update channel", | ||||||
| 	}, []string{"namespace", "machine", "status"}) | 	}, []string{"user", "machine", "status"}) | ||||||
| 	// TODO(kradalby): This is very debugging, we might want to remove it. | 	// TODO(kradalby): This is very debugging, we might want to remove it. | ||||||
| 	updateRequestsReceivedOnChannel = promauto.NewCounterVec(prometheus.CounterOpts{ | 	updateRequestsReceivedOnChannel = promauto.NewCounterVec(prometheus.CounterOpts{ | ||||||
| 		Namespace: prometheusNamespace, | 		Namespace: prometheusNamespace, | ||||||
| 		Name:      "update_request_received_on_channel_total", | 		Name:      "update_request_received_on_channel_total", | ||||||
| 		Help:      "The number of update requests received on an update channel", | 		Help:      "The number of update requests received on an update channel", | ||||||
| 	}, []string{"namespace", "machine"}) | 	}, []string{"user", "machine"}) | ||||||
| ) | ) | ||||||
|  | |||||||
							
								
								
									
										148
									
								
								namespaces.go
									
									
									
									
									
								
							
							
						
						
									
										148
									
								
								namespaces.go
									
									
									
									
									
								
							| @ -16,10 +16,10 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| 	ErrNamespaceExists          = Error("Namespace already exists") | 	ErrUserExists        = Error("User already exists") | ||||||
| 	ErrNamespaceNotFound        = Error("Namespace not found") | 	ErrUserNotFound      = Error("User not found") | ||||||
| 	ErrNamespaceNotEmptyOfNodes = Error("Namespace not empty: node(s) found") | 	ErrUserStillHasNodes = Error("User not empty: node(s) found") | ||||||
| 	ErrInvalidNamespaceName     = Error("Invalid namespace name") | 	ErrInvalidUserName   = Error("Invalid user name") | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| @ -27,55 +27,55 @@ const ( | |||||||
| 	labelHostnameLength = 63 | 	labelHostnameLength = 63 | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var invalidCharsInNamespaceRegex = regexp.MustCompile("[^a-z0-9-.]+") | var invalidCharsInUserRegex = regexp.MustCompile("[^a-z0-9-.]+") | ||||||
| 
 | 
 | ||||||
| // Namespace is the way Headscale implements the concept of users in Tailscale | // User is the way Headscale implements the concept of users in Tailscale | ||||||
| // | // | ||||||
| // At the end of the day, users in Tailscale are some kind of 'bubbles' or namespaces | // At the end of the day, users in Tailscale are some kind of 'bubbles' or users | ||||||
| // that contain our machines. | // that contain our machines. | ||||||
| type Namespace struct { | type User struct { | ||||||
| 	gorm.Model | 	gorm.Model | ||||||
| 	Name string `gorm:"unique"` | 	Name string `gorm:"unique"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // CreateNamespace creates a new Namespace. Returns error if could not be created | // CreateUser creates a new User. Returns error if could not be created | ||||||
| // or another namespace already exists. | // or another user already exists. | ||||||
| func (h *Headscale) CreateNamespace(name string) (*Namespace, error) { | func (h *Headscale) CreateUser(name string) (*User, error) { | ||||||
| 	err := CheckForFQDNRules(name) | 	err := CheckForFQDNRules(name) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	namespace := Namespace{} | 	user := User{} | ||||||
| 	if err := h.db.Where("name = ?", name).First(&namespace).Error; err == nil { | 	if err := h.db.Where("name = ?", name).First(&user).Error; err == nil { | ||||||
| 		return nil, ErrNamespaceExists | 		return nil, ErrUserExists | ||||||
| 	} | 	} | ||||||
| 	namespace.Name = name | 	user.Name = name | ||||||
| 	if err := h.db.Create(&namespace).Error; err != nil { | 	if err := h.db.Create(&user).Error; err != nil { | ||||||
| 		log.Error(). | 		log.Error(). | ||||||
| 			Str("func", "CreateNamespace"). | 			Str("func", "CreateUser"). | ||||||
| 			Err(err). | 			Err(err). | ||||||
| 			Msg("Could not create row") | 			Msg("Could not create row") | ||||||
| 
 | 
 | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return &namespace, nil | 	return &user, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // DestroyNamespace destroys a Namespace. Returns error if the Namespace does | // DestroyUser destroys a User. Returns error if the User does | ||||||
| // not exist or if there are machines associated with it. | // not exist or if there are machines associated with it. | ||||||
| func (h *Headscale) DestroyNamespace(name string) error { | func (h *Headscale) DestroyUser(name string) error { | ||||||
| 	namespace, err := h.GetNamespace(name) | 	user, err := h.GetUser(name) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return ErrNamespaceNotFound | 		return ErrUserNotFound | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	machines, err := h.ListMachinesInNamespace(name) | 	machines, err := h.ListMachinesByUser(name) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if len(machines) > 0 { | 	if len(machines) > 0 { | ||||||
| 		return ErrNamespaceNotEmptyOfNodes | 		return ErrUserStillHasNodes | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	keys, err := h.ListPreAuthKeys(name) | 	keys, err := h.ListPreAuthKeys(name) | ||||||
| @ -89,18 +89,18 @@ func (h *Headscale) DestroyNamespace(name string) error { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if result := h.db.Unscoped().Delete(&namespace); result.Error != nil { | 	if result := h.db.Unscoped().Delete(&user); result.Error != nil { | ||||||
| 		return result.Error | 		return result.Error | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // RenameNamespace renames a Namespace. Returns error if the Namespace does | // RenameUser renames a User. Returns error if the User does | ||||||
| // not exist or if another Namespace exists with the new name. | // not exist or if another User exists with the new name. | ||||||
| func (h *Headscale) RenameNamespace(oldName, newName string) error { | func (h *Headscale) RenameUser(oldName, newName string) error { | ||||||
| 	var err error | 	var err error | ||||||
| 	oldNamespace, err := h.GetNamespace(oldName) | 	oldUser, err := h.GetUser(oldName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @ -108,76 +108,76 @@ func (h *Headscale) RenameNamespace(oldName, newName string) error { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	_, err = h.GetNamespace(newName) | 	_, err = h.GetUser(newName) | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		return ErrNamespaceExists | 		return ErrUserExists | ||||||
| 	} | 	} | ||||||
| 	if !errors.Is(err, ErrNamespaceNotFound) { | 	if !errors.Is(err, ErrUserNotFound) { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	oldNamespace.Name = newName | 	oldUser.Name = newName | ||||||
| 
 | 
 | ||||||
| 	if result := h.db.Save(&oldNamespace); result.Error != nil { | 	if result := h.db.Save(&oldUser); result.Error != nil { | ||||||
| 		return result.Error | 		return result.Error | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetNamespace fetches a namespace by name. | // GetUser fetches a user by name. | ||||||
| func (h *Headscale) GetNamespace(name string) (*Namespace, error) { | func (h *Headscale) GetUser(name string) (*User, error) { | ||||||
| 	namespace := Namespace{} | 	user := User{} | ||||||
| 	if result := h.db.First(&namespace, "name = ?", name); errors.Is( | 	if result := h.db.First(&user, "name = ?", name); errors.Is( | ||||||
| 		result.Error, | 		result.Error, | ||||||
| 		gorm.ErrRecordNotFound, | 		gorm.ErrRecordNotFound, | ||||||
| 	) { | 	) { | ||||||
| 		return nil, ErrNamespaceNotFound | 		return nil, ErrUserNotFound | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return &namespace, nil | 	return &user, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ListNamespaces gets all the existing namespaces. | // ListUsers gets all the existing users. | ||||||
| func (h *Headscale) ListNamespaces() ([]Namespace, error) { | func (h *Headscale) ListUsers() ([]User, error) { | ||||||
| 	namespaces := []Namespace{} | 	users := []User{} | ||||||
| 	if err := h.db.Find(&namespaces).Error; err != nil { | 	if err := h.db.Find(&users).Error; err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return namespaces, nil | 	return users, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ListMachinesInNamespace gets all the nodes in a given namespace. | // ListMachinesByUser gets all the nodes in a given user. | ||||||
| func (h *Headscale) ListMachinesInNamespace(name string) ([]Machine, error) { | func (h *Headscale) ListMachinesByUser(name string) ([]Machine, error) { | ||||||
| 	err := CheckForFQDNRules(name) | 	err := CheckForFQDNRules(name) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	namespace, err := h.GetNamespace(name) | 	user, err := h.GetUser(name) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	machines := []Machine{} | 	machines := []Machine{} | ||||||
| 	if err := h.db.Preload("AuthKey").Preload("AuthKey.Namespace").Preload("Namespace").Where(&Machine{NamespaceID: namespace.ID}).Find(&machines).Error; err != nil { | 	if err := h.db.Preload("AuthKey").Preload("AuthKey.User").Preload("User").Where(&Machine{UserID: user.ID}).Find(&machines).Error; err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return machines, nil | 	return machines, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SetMachineNamespace assigns a Machine to a namespace. | // SetMachineUser assigns a Machine to a user. | ||||||
| func (h *Headscale) SetMachineNamespace(machine *Machine, namespaceName string) error { | func (h *Headscale) SetMachineUser(machine *Machine, username string) error { | ||||||
| 	err := CheckForFQDNRules(namespaceName) | 	err := CheckForFQDNRules(username) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	namespace, err := h.GetNamespace(namespaceName) | 	user, err := h.GetUser(username) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	machine.Namespace = *namespace | 	machine.User = *user | ||||||
| 	if result := h.db.Save(&machine); result.Error != nil { | 	if result := h.db.Save(&machine); result.Error != nil { | ||||||
| 		return result.Error | 		return result.Error | ||||||
| 	} | 	} | ||||||
| @ -185,7 +185,7 @@ func (h *Headscale) SetMachineNamespace(machine *Machine, namespaceName string) | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (n *Namespace) toUser() *tailcfg.User { | func (n *User) toTailscaleUser() *tailcfg.User { | ||||||
| 	user := tailcfg.User{ | 	user := tailcfg.User{ | ||||||
| 		ID:            tailcfg.UserID(n.ID), | 		ID:            tailcfg.UserID(n.ID), | ||||||
| 		LoginName:     n.Name, | 		LoginName:     n.Name, | ||||||
| @ -199,7 +199,7 @@ func (n *Namespace) toUser() *tailcfg.User { | |||||||
| 	return &user | 	return &user | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (n *Namespace) toLogin() *tailcfg.Login { | func (n *User) toTailscaleLogin() *tailcfg.Login { | ||||||
| 	login := tailcfg.Login{ | 	login := tailcfg.Login{ | ||||||
| 		ID:            tailcfg.LoginID(n.ID), | 		ID:            tailcfg.LoginID(n.ID), | ||||||
| 		LoginName:     n.Name, | 		LoginName:     n.Name, | ||||||
| @ -215,24 +215,24 @@ func (h *Headscale) getMapResponseUserProfiles( | |||||||
| 	machine Machine, | 	machine Machine, | ||||||
| 	peers Machines, | 	peers Machines, | ||||||
| ) []tailcfg.UserProfile { | ) []tailcfg.UserProfile { | ||||||
| 	namespaceMap := make(map[string]Namespace) | 	userMap := make(map[string]User) | ||||||
| 	namespaceMap[machine.Namespace.Name] = machine.Namespace | 	userMap[machine.User.Name] = machine.User | ||||||
| 	for _, peer := range peers { | 	for _, peer := range peers { | ||||||
| 		namespaceMap[peer.Namespace.Name] = peer.Namespace // not worth checking if already is there | 		userMap[peer.User.Name] = peer.User // not worth checking if already is there | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	profiles := []tailcfg.UserProfile{} | 	profiles := []tailcfg.UserProfile{} | ||||||
| 	for _, namespace := range namespaceMap { | 	for _, user := range userMap { | ||||||
| 		displayName := namespace.Name | 		displayName := user.Name | ||||||
| 
 | 
 | ||||||
| 		if h.cfg.BaseDomain != "" { | 		if h.cfg.BaseDomain != "" { | ||||||
| 			displayName = fmt.Sprintf("%s@%s", namespace.Name, h.cfg.BaseDomain) | 			displayName = fmt.Sprintf("%s@%s", user.Name, h.cfg.BaseDomain) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		profiles = append(profiles, | 		profiles = append(profiles, | ||||||
| 			tailcfg.UserProfile{ | 			tailcfg.UserProfile{ | ||||||
| 				ID:          tailcfg.UserID(namespace.ID), | 				ID:          tailcfg.UserID(user.ID), | ||||||
| 				LoginName:   namespace.Name, | 				LoginName:   user.Name, | ||||||
| 				DisplayName: displayName, | 				DisplayName: displayName, | ||||||
| 			}) | 			}) | ||||||
| 	} | 	} | ||||||
| @ -240,16 +240,16 @@ func (h *Headscale) getMapResponseUserProfiles( | |||||||
| 	return profiles | 	return profiles | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (n *Namespace) toProto() *v1.Namespace { | func (n *User) toProto() *v1.User { | ||||||
| 	return &v1.Namespace{ | 	return &v1.User{ | ||||||
| 		Id:        strconv.FormatUint(uint64(n.ID), Base10), | 		Id:        strconv.FormatUint(uint64(n.ID), Base10), | ||||||
| 		Name:      n.Name, | 		Name:      n.Name, | ||||||
| 		CreatedAt: timestamppb.New(n.CreatedAt), | 		CreatedAt: timestamppb.New(n.CreatedAt), | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NormalizeToFQDNRules will replace forbidden chars in namespace | // NormalizeToFQDNRules will replace forbidden chars in user | ||||||
| // it can also return an error if the namespace doesn't respect RFC 952 and 1123. | // it can also return an error if the user doesn't respect RFC 952 and 1123. | ||||||
| func NormalizeToFQDNRules(name string, stripEmailDomain bool) (string, error) { | func NormalizeToFQDNRules(name string, stripEmailDomain bool) (string, error) { | ||||||
| 	name = strings.ToLower(name) | 	name = strings.ToLower(name) | ||||||
| 	name = strings.ReplaceAll(name, "'", "") | 	name = strings.ReplaceAll(name, "'", "") | ||||||
| @ -259,14 +259,14 @@ func NormalizeToFQDNRules(name string, stripEmailDomain bool) (string, error) { | |||||||
| 	} else { | 	} else { | ||||||
| 		name = strings.ReplaceAll(name, "@", ".") | 		name = strings.ReplaceAll(name, "@", ".") | ||||||
| 	} | 	} | ||||||
| 	name = invalidCharsInNamespaceRegex.ReplaceAllString(name, "-") | 	name = invalidCharsInUserRegex.ReplaceAllString(name, "-") | ||||||
| 
 | 
 | ||||||
| 	for _, elt := range strings.Split(name, ".") { | 	for _, elt := range strings.Split(name, ".") { | ||||||
| 		if len(elt) > labelHostnameLength { | 		if len(elt) > labelHostnameLength { | ||||||
| 			return "", fmt.Errorf( | 			return "", fmt.Errorf( | ||||||
| 				"label %v is more than 63 chars: %w", | 				"label %v is more than 63 chars: %w", | ||||||
| 				elt, | 				elt, | ||||||
| 				ErrInvalidNamespaceName, | 				ErrInvalidUserName, | ||||||
| 			) | 			) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @ -279,21 +279,21 @@ func CheckForFQDNRules(name string) error { | |||||||
| 		return fmt.Errorf( | 		return fmt.Errorf( | ||||||
| 			"DNS segment must not be over 63 chars. %v doesn't comply with this rule: %w", | 			"DNS segment must not be over 63 chars. %v doesn't comply with this rule: %w", | ||||||
| 			name, | 			name, | ||||||
| 			ErrInvalidNamespaceName, | 			ErrInvalidUserName, | ||||||
| 		) | 		) | ||||||
| 	} | 	} | ||||||
| 	if strings.ToLower(name) != name { | 	if strings.ToLower(name) != name { | ||||||
| 		return fmt.Errorf( | 		return fmt.Errorf( | ||||||
| 			"DNS segment should be lowercase. %v doesn't comply with this rule: %w", | 			"DNS segment should be lowercase. %v doesn't comply with this rule: %w", | ||||||
| 			name, | 			name, | ||||||
| 			ErrInvalidNamespaceName, | 			ErrInvalidUserName, | ||||||
| 		) | 		) | ||||||
| 	} | 	} | ||||||
| 	if invalidCharsInNamespaceRegex.MatchString(name) { | 	if invalidCharsInUserRegex.MatchString(name) { | ||||||
| 		return fmt.Errorf( | 		return fmt.Errorf( | ||||||
| 			"DNS segment should only be composed of lowercase ASCII letters numbers, hyphen and dots. %v doesn't comply with theses rules: %w", | 			"DNS segment should only be composed of lowercase ASCII letters numbers, hyphen and dots. %v doesn't comply with theses rules: %w", | ||||||
| 			name, | 			name, | ||||||
| 			ErrInvalidNamespaceName, | 			ErrInvalidUserName, | ||||||
| 		) | 		) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,43 +8,43 @@ import ( | |||||||
| 	"gorm.io/gorm" | 	"gorm.io/gorm" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestCreateAndDestroyNamespace(c *check.C) { | func (s *Suite) TestCreateAndDestroyUser(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test") | 	user, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 	c.Assert(namespace.Name, check.Equals, "test") | 	c.Assert(user.Name, check.Equals, "test") | ||||||
| 
 | 
 | ||||||
| 	namespaces, err := app.ListNamespaces() | 	users, err := app.ListUsers() | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 	c.Assert(len(namespaces), check.Equals, 1) | 	c.Assert(len(users), check.Equals, 1) | ||||||
| 
 | 
 | ||||||
| 	err = app.DestroyNamespace("test") | 	err = app.DestroyUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetNamespace("test") | 	_, err = app.GetUser("test") | ||||||
| 	c.Assert(err, check.NotNil) | 	c.Assert(err, check.NotNil) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestDestroyNamespaceErrors(c *check.C) { | func (s *Suite) TestDestroyUserErrors(c *check.C) { | ||||||
| 	err := app.DestroyNamespace("test") | 	err := app.DestroyUser("test") | ||||||
| 	c.Assert(err, check.Equals, ErrNamespaceNotFound) | 	c.Assert(err, check.Equals, ErrUserNotFound) | ||||||
| 
 | 
 | ||||||
| 	namespace, err := app.CreateNamespace("test") | 	user, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	err = app.DestroyNamespace("test") | 	err = app.DestroyUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	result := app.db.Preload("Namespace").First(&pak, "key = ?", pak.Key) | 	result := app.db.Preload("User").First(&pak, "key = ?", pak.Key) | ||||||
| 	// destroying a namespace also deletes all associated preauthkeys | 	// destroying a user also deletes all associated preauthkeys | ||||||
| 	c.Assert(result.Error, check.Equals, gorm.ErrRecordNotFound) | 	c.Assert(result.Error, check.Equals, gorm.ErrRecordNotFound) | ||||||
| 
 | 
 | ||||||
| 	namespace, err = app.CreateNamespace("test") | 	user, err = app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err = app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err = app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	machine := Machine{ | 	machine := Machine{ | ||||||
| @ -53,57 +53,57 @@ func (s *Suite) TestDestroyNamespaceErrors(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testmachine", | 		Hostname:       "testmachine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 	} | 	} | ||||||
| 	app.db.Save(&machine) | 	app.db.Save(&machine) | ||||||
| 
 | 
 | ||||||
| 	err = app.DestroyNamespace("test") | 	err = app.DestroyUser("test") | ||||||
| 	c.Assert(err, check.Equals, ErrNamespaceNotEmptyOfNodes) | 	c.Assert(err, check.Equals, ErrUserStillHasNodes) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestRenameNamespace(c *check.C) { | func (s *Suite) TestRenameUser(c *check.C) { | ||||||
| 	namespaceTest, err := app.CreateNamespace("test") | 	userTest, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 	c.Assert(namespaceTest.Name, check.Equals, "test") | 	c.Assert(userTest.Name, check.Equals, "test") | ||||||
| 
 | 
 | ||||||
| 	namespaces, err := app.ListNamespaces() | 	users, err := app.ListUsers() | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 	c.Assert(len(namespaces), check.Equals, 1) | 	c.Assert(len(users), check.Equals, 1) | ||||||
| 
 | 
 | ||||||
| 	err = app.RenameNamespace("test", "test-renamed") | 	err = app.RenameUser("test", "test-renamed") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetNamespace("test") | 	_, err = app.GetUser("test") | ||||||
| 	c.Assert(err, check.Equals, ErrNamespaceNotFound) | 	c.Assert(err, check.Equals, ErrUserNotFound) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetNamespace("test-renamed") | 	_, err = app.GetUser("test-renamed") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	err = app.RenameNamespace("test-does-not-exit", "test") | 	err = app.RenameUser("test-does-not-exit", "test") | ||||||
| 	c.Assert(err, check.Equals, ErrNamespaceNotFound) | 	c.Assert(err, check.Equals, ErrUserNotFound) | ||||||
| 
 | 
 | ||||||
| 	namespaceTest2, err := app.CreateNamespace("test2") | 	userTest2, err := app.CreateUser("test2") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 	c.Assert(namespaceTest2.Name, check.Equals, "test2") | 	c.Assert(userTest2.Name, check.Equals, "test2") | ||||||
| 
 | 
 | ||||||
| 	err = app.RenameNamespace("test2", "test-renamed") | 	err = app.RenameUser("test2", "test-renamed") | ||||||
| 	c.Assert(err, check.Equals, ErrNamespaceExists) | 	c.Assert(err, check.Equals, ErrUserExists) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { | func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { | ||||||
| 	namespaceShared1, err := app.CreateNamespace("shared1") | 	userShared1, err := app.CreateUser("shared1") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	namespaceShared2, err := app.CreateNamespace("shared2") | 	userShared2, err := app.CreateUser("shared2") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	namespaceShared3, err := app.CreateNamespace("shared3") | 	userShared3, err := app.CreateUser("shared3") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	preAuthKeyShared1, err := app.CreatePreAuthKey( | 	preAuthKeyShared1, err := app.CreatePreAuthKey( | ||||||
| 		namespaceShared1.Name, | 		userShared1.Name, | ||||||
| 		false, | 		false, | ||||||
| 		false, | 		false, | ||||||
| 		nil, | 		nil, | ||||||
| @ -112,7 +112,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { | |||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	preAuthKeyShared2, err := app.CreatePreAuthKey( | 	preAuthKeyShared2, err := app.CreatePreAuthKey( | ||||||
| 		namespaceShared2.Name, | 		userShared2.Name, | ||||||
| 		false, | 		false, | ||||||
| 		false, | 		false, | ||||||
| 		nil, | 		nil, | ||||||
| @ -121,7 +121,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { | |||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	preAuthKeyShared3, err := app.CreatePreAuthKey( | 	preAuthKeyShared3, err := app.CreatePreAuthKey( | ||||||
| 		namespaceShared3.Name, | 		userShared3.Name, | ||||||
| 		false, | 		false, | ||||||
| 		false, | 		false, | ||||||
| 		nil, | 		nil, | ||||||
| @ -130,7 +130,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { | |||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	preAuthKey2Shared1, err := app.CreatePreAuthKey( | 	preAuthKey2Shared1, err := app.CreatePreAuthKey( | ||||||
| 		namespaceShared1.Name, | 		userShared1.Name, | ||||||
| 		false, | 		false, | ||||||
| 		false, | 		false, | ||||||
| 		nil, | 		nil, | ||||||
| @ -138,7 +138,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { | |||||||
| 	) | 	) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine(namespaceShared1.Name, "test_get_shared_nodes_1") | 	_, err = app.GetMachine(userShared1.Name, "test_get_shared_nodes_1") | ||||||
| 	c.Assert(err, check.NotNil) | 	c.Assert(err, check.NotNil) | ||||||
| 
 | 
 | ||||||
| 	machineInShared1 := &Machine{ | 	machineInShared1 := &Machine{ | ||||||
| @ -147,15 +147,15 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { | |||||||
| 		NodeKey:        "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", | 		NodeKey:        "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", | ||||||
| 		DiscoKey:       "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", | 		DiscoKey:       "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", | ||||||
| 		Hostname:       "test_get_shared_nodes_1", | 		Hostname:       "test_get_shared_nodes_1", | ||||||
| 		NamespaceID:    namespaceShared1.ID, | 		UserID:    userShared1.ID, | ||||||
| 		Namespace:      *namespaceShared1, | 		User:      *userShared1, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.1")}, | 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.1")}, | ||||||
| 		AuthKeyID:      uint(preAuthKeyShared1.ID), | 		AuthKeyID:      uint(preAuthKeyShared1.ID), | ||||||
| 	} | 	} | ||||||
| 	app.db.Save(machineInShared1) | 	app.db.Save(machineInShared1) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine(namespaceShared1.Name, machineInShared1.Hostname) | 	_, err = app.GetMachine(userShared1.Name, machineInShared1.Hostname) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	machineInShared2 := &Machine{ | 	machineInShared2 := &Machine{ | ||||||
| @ -164,15 +164,15 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { | |||||||
| 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | ||||||
| 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | ||||||
| 		Hostname:       "test_get_shared_nodes_2", | 		Hostname:       "test_get_shared_nodes_2", | ||||||
| 		NamespaceID:    namespaceShared2.ID, | 		UserID:    userShared2.ID, | ||||||
| 		Namespace:      *namespaceShared2, | 		User:      *userShared2, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.2")}, | 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.2")}, | ||||||
| 		AuthKeyID:      uint(preAuthKeyShared2.ID), | 		AuthKeyID:      uint(preAuthKeyShared2.ID), | ||||||
| 	} | 	} | ||||||
| 	app.db.Save(machineInShared2) | 	app.db.Save(machineInShared2) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine(namespaceShared2.Name, machineInShared2.Hostname) | 	_, err = app.GetMachine(userShared2.Name, machineInShared2.Hostname) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	machineInShared3 := &Machine{ | 	machineInShared3 := &Machine{ | ||||||
| @ -181,15 +181,15 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { | |||||||
| 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | ||||||
| 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | ||||||
| 		Hostname:       "test_get_shared_nodes_3", | 		Hostname:       "test_get_shared_nodes_3", | ||||||
| 		NamespaceID:    namespaceShared3.ID, | 		UserID:    userShared3.ID, | ||||||
| 		Namespace:      *namespaceShared3, | 		User:      *userShared3, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.3")}, | 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.3")}, | ||||||
| 		AuthKeyID:      uint(preAuthKeyShared3.ID), | 		AuthKeyID:      uint(preAuthKeyShared3.ID), | ||||||
| 	} | 	} | ||||||
| 	app.db.Save(machineInShared3) | 	app.db.Save(machineInShared3) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine(namespaceShared3.Name, machineInShared3.Hostname) | 	_, err = app.GetMachine(userShared3.Name, machineInShared3.Hostname) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	machine2InShared1 := &Machine{ | 	machine2InShared1 := &Machine{ | ||||||
| @ -198,8 +198,8 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { | |||||||
| 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | ||||||
| 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", | ||||||
| 		Hostname:       "test_get_shared_nodes_4", | 		Hostname:       "test_get_shared_nodes_4", | ||||||
| 		NamespaceID:    namespaceShared1.ID, | 		UserID:    userShared1.ID, | ||||||
| 		Namespace:      *namespaceShared1, | 		User:      *userShared1, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.4")}, | 		IPAddresses:    []netip.Addr{netip.MustParseAddr("100.64.0.4")}, | ||||||
| 		AuthKeyID:      uint(preAuthKey2Shared1.ID), | 		AuthKeyID:      uint(preAuthKey2Shared1.ID), | ||||||
| @ -218,7 +218,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { | |||||||
| 
 | 
 | ||||||
| 	found := false | 	found := false | ||||||
| 	for _, userProfiles := range userProfiles { | 	for _, userProfiles := range userProfiles { | ||||||
| 		if userProfiles.DisplayName == namespaceShared1.Name { | 		if userProfiles.DisplayName == userShared1.Name { | ||||||
| 			found = true | 			found = true | ||||||
| 
 | 
 | ||||||
| 			break | 			break | ||||||
| @ -228,7 +228,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { | |||||||
| 
 | 
 | ||||||
| 	found = false | 	found = false | ||||||
| 	for _, userProfile := range userProfiles { | 	for _, userProfile := range userProfiles { | ||||||
| 		if userProfile.DisplayName == namespaceShared2.Name { | 		if userProfile.DisplayName == userShared2.Name { | ||||||
| 			found = true | 			found = true | ||||||
| 
 | 
 | ||||||
| 			break | 			break | ||||||
| @ -294,7 +294,7 @@ func TestNormalizeToFQDNRules(t *testing.T) { | |||||||
| 			wantErr: false, | 			wantErr: false, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "namespace name with space", | 			name: "user name with space", | ||||||
| 			args: args{ | 			args: args{ | ||||||
| 				name:             "name space", | 				name:             "name space", | ||||||
| 				stripEmailDomain: false, | 				stripEmailDomain: false, | ||||||
| @ -303,7 +303,7 @@ func TestNormalizeToFQDNRules(t *testing.T) { | |||||||
| 			wantErr: false, | 			wantErr: false, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "namespace with quote", | 			name: "user with quote", | ||||||
| 			args: args{ | 			args: args{ | ||||||
| 				name:             "Jamie's iPhone 5", | 				name:             "Jamie's iPhone 5", | ||||||
| 				stripEmailDomain: false, | 				stripEmailDomain: false, | ||||||
| @ -341,29 +341,29 @@ func TestCheckForFQDNRules(t *testing.T) { | |||||||
| 		wantErr bool | 		wantErr bool | ||||||
| 	}{ | 	}{ | ||||||
| 		{ | 		{ | ||||||
| 			name:    "valid: namespace", | 			name:    "valid: user", | ||||||
| 			args:    args{name: "valid-namespace"}, | 			args:    args{name: "valid-user"}, | ||||||
| 			wantErr: false, | 			wantErr: false, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name:    "invalid: capitalized namespace", | 			name:    "invalid: capitalized user", | ||||||
| 			args:    args{name: "Invalid-CapItaLIzed-namespace"}, | 			args:    args{name: "Invalid-CapItaLIzed-user"}, | ||||||
| 			wantErr: true, | 			wantErr: true, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name:    "invalid: email as namespace", | 			name:    "invalid: email as user", | ||||||
| 			args:    args{name: "foo.bar@example.com"}, | 			args:    args{name: "foo.bar@example.com"}, | ||||||
| 			wantErr: true, | 			wantErr: true, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name:    "invalid: chars in namespace name", | 			name:    "invalid: chars in user name", | ||||||
| 			args:    args{name: "super-namespace+name"}, | 			args:    args{name: "super-user+name"}, | ||||||
| 			wantErr: true, | 			wantErr: true, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			name: "invalid: too long name for namespace", | 			name: "invalid: too long name for user", | ||||||
| 			args: args{ | 			args: args{ | ||||||
| 				name: "super-long-namespace-name-that-should-be-a-little-more-than-63-chars", | 				name: "super-long-user-name-that-should-be-a-little-more-than-63-chars", | ||||||
| 			}, | 			}, | ||||||
| 			wantErr: true, | 			wantErr: true, | ||||||
| 		}, | 		}, | ||||||
| @ -377,14 +377,14 @@ func TestCheckForFQDNRules(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestSetMachineNamespace(c *check.C) { | func (s *Suite) TestSetMachineUser(c *check.C) { | ||||||
| 	oldNamespace, err := app.CreateNamespace("old") | 	oldUser, err := app.CreateUser("old") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	newNamespace, err := app.CreateNamespace("new") | 	newUser, err := app.CreateUser("new") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(oldNamespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(oldUser.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	machine := Machine{ | 	machine := Machine{ | ||||||
| @ -393,23 +393,23 @@ func (s *Suite) TestSetMachineNamespace(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testmachine", | 		Hostname:       "testmachine", | ||||||
| 		NamespaceID:    oldNamespace.ID, | 		UserID:    oldUser.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 	} | 	} | ||||||
| 	app.db.Save(&machine) | 	app.db.Save(&machine) | ||||||
| 	c.Assert(machine.NamespaceID, check.Equals, oldNamespace.ID) | 	c.Assert(machine.UserID, check.Equals, oldUser.ID) | ||||||
| 
 | 
 | ||||||
| 	err = app.SetMachineNamespace(&machine, newNamespace.Name) | 	err = app.SetMachineUser(&machine, newUser.Name) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 	c.Assert(machine.NamespaceID, check.Equals, newNamespace.ID) | 	c.Assert(machine.UserID, check.Equals, newUser.ID) | ||||||
| 	c.Assert(machine.Namespace.Name, check.Equals, newNamespace.Name) | 	c.Assert(machine.User.Name, check.Equals, newUser.Name) | ||||||
| 
 | 
 | ||||||
| 	err = app.SetMachineNamespace(&machine, "non-existing-namespace") | 	err = app.SetMachineUser(&machine, "non-existing-user") | ||||||
| 	c.Assert(err, check.Equals, ErrNamespaceNotFound) | 	c.Assert(err, check.Equals, ErrUserNotFound) | ||||||
| 
 | 
 | ||||||
| 	err = app.SetMachineNamespace(&machine, newNamespace.Name) | 	err = app.SetMachineUser(&machine, newUser.Name) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 	c.Assert(machine.NamespaceID, check.Equals, newNamespace.ID) | 	c.Assert(machine.UserID, check.Equals, newUser.ID) | ||||||
| 	c.Assert(machine.Namespace.Name, check.Equals, newNamespace.Name) | 	c.Assert(machine.User.Name, check.Equals, newUser.Name) | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										42
									
								
								oidc.go
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								oidc.go
									
									
									
									
									
								
							| @ -171,7 +171,7 @@ var oidcCallbackTemplate = template.Must( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // OIDCCallback handles the callback from the OIDC endpoint | // OIDCCallback handles the callback from the OIDC endpoint | ||||||
| // Retrieves the nkey from the state cache and adds the machine to the users email namespace | // Retrieves the nkey from the state cache and adds the machine to the users email user | ||||||
| // TODO: A confirmation page for new machines should be added to avoid phishing vulnerabilities | // TODO: A confirmation page for new machines should be added to avoid phishing vulnerabilities | ||||||
| // TODO: Add groups information from OIDC tokens into machine HostInfo | // TODO: Add groups information from OIDC tokens into machine HostInfo | ||||||
| // Listens in /oidc/callback. | // Listens in /oidc/callback. | ||||||
| @ -223,7 +223,7 @@ func (h *Headscale) OIDCCallback( | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	namespaceName, err := getNamespaceName(writer, claims, h.cfg.OIDC.StripEmaildomain) | 	userName, err := getUserName(writer, claims, h.cfg.OIDC.StripEmaildomain) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @ -231,12 +231,12 @@ func (h *Headscale) OIDCCallback( | |||||||
| 	// register the machine if it's new | 	// register the machine if it's new | ||||||
| 	log.Debug().Msg("Registering new machine after successful callback") | 	log.Debug().Msg("Registering new machine after successful callback") | ||||||
| 
 | 
 | ||||||
| 	namespace, err := h.findOrCreateNewNamespaceForOIDCCallback(writer, namespaceName) | 	user, err := h.findOrCreateNewUserForOIDCCallback(writer, userName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err := h.registerMachineForOIDCCallback(writer, namespace, nodeKey, idToken.Expiry); err != nil { | 	if err := h.registerMachineForOIDCCallback(writer, user, nodeKey, idToken.Expiry); err != nil { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -606,12 +606,12 @@ func (h *Headscale) validateMachineForOIDCCallback( | |||||||
| 	return &nodeKey, false, nil | 	return &nodeKey, false, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func getNamespaceName( | func getUserName( | ||||||
| 	writer http.ResponseWriter, | 	writer http.ResponseWriter, | ||||||
| 	claims *IDTokenClaims, | 	claims *IDTokenClaims, | ||||||
| 	stripEmaildomain bool, | 	stripEmaildomain bool, | ||||||
| ) (string, error) { | ) (string, error) { | ||||||
| 	namespaceName, err := NormalizeToFQDNRules( | 	userName, err := NormalizeToFQDNRules( | ||||||
| 		claims.Email, | 		claims.Email, | ||||||
| 		stripEmaildomain, | 		stripEmaildomain, | ||||||
| 	) | 	) | ||||||
| @ -630,25 +630,25 @@ func getNamespaceName( | |||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return namespaceName, nil | 	return userName, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (h *Headscale) findOrCreateNewNamespaceForOIDCCallback( | func (h *Headscale) findOrCreateNewUserForOIDCCallback( | ||||||
| 	writer http.ResponseWriter, | 	writer http.ResponseWriter, | ||||||
| 	namespaceName string, | 	userName string, | ||||||
| ) (*Namespace, error) { | ) (*User, error) { | ||||||
| 	namespace, err := h.GetNamespace(namespaceName) | 	user, err := h.GetUser(userName) | ||||||
| 	if errors.Is(err, ErrNamespaceNotFound) { | 	if errors.Is(err, ErrUserNotFound) { | ||||||
| 		namespace, err = h.CreateNamespace(namespaceName) | 		user, err = h.CreateUser(userName) | ||||||
| 
 | 
 | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Error(). | 			log.Error(). | ||||||
| 				Err(err). | 				Err(err). | ||||||
| 				Caller(). | 				Caller(). | ||||||
| 				Msgf("could not create new namespace '%s'", namespaceName) | 				Msgf("could not create new user '%s'", userName) | ||||||
| 			writer.Header().Set("Content-Type", "text/plain; charset=utf-8") | 			writer.Header().Set("Content-Type", "text/plain; charset=utf-8") | ||||||
| 			writer.WriteHeader(http.StatusInternalServerError) | 			writer.WriteHeader(http.StatusInternalServerError) | ||||||
| 			_, werr := writer.Write([]byte("could not create namespace")) | 			_, werr := writer.Write([]byte("could not create user")) | ||||||
| 			if werr != nil { | 			if werr != nil { | ||||||
| 				log.Error(). | 				log.Error(). | ||||||
| 					Caller(). | 					Caller(). | ||||||
| @ -662,11 +662,11 @@ func (h *Headscale) findOrCreateNewNamespaceForOIDCCallback( | |||||||
| 		log.Error(). | 		log.Error(). | ||||||
| 			Caller(). | 			Caller(). | ||||||
| 			Err(err). | 			Err(err). | ||||||
| 			Str("namespace", namespaceName). | 			Str("user", userName). | ||||||
| 			Msg("could not find or create namespace") | 			Msg("could not find or create user") | ||||||
| 		writer.Header().Set("Content-Type", "text/plain; charset=utf-8") | 		writer.Header().Set("Content-Type", "text/plain; charset=utf-8") | ||||||
| 		writer.WriteHeader(http.StatusInternalServerError) | 		writer.WriteHeader(http.StatusInternalServerError) | ||||||
| 		_, werr := writer.Write([]byte("could not find or create namespace")) | 		_, werr := writer.Write([]byte("could not find or create user")) | ||||||
| 		if werr != nil { | 		if werr != nil { | ||||||
| 			log.Error(). | 			log.Error(). | ||||||
| 				Caller(). | 				Caller(). | ||||||
| @ -677,18 +677,18 @@ func (h *Headscale) findOrCreateNewNamespaceForOIDCCallback( | |||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return namespace, nil | 	return user, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (h *Headscale) registerMachineForOIDCCallback( | func (h *Headscale) registerMachineForOIDCCallback( | ||||||
| 	writer http.ResponseWriter, | 	writer http.ResponseWriter, | ||||||
| 	namespace *Namespace, | 	user *User, | ||||||
| 	nodeKey *key.NodePublic, | 	nodeKey *key.NodePublic, | ||||||
| 	expiry time.Time, | 	expiry time.Time, | ||||||
| ) error { | ) error { | ||||||
| 	if _, err := h.RegisterMachineFromAuthCallback( | 	if _, err := h.RegisterMachineFromAuthCallback( | ||||||
| 		nodeKey.String(), | 		nodeKey.String(), | ||||||
| 		namespace.Name, | 		user.Name, | ||||||
| 		&expiry, | 		&expiry, | ||||||
| 		RegisterMethodOIDC, | 		RegisterMethodOIDC, | ||||||
| 	); err != nil { | 	); err != nil { | ||||||
|  | |||||||
| @ -18,16 +18,16 @@ const ( | |||||||
| 	ErrPreAuthKeyNotFound          = Error("AuthKey not found") | 	ErrPreAuthKeyNotFound          = Error("AuthKey not found") | ||||||
| 	ErrPreAuthKeyExpired           = Error("AuthKey expired") | 	ErrPreAuthKeyExpired           = Error("AuthKey expired") | ||||||
| 	ErrSingleUseAuthKeyHasBeenUsed = Error("AuthKey has already been used") | 	ErrSingleUseAuthKeyHasBeenUsed = Error("AuthKey has already been used") | ||||||
| 	ErrNamespaceMismatch           = Error("namespace mismatch") | 	ErrUserMismatch           = Error("user mismatch") | ||||||
| 	ErrPreAuthKeyACLTagInvalid     = Error("AuthKey tag is invalid") | 	ErrPreAuthKeyACLTagInvalid     = Error("AuthKey tag is invalid") | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // PreAuthKey describes a pre-authorization key usable in a particular namespace. | // PreAuthKey describes a pre-authorization key usable in a particular user. | ||||||
| type PreAuthKey struct { | type PreAuthKey struct { | ||||||
| 	ID          uint64 `gorm:"primary_key"` | 	ID          uint64 `gorm:"primary_key"` | ||||||
| 	Key         string | 	Key         string | ||||||
| 	NamespaceID uint | 	UserID uint | ||||||
| 	Namespace   Namespace | 	User   User | ||||||
| 	Reusable    bool | 	Reusable    bool | ||||||
| 	Ephemeral   bool `gorm:"default:false"` | 	Ephemeral   bool `gorm:"default:false"` | ||||||
| 	Used        bool `gorm:"default:false"` | 	Used        bool `gorm:"default:false"` | ||||||
| @ -44,15 +44,15 @@ type PreAuthKeyACLTag struct { | |||||||
| 	Tag          string | 	Tag          string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // CreatePreAuthKey creates a new PreAuthKey in a namespace, and returns it. | // CreatePreAuthKey creates a new PreAuthKey in a user, and returns it. | ||||||
| func (h *Headscale) CreatePreAuthKey( | func (h *Headscale) CreatePreAuthKey( | ||||||
| 	namespaceName string, | 	userName string, | ||||||
| 	reusable bool, | 	reusable bool, | ||||||
| 	ephemeral bool, | 	ephemeral bool, | ||||||
| 	expiration *time.Time, | 	expiration *time.Time, | ||||||
| 	aclTags []string, | 	aclTags []string, | ||||||
| ) (*PreAuthKey, error) { | ) (*PreAuthKey, error) { | ||||||
| 	namespace, err := h.GetNamespace(namespaceName) | 	user, err := h.GetUser(userName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -71,8 +71,8 @@ func (h *Headscale) CreatePreAuthKey( | |||||||
| 
 | 
 | ||||||
| 	key := PreAuthKey{ | 	key := PreAuthKey{ | ||||||
| 		Key:         kstr, | 		Key:         kstr, | ||||||
| 		NamespaceID: namespace.ID, | 		UserID: user.ID, | ||||||
| 		Namespace:   *namespace, | 		User:   *user, | ||||||
| 		Reusable:    reusable, | 		Reusable:    reusable, | ||||||
| 		Ephemeral:   ephemeral, | 		Ephemeral:   ephemeral, | ||||||
| 		CreatedAt:   &now, | 		CreatedAt:   &now, | ||||||
| @ -110,15 +110,15 @@ func (h *Headscale) CreatePreAuthKey( | |||||||
| 	return &key, nil | 	return &key, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ListPreAuthKeys returns the list of PreAuthKeys for a namespace. | // ListPreAuthKeys returns the list of PreAuthKeys for a user. | ||||||
| func (h *Headscale) ListPreAuthKeys(namespaceName string) ([]PreAuthKey, error) { | func (h *Headscale) ListPreAuthKeys(userName string) ([]PreAuthKey, error) { | ||||||
| 	namespace, err := h.GetNamespace(namespaceName) | 	user, err := h.GetUser(userName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	keys := []PreAuthKey{} | 	keys := []PreAuthKey{} | ||||||
| 	if err := h.db.Preload("Namespace").Preload("ACLTags").Where(&PreAuthKey{NamespaceID: namespace.ID}).Find(&keys).Error; err != nil { | 	if err := h.db.Preload("User").Preload("ACLTags").Where(&PreAuthKey{UserID: user.ID}).Find(&keys).Error; err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -126,14 +126,14 @@ func (h *Headscale) ListPreAuthKeys(namespaceName string) ([]PreAuthKey, error) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetPreAuthKey returns a PreAuthKey for a given key. | // GetPreAuthKey returns a PreAuthKey for a given key. | ||||||
| func (h *Headscale) GetPreAuthKey(namespace string, key string) (*PreAuthKey, error) { | func (h *Headscale) GetPreAuthKey(user string, key string) (*PreAuthKey, error) { | ||||||
| 	pak, err := h.checkKeyValidity(key) | 	pak, err := h.checkKeyValidity(key) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if pak.Namespace.Name != namespace { | 	if pak.User.Name != user { | ||||||
| 		return nil, ErrNamespaceMismatch | 		return nil, ErrUserMismatch | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return pak, nil | 	return pak, nil | ||||||
| @ -178,7 +178,7 @@ func (h *Headscale) UsePreAuthKey(k *PreAuthKey) error { | |||||||
| // If returns no error and a PreAuthKey, it can be used. | // If returns no error and a PreAuthKey, it can be used. | ||||||
| func (h *Headscale) checkKeyValidity(k string) (*PreAuthKey, error) { | func (h *Headscale) checkKeyValidity(k string) (*PreAuthKey, error) { | ||||||
| 	pak := PreAuthKey{} | 	pak := PreAuthKey{} | ||||||
| 	if result := h.db.Preload("Namespace").Preload("ACLTags").First(&pak, "key = ?", k); errors.Is( | 	if result := h.db.Preload("User").Preload("ACLTags").First(&pak, "key = ?", k); errors.Is( | ||||||
| 		result.Error, | 		result.Error, | ||||||
| 		gorm.ErrRecordNotFound, | 		gorm.ErrRecordNotFound, | ||||||
| 	) { | 	) { | ||||||
| @ -217,7 +217,7 @@ func (h *Headscale) generateKey() (string, error) { | |||||||
| 
 | 
 | ||||||
| func (key *PreAuthKey) toProto() *v1.PreAuthKey { | func (key *PreAuthKey) toProto() *v1.PreAuthKey { | ||||||
| 	protoKey := v1.PreAuthKey{ | 	protoKey := v1.PreAuthKey{ | ||||||
| 		Namespace: key.Namespace.Name, | 		User: key.User.Name, | ||||||
| 		Id:        strconv.FormatUint(key.ID, Base10), | 		Id:        strconv.FormatUint(key.ID, Base10), | ||||||
| 		Key:       key.Key, | 		Key:       key.Key, | ||||||
| 		Ephemeral: key.Ephemeral, | 		Ephemeral: key.Ephemeral, | ||||||
|  | |||||||
| @ -11,36 +11,36 @@ func (*Suite) TestCreatePreAuthKey(c *check.C) { | |||||||
| 
 | 
 | ||||||
| 	c.Assert(err, check.NotNil) | 	c.Assert(err, check.NotNil) | ||||||
| 
 | 
 | ||||||
| 	namespace, err := app.CreateNamespace("test") | 	user, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	key, err := app.CreatePreAuthKey(namespace.Name, true, false, nil, nil) | 	key, err := app.CreatePreAuthKey(user.Name, true, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	// Did we get a valid key? | 	// Did we get a valid key? | ||||||
| 	c.Assert(key.Key, check.NotNil) | 	c.Assert(key.Key, check.NotNil) | ||||||
| 	c.Assert(len(key.Key), check.Equals, 48) | 	c.Assert(len(key.Key), check.Equals, 48) | ||||||
| 
 | 
 | ||||||
| 	// Make sure the Namespace association is populated | 	// Make sure the User association is populated | ||||||
| 	c.Assert(key.Namespace.Name, check.Equals, namespace.Name) | 	c.Assert(key.User.Name, check.Equals, user.Name) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.ListPreAuthKeys("bogus") | 	_, err = app.ListPreAuthKeys("bogus") | ||||||
| 	c.Assert(err, check.NotNil) | 	c.Assert(err, check.NotNil) | ||||||
| 
 | 
 | ||||||
| 	keys, err := app.ListPreAuthKeys(namespace.Name) | 	keys, err := app.ListPreAuthKeys(user.Name) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 	c.Assert(len(keys), check.Equals, 1) | 	c.Assert(len(keys), check.Equals, 1) | ||||||
| 
 | 
 | ||||||
| 	// Make sure the Namespace association is populated | 	// Make sure the User association is populated | ||||||
| 	c.Assert((keys)[0].Namespace.Name, check.Equals, namespace.Name) | 	c.Assert((keys)[0].User.Name, check.Equals, user.Name) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (*Suite) TestExpiredPreAuthKey(c *check.C) { | func (*Suite) TestExpiredPreAuthKey(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test2") | 	user, err := app.CreateUser("test2") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	now := time.Now() | 	now := time.Now() | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, true, false, &now, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, true, false, &now, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	key, err := app.checkKeyValidity(pak.Key) | 	key, err := app.checkKeyValidity(pak.Key) | ||||||
| @ -55,10 +55,10 @@ func (*Suite) TestPreAuthKeyDoesNotExist(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (*Suite) TestValidateKeyOk(c *check.C) { | func (*Suite) TestValidateKeyOk(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test3") | 	user, err := app.CreateUser("test3") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, true, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, true, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	key, err := app.checkKeyValidity(pak.Key) | 	key, err := app.checkKeyValidity(pak.Key) | ||||||
| @ -67,10 +67,10 @@ func (*Suite) TestValidateKeyOk(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (*Suite) TestAlreadyUsedKey(c *check.C) { | func (*Suite) TestAlreadyUsedKey(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test4") | 	user, err := app.CreateUser("test4") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	machine := Machine{ | 	machine := Machine{ | ||||||
| @ -79,7 +79,7 @@ func (*Suite) TestAlreadyUsedKey(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testest", | 		Hostname:       "testest", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 	} | 	} | ||||||
| @ -91,10 +91,10 @@ func (*Suite) TestAlreadyUsedKey(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (*Suite) TestReusableBeingUsedKey(c *check.C) { | func (*Suite) TestReusableBeingUsedKey(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test5") | 	user, err := app.CreateUser("test5") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, true, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, true, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	machine := Machine{ | 	machine := Machine{ | ||||||
| @ -103,7 +103,7 @@ func (*Suite) TestReusableBeingUsedKey(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testest", | 		Hostname:       "testest", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 	} | 	} | ||||||
| @ -115,10 +115,10 @@ func (*Suite) TestReusableBeingUsedKey(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (*Suite) TestNotReusableNotBeingUsedKey(c *check.C) { | func (*Suite) TestNotReusableNotBeingUsedKey(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test6") | 	user, err := app.CreateUser("test6") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	key, err := app.checkKeyValidity(pak.Key) | 	key, err := app.checkKeyValidity(pak.Key) | ||||||
| @ -127,10 +127,10 @@ func (*Suite) TestNotReusableNotBeingUsedKey(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (*Suite) TestEphemeralKey(c *check.C) { | func (*Suite) TestEphemeralKey(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test7") | 	user, err := app.CreateUser("test7") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, true, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, true, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	now := time.Now() | 	now := time.Now() | ||||||
| @ -140,7 +140,7 @@ func (*Suite) TestEphemeralKey(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testest", | 		Hostname:       "testest", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		LastSeen:       &now, | 		LastSeen:       &now, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| @ -162,10 +162,10 @@ func (*Suite) TestEphemeralKey(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (*Suite) TestExpirePreauthKey(c *check.C) { | func (*Suite) TestExpirePreauthKey(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test3") | 	user, err := app.CreateUser("test3") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, true, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, true, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 	c.Assert(pak.Expiration, check.IsNil) | 	c.Assert(pak.Expiration, check.IsNil) | ||||||
| 
 | 
 | ||||||
| @ -179,10 +179,10 @@ func (*Suite) TestExpirePreauthKey(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (*Suite) TestNotReusableMarkedAsUsed(c *check.C) { | func (*Suite) TestNotReusableMarkedAsUsed(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test6") | 	user, err := app.CreateUser("test6") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 	pak.Used = true | 	pak.Used = true | ||||||
| 	app.db.Save(&pak) | 	app.db.Save(&pak) | ||||||
| @ -192,15 +192,15 @@ func (*Suite) TestNotReusableMarkedAsUsed(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (*Suite) TestPreAuthKeyACLTags(c *check.C) { | func (*Suite) TestPreAuthKeyACLTags(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test8") | 	user, err := app.CreateUser("test8") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.CreatePreAuthKey(namespace.Name, false, false, nil, []string{"badtag"}) | 	_, err = app.CreatePreAuthKey(user.Name, false, false, nil, []string{"badtag"}) | ||||||
| 	c.Assert(err, check.NotNil) // Confirm that malformed tags are rejected | 	c.Assert(err, check.NotNil) // Confirm that malformed tags are rejected | ||||||
| 
 | 
 | ||||||
| 	tags := []string{"tag:test1", "tag:test2"} | 	tags := []string{"tag:test1", "tag:test2"} | ||||||
| 	tagsWithDuplicate := []string{"tag:test1", "tag:test2", "tag:test2"} | 	tagsWithDuplicate := []string{"tag:test1", "tag:test2", "tag:test2"} | ||||||
| 	_, err = app.CreatePreAuthKey(namespace.Name, false, false, nil, tagsWithDuplicate) | 	_, err = app.CreatePreAuthKey(user.Name, false, false, nil, tagsWithDuplicate) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	listedPaks, err := app.ListPreAuthKeys("test8") | 	listedPaks, err := app.ListPreAuthKeys("test8") | ||||||
|  | |||||||
| @ -325,7 +325,7 @@ func (h *Headscale) handleAuthKeyCommon( | |||||||
| 				Err(err). | 				Err(err). | ||||||
| 				Msg("Cannot encode message") | 				Msg("Cannot encode message") | ||||||
| 			http.Error(writer, "Internal server error", http.StatusInternalServerError) | 			http.Error(writer, "Internal server error", http.StatusInternalServerError) | ||||||
| 			machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name). | 			machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.User.Name). | ||||||
| 				Inc() | 				Inc() | ||||||
| 
 | 
 | ||||||
| 			return | 			return | ||||||
| @ -350,7 +350,7 @@ func (h *Headscale) handleAuthKeyCommon( | |||||||
| 			Msg("Failed authentication via AuthKey") | 			Msg("Failed authentication via AuthKey") | ||||||
| 
 | 
 | ||||||
| 		if pak != nil { | 		if pak != nil { | ||||||
| 			machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name). | 			machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.User.Name). | ||||||
| 				Inc() | 				Inc() | ||||||
| 		} else { | 		} else { | ||||||
| 			machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", "unknown").Inc() | 			machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", "unknown").Inc() | ||||||
| @ -428,7 +428,7 @@ func (h *Headscale) handleAuthKeyCommon( | |||||||
| 		machineToRegister := Machine{ | 		machineToRegister := Machine{ | ||||||
| 			Hostname:       registerRequest.Hostinfo.Hostname, | 			Hostname:       registerRequest.Hostinfo.Hostname, | ||||||
| 			GivenName:      givenName, | 			GivenName:      givenName, | ||||||
| 			NamespaceID:    pak.Namespace.ID, | 			UserID:    pak.User.ID, | ||||||
| 			MachineKey:     MachinePublicKeyStripPrefix(machineKey), | 			MachineKey:     MachinePublicKeyStripPrefix(machineKey), | ||||||
| 			RegisterMethod: RegisterMethodAuthKey, | 			RegisterMethod: RegisterMethodAuthKey, | ||||||
| 			Expiry:         ®isterRequest.Expiry, | 			Expiry:         ®isterRequest.Expiry, | ||||||
| @ -447,7 +447,7 @@ func (h *Headscale) handleAuthKeyCommon( | |||||||
| 				Bool("noise", isNoise). | 				Bool("noise", isNoise). | ||||||
| 				Err(err). | 				Err(err). | ||||||
| 				Msg("could not register machine") | 				Msg("could not register machine") | ||||||
| 			machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name). | 			machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.User.Name). | ||||||
| 				Inc() | 				Inc() | ||||||
| 			http.Error(writer, "Internal server error", http.StatusInternalServerError) | 			http.Error(writer, "Internal server error", http.StatusInternalServerError) | ||||||
| 
 | 
 | ||||||
| @ -462,7 +462,7 @@ func (h *Headscale) handleAuthKeyCommon( | |||||||
| 			Bool("noise", isNoise). | 			Bool("noise", isNoise). | ||||||
| 			Err(err). | 			Err(err). | ||||||
| 			Msg("Failed to use pre-auth key") | 			Msg("Failed to use pre-auth key") | ||||||
| 		machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name). | 		machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.User.Name). | ||||||
| 			Inc() | 			Inc() | ||||||
| 		http.Error(writer, "Internal server error", http.StatusInternalServerError) | 		http.Error(writer, "Internal server error", http.StatusInternalServerError) | ||||||
| 
 | 
 | ||||||
| @ -470,10 +470,10 @@ func (h *Headscale) handleAuthKeyCommon( | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	resp.MachineAuthorized = true | 	resp.MachineAuthorized = true | ||||||
| 	resp.User = *pak.Namespace.toUser() | 	resp.User = *pak.User.toTailscaleUser() | ||||||
| 	// Provide LoginName when registering with pre-auth key | 	// Provide LoginName when registering with pre-auth key | ||||||
| 	// Otherwise it will need to exec `tailscale up` twice to fetch the *LoginName* | 	// Otherwise it will need to exec `tailscale up` twice to fetch the *LoginName* | ||||||
| 	resp.Login = *pak.Namespace.toLogin() | 	resp.Login = *pak.User.toTailscaleLogin() | ||||||
| 
 | 
 | ||||||
| 	respBody, err := h.marshalResponse(resp, machineKey, isNoise) | 	respBody, err := h.marshalResponse(resp, machineKey, isNoise) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -484,13 +484,13 @@ func (h *Headscale) handleAuthKeyCommon( | |||||||
| 			Str("machine", registerRequest.Hostinfo.Hostname). | 			Str("machine", registerRequest.Hostinfo.Hostname). | ||||||
| 			Err(err). | 			Err(err). | ||||||
| 			Msg("Cannot encode message") | 			Msg("Cannot encode message") | ||||||
| 		machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name). | 		machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.User.Name). | ||||||
| 			Inc() | 			Inc() | ||||||
| 		http.Error(writer, "Internal server error", http.StatusInternalServerError) | 		http.Error(writer, "Internal server error", http.StatusInternalServerError) | ||||||
| 
 | 
 | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "success", pak.Namespace.Name). | 	machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "success", pak.User.Name). | ||||||
| 		Inc() | 		Inc() | ||||||
| 	writer.Header().Set("Content-Type", "application/json; charset=utf-8") | 	writer.Header().Set("Content-Type", "application/json; charset=utf-8") | ||||||
| 	writer.WriteHeader(http.StatusOK) | 	writer.WriteHeader(http.StatusOK) | ||||||
| @ -600,7 +600,7 @@ func (h *Headscale) handleMachineLogOutCommon( | |||||||
| 	resp.AuthURL = "" | 	resp.AuthURL = "" | ||||||
| 	resp.MachineAuthorized = false | 	resp.MachineAuthorized = false | ||||||
| 	resp.NodeKeyExpired = true | 	resp.NodeKeyExpired = true | ||||||
| 	resp.User = *machine.Namespace.toUser() | 	resp.User = *machine.User.toTailscaleUser() | ||||||
| 	respBody, err := h.marshalResponse(resp, machineKey, isNoise) | 	respBody, err := h.marshalResponse(resp, machineKey, isNoise) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error(). | 		log.Error(). | ||||||
| @ -662,8 +662,8 @@ func (h *Headscale) handleMachineValidRegistrationCommon( | |||||||
| 
 | 
 | ||||||
| 	resp.AuthURL = "" | 	resp.AuthURL = "" | ||||||
| 	resp.MachineAuthorized = true | 	resp.MachineAuthorized = true | ||||||
| 	resp.User = *machine.Namespace.toUser() | 	resp.User = *machine.User.toTailscaleUser() | ||||||
| 	resp.Login = *machine.Namespace.toLogin() | 	resp.Login = *machine.User.toTailscaleLogin() | ||||||
| 
 | 
 | ||||||
| 	respBody, err := h.marshalResponse(resp, machineKey, isNoise) | 	respBody, err := h.marshalResponse(resp, machineKey, isNoise) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -672,13 +672,13 @@ func (h *Headscale) handleMachineValidRegistrationCommon( | |||||||
| 			Bool("noise", isNoise). | 			Bool("noise", isNoise). | ||||||
| 			Err(err). | 			Err(err). | ||||||
| 			Msg("Cannot encode message") | 			Msg("Cannot encode message") | ||||||
| 		machineRegistrations.WithLabelValues("update", "web", "error", machine.Namespace.Name). | 		machineRegistrations.WithLabelValues("update", "web", "error", machine.User.Name). | ||||||
| 			Inc() | 			Inc() | ||||||
| 		http.Error(writer, "Internal server error", http.StatusInternalServerError) | 		http.Error(writer, "Internal server error", http.StatusInternalServerError) | ||||||
| 
 | 
 | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	machineRegistrations.WithLabelValues("update", "web", "success", machine.Namespace.Name). | 	machineRegistrations.WithLabelValues("update", "web", "success", machine.User.Name). | ||||||
| 		Inc() | 		Inc() | ||||||
| 
 | 
 | ||||||
| 	writer.Header().Set("Content-Type", "application/json; charset=utf-8") | 	writer.Header().Set("Content-Type", "application/json; charset=utf-8") | ||||||
| @ -726,7 +726,7 @@ func (h *Headscale) handleMachineRefreshKeyCommon( | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	resp.AuthURL = "" | 	resp.AuthURL = "" | ||||||
| 	resp.User = *machine.Namespace.toUser() | 	resp.User = *machine.User.toTailscaleUser() | ||||||
| 	respBody, err := h.marshalResponse(resp, machineKey, isNoise) | 	respBody, err := h.marshalResponse(resp, machineKey, isNoise) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error(). | 		log.Error(). | ||||||
| @ -801,13 +801,13 @@ func (h *Headscale) handleMachineExpiredOrLoggedOutCommon( | |||||||
| 			Bool("noise", isNoise). | 			Bool("noise", isNoise). | ||||||
| 			Err(err). | 			Err(err). | ||||||
| 			Msg("Cannot encode message") | 			Msg("Cannot encode message") | ||||||
| 		machineRegistrations.WithLabelValues("reauth", "web", "error", machine.Namespace.Name). | 		machineRegistrations.WithLabelValues("reauth", "web", "error", machine.User.Name). | ||||||
| 			Inc() | 			Inc() | ||||||
| 		http.Error(writer, "Internal server error", http.StatusInternalServerError) | 		http.Error(writer, "Internal server error", http.StatusInternalServerError) | ||||||
| 
 | 
 | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	machineRegistrations.WithLabelValues("reauth", "web", "success", machine.Namespace.Name). | 	machineRegistrations.WithLabelValues("reauth", "web", "success", machine.User.Name). | ||||||
| 		Inc() | 		Inc() | ||||||
| 
 | 
 | ||||||
| 	writer.Header().Set("Content-Type", "application/json; charset=utf-8") | 	writer.Header().Set("Content-Type", "application/json; charset=utf-8") | ||||||
|  | |||||||
| @ -183,7 +183,7 @@ func (h *Headscale) handlePollCommon( | |||||||
| 		} | 		} | ||||||
| 		// It sounds like we should update the nodes when we have received a endpoint update | 		// It sounds like we should update the nodes when we have received a endpoint update | ||||||
| 		// even tho the comments in the tailscale code dont explicitly say so. | 		// even tho the comments in the tailscale code dont explicitly say so. | ||||||
| 		updateRequestsFromNode.WithLabelValues(machine.Namespace.Name, machine.Hostname, "endpoint-update"). | 		updateRequestsFromNode.WithLabelValues(machine.User.Name, machine.Hostname, "endpoint-update"). | ||||||
| 			Inc() | 			Inc() | ||||||
| 		updateChan <- struct{}{} | 		updateChan <- struct{}{} | ||||||
| 
 | 
 | ||||||
| @ -216,7 +216,7 @@ func (h *Headscale) handlePollCommon( | |||||||
| 		Bool("noise", isNoise). | 		Bool("noise", isNoise). | ||||||
| 		Str("machine", machine.Hostname). | 		Str("machine", machine.Hostname). | ||||||
| 		Msg("Notifying peers") | 		Msg("Notifying peers") | ||||||
| 	updateRequestsFromNode.WithLabelValues(machine.Namespace.Name, machine.Hostname, "full-update"). | 	updateRequestsFromNode.WithLabelValues(machine.User.Name, machine.Hostname, "full-update"). | ||||||
| 		Inc() | 		Inc() | ||||||
| 	updateChan <- struct{}{} | 	updateChan <- struct{}{} | ||||||
| 
 | 
 | ||||||
| @ -342,7 +342,7 @@ func (h *Headscale) pollNetMapStream( | |||||||
| 			now := time.Now().UTC() | 			now := time.Now().UTC() | ||||||
| 			machine.LastSeen = &now | 			machine.LastSeen = &now | ||||||
| 
 | 
 | ||||||
| 			lastStateUpdate.WithLabelValues(machine.Namespace.Name, machine.Hostname). | 			lastStateUpdate.WithLabelValues(machine.User.Name, machine.Hostname). | ||||||
| 				Set(float64(now.Unix())) | 				Set(float64(now.Unix())) | ||||||
| 			machine.LastSuccessfulUpdate = &now | 			machine.LastSuccessfulUpdate = &now | ||||||
| 
 | 
 | ||||||
| @ -453,7 +453,7 @@ func (h *Headscale) pollNetMapStream( | |||||||
| 				Str("machine", machine.Hostname). | 				Str("machine", machine.Hostname). | ||||||
| 				Str("channel", "update"). | 				Str("channel", "update"). | ||||||
| 				Msg("Received a request for update") | 				Msg("Received a request for update") | ||||||
| 			updateRequestsReceivedOnChannel.WithLabelValues(machine.Namespace.Name, machine.Hostname). | 			updateRequestsReceivedOnChannel.WithLabelValues(machine.User.Name, machine.Hostname). | ||||||
| 				Inc() | 				Inc() | ||||||
| 
 | 
 | ||||||
| 			if h.isOutdated(machine) { | 			if h.isOutdated(machine) { | ||||||
| @ -466,7 +466,7 @@ func (h *Headscale) pollNetMapStream( | |||||||
| 					Bool("noise", isNoise). | 					Bool("noise", isNoise). | ||||||
| 					Str("machine", machine.Hostname). | 					Str("machine", machine.Hostname). | ||||||
| 					Time("last_successful_update", lastUpdate). | 					Time("last_successful_update", lastUpdate). | ||||||
| 					Time("last_state_change", h.getLastStateChange(machine.Namespace)). | 					Time("last_state_change", h.getLastStateChange(machine.User)). | ||||||
| 					Msgf("There has been updates since the last successful update to %s", machine.Hostname) | 					Msgf("There has been updates since the last successful update to %s", machine.Hostname) | ||||||
| 				data, err := h.getMapResponseData(mapRequest, machine, isNoise) | 				data, err := h.getMapResponseData(mapRequest, machine, isNoise) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| @ -489,7 +489,7 @@ func (h *Headscale) pollNetMapStream( | |||||||
| 						Str("channel", "update"). | 						Str("channel", "update"). | ||||||
| 						Err(err). | 						Err(err). | ||||||
| 						Msg("Could not write the map response") | 						Msg("Could not write the map response") | ||||||
| 					updateRequestsSentToNode.WithLabelValues(machine.Namespace.Name, machine.Hostname, "failed"). | 					updateRequestsSentToNode.WithLabelValues(machine.User.Name, machine.Hostname, "failed"). | ||||||
| 						Inc() | 						Inc() | ||||||
| 
 | 
 | ||||||
| 					return | 					return | ||||||
| @ -514,7 +514,7 @@ func (h *Headscale) pollNetMapStream( | |||||||
| 					Str("machine", machine.Hostname). | 					Str("machine", machine.Hostname). | ||||||
| 					Str("channel", "update"). | 					Str("channel", "update"). | ||||||
| 					Msg("Updated Map has been sent") | 					Msg("Updated Map has been sent") | ||||||
| 				updateRequestsSentToNode.WithLabelValues(machine.Namespace.Name, machine.Hostname, "success"). | 				updateRequestsSentToNode.WithLabelValues(machine.User.Name, machine.Hostname, "success"). | ||||||
| 					Inc() | 					Inc() | ||||||
| 
 | 
 | ||||||
| 				// Keep track of the last successful update, | 				// Keep track of the last successful update, | ||||||
| @ -540,7 +540,7 @@ func (h *Headscale) pollNetMapStream( | |||||||
| 				} | 				} | ||||||
| 				now := time.Now().UTC() | 				now := time.Now().UTC() | ||||||
| 
 | 
 | ||||||
| 				lastStateUpdate.WithLabelValues(machine.Namespace.Name, machine.Hostname). | 				lastStateUpdate.WithLabelValues(machine.User.Name, machine.Hostname). | ||||||
| 					Set(float64(now.Unix())) | 					Set(float64(now.Unix())) | ||||||
| 				machine.LastSuccessfulUpdate = &now | 				machine.LastSuccessfulUpdate = &now | ||||||
| 
 | 
 | ||||||
| @ -566,7 +566,7 @@ func (h *Headscale) pollNetMapStream( | |||||||
| 					Bool("noise", isNoise). | 					Bool("noise", isNoise). | ||||||
| 					Str("machine", machine.Hostname). | 					Str("machine", machine.Hostname). | ||||||
| 					Time("last_successful_update", lastUpdate). | 					Time("last_successful_update", lastUpdate). | ||||||
| 					Time("last_state_change", h.getLastStateChange(machine.Namespace)). | 					Time("last_state_change", h.getLastStateChange(machine.User)). | ||||||
| 					Msgf("%s is up to date", machine.Hostname) | 					Msgf("%s is up to date", machine.Hostname) | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| @ -676,7 +676,7 @@ func (h *Headscale) scheduledPollWorker( | |||||||
| 				Str("machine", machine.Hostname). | 				Str("machine", machine.Hostname). | ||||||
| 				Bool("noise", isNoise). | 				Bool("noise", isNoise). | ||||||
| 				Msg("Sending update request") | 				Msg("Sending update request") | ||||||
| 			updateRequestsFromNode.WithLabelValues(machine.Namespace.Name, machine.Hostname, "scheduled-update"). | 			updateRequestsFromNode.WithLabelValues(machine.User.Name, machine.Hostname, "scheduled-update"). | ||||||
| 				Inc() | 				Inc() | ||||||
| 			select { | 			select { | ||||||
| 			case updateChan <- struct{}{}: | 			case updateChan <- struct{}{}: | ||||||
|  | |||||||
| @ -10,10 +10,10 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestGetRoutes(c *check.C) { | func (s *Suite) TestGetRoutes(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test") | 	user, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine("test", "test_get_route_machine") | 	_, err = app.GetMachine("test", "test_get_route_machine") | ||||||
| @ -32,7 +32,7 @@ func (s *Suite) TestGetRoutes(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "test_get_route_machine", | 		Hostname:       "test_get_route_machine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 		HostInfo:       HostInfo(hostInfo), | 		HostInfo:       HostInfo(hostInfo), | ||||||
| @ -54,10 +54,10 @@ func (s *Suite) TestGetRoutes(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestGetEnableRoutes(c *check.C) { | func (s *Suite) TestGetEnableRoutes(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test") | 	user, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine("test", "test_enable_route_machine") | 	_, err = app.GetMachine("test", "test_enable_route_machine") | ||||||
| @ -83,7 +83,7 @@ func (s *Suite) TestGetEnableRoutes(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "test_enable_route_machine", | 		Hostname:       "test_enable_route_machine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 		HostInfo:       HostInfo(hostInfo), | 		HostInfo:       HostInfo(hostInfo), | ||||||
| @ -129,10 +129,10 @@ func (s *Suite) TestGetEnableRoutes(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestIsUniquePrefix(c *check.C) { | func (s *Suite) TestIsUniquePrefix(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test") | 	user, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine("test", "test_enable_route_machine") | 	_, err = app.GetMachine("test", "test_enable_route_machine") | ||||||
| @ -157,7 +157,7 @@ func (s *Suite) TestIsUniquePrefix(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "test_enable_route_machine", | 		Hostname:       "test_enable_route_machine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 		HostInfo:       HostInfo(hostInfo1), | 		HostInfo:       HostInfo(hostInfo1), | ||||||
| @ -182,7 +182,7 @@ func (s *Suite) TestIsUniquePrefix(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "test_enable_route_machine", | 		Hostname:       "test_enable_route_machine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 		HostInfo:       HostInfo(hostInfo2), | 		HostInfo:       HostInfo(hostInfo2), | ||||||
| @ -213,10 +213,10 @@ func (s *Suite) TestIsUniquePrefix(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestSubnetFailover(c *check.C) { | func (s *Suite) TestSubnetFailover(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test") | 	user, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine("test", "test_enable_route_machine") | 	_, err = app.GetMachine("test", "test_enable_route_machine") | ||||||
| @ -243,7 +243,7 @@ func (s *Suite) TestSubnetFailover(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "test_enable_route_machine", | 		Hostname:       "test_enable_route_machine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 		HostInfo:       HostInfo(hostInfo1), | 		HostInfo:       HostInfo(hostInfo1), | ||||||
| @ -280,7 +280,7 @@ func (s *Suite) TestSubnetFailover(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "test_enable_route_machine", | 		Hostname:       "test_enable_route_machine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 		HostInfo:       HostInfo(hostInfo2), | 		HostInfo:       HostInfo(hostInfo2), | ||||||
| @ -358,10 +358,10 @@ func (s *Suite) TestSubnetFailover(c *check.C) { | |||||||
| // including both the primary routes the node is responsible for, and the | // including both the primary routes the node is responsible for, and the | ||||||
| // exit node routes if enabled. | // exit node routes if enabled. | ||||||
| func (s *Suite) TestAllowedIPRoutes(c *check.C) { | func (s *Suite) TestAllowedIPRoutes(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test") | 	user, err := app.CreateUser("test") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine("test", "test_enable_route_machine") | 	_, err = app.GetMachine("test", "test_enable_route_machine") | ||||||
| @ -402,7 +402,7 @@ func (s *Suite) TestAllowedIPRoutes(c *check.C) { | |||||||
| 		NodeKey:        NodePublicKeyStripPrefix(nodeKey.Public()), | 		NodeKey:        NodePublicKeyStripPrefix(nodeKey.Public()), | ||||||
| 		DiscoKey:       DiscoPublicKeyStripPrefix(discoKey.Public()), | 		DiscoKey:       DiscoPublicKeyStripPrefix(discoKey.Public()), | ||||||
| 		Hostname:       "test_enable_route_machine", | 		Hostname:       "test_enable_route_machine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 		HostInfo:       HostInfo(hostInfo1), | 		HostInfo:       HostInfo(hostInfo1), | ||||||
|  | |||||||
| @ -22,10 +22,10 @@ func (s *Suite) TestGetUsedIps(c *check.C) { | |||||||
| 	ips, err := app.getAvailableIPs() | 	ips, err := app.getAvailableIPs() | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	namespace, err := app.CreateNamespace("test-ip") | 	user, err := app.CreateUser("test-ip") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine("test", "testmachine") | 	_, err = app.GetMachine("test", "testmachine") | ||||||
| @ -37,7 +37,7 @@ func (s *Suite) TestGetUsedIps(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testmachine", | 		Hostname:       "testmachine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 		IPAddresses:    ips, | 		IPAddresses:    ips, | ||||||
| @ -64,7 +64,7 @@ func (s *Suite) TestGetUsedIps(c *check.C) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Suite) TestGetMultiIp(c *check.C) { | func (s *Suite) TestGetMultiIp(c *check.C) { | ||||||
| 	namespace, err := app.CreateNamespace("test-ip-multi") | 	user, err := app.CreateUser("test-ip-multi") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	for index := 1; index <= 350; index++ { | 	for index := 1; index <= 350; index++ { | ||||||
| @ -73,7 +73,7 @@ func (s *Suite) TestGetMultiIp(c *check.C) { | |||||||
| 		ips, err := app.getAvailableIPs() | 		ips, err := app.getAvailableIPs() | ||||||
| 		c.Assert(err, check.IsNil) | 		c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 		pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 		pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 		c.Assert(err, check.IsNil) | 		c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 		_, err = app.GetMachine("test", "testmachine") | 		_, err = app.GetMachine("test", "testmachine") | ||||||
| @ -85,7 +85,7 @@ func (s *Suite) TestGetMultiIp(c *check.C) { | |||||||
| 			NodeKey:        "bar", | 			NodeKey:        "bar", | ||||||
| 			DiscoKey:       "faa", | 			DiscoKey:       "faa", | ||||||
| 			Hostname:       "testmachine", | 			Hostname:       "testmachine", | ||||||
| 			NamespaceID:    namespace.ID, | 			UserID:    user.ID, | ||||||
| 			RegisterMethod: RegisterMethodAuthKey, | 			RegisterMethod: RegisterMethodAuthKey, | ||||||
| 			AuthKeyID:      uint(pak.ID), | 			AuthKeyID:      uint(pak.ID), | ||||||
| 			IPAddresses:    ips, | 			IPAddresses:    ips, | ||||||
| @ -160,10 +160,10 @@ func (s *Suite) TestGetAvailableIpMachineWithoutIP(c *check.C) { | |||||||
| 	c.Assert(len(ips), check.Equals, 1) | 	c.Assert(len(ips), check.Equals, 1) | ||||||
| 	c.Assert(ips[0].String(), check.Equals, expected.String()) | 	c.Assert(ips[0].String(), check.Equals, expected.String()) | ||||||
| 
 | 
 | ||||||
| 	namespace, err := app.CreateNamespace("test-ip") | 	user, err := app.CreateUser("test-ip") | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | 	pak, err := app.CreatePreAuthKey(user.Name, false, false, nil, nil) | ||||||
| 	c.Assert(err, check.IsNil) | 	c.Assert(err, check.IsNil) | ||||||
| 
 | 
 | ||||||
| 	_, err = app.GetMachine("test", "testmachine") | 	_, err = app.GetMachine("test", "testmachine") | ||||||
| @ -175,7 +175,7 @@ func (s *Suite) TestGetAvailableIpMachineWithoutIP(c *check.C) { | |||||||
| 		NodeKey:        "bar", | 		NodeKey:        "bar", | ||||||
| 		DiscoKey:       "faa", | 		DiscoKey:       "faa", | ||||||
| 		Hostname:       "testmachine", | 		Hostname:       "testmachine", | ||||||
| 		NamespaceID:    namespace.ID, | 		UserID:    user.ID, | ||||||
| 		RegisterMethod: RegisterMethodAuthKey, | 		RegisterMethod: RegisterMethodAuthKey, | ||||||
| 		AuthKeyID:      uint(pak.ID), | 		AuthKeyID:      uint(pak.ID), | ||||||
| 	} | 	} | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user