mirror of
https://github.com/coder/coder.git
synced 2025-07-13 21:36:50 +00:00
feat: Add suspend/active user to cli (#1422)
* feat: Add suspend/active user to cli * UserID is now a string and allows for username too
This commit is contained in:
@ -78,7 +78,7 @@ func TestExecutorAutostartTemplateUpdated(t *testing.T) {
|
||||
require.Empty(t, workspace.AutostartSchedule)
|
||||
|
||||
// Given: the workspace template has been updated
|
||||
orgs, err := client.OrganizationsByUser(ctx, workspace.OwnerID)
|
||||
orgs, err := client.OrganizationsByUser(ctx, workspace.OwnerID.String())
|
||||
require.NoError(t, err)
|
||||
require.Len(t, orgs, 1)
|
||||
|
||||
|
@ -239,7 +239,10 @@ func New(options *Options) (http.Handler, func()) {
|
||||
r.Use(httpmw.ExtractUserParam(options.Database))
|
||||
r.Get("/", api.userByName)
|
||||
r.Put("/profile", api.putUserProfile)
|
||||
r.Put("/suspend", api.putUserSuspend)
|
||||
r.Route("/status", func(r chi.Router) {
|
||||
r.Put("/suspend", api.putUserStatus(database.UserStatusSuspended))
|
||||
r.Put("/active", api.putUserStatus(database.UserStatusActive))
|
||||
})
|
||||
r.Route("/password", func(r chi.Router) {
|
||||
r.Use(httpmw.WithRBACObject(rbac.ResourceUserPasswordRole))
|
||||
r.Put("/", authorize(api.putUserPassword, rbac.ActionUpdate))
|
||||
|
@ -254,7 +254,7 @@ func CreateAnotherUser(t *testing.T, client *codersdk.Client, organizationID uui
|
||||
}
|
||||
// TODO: @emyrk switch "other" to "client" when we support updating other
|
||||
// users.
|
||||
_, err := other.UpdateUserRoles(context.Background(), user.ID, codersdk.UpdateRoles{Roles: siteRoles})
|
||||
_, err := other.UpdateUserRoles(context.Background(), user.ID.String(), codersdk.UpdateRoles{Roles: siteRoles})
|
||||
require.NoError(t, err, "update site roles")
|
||||
|
||||
// Update org roles
|
||||
@ -262,7 +262,7 @@ func CreateAnotherUser(t *testing.T, client *codersdk.Client, organizationID uui
|
||||
organizationID, err := uuid.Parse(orgID)
|
||||
require.NoError(t, err, fmt.Sprintf("parse org id %q", orgID))
|
||||
// TODO: @Emyrk add the member to the organization if they do not already belong.
|
||||
_, err = other.UpdateOrganizationMemberRoles(context.Background(), organizationID, user.ID,
|
||||
_, err = other.UpdateOrganizationMemberRoles(context.Background(), organizationID, user.ID.String(),
|
||||
codersdk.UpdateRoles{Roles: append(roles, rbac.RoleOrgMember(organizationID))})
|
||||
require.NoError(t, err, "update org membership roles")
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ func TestGitSSHKey(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
client := coderdtest.New(t, nil)
|
||||
res := coderdtest.CreateFirstUser(t, client)
|
||||
key, err := client.GitSSHKey(ctx, res.UserID)
|
||||
key, err := client.GitSSHKey(ctx, res.UserID.String())
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, key.PublicKey)
|
||||
})
|
||||
@ -32,7 +32,7 @@ func TestGitSSHKey(t *testing.T) {
|
||||
SSHKeygenAlgorithm: gitsshkey.AlgorithmEd25519,
|
||||
})
|
||||
res := coderdtest.CreateFirstUser(t, client)
|
||||
key, err := client.GitSSHKey(ctx, res.UserID)
|
||||
key, err := client.GitSSHKey(ctx, res.UserID.String())
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, key.PublicKey)
|
||||
})
|
||||
@ -43,7 +43,7 @@ func TestGitSSHKey(t *testing.T) {
|
||||
SSHKeygenAlgorithm: gitsshkey.AlgorithmECDSA,
|
||||
})
|
||||
res := coderdtest.CreateFirstUser(t, client)
|
||||
key, err := client.GitSSHKey(ctx, res.UserID)
|
||||
key, err := client.GitSSHKey(ctx, res.UserID.String())
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, key.PublicKey)
|
||||
})
|
||||
@ -54,7 +54,7 @@ func TestGitSSHKey(t *testing.T) {
|
||||
SSHKeygenAlgorithm: gitsshkey.AlgorithmRSA4096,
|
||||
})
|
||||
res := coderdtest.CreateFirstUser(t, client)
|
||||
key, err := client.GitSSHKey(ctx, res.UserID)
|
||||
key, err := client.GitSSHKey(ctx, res.UserID.String())
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, key.PublicKey)
|
||||
})
|
||||
@ -65,10 +65,10 @@ func TestGitSSHKey(t *testing.T) {
|
||||
SSHKeygenAlgorithm: gitsshkey.AlgorithmEd25519,
|
||||
})
|
||||
res := coderdtest.CreateFirstUser(t, client)
|
||||
key1, err := client.GitSSHKey(ctx, res.UserID)
|
||||
key1, err := client.GitSSHKey(ctx, res.UserID.String())
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, key1.PublicKey)
|
||||
key2, err := client.RegenerateGitSSHKey(ctx, res.UserID)
|
||||
key2, err := client.RegenerateGitSSHKey(ctx, res.UserID.String())
|
||||
require.NoError(t, err)
|
||||
require.GreaterOrEqual(t, key2.UpdatedAt, key1.UpdatedAt)
|
||||
require.NotEmpty(t, key2.PublicKey)
|
||||
|
@ -107,7 +107,7 @@ func TestListRoles(t *testing.T) {
|
||||
member := coderdtest.CreateAnotherUser(t, client, admin.OrganizationID)
|
||||
orgAdmin := coderdtest.CreateAnotherUser(t, client, admin.OrganizationID, rbac.RoleOrgAdmin(admin.OrganizationID))
|
||||
|
||||
otherOrg, err := client.CreateOrganization(ctx, admin.UserID, codersdk.CreateOrganizationRequest{
|
||||
otherOrg, err := client.CreateOrganization(ctx, admin.UserID.String(), codersdk.CreateOrganizationRequest{
|
||||
Name: "other",
|
||||
})
|
||||
require.NoError(t, err, "create org")
|
||||
|
@ -303,31 +303,40 @@ func (api *api) putUserProfile(rw http.ResponseWriter, r *http.Request) {
|
||||
httpapi.Write(rw, http.StatusOK, convertUser(updatedUserProfile, organizationIDs))
|
||||
}
|
||||
|
||||
func (api *api) putUserSuspend(rw http.ResponseWriter, r *http.Request) {
|
||||
user := httpmw.UserParam(r)
|
||||
func (api *api) putUserStatus(status database.UserStatus) func(rw http.ResponseWriter, r *http.Request) {
|
||||
return func(rw http.ResponseWriter, r *http.Request) {
|
||||
user := httpmw.UserParam(r)
|
||||
apiKey := httpmw.APIKey(r)
|
||||
if status == database.UserStatusSuspended && user.ID == apiKey.UserID {
|
||||
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
|
||||
Message: "You cannot suspend yourself",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
suspendedUser, err := api.Database.UpdateUserStatus(r.Context(), database.UpdateUserStatusParams{
|
||||
ID: user.ID,
|
||||
Status: database.UserStatusSuspended,
|
||||
UpdatedAt: database.Now(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("put user suspended: %s", err.Error()),
|
||||
suspendedUser, err := api.Database.UpdateUserStatus(r.Context(), database.UpdateUserStatusParams{
|
||||
ID: user.ID,
|
||||
Status: status,
|
||||
UpdatedAt: database.Now(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
organizations, err := userOrganizationIDs(r.Context(), api, user)
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("get organization IDs: %s", err.Error()),
|
||||
})
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("put user suspended: %s", err.Error()),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
httpapi.Write(rw, http.StatusOK, convertUser(suspendedUser, organizations))
|
||||
organizations, err := userOrganizationIDs(r.Context(), api, user)
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: fmt.Sprintf("get organization IDs: %s", err.Error()),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
httpapi.Write(rw, http.StatusOK, convertUser(suspendedUser, organizations))
|
||||
}
|
||||
}
|
||||
|
||||
func (api *api) putUserPassword(rw http.ResponseWriter, r *http.Request) {
|
||||
|
@ -209,7 +209,7 @@ func TestUpdateUserProfile(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, nil)
|
||||
coderdtest.CreateFirstUser(t, client)
|
||||
_, err := client.UpdateUserProfile(context.Background(), uuid.New(), codersdk.UpdateUserProfileRequest{
|
||||
_, err := client.UpdateUserProfile(context.Background(), uuid.New().String(), codersdk.UpdateUserProfileRequest{
|
||||
Username: "newusername",
|
||||
Email: "newemail@coder.com",
|
||||
})
|
||||
@ -295,7 +295,7 @@ func TestUpdateUserPassword(t *testing.T) {
|
||||
client := coderdtest.New(t, nil)
|
||||
admin := coderdtest.CreateFirstUser(t, client)
|
||||
member := coderdtest.CreateAnotherUser(t, client, admin.OrganizationID)
|
||||
err := member.UpdateUserPassword(context.Background(), admin.UserID, codersdk.UpdateUserPasswordRequest{
|
||||
err := member.UpdateUserPassword(context.Background(), admin.UserID.String(), codersdk.UpdateUserPasswordRequest{
|
||||
Password: "newpassword",
|
||||
})
|
||||
require.Error(t, err, "member should not be able to update admin password")
|
||||
@ -312,7 +312,7 @@ func TestUpdateUserPassword(t *testing.T) {
|
||||
OrganizationID: admin.OrganizationID,
|
||||
})
|
||||
require.NoError(t, err, "create member")
|
||||
err = client.UpdateUserPassword(context.Background(), member.ID, codersdk.UpdateUserPasswordRequest{
|
||||
err = client.UpdateUserPassword(context.Background(), member.ID.String(), codersdk.UpdateUserPasswordRequest{
|
||||
Password: "newpassword",
|
||||
})
|
||||
require.NoError(t, err, "admin should be able to update member password")
|
||||
@ -339,7 +339,7 @@ func TestGrantRoles(t *testing.T) {
|
||||
})
|
||||
require.Error(t, err, "org role in site")
|
||||
|
||||
_, err = admin.UpdateUserRoles(ctx, uuid.New(), codersdk.UpdateRoles{
|
||||
_, err = admin.UpdateUserRoles(ctx, uuid.New().String(), codersdk.UpdateRoles{
|
||||
Roles: []string{rbac.RoleOrgMember(first.OrganizationID)},
|
||||
})
|
||||
require.Error(t, err, "user does not exist")
|
||||
@ -354,12 +354,12 @@ func TestGrantRoles(t *testing.T) {
|
||||
})
|
||||
require.Error(t, err, "role in org without membership")
|
||||
|
||||
_, err = member.UpdateUserRoles(ctx, first.UserID, codersdk.UpdateRoles{
|
||||
_, err = member.UpdateUserRoles(ctx, first.UserID.String(), codersdk.UpdateRoles{
|
||||
Roles: []string{rbac.RoleMember()},
|
||||
})
|
||||
require.Error(t, err, "member cannot change other's roles")
|
||||
|
||||
_, err = member.UpdateOrganizationMemberRoles(ctx, first.OrganizationID, first.UserID, codersdk.UpdateRoles{
|
||||
_, err = member.UpdateOrganizationMemberRoles(ctx, first.OrganizationID, first.UserID.String(), codersdk.UpdateRoles{
|
||||
Roles: []string{rbac.RoleMember()},
|
||||
})
|
||||
require.Error(t, err, "member cannot change other's org roles")
|
||||
@ -452,7 +452,7 @@ func TestPutUserSuspend(t *testing.T) {
|
||||
Password: "password",
|
||||
OrganizationID: me.OrganizationID,
|
||||
})
|
||||
user, err := client.SuspendUser(context.Background(), user.ID)
|
||||
user, err := client.UpdateUserStatus(context.Background(), user.Username, codersdk.UserStatusSuspended)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, user.Status, codersdk.UserStatusSuspended)
|
||||
})
|
||||
@ -462,10 +462,9 @@ func TestPutUserSuspend(t *testing.T) {
|
||||
client := coderdtest.New(t, nil)
|
||||
coderdtest.CreateFirstUser(t, client)
|
||||
client.User(context.Background(), codersdk.Me)
|
||||
suspendedUser, err := client.SuspendUser(context.Background(), codersdk.Me)
|
||||
_, err := client.UpdateUserStatus(context.Background(), codersdk.Me, codersdk.UserStatusSuspended)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, suspendedUser.Status, codersdk.UserStatusSuspended)
|
||||
require.ErrorContains(t, err, "suspend yourself", "cannot suspend yourself")
|
||||
})
|
||||
}
|
||||
|
||||
@ -490,7 +489,7 @@ func TestGetUser(t *testing.T) {
|
||||
client := coderdtest.New(t, nil)
|
||||
firstUser := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
user, err := client.User(context.Background(), firstUser.UserID)
|
||||
user, err := client.User(context.Background(), firstUser.UserID.String())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, firstUser.UserID, user.ID)
|
||||
require.Equal(t, firstUser.OrganizationID, user.OrganizationIDs[0])
|
||||
@ -501,10 +500,10 @@ func TestGetUser(t *testing.T) {
|
||||
|
||||
client := coderdtest.New(t, nil)
|
||||
firstUser := coderdtest.CreateFirstUser(t, client)
|
||||
exp, err := client.User(context.Background(), firstUser.UserID)
|
||||
exp, err := client.User(context.Background(), firstUser.UserID.String())
|
||||
require.NoError(t, err)
|
||||
|
||||
user, err := client.UserByUsername(context.Background(), exp.Username)
|
||||
user, err := client.User(context.Background(), exp.Username)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, exp, user)
|
||||
})
|
||||
@ -530,9 +529,15 @@ func TestGetUsers(t *testing.T) {
|
||||
})
|
||||
t.Run("ActiveUsers", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
active := make([]codersdk.User, 0)
|
||||
client := coderdtest.New(t, nil)
|
||||
first := coderdtest.CreateFirstUser(t, client)
|
||||
active := make([]codersdk.User, 0)
|
||||
|
||||
firstUser, err := client.User(context.Background(), first.UserID.String())
|
||||
require.NoError(t, err, "")
|
||||
active = append(active, firstUser)
|
||||
|
||||
// Alice will be suspended
|
||||
alice, err := client.CreateUser(context.Background(), codersdk.CreateUserRequest{
|
||||
Email: "alice@email.com",
|
||||
Username: "alice",
|
||||
@ -540,7 +545,6 @@ func TestGetUsers(t *testing.T) {
|
||||
OrganizationID: first.OrganizationID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
active = append(active, alice)
|
||||
|
||||
bruno, err := client.CreateUser(context.Background(), codersdk.CreateUserRequest{
|
||||
Email: "bruno@email.com",
|
||||
@ -551,7 +555,7 @@ func TestGetUsers(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
active = append(active, bruno)
|
||||
|
||||
_, err = client.SuspendUser(context.Background(), first.UserID)
|
||||
_, err = client.UpdateUserStatus(context.Background(), alice.Username, codersdk.UserStatusSuspended)
|
||||
require.NoError(t, err)
|
||||
|
||||
users, err := client.Users(context.Background(), codersdk.UsersRequest{
|
||||
|
@ -297,7 +297,7 @@ func TestPostWorkspaceBuild(t *testing.T) {
|
||||
require.Equal(t, workspace.LatestBuild.ID.String(), build.BeforeID.String())
|
||||
coderdtest.AwaitWorkspaceBuildJob(t, client, build.ID)
|
||||
|
||||
workspaces, err := client.WorkspacesByOwner(context.Background(), user.OrganizationID, user.UserID)
|
||||
workspaces, err := client.WorkspacesByOwner(context.Background(), user.OrganizationID, user.UserID.String())
|
||||
require.NoError(t, err)
|
||||
require.Len(t, workspaces, 0)
|
||||
})
|
||||
|
Reference in New Issue
Block a user