package routing import ( "context" "encoding/json" "errors" "fmt" "net/http" "time" "github.com/gorilla/mux" "github.com/matrix-org/dendrite/internal" "github.com/matrix-org/dendrite/internal/eventutil" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib/spec" "github.com/matrix-org/util" "github.com/nats-io/nats.go" "github.com/sirupsen/logrus" "github.com/matrix-org/dendrite/internal/httputil" roomserverAPI "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/jetstream" "github.com/matrix-org/dendrite/userapi/api" ) func AdminEvacuateRoom(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) } affected, err := rsAPI.PerformAdminEvacuateRoom(req.Context(), vars["roomID"]) switch err { case nil: case eventutil.ErrRoomNoExists: return util.JSONResponse{ Code: http.StatusNotFound, JSON: spec.NotFound(err.Error()), } default: logrus.WithError(err).WithField("roomID", vars["roomID"]).Error("Failed to evacuate room") return util.ErrorResponse(err) } return util.JSONResponse{ Code: 200, JSON: map[string]interface{}{ "affected": affected, }, } } func AdminEvacuateUser(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) } affected, err := rsAPI.PerformAdminEvacuateUser(req.Context(), vars["userID"]) if err != nil { logrus.WithError(err).WithField("userID", vars["userID"]).Error("Failed to evacuate user") return util.MessageResponse(http.StatusBadRequest, err.Error()) } return util.JSONResponse{ Code: 200, JSON: map[string]interface{}{ "affected": affected, }, } } func AdminPurgeRoom(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse { vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) } if err = rsAPI.PerformAdminPurgeRoom(context.Background(), vars["roomID"]); err != nil { return util.ErrorResponse(err) } return util.JSONResponse{ Code: 200, JSON: struct{}{}, } } func AdminResetPassword(req *http.Request, cfg *config.ClientAPI, device *api.Device, userAPI api.ClientUserAPI) util.JSONResponse { if req.Body == nil { return util.JSONResponse{ Code: http.StatusBadRequest, JSON: spec.Unknown("Missing request body"), } } vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) if err != nil { return util.ErrorResponse(err) } var localpart string userID := vars["userID"] localpart, serverName, err := cfg.Matrix.SplitLocalID('@', userID) if err != nil { return util.JSONResponse{ Code: http.StatusBadRequest, JSON: spec.InvalidParam(err.Error()), } } accAvailableResp := &api.QueryAccountAvailabilityResponse{} if err = userAPI.QueryAccountAvailability(req.Context(), &api.QueryAccountAvailabilityRequest{ Localpart: localpart, ServerName: serverName, }, accAvailableResp); err != nil { return util.JSONResponse{ Code: http.StatusInternalServerError, JSON: spec.InternalServerError(), } } if accAvailableResp.Available { return util.JSONResponse{ Code: http.StatusNotFound, JSON: spec.Unknown("User does not exist"), } } request := struct { Password string `json:"password"` }{} if err = json.NewDecoder(req.Body).Decode(&request); err != nil { return util.JSONResponse{ Code: http.StatusBadRequest, JSON: spec.Unknown("Failed to decode request body: " + err.Error()), } } if request.Password == "" { return util.JSONResponse{ Code: http.StatusBadRequest, JSON: spec.MissingParam("Expecting non-empty password."), } } if err = internal.ValidatePassword(request.Password); err != nil { return *internal.PasswordResponse(err) } updateReq := &api.PerformPasswordUpdateRequest{ Localpart: localpart, ServerName: serverName, Password: request.Password, LogoutDevices: true, } updateRes := &api.PerformPasswordUpdateResponse{} if err := userAPI.PerformPasswordUpdate(req.Context(), updateReq, updateRes); err != nil { return util.JSONResponse{ Code: http.StatusBadRequest, JSON: spec.Unknown("Failed to perform password update: " + err.Error()), } } return util.JSONResponse{ Code: http.StatusOK, JSON: struct { Updated bool `json:"password_updated"` }{ Updated: updateRes.PasswordUpdated, }, } } func AdminReindex(req *http.Request, cfg *config.ClientAPI, device *api.Device, natsClient *nats.Conn) util.JSONResponse { _, 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") return spec.InternalServerError() } return util.JSONResponse{ Code: http.StatusOK, JSON: struct{}{}, } } 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()) } if cfg.Matrix.IsLocalServerName(domain) { return util.JSONResponse{ Code: http.StatusBadRequest, JSON: spec.InvalidParam("Can not mark local device list as stale"), } } err = keyAPI.PerformMarkAsStaleIfNeeded(req.Context(), &api.PerformMarkAsStaleRequest{ UserID: userID, Domain: domain, }, &struct{}{}) if err != nil { return util.JSONResponse{ Code: http.StatusInternalServerError, JSON: spec.Unknown(fmt.Sprintf("Failed to mark device list as stale: %s", err)), } } return util.JSONResponse{ Code: http.StatusOK, JSON: struct{}{}, } } func AdminDownloadState(req *http.Request, device *api.Device, rsAPI roomserverAPI.ClientRoomserverAPI) util.JSONResponse { 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, JSON: spec.MissingParam("Expecting room ID."), } } serverName, ok := vars["serverName"] if !ok { return util.JSONResponse{ Code: http.StatusBadRequest, JSON: spec.MissingParam("Expecting remote server name."), } } if err = rsAPI.PerformAdminDownloadState(req.Context(), roomID, device.UserID, spec.ServerName(serverName)); err != nil { if errors.Is(err, eventutil.ErrRoomNoExists) { return util.JSONResponse{ Code: 200, JSON: spec.NotFound(eventutil.ErrRoomNoExists.Error()), } } logrus.WithError(err).WithFields(logrus.Fields{ "userID": device.UserID, "serverName": serverName, "roomID": roomID, }).Error("failed to download state") return util.ErrorResponse(err) } return util.JSONResponse{ Code: 200, JSON: struct{}{}, } }