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

[WIP] Add Presence module #1879

Closed
wants to merge 83 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
07677f9
Fix copy & paste mistake
S7evinK Jun 18, 2021
ebdae1f
Add presence routing
S7evinK Jun 18, 2021
2ae8084
Add presence to eduserver
S7evinK Jun 18, 2021
9e795ce
Add "forward" to sarama
S7evinK Jun 18, 2021
0d3a6e1
Add internal api
S7evinK Jun 18, 2021
63bba06
Make slice on creation
S7evinK Jun 18, 2021
6403c29
Add missing method to testEDUProducer
S7evinK Jun 18, 2021
8cb3ad6
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Jul 8, 2021
7f28d7a
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Jul 9, 2021
93b63c5
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Jul 11, 2021
6b80de2
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Jul 17, 2021
139817a
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Jul 23, 2021
e0f9c66
Add presence storage
S7evinK Jul 28, 2021
f74a310
Let users set/retrieve the presence status from clientapi
S7evinK Jul 28, 2021
e96b8c0
Add passing tests to whitelist
S7evinK Jul 28, 2021
0ce356c
Add configuration for the presence database
S7evinK Jul 28, 2021
3b55742
Add missing PresenceStatus type
S7evinK Jul 28, 2021
f759107
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Jul 28, 2021
bf8211b
Add missing files
S7evinK Jul 28, 2021
6f6a1d3
Make lint and tests happy
S7evinK Jul 28, 2021
9f5f41c
Fix copy&paste error
S7evinK Jul 28, 2021
62295ec
- Avoid clearing status_msg when going idle
S7evinK Jul 28, 2021
7347f40
Merge remote-tracking branch 'origin/add-presence' into add-presence
S7evinK Jul 30, 2021
9dee04c
Test
S7evinK Jul 30, 2021
0889cee
Make /sync requests work
S7evinK Jul 31, 2021
bdc30e5
Test
S7evinK Jul 31, 2021
4f204a6
Testing
S7evinK Jul 31, 2021
167c07b
Fix tests
S7evinK Jul 31, 2021
dc081bb
Update go.mod to use correct replacement
S7evinK Jul 31, 2021
3e937e4
Update go.mod..
S7evinK Jul 31, 2021
04f85e8
Merge branch 'master' into add-presence
S7evinK Jul 31, 2021
f1a19e7
Add presence federationsender
S7evinK Aug 1, 2021
040bce7
Update logging
S7evinK Aug 3, 2021
7003ae5
Add possibilty to query current max id for presence
S7evinK Aug 3, 2021
642ac3e
Add Setup method to PresenceStreamProvider
S7evinK Aug 3, 2021
4b94f52
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Aug 5, 2021
3e62a63
Add trace for new endpoints
S7evinK Aug 5, 2021
2b0261c
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Aug 9, 2021
86e387d
Fix issue with wrong timestamp
S7evinK Aug 9, 2021
d550acb
Let eduserver handle storing of presence data
S7evinK Jul 31, 2021
598308b
Store presence data on request
S7evinK Aug 10, 2021
ca7a6c9
Handle incomfing presence updates
S7evinK Jul 31, 2021
017d9fc
Update presence table
S7evinK Jul 31, 2021
9559563
Add possibilty to query current max id for presence
S7evinK Aug 3, 2021
134311e
Add Setup method to PresenceStreamProvider
S7evinK Aug 3, 2021
02bac18
Update sqlite query to use max(id) as new id
S7evinK Aug 10, 2021
02c991b
Merge branch 'master' into add-presence
S7evinK Aug 22, 2021
41017f4
Add missing file
S7evinK Aug 22, 2021
0973a4e
Fix linter issues
S7evinK Aug 22, 2021
a073a21
Add option to disable presence
S7evinK Aug 23, 2021
78b5465
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Aug 28, 2021
4cce705
Enable presence by default
S7evinK Oct 5, 2021
4033211
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Oct 15, 2021
174f9ca
Add passing tests
S7evinK Oct 15, 2021
ffe6f0f
Add more passing tests
S7evinK Oct 15, 2021
3d33441
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Oct 25, 2021
a7c096c
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Oct 30, 2021
b1f863a
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Nov 5, 2021
a9c6b40
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Nov 12, 2021
de23853
Make "Existing members see new member's presence" pass
S7evinK Nov 15, 2021
09573f2
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Nov 15, 2021
e2afaab
Fix typo
S7evinK Nov 19, 2021
e0fdba5
Set presence when syncing
S7evinK Nov 19, 2021
7fd146b
Change struct fields
S7evinK Nov 19, 2021
b6643ef
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Nov 19, 2021
a17119f
Add missing parameter
S7evinK Nov 19, 2021
9558d64
Add missing file..
S7evinK Nov 19, 2021
09dd5ef
Merge branch 'master' into add-presence
neilalexander Nov 25, 2021
aef302d
Merge branch 'master' into add-presence
S7evinK Dec 30, 2021
dcd1427
Remove duplicate endpoint
S7evinK Dec 30, 2021
49311d8
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Jan 6, 2022
4fff7bf
Add required changes for JetStream
S7evinK Jan 6, 2022
6562cf9
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Jan 7, 2022
e9e932a
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Jan 11, 2022
66a68a3
Create initial presence on account creation
S7evinK Jan 19, 2022
4efb347
Remove ToPresenceStatus
S7evinK Jan 19, 2022
b59f20e
Remove PresenceStatus to avoid confusion
S7evinK Jan 19, 2022
2e6eb4e
Remove ToPresenceStatus
S7evinK Jan 19, 2022
a3039af
Let wakeupUsers handle which users to wake
S7evinK Jan 20, 2022
aef128a
Use gmsl.MPresence
S7evinK Jan 20, 2022
43b87ee
Append presence data for newly joined members
S7evinK Jan 20, 2022
a019241
Merge branch 'master' of https://github.com/matrix-org/dendrite into …
S7evinK Jan 20, 2022
15c1094
Fix lint issues
S7evinK Jan 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build/gobind-pinecone/monolith.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ func (m *DendriteMonolith) Start() {
rsAPI := roomserver.NewInternalAPI(base)

fsAPI := federationapi.NewInternalAPI(
base, federation, rsAPI, base.Caches, keyRing, true,
base, federation, rsAPI, m.userAPI, base.Caches, keyRing, true,
)

keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, fsAPI)
Expand Down
8 changes: 4 additions & 4 deletions build/gobind-yggdrasil/monolith.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,14 @@ func (m *DendriteMonolith) Start() {

rsAPI := roomserver.NewInternalAPI(base)

fsAPI := federationapi.NewInternalAPI(
base, federation, rsAPI, base.Caches, keyRing, true,
)

keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, federation)
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI)
keyAPI.SetUserAPI(userAPI)

fsAPI := federationapi.NewInternalAPI(
base, federation, rsAPI, userAPI, base.Caches, keyRing, true,
)

eduInputAPI := eduserver.NewInternalAPI(
base, cache.New(), userAPI,
)
Expand Down
160 changes: 160 additions & 0 deletions clientapi/routing/presence.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// Copyright 2021 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 (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"

"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/eduserver/api"
roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/dendrite/userapi/types"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util"
"github.com/sirupsen/logrus"
)

type presenceRequest struct {
Presence string `json:"presence"`
StatusMsg *string `json:"status_msg"`
}

// SetPresence updates the users presence status
func SetPresence(req *http.Request,
eduAPI api.EDUServerInputAPI,
userAPI userapi.UserInternalAPI,
userID string,
device *userapi.Device,
) util.JSONResponse {
if userID != device.UserID {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("You cannot set the presence state of another user."),
}
}
data, err := ioutil.ReadAll(req.Body)
if err != nil {
logrus.WithError(err).Error("unable to read request body")
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("The request body could not be read: " + err.Error()),
}
}
defer req.Body.Close() // nolint:errcheck

// parse the request
var r presenceRequest
err = json.Unmarshal(data, &r)
if err != nil {
logrus.WithError(err).Error("unable to unmarshal request body")
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON("The request body could not be read: " + err.Error()),
}
}

p := strings.TrimSpace(strings.ToLower(r.Presence))
// requested new presence is not allowed by the spec
presence, ok := types.KnownPresence[p]
if !ok {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.BadJSON(
fmt.Sprintf("The sent presence value '%s' is not allowed.", p),
),
}
}

lastActive := gomatrixserverlib.AsTimestamp(time.Now())
if err := api.SetPresence(req.Context(), eduAPI, userAPI, userID, r.StatusMsg, presence, lastActive); err != nil {
logrus.WithError(err).Error("failed to send presence to eduserver")
return util.ErrorResponse(err)
}

return util.JSONResponse{
Code: http.StatusOK,
JSON: struct{}{},
}
}

type presenceResponse struct {
Presence string `json:"presence"`
StatusMsg *string `json:"status_msg,omitempty"`
LastActiveAgo int64 `json:"last_active_ago,omitempty"`
CurrentlyActive bool `json:"currently_active,omitempty"`
}

// GetPresence returns the presence status of a given user.
// If the requesting user doesn't share a room with this user, the request is denied.
func GetPresence(req *http.Request,
userAPI userapi.UserInternalAPI,
userID string,
rsAPI roomserverAPI.RoomserverInternalAPI,
device *userapi.Device,
) util.JSONResponse {
// Only check allowance to see presence, if it's not our own user. (Otherwise sytest fails..)
if device.UserID != userID {
rsResp := roomserverAPI.QueryKnownUsersResponse{}
rsQry := roomserverAPI.QueryKnownUsersRequest{UserID: device.UserID, SearchString: userID, Limit: 2}
if err := rsAPI.QueryKnownUsers(req.Context(), &rsQry, &rsResp); err != nil {
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: jsonerror.InternalServerError(),
}
}

// Users don't share a room, not allowed to see presence.
if len(rsResp.Users) == 0 {
return util.JSONResponse{
Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("You cannot view the presence for this user."),
}
}
}

presence := userapi.QueryPresenceForUserResponse{}
qry := userapi.QueryPresenceForUserRequest{UserID: userID}
err := userAPI.QueryPresenceForUser(req.Context(), &qry, &presence)
if err != nil {
logrus.WithFields(logrus.Fields{
"userID": userID,
}).WithError(err).Error("unable to query presence")
// return an empty presence, to make sytest happy
return util.JSONResponse{
Code: http.StatusOK,
JSON: presence,
}
}

resp := presenceResponse{}
lastActive := time.Since(presence.LastActiveTS.Time())
resp.LastActiveAgo = lastActive.Milliseconds()
resp.StatusMsg = presence.StatusMsg
resp.CurrentlyActive = lastActive <= time.Minute*5
if !resp.CurrentlyActive {
presence.Presence = types.Unavailable
}
resp.Presence = presence.Presence.String()
return util.JSONResponse{
Code: http.StatusOK,
JSON: resp,
}
}
47 changes: 33 additions & 14 deletions clientapi/routing/routing.go
Original file line number Diff line number Diff line change
Expand Up @@ -614,20 +614,6 @@ func Setup(
}),
).Methods(http.MethodPost, http.MethodOptions)

// Element logs get flooded unless this is handled
r0mux.Handle("/presence/{userID}/status",
httputil.MakeExternalAPI("presence", func(req *http.Request) util.JSONResponse {
if r := rateLimits.Limit(req); r != nil {
return *r
}
// TODO: Set presence (probably the responsibility of a presence server not clientapi)
return util.JSONResponse{
Code: http.StatusOK,
JSON: struct{}{},
}
}),
).Methods(http.MethodPut, http.MethodOptions)

r0mux.Handle("/voip/turnServer",
httputil.MakeAuthAPI("turn_server", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.Limit(req); r != nil {
Expand Down Expand Up @@ -1111,4 +1097,37 @@ func Setup(
return SetReceipt(req, eduAPI, device, vars["roomId"], vars["receiptType"], vars["eventId"])
}),
).Methods(http.MethodPost, http.MethodOptions)
r0mux.Handle("/presence/{userId}/status",
httputil.MakeAuthAPI("set_presence", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.Limit(req); r != nil {
return *r
}
if !cfg.Matrix.PresenceEnabled {
return util.JSONResponse{
Code: http.StatusOK,
JSON: struct{}{},
}
}
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
if err != nil {
return util.ErrorResponse(err)
}
return SetPresence(req, eduAPI, userAPI, vars["userId"], device)
}),
).Methods(http.MethodPut, http.MethodOptions)
r0mux.Handle("/presence/{userId}/status",
httputil.MakeAuthAPI("get_presence", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
if err != nil {
return util.ErrorResponse(err)
}
if !cfg.Matrix.PresenceEnabled {
return util.JSONResponse{
Code: http.StatusOK,
JSON: struct{}{},
}
}
return GetPresence(req, userAPI, vars["userId"], rsAPI, device)
}),
).Methods(http.MethodGet, http.MethodOptions)
}
2 changes: 1 addition & 1 deletion cmd/dendrite-demo-libp2p/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func main() {
asAPI := appservice.NewInternalAPI(&base.Base, userAPI, rsAPI)
rsAPI.SetAppserviceAPI(asAPI)
fsAPI := federationapi.NewInternalAPI(
&base.Base, federation, rsAPI, base.Base.Caches, nil, true,
&base.Base, federation, rsAPI, userAPI, base.Base.Caches, nil, true,
)
keyRing := fsAPI.KeyRing()
rsAPI.SetFederationAPI(fsAPI, keyRing)
Expand Down
3 changes: 2 additions & 1 deletion cmd/dendrite-demo-pinecone/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,13 @@ func main() {
rsComponent := roomserver.NewInternalAPI(base)
rsAPI := rsComponent
fsAPI := federationapi.NewInternalAPI(
base, federation, rsAPI, base.Caches, keyRing, true,
base, federation, rsAPI, nil, base.Caches, keyRing, true,
)

keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, fsAPI)
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, nil, keyAPI)
keyAPI.SetUserAPI(userAPI)
fsAPI.SetUserAPI(userAPI)

eduInputAPI := eduserver.NewInternalAPI(
base, cache.New(), userAPI,
Expand Down
2 changes: 1 addition & 1 deletion cmd/dendrite-demo-yggdrasil/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func main() {
asAPI := appservice.NewInternalAPI(base, userAPI, rsAPI)
rsAPI.SetAppserviceAPI(asAPI)
fsAPI := federationapi.NewInternalAPI(
base, federation, rsAPI, base.Caches, keyRing, true,
base, federation, rsAPI, userAPI, base.Caches, keyRing, true,
)

rsComponent.SetFederationAPI(fsAPI, keyRing)
Expand Down
3 changes: 2 additions & 1 deletion cmd/dendrite-monolith-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func main() {
}

fsAPI := federationapi.NewInternalAPI(
base, federation, rsAPI, base.Caches, nil, false,
base, federation, rsAPI, nil, base.Caches, nil, false,
)
if base.UseHTTPAPIs {
federationapi.AddInternalRoutes(base.InternalAPIMux, fsAPI)
Expand Down Expand Up @@ -133,6 +133,7 @@ func main() {
rsImpl.SetFederationAPI(fsAPI, keyRing)
rsImpl.SetAppserviceAPI(asAPI)
keyImpl.SetUserAPI(userAPI)
fsAPI.SetUserAPI(userAPI)

eduInputAPI := eduserver.NewInternalAPI(
base, cache.New(), userAPI,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func FederationAPI(base *basepkg.BaseDendrite, cfg *config.Dendrite) {
federation := base.CreateFederationClient()
rsAPI := base.RoomserverHTTPClient()
keyAPI := base.KeyServerHTTPClient()
fsAPI := federationapi.NewInternalAPI(base, federation, rsAPI, base.Caches, nil, true)
fsAPI := federationapi.NewInternalAPI(base, federation, rsAPI, userAPI, base.Caches, nil, true)
keyRing := fsAPI.KeyRing()

federationapi.AddPublicRoutes(
Expand Down
3 changes: 2 additions & 1 deletion cmd/dendritejs-pinecone/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@ func startup() {
base, userAPI, rsAPI,
)
rsAPI.SetAppserviceAPI(asQuery)
fedSenderAPI := federationapi.NewInternalAPI(base, federation, rsAPI, base.Caches, keyRing, true)

fedSenderAPI := federationapi.NewInternalAPI(base, federation, rsAPI, userAPI, base.Caches, keyRing, true)
rsAPI.SetFederationAPI(fedSenderAPI, keyRing)

monolith := setup.Monolith{
Expand Down
8 changes: 8 additions & 0 deletions dendrite-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ global:
# - private_key: old_matrix_key.pem
# expired_at: 1601024554498

# Enable/Disable presence. Enabling presence might cause a high load on your server.
presence_enabled: false

# How long a remote server can cache our server signing key before requesting it
# again. Increasing this number will reduce the number of requests made by other
# servers for our key but increases the period that a compromised key will be
Expand Down Expand Up @@ -341,6 +344,11 @@ user_api:
max_open_conns: 10
max_idle_conns: 2
conn_max_lifetime: -1
presence_database:
connection_string: file:userapi_presence.db
max_open_conns: 10
max_idle_conns: 2
conn_max_lifetime: -1
# The length of time that a token issued for a relying party from
# /_matrix/client/r0/user/{userId}/openid/request_token endpoint
# is considered to be valid in milliseconds.
Expand Down
20 changes: 20 additions & 0 deletions eduserver/api/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ package api
import (
"context"

types2 "github.com/matrix-org/dendrite/syncapi/types"
"github.com/matrix-org/dendrite/userapi/types"
"github.com/matrix-org/gomatrixserverlib"
)

Expand Down Expand Up @@ -75,6 +77,18 @@ type InputReceiptEventRequest struct {
// InputReceiptEventResponse is a response to InputReceiptEventRequest
type InputReceiptEventResponse struct{}

// InputPresenceRequest is a request to EDUServerInputAPI
type InputPresenceRequest struct {
UserID string `json:"user_id"`
Presence types.PresenceStatus `json:"status"`
StatusMsg *string `json:"status_msg"`
LastActiveTS gomatrixserverlib.Timestamp `json:"last_active_ts"`
StreamPos types2.StreamPosition `json:"stream_pos"`
}

// InputPresenceResponse is a response to InputPresenceRequest
type InputPresenceResponse struct{}

type InputCrossSigningKeyUpdateRequest struct {
CrossSigningKeyUpdate `json:"signing_keys"`
}
Expand All @@ -101,6 +115,12 @@ type EDUServerInputAPI interface {
response *InputReceiptEventResponse,
) error

InputPresence(
ctx context.Context,
request *InputPresenceRequest,
response *InputPresenceResponse,
) error

InputCrossSigningKeyUpdate(
ctx context.Context,
request *InputCrossSigningKeyUpdateRequest,
Expand Down
Loading