mirror of
https://github.com/juanfont/headscale.git
synced 2024-12-22 16:07:34 +00:00
fix tags not resolving to username if email is present (#2309)
* ensure valid tags is populated on user gets too Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * ensure forced tags are added Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * remove unused envvar in test Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * debug log auth/unauth tags in policy man Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * defer shutdown in tags test Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * add tag test with groups Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * add email, display name, picture to create user Updates #2166 Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * add ability to set display and email to cli Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * add email to test users in integration Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * fix issue where tags were only assigned to email, not username Fixes #2300 Fixes #2307 Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * expand principles to correct login name and if fix an issue where nodeip principles might not expand to all relevant IPs instead of taking the first in a prefix. Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * fix ssh unit test Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * update cli and oauth tests for users with email Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * index by test email Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * fix last test Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> --------- Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
parent
af4508b9dc
commit
770f3dcb93
@ -39,33 +39,33 @@ func init() {
|
|||||||
|
|
||||||
err := registerNodeCmd.MarkFlagRequired("user")
|
err := registerNodeCmd.MarkFlagRequired("user")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
registerNodeCmd.Flags().StringP("key", "k", "", "Key")
|
registerNodeCmd.Flags().StringP("key", "k", "", "Key")
|
||||||
err = registerNodeCmd.MarkFlagRequired("key")
|
err = registerNodeCmd.MarkFlagRequired("key")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
nodeCmd.AddCommand(registerNodeCmd)
|
nodeCmd.AddCommand(registerNodeCmd)
|
||||||
|
|
||||||
expireNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
|
expireNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
|
||||||
err = expireNodeCmd.MarkFlagRequired("identifier")
|
err = expireNodeCmd.MarkFlagRequired("identifier")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
nodeCmd.AddCommand(expireNodeCmd)
|
nodeCmd.AddCommand(expireNodeCmd)
|
||||||
|
|
||||||
renameNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
|
renameNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
|
||||||
err = renameNodeCmd.MarkFlagRequired("identifier")
|
err = renameNodeCmd.MarkFlagRequired("identifier")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
nodeCmd.AddCommand(renameNodeCmd)
|
nodeCmd.AddCommand(renameNodeCmd)
|
||||||
|
|
||||||
deleteNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
|
deleteNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
|
||||||
err = deleteNodeCmd.MarkFlagRequired("identifier")
|
err = deleteNodeCmd.MarkFlagRequired("identifier")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
nodeCmd.AddCommand(deleteNodeCmd)
|
nodeCmd.AddCommand(deleteNodeCmd)
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ func init() {
|
|||||||
|
|
||||||
err = moveNodeCmd.MarkFlagRequired("identifier")
|
err = moveNodeCmd.MarkFlagRequired("identifier")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
moveNodeCmd.Flags().StringP("user", "u", "", "New user")
|
moveNodeCmd.Flags().StringP("user", "u", "", "New user")
|
||||||
@ -85,7 +85,7 @@ func init() {
|
|||||||
|
|
||||||
err = moveNodeCmd.MarkFlagRequired("user")
|
err = moveNodeCmd.MarkFlagRequired("user")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
nodeCmd.AddCommand(moveNodeCmd)
|
nodeCmd.AddCommand(moveNodeCmd)
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ func init() {
|
|||||||
|
|
||||||
err = tagCmd.MarkFlagRequired("identifier")
|
err = tagCmd.MarkFlagRequired("identifier")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
tagCmd.Flags().
|
tagCmd.Flags().
|
||||||
StringSliceP("tags", "t", []string{}, "List of tags to add to the node")
|
StringSliceP("tags", "t", []string{}, "List of tags to add to the node")
|
||||||
|
@ -25,21 +25,21 @@ func init() {
|
|||||||
enableRouteCmd.Flags().Uint64P("route", "r", 0, "Route identifier (ID)")
|
enableRouteCmd.Flags().Uint64P("route", "r", 0, "Route identifier (ID)")
|
||||||
err := enableRouteCmd.MarkFlagRequired("route")
|
err := enableRouteCmd.MarkFlagRequired("route")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
routesCmd.AddCommand(enableRouteCmd)
|
routesCmd.AddCommand(enableRouteCmd)
|
||||||
|
|
||||||
disableRouteCmd.Flags().Uint64P("route", "r", 0, "Route identifier (ID)")
|
disableRouteCmd.Flags().Uint64P("route", "r", 0, "Route identifier (ID)")
|
||||||
err = disableRouteCmd.MarkFlagRequired("route")
|
err = disableRouteCmd.MarkFlagRequired("route")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
routesCmd.AddCommand(disableRouteCmd)
|
routesCmd.AddCommand(disableRouteCmd)
|
||||||
|
|
||||||
deleteRouteCmd.Flags().Uint64P("route", "r", 0, "Route identifier (ID)")
|
deleteRouteCmd.Flags().Uint64P("route", "r", 0, "Route identifier (ID)")
|
||||||
err = deleteRouteCmd.MarkFlagRequired("route")
|
err = deleteRouteCmd.MarkFlagRequired("route")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err.Error())
|
log.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
routesCmd.AddCommand(deleteRouteCmd)
|
routesCmd.AddCommand(deleteRouteCmd)
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
survey "github.com/AlecAivazis/survey/v2"
|
survey "github.com/AlecAivazis/survey/v2"
|
||||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||||
@ -40,6 +41,9 @@ func usernameAndIDFromFlag(cmd *cobra.Command) (uint64, string) {
|
|||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(userCmd)
|
rootCmd.AddCommand(userCmd)
|
||||||
userCmd.AddCommand(createUserCmd)
|
userCmd.AddCommand(createUserCmd)
|
||||||
|
createUserCmd.Flags().StringP("display-name", "d", "", "Display name")
|
||||||
|
createUserCmd.Flags().StringP("email", "e", "", "Email")
|
||||||
|
createUserCmd.Flags().StringP("picture-url", "p", "", "Profile picture URL")
|
||||||
userCmd.AddCommand(listUsersCmd)
|
userCmd.AddCommand(listUsersCmd)
|
||||||
usernameAndIDFlag(listUsersCmd)
|
usernameAndIDFlag(listUsersCmd)
|
||||||
listUsersCmd.Flags().StringP("email", "e", "", "Email")
|
listUsersCmd.Flags().StringP("email", "e", "", "Email")
|
||||||
@ -83,6 +87,28 @@ var createUserCmd = &cobra.Command{
|
|||||||
|
|
||||||
request := &v1.CreateUserRequest{Name: userName}
|
request := &v1.CreateUserRequest{Name: userName}
|
||||||
|
|
||||||
|
if displayName, _ := cmd.Flags().GetString("display-name"); displayName != "" {
|
||||||
|
request.DisplayName = displayName
|
||||||
|
}
|
||||||
|
|
||||||
|
if email, _ := cmd.Flags().GetString("email"); email != "" {
|
||||||
|
request.Email = email
|
||||||
|
}
|
||||||
|
|
||||||
|
if pictureURL, _ := cmd.Flags().GetString("picture-url"); pictureURL != "" {
|
||||||
|
if _, err := url.Parse(pictureURL); err != nil {
|
||||||
|
ErrorOutput(
|
||||||
|
err,
|
||||||
|
fmt.Sprintf(
|
||||||
|
"Invalid Picture URL: %s",
|
||||||
|
err,
|
||||||
|
),
|
||||||
|
output,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
request.PictureUrl = pictureURL
|
||||||
|
}
|
||||||
|
|
||||||
log.Trace().Interface("request", request).Msg("Sending CreateUser request")
|
log.Trace().Interface("request", request).Msg("Sending CreateUser request")
|
||||||
response, err := client.CreateUser(ctx, request)
|
response, err := client.CreateUser(ctx, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.35.1
|
// protoc-gen-go v1.35.2
|
||||||
// protoc (unknown)
|
// protoc (unknown)
|
||||||
// source: headscale/v1/apikey.proto
|
// source: headscale/v1/apikey.proto
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.35.1
|
// protoc-gen-go v1.35.2
|
||||||
// protoc (unknown)
|
// protoc (unknown)
|
||||||
// source: headscale/v1/device.proto
|
// source: headscale/v1/device.proto
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.35.1
|
// protoc-gen-go v1.35.2
|
||||||
// protoc (unknown)
|
// protoc (unknown)
|
||||||
// source: headscale/v1/headscale.proto
|
// source: headscale/v1/headscale.proto
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.35.1
|
// protoc-gen-go v1.35.2
|
||||||
// protoc (unknown)
|
// protoc (unknown)
|
||||||
// source: headscale/v1/node.proto
|
// source: headscale/v1/node.proto
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.35.1
|
// protoc-gen-go v1.35.2
|
||||||
// protoc (unknown)
|
// protoc (unknown)
|
||||||
// source: headscale/v1/policy.proto
|
// source: headscale/v1/policy.proto
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.35.1
|
// protoc-gen-go v1.35.2
|
||||||
// protoc (unknown)
|
// protoc (unknown)
|
||||||
// source: headscale/v1/preauthkey.proto
|
// source: headscale/v1/preauthkey.proto
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.35.1
|
// protoc-gen-go v1.35.2
|
||||||
// protoc (unknown)
|
// protoc (unknown)
|
||||||
// source: headscale/v1/routes.proto
|
// source: headscale/v1/routes.proto
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.35.1
|
// protoc-gen-go v1.35.2
|
||||||
// protoc (unknown)
|
// protoc (unknown)
|
||||||
// source: headscale/v1/user.proto
|
// source: headscale/v1/user.proto
|
||||||
|
|
||||||
@ -127,7 +127,10 @@ type CreateUserRequest struct {
|
|||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
|
DisplayName string `protobuf:"bytes,2,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"`
|
||||||
|
Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"`
|
||||||
|
PictureUrl string `protobuf:"bytes,4,opt,name=picture_url,json=pictureUrl,proto3" json:"picture_url,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *CreateUserRequest) Reset() {
|
func (x *CreateUserRequest) Reset() {
|
||||||
@ -167,6 +170,27 @@ func (x *CreateUserRequest) GetName() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *CreateUserRequest) GetDisplayName() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.DisplayName
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateUserRequest) GetEmail() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Email
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CreateUserRequest) GetPictureUrl() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.PictureUrl
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
type CreateUserResponse struct {
|
type CreateUserResponse struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@ -520,38 +544,43 @@ var file_headscale_v1_user_proto_rawDesc = []byte{
|
|||||||
0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72,
|
0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72,
|
||||||
0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c,
|
0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c,
|
||||||
0x65, 0x5f, 0x70, 0x69, 0x63, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52,
|
0x65, 0x5f, 0x70, 0x69, 0x63, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x0d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x55, 0x72, 0x6c, 0x22, 0x27,
|
0x0d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x55, 0x72, 0x6c, 0x22, 0x81,
|
||||||
0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75,
|
0x01, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71,
|
||||||
0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3c, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74,
|
0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70,
|
||||||
|
0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
|
||||||
|
0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65,
|
||||||
|
0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69,
|
||||||
|
0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x75, 0x72, 0x6c,
|
||||||
|
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x55,
|
||||||
|
0x72, 0x6c, 0x22, 0x3c, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72,
|
||||||
|
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72,
|
||||||
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61,
|
||||||
|
0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72,
|
||||||
|
0x22, 0x45, 0x0a, 0x11, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65,
|
||||||
|
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18,
|
||||||
|
0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6f, 0x6c, 0x64, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08,
|
||||||
|
0x6e, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
|
||||||
|
0x6e, 0x65, 0x77, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x3c, 0x0a, 0x12, 0x52, 0x65, 0x6e, 0x61, 0x6d,
|
||||||
0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a,
|
0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a,
|
||||||
0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65,
|
0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65,
|
||||||
0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52,
|
0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52,
|
||||||
0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x45, 0x0a, 0x11, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x55,
|
0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x23, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55,
|
||||||
0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x6c,
|
0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
|
||||||
0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6f, 0x6c, 0x64, 0x49,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, 0x14, 0x0a, 0x12, 0x44, 0x65,
|
||||||
0x64, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
|
0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x3c, 0x0a, 0x12,
|
0x22, 0x4c, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71,
|
||||||
0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
|
||||||
0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
|
0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
|
||||||
0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e,
|
0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69,
|
||||||
0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x23, 0x0a, 0x11, 0x44, 0x65,
|
0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x3d,
|
||||||
0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||||
0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x22,
|
0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03,
|
||||||
0x14, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73,
|
0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76,
|
||||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4c, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65,
|
0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x42, 0x29, 0x5a,
|
||||||
0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
|
0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x75, 0x61, 0x6e,
|
||||||
0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
|
0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x67,
|
||||||
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a,
|
0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d,
|
|
||||||
0x61, 0x69, 0x6c, 0x22, 0x3d, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73,
|
|
||||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72,
|
|
||||||
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63,
|
|
||||||
0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x05, 0x75, 0x73, 0x65,
|
|
||||||
0x72, 0x73, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
|
||||||
0x2f, 0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63,
|
|
||||||
0x61, 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70,
|
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -1039,6 +1039,15 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"displayName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"pictureUrl": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -278,9 +278,9 @@ func TestConstraints(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "no-duplicate-username-if-no-oidc",
|
name: "no-duplicate-username-if-no-oidc",
|
||||||
run: func(t *testing.T, db *gorm.DB) {
|
run: func(t *testing.T, db *gorm.DB) {
|
||||||
_, err := CreateUser(db, "user1")
|
_, err := CreateUser(db, types.User{Name: "user1"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_, err = CreateUser(db, "user1")
|
_, err = CreateUser(db, types.User{Name: "user1"})
|
||||||
requireConstraintFailed(t, err)
|
requireConstraintFailed(t, err)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -331,7 +331,7 @@ func TestConstraints(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "allow-duplicate-username-cli-then-oidc",
|
name: "allow-duplicate-username-cli-then-oidc",
|
||||||
run: func(t *testing.T, db *gorm.DB) {
|
run: func(t *testing.T, db *gorm.DB) {
|
||||||
_, err := CreateUser(db, "user1") // Create CLI username
|
_, err := CreateUser(db, types.User{Name: "user1"}) // Create CLI username
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
user := types.User{
|
user := types.User{
|
||||||
@ -354,7 +354,7 @@ func TestConstraints(t *testing.T) {
|
|||||||
err := db.Save(&user).Error
|
err := db.Save(&user).Error
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = CreateUser(db, "user1") // Create CLI username
|
_, err = CreateUser(db, types.User{Name: "user1"}) // Create CLI username
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -27,7 +27,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (s *Suite) TestGetNode(c *check.C) {
|
func (s *Suite) TestGetNode(c *check.C) {
|
||||||
user, err := db.CreateUser("test")
|
user, err := db.CreateUser(types.User{Name: "test"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
||||||
@ -56,7 +56,7 @@ func (s *Suite) TestGetNode(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestGetNodeByID(c *check.C) {
|
func (s *Suite) TestGetNodeByID(c *check.C) {
|
||||||
user, err := db.CreateUser("test")
|
user, err := db.CreateUser(types.User{Name: "test"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
||||||
@ -85,7 +85,7 @@ func (s *Suite) TestGetNodeByID(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestGetNodeByAnyNodeKey(c *check.C) {
|
func (s *Suite) TestGetNodeByAnyNodeKey(c *check.C) {
|
||||||
user, err := db.CreateUser("test")
|
user, err := db.CreateUser(types.User{Name: "test"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
||||||
@ -116,7 +116,7 @@ func (s *Suite) TestGetNodeByAnyNodeKey(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestHardDeleteNode(c *check.C) {
|
func (s *Suite) TestHardDeleteNode(c *check.C) {
|
||||||
user, err := db.CreateUser("test")
|
user, err := db.CreateUser(types.User{Name: "test"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
nodeKey := key.NewNode()
|
nodeKey := key.NewNode()
|
||||||
@ -141,7 +141,7 @@ func (s *Suite) TestHardDeleteNode(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestListPeers(c *check.C) {
|
func (s *Suite) TestListPeers(c *check.C) {
|
||||||
user, err := db.CreateUser("test")
|
user, err := db.CreateUser(types.User{Name: "test"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
||||||
@ -188,7 +188,7 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) {
|
|||||||
stor := make([]base, 0)
|
stor := make([]base, 0)
|
||||||
|
|
||||||
for _, name := range []string{"test", "admin"} {
|
for _, name := range []string{"test", "admin"} {
|
||||||
user, err := db.CreateUser(name)
|
user, err := db.CreateUser(types.User{Name: name})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
@ -279,7 +279,7 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestExpireNode(c *check.C) {
|
func (s *Suite) TestExpireNode(c *check.C) {
|
||||||
user, err := db.CreateUser("test")
|
user, err := db.CreateUser(types.User{Name: "test"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
||||||
@ -320,7 +320,7 @@ func (s *Suite) TestExpireNode(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestSetTags(c *check.C) {
|
func (s *Suite) TestSetTags(c *check.C) {
|
||||||
user, err := db.CreateUser("test")
|
user, err := db.CreateUser(types.User{Name: "test"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
||||||
@ -565,7 +565,7 @@ func TestAutoApproveRoutes(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, pol)
|
require.NotNil(t, pol)
|
||||||
|
|
||||||
user, err := adb.CreateUser("test")
|
user, err := adb.CreateUser(types.User{Name: "test"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pak, err := adb.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
pak, err := adb.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
||||||
@ -706,7 +706,7 @@ func TestListEphemeralNodes(t *testing.T) {
|
|||||||
t.Fatalf("creating db: %s", err)
|
t.Fatalf("creating db: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := db.CreateUser("test")
|
user, err := db.CreateUser(types.User{Name: "test"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
||||||
@ -762,10 +762,10 @@ func TestRenameNode(t *testing.T) {
|
|||||||
t.Fatalf("creating db: %s", err)
|
t.Fatalf("creating db: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := db.CreateUser("test")
|
user, err := db.CreateUser(types.User{Name: "test"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
user2, err := db.CreateUser("test2")
|
user2, err := db.CreateUser(types.User{Name: "user2"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
node := types.Node{
|
node := types.Node{
|
||||||
|
@ -15,7 +15,7 @@ func (*Suite) TestCreatePreAuthKey(c *check.C) {
|
|||||||
_, err := db.CreatePreAuthKey(12345, true, false, nil, nil)
|
_, err := db.CreatePreAuthKey(12345, true, false, nil, nil)
|
||||||
c.Assert(err, check.NotNil)
|
c.Assert(err, check.NotNil)
|
||||||
|
|
||||||
user, err := db.CreateUser("test")
|
user, err := db.CreateUser(types.User{Name: "test"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
key, err := db.CreatePreAuthKey(types.UserID(user.ID), true, false, nil, nil)
|
key, err := db.CreatePreAuthKey(types.UserID(user.ID), true, false, nil, nil)
|
||||||
@ -41,7 +41,7 @@ func (*Suite) TestCreatePreAuthKey(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (*Suite) TestExpiredPreAuthKey(c *check.C) {
|
func (*Suite) TestExpiredPreAuthKey(c *check.C) {
|
||||||
user, err := db.CreateUser("test2")
|
user, err := db.CreateUser(types.User{Name: "test2"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
now := time.Now().Add(-5 * time.Second)
|
now := time.Now().Add(-5 * time.Second)
|
||||||
@ -60,7 +60,7 @@ func (*Suite) TestPreAuthKeyDoesNotExist(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (*Suite) TestValidateKeyOk(c *check.C) {
|
func (*Suite) TestValidateKeyOk(c *check.C) {
|
||||||
user, err := db.CreateUser("test3")
|
user, err := db.CreateUser(types.User{Name: "test3"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), true, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), true, false, nil, nil)
|
||||||
@ -72,7 +72,7 @@ func (*Suite) TestValidateKeyOk(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (*Suite) TestAlreadyUsedKey(c *check.C) {
|
func (*Suite) TestAlreadyUsedKey(c *check.C) {
|
||||||
user, err := db.CreateUser("test4")
|
user, err := db.CreateUser(types.User{Name: "test4"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
||||||
@ -94,7 +94,7 @@ func (*Suite) TestAlreadyUsedKey(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (*Suite) TestReusableBeingUsedKey(c *check.C) {
|
func (*Suite) TestReusableBeingUsedKey(c *check.C) {
|
||||||
user, err := db.CreateUser("test5")
|
user, err := db.CreateUser(types.User{Name: "test5"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), true, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), true, false, nil, nil)
|
||||||
@ -116,7 +116,7 @@ func (*Suite) TestReusableBeingUsedKey(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (*Suite) TestNotReusableNotBeingUsedKey(c *check.C) {
|
func (*Suite) TestNotReusableNotBeingUsedKey(c *check.C) {
|
||||||
user, err := db.CreateUser("test6")
|
user, err := db.CreateUser(types.User{Name: "test6"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
||||||
@ -128,7 +128,7 @@ func (*Suite) TestNotReusableNotBeingUsedKey(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (*Suite) TestExpirePreauthKey(c *check.C) {
|
func (*Suite) TestExpirePreauthKey(c *check.C) {
|
||||||
user, err := db.CreateUser("test3")
|
user, err := db.CreateUser(types.User{Name: "test3"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), true, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), true, false, nil, nil)
|
||||||
@ -145,7 +145,7 @@ func (*Suite) TestExpirePreauthKey(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (*Suite) TestNotReusableMarkedAsUsed(c *check.C) {
|
func (*Suite) TestNotReusableMarkedAsUsed(c *check.C) {
|
||||||
user, err := db.CreateUser("test6")
|
user, err := db.CreateUser(types.User{Name: "test6"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
||||||
@ -158,7 +158,7 @@ func (*Suite) TestNotReusableMarkedAsUsed(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (*Suite) TestPreAuthKeyACLTags(c *check.C) {
|
func (*Suite) TestPreAuthKeyACLTags(c *check.C) {
|
||||||
user, err := db.CreateUser("test8")
|
user, err := db.CreateUser(types.User{Name: "test8"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
_, err = db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, []string{"badtag"})
|
_, err = db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, []string{"badtag"})
|
||||||
|
@ -32,7 +32,7 @@ var mp = func(p string) netip.Prefix {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestGetRoutes(c *check.C) {
|
func (s *Suite) TestGetRoutes(c *check.C) {
|
||||||
user, err := db.CreateUser("test")
|
user, err := db.CreateUser(types.User{Name: "test"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
||||||
@ -76,7 +76,7 @@ func (s *Suite) TestGetRoutes(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestGetEnableRoutes(c *check.C) {
|
func (s *Suite) TestGetEnableRoutes(c *check.C) {
|
||||||
user, err := db.CreateUser("test")
|
user, err := db.CreateUser(types.User{Name: "test"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
||||||
@ -150,7 +150,7 @@ func (s *Suite) TestGetEnableRoutes(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestIsUniquePrefix(c *check.C) {
|
func (s *Suite) TestIsUniquePrefix(c *check.C) {
|
||||||
user, err := db.CreateUser("test")
|
user, err := db.CreateUser(types.User{Name: "test"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
||||||
@ -231,7 +231,7 @@ func (s *Suite) TestIsUniquePrefix(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestDeleteRoutes(c *check.C) {
|
func (s *Suite) TestDeleteRoutes(c *check.C) {
|
||||||
user, err := db.CreateUser("test")
|
user, err := db.CreateUser(types.User{Name: "test"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
||||||
|
@ -15,22 +15,19 @@ var (
|
|||||||
ErrUserStillHasNodes = errors.New("user not empty: node(s) found")
|
ErrUserStillHasNodes = errors.New("user not empty: node(s) found")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (hsdb *HSDatabase) CreateUser(name string) (*types.User, error) {
|
func (hsdb *HSDatabase) CreateUser(user types.User) (*types.User, error) {
|
||||||
return Write(hsdb.DB, func(tx *gorm.DB) (*types.User, error) {
|
return Write(hsdb.DB, func(tx *gorm.DB) (*types.User, error) {
|
||||||
return CreateUser(tx, name)
|
return CreateUser(tx, user)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateUser creates a new User. Returns error if could not be created
|
// CreateUser creates a new User. Returns error if could not be created
|
||||||
// or another user already exists.
|
// or another user already exists.
|
||||||
func CreateUser(tx *gorm.DB, name string) (*types.User, error) {
|
func CreateUser(tx *gorm.DB, user types.User) (*types.User, error) {
|
||||||
err := util.CheckForFQDNRules(name)
|
err := util.CheckForFQDNRules(user.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
user := types.User{
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
if err := tx.Create(&user).Error; err != nil {
|
if err := tx.Create(&user).Error; err != nil {
|
||||||
return nil, fmt.Errorf("creating user: %w", err)
|
return nil, fmt.Errorf("creating user: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (s *Suite) TestCreateAndDestroyUser(c *check.C) {
|
func (s *Suite) TestCreateAndDestroyUser(c *check.C) {
|
||||||
user, err := db.CreateUser("test")
|
user, err := db.CreateUser(types.User{Name: "test"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
c.Assert(user.Name, check.Equals, "test")
|
c.Assert(user.Name, check.Equals, "test")
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ func (s *Suite) TestDestroyUserErrors(c *check.C) {
|
|||||||
err := db.DestroyUser(9998)
|
err := db.DestroyUser(9998)
|
||||||
c.Assert(err, check.Equals, ErrUserNotFound)
|
c.Assert(err, check.Equals, ErrUserNotFound)
|
||||||
|
|
||||||
user, err := db.CreateUser("test")
|
user, err := db.CreateUser(types.User{Name: "test"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
||||||
@ -43,7 +43,7 @@ func (s *Suite) TestDestroyUserErrors(c *check.C) {
|
|||||||
// destroying a user 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)
|
||||||
|
|
||||||
user, err = db.CreateUser("test")
|
user, err = db.CreateUser(types.User{Name: "test"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err = db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
pak, err = db.CreatePreAuthKey(types.UserID(user.ID), false, false, nil, nil)
|
||||||
@ -64,7 +64,7 @@ func (s *Suite) TestDestroyUserErrors(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestRenameUser(c *check.C) {
|
func (s *Suite) TestRenameUser(c *check.C) {
|
||||||
userTest, err := db.CreateUser("test")
|
userTest, err := db.CreateUser(types.User{Name: "test"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
c.Assert(userTest.Name, check.Equals, "test")
|
c.Assert(userTest.Name, check.Equals, "test")
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ func (s *Suite) TestRenameUser(c *check.C) {
|
|||||||
err = db.RenameUser(99988, "test")
|
err = db.RenameUser(99988, "test")
|
||||||
c.Assert(err, check.Equals, ErrUserNotFound)
|
c.Assert(err, check.Equals, ErrUserNotFound)
|
||||||
|
|
||||||
userTest2, err := db.CreateUser("test2")
|
userTest2, err := db.CreateUser(types.User{Name: "test2"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
c.Assert(userTest2.Name, check.Equals, "test2")
|
c.Assert(userTest2.Name, check.Equals, "test2")
|
||||||
|
|
||||||
@ -98,10 +98,10 @@ func (s *Suite) TestRenameUser(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestSetMachineUser(c *check.C) {
|
func (s *Suite) TestSetMachineUser(c *check.C) {
|
||||||
oldUser, err := db.CreateUser("old")
|
oldUser, err := db.CreateUser(types.User{Name: "old"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
newUser, err := db.CreateUser("new")
|
newUser, err := db.CreateUser(types.User{Name: "new"})
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
pak, err := db.CreatePreAuthKey(types.UserID(oldUser.ID), false, false, nil, nil)
|
pak, err := db.CreatePreAuthKey(types.UserID(oldUser.ID), false, false, nil, nil)
|
||||||
|
@ -11,7 +11,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/puzpuzpuz/xsync/v3"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/samber/lo"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
@ -21,6 +23,7 @@ import (
|
|||||||
|
|
||||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||||
"github.com/juanfont/headscale/hscontrol/db"
|
"github.com/juanfont/headscale/hscontrol/db"
|
||||||
|
"github.com/juanfont/headscale/hscontrol/policy"
|
||||||
"github.com/juanfont/headscale/hscontrol/types"
|
"github.com/juanfont/headscale/hscontrol/types"
|
||||||
"github.com/juanfont/headscale/hscontrol/util"
|
"github.com/juanfont/headscale/hscontrol/util"
|
||||||
)
|
)
|
||||||
@ -40,7 +43,13 @@ func (api headscaleV1APIServer) CreateUser(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *v1.CreateUserRequest,
|
request *v1.CreateUserRequest,
|
||||||
) (*v1.CreateUserResponse, error) {
|
) (*v1.CreateUserResponse, error) {
|
||||||
user, err := api.h.db.CreateUser(request.GetName())
|
newUser := types.User{
|
||||||
|
Name: request.GetName(),
|
||||||
|
DisplayName: request.GetDisplayName(),
|
||||||
|
Email: request.GetEmail(),
|
||||||
|
ProfilePicURL: request.GetPictureUrl(),
|
||||||
|
}
|
||||||
|
user, err := api.h.db.CreateUser(newUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -457,19 +466,7 @@ func (api headscaleV1APIServer) ListNodes(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
response := make([]*v1.Node, len(nodes))
|
response := nodesToProto(api.h.polMan, isLikelyConnected, nodes)
|
||||||
for index, node := range nodes {
|
|
||||||
resp := node.Proto()
|
|
||||||
|
|
||||||
// Populate the online field based on
|
|
||||||
// currently connected nodes.
|
|
||||||
if val, ok := isLikelyConnected.Load(node.ID); ok && val {
|
|
||||||
resp.Online = true
|
|
||||||
}
|
|
||||||
|
|
||||||
response[index] = resp
|
|
||||||
}
|
|
||||||
|
|
||||||
return &v1.ListNodesResponse{Nodes: response}, nil
|
return &v1.ListNodesResponse{Nodes: response}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,6 +479,11 @@ func (api headscaleV1APIServer) ListNodes(
|
|||||||
return nodes[i].ID < nodes[j].ID
|
return nodes[i].ID < nodes[j].ID
|
||||||
})
|
})
|
||||||
|
|
||||||
|
response := nodesToProto(api.h.polMan, isLikelyConnected, nodes)
|
||||||
|
return &v1.ListNodesResponse{Nodes: response}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodesToProto(polMan policy.PolicyManager, isLikelyConnected *xsync.MapOf[types.NodeID, bool], nodes types.Nodes) []*v1.Node {
|
||||||
response := make([]*v1.Node, len(nodes))
|
response := make([]*v1.Node, len(nodes))
|
||||||
for index, node := range nodes {
|
for index, node := range nodes {
|
||||||
resp := node.Proto()
|
resp := node.Proto()
|
||||||
@ -492,12 +494,12 @@ func (api headscaleV1APIServer) ListNodes(
|
|||||||
resp.Online = true
|
resp.Online = true
|
||||||
}
|
}
|
||||||
|
|
||||||
validTags := api.h.polMan.Tags(node)
|
tags := polMan.Tags(node)
|
||||||
resp.ValidTags = validTags
|
resp.ValidTags = lo.Uniq(append(tags, node.ForcedTags...))
|
||||||
response[index] = resp
|
response[index] = resp
|
||||||
}
|
}
|
||||||
|
|
||||||
return &v1.ListNodesResponse{Nodes: response}, nil
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api headscaleV1APIServer) MoveNode(
|
func (api headscaleV1APIServer) MoveNode(
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"iter"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
@ -361,37 +362,67 @@ func (pol *ACLPolicy) CompileSSHPolicy(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
principals := make([]*tailcfg.SSHPrincipal, 0, len(sshACL.Sources))
|
var principals []*tailcfg.SSHPrincipal
|
||||||
for innerIndex, rawSrc := range sshACL.Sources {
|
for innerIndex, srcToken := range sshACL.Sources {
|
||||||
if isWildcard(rawSrc) {
|
if isWildcard(srcToken) {
|
||||||
principals = append(principals, &tailcfg.SSHPrincipal{
|
principals = []*tailcfg.SSHPrincipal{{
|
||||||
Any: true,
|
Any: true,
|
||||||
})
|
}}
|
||||||
} else if isGroup(rawSrc) {
|
break
|
||||||
users, err := pol.expandUsersFromGroup(rawSrc)
|
}
|
||||||
|
|
||||||
|
// If the token is a group, expand the users and validate
|
||||||
|
// them. Then use the .Username() to get the login name
|
||||||
|
// that corresponds with the User info in the netmap.
|
||||||
|
if isGroup(srcToken) {
|
||||||
|
usersFromGroup, err := pol.expandUsersFromGroup(srcToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("parsing SSH policy, expanding user from group, index: %d->%d: %w", index, innerIndex, err)
|
return nil, fmt.Errorf("parsing SSH policy, expanding user from group, index: %d->%d: %w", index, innerIndex, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, user := range users {
|
for _, userStr := range usersFromGroup {
|
||||||
|
user, err := findUserFromTokenOrErr(users, userStr)
|
||||||
|
if err != nil {
|
||||||
|
log.Trace().Err(err).Msg("user not found")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
principals = append(principals, &tailcfg.SSHPrincipal{
|
principals = append(principals, &tailcfg.SSHPrincipal{
|
||||||
UserLogin: user,
|
UserLogin: user.Username(),
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
expandedSrcs, err := pol.ExpandAlias(
|
|
||||||
peers,
|
|
||||||
users,
|
|
||||||
rawSrc,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("parsing SSH policy, expanding alias, index: %d->%d: %w", index, innerIndex, err)
|
|
||||||
}
|
|
||||||
for _, expandedSrc := range expandedSrcs.Prefixes() {
|
|
||||||
principals = append(principals, &tailcfg.SSHPrincipal{
|
|
||||||
NodeIP: expandedSrc.Addr().String(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to check if the token is a user, if it is, then we
|
||||||
|
// can use the .Username() to get the login name that
|
||||||
|
// corresponds with the User info in the netmap.
|
||||||
|
// TODO(kradalby): This is a bit of a hack, and it should go
|
||||||
|
// away with the new policy where users can be reliably determined.
|
||||||
|
if user, err := findUserFromTokenOrErr(users, srcToken); err == nil {
|
||||||
|
principals = append(principals, &tailcfg.SSHPrincipal{
|
||||||
|
UserLogin: user.Username(),
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is kind of then non-ideal scenario where we dont really know
|
||||||
|
// what to do with the token, so we expand it to IP addresses of nodes.
|
||||||
|
// The pro here is that we have a pretty good lockdown on the mapping
|
||||||
|
// between users and node, but it can explode if a user owns many nodes.
|
||||||
|
ips, err := pol.ExpandAlias(
|
||||||
|
peers,
|
||||||
|
users,
|
||||||
|
srcToken,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing SSH policy, expanding alias, index: %d->%d: %w", index, innerIndex, err)
|
||||||
|
}
|
||||||
|
for addr := range ipSetAll(ips) {
|
||||||
|
principals = append(principals, &tailcfg.SSHPrincipal{
|
||||||
|
NodeIP: addr.String(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,6 +442,19 @@ func (pol *ACLPolicy) CompileSSHPolicy(
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ipSetAll returns a function that iterates over all the IPs in the IPSet.
|
||||||
|
func ipSetAll(ipSet *netipx.IPSet) iter.Seq[netip.Addr] {
|
||||||
|
return func(yield func(netip.Addr) bool) {
|
||||||
|
for _, rng := range ipSet.Ranges() {
|
||||||
|
for ip := rng.From(); ip.Compare(rng.To()) <= 0; ip = ip.Next() {
|
||||||
|
if !yield(ip) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func sshCheckAction(duration string) (*tailcfg.SSHAction, error) {
|
func sshCheckAction(duration string) (*tailcfg.SSHAction, error) {
|
||||||
sessionLength, err := time.ParseDuration(duration)
|
sessionLength, err := time.ParseDuration(duration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -934,6 +978,7 @@ func isAutoGroup(str string) bool {
|
|||||||
// Invalid tags are tags added by a user on a node, and that user doesn't have authority to add this tag.
|
// Invalid tags are tags added by a user on a node, and that user doesn't have authority to add this tag.
|
||||||
// Valid tags are tags added by a user that is allowed in the ACL policy to add this tag.
|
// Valid tags are tags added by a user that is allowed in the ACL policy to add this tag.
|
||||||
func (pol *ACLPolicy) TagsOfNode(
|
func (pol *ACLPolicy) TagsOfNode(
|
||||||
|
users []types.User,
|
||||||
node *types.Node,
|
node *types.Node,
|
||||||
) ([]string, []string) {
|
) ([]string, []string) {
|
||||||
var validTags []string
|
var validTags []string
|
||||||
@ -956,7 +1001,12 @@ func (pol *ACLPolicy) TagsOfNode(
|
|||||||
}
|
}
|
||||||
var found bool
|
var found bool
|
||||||
for _, owner := range owners {
|
for _, owner := range owners {
|
||||||
if node.User.Username() == owner {
|
user, err := findUserFromTokenOrErr(users, owner)
|
||||||
|
if err != nil {
|
||||||
|
log.Trace().Caller().Err(err).Msg("could not determine user to filter tags by")
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.User.ID == user.ID {
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -988,30 +1038,12 @@ func (pol *ACLPolicy) TagsOfNode(
|
|||||||
func filterNodesByUser(nodes types.Nodes, users []types.User, userToken string) types.Nodes {
|
func filterNodesByUser(nodes types.Nodes, users []types.User, userToken string) types.Nodes {
|
||||||
var out types.Nodes
|
var out types.Nodes
|
||||||
|
|
||||||
var potentialUsers []types.User
|
user, err := findUserFromTokenOrErr(users, userToken)
|
||||||
for _, user := range users {
|
if err != nil {
|
||||||
if user.ProviderIdentifier.Valid && user.ProviderIdentifier.String == userToken {
|
log.Trace().Caller().Err(err).Msg("could not determine user to filter nodes by")
|
||||||
// If a user is matching with a known unique field,
|
return out
|
||||||
// disgard all other users and only keep the current
|
|
||||||
// user.
|
|
||||||
potentialUsers = []types.User{user}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if user.Email == userToken {
|
|
||||||
potentialUsers = append(potentialUsers, user)
|
|
||||||
}
|
|
||||||
if user.Name == userToken {
|
|
||||||
potentialUsers = append(potentialUsers, user)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(potentialUsers) != 1 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
user := potentialUsers[0]
|
|
||||||
|
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
if node.User.ID == user.ID {
|
if node.User.ID == user.ID {
|
||||||
out = append(out, node)
|
out = append(out, node)
|
||||||
@ -1021,6 +1053,44 @@ func filterNodesByUser(nodes types.Nodes, users []types.User, userToken string)
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrorNoUserMatching = errors.New("no user matching")
|
||||||
|
ErrorMultipleUserMatching = errors.New("multiple users matching")
|
||||||
|
)
|
||||||
|
|
||||||
|
func findUserFromTokenOrErr(
|
||||||
|
users []types.User,
|
||||||
|
token string,
|
||||||
|
) (types.User, error) {
|
||||||
|
var potentialUsers []types.User
|
||||||
|
for _, user := range users {
|
||||||
|
if user.ProviderIdentifier.Valid && user.ProviderIdentifier.String == token {
|
||||||
|
// If a user is matching with a known unique field,
|
||||||
|
// disgard all other users and only keep the current
|
||||||
|
// user.
|
||||||
|
potentialUsers = []types.User{user}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if user.Email == token {
|
||||||
|
potentialUsers = append(potentialUsers, user)
|
||||||
|
}
|
||||||
|
if user.Name == token {
|
||||||
|
potentialUsers = append(potentialUsers, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(potentialUsers) == 0 {
|
||||||
|
return types.User{}, fmt.Errorf("user with token %q not found: %w", token, ErrorNoUserMatching)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(potentialUsers) > 1 {
|
||||||
|
return types.User{}, fmt.Errorf("multiple users with token %q found: %w", token, ErrorNoUserMatching)
|
||||||
|
}
|
||||||
|
|
||||||
|
return potentialUsers[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
// FilterNodesByACL returns the list of peers authorized to be accessed from a given node.
|
// FilterNodesByACL returns the list of peers authorized to be accessed from a given node.
|
||||||
func FilterNodesByACL(
|
func FilterNodesByACL(
|
||||||
node *types.Node,
|
node *types.Node,
|
||||||
|
@ -2735,6 +2735,12 @@ func TestReduceFilterRules(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_getTags(t *testing.T) {
|
func Test_getTags(t *testing.T) {
|
||||||
|
users := []types.User{
|
||||||
|
{
|
||||||
|
Model: gorm.Model{ID: 1},
|
||||||
|
Name: "joe",
|
||||||
|
},
|
||||||
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
aclPolicy *ACLPolicy
|
aclPolicy *ACLPolicy
|
||||||
node *types.Node
|
node *types.Node
|
||||||
@ -2754,9 +2760,7 @@ func Test_getTags(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
node: &types.Node{
|
node: &types.Node{
|
||||||
User: types.User{
|
User: users[0],
|
||||||
Name: "joe",
|
|
||||||
},
|
|
||||||
Hostinfo: &tailcfg.Hostinfo{
|
Hostinfo: &tailcfg.Hostinfo{
|
||||||
RequestTags: []string{"tag:valid"},
|
RequestTags: []string{"tag:valid"},
|
||||||
},
|
},
|
||||||
@ -2774,9 +2778,7 @@ func Test_getTags(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
node: &types.Node{
|
node: &types.Node{
|
||||||
User: types.User{
|
User: users[0],
|
||||||
Name: "joe",
|
|
||||||
},
|
|
||||||
Hostinfo: &tailcfg.Hostinfo{
|
Hostinfo: &tailcfg.Hostinfo{
|
||||||
RequestTags: []string{"tag:valid", "tag:invalid"},
|
RequestTags: []string{"tag:valid", "tag:invalid"},
|
||||||
},
|
},
|
||||||
@ -2794,9 +2796,7 @@ func Test_getTags(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
node: &types.Node{
|
node: &types.Node{
|
||||||
User: types.User{
|
User: users[0],
|
||||||
Name: "joe",
|
|
||||||
},
|
|
||||||
Hostinfo: &tailcfg.Hostinfo{
|
Hostinfo: &tailcfg.Hostinfo{
|
||||||
RequestTags: []string{
|
RequestTags: []string{
|
||||||
"tag:invalid",
|
"tag:invalid",
|
||||||
@ -2818,9 +2818,7 @@ func Test_getTags(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
node: &types.Node{
|
node: &types.Node{
|
||||||
User: types.User{
|
User: users[0],
|
||||||
Name: "joe",
|
|
||||||
},
|
|
||||||
Hostinfo: &tailcfg.Hostinfo{
|
Hostinfo: &tailcfg.Hostinfo{
|
||||||
RequestTags: []string{"tag:invalid", "very-invalid"},
|
RequestTags: []string{"tag:invalid", "very-invalid"},
|
||||||
},
|
},
|
||||||
@ -2834,9 +2832,7 @@ func Test_getTags(t *testing.T) {
|
|||||||
args: args{
|
args: args{
|
||||||
aclPolicy: &ACLPolicy{},
|
aclPolicy: &ACLPolicy{},
|
||||||
node: &types.Node{
|
node: &types.Node{
|
||||||
User: types.User{
|
User: users[0],
|
||||||
Name: "joe",
|
|
||||||
},
|
|
||||||
Hostinfo: &tailcfg.Hostinfo{
|
Hostinfo: &tailcfg.Hostinfo{
|
||||||
RequestTags: []string{"tag:invalid", "very-invalid"},
|
RequestTags: []string{"tag:invalid", "very-invalid"},
|
||||||
},
|
},
|
||||||
@ -2849,6 +2845,7 @@ func Test_getTags(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
gotValid, gotInvalid := test.args.aclPolicy.TagsOfNode(
|
gotValid, gotInvalid := test.args.aclPolicy.TagsOfNode(
|
||||||
|
users,
|
||||||
test.args.node,
|
test.args.node,
|
||||||
)
|
)
|
||||||
for _, valid := range gotValid {
|
for _, valid := range gotValid {
|
||||||
@ -3542,6 +3539,11 @@ func Test_getFilteredByACLPeers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSSHRules(t *testing.T) {
|
func TestSSHRules(t *testing.T) {
|
||||||
|
users := []types.User{
|
||||||
|
{
|
||||||
|
Name: "user1",
|
||||||
|
},
|
||||||
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
node types.Node
|
node types.Node
|
||||||
@ -3555,18 +3557,14 @@ func TestSSHRules(t *testing.T) {
|
|||||||
Hostname: "testnodes",
|
Hostname: "testnodes",
|
||||||
IPv4: iap("100.64.99.42"),
|
IPv4: iap("100.64.99.42"),
|
||||||
UserID: 0,
|
UserID: 0,
|
||||||
User: types.User{
|
User: users[0],
|
||||||
Name: "user1",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
peers: types.Nodes{
|
peers: types.Nodes{
|
||||||
&types.Node{
|
&types.Node{
|
||||||
Hostname: "testnodes2",
|
Hostname: "testnodes2",
|
||||||
IPv4: iap("100.64.0.1"),
|
IPv4: iap("100.64.0.1"),
|
||||||
UserID: 0,
|
UserID: 0,
|
||||||
User: types.User{
|
User: users[0],
|
||||||
Name: "user1",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pol: ACLPolicy{
|
pol: ACLPolicy{
|
||||||
@ -3679,18 +3677,14 @@ func TestSSHRules(t *testing.T) {
|
|||||||
Hostname: "testnodes",
|
Hostname: "testnodes",
|
||||||
IPv4: iap("100.64.0.1"),
|
IPv4: iap("100.64.0.1"),
|
||||||
UserID: 0,
|
UserID: 0,
|
||||||
User: types.User{
|
User: users[0],
|
||||||
Name: "user1",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
peers: types.Nodes{
|
peers: types.Nodes{
|
||||||
&types.Node{
|
&types.Node{
|
||||||
Hostname: "testnodes2",
|
Hostname: "testnodes2",
|
||||||
IPv4: iap("100.64.99.42"),
|
IPv4: iap("100.64.99.42"),
|
||||||
UserID: 0,
|
UserID: 0,
|
||||||
User: types.User{
|
User: users[0],
|
||||||
Name: "user1",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pol: ACLPolicy{
|
pol: ACLPolicy{
|
||||||
@ -3728,7 +3722,7 @@ func TestSSHRules(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
got, err := tt.pol.CompileSSHPolicy(&tt.node, []types.User{}, tt.peers)
|
got, err := tt.pol.CompileSSHPolicy(&tt.node, users, tt.peers)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
if diff := cmp.Diff(tt.want, got); diff != "" {
|
if diff := cmp.Diff(tt.want, got); diff != "" {
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/juanfont/headscale/hscontrol/types"
|
"github.com/juanfont/headscale/hscontrol/types"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
"go4.org/netipx"
|
"go4.org/netipx"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/util/deephash"
|
"tailscale.com/util/deephash"
|
||||||
@ -161,7 +162,8 @@ func (pm *PolicyManagerV1) Tags(node *types.Node) []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
tags, _ := pm.pol.TagsOfNode(node)
|
tags, invalid := pm.pol.TagsOfNode(pm.users, node)
|
||||||
|
log.Debug().Strs("authorised_tags", tags).Strs("unauthorised_tags", invalid).Uint64("node.id", node.ID.Uint64()).Msg("tags provided by policy")
|
||||||
return tags
|
return tags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,8 +119,8 @@ func TestACLHostsInNetMapTable(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, want: map[string]int{
|
}, want: map[string]int{
|
||||||
"user1": 3, // ns1 + ns2
|
"user1@test.no": 3, // ns1 + ns2
|
||||||
"user2": 3, // ns2 + ns1
|
"user2@test.no": 3, // ns2 + ns1
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Test that when we have two users, which cannot see
|
// Test that when we have two users, which cannot see
|
||||||
@ -145,8 +145,8 @@ func TestACLHostsInNetMapTable(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, want: map[string]int{
|
}, want: map[string]int{
|
||||||
"user1": 1,
|
"user1@test.no": 1,
|
||||||
"user2": 1,
|
"user2@test.no": 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Test that when we have two users, with ACLs and they
|
// Test that when we have two users, with ACLs and they
|
||||||
@ -181,8 +181,8 @@ func TestACLHostsInNetMapTable(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, want: map[string]int{
|
}, want: map[string]int{
|
||||||
"user1": 3,
|
"user1@test.no": 3,
|
||||||
"user2": 3,
|
"user2@test.no": 3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Test that when we have two users, that are isolated,
|
// Test that when we have two users, that are isolated,
|
||||||
@ -213,8 +213,8 @@ func TestACLHostsInNetMapTable(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, want: map[string]int{
|
}, want: map[string]int{
|
||||||
"user1": 3, // ns1 + ns2
|
"user1@test.no": 3, // ns1 + ns2
|
||||||
"user2": 3, // ns1 + ns2 (return path)
|
"user2@test.no": 3, // ns1 + ns2 (return path)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"very-large-destination-prefix-1372": {
|
"very-large-destination-prefix-1372": {
|
||||||
@ -241,8 +241,8 @@ func TestACLHostsInNetMapTable(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, want: map[string]int{
|
}, want: map[string]int{
|
||||||
"user1": 3, // ns1 + ns2
|
"user1@test.no": 3, // ns1 + ns2
|
||||||
"user2": 3, // ns1 + ns2 (return path)
|
"user2@test.no": 3, // ns1 + ns2 (return path)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ipv6-acls-1470": {
|
"ipv6-acls-1470": {
|
||||||
@ -259,8 +259,8 @@ func TestACLHostsInNetMapTable(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, want: map[string]int{
|
}, want: map[string]int{
|
||||||
"user1": 3, // ns1 + ns2
|
"user1@test.no": 3, // ns1 + ns2
|
||||||
"user2": 3, // ns2 + ns1
|
"user2@test.no": 3, // ns2 + ns1
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -282,7 +282,7 @@ func TestACLHostsInNetMapTable(t *testing.T) {
|
|||||||
allClients, err := scenario.ListTailscaleClients()
|
allClients, err := scenario.ListTailscaleClients()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = scenario.WaitForTailscaleSyncWithPeerCount(testCase.want["user1"])
|
err = scenario.WaitForTailscaleSyncWithPeerCount(testCase.want["user1@test.no"])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for _, client := range allClients {
|
for _, client := range allClients {
|
||||||
|
@ -130,8 +130,9 @@ func TestOIDCAuthenticationPingAll(t *testing.T) {
|
|||||||
|
|
||||||
want := []v1.User{
|
want := []v1.User{
|
||||||
{
|
{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
Name: "user1",
|
Name: "user1",
|
||||||
|
Email: "user1@test.no",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 2,
|
Id: 2,
|
||||||
@ -141,8 +142,9 @@ func TestOIDCAuthenticationPingAll(t *testing.T) {
|
|||||||
ProviderId: oidcConfig.Issuer + "/user1",
|
ProviderId: oidcConfig.Issuer + "/user1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 3,
|
Id: 3,
|
||||||
Name: "user2",
|
Name: "user2",
|
||||||
|
Email: "user2@test.no",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 4,
|
Id: 4,
|
||||||
@ -260,8 +262,9 @@ func TestOIDC024UserCreation(t *testing.T) {
|
|||||||
want: func(iss string) []v1.User {
|
want: func(iss string) []v1.User {
|
||||||
return []v1.User{
|
return []v1.User{
|
||||||
{
|
{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
Name: "user1",
|
Name: "user1",
|
||||||
|
Email: "user1@test.no",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 2,
|
Id: 2,
|
||||||
@ -271,8 +274,9 @@ func TestOIDC024UserCreation(t *testing.T) {
|
|||||||
ProviderId: iss + "/user1",
|
ProviderId: iss + "/user1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 3,
|
Id: 3,
|
||||||
Name: "user2",
|
Name: "user2",
|
||||||
|
Email: "user2@test.no",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 4,
|
Id: 4,
|
||||||
@ -295,8 +299,9 @@ func TestOIDC024UserCreation(t *testing.T) {
|
|||||||
want: func(iss string) []v1.User {
|
want: func(iss string) []v1.User {
|
||||||
return []v1.User{
|
return []v1.User{
|
||||||
{
|
{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
Name: "user1",
|
Name: "user1",
|
||||||
|
Email: "user1@test.no",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 2,
|
Id: 2,
|
||||||
@ -305,8 +310,9 @@ func TestOIDC024UserCreation(t *testing.T) {
|
|||||||
ProviderId: iss + "/user1",
|
ProviderId: iss + "/user1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 3,
|
Id: 3,
|
||||||
Name: "user2",
|
Name: "user2",
|
||||||
|
Email: "user2@test.no",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 4,
|
Id: 4,
|
||||||
@ -357,8 +363,9 @@ func TestOIDC024UserCreation(t *testing.T) {
|
|||||||
want: func(iss string) []v1.User {
|
want: func(iss string) []v1.User {
|
||||||
return []v1.User{
|
return []v1.User{
|
||||||
{
|
{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
Name: "user1",
|
Name: "user1",
|
||||||
|
Email: "user1@test.no",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 2,
|
Id: 2,
|
||||||
@ -367,8 +374,9 @@ func TestOIDC024UserCreation(t *testing.T) {
|
|||||||
ProviderId: iss + "/user1",
|
ProviderId: iss + "/user1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 3,
|
Id: 3,
|
||||||
Name: "user2",
|
Name: "user2",
|
||||||
|
Email: "user2@test.no",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 4,
|
Id: 4,
|
||||||
@ -421,8 +429,9 @@ func TestOIDC024UserCreation(t *testing.T) {
|
|||||||
want: func(iss string) []v1.User {
|
want: func(iss string) []v1.User {
|
||||||
return []v1.User{
|
return []v1.User{
|
||||||
{
|
{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
Name: "user1.headscale.net",
|
Name: "user1.headscale.net",
|
||||||
|
Email: "user1.headscale.net@test.no",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 2,
|
Id: 2,
|
||||||
@ -431,8 +440,9 @@ func TestOIDC024UserCreation(t *testing.T) {
|
|||||||
ProviderId: iss + "/user1",
|
ProviderId: iss + "/user1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 3,
|
Id: 3,
|
||||||
Name: "user2.headscale.net",
|
Name: "user2.headscale.net",
|
||||||
|
Email: "user2.headscale.net@test.no",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 4,
|
Id: 4,
|
||||||
|
@ -135,8 +135,9 @@ func TestUserCommand(t *testing.T) {
|
|||||||
slices.SortFunc(listByUsername, sortWithID)
|
slices.SortFunc(listByUsername, sortWithID)
|
||||||
want := []*v1.User{
|
want := []*v1.User{
|
||||||
{
|
{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
Name: "user1",
|
Name: "user1",
|
||||||
|
Email: "user1@test.no",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,8 +162,9 @@ func TestUserCommand(t *testing.T) {
|
|||||||
slices.SortFunc(listByID, sortWithID)
|
slices.SortFunc(listByID, sortWithID)
|
||||||
want = []*v1.User{
|
want = []*v1.User{
|
||||||
{
|
{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
Name: "user1",
|
Name: "user1",
|
||||||
|
Email: "user1@test.no",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,8 +201,9 @@ func TestUserCommand(t *testing.T) {
|
|||||||
slices.SortFunc(listAfterIDDelete, sortWithID)
|
slices.SortFunc(listAfterIDDelete, sortWithID)
|
||||||
want = []*v1.User{
|
want = []*v1.User{
|
||||||
{
|
{
|
||||||
Id: 2,
|
Id: 2,
|
||||||
Name: "newname",
|
Name: "newname",
|
||||||
|
Email: "user2@test.no",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -930,7 +933,23 @@ func TestNodeAdvertiseTagCommand(t *testing.T) {
|
|||||||
wantTag: false,
|
wantTag: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with-policy",
|
name: "with-policy-email",
|
||||||
|
policy: &policy.ACLPolicy{
|
||||||
|
ACLs: []policy.ACL{
|
||||||
|
{
|
||||||
|
Action: "accept",
|
||||||
|
Sources: []string{"*"},
|
||||||
|
Destinations: []string{"*:*"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TagOwners: map[string][]string{
|
||||||
|
"tag:test": {"user1@test.no"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantTag: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with-policy-username",
|
||||||
policy: &policy.ACLPolicy{
|
policy: &policy.ACLPolicy{
|
||||||
ACLs: []policy.ACL{
|
ACLs: []policy.ACL{
|
||||||
{
|
{
|
||||||
@ -945,13 +964,32 @@ func TestNodeAdvertiseTagCommand(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantTag: true,
|
wantTag: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "with-policy-groups",
|
||||||
|
policy: &policy.ACLPolicy{
|
||||||
|
Groups: policy.Groups{
|
||||||
|
"group:admins": []string{"user1"},
|
||||||
|
},
|
||||||
|
ACLs: []policy.ACL{
|
||||||
|
{
|
||||||
|
Action: "accept",
|
||||||
|
Sources: []string{"*"},
|
||||||
|
Destinations: []string{"*:*"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TagOwners: map[string][]string{
|
||||||
|
"tag:test": {"group:admins"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantTag: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
scenario, err := NewScenario(dockertestMaxWait())
|
scenario, err := NewScenario(dockertestMaxWait())
|
||||||
assertNoErr(t, err)
|
assertNoErr(t, err)
|
||||||
// defer scenario.ShutdownAssertNoPanics(t)
|
defer scenario.ShutdownAssertNoPanics(t)
|
||||||
|
|
||||||
spec := map[string]int{
|
spec := map[string]int{
|
||||||
"user1": 1,
|
"user1": 1,
|
||||||
|
@ -702,7 +702,7 @@ func (t *HeadscaleInContainer) WaitForRunning() error {
|
|||||||
func (t *HeadscaleInContainer) CreateUser(
|
func (t *HeadscaleInContainer) CreateUser(
|
||||||
user string,
|
user string,
|
||||||
) error {
|
) error {
|
||||||
command := []string{"headscale", "users", "create", user}
|
command := []string{"headscale", "users", "create", user, fmt.Sprintf("--email=%s@test.no", user)}
|
||||||
|
|
||||||
_, _, err := dockertestutil.ExecuteCommand(
|
_, _, err := dockertestutil.ExecuteCommand(
|
||||||
t.container,
|
t.container,
|
||||||
|
@ -69,9 +69,6 @@ func sshScenario(t *testing.T, policy *policy.ACLPolicy, clientsPerUser int) *Sc
|
|||||||
},
|
},
|
||||||
hsic.WithACLPolicy(policy),
|
hsic.WithACLPolicy(policy),
|
||||||
hsic.WithTestName("ssh"),
|
hsic.WithTestName("ssh"),
|
||||||
hsic.WithConfigEnv(map[string]string{
|
|
||||||
"HEADSCALE_EXPERIMENTAL_FEATURE_SSH": "1",
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
assertNoErr(t, err)
|
assertNoErr(t, err)
|
||||||
|
|
||||||
|
@ -15,7 +15,12 @@ message User {
|
|||||||
string profile_pic_url = 8;
|
string profile_pic_url = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
message CreateUserRequest { string name = 1; }
|
message CreateUserRequest {
|
||||||
|
string name = 1;
|
||||||
|
string display_name = 2;
|
||||||
|
string email = 3;
|
||||||
|
string picture_url = 4;
|
||||||
|
}
|
||||||
|
|
||||||
message CreateUserResponse { User user = 1; }
|
message CreateUserResponse { User user = 1; }
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user