Skip to content
This repository was archived by the owner on Nov 25, 2024. It is now read-only.

Commit ad0a7d0

Browse files
authored
Add getting/deleting single event report (#3344)
Based on #3342 Adds `GET /_synapse/admin/v1/event_reports/{reportID}` and `DELETE /_synapse/admin/v1/event_reports/{reportID}`
1 parent 81f73c9 commit ad0a7d0

File tree

12 files changed

+380
-1
lines changed

12 files changed

+380
-1
lines changed

clientapi/admin_test.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,3 +1336,140 @@ func TestAdminQueryEventReports(t *testing.T) {
13361336
})
13371337
})
13381338
}
1339+
1340+
func TestEventReportsGetDelete(t *testing.T) {
1341+
alice := test.NewUser(t, test.WithAccountType(uapi.AccountTypeAdmin))
1342+
bob := test.NewUser(t)
1343+
room := test.NewRoom(t, alice)
1344+
1345+
// Add a name and alias
1346+
roomName := "Testing"
1347+
alias := "#testing"
1348+
room.CreateAndInsert(t, alice, spec.MRoomName, map[string]string{"name": roomName}, test.WithStateKey(""))
1349+
room.CreateAndInsert(t, alice, spec.MRoomCanonicalAlias, map[string]string{"alias": alias}, test.WithStateKey(""))
1350+
1351+
// Join the rooms with Bob
1352+
room.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{
1353+
"membership": "join",
1354+
}, test.WithStateKey(bob.ID))
1355+
1356+
// Create a few events to report
1357+
1358+
eventIDToReport := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": "hello world"})
1359+
1360+
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
1361+
cfg, processCtx, close := testrig.CreateConfig(t, dbType)
1362+
routers := httputil.NewRouters()
1363+
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
1364+
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
1365+
defer close()
1366+
natsInstance := jetstream.NATSInstance{}
1367+
jsctx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream)
1368+
defer jetstream.DeleteAllStreams(jsctx, &cfg.Global.JetStream)
1369+
1370+
// Use an actual roomserver for this
1371+
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
1372+
rsAPI.SetFederationAPI(nil, nil)
1373+
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
1374+
1375+
if err := api.SendEvents(context.Background(), rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
1376+
t.Fatalf("failed to send events: %v", err)
1377+
}
1378+
1379+
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
1380+
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
1381+
1382+
accessTokens := map[*test.User]userDevice{
1383+
alice: {},
1384+
bob: {},
1385+
}
1386+
createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers)
1387+
1388+
reqBody := map[string]any{
1389+
"reason": "baaad",
1390+
"score": -100,
1391+
}
1392+
body, err := json.Marshal(reqBody)
1393+
if err != nil {
1394+
t.Fatal(err)
1395+
}
1396+
1397+
w := httptest.NewRecorder()
1398+
1399+
var req *http.Request
1400+
// Report the event
1401+
req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/report/%s", room.ID, eventIDToReport.EventID()), strings.NewReader(string(body)))
1402+
req.Header.Set("Authorization", "Bearer "+accessTokens[bob].accessToken)
1403+
1404+
routers.Client.ServeHTTP(w, req)
1405+
1406+
if w.Code != http.StatusOK {
1407+
t.Fatalf("expected report to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
1408+
}
1409+
1410+
t.Run("Can not query with invalid ID", func(t *testing.T) {
1411+
w = httptest.NewRecorder()
1412+
req = httptest.NewRequest(http.MethodGet, "/_synapse/admin/v1/event_reports/abc", strings.NewReader(string(body)))
1413+
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
1414+
1415+
routers.SynapseAdmin.ServeHTTP(w, req)
1416+
1417+
if w.Code != http.StatusBadRequest {
1418+
t.Fatalf("expected getting report to fail, got HTTP %d instead: %s", w.Code, w.Body.String())
1419+
}
1420+
})
1421+
1422+
t.Run("Can query with valid ID", func(t *testing.T) {
1423+
w = httptest.NewRecorder()
1424+
req = httptest.NewRequest(http.MethodGet, "/_synapse/admin/v1/event_reports/1", strings.NewReader(string(body)))
1425+
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
1426+
1427+
routers.SynapseAdmin.ServeHTTP(w, req)
1428+
1429+
if w.Code != http.StatusOK {
1430+
t.Fatalf("expected getting report to fail, got HTTP %d instead: %s", w.Code, w.Body.String())
1431+
}
1432+
resp := api.QueryAdminEventReportResponse{}
1433+
if err = json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
1434+
t.Fatal(err)
1435+
}
1436+
// test a few things
1437+
if resp.EventID != eventIDToReport.EventID() {
1438+
t.Fatalf("expected eventID to be %s, got %s instead", eventIDToReport.EventID(), resp.EventID)
1439+
}
1440+
if resp.RoomName != roomName {
1441+
t.Fatalf("expected roomName to be %s, got %s instead", roomName, resp.RoomName)
1442+
}
1443+
if resp.CanonicalAlias != alias {
1444+
t.Fatalf("expected alias to be %s, got %s instead", alias, resp.CanonicalAlias)
1445+
}
1446+
if reflect.DeepEqual(resp.EventJSON, eventIDToReport.JSON()) {
1447+
t.Fatal("mismatching eventJSON")
1448+
}
1449+
})
1450+
1451+
t.Run("Can delete with a valid ID", func(t *testing.T) {
1452+
w = httptest.NewRecorder()
1453+
req = httptest.NewRequest(http.MethodDelete, "/_synapse/admin/v1/event_reports/1", strings.NewReader(string(body)))
1454+
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
1455+
1456+
routers.SynapseAdmin.ServeHTTP(w, req)
1457+
1458+
if w.Code != http.StatusOK {
1459+
t.Fatalf("expected getting report to fail, got HTTP %d instead: %s", w.Code, w.Body.String())
1460+
}
1461+
})
1462+
1463+
t.Run("Can not query deleted report", func(t *testing.T) {
1464+
w = httptest.NewRecorder()
1465+
req = httptest.NewRequest(http.MethodGet, "/_synapse/admin/v1/event_reports/1", strings.NewReader(string(body)))
1466+
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
1467+
1468+
routers.SynapseAdmin.ServeHTTP(w, req)
1469+
1470+
if w.Code == http.StatusOK {
1471+
t.Fatalf("expected getting report to fail, got HTTP %d instead: %s", w.Code, w.Body.String())
1472+
}
1473+
})
1474+
})
1475+
}

clientapi/routing/admin.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,54 @@ func GetEventReports(
530530
}
531531
}
532532

533+
func GetEventReport(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, reportID string) util.JSONResponse {
534+
parsedReportID, err := strconv.ParseUint(reportID, 10, 64)
535+
if err != nil {
536+
return util.JSONResponse{
537+
Code: http.StatusBadRequest,
538+
// Given this is an admin endpoint, let them know what didn't work.
539+
JSON: spec.InvalidParam(err.Error()),
540+
}
541+
}
542+
543+
report, err := rsAPI.QueryAdminEventReport(req.Context(), parsedReportID)
544+
if err != nil {
545+
return util.JSONResponse{
546+
Code: http.StatusInternalServerError,
547+
JSON: spec.Unknown(err.Error()),
548+
}
549+
}
550+
551+
return util.JSONResponse{
552+
Code: http.StatusOK,
553+
JSON: report,
554+
}
555+
}
556+
557+
func DeleteEventReport(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, reportID string) util.JSONResponse {
558+
parsedReportID, err := strconv.ParseUint(reportID, 10, 64)
559+
if err != nil {
560+
return util.JSONResponse{
561+
Code: http.StatusBadRequest,
562+
// Given this is an admin endpoint, let them know what didn't work.
563+
JSON: spec.InvalidParam(err.Error()),
564+
}
565+
}
566+
567+
err = rsAPI.PerformAdminDeleteEventReport(req.Context(), parsedReportID)
568+
if err != nil {
569+
return util.JSONResponse{
570+
Code: http.StatusInternalServerError,
571+
JSON: spec.Unknown(err.Error()),
572+
}
573+
}
574+
575+
return util.JSONResponse{
576+
Code: http.StatusOK,
577+
JSON: struct{}{},
578+
}
579+
}
580+
533581
func parseUint64OrDefault(input string, defaultValue uint64) uint64 {
534582
v, err := strconv.ParseUint(input, 10, 64)
535583
if err != nil {

clientapi/routing/routing.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1535,7 +1535,7 @@ func Setup(
15351535
).Methods(http.MethodPost, http.MethodOptions)
15361536

15371537
synapseAdminRouter.Handle("/admin/v1/event_reports",
1538-
httputil.MakeAdminAPI("admin_report_event", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
1538+
httputil.MakeAdminAPI("admin_report_events", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
15391539
from := parseUint64OrDefault(req.URL.Query().Get("from"), 0)
15401540
limit := parseUint64OrDefault(req.URL.Query().Get("limit"), 100)
15411541
dir := req.URL.Query().Get("dir")
@@ -1547,4 +1547,24 @@ func Setup(
15471547
return GetEventReports(req, rsAPI, from, limit, backwards, userID, roomID)
15481548
}),
15491549
).Methods(http.MethodGet, http.MethodOptions)
1550+
1551+
synapseAdminRouter.Handle("/admin/v1/event_reports/{reportID}",
1552+
httputil.MakeAdminAPI("admin_report_event", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
1553+
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
1554+
if err != nil {
1555+
return util.ErrorResponse(err)
1556+
}
1557+
return GetEventReport(req, rsAPI, vars["reportID"])
1558+
}),
1559+
).Methods(http.MethodGet, http.MethodOptions)
1560+
1561+
synapseAdminRouter.Handle("/admin/v1/event_reports/{reportID}",
1562+
httputil.MakeAdminAPI("admin_report_event_delete", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
1563+
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
1564+
if err != nil {
1565+
return util.ErrorResponse(err)
1566+
}
1567+
return DeleteEventReport(req, rsAPI, vars["reportID"])
1568+
}),
1569+
).Methods(http.MethodDelete, http.MethodOptions)
15501570
}

roomserver/api/api.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ type ClientRoomserverAPI interface {
272272
score int64,
273273
) (int64, error)
274274
QueryAdminEventReports(ctx context.Context, from, limit uint64, backwards bool, userID, roomID string) ([]QueryAdminEventReportsResponse, int64, error)
275+
QueryAdminEventReport(ctx context.Context, reportID uint64) (QueryAdminEventReportResponse, error)
276+
PerformAdminDeleteEventReport(ctx context.Context, reportID uint64) error
275277
}
276278

277279
type UserRoomserverAPI interface {

roomserver/api/query.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,11 @@ type QueryAdminEventReportsResponse struct {
363363
ReceivedTS spec.Timestamp `json:"received_ts"`
364364
}
365365

366+
type QueryAdminEventReportResponse struct {
367+
QueryAdminEventReportsResponse
368+
EventJSON json.RawMessage `json:"event_json"`
369+
}
370+
366371
// MarshalJSON stringifies the room ID and StateKeyTuple keys so they can be sent over the wire in HTTP API mode.
367372
func (r *QueryBulkStateContentResponse) MarshalJSON() ([]byte, error) {
368373
se := make(map[string]string)

roomserver/internal/perform/perform_admin.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,3 +354,7 @@ func (r *Admin) PerformAdminDownloadState(
354354

355355
return nil
356356
}
357+
358+
func (r *Admin) PerformAdminDeleteEventReport(ctx context.Context, reportID uint64) error {
359+
return r.DB.AdminDeleteEventReport(ctx, reportID)
360+
}

roomserver/internal/query/query.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,3 +1109,8 @@ func (r *Queryer) RoomsWithACLs(ctx context.Context) ([]string, error) {
11091109
func (r *Queryer) QueryAdminEventReports(ctx context.Context, from uint64, limit uint64, backwards bool, userID, roomID string) ([]api.QueryAdminEventReportsResponse, int64, error) {
11101110
return r.DB.QueryAdminEventReports(ctx, from, limit, backwards, userID, roomID)
11111111
}
1112+
1113+
// QueryAdminEventReport returns a single event report.
1114+
func (r *Queryer) QueryAdminEventReport(ctx context.Context, reportID uint64) (api.QueryAdminEventReportResponse, error) {
1115+
return r.DB.QueryAdminEventReport(ctx, reportID)
1116+
}

roomserver/storage/interface.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ type Database interface {
196196
// RoomsWithACLs returns all room IDs for rooms with ACLs
197197
RoomsWithACLs(ctx context.Context) ([]string, error)
198198
QueryAdminEventReports(ctx context.Context, from uint64, limit uint64, backwards bool, userID string, roomID string) ([]api.QueryAdminEventReportsResponse, int64, error)
199+
QueryAdminEventReport(ctx context.Context, reportID uint64) (api.QueryAdminEventReportResponse, error)
200+
AdminDeleteEventReport(ctx context.Context, reportID uint64) error
199201
}
200202

201203
type UserRoomKeys interface {

roomserver/storage/postgres/reported_events_table.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,20 @@ OFFSET $3
7575
LIMIT $4
7676
`
7777

78+
const selectReportedEventSQL = `
79+
SELECT id, room_nid, event_nid, reporting_user_nid, event_sender_nid, reason, score, received_ts
80+
FROM roomserver_reported_events
81+
WHERE id = $1
82+
`
83+
84+
const deleteReportedEventSQL = `DELETE FROM roomserver_reported_events WHERE id = $1`
85+
7886
type reportedEventsStatements struct {
7987
insertReportedEventsStmt *sql.Stmt
8088
selectReportedEventsDescStmt *sql.Stmt
8189
selectReportedEventsAscStmt *sql.Stmt
90+
selectReportedEventStmt *sql.Stmt
91+
deleteReportedEventStmt *sql.Stmt
8292
}
8393

8494
func CreateReportedEventsTable(db *sql.DB) error {
@@ -93,6 +103,8 @@ func PrepareReportedEventsTable(db *sql.DB) (tables.ReportedEvents, error) {
93103
{&s.insertReportedEventsStmt, insertReportedEventSQL},
94104
{&s.selectReportedEventsDescStmt, selectReportedEventsDescSQL},
95105
{&s.selectReportedEventsAscStmt, selectReportedEventsAscSQL},
106+
{&s.selectReportedEventStmt, selectReportedEventSQL},
107+
{&s.deleteReportedEventStmt, deleteReportedEventSQL},
96108
}.Prepare(db)
97109
}
98110

@@ -178,3 +190,32 @@ func (r *reportedEventsStatements) SelectReportedEvents(
178190

179191
return result, count, rows.Err()
180192
}
193+
194+
func (r *reportedEventsStatements) SelectReportedEvent(
195+
ctx context.Context,
196+
txn *sql.Tx,
197+
reportID uint64,
198+
) (api.QueryAdminEventReportResponse, error) {
199+
stmt := sqlutil.TxStmt(txn, r.selectReportedEventStmt)
200+
201+
var row api.QueryAdminEventReportResponse
202+
if err := stmt.QueryRowContext(ctx, reportID).Scan(
203+
&row.ID,
204+
&row.RoomNID,
205+
&row.EventNID,
206+
&row.ReportingUserNID,
207+
&row.SenderNID,
208+
&row.Reason,
209+
&row.Score,
210+
&row.ReceivedTS,
211+
); err != nil {
212+
return api.QueryAdminEventReportResponse{}, err
213+
}
214+
return row, nil
215+
}
216+
217+
func (r *reportedEventsStatements) DeleteReportedEvent(ctx context.Context, txn *sql.Tx, reportID uint64) error {
218+
stmt := sqlutil.TxStmt(txn, r.deleteReportedEventStmt)
219+
_, err := stmt.ExecContext(ctx, reportID)
220+
return err
221+
}

0 commit comments

Comments
 (0)