mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-29 01:33:10 -06:00
Merge branch 'master' of https://github.com/matrix-org/dendrite into add-presence
This commit is contained in:
commit
4b94f52ce4
|
|
@ -31,7 +31,7 @@ If you have further questions, please take a look at [our FAQ](docs/FAQ.md) or j
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
To build Dendrite, you will need Go 1.13 or later.
|
To build Dendrite, you will need Go 1.15 or later.
|
||||||
|
|
||||||
For a usable federating Dendrite deployment, you will also need:
|
For a usable federating Dendrite deployment, you will also need:
|
||||||
- A domain name (or subdomain)
|
- A domain name (or subdomain)
|
||||||
|
|
|
||||||
|
|
@ -299,7 +299,7 @@ func (m *DendriteMonolith) Start() {
|
||||||
base, federation, rsAPI, keyRing, true,
|
base, federation, rsAPI, keyRing, true,
|
||||||
)
|
)
|
||||||
|
|
||||||
keyAPI := keyserver.NewInternalAPI(&base.Cfg.KeyServer, fsAPI)
|
keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, fsAPI)
|
||||||
m.userAPI = userapi.NewInternalAPI(accountDB, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI)
|
m.userAPI = userapi.NewInternalAPI(accountDB, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI)
|
||||||
keyAPI.SetUserAPI(m.userAPI)
|
keyAPI.SetUserAPI(m.userAPI)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ func (m *DendriteMonolith) Start() {
|
||||||
base, federation, rsAPI, keyRing, true,
|
base, federation, rsAPI, keyRing, true,
|
||||||
)
|
)
|
||||||
|
|
||||||
keyAPI := keyserver.NewInternalAPI(&base.Cfg.KeyServer, federation)
|
keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, federation)
|
||||||
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI)
|
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI)
|
||||||
keyAPI.SetUserAPI(userAPI)
|
keyAPI.SetUserAPI(userAPI)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ func (t *LoginTypePassword) Login(ctx context.Context, req interface{}) (*Login,
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return nil, &util.JSONResponse{
|
return nil, &util.JSONResponse{
|
||||||
Code: http.StatusUnauthorized,
|
Code: http.StatusUnauthorized,
|
||||||
JSON: jsonerror.BadJSON("'user' must be supplied."),
|
JSON: jsonerror.BadJSON("A username must be supplied."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
localpart, err := userutil.ParseUsernameParam(username, &t.Config.Matrix.ServerName)
|
localpart, err := userutil.ParseUsernameParam(username, &t.Config.Matrix.ServerName)
|
||||||
|
|
@ -68,7 +68,7 @@ func (t *LoginTypePassword) Login(ctx context.Context, req interface{}) (*Login,
|
||||||
// but that would leak the existence of the user.
|
// but that would leak the existence of the user.
|
||||||
return nil, &util.JSONResponse{
|
return nil, &util.JSONResponse{
|
||||||
Code: http.StatusForbidden,
|
Code: http.StatusForbidden,
|
||||||
JSON: jsonerror.Forbidden("username or password was incorrect, or the account does not exist"),
|
JSON: jsonerror.Forbidden("The username or password was incorrect or the account does not exist."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &r.Login, nil
|
return &r.Login, nil
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,7 @@ func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte, device *
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, &util.JSONResponse{
|
return nil, &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.BadJSON("unknown auth.type: " + authType),
|
JSON: jsonerror.BadJSON("Unknown auth.type: " + authType),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -231,7 +231,7 @@ func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte, device *
|
||||||
if !u.IsSingleStageFlow(authType) {
|
if !u.IsSingleStageFlow(authType) {
|
||||||
return nil, &util.JSONResponse{
|
return nil, &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.Unknown("missing or unknown auth.session"),
|
JSON: jsonerror.Unknown("The auth.session is missing or unknown."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,18 @@ func GuestAccessForbidden(msg string) *MatrixError {
|
||||||
return &MatrixError{"M_GUEST_ACCESS_FORBIDDEN", msg}
|
return &MatrixError{"M_GUEST_ACCESS_FORBIDDEN", msg}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InvalidSignature is an error which is returned when the client tries
|
||||||
|
// to upload invalid signatures.
|
||||||
|
func InvalidSignature(msg string) *MatrixError {
|
||||||
|
return &MatrixError{"M_INVALID_SIGNATURE", msg}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MissingParam is an error that is returned when a parameter was incorrect,
|
||||||
|
// traditionally with cross-signing.
|
||||||
|
func MissingParam(msg string) *MatrixError {
|
||||||
|
return &MatrixError{"M_MISSING_PARAM", msg}
|
||||||
|
}
|
||||||
|
|
||||||
type IncompatibleRoomVersionError struct {
|
type IncompatibleRoomVersionError struct {
|
||||||
RoomVersion string `json:"room_version"`
|
RoomVersion string `json:"room_version"`
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ func Deactivate(
|
||||||
return *errRes
|
return *errRes
|
||||||
}
|
}
|
||||||
|
|
||||||
localpart, _, err := gomatrixserverlib.SplitID('@', login.User)
|
localpart, _, err := gomatrixserverlib.SplitID('@', login.Username())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.GetLogger(req.Context()).WithError(err).Error("gomatrixserverlib.SplitID failed")
|
util.GetLogger(req.Context()).WithError(err).Error("gomatrixserverlib.SplitID failed")
|
||||||
return jsonerror.InternalServerError()
|
return jsonerror.InternalServerError()
|
||||||
|
|
|
||||||
125
clientapi/routing/key_crosssigning.go
Normal file
125
clientapi/routing/key_crosssigning.go
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
// 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"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/auth"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||||
|
"github.com/matrix-org/dendrite/clientapi/jsonerror"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
|
"github.com/matrix-org/dendrite/userapi/storage/accounts"
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UploadCrossSigningDeviceKeys(
|
||||||
|
req *http.Request, userInteractiveAuth *auth.UserInteractive,
|
||||||
|
keyserverAPI api.KeyInternalAPI, device *userapi.Device,
|
||||||
|
accountDB accounts.Database, cfg *config.ClientAPI,
|
||||||
|
) util.JSONResponse {
|
||||||
|
uploadReq := &api.PerformUploadDeviceKeysRequest{}
|
||||||
|
uploadRes := &api.PerformUploadDeviceKeysResponse{}
|
||||||
|
|
||||||
|
ctx := req.Context()
|
||||||
|
defer req.Body.Close() // nolint:errcheck
|
||||||
|
bodyBytes, err := ioutil.ReadAll(req.Body)
|
||||||
|
if err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: jsonerror.BadJSON("The request body could not be read: " + err.Error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := userInteractiveAuth.Verify(ctx, bodyBytes, device); err != nil {
|
||||||
|
return *err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = json.Unmarshal(bodyBytes, &uploadReq); err != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: jsonerror.BadJSON("The request body could not be unmarshalled: " + err.Error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadReq.UserID = device.UserID
|
||||||
|
keyserverAPI.PerformUploadDeviceKeys(req.Context(), uploadReq, uploadRes)
|
||||||
|
|
||||||
|
if err := uploadRes.Error; err != nil {
|
||||||
|
switch {
|
||||||
|
case err.IsInvalidSignature:
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: jsonerror.InvalidSignature(err.Error()),
|
||||||
|
}
|
||||||
|
case err.IsMissingParam:
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: jsonerror.MissingParam(err.Error()),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: jsonerror.Unknown(err.Error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: struct{}{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UploadCrossSigningDeviceSignatures(req *http.Request, keyserverAPI api.KeyInternalAPI, device *userapi.Device) util.JSONResponse {
|
||||||
|
uploadReq := &api.PerformUploadDeviceSignaturesRequest{}
|
||||||
|
uploadRes := &api.PerformUploadDeviceSignaturesResponse{}
|
||||||
|
|
||||||
|
if err := httputil.UnmarshalJSONRequest(req, &uploadReq.Signatures); err != nil {
|
||||||
|
return *err
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadReq.UserID = device.UserID
|
||||||
|
keyserverAPI.PerformUploadDeviceSignatures(req.Context(), uploadReq, uploadRes)
|
||||||
|
|
||||||
|
if err := uploadRes.Error; err != nil {
|
||||||
|
switch {
|
||||||
|
case err.IsInvalidSignature:
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: jsonerror.InvalidSignature(err.Error()),
|
||||||
|
}
|
||||||
|
case err.IsMissingParam:
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: jsonerror.MissingParam(err.Error()),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
JSON: jsonerror.Unknown(err.Error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
JSON: struct{}{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -100,7 +100,7 @@ func (r *queryKeysRequest) GetTimeout() time.Duration {
|
||||||
return time.Duration(r.Timeout) * time.Millisecond
|
return time.Duration(r.Timeout) * time.Millisecond
|
||||||
}
|
}
|
||||||
|
|
||||||
func QueryKeys(req *http.Request, keyAPI api.KeyInternalAPI) util.JSONResponse {
|
func QueryKeys(req *http.Request, keyAPI api.KeyInternalAPI, device *userapi.Device) util.JSONResponse {
|
||||||
var r queryKeysRequest
|
var r queryKeysRequest
|
||||||
resErr := httputil.UnmarshalJSONRequest(req, &r)
|
resErr := httputil.UnmarshalJSONRequest(req, &r)
|
||||||
if resErr != nil {
|
if resErr != nil {
|
||||||
|
|
@ -108,6 +108,7 @@ func QueryKeys(req *http.Request, keyAPI api.KeyInternalAPI) util.JSONResponse {
|
||||||
}
|
}
|
||||||
queryRes := api.QueryKeysResponse{}
|
queryRes := api.QueryKeysResponse{}
|
||||||
keyAPI.QueryKeys(req.Context(), &api.QueryKeysRequest{
|
keyAPI.QueryKeys(req.Context(), &api.QueryKeysRequest{
|
||||||
|
UserID: device.UserID,
|
||||||
UserToDevices: r.DeviceKeys,
|
UserToDevices: r.DeviceKeys,
|
||||||
Timeout: r.GetTimeout(),
|
Timeout: r.GetTimeout(),
|
||||||
// TODO: Token?
|
// TODO: Token?
|
||||||
|
|
@ -115,8 +116,11 @@ func QueryKeys(req *http.Request, keyAPI api.KeyInternalAPI) util.JSONResponse {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: 200,
|
Code: 200,
|
||||||
JSON: map[string]interface{}{
|
JSON: map[string]interface{}{
|
||||||
"device_keys": queryRes.DeviceKeys,
|
"device_keys": queryRes.DeviceKeys,
|
||||||
"failures": queryRes.Failures,
|
"master_keys": queryRes.MasterKeys,
|
||||||
|
"self_signing_keys": queryRes.SelfSigningKeys,
|
||||||
|
"user_signing_keys": queryRes.UserSigningKeys,
|
||||||
|
"failures": queryRes.Failures,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,9 @@ func Setup(
|
||||||
rateLimits := newRateLimits(&cfg.RateLimiting)
|
rateLimits := newRateLimits(&cfg.RateLimiting)
|
||||||
userInteractiveAuth := auth.NewUserInteractive(accountDB.GetAccountByPassword, cfg)
|
userInteractiveAuth := auth.NewUserInteractive(accountDB.GetAccountByPassword, cfg)
|
||||||
|
|
||||||
unstableFeatures := make(map[string]bool)
|
unstableFeatures := map[string]bool{
|
||||||
|
//"org.matrix.e2e_cross_signing": true,
|
||||||
|
}
|
||||||
for _, msc := range cfg.MSCs.MSCs {
|
for _, msc := range cfg.MSCs.MSCs {
|
||||||
unstableFeatures["org.matrix."+msc] = true
|
unstableFeatures["org.matrix."+msc] = true
|
||||||
}
|
}
|
||||||
|
|
@ -1052,6 +1054,22 @@ func Setup(
|
||||||
|
|
||||||
// Deleting E2E Backup Keys
|
// Deleting E2E Backup Keys
|
||||||
|
|
||||||
|
// Cross-signing device keys
|
||||||
|
|
||||||
|
postDeviceSigningKeys := httputil.MakeAuthAPI("post_device_signing_keys", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
|
return UploadCrossSigningDeviceKeys(req, userInteractiveAuth, keyAPI, device, accountDB, cfg)
|
||||||
|
})
|
||||||
|
|
||||||
|
postDeviceSigningSignatures := httputil.MakeAuthAPI("post_device_signing_signatures", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
|
return UploadCrossSigningDeviceSignatures(req, keyAPI, device)
|
||||||
|
})
|
||||||
|
|
||||||
|
r0mux.Handle("/keys/device_signing/upload", postDeviceSigningKeys).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
r0mux.Handle("/keys/signatures/upload", postDeviceSigningSignatures).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
|
unstableMux.Handle("/keys/device_signing/upload", postDeviceSigningKeys).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
unstableMux.Handle("/keys/signatures/upload", postDeviceSigningSignatures).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
// Supplying a device ID is deprecated.
|
// Supplying a device ID is deprecated.
|
||||||
r0mux.Handle("/keys/upload/{deviceID}",
|
r0mux.Handle("/keys/upload/{deviceID}",
|
||||||
httputil.MakeAuthAPI("keys_upload", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
httputil.MakeAuthAPI("keys_upload", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
|
|
@ -1065,7 +1083,7 @@ func Setup(
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
r0mux.Handle("/keys/query",
|
r0mux.Handle("/keys/query",
|
||||||
httputil.MakeAuthAPI("keys_query", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
httputil.MakeAuthAPI("keys_query", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
return QueryKeys(req, keyAPI)
|
return QueryKeys(req, keyAPI, device)
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
).Methods(http.MethodPost, http.MethodOptions)
|
||||||
r0mux.Handle("/keys/claim",
|
r0mux.Handle("/keys/claim",
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ func main() {
|
||||||
|
|
||||||
accountDB := base.Base.CreateAccountsDB()
|
accountDB := base.Base.CreateAccountsDB()
|
||||||
federation := createFederationClient(base)
|
federation := createFederationClient(base)
|
||||||
keyAPI := keyserver.NewInternalAPI(&base.Base.Cfg.KeyServer, federation)
|
keyAPI := keyserver.NewInternalAPI(&base.Base, &base.Base.Cfg.KeyServer, federation)
|
||||||
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, nil, keyAPI)
|
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, nil, keyAPI)
|
||||||
keyAPI.SetUserAPI(userAPI)
|
keyAPI.SetUserAPI(userAPI)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ func main() {
|
||||||
base, federation, rsAPI, keyRing, true,
|
base, federation, rsAPI, keyRing, true,
|
||||||
)
|
)
|
||||||
|
|
||||||
keyAPI := keyserver.NewInternalAPI(&base.Cfg.KeyServer, fsAPI)
|
keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, fsAPI)
|
||||||
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, nil, keyAPI)
|
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, nil, keyAPI)
|
||||||
keyAPI.SetUserAPI(userAPI)
|
keyAPI.SetUserAPI(userAPI)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Yggdrasil Demo
|
# Yggdrasil Demo
|
||||||
|
|
||||||
This is the Dendrite Yggdrasil demo! It's easy to get started - all you need is Go 1.14 or later.
|
This is the Dendrite Yggdrasil demo! It's easy to get started - all you need is Go 1.15 or later.
|
||||||
|
|
||||||
To run the homeserver, start at the root of the Dendrite repository and run:
|
To run the homeserver, start at the root of the Dendrite repository and run:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ func main() {
|
||||||
serverKeyAPI := &signing.YggdrasilKeys{}
|
serverKeyAPI := &signing.YggdrasilKeys{}
|
||||||
keyRing := serverKeyAPI.KeyRing()
|
keyRing := serverKeyAPI.KeyRing()
|
||||||
|
|
||||||
keyAPI := keyserver.NewInternalAPI(&base.Cfg.KeyServer, federation)
|
keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, federation)
|
||||||
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, nil, keyAPI)
|
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, nil, keyAPI)
|
||||||
keyAPI.SetUserAPI(userAPI)
|
keyAPI.SetUserAPI(userAPI)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"github.com/matrix-org/dendrite/setup/mscs"
|
"github.com/matrix-org/dendrite/setup/mscs"
|
||||||
"github.com/matrix-org/dendrite/signingkeyserver"
|
"github.com/matrix-org/dendrite/signingkeyserver"
|
||||||
"github.com/matrix-org/dendrite/userapi"
|
"github.com/matrix-org/dendrite/userapi"
|
||||||
|
uapi "github.com/matrix-org/dendrite/userapi/api"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
|
@ -111,9 +112,14 @@ func main() {
|
||||||
// This is different to rsAPI which can be the http client which doesn't need this dependency
|
// This is different to rsAPI which can be the http client which doesn't need this dependency
|
||||||
rsImpl.SetFederationSenderAPI(fsAPI)
|
rsImpl.SetFederationSenderAPI(fsAPI)
|
||||||
|
|
||||||
keyAPI := keyserver.NewInternalAPI(&base.Cfg.KeyServer, fsAPI)
|
keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, fsAPI)
|
||||||
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI)
|
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI)
|
||||||
keyAPI.SetUserAPI(userAPI)
|
keyAPI.SetUserAPI(userAPI)
|
||||||
|
if traceInternal {
|
||||||
|
userAPI = &uapi.UserInternalAPITrace{
|
||||||
|
Impl: userAPI,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
eduInputAPI := eduserver.NewInternalAPI(
|
eduInputAPI := eduserver.NewInternalAPI(
|
||||||
base, cache.New(), userAPI,
|
base, cache.New(), userAPI,
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import (
|
||||||
|
|
||||||
func KeyServer(base *setup.BaseDendrite, cfg *config.Dendrite) {
|
func KeyServer(base *setup.BaseDendrite, cfg *config.Dendrite) {
|
||||||
fsAPI := base.FederationSenderHTTPClient()
|
fsAPI := base.FederationSenderHTTPClient()
|
||||||
intAPI := keyserver.NewInternalAPI(&base.Cfg.KeyServer, fsAPI)
|
intAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, fsAPI)
|
||||||
intAPI.SetUserAPI(base.UserAPIClient())
|
intAPI.SetUserAPI(base.UserAPIClient())
|
||||||
|
|
||||||
keyserver.AddInternalRoutes(base.InternalAPIMux, intAPI)
|
keyserver.AddInternalRoutes(base.InternalAPIMux, intAPI)
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ func startup() {
|
||||||
|
|
||||||
accountDB := base.CreateAccountsDB()
|
accountDB := base.CreateAccountsDB()
|
||||||
federation := conn.CreateFederationClient(base, pSessions)
|
federation := conn.CreateFederationClient(base, pSessions)
|
||||||
keyAPI := keyserver.NewInternalAPI(&base.Cfg.KeyServer, federation)
|
keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, federation)
|
||||||
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, nil, keyAPI)
|
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, nil, keyAPI)
|
||||||
keyAPI.SetUserAPI(userAPI)
|
keyAPI.SetUserAPI(userAPI)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -192,7 +192,7 @@ func main() {
|
||||||
|
|
||||||
accountDB := base.CreateAccountsDB()
|
accountDB := base.CreateAccountsDB()
|
||||||
federation := createFederationClient(cfg, node)
|
federation := createFederationClient(cfg, node)
|
||||||
keyAPI := keyserver.NewInternalAPI(&base.Cfg.KeyServer, federation)
|
keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, federation)
|
||||||
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, nil, keyAPI)
|
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, nil, keyAPI)
|
||||||
keyAPI.SetUserAPI(userAPI)
|
keyAPI.SetUserAPI(userAPI)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ use in production environments just yet!
|
||||||
|
|
||||||
Dendrite requires:
|
Dendrite requires:
|
||||||
|
|
||||||
* Go 1.14 or higher
|
* Go 1.15 or higher
|
||||||
* Postgres 9.6 or higher (if using Postgres databases, not needed for SQLite)
|
* Postgres 9.6 or higher (if using Postgres databases, not needed for SQLite)
|
||||||
|
|
||||||
If you want to run a polylith deployment, you also need:
|
If you want to run a polylith deployment, you also need:
|
||||||
|
|
|
||||||
|
|
@ -166,32 +166,6 @@ func (t *testRoomserverAPI) QueryEventsByID(
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query the membership event for an user for a room.
|
|
||||||
func (t *testRoomserverAPI) QueryMembershipForUser(
|
|
||||||
ctx context.Context,
|
|
||||||
request *api.QueryMembershipForUserRequest,
|
|
||||||
response *api.QueryMembershipForUserResponse,
|
|
||||||
) error {
|
|
||||||
return fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *testRoomserverAPI) QueryPublishedRooms(
|
|
||||||
ctx context.Context,
|
|
||||||
request *api.QueryPublishedRoomsRequest,
|
|
||||||
response *api.QueryPublishedRoomsResponse,
|
|
||||||
) error {
|
|
||||||
return fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query a list of membership events for a room
|
|
||||||
func (t *testRoomserverAPI) QueryMembershipsForRoom(
|
|
||||||
ctx context.Context,
|
|
||||||
request *api.QueryMembershipsForRoomRequest,
|
|
||||||
response *api.QueryMembershipsForRoomResponse,
|
|
||||||
) error {
|
|
||||||
return fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query if a server is joined to a room
|
// Query if a server is joined to a room
|
||||||
func (t *testRoomserverAPI) QueryServerJoinedToRoom(
|
func (t *testRoomserverAPI) QueryServerJoinedToRoom(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
|
@ -203,53 +177,6 @@ func (t *testRoomserverAPI) QueryServerJoinedToRoom(
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query whether a server is allowed to see an event
|
|
||||||
func (t *testRoomserverAPI) QueryServerAllowedToSeeEvent(
|
|
||||||
ctx context.Context,
|
|
||||||
request *api.QueryServerAllowedToSeeEventRequest,
|
|
||||||
response *api.QueryServerAllowedToSeeEventResponse,
|
|
||||||
) error {
|
|
||||||
return fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query missing events for a room from roomserver
|
|
||||||
func (t *testRoomserverAPI) QueryMissingEvents(
|
|
||||||
ctx context.Context,
|
|
||||||
request *api.QueryMissingEventsRequest,
|
|
||||||
response *api.QueryMissingEventsResponse,
|
|
||||||
) error {
|
|
||||||
return fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query to get state and auth chain for a (potentially hypothetical) event.
|
|
||||||
// Takes lists of PrevEventIDs and AuthEventsIDs and uses them to calculate
|
|
||||||
// the state and auth chain to return.
|
|
||||||
func (t *testRoomserverAPI) QueryStateAndAuthChain(
|
|
||||||
ctx context.Context,
|
|
||||||
request *api.QueryStateAndAuthChainRequest,
|
|
||||||
response *api.QueryStateAndAuthChainResponse,
|
|
||||||
) error {
|
|
||||||
return fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query a given amount (or less) of events prior to a given set of events.
|
|
||||||
func (t *testRoomserverAPI) PerformBackfill(
|
|
||||||
ctx context.Context,
|
|
||||||
request *api.PerformBackfillRequest,
|
|
||||||
response *api.PerformBackfillResponse,
|
|
||||||
) error {
|
|
||||||
return fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Asks for the default room version as preferred by the server.
|
|
||||||
func (t *testRoomserverAPI) QueryRoomVersionCapabilities(
|
|
||||||
ctx context.Context,
|
|
||||||
request *api.QueryRoomVersionCapabilitiesRequest,
|
|
||||||
response *api.QueryRoomVersionCapabilitiesResponse,
|
|
||||||
) error {
|
|
||||||
return fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Asks for the room version for a given room.
|
// Asks for the room version for a given room.
|
||||||
func (t *testRoomserverAPI) QueryRoomVersionForRoom(
|
func (t *testRoomserverAPI) QueryRoomVersionForRoom(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
|
@ -260,72 +187,10 @@ func (t *testRoomserverAPI) QueryRoomVersionForRoom(
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a room alias
|
func (t *testRoomserverAPI) QueryServerBannedFromRoom(
|
||||||
func (t *testRoomserverAPI) SetRoomAlias(
|
ctx context.Context, req *api.QueryServerBannedFromRoomRequest, res *api.QueryServerBannedFromRoomResponse,
|
||||||
ctx context.Context,
|
|
||||||
req *api.SetRoomAliasRequest,
|
|
||||||
response *api.SetRoomAliasResponse,
|
|
||||||
) error {
|
) error {
|
||||||
return fmt.Errorf("not implemented")
|
res.Banned = false
|
||||||
}
|
|
||||||
|
|
||||||
// Get the room ID for an alias
|
|
||||||
func (t *testRoomserverAPI) GetRoomIDForAlias(
|
|
||||||
ctx context.Context,
|
|
||||||
req *api.GetRoomIDForAliasRequest,
|
|
||||||
response *api.GetRoomIDForAliasResponse,
|
|
||||||
) error {
|
|
||||||
return fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all known aliases for a room ID
|
|
||||||
func (t *testRoomserverAPI) GetAliasesForRoomID(
|
|
||||||
ctx context.Context,
|
|
||||||
req *api.GetAliasesForRoomIDRequest,
|
|
||||||
response *api.GetAliasesForRoomIDResponse,
|
|
||||||
) error {
|
|
||||||
return fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the user ID of the creator of an alias
|
|
||||||
func (t *testRoomserverAPI) GetCreatorIDForAlias(
|
|
||||||
ctx context.Context,
|
|
||||||
req *api.GetCreatorIDForAliasRequest,
|
|
||||||
response *api.GetCreatorIDForAliasResponse,
|
|
||||||
) error {
|
|
||||||
return fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove a room alias
|
|
||||||
func (t *testRoomserverAPI) RemoveRoomAlias(
|
|
||||||
ctx context.Context,
|
|
||||||
req *api.RemoveRoomAliasRequest,
|
|
||||||
response *api.RemoveRoomAliasResponse,
|
|
||||||
) error {
|
|
||||||
return fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *testRoomserverAPI) QueryCurrentState(ctx context.Context, req *api.QueryCurrentStateRequest, res *api.QueryCurrentStateResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *testRoomserverAPI) QueryRoomsForUser(ctx context.Context, req *api.QueryRoomsForUserRequest, res *api.QueryRoomsForUserResponse) error {
|
|
||||||
return fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *testRoomserverAPI) QueryBulkStateContent(ctx context.Context, req *api.QueryBulkStateContentRequest, res *api.QueryBulkStateContentResponse) error {
|
|
||||||
return fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *testRoomserverAPI) QuerySharedUsers(ctx context.Context, req *api.QuerySharedUsersRequest, res *api.QuerySharedUsersResponse) error {
|
|
||||||
return fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *testRoomserverAPI) QueryKnownUsers(ctx context.Context, req *api.QueryKnownUsersRequest, res *api.QueryKnownUsersResponse) error {
|
|
||||||
return fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *testRoomserverAPI) QueryServerBannedFromRoom(ctx context.Context, req *api.QueryServerBannedFromRoomRequest, res *api.QueryServerBannedFromRoomResponse) error {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
34
go.mod
34
go.mod
|
|
@ -1,25 +1,23 @@
|
||||||
module github.com/matrix-org/dendrite
|
module github.com/matrix-org/dendrite
|
||||||
|
|
||||||
replace github.com/matrix-org/gomatrixserverlib => github.com/S7evinK/gomatrixserverlib v0.0.0-20210731142023-4060a37948a5
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Arceliar/ironwood v0.0.0-20210619124114-6ad55cae5031
|
github.com/Arceliar/ironwood v0.0.0-20210619124114-6ad55cae5031
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
github.com/DATA-DOG/go-sqlmock v1.5.0
|
||||||
github.com/HdrHistogram/hdrhistogram-go v1.0.1 // indirect
|
github.com/HdrHistogram/hdrhistogram-go v1.0.1 // indirect
|
||||||
github.com/Masterminds/semver/v3 v3.1.1
|
github.com/Masterminds/semver/v3 v3.1.1
|
||||||
github.com/Shopify/sarama v1.28.0
|
github.com/Shopify/sarama v1.29.1
|
||||||
github.com/codeclysm/extract v2.2.0+incompatible
|
github.com/codeclysm/extract v2.2.0+incompatible
|
||||||
github.com/containerd/containerd v1.5.2 // indirect
|
github.com/containerd/containerd v1.5.5 // indirect
|
||||||
github.com/docker/docker v20.10.7+incompatible
|
github.com/docker/docker v20.10.7+incompatible
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/getsentry/sentry-go v0.10.0
|
github.com/getsentry/sentry-go v0.11.0
|
||||||
github.com/gologme/log v1.2.0
|
github.com/gologme/log v1.2.0
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/h2non/filetype v1.1.1 // indirect
|
github.com/h2non/filetype v1.1.1 // indirect
|
||||||
github.com/hashicorp/golang-lru v0.5.4
|
github.com/hashicorp/golang-lru v0.5.4
|
||||||
github.com/juju/testing v0.0.0-20210324180055-18c50b0c2098 // indirect
|
github.com/juju/testing v0.0.0-20210324180055-18c50b0c2098 // indirect
|
||||||
github.com/lib/pq v1.9.0
|
github.com/lib/pq v1.10.2
|
||||||
github.com/libp2p/go-libp2p v0.13.0
|
github.com/libp2p/go-libp2p v0.13.0
|
||||||
github.com/libp2p/go-libp2p-circuit v0.4.0
|
github.com/libp2p/go-libp2p-circuit v0.4.0
|
||||||
github.com/libp2p/go-libp2p-core v0.8.3
|
github.com/libp2p/go-libp2p-core v0.8.3
|
||||||
|
|
@ -28,16 +26,16 @@ require (
|
||||||
github.com/libp2p/go-libp2p-kad-dht v0.11.1
|
github.com/libp2p/go-libp2p-kad-dht v0.11.1
|
||||||
github.com/libp2p/go-libp2p-pubsub v0.4.1
|
github.com/libp2p/go-libp2p-pubsub v0.4.1
|
||||||
github.com/libp2p/go-libp2p-record v0.1.3
|
github.com/libp2p/go-libp2p-record v0.1.3
|
||||||
github.com/lucas-clemente/quic-go v0.19.3
|
github.com/lucas-clemente/quic-go v0.22.0
|
||||||
github.com/matrix-org/dugong v0.0.0-20180820122854-51a565b5666b
|
github.com/matrix-org/dugong v0.0.0-20210603171012-8379174dca81
|
||||||
github.com/matrix-org/go-http-js-libp2p v0.0.0-20200518170932-783164aeeda4
|
github.com/matrix-org/go-http-js-libp2p v0.0.0-20200518170932-783164aeeda4
|
||||||
github.com/matrix-org/go-sqlite3-js v0.0.0-20210709140738-b0d1ba599a6d
|
github.com/matrix-org/go-sqlite3-js v0.0.0-20210709140738-b0d1ba599a6d
|
||||||
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16
|
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16
|
||||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20210722110442-5061d6986876
|
github.com/matrix-org/gomatrixserverlib v0.0.0-20210802144451-bec8d2252d83
|
||||||
github.com/matrix-org/naffka v0.0.0-20210623111924-14ff508b58e0
|
github.com/matrix-org/naffka v0.0.0-20210623111924-14ff508b58e0
|
||||||
github.com/matrix-org/pinecone v0.0.0-20210623102758-74f885644c1b
|
github.com/matrix-org/pinecone v0.0.0-20210623102758-74f885644c1b
|
||||||
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4
|
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4
|
||||||
github.com/mattn/go-sqlite3 v1.14.7-0.20210414154423-1157a4212dcb
|
github.com/mattn/go-sqlite3 v1.14.8
|
||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
github.com/neilalexander/utp v0.1.1-0.20210727203401-54ae7b1cd5f9
|
github.com/neilalexander/utp v0.1.1-0.20210727203401-54ae7b1cd5f9
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||||
|
|
@ -46,23 +44,23 @@ require (
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/pressly/goose v2.7.0+incompatible
|
github.com/pressly/goose v2.7.0+incompatible
|
||||||
github.com/prometheus/client_golang v1.9.0
|
github.com/prometheus/client_golang v1.11.0
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/tidwall/gjson v1.8.1
|
github.com/tidwall/gjson v1.8.1
|
||||||
github.com/tidwall/pretty v1.2.0 // indirect
|
github.com/tidwall/pretty v1.2.0 // indirect
|
||||||
github.com/tidwall/sjson v1.1.7
|
github.com/tidwall/sjson v1.1.7
|
||||||
github.com/uber/jaeger-client-go v2.25.0+incompatible
|
github.com/uber/jaeger-client-go v2.29.1+incompatible
|
||||||
github.com/uber/jaeger-lib v2.4.0+incompatible
|
github.com/uber/jaeger-lib v2.4.1+incompatible
|
||||||
github.com/yggdrasil-network/yggdrasil-go v0.4.1-0.20210715083903-52309d094c00
|
github.com/yggdrasil-network/yggdrasil-go v0.4.1-0.20210715083903-52309d094c00
|
||||||
go.uber.org/atomic v1.7.0
|
go.uber.org/atomic v1.9.0
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
||||||
golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08
|
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554
|
||||||
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b
|
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1
|
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
|
||||||
gopkg.in/h2non/bimg.v1 v1.1.5
|
gopkg.in/h2non/bimg.v1 v1.1.5
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
nhooyr.io/websocket v1.8.7
|
nhooyr.io/websocket v1.8.7
|
||||||
)
|
)
|
||||||
|
|
||||||
go 1.14
|
go 1.15
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ type KeyInternalAPI interface {
|
||||||
PerformUploadKeys(ctx context.Context, req *PerformUploadKeysRequest, res *PerformUploadKeysResponse)
|
PerformUploadKeys(ctx context.Context, req *PerformUploadKeysRequest, res *PerformUploadKeysResponse)
|
||||||
// PerformClaimKeys claims one-time keys for use in pre-key messages
|
// PerformClaimKeys claims one-time keys for use in pre-key messages
|
||||||
PerformClaimKeys(ctx context.Context, req *PerformClaimKeysRequest, res *PerformClaimKeysResponse)
|
PerformClaimKeys(ctx context.Context, req *PerformClaimKeysRequest, res *PerformClaimKeysResponse)
|
||||||
|
PerformUploadDeviceKeys(ctx context.Context, req *PerformUploadDeviceKeysRequest, res *PerformUploadDeviceKeysResponse)
|
||||||
|
PerformUploadDeviceSignatures(ctx context.Context, req *PerformUploadDeviceSignaturesRequest, res *PerformUploadDeviceSignaturesResponse)
|
||||||
QueryKeys(ctx context.Context, req *QueryKeysRequest, res *QueryKeysResponse)
|
QueryKeys(ctx context.Context, req *QueryKeysRequest, res *QueryKeysResponse)
|
||||||
QueryKeyChanges(ctx context.Context, req *QueryKeyChangesRequest, res *QueryKeyChangesResponse)
|
QueryKeyChanges(ctx context.Context, req *QueryKeyChangesRequest, res *QueryKeyChangesResponse)
|
||||||
QueryOneTimeKeys(ctx context.Context, req *QueryOneTimeKeysRequest, res *QueryOneTimeKeysResponse)
|
QueryOneTimeKeys(ctx context.Context, req *QueryOneTimeKeysRequest, res *QueryOneTimeKeysResponse)
|
||||||
|
|
@ -40,7 +42,9 @@ type KeyInternalAPI interface {
|
||||||
|
|
||||||
// KeyError is returned if there was a problem performing/querying the server
|
// KeyError is returned if there was a problem performing/querying the server
|
||||||
type KeyError struct {
|
type KeyError struct {
|
||||||
Err string
|
Err string `json:"error"`
|
||||||
|
IsInvalidSignature bool `json:"is_invalid_signature,omitempty"` // M_INVALID_SIGNATURE
|
||||||
|
IsMissingParam bool `json:"is_missing_param,omitempty"` // M_MISSING_PARAM
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *KeyError) Error() string {
|
func (k *KeyError) Error() string {
|
||||||
|
|
@ -151,7 +155,30 @@ type PerformClaimKeysResponse struct {
|
||||||
Error *KeyError
|
Error *KeyError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PerformUploadDeviceKeysRequest struct {
|
||||||
|
gomatrixserverlib.CrossSigningKeys
|
||||||
|
// The user that uploaded the key, should be populated by the clientapi.
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PerformUploadDeviceKeysResponse struct {
|
||||||
|
Error *KeyError
|
||||||
|
}
|
||||||
|
|
||||||
|
type PerformUploadDeviceSignaturesRequest struct {
|
||||||
|
Signatures map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice
|
||||||
|
// The user that uploaded the sig, should be populated by the clientapi.
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PerformUploadDeviceSignaturesResponse struct {
|
||||||
|
Error *KeyError
|
||||||
|
}
|
||||||
|
|
||||||
type QueryKeysRequest struct {
|
type QueryKeysRequest struct {
|
||||||
|
// The user ID asking for the keys, e.g. if from a client API request.
|
||||||
|
// Will not be populated if the key request came from federation.
|
||||||
|
UserID string
|
||||||
// Maps user IDs to a list of devices
|
// Maps user IDs to a list of devices
|
||||||
UserToDevices map[string][]string
|
UserToDevices map[string][]string
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
|
|
@ -162,6 +189,10 @@ type QueryKeysResponse struct {
|
||||||
Failures map[string]interface{}
|
Failures map[string]interface{}
|
||||||
// Map of user_id to device_id to device_key
|
// Map of user_id to device_id to device_key
|
||||||
DeviceKeys map[string]map[string]json.RawMessage
|
DeviceKeys map[string]map[string]json.RawMessage
|
||||||
|
// Maps of user_id to cross signing key
|
||||||
|
MasterKeys map[string]gomatrixserverlib.CrossSigningKey
|
||||||
|
SelfSigningKeys map[string]gomatrixserverlib.CrossSigningKey
|
||||||
|
UserSigningKeys map[string]gomatrixserverlib.CrossSigningKey
|
||||||
// Set if there was a fatal error processing this query
|
// Set if there was a fatal error processing this query
|
||||||
Error *KeyError
|
Error *KeyError
|
||||||
}
|
}
|
||||||
|
|
|
||||||
61
keyserver/consumers/eduserver.go
Normal file
61
keyserver/consumers/eduserver.go
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
package consumers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/internal"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/storage"
|
||||||
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
"github.com/matrix-org/dendrite/setup/process"
|
||||||
|
|
||||||
|
"github.com/Shopify/sarama"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OutputSigningKeyUpdateConsumer struct {
|
||||||
|
eduServerConsumer *internal.ContinualConsumer
|
||||||
|
keyDB storage.Database
|
||||||
|
keyAPI api.KeyInternalAPI
|
||||||
|
serverName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOutputSigningKeyUpdateConsumer(
|
||||||
|
process *process.ProcessContext,
|
||||||
|
cfg *config.Dendrite,
|
||||||
|
kafkaConsumer sarama.Consumer,
|
||||||
|
keyDB storage.Database,
|
||||||
|
keyAPI api.KeyInternalAPI,
|
||||||
|
) *OutputSigningKeyUpdateConsumer {
|
||||||
|
consumer := internal.ContinualConsumer{
|
||||||
|
Process: process,
|
||||||
|
ComponentName: "keyserver/eduserver",
|
||||||
|
Topic: cfg.Global.Kafka.TopicFor(config.TopicOutputSigningKeyUpdate),
|
||||||
|
Consumer: kafkaConsumer,
|
||||||
|
PartitionStore: keyDB,
|
||||||
|
}
|
||||||
|
s := &OutputSigningKeyUpdateConsumer{
|
||||||
|
eduServerConsumer: &consumer,
|
||||||
|
keyDB: keyDB,
|
||||||
|
keyAPI: keyAPI,
|
||||||
|
serverName: string(cfg.Global.ServerName),
|
||||||
|
}
|
||||||
|
consumer.ProcessMessage = s.onMessage
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OutputSigningKeyUpdateConsumer) Start() error {
|
||||||
|
return s.eduServerConsumer.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OutputSigningKeyUpdateConsumer) onMessage(msg *sarama.ConsumerMessage) error {
|
||||||
|
/*
|
||||||
|
var output eduapi.OutputSigningKeyUpdate
|
||||||
|
if err := json.Unmarshal(msg.Value, &output); err != nil {
|
||||||
|
log.WithError(err).Errorf("eduserver output log: message parse failure")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
*/
|
||||||
|
return fmt.Errorf("TODO")
|
||||||
|
}
|
||||||
389
keyserver/internal/cross_signing.go
Normal file
389
keyserver/internal/cross_signing.go
Normal file
|
|
@ -0,0 +1,389 @@
|
||||||
|
// 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 internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/types"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func sanityCheckKey(key gomatrixserverlib.CrossSigningKey, userID string, purpose gomatrixserverlib.CrossSigningKeyPurpose) error {
|
||||||
|
// Is there exactly one key?
|
||||||
|
if len(key.Keys) != 1 {
|
||||||
|
return fmt.Errorf("should contain exactly one key")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does the key ID match the key value? Iterates exactly once
|
||||||
|
for keyID, keyData := range key.Keys {
|
||||||
|
b64 := keyData.Encode()
|
||||||
|
tokens := strings.Split(string(keyID), ":")
|
||||||
|
if len(tokens) != 2 {
|
||||||
|
return fmt.Errorf("key ID is incorrectly formatted")
|
||||||
|
}
|
||||||
|
if tokens[1] != b64 {
|
||||||
|
return fmt.Errorf("key ID isn't correct")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does the key claim to be from the right user?
|
||||||
|
if userID != key.UserID {
|
||||||
|
return fmt.Errorf("key has a user ID mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does the key contain the correct purpose?
|
||||||
|
useful := false
|
||||||
|
for _, usage := range key.Usage {
|
||||||
|
if usage == purpose {
|
||||||
|
useful = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !useful {
|
||||||
|
return fmt.Errorf("key does not contain correct usage purpose")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint:gocyclo
|
||||||
|
func (a *KeyInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.PerformUploadDeviceKeysRequest, res *api.PerformUploadDeviceKeysResponse) {
|
||||||
|
var masterKey gomatrixserverlib.Base64Bytes
|
||||||
|
hasMasterKey := false
|
||||||
|
|
||||||
|
if len(req.MasterKey.Keys) > 0 {
|
||||||
|
if err := sanityCheckKey(req.MasterKey, req.UserID, gomatrixserverlib.CrossSigningKeyPurposeMaster); err != nil {
|
||||||
|
res.Error = &api.KeyError{
|
||||||
|
Err: "Master key sanity check failed: " + err.Error(),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hasMasterKey = true
|
||||||
|
for _, keyData := range req.MasterKey.Keys { // iterates once, because sanityCheckKey requires one key
|
||||||
|
masterKey = keyData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.SelfSigningKey.Keys) > 0 {
|
||||||
|
if err := sanityCheckKey(req.SelfSigningKey, req.UserID, gomatrixserverlib.CrossSigningKeyPurposeSelfSigning); err != nil {
|
||||||
|
res.Error = &api.KeyError{
|
||||||
|
Err: "Self-signing key sanity check failed: " + err.Error(),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.UserSigningKey.Keys) > 0 {
|
||||||
|
if err := sanityCheckKey(req.UserSigningKey, req.UserID, gomatrixserverlib.CrossSigningKeyPurposeUserSigning); err != nil {
|
||||||
|
res.Error = &api.KeyError{
|
||||||
|
Err: "User-signing key sanity check failed: " + err.Error(),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user hasn't given a new master key, then let's go and get their
|
||||||
|
// existing keys from the database.
|
||||||
|
if !hasMasterKey {
|
||||||
|
existingKeys, err := a.DB.CrossSigningKeysForUser(ctx, req.UserID)
|
||||||
|
if err != nil {
|
||||||
|
res.Error = &api.KeyError{
|
||||||
|
Err: "Retrieving cross-signing keys from database failed: " + err.Error(),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
masterKey, hasMasterKey = existingKeys[gomatrixserverlib.CrossSigningKeyPurposeMaster]
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user isn't a local user and we haven't successfully found a key
|
||||||
|
// through any local means then ask over federation.
|
||||||
|
if !hasMasterKey {
|
||||||
|
_, host, err := gomatrixserverlib.SplitID('@', req.UserID)
|
||||||
|
if err != nil {
|
||||||
|
res.Error = &api.KeyError{
|
||||||
|
Err: "Retrieving cross-signing keys from federation failed: " + err.Error(),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
keys, err := a.FedClient.QueryKeys(ctx, host, map[string][]string{
|
||||||
|
req.UserID: {},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
res.Error = &api.KeyError{
|
||||||
|
Err: "Retrieving cross-signing keys from federation failed: " + err.Error(),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch k := keys.MasterKeys[req.UserID].CrossSigningBody.(type) {
|
||||||
|
case *gomatrixserverlib.CrossSigningKey:
|
||||||
|
if err := sanityCheckKey(*k, req.UserID, gomatrixserverlib.CrossSigningKeyPurposeMaster); err != nil {
|
||||||
|
res.Error = &api.KeyError{
|
||||||
|
Err: "Master key sanity check failed: " + err.Error(),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
res.Error = &api.KeyError{
|
||||||
|
Err: "Unexpected type for master key retrieved from federation",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we still don't have a master key at this point then there's nothing else
|
||||||
|
// we can do - we've checked both the request and the database.
|
||||||
|
if !hasMasterKey {
|
||||||
|
res.Error = &api.KeyError{
|
||||||
|
Err: "No master key was found, either in the database or in the request!",
|
||||||
|
IsMissingParam: true,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The key ID is basically the key itself.
|
||||||
|
masterKeyID := gomatrixserverlib.KeyID(fmt.Sprintf("ed25519:%s", masterKey.Encode()))
|
||||||
|
|
||||||
|
// Work out which things we need to verify the signatures for.
|
||||||
|
toVerify := make(map[gomatrixserverlib.CrossSigningKeyPurpose]gomatrixserverlib.CrossSigningKey, 3)
|
||||||
|
toStore := types.CrossSigningKeyMap{}
|
||||||
|
if len(req.MasterKey.Keys) > 0 {
|
||||||
|
toVerify[gomatrixserverlib.CrossSigningKeyPurposeMaster] = req.MasterKey
|
||||||
|
}
|
||||||
|
if len(req.SelfSigningKey.Keys) > 0 {
|
||||||
|
toVerify[gomatrixserverlib.CrossSigningKeyPurposeSelfSigning] = req.SelfSigningKey
|
||||||
|
}
|
||||||
|
if len(req.UserSigningKey.Keys) > 0 {
|
||||||
|
toVerify[gomatrixserverlib.CrossSigningKeyPurposeUserSigning] = req.UserSigningKey
|
||||||
|
}
|
||||||
|
for purpose, key := range toVerify {
|
||||||
|
// Collect together the key IDs we need to verify with. This will include
|
||||||
|
// all of the key IDs specified in the signatures. We don't do this for
|
||||||
|
// the master key because we have no means to verify the signatures - we
|
||||||
|
// instead just need to store them.
|
||||||
|
if purpose != gomatrixserverlib.CrossSigningKeyPurposeMaster {
|
||||||
|
// Marshal the specific key back into JSON so that we can verify the
|
||||||
|
// signature of it.
|
||||||
|
keyJSON, err := json.Marshal(key)
|
||||||
|
if err != nil {
|
||||||
|
res.Error = &api.KeyError{
|
||||||
|
Err: fmt.Sprintf("The JSON of the key section is invalid: %s", err.Error()),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now check if the subkey is signed by the master key.
|
||||||
|
if err := gomatrixserverlib.VerifyJSON(req.UserID, masterKeyID, ed25519.PublicKey(masterKey), keyJSON); err != nil {
|
||||||
|
res.Error = &api.KeyError{
|
||||||
|
Err: fmt.Sprintf("The %q sub-key failed master key signature verification: %s", purpose, err.Error()),
|
||||||
|
IsInvalidSignature: true,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've reached this point then all the signatures are valid so
|
||||||
|
// add the key to the list of keys to store.
|
||||||
|
for _, keyData := range key.Keys { // iterates once, see sanityCheckKey
|
||||||
|
toStore[purpose] = keyData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.DB.StoreCrossSigningKeysForUser(ctx, req.UserID, toStore); err != nil {
|
||||||
|
res.Error = &api.KeyError{
|
||||||
|
Err: fmt.Sprintf("a.DB.StoreCrossSigningKeysForUser: %s", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *KeyInternalAPI) PerformUploadDeviceSignatures(ctx context.Context, req *api.PerformUploadDeviceSignaturesRequest, res *api.PerformUploadDeviceSignaturesResponse) {
|
||||||
|
selfSignatures := map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{}
|
||||||
|
otherSignatures := map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{}
|
||||||
|
|
||||||
|
for userID, forUserID := range req.Signatures {
|
||||||
|
for keyID, keyOrDevice := range forUserID {
|
||||||
|
switch key := keyOrDevice.CrossSigningBody.(type) {
|
||||||
|
case *gomatrixserverlib.CrossSigningKey:
|
||||||
|
if key.UserID == req.UserID {
|
||||||
|
if _, ok := selfSignatures[userID]; !ok {
|
||||||
|
selfSignatures[userID] = map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{}
|
||||||
|
}
|
||||||
|
selfSignatures[userID][keyID] = keyOrDevice
|
||||||
|
} else {
|
||||||
|
if _, ok := otherSignatures[userID]; !ok {
|
||||||
|
otherSignatures[userID] = map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{}
|
||||||
|
}
|
||||||
|
otherSignatures[userID][keyID] = keyOrDevice
|
||||||
|
}
|
||||||
|
|
||||||
|
case *gomatrixserverlib.DeviceKeys:
|
||||||
|
if key.UserID == req.UserID {
|
||||||
|
if _, ok := selfSignatures[userID]; !ok {
|
||||||
|
selfSignatures[userID] = map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{}
|
||||||
|
}
|
||||||
|
selfSignatures[userID][keyID] = keyOrDevice
|
||||||
|
} else {
|
||||||
|
if _, ok := otherSignatures[userID]; !ok {
|
||||||
|
otherSignatures[userID] = map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice{}
|
||||||
|
}
|
||||||
|
otherSignatures[userID][keyID] = keyOrDevice
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.processSelfSignatures(ctx, req.UserID, selfSignatures); err != nil {
|
||||||
|
res.Error = &api.KeyError{
|
||||||
|
Err: fmt.Sprintf("a.processSelfSignatures: %s", err),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.processOtherSignatures(ctx, req.UserID, otherSignatures); err != nil {
|
||||||
|
res.Error = &api.KeyError{
|
||||||
|
Err: fmt.Sprintf("a.processOtherSignatures: %s", err),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *KeyInternalAPI) processSelfSignatures(
|
||||||
|
ctx context.Context, _ string,
|
||||||
|
signatures map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice,
|
||||||
|
) error {
|
||||||
|
// Here we will process:
|
||||||
|
// * The user signing their own devices using their self-signing key
|
||||||
|
// * The user signing their master key using one of their devices
|
||||||
|
|
||||||
|
for targetUserID, forTargetUserID := range signatures {
|
||||||
|
for targetKeyID, signature := range forTargetUserID {
|
||||||
|
switch sig := signature.CrossSigningBody.(type) {
|
||||||
|
case *gomatrixserverlib.CrossSigningKey:
|
||||||
|
for originUserID, forOriginUserID := range sig.Signatures {
|
||||||
|
for originKeyID, originSig := range forOriginUserID {
|
||||||
|
if err := a.DB.StoreCrossSigningSigsForTarget(
|
||||||
|
ctx, originUserID, originKeyID, targetUserID, targetKeyID, originSig,
|
||||||
|
); err != nil {
|
||||||
|
return fmt.Errorf("a.DB.StoreCrossSigningKeysForTarget: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *gomatrixserverlib.DeviceKeys:
|
||||||
|
for originUserID, forOriginUserID := range sig.Signatures {
|
||||||
|
for originKeyID, originSig := range forOriginUserID {
|
||||||
|
if err := a.DB.StoreCrossSigningSigsForTarget(
|
||||||
|
ctx, originUserID, originKeyID, targetUserID, targetKeyID, originSig,
|
||||||
|
); err != nil {
|
||||||
|
return fmt.Errorf("a.DB.StoreCrossSigningKeysForTarget: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unexpected type assertion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *KeyInternalAPI) processOtherSignatures(
|
||||||
|
ctx context.Context, userID string,
|
||||||
|
signatures map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.CrossSigningForKeyOrDevice,
|
||||||
|
) error {
|
||||||
|
// Here we will process:
|
||||||
|
// * A user signing someone else's master keys using their user-signing keys
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *KeyInternalAPI) crossSigningKeysFromDatabase(
|
||||||
|
ctx context.Context, req *api.QueryKeysRequest, res *api.QueryKeysResponse,
|
||||||
|
) {
|
||||||
|
for userID := range req.UserToDevices {
|
||||||
|
keys, err := a.DB.CrossSigningKeysForUser(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Errorf("Failed to get cross-signing keys for user %q", userID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for keyType, keyData := range keys {
|
||||||
|
b64 := keyData.Encode()
|
||||||
|
keyID := gomatrixserverlib.KeyID("ed25519:" + b64)
|
||||||
|
key := gomatrixserverlib.CrossSigningKey{
|
||||||
|
UserID: userID,
|
||||||
|
Usage: []gomatrixserverlib.CrossSigningKeyPurpose{
|
||||||
|
keyType,
|
||||||
|
},
|
||||||
|
Keys: map[gomatrixserverlib.KeyID]gomatrixserverlib.Base64Bytes{
|
||||||
|
keyID: keyData,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
sigs, err := a.DB.CrossSigningSigsForTarget(ctx, userID, keyID)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Errorf("Failed to get cross-signing signatures for user %q key %q", userID, keyID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
appendSignature := func(originUserID string, originKeyID gomatrixserverlib.KeyID, signature gomatrixserverlib.Base64Bytes) {
|
||||||
|
if key.Signatures == nil {
|
||||||
|
key.Signatures = types.CrossSigningSigMap{}
|
||||||
|
}
|
||||||
|
if _, ok := key.Signatures[originUserID]; !ok {
|
||||||
|
key.Signatures[originUserID] = make(map[gomatrixserverlib.KeyID]gomatrixserverlib.Base64Bytes)
|
||||||
|
}
|
||||||
|
key.Signatures[originUserID][originKeyID] = signature
|
||||||
|
}
|
||||||
|
|
||||||
|
for originUserID, forOrigin := range sigs {
|
||||||
|
for originKeyID, signature := range forOrigin {
|
||||||
|
switch {
|
||||||
|
case req.UserID != "" && originUserID == req.UserID:
|
||||||
|
// Include signatures that we created
|
||||||
|
appendSignature(originUserID, originKeyID, signature)
|
||||||
|
case originUserID == userID:
|
||||||
|
// Include signatures that were created by the person whose key
|
||||||
|
// we are processing
|
||||||
|
appendSignature(originUserID, originKeyID, signature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch keyType {
|
||||||
|
case gomatrixserverlib.CrossSigningKeyPurposeMaster:
|
||||||
|
res.MasterKeys[userID] = key
|
||||||
|
|
||||||
|
case gomatrixserverlib.CrossSigningKeyPurposeSelfSigning:
|
||||||
|
res.SelfSigningKeys[userID] = key
|
||||||
|
|
||||||
|
case gomatrixserverlib.CrossSigningKeyPurposeUserSigning:
|
||||||
|
res.UserSigningKeys[userID] = key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -82,6 +82,7 @@ type DeviceListUpdater struct {
|
||||||
mu *sync.Mutex // protects UserIDToMutex
|
mu *sync.Mutex // protects UserIDToMutex
|
||||||
|
|
||||||
db DeviceListUpdaterDatabase
|
db DeviceListUpdaterDatabase
|
||||||
|
api DeviceListUpdaterAPI
|
||||||
producer KeyChangeProducer
|
producer KeyChangeProducer
|
||||||
fedClient fedsenderapi.FederationClient
|
fedClient fedsenderapi.FederationClient
|
||||||
workerChans []chan gomatrixserverlib.ServerName
|
workerChans []chan gomatrixserverlib.ServerName
|
||||||
|
|
@ -114,6 +115,10 @@ type DeviceListUpdaterDatabase interface {
|
||||||
DeviceKeysJSON(ctx context.Context, keys []api.DeviceMessage) error
|
DeviceKeysJSON(ctx context.Context, keys []api.DeviceMessage) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DeviceListUpdaterAPI interface {
|
||||||
|
PerformUploadDeviceKeys(ctx context.Context, req *api.PerformUploadDeviceKeysRequest, res *api.PerformUploadDeviceKeysResponse)
|
||||||
|
}
|
||||||
|
|
||||||
// KeyChangeProducer is the interface for producers.KeyChange useful for testing.
|
// KeyChangeProducer is the interface for producers.KeyChange useful for testing.
|
||||||
type KeyChangeProducer interface {
|
type KeyChangeProducer interface {
|
||||||
ProduceKeyChanges(keys []api.DeviceMessage) error
|
ProduceKeyChanges(keys []api.DeviceMessage) error
|
||||||
|
|
@ -121,13 +126,14 @@ type KeyChangeProducer interface {
|
||||||
|
|
||||||
// NewDeviceListUpdater creates a new updater which fetches fresh device lists when they go stale.
|
// NewDeviceListUpdater creates a new updater which fetches fresh device lists when they go stale.
|
||||||
func NewDeviceListUpdater(
|
func NewDeviceListUpdater(
|
||||||
db DeviceListUpdaterDatabase, producer KeyChangeProducer, fedClient fedsenderapi.FederationClient,
|
db DeviceListUpdaterDatabase, api DeviceListUpdaterAPI, producer KeyChangeProducer,
|
||||||
numWorkers int,
|
fedClient fedsenderapi.FederationClient, numWorkers int,
|
||||||
) *DeviceListUpdater {
|
) *DeviceListUpdater {
|
||||||
return &DeviceListUpdater{
|
return &DeviceListUpdater{
|
||||||
userIDToMutex: make(map[string]*sync.Mutex),
|
userIDToMutex: make(map[string]*sync.Mutex),
|
||||||
mu: &sync.Mutex{},
|
mu: &sync.Mutex{},
|
||||||
db: db,
|
db: db,
|
||||||
|
api: api,
|
||||||
producer: producer,
|
producer: producer,
|
||||||
fedClient: fedClient,
|
fedClient: fedClient,
|
||||||
workerChans: make([]chan gomatrixserverlib.ServerName, numWorkers),
|
workerChans: make([]chan gomatrixserverlib.ServerName, numWorkers),
|
||||||
|
|
@ -367,6 +373,23 @@ func (u *DeviceListUpdater) processServer(serverName gomatrixserverlib.ServerNam
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if res.MasterKey != nil || res.SelfSigningKey != nil {
|
||||||
|
uploadReq := &api.PerformUploadDeviceKeysRequest{
|
||||||
|
UserID: userID,
|
||||||
|
}
|
||||||
|
uploadRes := &api.PerformUploadDeviceKeysResponse{}
|
||||||
|
if res.MasterKey != nil {
|
||||||
|
if err = sanityCheckKey(*res.MasterKey, userID, gomatrixserverlib.CrossSigningKeyPurposeMaster); err == nil {
|
||||||
|
uploadReq.MasterKey = *res.MasterKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if res.SelfSigningKey != nil {
|
||||||
|
if err = sanityCheckKey(*res.SelfSigningKey, userID, gomatrixserverlib.CrossSigningKeyPurposeSelfSigning); err == nil {
|
||||||
|
uploadReq.SelfSigningKey = *res.SelfSigningKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u.api.PerformUploadDeviceKeys(ctx, uploadReq, uploadRes)
|
||||||
|
}
|
||||||
err = u.updateDeviceList(&res)
|
err = u.updateDeviceList(&res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.WithError(err).WithField("user_id", userID).Error("fetched device list but failed to store/emit it")
|
logger.WithError(err).WithField("user_id", userID).Error("fetched device list but failed to store/emit it")
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,13 @@ func (d *mockDeviceListUpdaterDatabase) DeviceKeysJSON(ctx context.Context, keys
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mockDeviceListUpdaterAPI struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *mockDeviceListUpdaterAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.PerformUploadDeviceKeysRequest, res *api.PerformUploadDeviceKeysResponse) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
type roundTripper struct {
|
type roundTripper struct {
|
||||||
fn func(*http.Request) (*http.Response, error)
|
fn func(*http.Request) (*http.Response, error)
|
||||||
}
|
}
|
||||||
|
|
@ -122,8 +129,9 @@ func TestUpdateHavePrevID(t *testing.T) {
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
ap := &mockDeviceListUpdaterAPI{}
|
||||||
producer := &mockKeyChangeProducer{}
|
producer := &mockKeyChangeProducer{}
|
||||||
updater := NewDeviceListUpdater(db, producer, nil, 1)
|
updater := NewDeviceListUpdater(db, ap, producer, nil, 1)
|
||||||
event := gomatrixserverlib.DeviceListUpdateEvent{
|
event := gomatrixserverlib.DeviceListUpdateEvent{
|
||||||
DeviceDisplayName: "Foo Bar",
|
DeviceDisplayName: "Foo Bar",
|
||||||
Deleted: false,
|
Deleted: false,
|
||||||
|
|
@ -166,6 +174,7 @@ func TestUpdateNoPrevID(t *testing.T) {
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
ap := &mockDeviceListUpdaterAPI{}
|
||||||
producer := &mockKeyChangeProducer{}
|
producer := &mockKeyChangeProducer{}
|
||||||
remoteUserID := "@alice:example.somewhere"
|
remoteUserID := "@alice:example.somewhere"
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
@ -193,7 +202,7 @@ func TestUpdateNoPrevID(t *testing.T) {
|
||||||
`)),
|
`)),
|
||||||
}, nil
|
}, nil
|
||||||
})
|
})
|
||||||
updater := NewDeviceListUpdater(db, producer, fedClient, 2)
|
updater := NewDeviceListUpdater(db, ap, producer, fedClient, 2)
|
||||||
if err := updater.Start(); err != nil {
|
if err := updater.Start(); err != nil {
|
||||||
t.Fatalf("failed to start updater: %s", err)
|
t.Fatalf("failed to start updater: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -221,9 +221,17 @@ func (a *KeyInternalAPI) QueryDeviceMessages(ctx context.Context, req *api.Query
|
||||||
|
|
||||||
func (a *KeyInternalAPI) QueryKeys(ctx context.Context, req *api.QueryKeysRequest, res *api.QueryKeysResponse) {
|
func (a *KeyInternalAPI) QueryKeys(ctx context.Context, req *api.QueryKeysRequest, res *api.QueryKeysResponse) {
|
||||||
res.DeviceKeys = make(map[string]map[string]json.RawMessage)
|
res.DeviceKeys = make(map[string]map[string]json.RawMessage)
|
||||||
|
res.MasterKeys = make(map[string]gomatrixserverlib.CrossSigningKey)
|
||||||
|
res.SelfSigningKeys = make(map[string]gomatrixserverlib.CrossSigningKey)
|
||||||
|
res.UserSigningKeys = make(map[string]gomatrixserverlib.CrossSigningKey)
|
||||||
res.Failures = make(map[string]interface{})
|
res.Failures = make(map[string]interface{})
|
||||||
|
|
||||||
|
// get cross-signing keys from the database
|
||||||
|
a.crossSigningKeysFromDatabase(ctx, req, res)
|
||||||
|
|
||||||
// make a map from domain to device keys
|
// make a map from domain to device keys
|
||||||
domainToDeviceKeys := make(map[string]map[string][]string)
|
domainToDeviceKeys := make(map[string]map[string][]string)
|
||||||
|
domainToCrossSigningKeys := make(map[string]map[string]struct{})
|
||||||
for userID, deviceIDs := range req.UserToDevices {
|
for userID, deviceIDs := range req.UserToDevices {
|
||||||
_, serverName, err := gomatrixserverlib.SplitID('@', userID)
|
_, serverName, err := gomatrixserverlib.SplitID('@', userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -274,16 +282,30 @@ func (a *KeyInternalAPI) QueryKeys(ctx context.Context, req *api.QueryKeysReques
|
||||||
domainToDeviceKeys[domain] = make(map[string][]string)
|
domainToDeviceKeys[domain] = make(map[string][]string)
|
||||||
domainToDeviceKeys[domain][userID] = append(domainToDeviceKeys[domain][userID], deviceIDs...)
|
domainToDeviceKeys[domain][userID] = append(domainToDeviceKeys[domain][userID], deviceIDs...)
|
||||||
}
|
}
|
||||||
|
// work out if our cross-signing request for this user was
|
||||||
|
// satisfied, if not add them to the list of things to fetch
|
||||||
|
if _, ok := res.MasterKeys[userID]; !ok {
|
||||||
|
if _, ok := domainToCrossSigningKeys[domain]; !ok {
|
||||||
|
domainToCrossSigningKeys[domain] = make(map[string]struct{})
|
||||||
|
}
|
||||||
|
domainToCrossSigningKeys[domain][userID] = struct{}{}
|
||||||
|
}
|
||||||
|
if _, ok := res.SelfSigningKeys[userID]; !ok {
|
||||||
|
if _, ok := domainToCrossSigningKeys[domain]; !ok {
|
||||||
|
domainToCrossSigningKeys[domain] = make(map[string]struct{})
|
||||||
|
}
|
||||||
|
domainToCrossSigningKeys[domain][userID] = struct{}{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// attempt to satisfy key queries from the local database first as we should get device updates pushed to us
|
// attempt to satisfy key queries from the local database first as we should get device updates pushed to us
|
||||||
domainToDeviceKeys = a.remoteKeysFromDatabase(ctx, res, domainToDeviceKeys)
|
domainToDeviceKeys = a.remoteKeysFromDatabase(ctx, res, domainToDeviceKeys)
|
||||||
if len(domainToDeviceKeys) == 0 {
|
if len(domainToDeviceKeys) == 0 && len(domainToCrossSigningKeys) == 0 {
|
||||||
return // nothing to query
|
return // nothing to query
|
||||||
}
|
}
|
||||||
|
|
||||||
// perform key queries for remote devices
|
// perform key queries for remote devices
|
||||||
a.queryRemoteKeys(ctx, req.Timeout, res, domainToDeviceKeys)
|
a.queryRemoteKeys(ctx, req.Timeout, res, domainToDeviceKeys, domainToCrossSigningKeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *KeyInternalAPI) remoteKeysFromDatabase(
|
func (a *KeyInternalAPI) remoteKeysFromDatabase(
|
||||||
|
|
@ -313,18 +335,30 @@ func (a *KeyInternalAPI) remoteKeysFromDatabase(
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *KeyInternalAPI) queryRemoteKeys(
|
func (a *KeyInternalAPI) queryRemoteKeys(
|
||||||
ctx context.Context, timeout time.Duration, res *api.QueryKeysResponse, domainToDeviceKeys map[string]map[string][]string,
|
ctx context.Context, timeout time.Duration, res *api.QueryKeysResponse,
|
||||||
|
domainToDeviceKeys map[string]map[string][]string, domainToCrossSigningKeys map[string]map[string]struct{},
|
||||||
) {
|
) {
|
||||||
resultCh := make(chan *gomatrixserverlib.RespQueryKeys, len(domainToDeviceKeys))
|
resultCh := make(chan *gomatrixserverlib.RespQueryKeys, len(domainToDeviceKeys))
|
||||||
// allows us to wait until all federation servers have been poked
|
// allows us to wait until all federation servers have been poked
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(len(domainToDeviceKeys))
|
|
||||||
// mutex for writing directly to res (e.g failures)
|
// mutex for writing directly to res (e.g failures)
|
||||||
var respMu sync.Mutex
|
var respMu sync.Mutex
|
||||||
|
|
||||||
|
domains := map[string]struct{}{}
|
||||||
|
for domain := range domainToDeviceKeys {
|
||||||
|
domains[domain] = struct{}{}
|
||||||
|
}
|
||||||
|
for domain := range domainToCrossSigningKeys {
|
||||||
|
domains[domain] = struct{}{}
|
||||||
|
}
|
||||||
|
wg.Add(len(domains))
|
||||||
|
|
||||||
// fan out
|
// fan out
|
||||||
for domain, deviceKeys := range domainToDeviceKeys {
|
for domain := range domains {
|
||||||
go a.queryRemoteKeysOnServer(ctx, domain, deviceKeys, &wg, &respMu, timeout, resultCh, res)
|
go a.queryRemoteKeysOnServer(
|
||||||
|
ctx, domain, domainToDeviceKeys[domain], domainToCrossSigningKeys[domain],
|
||||||
|
&wg, &respMu, timeout, resultCh, res,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the result channel when the goroutines have quit so the for .. range exits
|
// Close the result channel when the goroutines have quit so the for .. range exits
|
||||||
|
|
@ -344,12 +378,29 @@ func (a *KeyInternalAPI) queryRemoteKeys(
|
||||||
res.DeviceKeys[userID][deviceID] = keyJSON
|
res.DeviceKeys[userID][deviceID] = keyJSON
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for userID, body := range result.MasterKeys {
|
||||||
|
switch b := body.CrossSigningBody.(type) {
|
||||||
|
case *gomatrixserverlib.CrossSigningKey:
|
||||||
|
res.MasterKeys[userID] = *b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for userID, body := range result.SelfSigningKeys {
|
||||||
|
switch b := body.CrossSigningBody.(type) {
|
||||||
|
case *gomatrixserverlib.CrossSigningKey:
|
||||||
|
res.SelfSigningKeys[userID] = *b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: do we want to persist these somewhere now
|
||||||
|
// that we have fetched them?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *KeyInternalAPI) queryRemoteKeysOnServer(
|
func (a *KeyInternalAPI) queryRemoteKeysOnServer(
|
||||||
ctx context.Context, serverName string, devKeys map[string][]string, wg *sync.WaitGroup,
|
ctx context.Context, serverName string, devKeys map[string][]string, crossSigningKeys map[string]struct{},
|
||||||
respMu *sync.Mutex, timeout time.Duration, resultCh chan<- *gomatrixserverlib.RespQueryKeys,
|
wg *sync.WaitGroup, respMu *sync.Mutex, timeout time.Duration, resultCh chan<- *gomatrixserverlib.RespQueryKeys,
|
||||||
res *api.QueryKeysResponse,
|
res *api.QueryKeysResponse,
|
||||||
) {
|
) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
@ -358,14 +409,24 @@ func (a *KeyInternalAPI) queryRemoteKeysOnServer(
|
||||||
// for users who we do not have any knowledge about, try to start doing device list updates for them
|
// for users who we do not have any knowledge about, try to start doing device list updates for them
|
||||||
// by hitting /users/devices - otherwise fallback to /keys/query which has nicer bulk properties but
|
// by hitting /users/devices - otherwise fallback to /keys/query which has nicer bulk properties but
|
||||||
// lack a stream ID.
|
// lack a stream ID.
|
||||||
var userIDsForAllDevices []string
|
userIDsForAllDevices := map[string]struct{}{}
|
||||||
for userID, deviceIDs := range devKeys {
|
for userID, deviceIDs := range devKeys {
|
||||||
if len(deviceIDs) == 0 {
|
if len(deviceIDs) == 0 {
|
||||||
userIDsForAllDevices = append(userIDsForAllDevices, userID)
|
userIDsForAllDevices[userID] = struct{}{}
|
||||||
delete(devKeys, userID)
|
delete(devKeys, userID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, userID := range userIDsForAllDevices {
|
// for cross-signing keys, it's probably easier just to hit /keys/query if we aren't already doing
|
||||||
|
// a device list update, so we'll populate those back into the /keys/query list if not
|
||||||
|
for userID := range crossSigningKeys {
|
||||||
|
if devKeys == nil {
|
||||||
|
devKeys = map[string][]string{}
|
||||||
|
}
|
||||||
|
if _, ok := userIDsForAllDevices[userID]; !ok {
|
||||||
|
devKeys[userID] = []string{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for userID := range userIDsForAllDevices {
|
||||||
err := a.Updater.ManualUpdate(context.Background(), gomatrixserverlib.ServerName(serverName), userID)
|
err := a.Updater.ManualUpdate(context.Background(), gomatrixserverlib.ServerName(serverName), userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithFields(logrus.Fields{
|
logrus.WithFields(logrus.Fields{
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,15 @@ import (
|
||||||
|
|
||||||
// HTTP paths for the internal HTTP APIs
|
// HTTP paths for the internal HTTP APIs
|
||||||
const (
|
const (
|
||||||
InputDeviceListUpdatePath = "/keyserver/inputDeviceListUpdate"
|
InputDeviceListUpdatePath = "/keyserver/inputDeviceListUpdate"
|
||||||
PerformUploadKeysPath = "/keyserver/performUploadKeys"
|
PerformUploadKeysPath = "/keyserver/performUploadKeys"
|
||||||
PerformClaimKeysPath = "/keyserver/performClaimKeys"
|
PerformClaimKeysPath = "/keyserver/performClaimKeys"
|
||||||
QueryKeysPath = "/keyserver/queryKeys"
|
PerformUploadDeviceKeysPath = "/keyserver/performUploadDeviceKeys"
|
||||||
QueryKeyChangesPath = "/keyserver/queryKeyChanges"
|
PerformUploadDeviceSignaturesPath = "/keyserver/performUploadDeviceSignatures"
|
||||||
QueryOneTimeKeysPath = "/keyserver/queryOneTimeKeys"
|
QueryKeysPath = "/keyserver/queryKeys"
|
||||||
QueryDeviceMessagesPath = "/keyserver/queryDeviceMessages"
|
QueryKeyChangesPath = "/keyserver/queryKeyChanges"
|
||||||
|
QueryOneTimeKeysPath = "/keyserver/queryOneTimeKeys"
|
||||||
|
QueryDeviceMessagesPath = "/keyserver/queryDeviceMessages"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewKeyServerClient creates a KeyInternalAPI implemented by talking to a HTTP POST API.
|
// NewKeyServerClient creates a KeyInternalAPI implemented by talking to a HTTP POST API.
|
||||||
|
|
@ -175,3 +177,37 @@ func (h *httpKeyInternalAPI) QueryKeyChanges(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *httpKeyInternalAPI) PerformUploadDeviceKeys(
|
||||||
|
ctx context.Context,
|
||||||
|
request *api.PerformUploadDeviceKeysRequest,
|
||||||
|
response *api.PerformUploadDeviceKeysResponse,
|
||||||
|
) {
|
||||||
|
span, ctx := opentracing.StartSpanFromContext(ctx, "PerformUploadDeviceKeys")
|
||||||
|
defer span.Finish()
|
||||||
|
|
||||||
|
apiURL := h.apiURL + PerformUploadDeviceKeysPath
|
||||||
|
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
||||||
|
if err != nil {
|
||||||
|
response.Error = &api.KeyError{
|
||||||
|
Err: err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpKeyInternalAPI) PerformUploadDeviceSignatures(
|
||||||
|
ctx context.Context,
|
||||||
|
request *api.PerformUploadDeviceSignaturesRequest,
|
||||||
|
response *api.PerformUploadDeviceSignaturesResponse,
|
||||||
|
) {
|
||||||
|
span, ctx := opentracing.StartSpanFromContext(ctx, "PerformUploadDeviceSignatures")
|
||||||
|
defer span.Finish()
|
||||||
|
|
||||||
|
apiURL := h.apiURL + PerformUploadDeviceSignaturesPath
|
||||||
|
err := httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
|
||||||
|
if err != nil {
|
||||||
|
response.Error = &api.KeyError{
|
||||||
|
Err: err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,28 @@ func AddRoutes(internalAPIMux *mux.Router, s api.KeyInternalAPI) {
|
||||||
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
internalAPIMux.Handle(PerformUploadDeviceKeysPath,
|
||||||
|
httputil.MakeInternalAPI("performUploadDeviceKeys", func(req *http.Request) util.JSONResponse {
|
||||||
|
request := api.PerformUploadDeviceKeysRequest{}
|
||||||
|
response := api.PerformUploadDeviceKeysResponse{}
|
||||||
|
if err := json.NewDecoder(req.Body).Decode(&request.CrossSigningKeys); err != nil {
|
||||||
|
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
s.PerformUploadDeviceKeys(req.Context(), &request, &response)
|
||||||
|
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
internalAPIMux.Handle(PerformUploadDeviceSignaturesPath,
|
||||||
|
httputil.MakeInternalAPI("performUploadDeviceSignatures", func(req *http.Request) util.JSONResponse {
|
||||||
|
request := api.PerformUploadDeviceSignaturesRequest{}
|
||||||
|
response := api.PerformUploadDeviceSignaturesResponse{}
|
||||||
|
if err := json.NewDecoder(req.Body).Decode(&request.Signatures); err != nil {
|
||||||
|
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
s.PerformUploadDeviceSignatures(req.Context(), &request, &response)
|
||||||
|
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
|
||||||
|
}),
|
||||||
|
)
|
||||||
internalAPIMux.Handle(QueryKeysPath,
|
internalAPIMux.Handle(QueryKeysPath,
|
||||||
httputil.MakeInternalAPI("queryKeys", func(req *http.Request) util.JSONResponse {
|
httputil.MakeInternalAPI("queryKeys", func(req *http.Request) util.JSONResponse {
|
||||||
request := api.QueryKeysRequest{}
|
request := api.QueryKeysRequest{}
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,12 @@ import (
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
fedsenderapi "github.com/matrix-org/dendrite/federationsender/api"
|
fedsenderapi "github.com/matrix-org/dendrite/federationsender/api"
|
||||||
"github.com/matrix-org/dendrite/keyserver/api"
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/consumers"
|
||||||
"github.com/matrix-org/dendrite/keyserver/internal"
|
"github.com/matrix-org/dendrite/keyserver/internal"
|
||||||
"github.com/matrix-org/dendrite/keyserver/inthttp"
|
"github.com/matrix-org/dendrite/keyserver/inthttp"
|
||||||
"github.com/matrix-org/dendrite/keyserver/producers"
|
"github.com/matrix-org/dendrite/keyserver/producers"
|
||||||
"github.com/matrix-org/dendrite/keyserver/storage"
|
"github.com/matrix-org/dendrite/keyserver/storage"
|
||||||
|
"github.com/matrix-org/dendrite/setup"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
"github.com/matrix-org/dendrite/setup/kafka"
|
"github.com/matrix-org/dendrite/setup/kafka"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
@ -36,9 +38,9 @@ func AddInternalRoutes(router *mux.Router, intAPI api.KeyInternalAPI) {
|
||||||
// NewInternalAPI returns a concerete implementation of the internal API. Callers
|
// NewInternalAPI returns a concerete implementation of the internal API. Callers
|
||||||
// can call functions directly on the returned API or via an HTTP interface using AddInternalRoutes.
|
// can call functions directly on the returned API or via an HTTP interface using AddInternalRoutes.
|
||||||
func NewInternalAPI(
|
func NewInternalAPI(
|
||||||
cfg *config.KeyServer, fedClient fedsenderapi.FederationClient,
|
base *setup.BaseDendrite, cfg *config.KeyServer, fedClient fedsenderapi.FederationClient,
|
||||||
) api.KeyInternalAPI {
|
) api.KeyInternalAPI {
|
||||||
_, producer := kafka.SetupConsumerProducer(&cfg.Matrix.Kafka)
|
consumer, producer := kafka.SetupConsumerProducer(&cfg.Matrix.Kafka)
|
||||||
|
|
||||||
db, err := storage.NewDatabase(&cfg.Database)
|
db, err := storage.NewDatabase(&cfg.Database)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -49,17 +51,26 @@ func NewInternalAPI(
|
||||||
Producer: producer,
|
Producer: producer,
|
||||||
DB: db,
|
DB: db,
|
||||||
}
|
}
|
||||||
updater := internal.NewDeviceListUpdater(db, keyChangeProducer, fedClient, 8) // 8 workers TODO: configurable
|
ap := &internal.KeyInternalAPI{
|
||||||
|
DB: db,
|
||||||
|
ThisServer: cfg.Matrix.ServerName,
|
||||||
|
FedClient: fedClient,
|
||||||
|
Producer: keyChangeProducer,
|
||||||
|
}
|
||||||
|
updater := internal.NewDeviceListUpdater(db, ap, keyChangeProducer, fedClient, 8) // 8 workers TODO: configurable
|
||||||
|
ap.Updater = updater
|
||||||
go func() {
|
go func() {
|
||||||
if err := updater.Start(); err != nil {
|
if err := updater.Start(); err != nil {
|
||||||
logrus.WithError(err).Panicf("failed to start device list updater")
|
logrus.WithError(err).Panicf("failed to start device list updater")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return &internal.KeyInternalAPI{
|
|
||||||
DB: db,
|
keyconsumer := consumers.NewOutputSigningKeyUpdateConsumer(
|
||||||
ThisServer: cfg.Matrix.ServerName,
|
base.ProcessContext, base.Cfg, consumer, db, ap,
|
||||||
FedClient: fedClient,
|
)
|
||||||
Producer: keyChangeProducer,
|
if err := keyconsumer.Start(); err != nil {
|
||||||
Updater: updater,
|
logrus.WithError(err).Panicf("failed to start keyserver EDU server consumer")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ap
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,15 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/internal"
|
||||||
"github.com/matrix-org/dendrite/keyserver/api"
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/types"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Database interface {
|
type Database interface {
|
||||||
|
internal.PartitionStorer
|
||||||
|
|
||||||
// ExistingOneTimeKeys returns a map of keyIDWithAlgorithm to key JSON for the given parameters. If no keys exist with this combination
|
// ExistingOneTimeKeys returns a map of keyIDWithAlgorithm to key JSON for the given parameters. If no keys exist with this combination
|
||||||
// of user/device/key/algorithm 4-uple then it is omitted from the map. Returns an error when failing to communicate with the database.
|
// of user/device/key/algorithm 4-uple then it is omitted from the map. Returns an error when failing to communicate with the database.
|
||||||
ExistingOneTimeKeys(ctx context.Context, userID, deviceID string, keyIDsWithAlgorithms []string) (map[string]json.RawMessage, error)
|
ExistingOneTimeKeys(ctx context.Context, userID, deviceID string, keyIDsWithAlgorithms []string) (map[string]json.RawMessage, error)
|
||||||
|
|
@ -73,4 +77,10 @@ type Database interface {
|
||||||
|
|
||||||
// MarkDeviceListStale sets the stale bit for this user to isStale.
|
// MarkDeviceListStale sets the stale bit for this user to isStale.
|
||||||
MarkDeviceListStale(ctx context.Context, userID string, isStale bool) error
|
MarkDeviceListStale(ctx context.Context, userID string, isStale bool) error
|
||||||
|
|
||||||
|
CrossSigningKeysForUser(ctx context.Context, userID string) (types.CrossSigningKeyMap, error)
|
||||||
|
CrossSigningSigsForTarget(ctx context.Context, targetUserID string, targetKeyID gomatrixserverlib.KeyID) (types.CrossSigningSigMap, error)
|
||||||
|
|
||||||
|
StoreCrossSigningKeysForUser(ctx context.Context, userID string, keyMap types.CrossSigningKeyMap) error
|
||||||
|
StoreCrossSigningSigsForTarget(ctx context.Context, originUserID string, originKeyID gomatrixserverlib.KeyID, targetUserID string, targetKeyID gomatrixserverlib.KeyID, signature gomatrixserverlib.Base64Bytes) error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
102
keyserver/storage/postgres/cross_signing_keys_table.go
Normal file
102
keyserver/storage/postgres/cross_signing_keys_table.go
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
// 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 postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/internal"
|
||||||
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/storage/tables"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/types"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
)
|
||||||
|
|
||||||
|
var crossSigningKeysSchema = `
|
||||||
|
CREATE TABLE IF NOT EXISTS keyserver_cross_signing_keys (
|
||||||
|
user_id TEXT NOT NULL,
|
||||||
|
key_type SMALLINT NOT NULL,
|
||||||
|
key_data TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (user_id, key_type)
|
||||||
|
);
|
||||||
|
`
|
||||||
|
|
||||||
|
const selectCrossSigningKeysForUserSQL = "" +
|
||||||
|
"SELECT key_type, key_data FROM keyserver_cross_signing_keys" +
|
||||||
|
" WHERE user_id = $1"
|
||||||
|
|
||||||
|
const upsertCrossSigningKeysForUserSQL = "" +
|
||||||
|
"INSERT INTO keyserver_cross_signing_keys (user_id, key_type, key_data)" +
|
||||||
|
" VALUES($1, $2, $3)" +
|
||||||
|
" ON CONFLICT (user_id, key_type) DO UPDATE SET key_data = $3"
|
||||||
|
|
||||||
|
type crossSigningKeysStatements struct {
|
||||||
|
db *sql.DB
|
||||||
|
selectCrossSigningKeysForUserStmt *sql.Stmt
|
||||||
|
upsertCrossSigningKeysForUserStmt *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPostgresCrossSigningKeysTable(db *sql.DB) (tables.CrossSigningKeys, error) {
|
||||||
|
s := &crossSigningKeysStatements{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
_, err := db.Exec(crossSigningKeysSchema)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, sqlutil.StatementList{
|
||||||
|
{&s.selectCrossSigningKeysForUserStmt, selectCrossSigningKeysForUserSQL},
|
||||||
|
{&s.upsertCrossSigningKeysForUserStmt, upsertCrossSigningKeysForUserSQL},
|
||||||
|
}.Prepare(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *crossSigningKeysStatements) SelectCrossSigningKeysForUser(
|
||||||
|
ctx context.Context, txn *sql.Tx, userID string,
|
||||||
|
) (r types.CrossSigningKeyMap, err error) {
|
||||||
|
rows, err := sqlutil.TxStmt(txn, s.selectCrossSigningKeysForUserStmt).QueryContext(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer internal.CloseAndLogIfError(ctx, rows, "selectCrossSigningKeysForUserStmt: rows.close() failed")
|
||||||
|
r = types.CrossSigningKeyMap{}
|
||||||
|
for rows.Next() {
|
||||||
|
var keyTypeInt int16
|
||||||
|
var keyData gomatrixserverlib.Base64Bytes
|
||||||
|
if err := rows.Scan(&keyTypeInt, &keyData); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
keyType, ok := types.KeyTypeIntToPurpose[keyTypeInt]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unknown key purpose int %d", keyTypeInt)
|
||||||
|
}
|
||||||
|
r[keyType] = keyData
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *crossSigningKeysStatements) UpsertCrossSigningKeysForUser(
|
||||||
|
ctx context.Context, txn *sql.Tx, userID string, keyType gomatrixserverlib.CrossSigningKeyPurpose, keyData gomatrixserverlib.Base64Bytes,
|
||||||
|
) error {
|
||||||
|
keyTypeInt, ok := types.KeyTypePurposeToInt[keyType]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unknown key purpose %q", keyType)
|
||||||
|
}
|
||||||
|
if _, err := sqlutil.TxStmt(txn, s.upsertCrossSigningKeysForUserStmt).ExecContext(ctx, userID, keyTypeInt, keyData); err != nil {
|
||||||
|
return fmt.Errorf("s.upsertCrossSigningKeysForUserStmt: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
103
keyserver/storage/postgres/cross_signing_sigs_table.go
Normal file
103
keyserver/storage/postgres/cross_signing_sigs_table.go
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
// 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 postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/internal"
|
||||||
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/storage/tables"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/types"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
)
|
||||||
|
|
||||||
|
var crossSigningSigsSchema = `
|
||||||
|
CREATE TABLE IF NOT EXISTS keyserver_cross_signing_sigs (
|
||||||
|
origin_user_id TEXT NOT NULL,
|
||||||
|
origin_key_id TEXT NOT NULL,
|
||||||
|
target_user_id TEXT NOT NULL,
|
||||||
|
target_key_id TEXT NOT NULL,
|
||||||
|
signature TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (origin_user_id, target_user_id, target_key_id)
|
||||||
|
);
|
||||||
|
`
|
||||||
|
|
||||||
|
const selectCrossSigningSigsForTargetSQL = "" +
|
||||||
|
"SELECT origin_user_id, origin_key_id, signature FROM keyserver_cross_signing_sigs" +
|
||||||
|
" WHERE target_user_id = $1 AND target_key_id = $2"
|
||||||
|
|
||||||
|
const upsertCrossSigningSigsForTargetSQL = "" +
|
||||||
|
"INSERT INTO keyserver_cross_signing_sigs (origin_user_id, origin_key_id, target_user_id, target_key_id, signature)" +
|
||||||
|
" VALUES($1, $2, $3, $4, $5)" +
|
||||||
|
" ON CONFLICT (origin_user_id, target_user_id, target_key_id) DO UPDATE SET (origin_key_id, signature) = ($2, $5)"
|
||||||
|
|
||||||
|
type crossSigningSigsStatements struct {
|
||||||
|
db *sql.DB
|
||||||
|
selectCrossSigningSigsForTargetStmt *sql.Stmt
|
||||||
|
upsertCrossSigningSigsForTargetStmt *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPostgresCrossSigningSigsTable(db *sql.DB) (tables.CrossSigningSigs, error) {
|
||||||
|
s := &crossSigningSigsStatements{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
_, err := db.Exec(crossSigningSigsSchema)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, sqlutil.StatementList{
|
||||||
|
{&s.selectCrossSigningSigsForTargetStmt, selectCrossSigningSigsForTargetSQL},
|
||||||
|
{&s.upsertCrossSigningSigsForTargetStmt, upsertCrossSigningSigsForTargetSQL},
|
||||||
|
}.Prepare(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *crossSigningSigsStatements) SelectCrossSigningSigsForTarget(
|
||||||
|
ctx context.Context, txn *sql.Tx, targetUserID string, targetKeyID gomatrixserverlib.KeyID,
|
||||||
|
) (r types.CrossSigningSigMap, err error) {
|
||||||
|
rows, err := sqlutil.TxStmt(txn, s.selectCrossSigningSigsForTargetStmt).QueryContext(ctx, targetUserID, targetKeyID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer internal.CloseAndLogIfError(ctx, rows, "selectCrossSigningSigsForTargetStmt: rows.close() failed")
|
||||||
|
r = types.CrossSigningSigMap{}
|
||||||
|
for rows.Next() {
|
||||||
|
var userID string
|
||||||
|
var keyID gomatrixserverlib.KeyID
|
||||||
|
var signature gomatrixserverlib.Base64Bytes
|
||||||
|
if err := rows.Scan(&userID, &keyID, &signature); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, ok := r[userID]; !ok {
|
||||||
|
r[userID] = map[gomatrixserverlib.KeyID]gomatrixserverlib.Base64Bytes{}
|
||||||
|
}
|
||||||
|
r[userID][keyID] = signature
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *crossSigningSigsStatements) UpsertCrossSigningSigsForTarget(
|
||||||
|
ctx context.Context, txn *sql.Tx,
|
||||||
|
originUserID string, originKeyID gomatrixserverlib.KeyID,
|
||||||
|
targetUserID string, targetKeyID gomatrixserverlib.KeyID,
|
||||||
|
signature gomatrixserverlib.Base64Bytes,
|
||||||
|
) error {
|
||||||
|
if _, err := sqlutil.TxStmt(txn, s.upsertCrossSigningSigsForTargetStmt).ExecContext(ctx, originUserID, originKeyID, targetUserID, targetKeyID, signature); err != nil {
|
||||||
|
return fmt.Errorf("s.upsertCrossSigningSigsForTargetStmt: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -43,12 +43,26 @@ func NewDatabase(dbProperties *config.DatabaseOptions) (*shared.Database, error)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &shared.Database{
|
csk, err := NewPostgresCrossSigningKeysTable(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
css, err := NewPostgresCrossSigningSigsTable(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
d := &shared.Database{
|
||||||
DB: db,
|
DB: db,
|
||||||
Writer: sqlutil.NewDummyWriter(),
|
Writer: sqlutil.NewDummyWriter(),
|
||||||
OneTimeKeysTable: otk,
|
OneTimeKeysTable: otk,
|
||||||
DeviceKeysTable: dk,
|
DeviceKeysTable: dk,
|
||||||
KeyChangesTable: kc,
|
KeyChangesTable: kc,
|
||||||
StaleDeviceListsTable: sdl,
|
StaleDeviceListsTable: sdl,
|
||||||
}, nil
|
CrossSigningKeysTable: csk,
|
||||||
|
CrossSigningSigsTable: css,
|
||||||
|
}
|
||||||
|
if err = d.PartitionOffsetStatements.Prepare(db, d.Writer, "keyserver"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,12 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
"github.com/matrix-org/dendrite/keyserver/api"
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
"github.com/matrix-org/dendrite/keyserver/storage/tables"
|
"github.com/matrix-org/dendrite/keyserver/storage/tables"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/types"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -32,6 +34,9 @@ type Database struct {
|
||||||
DeviceKeysTable tables.DeviceKeys
|
DeviceKeysTable tables.DeviceKeys
|
||||||
KeyChangesTable tables.KeyChanges
|
KeyChangesTable tables.KeyChanges
|
||||||
StaleDeviceListsTable tables.StaleDeviceLists
|
StaleDeviceListsTable tables.StaleDeviceLists
|
||||||
|
CrossSigningKeysTable tables.CrossSigningKeys
|
||||||
|
CrossSigningSigsTable tables.CrossSigningSigs
|
||||||
|
sqlutil.PartitionOffsetStatements
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Database) ExistingOneTimeKeys(ctx context.Context, userID, deviceID string, keyIDsWithAlgorithms []string) (map[string]json.RawMessage, error) {
|
func (d *Database) ExistingOneTimeKeys(ctx context.Context, userID, deviceID string, keyIDsWithAlgorithms []string) (map[string]json.RawMessage, error) {
|
||||||
|
|
@ -152,3 +157,40 @@ func (d *Database) MarkDeviceListStale(ctx context.Context, userID string, isSta
|
||||||
return d.StaleDeviceListsTable.InsertStaleDeviceList(ctx, userID, isStale)
|
return d.StaleDeviceListsTable.InsertStaleDeviceList(ctx, userID, isStale)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CrossSigningKeysForUser returns the latest known cross-signing keys for a user, if any.
|
||||||
|
func (d *Database) CrossSigningKeysForUser(ctx context.Context, userID string) (types.CrossSigningKeyMap, error) {
|
||||||
|
return d.CrossSigningKeysTable.SelectCrossSigningKeysForUser(ctx, nil, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CrossSigningSigsForTarget returns the signatures for a given user's key ID, if any.
|
||||||
|
func (d *Database) CrossSigningSigsForTarget(ctx context.Context, targetUserID string, targetKeyID gomatrixserverlib.KeyID) (types.CrossSigningSigMap, error) {
|
||||||
|
return d.CrossSigningSigsTable.SelectCrossSigningSigsForTarget(ctx, nil, targetUserID, targetKeyID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreCrossSigningKeysForUser stores the latest known cross-signing keys for a user.
|
||||||
|
func (d *Database) StoreCrossSigningKeysForUser(ctx context.Context, userID string, keyMap types.CrossSigningKeyMap) error {
|
||||||
|
return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||||
|
for keyType, keyData := range keyMap {
|
||||||
|
if err := d.CrossSigningKeysTable.UpsertCrossSigningKeysForUser(ctx, txn, userID, keyType, keyData); err != nil {
|
||||||
|
return fmt.Errorf("d.CrossSigningKeysTable.InsertCrossSigningKeysForUser: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreCrossSigningSigsForTarget stores a signature for a target user ID and key/dvice.
|
||||||
|
func (d *Database) StoreCrossSigningSigsForTarget(
|
||||||
|
ctx context.Context,
|
||||||
|
originUserID string, originKeyID gomatrixserverlib.KeyID,
|
||||||
|
targetUserID string, targetKeyID gomatrixserverlib.KeyID,
|
||||||
|
signature gomatrixserverlib.Base64Bytes,
|
||||||
|
) error {
|
||||||
|
return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||||
|
if err := d.CrossSigningSigsTable.UpsertCrossSigningSigsForTarget(ctx, nil, originUserID, originKeyID, targetUserID, targetKeyID, signature); err != nil {
|
||||||
|
return fmt.Errorf("d.CrossSigningSigsTable.InsertCrossSigningSigsForTarget: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
101
keyserver/storage/sqlite3/cross_signing_keys_table.go
Normal file
101
keyserver/storage/sqlite3/cross_signing_keys_table.go
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
// 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 sqlite3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/internal"
|
||||||
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/storage/tables"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/types"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
)
|
||||||
|
|
||||||
|
var crossSigningKeysSchema = `
|
||||||
|
CREATE TABLE IF NOT EXISTS keyserver_cross_signing_keys (
|
||||||
|
user_id TEXT NOT NULL,
|
||||||
|
key_type INTEGER NOT NULL,
|
||||||
|
key_data TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (user_id, key_type)
|
||||||
|
);
|
||||||
|
`
|
||||||
|
|
||||||
|
const selectCrossSigningKeysForUserSQL = "" +
|
||||||
|
"SELECT key_type, key_data FROM keyserver_cross_signing_keys" +
|
||||||
|
" WHERE user_id = $1"
|
||||||
|
|
||||||
|
const upsertCrossSigningKeysForUserSQL = "" +
|
||||||
|
"INSERT OR REPLACE INTO keyserver_cross_signing_keys (user_id, key_type, key_data)" +
|
||||||
|
" VALUES($1, $2, $3)"
|
||||||
|
|
||||||
|
type crossSigningKeysStatements struct {
|
||||||
|
db *sql.DB
|
||||||
|
selectCrossSigningKeysForUserStmt *sql.Stmt
|
||||||
|
upsertCrossSigningKeysForUserStmt *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSqliteCrossSigningKeysTable(db *sql.DB) (tables.CrossSigningKeys, error) {
|
||||||
|
s := &crossSigningKeysStatements{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
_, err := db.Exec(crossSigningKeysSchema)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, sqlutil.StatementList{
|
||||||
|
{&s.selectCrossSigningKeysForUserStmt, selectCrossSigningKeysForUserSQL},
|
||||||
|
{&s.upsertCrossSigningKeysForUserStmt, upsertCrossSigningKeysForUserSQL},
|
||||||
|
}.Prepare(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *crossSigningKeysStatements) SelectCrossSigningKeysForUser(
|
||||||
|
ctx context.Context, txn *sql.Tx, userID string,
|
||||||
|
) (r types.CrossSigningKeyMap, err error) {
|
||||||
|
rows, err := sqlutil.TxStmt(txn, s.selectCrossSigningKeysForUserStmt).QueryContext(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer internal.CloseAndLogIfError(ctx, rows, "selectCrossSigningKeysForUserStmt: rows.close() failed")
|
||||||
|
r = types.CrossSigningKeyMap{}
|
||||||
|
for rows.Next() {
|
||||||
|
var keyTypeInt int16
|
||||||
|
var keyData gomatrixserverlib.Base64Bytes
|
||||||
|
if err := rows.Scan(&keyTypeInt, &keyData); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
keyType, ok := types.KeyTypeIntToPurpose[keyTypeInt]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unknown key purpose int %d", keyTypeInt)
|
||||||
|
}
|
||||||
|
r[keyType] = keyData
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *crossSigningKeysStatements) UpsertCrossSigningKeysForUser(
|
||||||
|
ctx context.Context, txn *sql.Tx, userID string, keyType gomatrixserverlib.CrossSigningKeyPurpose, keyData gomatrixserverlib.Base64Bytes,
|
||||||
|
) error {
|
||||||
|
keyTypeInt, ok := types.KeyTypePurposeToInt[keyType]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unknown key purpose %q", keyType)
|
||||||
|
}
|
||||||
|
if _, err := sqlutil.TxStmt(txn, s.upsertCrossSigningKeysForUserStmt).ExecContext(ctx, userID, keyTypeInt, keyData); err != nil {
|
||||||
|
return fmt.Errorf("s.upsertCrossSigningKeysForUserStmt: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
102
keyserver/storage/sqlite3/cross_signing_sigs_table.go
Normal file
102
keyserver/storage/sqlite3/cross_signing_sigs_table.go
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
// 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 sqlite3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/internal"
|
||||||
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/storage/tables"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/types"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
)
|
||||||
|
|
||||||
|
var crossSigningSigsSchema = `
|
||||||
|
CREATE TABLE IF NOT EXISTS keyserver_cross_signing_sigs (
|
||||||
|
origin_user_id TEXT NOT NULL,
|
||||||
|
origin_key_id TEXT NOT NULL,
|
||||||
|
target_user_id TEXT NOT NULL,
|
||||||
|
target_key_id TEXT NOT NULL,
|
||||||
|
signature TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (origin_user_id, target_user_id, target_key_id)
|
||||||
|
);
|
||||||
|
`
|
||||||
|
|
||||||
|
const selectCrossSigningSigsForTargetSQL = "" +
|
||||||
|
"SELECT origin_user_id, origin_key_id, signature FROM keyserver_cross_signing_sigs" +
|
||||||
|
" WHERE target_user_id = $1 AND target_key_id = $2"
|
||||||
|
|
||||||
|
const upsertCrossSigningSigsForTargetSQL = "" +
|
||||||
|
"INSERT OR REPLACE INTO keyserver_cross_signing_sigs (origin_user_id, origin_key_id, target_user_id, target_key_id, signature)" +
|
||||||
|
" VALUES($1, $2, $3, $4, $5)"
|
||||||
|
|
||||||
|
type crossSigningSigsStatements struct {
|
||||||
|
db *sql.DB
|
||||||
|
selectCrossSigningSigsForTargetStmt *sql.Stmt
|
||||||
|
upsertCrossSigningSigsForTargetStmt *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSqliteCrossSigningSigsTable(db *sql.DB) (tables.CrossSigningSigs, error) {
|
||||||
|
s := &crossSigningSigsStatements{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
_, err := db.Exec(crossSigningSigsSchema)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, sqlutil.StatementList{
|
||||||
|
{&s.selectCrossSigningSigsForTargetStmt, selectCrossSigningSigsForTargetSQL},
|
||||||
|
{&s.upsertCrossSigningSigsForTargetStmt, upsertCrossSigningSigsForTargetSQL},
|
||||||
|
}.Prepare(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *crossSigningSigsStatements) SelectCrossSigningSigsForTarget(
|
||||||
|
ctx context.Context, txn *sql.Tx, targetUserID string, targetKeyID gomatrixserverlib.KeyID,
|
||||||
|
) (r types.CrossSigningSigMap, err error) {
|
||||||
|
rows, err := sqlutil.TxStmt(txn, s.selectCrossSigningSigsForTargetStmt).QueryContext(ctx, targetUserID, targetKeyID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer internal.CloseAndLogIfError(ctx, rows, "selectCrossSigningSigsForTargetStmt: rows.close() failed")
|
||||||
|
r = types.CrossSigningSigMap{}
|
||||||
|
for rows.Next() {
|
||||||
|
var userID string
|
||||||
|
var keyID gomatrixserverlib.KeyID
|
||||||
|
var signature gomatrixserverlib.Base64Bytes
|
||||||
|
if err := rows.Scan(&userID, &keyID, &signature); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, ok := r[userID]; !ok {
|
||||||
|
r[userID] = map[gomatrixserverlib.KeyID]gomatrixserverlib.Base64Bytes{}
|
||||||
|
}
|
||||||
|
r[userID][keyID] = signature
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *crossSigningSigsStatements) UpsertCrossSigningSigsForTarget(
|
||||||
|
ctx context.Context, txn *sql.Tx,
|
||||||
|
originUserID string, originKeyID gomatrixserverlib.KeyID,
|
||||||
|
targetUserID string, targetKeyID gomatrixserverlib.KeyID,
|
||||||
|
signature gomatrixserverlib.Base64Bytes,
|
||||||
|
) error {
|
||||||
|
if _, err := sqlutil.TxStmt(txn, s.upsertCrossSigningSigsForTargetStmt).ExecContext(ctx, originUserID, originKeyID, targetUserID, targetKeyID, signature); err != nil {
|
||||||
|
return fmt.Errorf("s.upsertCrossSigningSigsForTargetStmt: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -41,12 +41,26 @@ func NewDatabase(dbProperties *config.DatabaseOptions) (*shared.Database, error)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &shared.Database{
|
csk, err := NewSqliteCrossSigningKeysTable(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
css, err := NewSqliteCrossSigningSigsTable(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
d := &shared.Database{
|
||||||
DB: db,
|
DB: db,
|
||||||
Writer: sqlutil.NewExclusiveWriter(),
|
Writer: sqlutil.NewExclusiveWriter(),
|
||||||
OneTimeKeysTable: otk,
|
OneTimeKeysTable: otk,
|
||||||
DeviceKeysTable: dk,
|
DeviceKeysTable: dk,
|
||||||
KeyChangesTable: kc,
|
KeyChangesTable: kc,
|
||||||
StaleDeviceListsTable: sdl,
|
StaleDeviceListsTable: sdl,
|
||||||
}, nil
|
CrossSigningKeysTable: csk,
|
||||||
|
CrossSigningSigsTable: css,
|
||||||
|
}
|
||||||
|
if err = d.PartitionOffsetStatements.Prepare(db, d.Writer, "keyserver"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/keyserver/api"
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/types"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -52,3 +53,13 @@ type StaleDeviceLists interface {
|
||||||
InsertStaleDeviceList(ctx context.Context, userID string, isStale bool) error
|
InsertStaleDeviceList(ctx context.Context, userID string, isStale bool) error
|
||||||
SelectUserIDsWithStaleDeviceLists(ctx context.Context, domains []gomatrixserverlib.ServerName) ([]string, error)
|
SelectUserIDsWithStaleDeviceLists(ctx context.Context, domains []gomatrixserverlib.ServerName) ([]string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CrossSigningKeys interface {
|
||||||
|
SelectCrossSigningKeysForUser(ctx context.Context, txn *sql.Tx, userID string) (r types.CrossSigningKeyMap, err error)
|
||||||
|
UpsertCrossSigningKeysForUser(ctx context.Context, txn *sql.Tx, userID string, keyType gomatrixserverlib.CrossSigningKeyPurpose, keyData gomatrixserverlib.Base64Bytes) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type CrossSigningSigs interface {
|
||||||
|
SelectCrossSigningSigsForTarget(ctx context.Context, txn *sql.Tx, targetUserID string, targetKeyID gomatrixserverlib.KeyID) (r types.CrossSigningSigMap, err error)
|
||||||
|
UpsertCrossSigningSigsForTarget(ctx context.Context, txn *sql.Tx, originUserID string, originKeyID gomatrixserverlib.KeyID, targetUserID string, targetKeyID gomatrixserverlib.KeyID, signature gomatrixserverlib.Base64Bytes) error
|
||||||
|
}
|
||||||
|
|
|
||||||
39
keyserver/types/storage.go
Normal file
39
keyserver/types/storage.go
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
// 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 types
|
||||||
|
|
||||||
|
import "github.com/matrix-org/gomatrixserverlib"
|
||||||
|
|
||||||
|
// KeyTypePurposeToInt maps a purpose to an integer, which is used in the
|
||||||
|
// database to reduce the amount of space taken up by this column.
|
||||||
|
var KeyTypePurposeToInt = map[gomatrixserverlib.CrossSigningKeyPurpose]int16{
|
||||||
|
gomatrixserverlib.CrossSigningKeyPurposeMaster: 1,
|
||||||
|
gomatrixserverlib.CrossSigningKeyPurposeSelfSigning: 2,
|
||||||
|
gomatrixserverlib.CrossSigningKeyPurposeUserSigning: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyTypeIntToPurpose maps an integer to a purpose, which is used in the
|
||||||
|
// database to reduce the amount of space taken up by this column.
|
||||||
|
var KeyTypeIntToPurpose = map[int16]gomatrixserverlib.CrossSigningKeyPurpose{
|
||||||
|
1: gomatrixserverlib.CrossSigningKeyPurposeMaster,
|
||||||
|
2: gomatrixserverlib.CrossSigningKeyPurposeSelfSigning,
|
||||||
|
3: gomatrixserverlib.CrossSigningKeyPurposeUserSigning,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map of purpose -> public key
|
||||||
|
type CrossSigningKeyMap map[gomatrixserverlib.CrossSigningKeyPurpose]gomatrixserverlib.Base64Bytes
|
||||||
|
|
||||||
|
// Map of user ID -> key ID -> signature
|
||||||
|
type CrossSigningSigMap map[string]map[gomatrixserverlib.KeyID]gomatrixserverlib.Base64Bytes
|
||||||
|
|
@ -99,6 +99,18 @@ func UpStateBlocksRefactor(tx *sql.Tx) error {
|
||||||
return fmt.Errorf("tx.Exec (create snapshots table): %w", err)
|
return fmt.Errorf("tx.Exec (create snapshots table): %w", err)
|
||||||
}
|
}
|
||||||
logrus.Warn("New tables created...")
|
logrus.Warn("New tables created...")
|
||||||
|
// some m.room.create events have a state snapshot but no state blocks at all which makes
|
||||||
|
// sense as there is no state before creation. The correct form should be to give the event
|
||||||
|
// in question a state snapshot NID of 0 to indicate 'no snapshot'.
|
||||||
|
// If we don't do this, we'll fail the assertions later on which try to ensure we didn't forget
|
||||||
|
// any snapshots.
|
||||||
|
_, err = tx.Exec(
|
||||||
|
`UPDATE roomserver_events SET state_snapshot_nid = 0 WHERE event_type_nid = $1 AND event_state_key_nid = $2`,
|
||||||
|
types.MRoomCreateNID, types.EmptyStateKeyNID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("resetting create events snapshots to 0 errored: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
batchsize := 100
|
batchsize := 100
|
||||||
for batchoffset := 0; batchoffset < snapshotcount; batchoffset += batchsize {
|
for batchoffset := 0; batchoffset < snapshotcount; batchoffset += batchsize {
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,20 @@ func UpStateBlocksRefactor(tx *sql.Tx) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var newblocks types.StateBlockNIDs
|
var newblocks types.StateBlockNIDs
|
||||||
|
if len(blocks) == 0 {
|
||||||
|
// some m.room.create events have a state snapshot but no state blocks at all which makes
|
||||||
|
// sense as there is no state before creation. The correct form should be to give the event
|
||||||
|
// in question a state snapshot NID of 0 to indicate 'no snapshot'.
|
||||||
|
// If we don't do this, we'll fail the assertions later on which try to ensure we didn't forget
|
||||||
|
// any snapshots.
|
||||||
|
_, err = tx.Exec(
|
||||||
|
`UPDATE roomserver_events SET state_snapshot_nid = 0 WHERE event_type_nid = $1 AND event_state_key_nid = $2 AND state_snapshot_nid = $3`,
|
||||||
|
types.MRoomCreateNID, types.EmptyStateKeyNID, snapshot,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("resetting create events snapshots to 0 errored: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, block := range blocks {
|
for _, block := range blocks {
|
||||||
if err = func() error {
|
if err = func() error {
|
||||||
blockrows, berr := tx.Query(`SELECT event_nid FROM _roomserver_state_block WHERE state_block_nid = $1`, block)
|
blockrows, berr := tx.Query(`SELECT event_nid FROM _roomserver_state_block WHERE state_block_nid = $1`, block)
|
||||||
|
|
|
||||||
|
|
@ -571,6 +571,9 @@ func (s *eventStatements) SelectRoomNIDsForEventNIDs(
|
||||||
}
|
}
|
||||||
|
|
||||||
func eventNIDsAsArray(eventNIDs []types.EventNID) string {
|
func eventNIDsAsArray(eventNIDs []types.EventNID) string {
|
||||||
|
if eventNIDs == nil {
|
||||||
|
eventNIDs = []types.EventNID{} // don't store 'null' in the DB
|
||||||
|
}
|
||||||
b, _ := json.Marshal(eventNIDs)
|
b, _ := json.Marshal(eventNIDs)
|
||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ func (s *stateBlockStatements) BulkInsertStateData(
|
||||||
entries types.StateEntries,
|
entries types.StateEntries,
|
||||||
) (id types.StateBlockNID, err error) {
|
) (id types.StateBlockNID, err error) {
|
||||||
entries = entries[:util.SortAndUnique(entries)]
|
entries = entries[:util.SortAndUnique(entries)]
|
||||||
var nids types.EventNIDs
|
nids := types.EventNIDs{} // zero slice to not store 'null' in the DB
|
||||||
for _, e := range entries {
|
for _, e := range entries {
|
||||||
nids = append(nids, e.EventNID)
|
nids = append(nids, e.EventNID)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,9 @@ func prepareStateSnapshotTable(db *sql.DB) (tables.StateSnapshot, error) {
|
||||||
func (s *stateSnapshotStatements) InsertState(
|
func (s *stateSnapshotStatements) InsertState(
|
||||||
ctx context.Context, txn *sql.Tx, roomNID types.RoomNID, stateBlockNIDs types.StateBlockNIDs,
|
ctx context.Context, txn *sql.Tx, roomNID types.RoomNID, stateBlockNIDs types.StateBlockNIDs,
|
||||||
) (stateNID types.StateSnapshotNID, err error) {
|
) (stateNID types.StateSnapshotNID, err error) {
|
||||||
|
if stateBlockNIDs == nil {
|
||||||
|
stateBlockNIDs = []types.StateBlockNID{} // zero slice to not store 'null' in the DB
|
||||||
|
}
|
||||||
stateBlockNIDs = stateBlockNIDs[:util.SortAndUnique(stateBlockNIDs)]
|
stateBlockNIDs = stateBlockNIDs[:util.SortAndUnique(stateBlockNIDs)]
|
||||||
stateBlockNIDsJSON, err := json.Marshal(stateBlockNIDs)
|
stateBlockNIDsJSON, err := json.Marshal(stateBlockNIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,11 @@ const (
|
||||||
TopicOutputRoomEvent = "OutputRoomEvent"
|
TopicOutputRoomEvent = "OutputRoomEvent"
|
||||||
TopicOutputClientData = "OutputClientData"
|
TopicOutputClientData = "OutputClientData"
|
||||||
TopicOutputReceiptEvent = "OutputReceiptEvent"
|
TopicOutputReceiptEvent = "OutputReceiptEvent"
|
||||||
|
<<<<<<< HEAD
|
||||||
TopicOutputPresenceData = "OutputPresenceData"
|
TopicOutputPresenceData = "OutputPresenceData"
|
||||||
|
=======
|
||||||
|
TopicOutputSigningKeyUpdate = "OutputSigningKeyUpdate"
|
||||||
|
>>>>>>> eb0efa46361ca2d80050ef871d0a8f20652398af
|
||||||
)
|
)
|
||||||
|
|
||||||
type Kafka struct {
|
type Kafka struct {
|
||||||
|
|
|
||||||
|
|
@ -497,51 +497,10 @@ func assertUnsignedChildren(t *testing.T, ev gomatrixserverlib.ClientEvent, relT
|
||||||
}
|
}
|
||||||
|
|
||||||
type testUserAPI struct {
|
type testUserAPI struct {
|
||||||
|
userapi.UserInternalAPITrace
|
||||||
accessTokens map[string]userapi.Device
|
accessTokens map[string]userapi.Device
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *testUserAPI) QueryPresenceAfter(ctx context.Context, req *userapi.QueryPresenceAfterRequest, res *userapi.QueryPresenceAfterResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *testUserAPI) InputPresenceData(ctx context.Context, req *userapi.InputPresenceRequest, res *userapi.InputPresenceResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *testUserAPI) QueryPresenceForUser(ctx context.Context, req *userapi.QueryPresenceForUserRequest, res *userapi.QueryPresenceForUserResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *testUserAPI) InputAccountData(ctx context.Context, req *userapi.InputAccountDataRequest, res *userapi.InputAccountDataResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) PerformAccountCreation(ctx context.Context, req *userapi.PerformAccountCreationRequest, res *userapi.PerformAccountCreationResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) PerformPasswordUpdate(ctx context.Context, req *userapi.PerformPasswordUpdateRequest, res *userapi.PerformPasswordUpdateResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) PerformDeviceCreation(ctx context.Context, req *userapi.PerformDeviceCreationRequest, res *userapi.PerformDeviceCreationResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) PerformDeviceDeletion(ctx context.Context, req *userapi.PerformDeviceDeletionRequest, res *userapi.PerformDeviceDeletionResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) PerformDeviceUpdate(ctx context.Context, req *userapi.PerformDeviceUpdateRequest, res *userapi.PerformDeviceUpdateResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) PerformLastSeenUpdate(ctx context.Context, req *userapi.PerformLastSeenUpdateRequest, res *userapi.PerformLastSeenUpdateResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) PerformAccountDeactivation(ctx context.Context, req *userapi.PerformAccountDeactivationRequest, res *userapi.PerformAccountDeactivationResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) PerformOpenIDTokenCreation(ctx context.Context, req *userapi.PerformOpenIDTokenCreationRequest, res *userapi.PerformOpenIDTokenCreationResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) QueryProfile(ctx context.Context, req *userapi.QueryProfileRequest, res *userapi.QueryProfileResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) QueryAccessToken(ctx context.Context, req *userapi.QueryAccessTokenRequest, res *userapi.QueryAccessTokenResponse) error {
|
func (u *testUserAPI) QueryAccessToken(ctx context.Context, req *userapi.QueryAccessTokenRequest, res *userapi.QueryAccessTokenResponse) error {
|
||||||
dev, ok := u.accessTokens[req.AccessToken]
|
dev, ok := u.accessTokens[req.AccessToken]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
@ -551,25 +510,6 @@ func (u *testUserAPI) QueryAccessToken(ctx context.Context, req *userapi.QueryAc
|
||||||
res.Device = &dev
|
res.Device = &dev
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (u *testUserAPI) QueryDevices(ctx context.Context, req *userapi.QueryDevicesRequest, res *userapi.QueryDevicesResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) QueryAccountData(ctx context.Context, req *userapi.QueryAccountDataRequest, res *userapi.QueryAccountDataResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) QueryDeviceInfos(ctx context.Context, req *userapi.QueryDeviceInfosRequest, res *userapi.QueryDeviceInfosResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) QuerySearchProfiles(ctx context.Context, req *userapi.QuerySearchProfilesRequest, res *userapi.QuerySearchProfilesResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) QueryOpenIDToken(ctx context.Context, req *userapi.QueryOpenIDTokenRequest, res *userapi.QueryOpenIDTokenResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) PerformKeyBackup(ctx context.Context, req *userapi.PerformKeyBackupRequest, res *userapi.PerformKeyBackupResponse) {
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) QueryKeyBackup(ctx context.Context, req *userapi.QueryKeyBackupRequest, res *userapi.QueryKeyBackupResponse) {
|
|
||||||
}
|
|
||||||
|
|
||||||
type testRoomserverAPI struct {
|
type testRoomserverAPI struct {
|
||||||
// use a trace API as it implements method stubs so we don't need to have them here.
|
// use a trace API as it implements method stubs so we don't need to have them here.
|
||||||
|
|
|
||||||
|
|
@ -340,55 +340,10 @@ func postSpaces(t *testing.T, expectCode int, accessToken, roomID string, req *g
|
||||||
}
|
}
|
||||||
|
|
||||||
type testUserAPI struct {
|
type testUserAPI struct {
|
||||||
|
userapi.UserInternalAPITrace
|
||||||
accessTokens map[string]userapi.Device
|
accessTokens map[string]userapi.Device
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *testUserAPI) QueryPresenceAfter(ctx context.Context, req *userapi.QueryPresenceAfterRequest, res *userapi.QueryPresenceAfterResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *testUserAPI) InputPresenceData(ctx context.Context, req *userapi.InputPresenceRequest, res *userapi.InputPresenceResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *testUserAPI) QueryPresenceForUser(ctx context.Context, req *userapi.QueryPresenceForUserRequest, res *userapi.QueryPresenceForUserResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *testUserAPI) InputAccountData(ctx context.Context, req *userapi.InputAccountDataRequest, res *userapi.InputAccountDataResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) PerformAccountCreation(ctx context.Context, req *userapi.PerformAccountCreationRequest, res *userapi.PerformAccountCreationResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) PerformPasswordUpdate(ctx context.Context, req *userapi.PerformPasswordUpdateRequest, res *userapi.PerformPasswordUpdateResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) PerformDeviceCreation(ctx context.Context, req *userapi.PerformDeviceCreationRequest, res *userapi.PerformDeviceCreationResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) PerformDeviceDeletion(ctx context.Context, req *userapi.PerformDeviceDeletionRequest, res *userapi.PerformDeviceDeletionResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) PerformDeviceUpdate(ctx context.Context, req *userapi.PerformDeviceUpdateRequest, res *userapi.PerformDeviceUpdateResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) PerformLastSeenUpdate(ctx context.Context, req *userapi.PerformLastSeenUpdateRequest, res *userapi.PerformLastSeenUpdateResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) PerformAccountDeactivation(ctx context.Context, req *userapi.PerformAccountDeactivationRequest, res *userapi.PerformAccountDeactivationResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) PerformOpenIDTokenCreation(ctx context.Context, req *userapi.PerformOpenIDTokenCreationRequest, res *userapi.PerformOpenIDTokenCreationResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) QueryProfile(ctx context.Context, req *userapi.QueryProfileRequest, res *userapi.QueryProfileResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) PerformKeyBackup(ctx context.Context, req *userapi.PerformKeyBackupRequest, res *userapi.PerformKeyBackupResponse) {
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) QueryKeyBackup(ctx context.Context, req *userapi.QueryKeyBackupRequest, res *userapi.QueryKeyBackupResponse) {
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) QueryAccessToken(ctx context.Context, req *userapi.QueryAccessTokenRequest, res *userapi.QueryAccessTokenResponse) error {
|
func (u *testUserAPI) QueryAccessToken(ctx context.Context, req *userapi.QueryAccessTokenRequest, res *userapi.QueryAccessTokenResponse) error {
|
||||||
dev, ok := u.accessTokens[req.AccessToken]
|
dev, ok := u.accessTokens[req.AccessToken]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
@ -398,21 +353,6 @@ func (u *testUserAPI) QueryAccessToken(ctx context.Context, req *userapi.QueryAc
|
||||||
res.Device = &dev
|
res.Device = &dev
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (u *testUserAPI) QueryDevices(ctx context.Context, req *userapi.QueryDevicesRequest, res *userapi.QueryDevicesResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) QueryAccountData(ctx context.Context, req *userapi.QueryAccountDataRequest, res *userapi.QueryAccountDataResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) QueryDeviceInfos(ctx context.Context, req *userapi.QueryDeviceInfosRequest, res *userapi.QueryDeviceInfosResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) QuerySearchProfiles(ctx context.Context, req *userapi.QuerySearchProfilesRequest, res *userapi.QuerySearchProfilesResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (u *testUserAPI) QueryOpenIDToken(ctx context.Context, req *userapi.QueryOpenIDTokenRequest, res *userapi.QueryOpenIDTokenResponse) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type testRoomserverAPI struct {
|
type testRoomserverAPI struct {
|
||||||
// use a trace API as it implements method stubs so we don't need to have them here.
|
// use a trace API as it implements method stubs so we don't need to have them here.
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,10 @@ func (k *mockKeyAPI) SetUserAPI(i userapi.UserInternalAPI) {}
|
||||||
// PerformClaimKeys claims one-time keys for use in pre-key messages
|
// PerformClaimKeys claims one-time keys for use in pre-key messages
|
||||||
func (k *mockKeyAPI) PerformClaimKeys(ctx context.Context, req *keyapi.PerformClaimKeysRequest, res *keyapi.PerformClaimKeysResponse) {
|
func (k *mockKeyAPI) PerformClaimKeys(ctx context.Context, req *keyapi.PerformClaimKeysRequest, res *keyapi.PerformClaimKeysResponse) {
|
||||||
}
|
}
|
||||||
|
func (k *mockKeyAPI) PerformUploadDeviceKeys(ctx context.Context, req *keyapi.PerformUploadDeviceKeysRequest, res *keyapi.PerformUploadDeviceKeysResponse) {
|
||||||
|
}
|
||||||
|
func (k *mockKeyAPI) PerformUploadDeviceSignatures(ctx context.Context, req *keyapi.PerformUploadDeviceSignaturesRequest, res *keyapi.PerformUploadDeviceSignaturesResponse) {
|
||||||
|
}
|
||||||
func (k *mockKeyAPI) QueryKeys(ctx context.Context, req *keyapi.QueryKeysRequest, res *keyapi.QueryKeysResponse) {
|
func (k *mockKeyAPI) QueryKeys(ctx context.Context, req *keyapi.QueryKeysRequest, res *keyapi.QueryKeysResponse) {
|
||||||
}
|
}
|
||||||
func (k *mockKeyAPI) QueryKeyChanges(ctx context.Context, req *keyapi.QueryKeyChangesRequest, res *keyapi.QueryKeyChangesResponse) {
|
func (k *mockKeyAPI) QueryKeyChanges(ctx context.Context, req *keyapi.QueryKeyChangesRequest, res *keyapi.QueryKeyChangesResponse) {
|
||||||
|
|
|
||||||
|
|
@ -552,4 +552,7 @@ Can delete backup
|
||||||
Deleted & recreated backups are empty
|
Deleted & recreated backups are empty
|
||||||
GET /presence/:user_id/status fetches initial status
|
GET /presence/:user_id/status fetches initial status
|
||||||
PUT /presence/:user_id/status updates my presence
|
PUT /presence/:user_id/status updates my presence
|
||||||
Presence change reports an event to myself
|
Presence change reports an event to myself
|
||||||
|
Can upload self-signing keys
|
||||||
|
Fails to upload self-signing keys with no auth
|
||||||
|
Fails to upload self-signing key without master key
|
||||||
|
|
|
||||||
125
userapi/api/api_trace.go
Normal file
125
userapi/api/api_trace.go
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
// 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 api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/matrix-org/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserInternalAPITrace wraps a RoomserverInternalAPI and logs the
|
||||||
|
// complete request/response/error
|
||||||
|
type UserInternalAPITrace struct {
|
||||||
|
Impl UserInternalAPI
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *UserInternalAPITrace) InputAccountData(ctx context.Context, req *InputAccountDataRequest, res *InputAccountDataResponse) error {
|
||||||
|
err := t.Impl.InputAccountData(ctx, req, res)
|
||||||
|
util.GetLogger(ctx).Infof("InputAccountData req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (t *UserInternalAPITrace) PerformAccountCreation(ctx context.Context, req *PerformAccountCreationRequest, res *PerformAccountCreationResponse) error {
|
||||||
|
err := t.Impl.PerformAccountCreation(ctx, req, res)
|
||||||
|
util.GetLogger(ctx).Infof("PerformAccountCreation req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (t *UserInternalAPITrace) PerformPasswordUpdate(ctx context.Context, req *PerformPasswordUpdateRequest, res *PerformPasswordUpdateResponse) error {
|
||||||
|
err := t.Impl.PerformPasswordUpdate(ctx, req, res)
|
||||||
|
util.GetLogger(ctx).Infof("PerformPasswordUpdate req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *UserInternalAPITrace) PerformDeviceCreation(ctx context.Context, req *PerformDeviceCreationRequest, res *PerformDeviceCreationResponse) error {
|
||||||
|
err := t.Impl.PerformDeviceCreation(ctx, req, res)
|
||||||
|
util.GetLogger(ctx).Infof("PerformDeviceCreation req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (t *UserInternalAPITrace) PerformDeviceDeletion(ctx context.Context, req *PerformDeviceDeletionRequest, res *PerformDeviceDeletionResponse) error {
|
||||||
|
err := t.Impl.PerformDeviceDeletion(ctx, req, res)
|
||||||
|
util.GetLogger(ctx).Infof("PerformDeviceDeletion req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (t *UserInternalAPITrace) PerformLastSeenUpdate(ctx context.Context, req *PerformLastSeenUpdateRequest, res *PerformLastSeenUpdateResponse) error {
|
||||||
|
err := t.Impl.PerformLastSeenUpdate(ctx, req, res)
|
||||||
|
util.GetLogger(ctx).Infof("PerformLastSeenUpdate req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (t *UserInternalAPITrace) PerformDeviceUpdate(ctx context.Context, req *PerformDeviceUpdateRequest, res *PerformDeviceUpdateResponse) error {
|
||||||
|
err := t.Impl.PerformDeviceUpdate(ctx, req, res)
|
||||||
|
util.GetLogger(ctx).Infof("PerformDeviceUpdate req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (t *UserInternalAPITrace) PerformAccountDeactivation(ctx context.Context, req *PerformAccountDeactivationRequest, res *PerformAccountDeactivationResponse) error {
|
||||||
|
err := t.Impl.PerformAccountDeactivation(ctx, req, res)
|
||||||
|
util.GetLogger(ctx).Infof("PerformAccountDeactivation req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (t *UserInternalAPITrace) PerformOpenIDTokenCreation(ctx context.Context, req *PerformOpenIDTokenCreationRequest, res *PerformOpenIDTokenCreationResponse) error {
|
||||||
|
err := t.Impl.PerformOpenIDTokenCreation(ctx, req, res)
|
||||||
|
util.GetLogger(ctx).Infof("PerformOpenIDTokenCreation req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (t *UserInternalAPITrace) PerformKeyBackup(ctx context.Context, req *PerformKeyBackupRequest, res *PerformKeyBackupResponse) {
|
||||||
|
t.Impl.PerformKeyBackup(ctx, req, res)
|
||||||
|
}
|
||||||
|
func (t *UserInternalAPITrace) QueryKeyBackup(ctx context.Context, req *QueryKeyBackupRequest, res *QueryKeyBackupResponse) {
|
||||||
|
t.Impl.QueryKeyBackup(ctx, req, res)
|
||||||
|
}
|
||||||
|
func (t *UserInternalAPITrace) QueryProfile(ctx context.Context, req *QueryProfileRequest, res *QueryProfileResponse) error {
|
||||||
|
err := t.Impl.QueryProfile(ctx, req, res)
|
||||||
|
util.GetLogger(ctx).Infof("QueryProfile req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (t *UserInternalAPITrace) QueryAccessToken(ctx context.Context, req *QueryAccessTokenRequest, res *QueryAccessTokenResponse) error {
|
||||||
|
err := t.Impl.QueryAccessToken(ctx, req, res)
|
||||||
|
util.GetLogger(ctx).Infof("QueryAccessToken req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (t *UserInternalAPITrace) QueryDevices(ctx context.Context, req *QueryDevicesRequest, res *QueryDevicesResponse) error {
|
||||||
|
err := t.Impl.QueryDevices(ctx, req, res)
|
||||||
|
util.GetLogger(ctx).Infof("QueryDevices req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (t *UserInternalAPITrace) QueryAccountData(ctx context.Context, req *QueryAccountDataRequest, res *QueryAccountDataResponse) error {
|
||||||
|
err := t.Impl.QueryAccountData(ctx, req, res)
|
||||||
|
util.GetLogger(ctx).Infof("QueryAccountData req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (t *UserInternalAPITrace) QueryDeviceInfos(ctx context.Context, req *QueryDeviceInfosRequest, res *QueryDeviceInfosResponse) error {
|
||||||
|
err := t.Impl.QueryDeviceInfos(ctx, req, res)
|
||||||
|
util.GetLogger(ctx).Infof("QueryDeviceInfos req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (t *UserInternalAPITrace) QuerySearchProfiles(ctx context.Context, req *QuerySearchProfilesRequest, res *QuerySearchProfilesResponse) error {
|
||||||
|
err := t.Impl.QuerySearchProfiles(ctx, req, res)
|
||||||
|
util.GetLogger(ctx).Infof("QuerySearchProfiles req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (t *UserInternalAPITrace) QueryOpenIDToken(ctx context.Context, req *QueryOpenIDTokenRequest, res *QueryOpenIDTokenResponse) error {
|
||||||
|
err := t.Impl.QueryOpenIDToken(ctx, req, res)
|
||||||
|
util.GetLogger(ctx).Infof("QueryOpenIDToken req=%+v res=%+v", js(req), js(res))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func js(thing interface{}) string {
|
||||||
|
b, err := json.Marshal(thing)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("Marshal error:%s", err)
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue