aboutsummaryrefslogtreecommitdiff
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
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>
-rw-r--r--build/gobind-pinecone/monolith.go1
-rw-r--r--build/gobind-yggdrasil/monolith.go1
-rw-r--r--clientapi/clientapi.go4
-rw-r--r--clientapi/routing/routing.go42
-rw-r--r--cmd/dendrite-demo-pinecone/main.go1
-rw-r--r--cmd/dendrite-demo-yggdrasil/main.go1
-rw-r--r--cmd/dendrite-monolith-server/main.go1
-rw-r--r--cmd/dendrite-polylith-multi/personalities/clientapi.go6
-rw-r--r--cmd/dendritejs-pinecone/main.go1
-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
-rw-r--r--setup/monolith.go4
17 files changed, 288 insertions, 16 deletions
diff --git a/build/gobind-pinecone/monolith.go b/build/gobind-pinecone/monolith.go
index 9cc94d65..6b253349 100644
--- a/build/gobind-pinecone/monolith.go
+++ b/build/gobind-pinecone/monolith.go
@@ -314,6 +314,7 @@ func (m *DendriteMonolith) Start() {
base.PublicWellKnownAPIMux,
base.PublicMediaAPIMux,
base.SynapseAdminMux,
+ base.DendriteAdminMux,
)
httpRouter := mux.NewRouter().SkipClean(true).UseEncodedPath()
diff --git a/build/gobind-yggdrasil/monolith.go b/build/gobind-yggdrasil/monolith.go
index 87dcad2e..b9c6c1b7 100644
--- a/build/gobind-yggdrasil/monolith.go
+++ b/build/gobind-yggdrasil/monolith.go
@@ -152,6 +152,7 @@ func (m *DendriteMonolith) Start() {
base.PublicWellKnownAPIMux,
base.PublicMediaAPIMux,
base.SynapseAdminMux,
+ base.DendriteAdminMux,
)
httpRouter := mux.NewRouter()
diff --git a/clientapi/clientapi.go b/clientapi/clientapi.go
index e2f8d3f3..ad277056 100644
--- a/clientapi/clientapi.go
+++ b/clientapi/clientapi.go
@@ -36,6 +36,7 @@ func AddPublicRoutes(
process *process.ProcessContext,
router *mux.Router,
synapseAdminRouter *mux.Router,
+ dendriteAdminRouter *mux.Router,
cfg *config.ClientAPI,
federation *gomatrixserverlib.FederationClient,
rsAPI roomserverAPI.RoomserverInternalAPI,
@@ -62,7 +63,8 @@ func AddPublicRoutes(
}
routing.Setup(
- router, synapseAdminRouter, cfg, rsAPI, asAPI,
+ router, synapseAdminRouter, dendriteAdminRouter,
+ cfg, rsAPI, asAPI,
userAPI, userDirectoryProvider, federation,
syncProducer, transactionsCache, fsAPI, keyAPI,
extRoomsProvider, mscCfg, natsClient,
diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go
index f370b4f8..ec90b80d 100644
--- a/clientapi/routing/routing.go
+++ b/clientapi/routing/routing.go
@@ -48,7 +48,8 @@ import (
// applied:
// nolint: gocyclo
func Setup(
- publicAPIMux, synapseAdminRouter *mux.Router, cfg *config.ClientAPI,
+ publicAPIMux, synapseAdminRouter, dendriteAdminRouter *mux.Router,
+ cfg *config.ClientAPI,
rsAPI roomserverAPI.RoomserverInternalAPI,
asAPI appserviceAPI.AppServiceQueryAPI,
userAPI userapi.UserInternalAPI,
@@ -119,6 +120,45 @@ func Setup(
).Methods(http.MethodGet, http.MethodPost, http.MethodOptions)
}
+ dendriteAdminRouter.Handle("/admin/evacuateRoom/{roomID}",
+ httputil.MakeAuthAPI("admin_evacuate_room", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
+ if device.AccountType != userapi.AccountTypeAdmin {
+ return util.JSONResponse{
+ Code: http.StatusForbidden,
+ JSON: jsonerror.Forbidden("This API can only be used by admin users."),
+ }
+ }
+ 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: jsonerror.MissingArgument("Expecting room ID."),
+ }
+ }
+ res := &roomserverAPI.PerformAdminEvacuateRoomResponse{}
+ rsAPI.PerformAdminEvacuateRoom(
+ req.Context(),
+ &roomserverAPI.PerformAdminEvacuateRoomRequest{
+ RoomID: roomID,
+ },
+ res,
+ )
+ if err := res.Error; err != nil {
+ return err.JSONResponse()
+ }
+ return util.JSONResponse{
+ Code: 200,
+ JSON: map[string]interface{}{
+ "affected": res.Affected,
+ },
+ }
+ }),
+ ).Methods(http.MethodGet, http.MethodOptions)
+
// server notifications
if cfg.Matrix.ServerNotices.Enabled {
logrus.Info("Enabling server notices at /_synapse/admin/v1/send_server_notice")
diff --git a/cmd/dendrite-demo-pinecone/main.go b/cmd/dendrite-demo-pinecone/main.go
index dd1ab369..7ec810c9 100644
--- a/cmd/dendrite-demo-pinecone/main.go
+++ b/cmd/dendrite-demo-pinecone/main.go
@@ -193,6 +193,7 @@ func main() {
base.PublicWellKnownAPIMux,
base.PublicMediaAPIMux,
base.SynapseAdminMux,
+ base.DendriteAdminMux,
)
wsUpgrader := websocket.Upgrader{
diff --git a/cmd/dendrite-demo-yggdrasil/main.go b/cmd/dendrite-demo-yggdrasil/main.go
index b840eb2b..54231f30 100644
--- a/cmd/dendrite-demo-yggdrasil/main.go
+++ b/cmd/dendrite-demo-yggdrasil/main.go
@@ -150,6 +150,7 @@ func main() {
base.PublicWellKnownAPIMux,
base.PublicMediaAPIMux,
base.SynapseAdminMux,
+ base.DendriteAdminMux,
)
if err := mscs.Enable(base, &monolith); err != nil {
logrus.WithError(err).Fatalf("Failed to enable MSCs")
diff --git a/cmd/dendrite-monolith-server/main.go b/cmd/dendrite-monolith-server/main.go
index 1443ab5b..5fd5c0b5 100644
--- a/cmd/dendrite-monolith-server/main.go
+++ b/cmd/dendrite-monolith-server/main.go
@@ -153,6 +153,7 @@ func main() {
base.PublicWellKnownAPIMux,
base.PublicMediaAPIMux,
base.SynapseAdminMux,
+ base.DendriteAdminMux,
)
if len(base.Cfg.MSCs.MSCs) > 0 {
diff --git a/cmd/dendrite-polylith-multi/personalities/clientapi.go b/cmd/dendrite-polylith-multi/personalities/clientapi.go
index 1e509f88..7ed2075a 100644
--- a/cmd/dendrite-polylith-multi/personalities/clientapi.go
+++ b/cmd/dendrite-polylith-multi/personalities/clientapi.go
@@ -31,8 +31,10 @@ func ClientAPI(base *basepkg.BaseDendrite, cfg *config.Dendrite) {
keyAPI := base.KeyServerHTTPClient()
clientapi.AddPublicRoutes(
- base.ProcessContext, base.PublicClientAPIMux, base.SynapseAdminMux, &base.Cfg.ClientAPI,
- federation, rsAPI, asQuery, transactions.New(), fsAPI, userAPI, userAPI,
+ base.ProcessContext, base.PublicClientAPIMux,
+ base.SynapseAdminMux, base.DendriteAdminMux,
+ &base.Cfg.ClientAPI, federation, rsAPI, asQuery,
+ transactions.New(), fsAPI, userAPI, userAPI,
keyAPI, nil, &cfg.MSCs,
)
diff --git a/cmd/dendritejs-pinecone/main.go b/cmd/dendritejs-pinecone/main.go
index 211b3e13..0d4b2fbc 100644
--- a/cmd/dendritejs-pinecone/main.go
+++ b/cmd/dendritejs-pinecone/main.go
@@ -220,6 +220,7 @@ func startup() {
base.PublicWellKnownAPIMux,
base.PublicMediaAPIMux,
base.SynapseAdminMux,
+ base.DendriteAdminMux,
)
httpRouter := mux.NewRouter().SkipClean(true).UseEncodedPath()
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 {
diff --git a/setup/monolith.go b/setup/monolith.go
index 32f1a649..c86ec7b6 100644
--- a/setup/monolith.go
+++ b/setup/monolith.go
@@ -54,13 +54,13 @@ type Monolith struct {
}
// AddAllPublicRoutes attaches all public paths to the given router
-func (m *Monolith) AddAllPublicRoutes(process *process.ProcessContext, csMux, ssMux, keyMux, wkMux, mediaMux, synapseMux *mux.Router) {
+func (m *Monolith) AddAllPublicRoutes(process *process.ProcessContext, csMux, ssMux, keyMux, wkMux, mediaMux, synapseMux, dendriteMux *mux.Router) {
userDirectoryProvider := m.ExtUserDirectoryProvider
if userDirectoryProvider == nil {
userDirectoryProvider = m.UserAPI
}
clientapi.AddPublicRoutes(
- process, csMux, synapseMux, &m.Config.ClientAPI,
+ process, csMux, synapseMux, dendriteMux, &m.Config.ClientAPI,
m.FedClient, m.RoomserverAPI,
m.AppserviceAPI, transactions.New(),
m.FederationAPI, m.UserAPI, userDirectoryProvider, m.KeyAPI,