mirror of
https://github.com/juanfont/headscale.git
synced 2026-09-30 11:59:39 +09:00
cli: accept a user name in preauthkeys create --user
Resolved through lookupUser like the users commands, so scripts skip the users list round trip. Digit-only values stay IDs.
This commit is contained in:
committed by
Kristoffer Dalby
parent
715c5a4a1c
commit
62f89ca25d
@@ -94,6 +94,7 @@ removed on this schedule:
|
||||
- Fix `headscale users destroy`/`rename` reporting "multiple users match query" when no user matches; an ambiguous match now lists the matching users [#3476](https://github.com/juanfont/headscale/pull/3476)
|
||||
- Deleting a user that still owns nodes now lists the nodes (ID and hostname) that must be deleted first [#3475](https://github.com/juanfont/headscale/pull/3475)
|
||||
- Headscale now requires Go 1.27 to build
|
||||
- `headscale preauthkeys create --user` accepts a user name as well as an ID
|
||||
- `derp.paths` files may be Tailscale JSON or HuJSON DERP maps as well as YAML
|
||||
- `dns.extra_records_path` files may be HuJSON or YAML as well as JSON
|
||||
- A `derp.paths` region set to `null` removes that region again, as documented
|
||||
|
||||
@@ -30,7 +30,7 @@ func init() {
|
||||
StringP("expiration", "e", DefaultPreAuthKeyExpiry, "Human-readable expiration of the key (e.g. 30m, 24h)")
|
||||
createPreAuthKeyCmd.Flags().
|
||||
StringSlice("tags", []string{}, "Tags to automatically assign to node")
|
||||
createPreAuthKeyCmd.PersistentFlags().Uint64P("user", "u", 0, "User identifier (ID)")
|
||||
createPreAuthKeyCmd.PersistentFlags().StringP("user", "u", "", "User ID, or name if not a number")
|
||||
expirePreAuthKeyCmd.PersistentFlags().Uint64P("id", "i", 0, "Authkey ID")
|
||||
deletePreAuthKeyCmd.PersistentFlags().Uint64P("id", "i", 0, "Authkey ID")
|
||||
}
|
||||
@@ -102,7 +102,7 @@ var createPreAuthKeyCmd = &cobra.Command{
|
||||
Short: "Creates a new preauthkey",
|
||||
Aliases: []string{"c", cmdNew},
|
||||
RunE: clientRunE(func(ctx context.Context, client *clientv1.ClientWithResponses, cmd *cobra.Command, args []string) error {
|
||||
user, _ := cmd.Flags().GetUint64("user")
|
||||
userArg, _ := cmd.Flags().GetString("user")
|
||||
reusable, _ := cmd.Flags().GetBool("reusable")
|
||||
ephemeral, _ := cmd.Flags().GetBool("ephemeral")
|
||||
tags, _ := cmd.Flags().GetStringSlice("tags")
|
||||
@@ -112,7 +112,10 @@ var createPreAuthKeyCmd = &cobra.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
userStr := strconv.FormatUint(user, util.Base10)
|
||||
userStr, err := userIDFromArg(ctx, client, userArg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
request := clientv1.CreatePreAuthKeyJSONRequestBody{
|
||||
User: &userStr,
|
||||
|
||||
@@ -67,6 +67,30 @@ func resolveSingleUser(
|
||||
return lookupUser(ctx, client, id, username)
|
||||
}
|
||||
|
||||
// userIDFromArg resolves a --user value: an ID, or a user name when it is not
|
||||
// a number, so a user whose name is all digits must be given by ID.
|
||||
func userIDFromArg(
|
||||
ctx context.Context,
|
||||
client *clientv1.ClientWithResponses,
|
||||
arg string,
|
||||
) (string, error) {
|
||||
if arg == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
_, err := strconv.ParseUint(arg, util.Base10, 64)
|
||||
if err == nil {
|
||||
return arg, nil
|
||||
}
|
||||
|
||||
id, _, err := lookupUser(ctx, client, 0, arg)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("--user %q: %w", arg, err)
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// lookupUser resolves exactly one user by ID and/or name (0 and "" are unset),
|
||||
// returning the identifier of the matched user and the user itself.
|
||||
func lookupUser(
|
||||
|
||||
@@ -184,3 +184,54 @@ func TestResolveSingleUser(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserIDFromArg(t *testing.T) {
|
||||
alice := clientv1.User{Id: "3", Name: "alice"}
|
||||
digits := clientv1.User{Id: "7", Name: "42"}
|
||||
aliceDup := clientv1.User{Id: "8", Name: "alice"}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
users []clientv1.User
|
||||
arg string
|
||||
wantID string
|
||||
wantErr bool
|
||||
}{
|
||||
{name: "unset stays unset", arg: "", wantID: ""},
|
||||
{name: "number is an ID", users: []clientv1.User{alice}, arg: "3", wantID: "3"},
|
||||
// Numbers never hit the name lookup, so user "42" needs its ID.
|
||||
{name: "digit-only name is an ID", users: []clientv1.User{digits}, arg: "42", wantID: "42"},
|
||||
{name: "name resolves to its ID", users: []clientv1.User{alice, digits}, arg: "alice", wantID: "3"},
|
||||
{name: "unknown name is an error", users: []clientv1.User{alice}, arg: "bob", wantErr: true},
|
||||
{name: "ambiguous name is an error", users: []clientv1.User{alice, aliceDup}, arg: "alice", wantErr: true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
server := filterUsersServer(t, tt.users)
|
||||
defer server.Close()
|
||||
|
||||
client, err := clientv1.NewClientWithResponses(server.URL)
|
||||
if err != nil {
|
||||
t.Fatalf("creating client: %v", err)
|
||||
}
|
||||
|
||||
id, err := userIDFromArg(context.Background(), client, tt.arg)
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Fatalf("userIDFromArg(%q) error = nil, want error", tt.arg)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("userIDFromArg(%q) error = %v", tt.arg, err)
|
||||
}
|
||||
|
||||
if id != tt.wantID {
|
||||
t.Errorf("userIDFromArg(%q) = %q, want %q", tt.arg, id, tt.wantID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,10 +105,10 @@ Its best suited for automation.
|
||||
headscale users create <USER>
|
||||
```
|
||||
|
||||
Use the `headscale user list` command to learn its `<USER_ID>` and create a new pre authenticated key for your user:
|
||||
Create a new pre authenticated key for your user, by name or by the `<USER_ID>` that `headscale user list` shows:
|
||||
|
||||
```console
|
||||
headscale preauthkeys create --user <USER_ID>
|
||||
headscale preauthkeys create --user <USER>
|
||||
```
|
||||
|
||||
The above prints a pre authenticated key with the default settings (can be used once and is valid for one hour). Use
|
||||
|
||||
@@ -133,14 +133,14 @@ headscale instance. By default, the key is valid for one hour and can only be us
|
||||
=== "Native"
|
||||
|
||||
```shell
|
||||
headscale preauthkeys create --user <USER_ID>
|
||||
headscale preauthkeys create --user <USER>
|
||||
```
|
||||
|
||||
=== "Container"
|
||||
|
||||
```shell
|
||||
docker exec -it headscale \
|
||||
headscale preauthkeys create --user <USER_ID>
|
||||
headscale preauthkeys create --user <USER>
|
||||
```
|
||||
|
||||
The command returns the preauthkey on success which is used to connect a node to the headscale instance via the
|
||||
|
||||
Reference in New Issue
Block a user