aboutsummaryrefslogtreecommitdiff
path: root/roomserver
diff options
context:
space:
mode:
authorNeil Alexander <neilalexander@users.noreply.github.com>2022-04-28 16:02:30 +0100
committerGitHub <noreply@github.com>2022-04-28 16:02:30 +0100
commitc6ea2c9ff26ca6ae4c799db08a3f72c6b4d99256 (patch)
treebf7c7f0a18b6a42089500cbf76e04fa3109a9254 /roomserver
parent21ee5b36a41f2cb3960f63ef6f19106d36312aae (diff)
Add `/_dendrite/admin/evacuateRoom/{roomID}` (#2401)
* Add new endpoint to allow admins to evacuate the local server from the room * Guard endpoint * Use right prefix * Auth API * More useful return error rather than a panic * More useful return value again * Update the path * Try using inputer instead * oh provide the config * Try that again * Return affected user IDs * Don't create so many forward extremities * Add missing `Path` to name Co-authored-by: Till <2353100+S7evinK@users.noreply.github.com>
Diffstat (limited to 'roomserver')
-rw-r--r--roomserver/api/api.go6
-rw-r--r--roomserver/api/api_trace.go9
-rw-r--r--roomserver/api/perform.go9
-rw-r--r--roomserver/internal/api.go7
-rw-r--r--roomserver/internal/perform/perform_admin.go162
-rw-r--r--roomserver/inthttp/client.go38
-rw-r--r--roomserver/inthttp/server.go11
7 files changed, 232 insertions, 10 deletions
diff --git a/roomserver/api/api.go b/roomserver/api/api.go
index fb77423f..f0ca8a61 100644
--- a/roomserver/api/api.go
+++ b/roomserver/api/api.go
@@ -66,6 +66,12 @@ type RoomserverInternalAPI interface {
res *PerformInboundPeekResponse,
) error
+ PerformAdminEvacuateRoom(
+ ctx context.Context,
+ req *PerformAdminEvacuateRoomRequest,
+ res *PerformAdminEvacuateRoomResponse,
+ )
+
QueryPublishedRooms(
ctx context.Context,
req *QueryPublishedRoomsRequest,
diff --git a/roomserver/api/api_trace.go b/roomserver/api/api_trace.go
index ec7211ef..61c06e88 100644
--- a/roomserver/api/api_trace.go
+++ b/roomserver/api/api_trace.go
@@ -104,6 +104,15 @@ func (t *RoomserverInternalAPITrace) PerformPublish(
util.GetLogger(ctx).Infof("PerformPublish req=%+v res=%+v", js(req), js(res))
}
+func (t *RoomserverInternalAPITrace) PerformAdminEvacuateRoom(
+ ctx context.Context,
+ req *PerformAdminEvacuateRoomRequest,
+ res *PerformAdminEvacuateRoomResponse,
+) {
+ t.Impl.PerformAdminEvacuateRoom(ctx, req, res)
+ util.GetLogger(ctx).Infof("PerformAdminEvacuateRoom req=%+v res=%+v", js(req), js(res))
+}
+
func (t *RoomserverInternalAPITrace) PerformInboundPeek(
ctx context.Context,
req *PerformInboundPeekRequest,
diff --git a/roomserver/api/perform.go b/roomserver/api/perform.go
index cda4b3ee..30aa2cf1 100644
--- a/roomserver/api/perform.go
+++ b/roomserver/api/perform.go
@@ -214,3 +214,12 @@ type PerformRoomUpgradeResponse struct {
NewRoomID string
Error *PerformError
}
+
+type PerformAdminEvacuateRoomRequest struct {
+ RoomID string `json:"room_id"`
+}
+
+type PerformAdminEvacuateRoomResponse struct {
+ Affected []string `json:"affected"`
+ Error *PerformError
+}
diff --git a/roomserver/internal/api.go b/roomserver/internal/api.go
index 59f485cf..267cd409 100644
--- a/roomserver/internal/api.go
+++ b/roomserver/internal/api.go
@@ -35,6 +35,7 @@ type RoomserverInternalAPI struct {
*perform.Backfiller
*perform.Forgetter
*perform.Upgrader
+ *perform.Admin
ProcessContext *process.ProcessContext
DB storage.Database
Cfg *config.RoomServer
@@ -164,6 +165,12 @@ func (r *RoomserverInternalAPI) SetFederationAPI(fsAPI fsAPI.FederationInternalA
Cfg: r.Cfg,
URSAPI: r,
}
+ r.Admin = &perform.Admin{
+ DB: r.DB,
+ Cfg: r.Cfg,
+ Inputer: r.Inputer,
+ Queryer: r.Queryer,
+ }
if err := r.Inputer.Start(); err != nil {
logrus.WithError(err).Panic("failed to start roomserver input API")
diff --git a/roomserver/internal/perform/perform_admin.go b/roomserver/internal/perform/perform_admin.go
new file mode 100644
index 00000000..2de6477c
--- /dev/null
+++ b/roomserver/internal/perform/perform_admin.go
@@ -0,0 +1,162 @@
+// Copyright 2022 The Matrix.org Foundation C.I.C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package perform
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "time"
+
+ "github.com/matrix-org/dendrite/internal/eventutil"
+ "github.com/matrix-org/dendrite/roomserver/api"
+ "github.com/matrix-org/dendrite/roomserver/internal/input"
+ "github.com/matrix-org/dendrite/roomserver/internal/query"
+ "github.com/matrix-org/dendrite/roomserver/storage"
+ "github.com/matrix-org/dendrite/setup/config"
+ "github.com/matrix-org/gomatrixserverlib"
+)
+
+type Admin struct {
+ DB storage.Database
+ Cfg *config.RoomServer
+ Queryer *query.Queryer
+ Inputer *input.Inputer
+}
+
+// PerformEvacuateRoom will remove all local users from the given room.
+func (r *Admin) PerformAdminEvacuateRoom(
+ ctx context.Context,
+ req *api.PerformAdminEvacuateRoomRequest,
+ res *api.PerformAdminEvacuateRoomResponse,
+) {
+ roomInfo, err := r.DB.RoomInfo(ctx, req.RoomID)
+ if err != nil {
+ res.Error = &api.PerformError{
+ Code: api.PerformErrorBadRequest,
+ Msg: fmt.Sprintf("r.DB.RoomInfo: %s", err),
+ }
+ return
+ }
+ if roomInfo == nil || roomInfo.IsStub {
+ res.Error = &api.PerformError{
+ Code: api.PerformErrorNoRoom,
+ Msg: fmt.Sprintf("Room %s not found", req.RoomID),
+ }
+ return
+ }
+
+ memberNIDs, err := r.DB.GetMembershipEventNIDsForRoom(ctx, roomInfo.RoomNID, true, true)
+ if err != nil {
+ res.Error = &api.PerformError{
+ Code: api.PerformErrorBadRequest,
+ Msg: fmt.Sprintf("r.DB.GetMembershipEventNIDsForRoom: %s", err),
+ }
+ return
+ }
+
+ memberEvents, err := r.DB.Events(ctx, memberNIDs)
+ if err != nil {
+ res.Error = &api.PerformError{
+ Code: api.PerformErrorBadRequest,
+ Msg: fmt.Sprintf("r.DB.Events: %s", err),
+ }
+ return
+ }
+
+ inputEvents := make([]api.InputRoomEvent, 0, len(memberEvents))
+ res.Affected = make([]string, 0, len(memberEvents))
+ latestReq := &api.QueryLatestEventsAndStateRequest{
+ RoomID: req.RoomID,
+ }
+ latestRes := &api.QueryLatestEventsAndStateResponse{}
+ if err = r.Queryer.QueryLatestEventsAndState(ctx, latestReq, latestRes); err != nil {
+ res.Error = &api.PerformError{
+ Code: api.PerformErrorBadRequest,
+ Msg: fmt.Sprintf("r.Queryer.QueryLatestEventsAndState: %s", err),
+ }
+ return
+ }
+
+ prevEvents := latestRes.LatestEvents
+ for _, memberEvent := range memberEvents {
+ if memberEvent.StateKey() == nil {
+ continue
+ }
+
+ var memberContent gomatrixserverlib.MemberContent
+ if err = json.Unmarshal(memberEvent.Content(), &memberContent); err != nil {
+ res.Error = &api.PerformError{
+ Code: api.PerformErrorBadRequest,
+ Msg: fmt.Sprintf("json.Unmarshal: %s", err),
+ }
+ return
+ }
+ memberContent.Membership = gomatrixserverlib.Leave
+
+ stateKey := *memberEvent.StateKey()
+ fledglingEvent := &gomatrixserverlib.EventBuilder{
+ RoomID: req.RoomID,
+ Type: gomatrixserverlib.MRoomMember,
+ StateKey: &stateKey,
+ Sender: stateKey,
+ PrevEvents: prevEvents,
+ }
+
+ if fledglingEvent.Content, err = json.Marshal(memberContent); err != nil {
+ res.Error = &api.PerformError{
+ Code: api.PerformErrorBadRequest,
+ Msg: fmt.Sprintf("json.Marshal: %s", err),
+ }
+ return
+ }
+
+ eventsNeeded, err := gomatrixserverlib.StateNeededForEventBuilder(fledglingEvent)
+ if err != nil {
+ res.Error = &api.PerformError{
+ Code: api.PerformErrorBadRequest,
+ Msg: fmt.Sprintf("gomatrixserverlib.StateNeededForEventBuilder: %s", err),
+ }
+ return
+ }
+
+ event, err := eventutil.BuildEvent(ctx, fledglingEvent, r.Cfg.Matrix, time.Now(), &eventsNeeded, latestRes)
+ if err != nil {
+ res.Error = &api.PerformError{
+ Code: api.PerformErrorBadRequest,
+ Msg: fmt.Sprintf("eventutil.BuildEvent: %s", err),
+ }
+ return
+ }
+
+ inputEvents = append(inputEvents, api.InputRoomEvent{
+ Kind: api.KindNew,
+ Event: event,
+ Origin: r.Cfg.Matrix.ServerName,
+ SendAsServer: string(r.Cfg.Matrix.ServerName),
+ })
+ res.Affected = append(res.Affected, stateKey)
+ prevEvents = []gomatrixserverlib.EventReference{
+ event.EventReference(),
+ }
+ }
+
+ inputReq := &api.InputRoomEventsRequest{
+ InputRoomEvents: inputEvents,
+ Asynchronous: true,
+ }
+ inputRes := &api.InputRoomEventsResponse{}
+ r.Inputer.InputRoomEvents(ctx, inputReq, inputRes)
+}
diff --git a/roomserver/inthttp/client.go b/roomserver/inthttp/client.go
index d55805a9..3b29001e 100644
--- a/roomserver/inthttp/client.go
+++ b/roomserver/inthttp/client.go
@@ -29,16 +29,17 @@ const (
RoomserverInputRoomEventsPath = "/roomserver/inputRoomEvents"
// Perform operations
- RoomserverPerformInvitePath = "/roomserver/performInvite"
- RoomserverPerformPeekPath = "/roomserver/performPeek"
- RoomserverPerformUnpeekPath = "/roomserver/performUnpeek"
- RoomserverPerformRoomUpgradePath = "/roomserver/performRoomUpgrade"
- RoomserverPerformJoinPath = "/roomserver/performJoin"
- RoomserverPerformLeavePath = "/roomserver/performLeave"
- RoomserverPerformBackfillPath = "/roomserver/performBackfill"
- RoomserverPerformPublishPath = "/roomserver/performPublish"
- RoomserverPerformInboundPeekPath = "/roomserver/performInboundPeek"
- RoomserverPerformForgetPath = "/roomserver/performForget"
+ RoomserverPerformInvitePath = "/roomserver/performInvite"
+ RoomserverPerformPeekPath = "/roomserver/performPeek"
+ RoomserverPerformUnpeekPath = "/roomserver/performUnpeek"
+ RoomserverPerformRoomUpgradePath = "/roomserver/performRoomUpgrade"
+ RoomserverPerformJoinPath = "/roomserver/performJoin"
+ RoomserverPerformLeavePath = "/roomserver/performLeave"
+ RoomserverPerformBackfillPath = "/roomserver/performBackfill"
+ RoomserverPerformPublishPath = "/roomserver/performPublish"
+ RoomserverPerformInboundPeekPath = "/roomserver/performInboundPeek"
+ RoomserverPerformForgetPath = "/roomserver/performForget"
+ RoomserverPerformAdminEvacuateRoomPath = "/roomserver/performAdminEvacuateRoom"
// Query operations
RoomserverQueryLatestEventsAndStatePath = "/roomserver/queryLatestEventsAndState"
@@ -299,6 +300,23 @@ func (h *httpRoomserverInternalAPI) PerformPublish(
}
}
+func (h *httpRoomserverInternalAPI) PerformAdminEvacuateRoom(
+ ctx context.Context,
+ req *api.PerformAdminEvacuateRoomRequest,
+ res *api.PerformAdminEvacuateRoomResponse,
+) {
+ span, ctx := opentracing.StartSpanFromContext(ctx, "PerformAdminEvacuateRoom")
+ defer span.Finish()
+
+ apiURL := h.roomserverURL + RoomserverPerformAdminEvacuateRoomPath
+ err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, req, res)
+ if err != nil {
+ res.Error = &api.PerformError{
+ Msg: fmt.Sprintf("failed to communicate with roomserver: %s", err),
+ }
+ }
+}
+
// QueryLatestEventsAndState implements RoomserverQueryAPI
func (h *httpRoomserverInternalAPI) QueryLatestEventsAndState(
ctx context.Context,
diff --git a/roomserver/inthttp/server.go b/roomserver/inthttp/server.go
index 0b27b5a8..c5159a63 100644
--- a/roomserver/inthttp/server.go
+++ b/roomserver/inthttp/server.go
@@ -118,6 +118,17 @@ func AddRoutes(r api.RoomserverInternalAPI, internalAPIMux *mux.Router) {
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}),
)
+ internalAPIMux.Handle(RoomserverPerformAdminEvacuateRoomPath,
+ httputil.MakeInternalAPI("performAdminEvacuateRoom", func(req *http.Request) util.JSONResponse {
+ var request api.PerformAdminEvacuateRoomRequest
+ var response api.PerformAdminEvacuateRoomResponse
+ if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
+ return util.MessageResponse(http.StatusBadRequest, err.Error())
+ }
+ r.PerformAdminEvacuateRoom(req.Context(), &request, &response)
+ return util.JSONResponse{Code: http.StatusOK, JSON: &response}
+ }),
+ )
internalAPIMux.Handle(
RoomserverQueryPublishedRoomsPath,
httputil.MakeInternalAPI("queryPublishedRooms", func(req *http.Request) util.JSONResponse {