mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat(cli): add coder users delete command (#10115)
This commit is contained in:
+1
@@ -11,6 +11,7 @@ SUBCOMMANDS:
|
||||
activate Update a user's status to 'active'. Active users can fully
|
||||
interact with the platform
|
||||
create
|
||||
delete Delete a user by username or user_id.
|
||||
list
|
||||
show Show a single user. Use 'me' to indicate the currently
|
||||
authenticated user.
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
coder v0.0.0-devel
|
||||
|
||||
USAGE:
|
||||
coder users delete <username|user_id>
|
||||
|
||||
Delete a user by username or user_id.
|
||||
|
||||
Aliases: rm
|
||||
|
||||
———
|
||||
Run `coder --help` for a list of global options.
|
||||
@@ -6,8 +6,6 @@ USAGE:
|
||||
Update a user's status to 'suspended'. A suspended user cannot log into the
|
||||
platform
|
||||
|
||||
Aliases: rm, delete
|
||||
|
||||
$ coder users suspend example_user
|
||||
|
||||
OPTIONS:
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
package cli_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/cli/clitest"
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/cryptorand"
|
||||
"github.com/coder/coder/v2/pty/ptytest"
|
||||
)
|
||||
|
||||
func TestUserDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("Username", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
client := coderdtest.New(t, nil)
|
||||
aUser := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
pw, err := cryptorand.String(16)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = client.CreateUser(ctx, codersdk.CreateUserRequest{
|
||||
Email: "colin5@coder.com",
|
||||
Username: "coolin",
|
||||
Password: pw,
|
||||
UserLoginType: codersdk.LoginTypePassword,
|
||||
OrganizationID: aUser.OrganizationID,
|
||||
DisableLogin: false,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
inv, root := clitest.New(t, "users", "delete", "coolin")
|
||||
clitest.SetupConfig(t, client, root)
|
||||
pty := ptytest.New(t).Attach(inv)
|
||||
errC := make(chan error)
|
||||
go func() {
|
||||
errC <- inv.Run()
|
||||
}()
|
||||
require.NoError(t, <-errC)
|
||||
pty.ExpectMatch("coolin")
|
||||
})
|
||||
|
||||
t.Run("UserID", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
client := coderdtest.New(t, nil)
|
||||
aUser := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
pw, err := cryptorand.String(16)
|
||||
require.NoError(t, err)
|
||||
|
||||
user, err := client.CreateUser(ctx, codersdk.CreateUserRequest{
|
||||
Email: "colin5@coder.com",
|
||||
Username: "coolin",
|
||||
Password: pw,
|
||||
UserLoginType: codersdk.LoginTypePassword,
|
||||
OrganizationID: aUser.OrganizationID,
|
||||
DisableLogin: false,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
inv, root := clitest.New(t, "users", "delete", user.ID.String())
|
||||
clitest.SetupConfig(t, client, root)
|
||||
pty := ptytest.New(t).Attach(inv)
|
||||
errC := make(chan error)
|
||||
go func() {
|
||||
errC <- inv.Run()
|
||||
}()
|
||||
require.NoError(t, <-errC)
|
||||
pty.ExpectMatch("coolin")
|
||||
})
|
||||
|
||||
t.Run("UserID", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
client := coderdtest.New(t, nil)
|
||||
aUser := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
pw, err := cryptorand.String(16)
|
||||
require.NoError(t, err)
|
||||
|
||||
user, err := client.CreateUser(ctx, codersdk.CreateUserRequest{
|
||||
Email: "colin5@coder.com",
|
||||
Username: "coolin",
|
||||
Password: pw,
|
||||
UserLoginType: codersdk.LoginTypePassword,
|
||||
OrganizationID: aUser.OrganizationID,
|
||||
DisableLogin: false,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
inv, root := clitest.New(t, "users", "delete", user.ID.String())
|
||||
clitest.SetupConfig(t, client, root)
|
||||
pty := ptytest.New(t).Attach(inv)
|
||||
errC := make(chan error)
|
||||
go func() {
|
||||
errC <- inv.Run()
|
||||
}()
|
||||
require.NoError(t, <-errC)
|
||||
pty.ExpectMatch("coolin")
|
||||
})
|
||||
|
||||
// TODO: reenable this test case. Fetching users without perms returns a
|
||||
// "user "testuser@coder.com" must be a member of at least one organization"
|
||||
// error.
|
||||
// t.Run("NoPerms", func(t *testing.T) {
|
||||
// t.Parallel()
|
||||
// ctx := context.Background()
|
||||
// client := coderdtest.New(t, nil)
|
||||
// aUser := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
// pw, err := cryptorand.String(16)
|
||||
// require.NoError(t, err)
|
||||
|
||||
// fmt.Println(aUser.OrganizationID)
|
||||
// toDelete, err := client.CreateUser(ctx, codersdk.CreateUserRequest{
|
||||
// Email: "colin5@coder.com",
|
||||
// Username: "coolin",
|
||||
// Password: pw,
|
||||
// UserLoginType: codersdk.LoginTypePassword,
|
||||
// OrganizationID: aUser.OrganizationID,
|
||||
// DisableLogin: false,
|
||||
// })
|
||||
// require.NoError(t, err)
|
||||
|
||||
// uClient, _ := coderdtest.CreateAnotherUser(t, client, aUser.OrganizationID)
|
||||
// _ = uClient
|
||||
// _ = toDelete
|
||||
|
||||
// inv, root := clitest.New(t, "users", "delete", "coolin")
|
||||
// clitest.SetupConfig(t, uClient, root)
|
||||
// require.ErrorContains(t, inv.Run(), "...")
|
||||
// })
|
||||
|
||||
t.Run("DeleteSelf", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
client := coderdtest.New(t, nil)
|
||||
aUser := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
pw, err := cryptorand.String(16)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = client.CreateUser(ctx, codersdk.CreateUserRequest{
|
||||
Email: "colin5@coder.com",
|
||||
Username: "coolin",
|
||||
Password: pw,
|
||||
UserLoginType: codersdk.LoginTypePassword,
|
||||
OrganizationID: aUser.OrganizationID,
|
||||
DisableLogin: false,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
coderdtest.CreateAnotherUser(t, client, aUser.OrganizationID)
|
||||
|
||||
inv, root := clitest.New(t, "users", "delete", "me")
|
||||
clitest.SetupConfig(t, client, root)
|
||||
require.ErrorContains(t, inv.Run(), "You cannot delete yourself!")
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/v2/cli/clibase"
|
||||
"github.com/coder/coder/v2/cli/cliui"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/pretty"
|
||||
)
|
||||
|
||||
func (r *RootCmd) userDelete() *clibase.Cmd {
|
||||
client := new(codersdk.Client)
|
||||
cmd := &clibase.Cmd{
|
||||
Use: "delete <username|user_id>",
|
||||
Short: "Delete a user by username or user_id.",
|
||||
Middleware: clibase.Chain(
|
||||
clibase.RequireNArgs(1),
|
||||
r.InitClient(client),
|
||||
),
|
||||
Handler: func(inv *clibase.Invocation) error {
|
||||
ctx := inv.Context()
|
||||
user, err := client.User(ctx, inv.Args[0])
|
||||
if err != nil {
|
||||
return xerrors.Errorf("fetch user: %w", err)
|
||||
}
|
||||
|
||||
err = client.DeleteUser(ctx, user.ID)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("delete user: %w", err)
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintln(inv.Stderr,
|
||||
"Successfully deleted "+pretty.Sprint(cliui.DefaultStyles.Keyword, user.Username)+".",
|
||||
)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
@@ -17,6 +17,7 @@ func (r *RootCmd) users() *clibase.Cmd {
|
||||
r.userCreate(),
|
||||
r.userList(),
|
||||
r.userSingle(),
|
||||
r.userDelete(),
|
||||
r.createUserStatusCommand(codersdk.UserStatusActive),
|
||||
r.createUserStatusCommand(codersdk.UserStatusSuspended),
|
||||
},
|
||||
|
||||
@@ -28,7 +28,6 @@ func (r *RootCmd) createUserStatusCommand(sdkStatus codersdk.UserStatus) *clibas
|
||||
case codersdk.UserStatusSuspended:
|
||||
verb = "suspend"
|
||||
pastVerb = "suspended"
|
||||
aliases = []string{"rm", "delete"}
|
||||
short = "Update a user's status to 'suspended'. A suspended user cannot log into the platform"
|
||||
default:
|
||||
panic(fmt.Sprintf("%s is not supported", sdkStatus))
|
||||
|
||||
Reference in New Issue
Block a user