From a56752f3f6650f58b16e90fd5b6ce6a438d23869 Mon Sep 17 00:00:00 2001 From: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> Date: Tue, 17 Jul 2018 08:45:30 -0700 Subject: [PATCH] implement AS timestamp massaging (#542) --- .../dendrite/clientapi/auth/auth.go | 14 ++++++++- .../dendrite/clientapi/routing/createroom.go | 20 ++++++++----- .../dendrite/clientapi/routing/joinroom.go | 7 ++--- .../dendrite/clientapi/routing/membership.go | 11 ++++--- .../dendrite/clientapi/routing/profile.go | 9 +++--- .../dendrite/clientapi/routing/sendevent.go | 2 +- .../dendrite/clientapi/threepid/invites.go | 12 ++++---- .../matrix-org/dendrite/common/events.go | 29 ++++++++++++++++--- .../dendrite/federationapi/routing/join.go | 3 +- .../dendrite/federationapi/routing/leave.go | 2 +- .../dendrite/federationapi/routing/routing.go | 2 +- .../federationapi/routing/threepid.go | 21 +++++++------- 12 files changed, 82 insertions(+), 50 deletions(-) diff --git a/src/github.com/matrix-org/dendrite/clientapi/auth/auth.go b/src/github.com/matrix-org/dendrite/clientapi/auth/auth.go index 5aaf0905..80df0e72 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/auth/auth.go +++ b/src/github.com/matrix-org/dendrite/clientapi/auth/auth.go @@ -68,7 +68,7 @@ func VerifyUserFromRequest( // Try to find local user from device database dev, devErr := verifyAccessToken(req, data.DeviceDB) if devErr == nil { - return dev, nil + return dev, verifyUserParameters(req) } // Try to find the Application Service user @@ -134,6 +134,18 @@ func VerifyUserFromRequest( } } +// verifyUserParameters ensures that a request coming from a regular user is not +// using any query parameters reserved for an application service +func verifyUserParameters(req *http.Request) *util.JSONResponse { + if req.URL.Query().Get("ts") != "" { + return &util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: jsonerror.Unknown("parameter 'ts' not allowed without valid parameter 'access_token'"), + } + } + return nil +} + // verifyAccessToken verifies that an access token was supplied in the given HTTP request // and returns the device it corresponds to. Returns resErr (an error response which can be // sent to the client) if the token is invalid or there was a problem querying the database. diff --git a/src/github.com/matrix-org/dendrite/clientapi/routing/createroom.go b/src/github.com/matrix-org/dendrite/clientapi/routing/createroom.go index 7b32ca50..a43d0080 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/routing/createroom.go +++ b/src/github.com/matrix-org/dendrite/clientapi/routing/createroom.go @@ -18,7 +18,6 @@ import ( "fmt" "net/http" "strings" - "time" "github.com/matrix-org/dendrite/roomserver/api" @@ -113,7 +112,8 @@ type fledglingEvent struct { } // CreateRoom implements /createRoom -func CreateRoom(req *http.Request, device *authtypes.Device, +func CreateRoom( + req *http.Request, device *authtypes.Device, cfg config.Dendrite, producer *producers.RoomserverProducer, accountDB *accounts.Database, aliasAPI api.RoomserverAliasAPI, ) util.JSONResponse { @@ -125,7 +125,8 @@ func CreateRoom(req *http.Request, device *authtypes.Device, // createRoom implements /createRoom // nolint: gocyclo -func createRoom(req *http.Request, device *authtypes.Device, +func createRoom( + req *http.Request, device *authtypes.Device, cfg config.Dendrite, roomID string, producer *producers.RoomserverProducer, accountDB *accounts.Database, aliasAPI api.RoomserverAliasAPI, ) util.JSONResponse { @@ -248,7 +249,7 @@ func createRoom(req *http.Request, device *authtypes.Device, builder.PrevEvents = []gomatrixserverlib.EventReference{builtEvents[i-1].EventReference()} } var ev *gomatrixserverlib.Event - ev, err = buildEvent(&builder, &authEvents, cfg) + ev, err = buildEvent(req, &builder, &authEvents, cfg) if err != nil { return httputil.LogThenError(req, err) } @@ -307,9 +308,12 @@ func createRoom(req *http.Request, device *authtypes.Device, } // buildEvent fills out auth_events for the builder then builds the event -func buildEvent(builder *gomatrixserverlib.EventBuilder, +func buildEvent( + req *http.Request, + builder *gomatrixserverlib.EventBuilder, provider gomatrixserverlib.AuthEventProvider, - cfg config.Dendrite) (*gomatrixserverlib.Event, error) { + cfg config.Dendrite, +) (*gomatrixserverlib.Event, error) { eventsNeeded, err := gomatrixserverlib.StateNeededForEventBuilder(builder) if err != nil { @@ -321,8 +325,8 @@ func buildEvent(builder *gomatrixserverlib.EventBuilder, } builder.AuthEvents = refs eventID := fmt.Sprintf("$%s:%s", util.RandomString(16), cfg.Matrix.ServerName) - now := time.Now() - event, err := builder.Build(eventID, now, cfg.Matrix.ServerName, cfg.Matrix.KeyID, cfg.Matrix.PrivateKey) + eventTime := common.ParseTSParam(req) + event, err := builder.Build(eventID, eventTime, cfg.Matrix.ServerName, cfg.Matrix.KeyID, cfg.Matrix.PrivateKey) if err != nil { return nil, fmt.Errorf("cannot build event %s : Builder failed to build. %s", builder.Type, err) } diff --git a/src/github.com/matrix-org/dendrite/clientapi/routing/joinroom.go b/src/github.com/matrix-org/dendrite/clientapi/routing/joinroom.go index c1bd251b..84c1f24c 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/routing/joinroom.go +++ b/src/github.com/matrix-org/dendrite/clientapi/routing/joinroom.go @@ -18,7 +18,6 @@ import ( "fmt" "net/http" "strings" - "time" "github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/clientapi/auth/storage/accounts" @@ -215,7 +214,7 @@ func (r joinRoomReq) joinRoomUsingServers( } var queryRes roomserverAPI.QueryLatestEventsAndStateResponse - event, err := common.BuildEvent(r.req.Context(), &eb, r.cfg, r.queryAPI, &queryRes) + event, err := common.BuildEvent(r.req, &eb, r.cfg, r.queryAPI, &queryRes) if err == nil { if _, err = r.producer.SendEvents(r.req.Context(), []gomatrixserverlib.Event{*event}, r.cfg.Matrix.ServerName, nil); err != nil { return httputil.LogThenError(r.req, err) @@ -285,10 +284,10 @@ func (r joinRoomReq) joinRoomUsingServer(roomID string, server gomatrixserverlib return nil, err } - now := time.Now() eventID := fmt.Sprintf("$%s:%s", util.RandomString(16), r.cfg.Matrix.ServerName) + eventTime := common.ParseTSParam(r.req) event, err := respMakeJoin.JoinEvent.Build( - eventID, now, r.cfg.Matrix.ServerName, r.cfg.Matrix.KeyID, r.cfg.Matrix.PrivateKey, + eventID, eventTime, r.cfg.Matrix.ServerName, r.cfg.Matrix.KeyID, r.cfg.Matrix.PrivateKey, ) if err != nil { res := httputil.LogThenError(r.req, err) diff --git a/src/github.com/matrix-org/dendrite/clientapi/routing/membership.go b/src/github.com/matrix-org/dendrite/clientapi/routing/membership.go index 05ebbdd8..cf4d075e 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/routing/membership.go +++ b/src/github.com/matrix-org/dendrite/clientapi/routing/membership.go @@ -48,8 +48,7 @@ func SendMembership( } inviteStored, err := threepid.CheckAndProcessInvite( - req.Context(), - device, &body, cfg, queryAPI, accountDB, producer, membership, roomID, + req, device, &body, cfg, queryAPI, accountDB, producer, membership, roomID, ) if err == threepid.ErrMissingParameter { return util.JSONResponse{ @@ -81,7 +80,7 @@ func SendMembership( } event, err := buildMembershipEvent( - req.Context(), body, accountDB, device, membership, roomID, cfg, queryAPI, + req, body, accountDB, device, membership, roomID, cfg, queryAPI, ) if err == errMissingUserID { return util.JSONResponse{ @@ -110,7 +109,7 @@ func SendMembership( } func buildMembershipEvent( - ctx context.Context, + req *http.Request, body threepid.MembershipRequest, accountDB *accounts.Database, device *authtypes.Device, membership string, roomID string, cfg config.Dendrite, queryAPI api.RoomserverQueryAPI, @@ -120,7 +119,7 @@ func buildMembershipEvent( return nil, err } - profile, err := loadProfile(ctx, stateKey, cfg, accountDB) + profile, err := loadProfile(req.Context(), stateKey, cfg, accountDB) if err != nil { return nil, err } @@ -148,7 +147,7 @@ func buildMembershipEvent( return nil, err } - return common.BuildEvent(ctx, &builder, cfg, queryAPI, nil) + return common.BuildEvent(req, &builder, cfg, queryAPI, nil) } // loadProfile lookups the profile of a given user from the database and returns diff --git a/src/github.com/matrix-org/dendrite/clientapi/routing/profile.go b/src/github.com/matrix-org/dendrite/clientapi/routing/profile.go index cc1180b0..35b7226a 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/routing/profile.go +++ b/src/github.com/matrix-org/dendrite/clientapi/routing/profile.go @@ -15,7 +15,6 @@ package routing import ( - "context" "database/sql" "net/http" @@ -151,7 +150,7 @@ func SetAvatarURL( AvatarURL: r.AvatarURL, } - events, err := buildMembershipEvents(req.Context(), memberships, newProfile, userID, cfg, queryAPI) + events, err := buildMembershipEvents(req, memberships, newProfile, userID, cfg, queryAPI) if err != nil { return httputil.LogThenError(req, err) } @@ -239,7 +238,7 @@ func SetDisplayName( AvatarURL: oldProfile.AvatarURL, } - events, err := buildMembershipEvents(req.Context(), memberships, newProfile, userID, cfg, queryAPI) + events, err := buildMembershipEvents(req, memberships, newProfile, userID, cfg, queryAPI) if err != nil { return httputil.LogThenError(req, err) } @@ -259,7 +258,7 @@ func SetDisplayName( } func buildMembershipEvents( - ctx context.Context, + req *http.Request, memberships []authtypes.Membership, newProfile authtypes.Profile, userID string, cfg *config.Dendrite, queryAPI api.RoomserverQueryAPI, @@ -285,7 +284,7 @@ func buildMembershipEvents( return nil, err } - event, err := common.BuildEvent(ctx, &builder, *cfg, queryAPI, nil) + event, err := common.BuildEvent(req, &builder, *cfg, queryAPI, nil) if err != nil { return nil, err } diff --git a/src/github.com/matrix-org/dendrite/clientapi/routing/sendevent.go b/src/github.com/matrix-org/dendrite/clientapi/routing/sendevent.go index 1419df40..52d2b107 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/routing/sendevent.go +++ b/src/github.com/matrix-org/dendrite/clientapi/routing/sendevent.go @@ -76,7 +76,7 @@ func SendEvent( } var queryRes api.QueryLatestEventsAndStateResponse - e, err := common.BuildEvent(req.Context(), &builder, cfg, queryAPI, &queryRes) + e, err := common.BuildEvent(req, &builder, cfg, queryAPI, &queryRes) if err == common.ErrRoomNoExists { return util.JSONResponse{ Code: http.StatusNotFound, diff --git a/src/github.com/matrix-org/dendrite/clientapi/threepid/invites.go b/src/github.com/matrix-org/dendrite/clientapi/threepid/invites.go index 23b97e93..44c0c9a1 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/threepid/invites.go +++ b/src/github.com/matrix-org/dendrite/clientapi/threepid/invites.go @@ -85,7 +85,7 @@ var ( // fills the Matrix ID in the request body so a normal invite membership event // can be emitted. func CheckAndProcessInvite( - ctx context.Context, + req *http.Request, device *authtypes.Device, body *MembershipRequest, cfg config.Dendrite, queryAPI api.RoomserverQueryAPI, db *accounts.Database, producer *producers.RoomserverProducer, membership string, roomID string, @@ -101,7 +101,7 @@ func CheckAndProcessInvite( return } - lookupRes, storeInviteRes, err := queryIDServer(ctx, db, cfg, device, body, roomID) + lookupRes, storeInviteRes, err := queryIDServer(req.Context(), db, cfg, device, body, roomID) if err != nil { return } @@ -110,7 +110,7 @@ func CheckAndProcessInvite( // No Matrix ID could be found for this 3PID, meaning that a // "m.room.third_party_invite" have to be emitted from the data in // storeInviteRes. - err = emit3PIDInviteEvent(ctx, body, storeInviteRes, device, roomID, cfg, queryAPI, producer) + err = emit3PIDInviteEvent(req, body, storeInviteRes, device, roomID, cfg, queryAPI, producer) inviteStoredOnIDServer = err == nil return @@ -325,7 +325,7 @@ func checkIDServerSignatures( // emit3PIDInviteEvent builds and sends a "m.room.third_party_invite" event. // Returns an error if something failed in the process. func emit3PIDInviteEvent( - ctx context.Context, + req *http.Request, body *MembershipRequest, res *idServerStoreInviteResponse, device *authtypes.Device, roomID string, cfg config.Dendrite, queryAPI api.RoomserverQueryAPI, producer *producers.RoomserverProducer, @@ -350,11 +350,11 @@ func emit3PIDInviteEvent( } var queryRes *api.QueryLatestEventsAndStateResponse - event, err := common.BuildEvent(ctx, builder, cfg, queryAPI, queryRes) + event, err := common.BuildEvent(req, builder, cfg, queryAPI, queryRes) if err != nil { return err } - _, err = producer.SendEvents(ctx, []gomatrixserverlib.Event{*event}, cfg.Matrix.ServerName, nil) + _, err = producer.SendEvents(req.Context(), []gomatrixserverlib.Event{*event}, cfg.Matrix.ServerName, nil) return err } diff --git a/src/github.com/matrix-org/dendrite/common/events.go b/src/github.com/matrix-org/dendrite/common/events.go index cf652b08..41022c7c 100644 --- a/src/github.com/matrix-org/dendrite/common/events.go +++ b/src/github.com/matrix-org/dendrite/common/events.go @@ -18,6 +18,8 @@ import ( "context" "errors" "fmt" + "net/http" + "strconv" "time" "github.com/matrix-org/dendrite/common/config" @@ -38,18 +40,18 @@ var ErrRoomNoExists = errors.New("Room does not exist") // the room doesn't exist // Returns an error if something else went wrong func BuildEvent( - ctx context.Context, + req *http.Request, builder *gomatrixserverlib.EventBuilder, cfg config.Dendrite, queryAPI api.RoomserverQueryAPI, queryRes *api.QueryLatestEventsAndStateResponse, ) (*gomatrixserverlib.Event, error) { - err := AddPrevEventsToEvent(ctx, builder, queryAPI, queryRes) + err := AddPrevEventsToEvent(req.Context(), builder, queryAPI, queryRes) if err != nil { return nil, err } eventID := fmt.Sprintf("$%s:%s", util.RandomString(16), cfg.Matrix.ServerName) - now := time.Now() - event, err := builder.Build(eventID, now, cfg.Matrix.ServerName, cfg.Matrix.KeyID, cfg.Matrix.PrivateKey) + eventTime := ParseTSParam(req) + event, err := builder.Build(eventID, eventTime, cfg.Matrix.ServerName, cfg.Matrix.KeyID, cfg.Matrix.PrivateKey) if err != nil { return nil, err } @@ -57,6 +59,25 @@ func BuildEvent( return &event, nil } +// ParseTSParam takes a req from an application service and parses a Time object +// from the req if it exists in the query parameters. If it doesn't exist, the +// current time is returned. +func ParseTSParam(req *http.Request) time.Time { + // Use the ts parameter's value for event time if present + tsStr := req.URL.Query().Get("ts") + if tsStr == "" { + return time.Now() + } + + // The parameter exists, parse into a Time object + ts, err := strconv.ParseInt(tsStr, 10, 64) + if err != nil { + return time.Unix(ts/1000, 0) + } + + return time.Unix(ts/1000, 0) +} + // AddPrevEventsToEvent fills out the prev_events and auth_events fields in builder func AddPrevEventsToEvent( ctx context.Context, diff --git a/src/github.com/matrix-org/dendrite/federationapi/routing/join.go b/src/github.com/matrix-org/dendrite/federationapi/routing/join.go index 7bae4e70..cb285c2e 100644 --- a/src/github.com/matrix-org/dendrite/federationapi/routing/join.go +++ b/src/github.com/matrix-org/dendrite/federationapi/routing/join.go @@ -31,7 +31,6 @@ import ( // MakeJoin implements the /make_join API func MakeJoin( - ctx context.Context, httpReq *http.Request, request *gomatrixserverlib.FederationRequest, cfg config.Dendrite, @@ -65,7 +64,7 @@ func MakeJoin( } var queryRes api.QueryLatestEventsAndStateResponse - event, err := common.BuildEvent(ctx, &builder, cfg, query, &queryRes) + event, err := common.BuildEvent(httpReq, &builder, cfg, query, &queryRes) if err == common.ErrRoomNoExists { return util.JSONResponse{ Code: http.StatusNotFound, diff --git a/src/github.com/matrix-org/dendrite/federationapi/routing/leave.go b/src/github.com/matrix-org/dendrite/federationapi/routing/leave.go index d9527a4c..c2e78fb6 100644 --- a/src/github.com/matrix-org/dendrite/federationapi/routing/leave.go +++ b/src/github.com/matrix-org/dendrite/federationapi/routing/leave.go @@ -61,7 +61,7 @@ func MakeLeave( } var queryRes api.QueryLatestEventsAndStateResponse - event, err := common.BuildEvent(httpReq.Context(), &builder, cfg, query, &queryRes) + event, err := common.BuildEvent(httpReq, &builder, cfg, query, &queryRes) if err == common.ErrRoomNoExists { return util.JSONResponse{ Code: http.StatusNotFound, diff --git a/src/github.com/matrix-org/dendrite/federationapi/routing/routing.go b/src/github.com/matrix-org/dendrite/federationapi/routing/routing.go index 2cabea6d..e3215f63 100644 --- a/src/github.com/matrix-org/dendrite/federationapi/routing/routing.go +++ b/src/github.com/matrix-org/dendrite/federationapi/routing/routing.go @@ -165,7 +165,7 @@ func Setup( roomID := vars["roomID"] userID := vars["userID"] return MakeJoin( - httpReq.Context(), httpReq, request, cfg, query, roomID, userID, + httpReq, request, cfg, query, roomID, userID, ) }, )).Methods(http.MethodGet) diff --git a/src/github.com/matrix-org/dendrite/federationapi/routing/threepid.go b/src/github.com/matrix-org/dendrite/federationapi/routing/threepid.go index bc17060c..606190a7 100644 --- a/src/github.com/matrix-org/dendrite/federationapi/routing/threepid.go +++ b/src/github.com/matrix-org/dendrite/federationapi/routing/threepid.go @@ -20,7 +20,6 @@ import ( "errors" "fmt" "net/http" - "time" "github.com/matrix-org/dendrite/clientapi/auth/storage/accounts" "github.com/matrix-org/dendrite/clientapi/httputil" @@ -70,7 +69,7 @@ func CreateInvitesFrom3PIDInvites( evs := []gomatrixserverlib.Event{} for _, inv := range body.Invites { event, err := createInviteFrom3PIDInvite( - req.Context(), queryAPI, cfg, inv, federation, accountDB, + req, queryAPI, cfg, inv, federation, accountDB, ) if err != nil { return httputil.LogThenError(req, err) @@ -135,7 +134,7 @@ func ExchangeThirdPartyInvite( } // Auth and build the event from what the remote server sent us - event, err := buildMembershipEvent(httpReq.Context(), &builder, queryAPI, cfg) + event, err := buildMembershipEvent(httpReq, &builder, queryAPI, cfg) if err == errNotInRoom { return util.JSONResponse{ Code: http.StatusNotFound, @@ -170,7 +169,7 @@ func ExchangeThirdPartyInvite( // Returns an error if there was a problem building the event or fetching the // necessary data to do so. func createInviteFrom3PIDInvite( - ctx context.Context, queryAPI api.RoomserverQueryAPI, cfg config.Dendrite, + req *http.Request, queryAPI api.RoomserverQueryAPI, cfg config.Dendrite, inv invite, federation *gomatrixserverlib.FederationClient, accountDB *accounts.Database, ) (*gomatrixserverlib.Event, error) { @@ -191,7 +190,7 @@ func createInviteFrom3PIDInvite( StateKey: &inv.MXID, } - profile, err := accountDB.GetProfileByLocalpart(ctx, localpart) + profile, err := accountDB.GetProfileByLocalpart(req.Context(), localpart) if err != nil { return nil, err } @@ -209,9 +208,9 @@ func createInviteFrom3PIDInvite( return nil, err } - event, err := buildMembershipEvent(ctx, builder, queryAPI, cfg) + event, err := buildMembershipEvent(req, builder, queryAPI, cfg) if err == errNotInRoom { - return nil, sendToRemoteServer(ctx, inv, federation, cfg, *builder) + return nil, sendToRemoteServer(req.Context(), inv, federation, cfg, *builder) } if err != nil { return nil, err @@ -226,7 +225,7 @@ func createInviteFrom3PIDInvite( // Returns errNotInRoom if the server is not in the room the invite is for. // Returns an error if something failed during the process. func buildMembershipEvent( - ctx context.Context, + req *http.Request, builder *gomatrixserverlib.EventBuilder, queryAPI api.RoomserverQueryAPI, cfg config.Dendrite, ) (*gomatrixserverlib.Event, error) { @@ -241,7 +240,7 @@ func buildMembershipEvent( StateToFetch: eventsNeeded.Tuples(), } var queryRes api.QueryLatestEventsAndStateResponse - if err = queryAPI.QueryLatestEventsAndState(ctx, &queryReq, &queryRes); err != nil { + if err = queryAPI.QueryLatestEventsAndState(req.Context(), &queryReq, &queryRes); err != nil { return nil, err } @@ -274,8 +273,8 @@ func buildMembershipEvent( builder.AuthEvents = refs eventID := fmt.Sprintf("$%s:%s", util.RandomString(16), cfg.Matrix.ServerName) - now := time.Now() - event, err := builder.Build(eventID, now, cfg.Matrix.ServerName, cfg.Matrix.KeyID, cfg.Matrix.PrivateKey) + eventTime := common.ParseTSParam(req) + event, err := builder.Build(eventID, eventTime, cfg.Matrix.ServerName, cfg.Matrix.KeyID, cfg.Matrix.PrivateKey) return &event, err }