diff --git a/api4/user_local.go b/api4/user_local.go index d753d0ab4f..68b36b66b4 100644 --- a/api4/user_local.go +++ b/api4/user_local.go @@ -6,11 +6,13 @@ package api4 import ( "net/http" + "github.com/mattermost/mattermost-server/v5/audit" "github.com/mattermost/mattermost-server/v5/model" ) func (api *API) InitUserLocal() { api.BaseRoutes.Users.Handle("", api.ApiLocal(getUsers)).Methods("GET") + api.BaseRoutes.Users.Handle("", api.ApiLocal(localPermanentDeleteAllUsers)).Methods("DELETE") api.BaseRoutes.Users.Handle("", api.ApiLocal(createUser)).Methods("POST") api.BaseRoutes.Users.Handle("/password/reset/send", api.ApiLocal(sendPasswordReset)).Methods("POST") api.BaseRoutes.Users.Handle("/ids", api.ApiLocal(getUsersByIds)).Methods("POST") @@ -29,6 +31,19 @@ func (api *API) InitUserLocal() { api.BaseRoutes.User.Handle("/tokens", api.ApiLocal(createUserAccessToken)).Methods("POST") } +func localPermanentDeleteAllUsers(c *Context, w http.ResponseWriter, r *http.Request) { + auditRec := c.MakeAuditRecord("localPermanentDeleteAllUsers", audit.Fail) + defer c.LogAuditRec(auditRec) + + if err := c.App.PermanentDeleteAllUsers(); err != nil { + c.Err = err + return + } + + auditRec.Success() + ReturnStatusOK(w) +} + func localGetUserByUsername(c *Context, w http.ResponseWriter, r *http.Request) { c.RequireUsername() if c.Err != nil { diff --git a/api4/user_test.go b/api4/user_test.go index 794e34afeb..092b684399 100644 --- a/api4/user_test.go +++ b/api4/user_test.go @@ -1898,6 +1898,69 @@ func TestDeleteUser(t *testing.T) { CheckNoError(t, resp) } +func TestPermanentDeleteAllUsers(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + + t.Run("The endpoint should not be available for neither normal nor sysadmin users", func(t *testing.T) { + _, resp := th.Client.PermanentDeleteAllUsers() + CheckNotFoundStatus(t, resp) + + _, resp = th.SystemAdminClient.PermanentDeleteAllUsers() + CheckNotFoundStatus(t, resp) + }) + + t.Run("The endpoint should permanently delete all users", func(t *testing.T) { + // Basic user creates a team and a channel + team, err := th.App.CreateTeamWithUser(&model.Team{ + DisplayName: "User Created Team", + Name: "user-created-team", + Email: "usercreatedteam@test.com", + Type: model.TEAM_OPEN, + }, th.BasicUser.Id) + require.Nil(t, err) + + channel, err := th.App.CreateChannelWithUser(&model.Channel{ + DisplayName: "User Created Channel", + Name: "user-created-channel", + Type: model.CHANNEL_OPEN, + TeamId: team.Id, + }, th.BasicUser.Id) + require.Nil(t, err) + + // Check that we have users and posts in the database + users, err := th.App.Srv().Store.User().GetAll() + require.Nil(t, err) + require.Greater(t, len(users), 0) + + postCount, err := th.App.Srv().Store.Post().AnalyticsPostCount("", false, false) + require.Nil(t, err) + require.Greater(t, postCount, int64(0)) + + // Delete all users and their posts + _, resp := th.LocalClient.PermanentDeleteAllUsers() + require.Nil(t, resp.Error) + + // Check that both user and post tables are empty + users, err = th.App.Srv().Store.User().GetAll() + require.Nil(t, err) + require.Len(t, users, 0) + + postCount, err = th.App.Srv().Store.Post().AnalyticsPostCount("", false, false) + require.Nil(t, err) + require.Equal(t, postCount, int64(0)) + + // Check that the channel and team created by the user were not deleted + rTeam, err := th.App.GetTeam(team.Id) + require.Nil(t, err) + require.NotNil(t, rTeam) + + rChannel, err := th.App.GetChannel(channel.Id) + require.Nil(t, err) + require.NotNil(t, rChannel) + }) +} + func TestUpdateUserRoles(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() diff --git a/model/client4.go b/model/client4.go index 17dea3dc53..448c9f9ab1 100644 --- a/model/client4.go +++ b/model/client4.go @@ -1211,6 +1211,16 @@ func (c *Client4) DeleteUser(userId string) (bool, *Response) { return CheckStatusOK(r), BuildResponse(r) } +// PermanentDeleteAll permanently deletes all users in the system. This is a local only endpoint +func (c *Client4) PermanentDeleteAllUsers() (bool, *Response) { + r, err := c.DoApiDelete(c.GetUsersRoute()) + if err != nil { + return false, BuildErrorResponse(r, err) + } + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) +} + // SendPasswordResetEmail will send a link for password resetting to a user with the // provided email. func (c *Client4) SendPasswordResetEmail(email string) (bool, *Response) {