2022-04-29 09:10:08 +01:00
|
|
|
package routing
|
|
|
|
|
|
|
|
import (
|
2023-01-19 20:02:32 +00:00
|
|
|
"context"
|
2022-08-12 12:00:07 +01:00
|
|
|
"encoding/json"
|
2023-04-28 16:46:01 +01:00
|
|
|
"errors"
|
2022-09-30 09:32:31 +01:00
|
|
|
"fmt"
|
2022-04-29 09:10:08 +01:00
|
|
|
"net/http"
|
2022-09-27 17:06:49 +01:00
|
|
|
"time"
|
2022-04-29 09:10:08 +01:00
|
|
|
|
|
|
|
"github.com/gorilla/mux"
|
2022-12-22 10:54:03 +00:00
|
|
|
"github.com/matrix-org/dendrite/internal"
|
2023-04-28 16:46:01 +01:00
|
|
|
"github.com/matrix-org/dendrite/internal/eventutil"
|
2022-09-30 09:32:31 +01:00
|
|
|
"github.com/matrix-org/gomatrixserverlib"
|
2023-04-19 15:50:33 +01:00
|
|
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
2022-09-30 09:32:31 +01:00
|
|
|
"github.com/matrix-org/util"
|
|
|
|
"github.com/nats-io/nats.go"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
|
2022-04-29 09:10:08 +01:00
|
|
|
"github.com/matrix-org/dendrite/internal/httputil"
|
|
|
|
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
|
2022-08-12 12:00:07 +01:00
|
|
|
"github.com/matrix-org/dendrite/setup/config"
|
2022-09-27 17:06:49 +01:00
|
|
|
"github.com/matrix-org/dendrite/setup/jetstream"
|
2023-02-20 13:58:03 +00:00
|
|
|
"github.com/matrix-org/dendrite/userapi/api"
|
2022-04-29 09:10:08 +01:00
|
|
|
)
|
|
|
|
|
2023-03-27 14:39:33 +01:00
|
|
|
func AdminEvacuateRoom(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse {
|
2022-04-29 09:10:08 +01:00
|
|
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
|
|
|
if err != nil {
|
|
|
|
return util.ErrorResponse(err)
|
|
|
|
}
|
2023-04-28 16:46:01 +01:00
|
|
|
|
|
|
|
affected, err := rsAPI.PerformAdminEvacuateRoom(req.Context(), vars["roomID"])
|
2023-05-17 01:33:27 +01:00
|
|
|
switch err.(type) {
|
2023-04-28 16:46:01 +01:00
|
|
|
case nil:
|
|
|
|
case eventutil.ErrRoomNoExists:
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusNotFound,
|
2023-05-09 23:46:49 +01:00
|
|
|
JSON: spec.NotFound(err.Error()),
|
2023-04-28 16:46:01 +01:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
logrus.WithError(err).WithField("roomID", vars["roomID"]).Error("Failed to evacuate room")
|
2022-08-11 15:29:33 +01:00
|
|
|
return util.ErrorResponse(err)
|
|
|
|
}
|
2022-04-29 09:10:08 +01:00
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 200,
|
|
|
|
JSON: map[string]interface{}{
|
2023-04-28 16:46:01 +01:00
|
|
|
"affected": affected,
|
2022-04-29 09:10:08 +01:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2022-06-29 15:29:39 +01:00
|
|
|
|
2023-04-28 16:46:01 +01:00
|
|
|
func AdminEvacuateUser(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse {
|
2022-06-29 15:29:39 +01:00
|
|
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
|
|
|
if err != nil {
|
|
|
|
return util.ErrorResponse(err)
|
|
|
|
}
|
2023-03-27 14:39:33 +01:00
|
|
|
|
2023-04-28 16:46:01 +01:00
|
|
|
affected, err := rsAPI.PerformAdminEvacuateUser(req.Context(), vars["userID"])
|
2022-08-12 12:00:07 +01:00
|
|
|
if err != nil {
|
2023-04-28 16:46:01 +01:00
|
|
|
logrus.WithError(err).WithField("userID", vars["userID"]).Error("Failed to evacuate user")
|
2022-08-12 12:00:07 +01:00
|
|
|
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
|
|
}
|
2023-04-28 16:46:01 +01:00
|
|
|
|
2022-06-29 15:29:39 +01:00
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 200,
|
|
|
|
JSON: map[string]interface{}{
|
2023-04-28 16:46:01 +01:00
|
|
|
"affected": affected,
|
2022-06-29 15:29:39 +01:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2022-08-12 12:00:07 +01:00
|
|
|
|
2023-04-28 16:46:01 +01:00
|
|
|
func AdminPurgeRoom(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse {
|
2023-01-19 20:02:32 +00:00
|
|
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
|
|
|
if err != nil {
|
|
|
|
return util.ErrorResponse(err)
|
|
|
|
}
|
2023-03-27 14:39:33 +01:00
|
|
|
|
2023-04-28 16:46:01 +01:00
|
|
|
if err = rsAPI.PerformAdminPurgeRoom(context.Background(), vars["roomID"]); err != nil {
|
2023-01-19 20:02:32 +00:00
|
|
|
return util.ErrorResponse(err)
|
|
|
|
}
|
2023-04-28 16:46:01 +01:00
|
|
|
|
2023-01-19 20:02:32 +00:00
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 200,
|
2023-04-28 16:46:01 +01:00
|
|
|
JSON: struct{}{},
|
2023-01-19 20:02:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-20 13:58:03 +00:00
|
|
|
func AdminResetPassword(req *http.Request, cfg *config.ClientAPI, device *api.Device, userAPI api.ClientUserAPI) util.JSONResponse {
|
2022-12-22 10:54:03 +00:00
|
|
|
if req.Body == nil {
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusBadRequest,
|
2023-05-09 23:46:49 +01:00
|
|
|
JSON: spec.Unknown("Missing request body"),
|
2022-12-22 10:54:03 +00:00
|
|
|
}
|
|
|
|
}
|
2022-08-12 12:00:07 +01:00
|
|
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
|
|
|
if err != nil {
|
|
|
|
return util.ErrorResponse(err)
|
|
|
|
}
|
2022-12-22 10:54:03 +00:00
|
|
|
var localpart string
|
|
|
|
userID := vars["userID"]
|
|
|
|
localpart, serverName, err := cfg.Matrix.SplitLocalID('@', userID)
|
|
|
|
if err != nil {
|
2022-08-12 12:00:07 +01:00
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusBadRequest,
|
2023-05-09 23:46:49 +01:00
|
|
|
JSON: spec.InvalidParam(err.Error()),
|
2022-12-22 10:54:03 +00:00
|
|
|
}
|
|
|
|
}
|
2023-02-20 13:58:03 +00:00
|
|
|
accAvailableResp := &api.QueryAccountAvailabilityResponse{}
|
|
|
|
if err = userAPI.QueryAccountAvailability(req.Context(), &api.QueryAccountAvailabilityRequest{
|
2022-12-22 10:54:03 +00:00
|
|
|
Localpart: localpart,
|
|
|
|
ServerName: serverName,
|
|
|
|
}, accAvailableResp); err != nil {
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusInternalServerError,
|
2023-05-17 01:33:27 +01:00
|
|
|
JSON: spec.InternalServerError{},
|
2022-08-12 12:00:07 +01:00
|
|
|
}
|
|
|
|
}
|
2022-12-22 10:54:03 +00:00
|
|
|
if accAvailableResp.Available {
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusNotFound,
|
2023-05-09 23:46:49 +01:00
|
|
|
JSON: spec.Unknown("User does not exist"),
|
2022-12-22 10:54:03 +00:00
|
|
|
}
|
2022-11-11 16:41:37 +00:00
|
|
|
}
|
2022-08-12 12:00:07 +01:00
|
|
|
request := struct {
|
2023-05-30 09:02:53 +01:00
|
|
|
Password string `json:"password"`
|
|
|
|
LogoutDevices bool `json:"logout_devices"`
|
2022-08-12 12:00:07 +01:00
|
|
|
}{}
|
2022-12-23 13:11:11 +00:00
|
|
|
if err = json.NewDecoder(req.Body).Decode(&request); err != nil {
|
2022-08-12 12:00:07 +01:00
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusBadRequest,
|
2023-05-09 23:46:49 +01:00
|
|
|
JSON: spec.Unknown("Failed to decode request body: " + err.Error()),
|
2022-08-12 12:00:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if request.Password == "" {
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusBadRequest,
|
2023-05-09 23:46:49 +01:00
|
|
|
JSON: spec.MissingParam("Expecting non-empty password."),
|
2022-08-12 12:00:07 +01:00
|
|
|
}
|
|
|
|
}
|
2022-12-22 10:54:03 +00:00
|
|
|
|
2022-12-23 13:11:11 +00:00
|
|
|
if err = internal.ValidatePassword(request.Password); err != nil {
|
|
|
|
return *internal.PasswordResponse(err)
|
2022-12-22 10:54:03 +00:00
|
|
|
}
|
|
|
|
|
2023-02-20 13:58:03 +00:00
|
|
|
updateReq := &api.PerformPasswordUpdateRequest{
|
2022-08-12 12:00:07 +01:00
|
|
|
Localpart: localpart,
|
2022-11-11 16:41:37 +00:00
|
|
|
ServerName: serverName,
|
2022-08-12 12:00:07 +01:00
|
|
|
Password: request.Password,
|
2023-05-30 09:02:53 +01:00
|
|
|
LogoutDevices: request.LogoutDevices,
|
2022-08-12 12:00:07 +01:00
|
|
|
}
|
2023-02-20 13:58:03 +00:00
|
|
|
updateRes := &api.PerformPasswordUpdateResponse{}
|
2022-08-12 12:00:07 +01:00
|
|
|
if err := userAPI.PerformPasswordUpdate(req.Context(), updateReq, updateRes); err != nil {
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusBadRequest,
|
2023-05-09 23:46:49 +01:00
|
|
|
JSON: spec.Unknown("Failed to perform password update: " + err.Error()),
|
2022-08-12 12:00:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusOK,
|
|
|
|
JSON: struct {
|
|
|
|
Updated bool `json:"password_updated"`
|
|
|
|
}{
|
|
|
|
Updated: updateRes.PasswordUpdated,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2022-09-27 17:06:49 +01:00
|
|
|
|
2023-02-20 13:58:03 +00:00
|
|
|
func AdminReindex(req *http.Request, cfg *config.ClientAPI, device *api.Device, natsClient *nats.Conn) util.JSONResponse {
|
2022-09-27 17:06:49 +01:00
|
|
|
_, err := natsClient.RequestMsg(nats.NewMsg(cfg.Matrix.JetStream.Prefixed(jetstream.InputFulltextReindex)), time.Second*10)
|
|
|
|
if err != nil {
|
|
|
|
logrus.WithError(err).Error("failed to publish nats message")
|
2023-05-17 01:33:27 +01:00
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusInternalServerError,
|
|
|
|
JSON: spec.InternalServerError{},
|
|
|
|
}
|
2022-09-27 17:06:49 +01:00
|
|
|
}
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusOK,
|
|
|
|
JSON: struct{}{},
|
|
|
|
}
|
|
|
|
}
|
2022-09-30 09:32:31 +01:00
|
|
|
|
|
|
|
func AdminMarkAsStale(req *http.Request, cfg *config.ClientAPI, keyAPI api.ClientKeyAPI) util.JSONResponse {
|
|
|
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
|
|
|
if err != nil {
|
|
|
|
return util.ErrorResponse(err)
|
|
|
|
}
|
|
|
|
userID := vars["userID"]
|
|
|
|
|
|
|
|
_, domain, err := gomatrixserverlib.SplitID('@', userID)
|
|
|
|
if err != nil {
|
|
|
|
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
|
|
|
}
|
2022-10-26 12:59:19 +01:00
|
|
|
if cfg.Matrix.IsLocalServerName(domain) {
|
2022-09-30 09:32:31 +01:00
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusBadRequest,
|
2023-05-09 23:46:49 +01:00
|
|
|
JSON: spec.InvalidParam("Can not mark local device list as stale"),
|
2022-09-30 09:32:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = keyAPI.PerformMarkAsStaleIfNeeded(req.Context(), &api.PerformMarkAsStaleRequest{
|
|
|
|
UserID: userID,
|
|
|
|
Domain: domain,
|
|
|
|
}, &struct{}{})
|
|
|
|
if err != nil {
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusInternalServerError,
|
2023-05-09 23:46:49 +01:00
|
|
|
JSON: spec.Unknown(fmt.Sprintf("Failed to mark device list as stale: %s", err)),
|
2022-09-30 09:32:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusOK,
|
|
|
|
JSON: struct{}{},
|
|
|
|
}
|
|
|
|
}
|
2022-10-31 09:13:28 +00:00
|
|
|
|
2023-04-28 16:46:01 +01:00
|
|
|
func AdminDownloadState(req *http.Request, device *api.Device, rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse {
|
2022-10-31 09:13:28 +00:00
|
|
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
|
|
|
if err != nil {
|
|
|
|
return util.ErrorResponse(err)
|
|
|
|
}
|
|
|
|
roomID, ok := vars["roomID"]
|
|
|
|
if !ok {
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusBadRequest,
|
2023-05-09 23:46:49 +01:00
|
|
|
JSON: spec.MissingParam("Expecting room ID."),
|
2022-10-31 09:13:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
serverName, ok := vars["serverName"]
|
|
|
|
if !ok {
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusBadRequest,
|
2023-05-09 23:46:49 +01:00
|
|
|
JSON: spec.MissingParam("Expecting remote server name."),
|
2022-10-31 09:13:28 +00:00
|
|
|
}
|
|
|
|
}
|
2023-04-28 16:46:01 +01:00
|
|
|
if err = rsAPI.PerformAdminDownloadState(req.Context(), roomID, device.UserID, spec.ServerName(serverName)); err != nil {
|
2023-05-17 01:33:27 +01:00
|
|
|
if errors.Is(err, eventutil.ErrRoomNoExists{}) {
|
2023-04-28 16:46:01 +01:00
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 200,
|
2023-05-17 01:33:27 +01:00
|
|
|
JSON: spec.NotFound(err.Error()),
|
2023-04-28 16:46:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
logrus.WithError(err).WithFields(logrus.Fields{
|
|
|
|
"userID": device.UserID,
|
|
|
|
"serverName": serverName,
|
|
|
|
"roomID": roomID,
|
|
|
|
}).Error("failed to download state")
|
|
|
|
return util.ErrorResponse(err)
|
2022-10-31 09:13:28 +00:00
|
|
|
}
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 200,
|
2023-04-28 16:46:01 +01:00
|
|
|
JSON: struct{}{},
|
2022-10-31 09:13:28 +00:00
|
|
|
}
|
|
|
|
}
|