mirror of
https://github.com/matrix-org/dendrite.git
synced 2026-01-16 10:33:11 -06:00
Merge branch 'main' of github.com:matrix-org/dendrite into s7evink/usagestats
This commit is contained in:
commit
80a4a599ef
23
.github/ISSUE_TEMPLATE/BUG_REPORT.md
vendored
23
.github/ISSUE_TEMPLATE/BUG_REPORT.md
vendored
|
|
@ -7,24 +7,28 @@ about: Create a report to help us improve
|
|||
<!--
|
||||
All bug reports must provide the following background information
|
||||
Text between <!-- and --> marks will be invisible in the report.
|
||||
|
||||
IF YOUR ISSUE IS CONSIDERED A SECURITY VULNERABILITY THEN PLEASE STOP
|
||||
AND DO NOT POST IT AS A GITHUB ISSUE! Please report the issue responsibly by
|
||||
disclosing in private by email to security@matrix.org instead. For more details, please
|
||||
see: https://www.matrix.org/security-disclosure-policy/
|
||||
-->
|
||||
|
||||
### Background information
|
||||
<!-- Please include versions of all software when known e.g database versions, docker versions, client versions -->
|
||||
- **Dendrite version or git SHA**:
|
||||
- **Monolith or Polylith?**:
|
||||
- **SQLite3 or Postgres?**:
|
||||
- **Running in Docker?**:
|
||||
- **Dendrite version or git SHA**:
|
||||
- **Monolith or Polylith?**:
|
||||
- **SQLite3 or Postgres?**:
|
||||
- **Running in Docker?**:
|
||||
- **`go version`**:
|
||||
- **Client used (if applicable)**:
|
||||
|
||||
|
||||
### Description
|
||||
|
||||
- **What** is the problem:
|
||||
- **Who** is affected:
|
||||
- **How** is this bug manifesting:
|
||||
- **When** did this first appear:
|
||||
- **What** is the problem:
|
||||
- **Who** is affected:
|
||||
- **How** is this bug manifesting:
|
||||
- **When** did this first appear:
|
||||
|
||||
<!--
|
||||
Examples of good descriptions:
|
||||
|
|
@ -38,7 +42,6 @@ Examples of good descriptions:
|
|||
- How: "Lots of logs about device change updates"
|
||||
- When: "After my server joined Matrix HQ"
|
||||
|
||||
|
||||
Examples of bad descriptions:
|
||||
- What: "Can't send messages" - This is bad because it isn't specfic enough. Which endpoint isn't working and what is the response code? Does the message send but encryption fail?
|
||||
- Who: "Me" - Who are you? Running the server or a user on a Dendrite server?
|
||||
|
|
|
|||
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
|
@ -1,8 +1,8 @@
|
|||
### Pull Request Checklist
|
||||
|
||||
<!-- Please read docs/CONTRIBUTING.md before submitting your pull request -->
|
||||
<!-- Please read https://matrix-org.github.io/dendrite/development/contributing before submitting your pull request -->
|
||||
|
||||
* [ ] I have added tests for PR _or_ I have justified why this PR doesn't need tests.
|
||||
* [ ] Pull request includes a [sign off](https://github.com/matrix-org/dendrite/blob/main/docs/CONTRIBUTING.md#sign-off)
|
||||
* [ ] Pull request includes a [sign off below using a legally identifiable name](https://matrix-org.github.io/dendrite/development/contributing#sign-off) _or_ I have already signed off privately
|
||||
|
||||
Signed-off-by: `Your Name <your@email.example.org>`
|
||||
|
|
|
|||
2
.github/workflows/dendrite.yml
vendored
2
.github/workflows/dendrite.yml
vendored
|
|
@ -342,7 +342,7 @@ jobs:
|
|||
# See https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-Readme.md specifically GOROOT_1_17_X64
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install -y libolm3 libolm-dev
|
||||
go get -v github.com/haveyoudebuggedit/gotestfmt/v2/cmd/gotestfmt@latest
|
||||
go get -v github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
|
||||
|
||||
- name: Run actions/checkout@v2 for dendrite
|
||||
uses: actions/checkout@v2
|
||||
|
|
|
|||
|
|
@ -643,7 +643,7 @@ fed Inbound federation redacts events from erased users
|
|||
fme Outbound federation can request missing events
|
||||
fme Inbound federation can return missing events for world_readable visibility
|
||||
fme Inbound federation can return missing events for shared visibility
|
||||
fme Inbound federation can return missing events for invite visibility
|
||||
fme Inbound federation can return missing events for invited visibility
|
||||
fme Inbound federation can return missing events for joined visibility
|
||||
fme outliers whose auth_events are in a different room are correctly rejected
|
||||
fbk Outbound federation can backfill events
|
||||
|
|
|
|||
|
|
@ -68,6 +68,12 @@ func (t *LoginTypePassword) Login(ctx context.Context, req interface{}) (*Login,
|
|||
JSON: jsonerror.BadJSON("A username must be supplied."),
|
||||
}
|
||||
}
|
||||
if len(r.Password) == 0 {
|
||||
return nil, &util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: jsonerror.BadJSON("A password must be supplied."),
|
||||
}
|
||||
}
|
||||
localpart, err := userutil.ParseUsernameParam(username, &t.Config.Matrix.ServerName)
|
||||
if err != nil {
|
||||
return nil, &util.JSONResponse{
|
||||
|
|
|
|||
|
|
@ -154,33 +154,31 @@ func SaveReadMarker(
|
|||
return *resErr
|
||||
}
|
||||
|
||||
if r.FullyRead == "" {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: jsonerror.BadJSON("Missing m.fully_read mandatory field"),
|
||||
if r.FullyRead != "" {
|
||||
data, err := json.Marshal(fullyReadEvent{EventID: r.FullyRead})
|
||||
if err != nil {
|
||||
return jsonerror.InternalServerError()
|
||||
}
|
||||
|
||||
dataReq := api.InputAccountDataRequest{
|
||||
UserID: device.UserID,
|
||||
DataType: "m.fully_read",
|
||||
RoomID: roomID,
|
||||
AccountData: data,
|
||||
}
|
||||
dataRes := api.InputAccountDataResponse{}
|
||||
if err := userAPI.InputAccountData(req.Context(), &dataReq, &dataRes); err != nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Error("userAPI.InputAccountData failed")
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
}
|
||||
|
||||
data, err := json.Marshal(fullyReadEvent{EventID: r.FullyRead})
|
||||
if err != nil {
|
||||
return jsonerror.InternalServerError()
|
||||
}
|
||||
|
||||
dataReq := api.InputAccountDataRequest{
|
||||
UserID: device.UserID,
|
||||
DataType: "m.fully_read",
|
||||
RoomID: roomID,
|
||||
AccountData: data,
|
||||
}
|
||||
dataRes := api.InputAccountDataResponse{}
|
||||
if err := userAPI.InputAccountData(req.Context(), &dataReq, &dataRes); err != nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Error("userAPI.InputAccountData failed")
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
|
||||
// Handle the read receipt that may be included in the read marker
|
||||
// Handle the read receipts that may be included in the read marker.
|
||||
if r.Read != "" {
|
||||
return SetReceipt(req, syncProducer, device, roomID, "m.read", r.Read)
|
||||
return SetReceipt(req, userAPI, syncProducer, device, roomID, "m.read", r.Read)
|
||||
}
|
||||
if r.ReadPrivate != "" {
|
||||
return SetReceipt(req, userAPI, syncProducer, device, roomID, "m.read.private", r.ReadPrivate)
|
||||
}
|
||||
|
||||
return util.JSONResponse{
|
||||
|
|
|
|||
|
|
@ -1,138 +0,0 @@
|
|||
// Copyright 2019 Alex Chen
|
||||
//
|
||||
// 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 routing
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
"github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/util"
|
||||
)
|
||||
|
||||
type getEventRequest struct {
|
||||
req *http.Request
|
||||
device *userapi.Device
|
||||
roomID string
|
||||
eventID string
|
||||
cfg *config.ClientAPI
|
||||
requestedEvent *gomatrixserverlib.Event
|
||||
}
|
||||
|
||||
// GetEvent implements GET /_matrix/client/r0/rooms/{roomId}/event/{eventId}
|
||||
// https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-rooms-roomid-event-eventid
|
||||
func GetEvent(
|
||||
req *http.Request,
|
||||
device *userapi.Device,
|
||||
roomID string,
|
||||
eventID string,
|
||||
cfg *config.ClientAPI,
|
||||
rsAPI api.ClientRoomserverAPI,
|
||||
) util.JSONResponse {
|
||||
eventsReq := api.QueryEventsByIDRequest{
|
||||
EventIDs: []string{eventID},
|
||||
}
|
||||
var eventsResp api.QueryEventsByIDResponse
|
||||
err := rsAPI.QueryEventsByID(req.Context(), &eventsReq, &eventsResp)
|
||||
if err != nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Error("queryAPI.QueryEventsByID failed")
|
||||
return jsonerror.InternalServerError()
|
||||
}
|
||||
|
||||
if len(eventsResp.Events) == 0 {
|
||||
// Event not found locally
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusNotFound,
|
||||
JSON: jsonerror.NotFound("The event was not found or you do not have permission to read this event"),
|
||||
}
|
||||
}
|
||||
|
||||
requestedEvent := eventsResp.Events[0].Event
|
||||
|
||||
r := getEventRequest{
|
||||
req: req,
|
||||
device: device,
|
||||
roomID: roomID,
|
||||
eventID: eventID,
|
||||
cfg: cfg,
|
||||
requestedEvent: requestedEvent,
|
||||
}
|
||||
|
||||
stateReq := api.QueryStateAfterEventsRequest{
|
||||
RoomID: r.requestedEvent.RoomID(),
|
||||
PrevEventIDs: r.requestedEvent.PrevEventIDs(),
|
||||
StateToFetch: []gomatrixserverlib.StateKeyTuple{{
|
||||
EventType: gomatrixserverlib.MRoomMember,
|
||||
StateKey: device.UserID,
|
||||
}},
|
||||
}
|
||||
var stateResp api.QueryStateAfterEventsResponse
|
||||
if err := rsAPI.QueryStateAfterEvents(req.Context(), &stateReq, &stateResp); err != nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Error("queryAPI.QueryStateAfterEvents failed")
|
||||
return jsonerror.InternalServerError()
|
||||
}
|
||||
|
||||
if !stateResp.RoomExists {
|
||||
util.GetLogger(req.Context()).Errorf("Expected to find room for event %s but failed", r.requestedEvent.EventID())
|
||||
return jsonerror.InternalServerError()
|
||||
}
|
||||
|
||||
if !stateResp.PrevEventsExist {
|
||||
// Missing some events locally; stateResp.StateEvents unavailable.
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusNotFound,
|
||||
JSON: jsonerror.NotFound("The event was not found or you do not have permission to read this event"),
|
||||
}
|
||||
}
|
||||
|
||||
var appService *config.ApplicationService
|
||||
if device.AppserviceID != "" {
|
||||
for _, as := range cfg.Derived.ApplicationServices {
|
||||
if as.ID == device.AppserviceID {
|
||||
appService = &as
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, stateEvent := range stateResp.StateEvents {
|
||||
if appService != nil {
|
||||
if !appService.IsInterestedInUserID(*stateEvent.StateKey()) {
|
||||
continue
|
||||
}
|
||||
} else if !stateEvent.StateKeyEquals(device.UserID) {
|
||||
continue
|
||||
}
|
||||
membership, err := stateEvent.Membership()
|
||||
if err != nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Error("stateEvent.Membership failed")
|
||||
return jsonerror.InternalServerError()
|
||||
}
|
||||
if membership == gomatrixserverlib.Join {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: gomatrixserverlib.ToClientEvent(r.requestedEvent, gomatrixserverlib.FormatAll),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusNotFound,
|
||||
JSON: jsonerror.NotFound("The event was not found or you do not have permission to read this event"),
|
||||
}
|
||||
}
|
||||
|
|
@ -15,19 +15,22 @@
|
|||
package routing
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
"github.com/matrix-org/dendrite/clientapi/producers"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
|
||||
"github.com/matrix-org/dendrite/userapi/api"
|
||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func SetReceipt(req *http.Request, syncProducer *producers.SyncAPIProducer, device *userapi.Device, roomID, receiptType, eventID string) util.JSONResponse {
|
||||
func SetReceipt(req *http.Request, userAPI api.ClientUserAPI, syncProducer *producers.SyncAPIProducer, device *userapi.Device, roomID, receiptType, eventID string) util.JSONResponse {
|
||||
timestamp := gomatrixserverlib.AsTimestamp(time.Now())
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"roomID": roomID,
|
||||
|
|
@ -37,13 +40,32 @@ func SetReceipt(req *http.Request, syncProducer *producers.SyncAPIProducer, devi
|
|||
"timestamp": timestamp,
|
||||
}).Debug("Setting receipt")
|
||||
|
||||
// currently only m.read is accepted
|
||||
if receiptType != "m.read" {
|
||||
return util.MessageResponse(400, fmt.Sprintf("receipt type must be m.read not '%s'", receiptType))
|
||||
}
|
||||
switch receiptType {
|
||||
case "m.read", "m.read.private":
|
||||
if err := syncProducer.SendReceipt(req.Context(), device.UserID, roomID, eventID, receiptType, timestamp); err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
|
||||
if err := syncProducer.SendReceipt(req.Context(), device.UserID, roomID, eventID, receiptType, timestamp); err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
case "m.fully_read":
|
||||
data, err := json.Marshal(fullyReadEvent{EventID: eventID})
|
||||
if err != nil {
|
||||
return jsonerror.InternalServerError()
|
||||
}
|
||||
|
||||
dataReq := api.InputAccountDataRequest{
|
||||
UserID: device.UserID,
|
||||
DataType: "m.fully_read",
|
||||
RoomID: roomID,
|
||||
AccountData: data,
|
||||
}
|
||||
dataRes := api.InputAccountDataResponse{}
|
||||
if err := userAPI.InputAccountData(req.Context(), &dataReq, &dataRes); err != nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Error("userAPI.InputAccountData failed")
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
|
||||
default:
|
||||
return util.MessageResponse(400, fmt.Sprintf("Receipt type '%s' not known", receiptType))
|
||||
}
|
||||
|
||||
return util.JSONResponse{
|
||||
|
|
|
|||
|
|
@ -367,15 +367,6 @@ func Setup(
|
|||
nil, cfg, rsAPI, transactionsCache)
|
||||
}),
|
||||
).Methods(http.MethodPut, http.MethodOptions)
|
||||
v3mux.Handle("/rooms/{roomID}/event/{eventID}",
|
||||
httputil.MakeAuthAPI("rooms_get_event", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
return GetEvent(req, device, vars["roomID"], vars["eventID"], cfg, rsAPI)
|
||||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/state", httputil.MakeAuthAPI("room_state", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
|
@ -1352,7 +1343,7 @@ func Setup(
|
|||
return util.ErrorResponse(err)
|
||||
}
|
||||
|
||||
return SetReceipt(req, syncProducer, device, vars["roomId"], vars["receiptType"], vars["eventId"])
|
||||
return SetReceipt(req, userAPI, syncProducer, device, vars["roomId"], vars["receiptType"], vars["eventId"])
|
||||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
v3mux.Handle("/presence/{userId}/status",
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ matrix.example.com {
|
|||
# Change the end of each reverse_proxy line to the correct
|
||||
# address for your various services.
|
||||
@sync_api {
|
||||
path_regexp /_matrix/client/.*?/(sync|user/.*?/filter/?.*|keys/changes|rooms/.*?/messages)$
|
||||
path_regexp /_matrix/client/.*?/(sync|user/.*?/filter/?.*|keys/changes|rooms/.*?/(messages|context/.*?|event/.*?))$
|
||||
}
|
||||
reverse_proxy @sync_api sync_api:8073
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,10 @@ VirtualHost {
|
|||
# /_matrix/client/.*/user/{userId}/filter/{filterID}
|
||||
# /_matrix/client/.*/keys/changes
|
||||
# /_matrix/client/.*/rooms/{roomId}/messages
|
||||
# /_matrix/client/.*/rooms/{roomId}/context/{eventID}
|
||||
# /_matrix/client/.*/rooms/{roomId}/event/{eventID}
|
||||
# to sync_api
|
||||
ReverseProxy = /_matrix/client/.*?/(sync|user/.*?/filter/?.*|keys/changes|rooms/.*?/messages) http://localhost:8073 600
|
||||
ReverseProxy = /_matrix/client/.*?/(sync|user/.*?/filter/?.*|keys/changes|rooms/.*?/(messages|context/.*?|event/.*?))$ http://localhost:8073 600
|
||||
ReverseProxy = /_matrix/client http://localhost:8071 600
|
||||
ReverseProxy = /_matrix/federation http://localhost:8072 600
|
||||
ReverseProxy = /_matrix/key http://localhost:8072 600
|
||||
|
|
|
|||
|
|
@ -28,8 +28,10 @@ server {
|
|||
# /_matrix/client/.*/user/{userId}/filter/{filterID}
|
||||
# /_matrix/client/.*/keys/changes
|
||||
# /_matrix/client/.*/rooms/{roomId}/messages
|
||||
# /_matrix/client/.*/rooms/{roomId}/context/{eventID}
|
||||
# /_matrix/client/.*/rooms/{roomId}/event/{eventID}
|
||||
# to sync_api
|
||||
location ~ /_matrix/client/.*?/(sync|user/.*?/filter/?.*|keys/changes|rooms/.*?/messages)$ {
|
||||
location ~ /_matrix/client/.*?/(sync|user/.*?/filter/?.*|keys/changes|rooms/.*?/(messages|context/.*?|event/.*?))$ {
|
||||
proxy_pass http://sync_api:8073;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/matrix-org/dendrite/federationapi/queue"
|
||||
"github.com/matrix-org/dendrite/federationapi/storage"
|
||||
"github.com/matrix-org/dendrite/federationapi/types"
|
||||
|
|
@ -26,9 +31,6 @@ import (
|
|||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||
"github.com/matrix-org/dendrite/setup/process"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// KeyChangeConsumer consumes events that originate in key server.
|
||||
|
|
@ -78,6 +80,7 @@ func (t *KeyChangeConsumer) onMessage(ctx context.Context, msgs []*nats.Msg) boo
|
|||
msg := msgs[0] // Guaranteed to exist if onMessage is called
|
||||
var m api.DeviceMessage
|
||||
if err := json.Unmarshal(msg.Data, &m); err != nil {
|
||||
sentry.CaptureException(err)
|
||||
logrus.WithError(err).Errorf("failed to read device message from key change topic")
|
||||
return true
|
||||
}
|
||||
|
|
@ -105,6 +108,7 @@ func (t *KeyChangeConsumer) onDeviceKeyMessage(m api.DeviceMessage) bool {
|
|||
// only send key change events which originated from us
|
||||
_, originServerName, err := gomatrixserverlib.SplitID('@', m.UserID)
|
||||
if err != nil {
|
||||
sentry.CaptureException(err)
|
||||
logger.WithError(err).Error("Failed to extract domain from key change event")
|
||||
return true
|
||||
}
|
||||
|
|
@ -118,6 +122,7 @@ func (t *KeyChangeConsumer) onDeviceKeyMessage(m api.DeviceMessage) bool {
|
|||
WantMembership: "join",
|
||||
}, &queryRes)
|
||||
if err != nil {
|
||||
sentry.CaptureException(err)
|
||||
logger.WithError(err).Error("failed to calculate joined rooms for user")
|
||||
return true
|
||||
}
|
||||
|
|
@ -125,6 +130,7 @@ func (t *KeyChangeConsumer) onDeviceKeyMessage(m api.DeviceMessage) bool {
|
|||
// send this key change to all servers who share rooms with this user.
|
||||
destinations, err := t.db.GetJoinedHostsForRooms(t.ctx, queryRes.RoomIDs, true)
|
||||
if err != nil {
|
||||
sentry.CaptureException(err)
|
||||
logger.WithError(err).Error("failed to calculate joined hosts for rooms user is in")
|
||||
return true
|
||||
}
|
||||
|
|
@ -147,6 +153,7 @@ func (t *KeyChangeConsumer) onDeviceKeyMessage(m api.DeviceMessage) bool {
|
|||
Keys: m.KeyJSON,
|
||||
}
|
||||
if edu.Content, err = json.Marshal(event); err != nil {
|
||||
sentry.CaptureException(err)
|
||||
logger.WithError(err).Error("failed to marshal EDU JSON")
|
||||
return true
|
||||
}
|
||||
|
|
@ -160,6 +167,7 @@ func (t *KeyChangeConsumer) onCrossSigningMessage(m api.DeviceMessage) bool {
|
|||
output := m.CrossSigningKeyUpdate
|
||||
_, host, err := gomatrixserverlib.SplitID('@', output.UserID)
|
||||
if err != nil {
|
||||
sentry.CaptureException(err)
|
||||
logrus.WithError(err).Errorf("fedsender key change consumer: user ID parse failure")
|
||||
return true
|
||||
}
|
||||
|
|
@ -176,12 +184,14 @@ func (t *KeyChangeConsumer) onCrossSigningMessage(m api.DeviceMessage) bool {
|
|||
WantMembership: "join",
|
||||
}, &queryRes)
|
||||
if err != nil {
|
||||
sentry.CaptureException(err)
|
||||
logger.WithError(err).Error("fedsender key change consumer: failed to calculate joined rooms for user")
|
||||
return true
|
||||
}
|
||||
// send this key change to all servers who share rooms with this user.
|
||||
destinations, err := t.db.GetJoinedHostsForRooms(t.ctx, queryRes.RoomIDs, true)
|
||||
if err != nil {
|
||||
sentry.CaptureException(err)
|
||||
logger.WithError(err).Error("fedsender key change consumer: failed to calculate joined hosts for rooms user is in")
|
||||
return true
|
||||
}
|
||||
|
|
@ -196,6 +206,7 @@ func (t *KeyChangeConsumer) onCrossSigningMessage(m api.DeviceMessage) bool {
|
|||
Origin: string(t.serverName),
|
||||
}
|
||||
if edu.Content, err = json.Marshal(output); err != nil {
|
||||
sentry.CaptureException(err)
|
||||
logger.WithError(err).Error("fedsender key change consumer: failed to marshal output, dropping")
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,6 +81,14 @@ func (t *OutputReceiptConsumer) onMessage(ctx context.Context, msgs []*nats.Msg)
|
|||
Type: msg.Header.Get("type"),
|
||||
}
|
||||
|
||||
switch receipt.Type {
|
||||
case "m.read":
|
||||
// These are allowed to be sent over federation
|
||||
case "m.read.private", "m.fully_read":
|
||||
// These must not be sent over federation
|
||||
return true
|
||||
}
|
||||
|
||||
// only send receipt events which originated from us
|
||||
_, receiptServerName, err := gomatrixserverlib.SplitID('@', receipt.UserID)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -18,16 +18,18 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/nats-io/nats.go"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/matrix-org/dendrite/federationapi/queue"
|
||||
"github.com/matrix-org/dendrite/federationapi/storage"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/setup/jetstream"
|
||||
"github.com/matrix-org/dendrite/setup/process"
|
||||
syncTypes "github.com/matrix-org/dendrite/syncapi/types"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/nats-io/nats.go"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// OutputSendToDeviceConsumer consumes events that originate in the clientapi.
|
||||
|
|
@ -76,6 +78,7 @@ func (t *OutputSendToDeviceConsumer) onMessage(ctx context.Context, msgs []*nats
|
|||
sender := msg.Header.Get("sender")
|
||||
_, originServerName, err := gomatrixserverlib.SplitID('@', sender)
|
||||
if err != nil {
|
||||
sentry.CaptureException(err)
|
||||
log.WithError(err).WithField("user_id", sender).Error("Failed to extract domain from send-to-device sender")
|
||||
return true
|
||||
}
|
||||
|
|
@ -85,12 +88,14 @@ func (t *OutputSendToDeviceConsumer) onMessage(ctx context.Context, msgs []*nats
|
|||
// Extract the send-to-device event from msg.
|
||||
var ote syncTypes.OutputSendToDeviceEvent
|
||||
if err = json.Unmarshal(msg.Data, &ote); err != nil {
|
||||
sentry.CaptureException(err)
|
||||
log.WithError(err).Errorf("output log: message parse failed (expected send-to-device)")
|
||||
return true
|
||||
}
|
||||
|
||||
_, destServerName, err := gomatrixserverlib.SplitID('@', ote.UserID)
|
||||
if err != nil {
|
||||
sentry.CaptureException(err)
|
||||
log.WithError(err).WithField("user_id", ote.UserID).Error("Failed to extract domain from send-to-device destination")
|
||||
return true
|
||||
}
|
||||
|
|
@ -116,6 +121,7 @@ func (t *OutputSendToDeviceConsumer) onMessage(ctx context.Context, msgs []*nats
|
|||
},
|
||||
}
|
||||
if edu.Content, err = json.Marshal(tdm); err != nil {
|
||||
sentry.CaptureException(err)
|
||||
log.WithError(err).Error("failed to marshal EDU JSON")
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
|
@ -307,11 +308,13 @@ func (oqs *OutgoingQueues) SendEDU(
|
|||
|
||||
ephemeralJSON, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
sentry.CaptureException(err)
|
||||
return fmt.Errorf("json.Marshal: %w", err)
|
||||
}
|
||||
|
||||
nid, err := oqs.db.StoreJSON(oqs.process.Context(), string(ephemeralJSON))
|
||||
if err != nil {
|
||||
sentry.CaptureException(err)
|
||||
return fmt.Errorf("sendevent: oqs.db.StoreJSON: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
"github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/util"
|
||||
|
|
@ -95,7 +96,10 @@ func fetchEvent(ctx context.Context, rsAPI api.FederationRoomserverAPI, eventID
|
|||
}
|
||||
|
||||
if len(eventsResponse.Events) == 0 {
|
||||
return nil, &util.JSONResponse{Code: http.StatusNotFound, JSON: nil}
|
||||
return nil, &util.JSONResponse{
|
||||
Code: http.StatusNotFound,
|
||||
JSON: jsonerror.NotFound("Event not found"),
|
||||
}
|
||||
}
|
||||
|
||||
return eventsResponse.Events[0].Event, nil
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
|
@ -350,6 +351,7 @@ func (t *txnReq) processEDUs(ctx context.Context) {
|
|||
for deviceID, message := range byUser {
|
||||
// TODO: check that the user and the device actually exist here
|
||||
if err := t.producer.SendToDevice(ctx, directPayload.Sender, userID, deviceID, directPayload.Type, message); err != nil {
|
||||
sentry.CaptureException(err)
|
||||
util.GetLogger(ctx).WithError(err).WithFields(logrus.Fields{
|
||||
"sender": directPayload.Sender,
|
||||
"user_id": userID,
|
||||
|
|
@ -360,6 +362,7 @@ func (t *txnReq) processEDUs(ctx context.Context) {
|
|||
}
|
||||
case gomatrixserverlib.MDeviceListUpdate:
|
||||
if err := t.producer.SendDeviceListUpdate(ctx, e.Content, t.Origin); err != nil {
|
||||
sentry.CaptureException(err)
|
||||
util.GetLogger(ctx).WithError(err).Error("failed to InputDeviceListUpdate")
|
||||
}
|
||||
case gomatrixserverlib.MReceipt:
|
||||
|
|
@ -395,6 +398,7 @@ func (t *txnReq) processEDUs(ctx context.Context) {
|
|||
}
|
||||
case types.MSigningKeyUpdate:
|
||||
if err := t.producer.SendSigningKeyUpdate(ctx, e.Content, t.Origin); err != nil {
|
||||
sentry.CaptureException(err)
|
||||
logrus.WithError(err).Errorf("Failed to process signing key update")
|
||||
}
|
||||
case gomatrixserverlib.MPresence:
|
||||
|
|
|
|||
|
|
@ -135,23 +135,24 @@ func getState(
|
|||
return nil, nil, &resErr
|
||||
}
|
||||
|
||||
if !response.StateKnown {
|
||||
switch {
|
||||
case !response.RoomExists:
|
||||
return nil, nil, &util.JSONResponse{
|
||||
Code: http.StatusNotFound,
|
||||
JSON: jsonerror.NotFound("Room not found"),
|
||||
}
|
||||
case !response.StateKnown:
|
||||
return nil, nil, &util.JSONResponse{
|
||||
Code: http.StatusNotFound,
|
||||
JSON: jsonerror.NotFound("State not known"),
|
||||
}
|
||||
}
|
||||
if response.IsRejected {
|
||||
case response.IsRejected:
|
||||
return nil, nil, &util.JSONResponse{
|
||||
Code: http.StatusNotFound,
|
||||
JSON: jsonerror.NotFound("Event not found"),
|
||||
}
|
||||
}
|
||||
|
||||
if !response.RoomExists {
|
||||
return nil, nil, &util.JSONResponse{Code: http.StatusNotFound, JSON: nil}
|
||||
}
|
||||
|
||||
return response.StateEvents, response.AuthChainEvents, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,27 +70,27 @@ func (d *Database) UpdateRoom(
|
|||
) (joinedHosts []types.JoinedHost, err error) {
|
||||
err = d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||
if purgeRoomFirst {
|
||||
// If the event is a create event then we'll delete all of the existing
|
||||
// data for the room. The only reason that a create event would be replayed
|
||||
// to us in this way is if we're about to receive the entire room state.
|
||||
if err = d.FederationJoinedHosts.DeleteJoinedHostsForRoom(ctx, txn, roomID); err != nil {
|
||||
return fmt.Errorf("d.FederationJoinedHosts.DeleteJoinedHosts: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
joinedHosts, err = d.FederationJoinedHosts.SelectJoinedHostsWithTx(ctx, txn, roomID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, add := range addHosts {
|
||||
err = d.FederationJoinedHosts.InsertJoinedHosts(ctx, txn, roomID, add.MemberEventID, add.ServerName)
|
||||
if err != nil {
|
||||
for _, add := range addHosts {
|
||||
if err = d.FederationJoinedHosts.InsertJoinedHosts(ctx, txn, roomID, add.MemberEventID, add.ServerName); err != nil {
|
||||
return err
|
||||
}
|
||||
joinedHosts = append(joinedHosts, add)
|
||||
}
|
||||
} else {
|
||||
if joinedHosts, err = d.FederationJoinedHosts.SelectJoinedHostsWithTx(ctx, txn, roomID); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, add := range addHosts {
|
||||
if err = d.FederationJoinedHosts.InsertJoinedHosts(ctx, txn, roomID, add.MemberEventID, add.ServerName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = d.FederationJoinedHosts.DeleteJoinedHosts(ctx, txn, removeHosts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = d.FederationJoinedHosts.DeleteJoinedHosts(ctx, txn, removeHosts); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
|
|
|||
|
|
@ -35,8 +35,9 @@ type AccountData struct {
|
|||
}
|
||||
|
||||
type ReadMarkerJSON struct {
|
||||
FullyRead string `json:"m.fully_read"`
|
||||
Read string `json:"m.read"`
|
||||
FullyRead string `json:"m.fully_read"`
|
||||
Read string `json:"m.read"`
|
||||
ReadPrivate string `json:"m.read.private"`
|
||||
}
|
||||
|
||||
// NotificationData contains statistics about notifications, sent from
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
)
|
||||
|
||||
|
|
@ -30,7 +28,7 @@ func IsServerAllowed(
|
|||
historyVisibility := HistoryVisibilityForRoom(authEvents)
|
||||
|
||||
// 1. If the history_visibility was set to world_readable, allow.
|
||||
if historyVisibility == "world_readable" {
|
||||
if historyVisibility == gomatrixserverlib.HistoryVisibilityWorldReadable {
|
||||
return true
|
||||
}
|
||||
// 2. If the user's membership was join, allow.
|
||||
|
|
@ -39,12 +37,12 @@ func IsServerAllowed(
|
|||
return true
|
||||
}
|
||||
// 3. If history_visibility was set to shared, and the user joined the room at any point after the event was sent, allow.
|
||||
if historyVisibility == "shared" && serverCurrentlyInRoom {
|
||||
if historyVisibility == gomatrixserverlib.HistoryVisibilityShared && serverCurrentlyInRoom {
|
||||
return true
|
||||
}
|
||||
// 4. If the user's membership was invite, and the history_visibility was set to invited, allow.
|
||||
invitedUserExists := IsAnyUserOnServerWithMembership(serverName, authEvents, gomatrixserverlib.Invite)
|
||||
if invitedUserExists && historyVisibility == "invited" {
|
||||
if invitedUserExists && historyVisibility == gomatrixserverlib.HistoryVisibilityInvited {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -52,27 +50,16 @@ func IsServerAllowed(
|
|||
return false
|
||||
}
|
||||
|
||||
func HistoryVisibilityForRoom(authEvents []*gomatrixserverlib.Event) string {
|
||||
func HistoryVisibilityForRoom(authEvents []*gomatrixserverlib.Event) gomatrixserverlib.HistoryVisibility {
|
||||
// https://matrix.org/docs/spec/client_server/r0.6.0#id87
|
||||
// By default if no history_visibility is set, or if the value is not understood, the visibility is assumed to be shared.
|
||||
visibility := "shared"
|
||||
knownStates := []string{"invited", "joined", "shared", "world_readable"}
|
||||
visibility := gomatrixserverlib.HistoryVisibilityShared
|
||||
for _, ev := range authEvents {
|
||||
if ev.Type() != gomatrixserverlib.MRoomHistoryVisibility {
|
||||
continue
|
||||
}
|
||||
// TODO: This should be HistoryVisibilityContent to match things like 'MemberContent'. Do this when moving to GMSL
|
||||
content := struct {
|
||||
HistoryVisibility string `json:"history_visibility"`
|
||||
}{}
|
||||
if err := json.Unmarshal(ev.Content(), &content); err != nil {
|
||||
break // value is not understood
|
||||
}
|
||||
for _, s := range knownStates {
|
||||
if s == content.HistoryVisibility {
|
||||
visibility = s
|
||||
break
|
||||
}
|
||||
if vis, err := ev.HistoryVisibility(); err == nil {
|
||||
visibility = vis
|
||||
}
|
||||
}
|
||||
return visibility
|
||||
|
|
@ -80,6 +67,9 @@ func HistoryVisibilityForRoom(authEvents []*gomatrixserverlib.Event) string {
|
|||
|
||||
func IsAnyUserOnServerWithMembership(serverName gomatrixserverlib.ServerName, authEvents []*gomatrixserverlib.Event, wantMembership string) bool {
|
||||
for _, ev := range authEvents {
|
||||
if ev.Type() != gomatrixserverlib.MRoomMember {
|
||||
continue
|
||||
}
|
||||
membership, err := ev.Membership()
|
||||
if err != nil || membership != wantMembership {
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ func slowGetHistoryVisibilityState(
|
|||
func ScanEventTree(
|
||||
ctx context.Context, db storage.Database, info *types.RoomInfo, front []string, visited map[string]bool, limit int,
|
||||
serverName gomatrixserverlib.ServerName,
|
||||
) ([]types.EventNID, error) {
|
||||
) ([]types.EventNID, map[string]struct{}, error) {
|
||||
var resultNIDs []types.EventNID
|
||||
var err error
|
||||
var allowed bool
|
||||
|
|
@ -345,6 +345,7 @@ func ScanEventTree(
|
|||
|
||||
var checkedServerInRoom bool
|
||||
var isServerInRoom bool
|
||||
redactEventIDs := make(map[string]struct{})
|
||||
|
||||
// Loop through the event IDs to retrieve the requested events and go
|
||||
// through the whole tree (up to the provided limit) using the events'
|
||||
|
|
@ -358,7 +359,7 @@ BFSLoop:
|
|||
// Retrieve the events to process from the database.
|
||||
events, err = db.EventsFromIDs(ctx, front)
|
||||
if err != nil {
|
||||
return resultNIDs, err
|
||||
return resultNIDs, redactEventIDs, err
|
||||
}
|
||||
|
||||
if !checkedServerInRoom && len(events) > 0 {
|
||||
|
|
@ -395,16 +396,16 @@ BFSLoop:
|
|||
)
|
||||
// drop the error, as we will often error at the DB level if we don't have the prev_event itself. Let's
|
||||
// just return what we have.
|
||||
return resultNIDs, nil
|
||||
return resultNIDs, redactEventIDs, nil
|
||||
}
|
||||
|
||||
// If the event hasn't been seen before and the HS
|
||||
// requesting to retrieve it is allowed to do so, add it to
|
||||
// the list of events to retrieve.
|
||||
if allowed {
|
||||
next = append(next, pre)
|
||||
} else {
|
||||
next = append(next, pre)
|
||||
if !allowed {
|
||||
util.GetLogger(ctx).WithField("server", serverName).WithField("event_id", pre).Info("Not allowed to see event")
|
||||
redactEventIDs[pre] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -413,7 +414,7 @@ BFSLoop:
|
|||
front = next
|
||||
}
|
||||
|
||||
return resultNIDs, err
|
||||
return resultNIDs, redactEventIDs, err
|
||||
}
|
||||
|
||||
func QueryLatestEventsAndState(
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ func (r *Backfiller) PerformBackfill(
|
|||
}
|
||||
|
||||
// Scan the event tree for events to send back.
|
||||
resultNIDs, err := helpers.ScanEventTree(ctx, r.DB, info, front, visited, request.Limit, request.ServerName)
|
||||
resultNIDs, redactEventIDs, err := helpers.ScanEventTree(ctx, r.DB, info, front, visited, request.Limit, request.ServerName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -95,6 +95,9 @@ func (r *Backfiller) PerformBackfill(
|
|||
}
|
||||
|
||||
for _, event := range loadedEvents {
|
||||
if _, ok := redactEventIDs[event.EventID()]; ok {
|
||||
event.Redact()
|
||||
}
|
||||
response.Events = append(response.Events, event.Headered(info.RoomVersion))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -453,7 +453,7 @@ func (r *Queryer) QueryMissingEvents(
|
|||
return fmt.Errorf("missing RoomInfo for room %s", events[0].RoomID())
|
||||
}
|
||||
|
||||
resultNIDs, err := helpers.ScanEventTree(ctx, r.DB, info, front, visited, request.Limit, request.ServerName)
|
||||
resultNIDs, redactEventIDs, err := helpers.ScanEventTree(ctx, r.DB, info, front, visited, request.Limit, request.ServerName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -470,7 +470,9 @@ func (r *Queryer) QueryMissingEvents(
|
|||
if verr != nil {
|
||||
return verr
|
||||
}
|
||||
|
||||
if _, ok := redactEventIDs[event.EventID()]; ok {
|
||||
event.Redact()
|
||||
}
|
||||
response.Events = append(response.Events, event.Headered(roomVersion))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,10 +21,11 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/lib/pq"
|
||||
"github.com/matrix-org/util"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/dendrite/roomserver/storage/tables"
|
||||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
"github.com/matrix-org/util"
|
||||
)
|
||||
|
||||
const stateSnapshotSchema = `
|
||||
|
|
@ -91,6 +92,7 @@ const bulkSelectStateForHistoryVisibilitySQL = `
|
|||
WHERE state_snapshot_nid = $1
|
||||
)
|
||||
)
|
||||
ORDER BY depth ASC
|
||||
) AS roomserver_events
|
||||
INNER JOIN roomserver_event_state_keys
|
||||
ON roomserver_events.event_state_key_nid = roomserver_event_state_keys.event_state_key_nid
|
||||
|
|
|
|||
|
|
@ -19,11 +19,13 @@ import (
|
|||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/dendrite/syncapi/storage"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/dendrite/syncapi/storage"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
@ -189,7 +191,7 @@ func visibilityForEvents(
|
|||
UserID: userID,
|
||||
}, membershipResp)
|
||||
if err != nil {
|
||||
return result, err
|
||||
logrus.WithError(err).Error("visibilityForEvents: failed to fetch membership at event, defaulting to 'leave'")
|
||||
}
|
||||
|
||||
// Create a map from eventID -> eventVisibility
|
||||
|
|
|
|||
102
syncapi/routing/getevent.go
Normal file
102
syncapi/routing/getevent.go
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
// 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 routing
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||
"github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/dendrite/setup/config"
|
||||
"github.com/matrix-org/dendrite/syncapi/internal"
|
||||
"github.com/matrix-org/dendrite/syncapi/storage"
|
||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
)
|
||||
|
||||
// GetEvent implements
|
||||
//
|
||||
// GET /_matrix/client/r0/rooms/{roomId}/event/{eventId}
|
||||
//
|
||||
// https://spec.matrix.org/v1.4/client-server-api/#get_matrixclientv3roomsroomideventeventid
|
||||
func GetEvent(
|
||||
req *http.Request,
|
||||
device *userapi.Device,
|
||||
roomID string,
|
||||
eventID string,
|
||||
cfg *config.SyncAPI,
|
||||
syncDB storage.Database,
|
||||
rsAPI api.SyncRoomserverAPI,
|
||||
) util.JSONResponse {
|
||||
ctx := req.Context()
|
||||
db, err := syncDB.NewDatabaseTransaction(ctx)
|
||||
logger := util.GetLogger(ctx).WithFields(logrus.Fields{
|
||||
"event_id": eventID,
|
||||
"room_id": roomID,
|
||||
})
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("GetEvent: syncDB.NewDatabaseTransaction failed")
|
||||
return jsonerror.InternalServerError()
|
||||
}
|
||||
|
||||
events, err := db.Events(ctx, []string{eventID})
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("GetEvent: syncDB.Events failed")
|
||||
return jsonerror.InternalServerError()
|
||||
}
|
||||
|
||||
// The requested event does not exist in our database
|
||||
if len(events) == 0 {
|
||||
logger.Debugf("GetEvent: requested event doesn't exist locally")
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusNotFound,
|
||||
JSON: jsonerror.NotFound("The event was not found or you do not have permission to read this event"),
|
||||
}
|
||||
}
|
||||
|
||||
// If the request is coming from an appservice, get the user from the request
|
||||
userID := device.UserID
|
||||
if asUserID := req.FormValue("user_id"); device.AppserviceID != "" && asUserID != "" {
|
||||
userID = asUserID
|
||||
}
|
||||
|
||||
// Apply history visibility to determine if the user is allowed to view the event
|
||||
events, err = internal.ApplyHistoryVisibilityFilter(ctx, db, rsAPI, events, nil, userID, "event")
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("GetEvent: internal.ApplyHistoryVisibilityFilter failed")
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: jsonerror.InternalServerError(),
|
||||
}
|
||||
}
|
||||
|
||||
// We only ever expect there to be one event
|
||||
if len(events) != 1 {
|
||||
// 0 events -> not allowed to view event; > 1 events -> something that shouldn't happen
|
||||
logger.WithField("event_count", len(events)).Debug("GetEvent: can't return the requested event")
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusNotFound,
|
||||
JSON: jsonerror.NotFound("The event was not found or you do not have permission to read this event"),
|
||||
}
|
||||
}
|
||||
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: gomatrixserverlib.HeaderedToClientEvent(events[0], gomatrixserverlib.FormatAll),
|
||||
}
|
||||
}
|
||||
|
|
@ -60,6 +60,16 @@ func Setup(
|
|||
return OnIncomingMessagesRequest(req, syncDB, vars["roomID"], device, rsAPI, cfg, srp, lazyLoadCache)
|
||||
})).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/event/{eventID}",
|
||||
httputil.MakeAuthAPI("rooms_get_event", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
return GetEvent(req, device, vars["roomID"], vars["eventID"], cfg, syncDB, rsAPI)
|
||||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/user/{userId}/filter",
|
||||
httputil.MakeAuthAPI("put_filter", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
|
|
|
|||
|
|
@ -360,34 +360,50 @@ func (d *DatabaseTransaction) GetStateDeltas(
|
|||
newlyJoinedRooms := make(map[string]bool, len(state))
|
||||
for roomID, stateStreamEvents := range state {
|
||||
for _, ev := range stateStreamEvents {
|
||||
if membership, prevMembership := getMembershipFromEvent(ev.Event, userID); membership != "" {
|
||||
if membership == gomatrixserverlib.Join && prevMembership != membership {
|
||||
// send full room state down instead of a delta
|
||||
// Look for our membership in the state events and skip over any
|
||||
// membership events that are not related to us.
|
||||
membership, prevMembership := getMembershipFromEvent(ev.Event, userID)
|
||||
if membership == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if membership == gomatrixserverlib.Join {
|
||||
// If our membership is now join but the previous membership wasn't
|
||||
// then this is a "join transition", so we'll insert this room.
|
||||
if prevMembership != membership {
|
||||
// Get the full room state, as we'll send that down for a newly
|
||||
// joined room instead of a delta.
|
||||
var s []types.StreamEvent
|
||||
s, err = d.currentStateStreamEventsForRoom(ctx, roomID, stateFilter)
|
||||
if err != nil {
|
||||
if s, err = d.currentStateStreamEventsForRoom(ctx, roomID, stateFilter); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
continue
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Add the information for this room into the state so that
|
||||
// it will get added with all of the rest of the joined rooms.
|
||||
state[roomID] = s
|
||||
newlyJoinedRooms[roomID] = true
|
||||
continue // we'll add this room in when we do joined rooms
|
||||
}
|
||||
|
||||
deltas = append(deltas, types.StateDelta{
|
||||
Membership: membership,
|
||||
MembershipPos: ev.StreamPosition,
|
||||
StateEvents: d.StreamEventsToEvents(device, stateStreamEvents),
|
||||
RoomID: roomID,
|
||||
})
|
||||
break
|
||||
// We won't add joined rooms into the delta at this point as they
|
||||
// are added later on.
|
||||
continue
|
||||
}
|
||||
|
||||
deltas = append(deltas, types.StateDelta{
|
||||
Membership: membership,
|
||||
MembershipPos: ev.StreamPosition,
|
||||
StateEvents: d.StreamEventsToEvents(device, stateStreamEvents),
|
||||
RoomID: roomID,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Add in currently joined rooms
|
||||
// Finally, add in currently joined rooms, including those from the
|
||||
// join transitions above.
|
||||
for _, joinedRoomID := range joinedRoomIDs {
|
||||
deltas = append(deltas, types.StateDelta{
|
||||
Membership: gomatrixserverlib.Join,
|
||||
|
|
|
|||
|
|
@ -67,6 +67,10 @@ func (p *ReceiptStreamProvider) IncrementalSync(
|
|||
if _, ok := req.IgnoredUsers.List[receipt.UserID]; ok {
|
||||
continue
|
||||
}
|
||||
// Don't send private read receipts to other users
|
||||
if receipt.Type == "m.read.private" && req.Device.UserID != receipt.UserID {
|
||||
continue
|
||||
}
|
||||
receiptsByRoom[receipt.RoomID] = append(receiptsByRoom[receipt.RoomID], receipt)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ Notifications can be viewed with GET /notifications
|
|||
|
||||
# More flakey
|
||||
|
||||
If remote user leaves room we no longer receive device updates
|
||||
Guest users can join guest_access rooms
|
||||
|
||||
# This will fail in HTTP API mode, so blacklisted for now
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@ Alternative server names do not cause a routing loop
|
|||
Events whose auth_events are in the wrong room do not mess up the room state
|
||||
Inbound federation can return events
|
||||
Inbound federation can return missing events for world_readable visibility
|
||||
Inbound federation can return missing events for invite visibility
|
||||
Inbound federation can return missing events for invited visibility
|
||||
Inbound federation can get public room list
|
||||
PUT /rooms/:room_id/redact/:event_id/:txn_id as power user redacts message
|
||||
PUT /rooms/:room_id/redact/:event_id/:txn_id as original message sender redacts message
|
||||
|
|
@ -742,3 +742,5 @@ User in private room doesn't appear in user directory
|
|||
User joining then leaving public room appears and dissappears from directory
|
||||
User in remote room doesn't appear in user directory after server left room
|
||||
User in shared private room does appear in user directory until leave
|
||||
Existing members see new member's presence
|
||||
Inbound federation can return missing events for joined visibility
|
||||
|
|
@ -838,6 +838,8 @@ func (a *UserInternalAPI) QueryAccountByPassword(ctx context.Context, req *api.Q
|
|||
return nil
|
||||
case bcrypt.ErrMismatchedHashAndPassword: // user exists, but password doesn't match
|
||||
return nil
|
||||
case bcrypt.ErrHashTooShort: // user exists, but probably a passwordless account
|
||||
return nil
|
||||
default:
|
||||
res.Exists = true
|
||||
res.Account = acc
|
||||
|
|
|
|||
|
|
@ -74,6 +74,9 @@ func (d *Database) GetAccountByPassword(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(hash) == 0 && len(plaintextPassword) > 0 {
|
||||
return nil, bcrypt.ErrHashTooShort
|
||||
}
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(plaintextPassword)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,6 +151,33 @@ func TestQueryProfile(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
// TestPasswordlessLoginFails ensures that a passwordless account cannot
|
||||
// be logged into using an arbitrary password (effectively a regression test
|
||||
// for https://github.com/matrix-org/dendrite/issues/2780).
|
||||
func TestPasswordlessLoginFails(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
userAPI, accountDB, close := MustMakeInternalAPI(t, apiTestOpts{}, dbType)
|
||||
defer close()
|
||||
_, err := accountDB.CreateAccount(ctx, "auser", "", "", api.AccountTypeAppService)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to make account: %s", err)
|
||||
}
|
||||
|
||||
userReq := &api.QueryAccountByPasswordRequest{
|
||||
Localpart: "auser",
|
||||
PlaintextPassword: "apassword",
|
||||
}
|
||||
userRes := &api.QueryAccountByPasswordResponse{}
|
||||
if err := userAPI.QueryAccountByPassword(ctx, userReq, userRes); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if userRes.Exists || userRes.Account != nil {
|
||||
t.Fatal("QueryAccountByPassword should not return correctly for a passwordless account")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestLoginToken(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue