Merge branch 'master' into nats

This commit is contained in:
Neil Alexander 2021-12-03 17:35:36 +00:00
commit 9bc0731ec4
No known key found for this signature in database
GPG key ID: A02A2019A2BB0944
30 changed files with 281 additions and 123 deletions

3
.gitignore vendored
View file

@ -60,3 +60,6 @@ cmd/dendrite-demo-yggdrasil/embed/fs*.go
# Test dependencies # Test dependencies
test/wasm/node_modules test/wasm/node_modules
media_store/

View file

@ -131,10 +131,11 @@ func generateAppServiceAccount(
} }
var devRes userapi.PerformDeviceCreationResponse var devRes userapi.PerformDeviceCreationResponse
err = userAPI.PerformDeviceCreation(context.Background(), &userapi.PerformDeviceCreationRequest{ err = userAPI.PerformDeviceCreation(context.Background(), &userapi.PerformDeviceCreationRequest{
Localpart: as.SenderLocalpart, Localpart: as.SenderLocalpart,
AccessToken: as.ASToken, AccessToken: as.ASToken,
DeviceID: &as.SenderLocalpart, DeviceID: &as.SenderLocalpart,
DeviceDisplayName: &as.SenderLocalpart, DeviceDisplayName: &as.SenderLocalpart,
NoDeviceListUpdate: true,
}, &devRes) }, &devRes)
return err return err
} }

View file

@ -3,7 +3,7 @@ services:
# PostgreSQL is needed for both polylith and monolith modes. # PostgreSQL is needed for both polylith and monolith modes.
postgres: postgres:
hostname: postgres hostname: postgres
image: postgres:11 image: postgres:14
restart: always restart: always
volumes: volumes:
- ./postgres/create_db.sh:/docker-entrypoint-initdb.d/20-create_db.sh - ./postgres/create_db.sh:/docker-entrypoint-initdb.d/20-create_db.sh

View file

@ -13,6 +13,7 @@ import (
"net" "net"
"net/http" "net/http"
"os" "os"
"strings"
"sync" "sync"
"time" "time"
@ -87,15 +88,15 @@ func (m *DendriteMonolith) SetMulticastEnabled(enabled bool) {
m.PineconeMulticast.Start() m.PineconeMulticast.Start()
} else { } else {
m.PineconeMulticast.Stop() m.PineconeMulticast.Stop()
m.DisconnectType(pineconeRouter.PeerTypeMulticast) m.DisconnectType(int(pineconeRouter.PeerTypeMulticast))
} }
} }
func (m *DendriteMonolith) SetStaticPeer(uri string) { func (m *DendriteMonolith) SetStaticPeer(uri string) {
m.staticPeerMutex.Lock() m.staticPeerMutex.Lock()
m.staticPeerURI = uri m.staticPeerURI = strings.TrimSpace(uri)
m.staticPeerMutex.Unlock() m.staticPeerMutex.Unlock()
m.DisconnectType(pineconeRouter.PeerTypeRemote) m.DisconnectType(int(pineconeRouter.PeerTypeRemote))
if uri != "" { if uri != "" {
go func() { go func() {
m.staticPeerAttempt <- struct{}{} m.staticPeerAttempt <- struct{}{}
@ -105,7 +106,7 @@ func (m *DendriteMonolith) SetStaticPeer(uri string) {
func (m *DendriteMonolith) DisconnectType(peertype int) { func (m *DendriteMonolith) DisconnectType(peertype int) {
for _, p := range m.PineconeRouter.Peers() { for _, p := range m.PineconeRouter.Peers() {
if peertype == p.PeerType { if int(peertype) == p.PeerType {
m.PineconeRouter.Disconnect(types.SwitchPortID(p.Port), nil) m.PineconeRouter.Disconnect(types.SwitchPortID(p.Port), nil)
} }
} }
@ -133,7 +134,11 @@ func (m *DendriteMonolith) Conduit(zone string, peertype int) (*Conduit, error)
for i := 1; i <= 10; i++ { for i := 1; i <= 10; i++ {
logrus.Errorf("Attempting authenticated connect (attempt %d)", i) logrus.Errorf("Attempting authenticated connect (attempt %d)", i)
var err error var err error
conduit.port, err = m.PineconeRouter.AuthenticatedConnect(l, zone, peertype, true) conduit.port, err = m.PineconeRouter.Connect(
l,
pineconeRouter.ConnectionZone(zone),
pineconeRouter.ConnectionPeerType(peertype),
)
switch err { switch err {
case io.ErrClosedPipe: case io.ErrClosedPipe:
logrus.Errorf("Authenticated connect failed due to closed pipe (attempt %d)", i) logrus.Errorf("Authenticated connect failed due to closed pipe (attempt %d)", i)
@ -195,16 +200,28 @@ func (m *DendriteMonolith) RegisterDevice(localpart, deviceID string) (string, e
} }
func (m *DendriteMonolith) staticPeerConnect() { func (m *DendriteMonolith) staticPeerConnect() {
connected := map[string]bool{} // URI -> connected?
attempt := func() { attempt := func() {
if m.PineconeRouter.PeerCount(pineconeRouter.PeerTypeRemote) == 0 { m.staticPeerMutex.RLock()
m.staticPeerMutex.RLock() uri := m.staticPeerURI
uri := m.staticPeerURI m.staticPeerMutex.RUnlock()
m.staticPeerMutex.RUnlock() if uri == "" {
if uri == "" { return
return }
} for k := range connected {
if err := conn.ConnectToPeer(m.PineconeRouter, uri); err != nil { delete(connected, k)
logrus.WithError(err).Error("Failed to connect to static peer") }
for _, uri := range strings.Split(uri, ",") {
connected[strings.TrimSpace(uri)] = false
}
for _, info := range m.PineconeRouter.Peers() {
connected[info.URI] = true
}
for k, online := range connected {
if !online {
if err := conn.ConnectToPeer(m.PineconeRouter, k); err != nil {
logrus.WithError(err).Error("Failed to connect to static peer")
}
} }
} }
} }
@ -271,7 +288,7 @@ func (m *DendriteMonolith) Start() {
cfg.SyncAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/%s-syncapi.db", m.StorageDirectory, prefix)) cfg.SyncAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/%s-syncapi.db", m.StorageDirectory, prefix))
cfg.RoomServer.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/%s-roomserver.db", m.StorageDirectory, prefix)) cfg.RoomServer.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/%s-roomserver.db", m.StorageDirectory, prefix))
cfg.KeyServer.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/%s-keyserver.db", m.StorageDirectory, prefix)) cfg.KeyServer.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/%s-keyserver.db", m.StorageDirectory, prefix))
cfg.FederationAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/%s-federationapi.db", m.StorageDirectory, prefix)) cfg.FederationAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/%s-federationsender.db", m.StorageDirectory, prefix))
cfg.AppServiceAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/%s-appservice.db", m.StorageDirectory, prefix)) cfg.AppServiceAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/%s-appservice.db", m.StorageDirectory, prefix))
cfg.MediaAPI.BasePath = config.Path(fmt.Sprintf("%s/media", m.CacheDirectory)) cfg.MediaAPI.BasePath = config.Path(fmt.Sprintf("%s/media", m.CacheDirectory))
cfg.MediaAPI.AbsBasePath = config.Path(fmt.Sprintf("%s/media", m.CacheDirectory)) cfg.MediaAPI.AbsBasePath = config.Path(fmt.Sprintf("%s/media", m.CacheDirectory))

View file

@ -93,7 +93,7 @@ func (m *DendriteMonolith) Start() {
cfg.SyncAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/dendrite-p2p-syncapi.db", m.StorageDirectory)) cfg.SyncAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/dendrite-p2p-syncapi.db", m.StorageDirectory))
cfg.RoomServer.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/dendrite-p2p-roomserver.db", m.StorageDirectory)) cfg.RoomServer.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/dendrite-p2p-roomserver.db", m.StorageDirectory))
cfg.KeyServer.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/dendrite-p2p-keyserver.db", m.StorageDirectory)) cfg.KeyServer.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/dendrite-p2p-keyserver.db", m.StorageDirectory))
cfg.FederationAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/dendrite-p2p-federationapi.db", m.StorageDirectory)) cfg.FederationAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/dendrite-p2p-federationsender.db", m.StorageDirectory))
cfg.AppServiceAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/dendrite-p2p-appservice.db", m.StorageDirectory)) cfg.AppServiceAPI.Database.ConnectionString = config.DataSource(fmt.Sprintf("file:%s/dendrite-p2p-appservice.db", m.StorageDirectory))
cfg.MediaAPI.BasePath = config.Path(fmt.Sprintf("%s/tmp", m.StorageDirectory)) cfg.MediaAPI.BasePath = config.Path(fmt.Sprintf("%s/tmp", m.StorageDirectory))
cfg.MediaAPI.AbsBasePath = config.Path(fmt.Sprintf("%s/tmp", m.StorageDirectory)) cfg.MediaAPI.AbsBasePath = config.Path(fmt.Sprintf("%s/tmp", m.StorageDirectory))

View file

@ -70,11 +70,11 @@ func VerifyUserFromRequest(
jsonErr := jsonerror.InternalServerError() jsonErr := jsonerror.InternalServerError()
return nil, &jsonErr return nil, &jsonErr
} }
if res.Err != nil { if res.Err != "" {
if forbidden, ok := res.Err.(*api.ErrorForbidden); ok { if strings.HasPrefix(strings.ToLower(res.Err), "forbidden:") { // TODO: use actual error and no string comparison
return nil, &util.JSONResponse{ return nil, &util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden(forbidden.Message), JSON: jsonerror.Forbidden(res.Err),
} }
} }
} }

View file

@ -17,6 +17,7 @@ package auth
import ( import (
"context" "context"
"net/http" "net/http"
"strings"
"github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/clientapi/userutil" "github.com/matrix-org/dendrite/clientapi/userutil"
@ -48,7 +49,8 @@ func (t *LoginTypePassword) Request() interface{} {
func (t *LoginTypePassword) Login(ctx context.Context, req interface{}) (*Login, *util.JSONResponse) { func (t *LoginTypePassword) Login(ctx context.Context, req interface{}) (*Login, *util.JSONResponse) {
r := req.(*PasswordRequest) r := req.(*PasswordRequest)
username := r.Username() // Squash username to all lowercase letters
username := strings.ToLower(r.Username())
if username == "" { if username == "" {
return nil, &util.JSONResponse{ return nil, &util.JSONResponse{
Code: http.StatusUnauthorized, Code: http.StatusUnauthorized,

View file

@ -62,12 +62,14 @@ func CreateKeyBackupVersion(req *http.Request, userAPI userapi.UserInternalAPI,
return *resErr return *resErr
} }
var performKeyBackupResp userapi.PerformKeyBackupResponse var performKeyBackupResp userapi.PerformKeyBackupResponse
userAPI.PerformKeyBackup(req.Context(), &userapi.PerformKeyBackupRequest{ if err := userAPI.PerformKeyBackup(req.Context(), &userapi.PerformKeyBackupRequest{
UserID: device.UserID, UserID: device.UserID,
Version: "", Version: "",
AuthData: kb.AuthData, AuthData: kb.AuthData,
Algorithm: kb.Algorithm, Algorithm: kb.Algorithm,
}, &performKeyBackupResp) }, &performKeyBackupResp); err != nil {
return jsonerror.InternalServerError()
}
if performKeyBackupResp.Error != "" { if performKeyBackupResp.Error != "" {
if performKeyBackupResp.BadInput { if performKeyBackupResp.BadInput {
return util.JSONResponse{ return util.JSONResponse{
@ -123,12 +125,14 @@ func ModifyKeyBackupVersionAuthData(req *http.Request, userAPI userapi.UserInter
return *resErr return *resErr
} }
var performKeyBackupResp userapi.PerformKeyBackupResponse var performKeyBackupResp userapi.PerformKeyBackupResponse
userAPI.PerformKeyBackup(req.Context(), &userapi.PerformKeyBackupRequest{ if err := userAPI.PerformKeyBackup(req.Context(), &userapi.PerformKeyBackupRequest{
UserID: device.UserID, UserID: device.UserID,
Version: version, Version: version,
AuthData: kb.AuthData, AuthData: kb.AuthData,
Algorithm: kb.Algorithm, Algorithm: kb.Algorithm,
}, &performKeyBackupResp) }, &performKeyBackupResp); err != nil {
return jsonerror.InternalServerError()
}
if performKeyBackupResp.Error != "" { if performKeyBackupResp.Error != "" {
if performKeyBackupResp.BadInput { if performKeyBackupResp.BadInput {
return util.JSONResponse{ return util.JSONResponse{
@ -157,11 +161,13 @@ func ModifyKeyBackupVersionAuthData(req *http.Request, userAPI userapi.UserInter
// Implements DELETE /_matrix/client/r0/room_keys/version/{version} // Implements DELETE /_matrix/client/r0/room_keys/version/{version}
func DeleteKeyBackupVersion(req *http.Request, userAPI userapi.UserInternalAPI, device *userapi.Device, version string) util.JSONResponse { func DeleteKeyBackupVersion(req *http.Request, userAPI userapi.UserInternalAPI, device *userapi.Device, version string) util.JSONResponse {
var performKeyBackupResp userapi.PerformKeyBackupResponse var performKeyBackupResp userapi.PerformKeyBackupResponse
userAPI.PerformKeyBackup(req.Context(), &userapi.PerformKeyBackupRequest{ if err := userAPI.PerformKeyBackup(req.Context(), &userapi.PerformKeyBackupRequest{
UserID: device.UserID, UserID: device.UserID,
Version: version, Version: version,
DeleteBackup: true, DeleteBackup: true,
}, &performKeyBackupResp) }, &performKeyBackupResp); err != nil {
return jsonerror.InternalServerError()
}
if performKeyBackupResp.Error != "" { if performKeyBackupResp.Error != "" {
if performKeyBackupResp.BadInput { if performKeyBackupResp.BadInput {
return util.JSONResponse{ return util.JSONResponse{
@ -191,11 +197,13 @@ func UploadBackupKeys(
req *http.Request, userAPI userapi.UserInternalAPI, device *userapi.Device, version string, keys *keyBackupSessionRequest, req *http.Request, userAPI userapi.UserInternalAPI, device *userapi.Device, version string, keys *keyBackupSessionRequest,
) util.JSONResponse { ) util.JSONResponse {
var performKeyBackupResp userapi.PerformKeyBackupResponse var performKeyBackupResp userapi.PerformKeyBackupResponse
userAPI.PerformKeyBackup(req.Context(), &userapi.PerformKeyBackupRequest{ if err := userAPI.PerformKeyBackup(req.Context(), &userapi.PerformKeyBackupRequest{
UserID: device.UserID, UserID: device.UserID,
Version: version, Version: version,
Keys: *keys, Keys: *keys,
}, &performKeyBackupResp) }, &performKeyBackupResp); err != nil && performKeyBackupResp.Error == "" {
return jsonerror.InternalServerError()
}
if performKeyBackupResp.Error != "" { if performKeyBackupResp.Error != "" {
if performKeyBackupResp.BadInput { if performKeyBackupResp.BadInput {
return util.JSONResponse{ return util.JSONResponse{

View file

@ -61,7 +61,7 @@ func Setup(
extRoomsProvider api.ExtraPublicRoomsProvider, extRoomsProvider api.ExtraPublicRoomsProvider,
mscCfg *config.MSCs, mscCfg *config.MSCs,
) { ) {
rateLimits := newRateLimits(&cfg.RateLimiting) rateLimits := httputil.NewRateLimits(&cfg.RateLimiting)
userInteractiveAuth := auth.NewUserInteractive(accountDB.GetAccountByPassword, cfg) userInteractiveAuth := auth.NewUserInteractive(accountDB.GetAccountByPassword, cfg)
unstableFeatures := map[string]bool{ unstableFeatures := map[string]bool{
@ -127,7 +127,7 @@ func Setup(
).Methods(http.MethodPost, http.MethodOptions) ).Methods(http.MethodPost, http.MethodOptions)
r0mux.Handle("/join/{roomIDOrAlias}", r0mux.Handle("/join/{roomIDOrAlias}",
httputil.MakeAuthAPI(gomatrixserverlib.Join, userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI(gomatrixserverlib.Join, userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
@ -143,7 +143,7 @@ func Setup(
if mscCfg.Enabled("msc2753") { if mscCfg.Enabled("msc2753") {
r0mux.Handle("/peek/{roomIDOrAlias}", r0mux.Handle("/peek/{roomIDOrAlias}",
httputil.MakeAuthAPI(gomatrixserverlib.Peek, userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI(gomatrixserverlib.Peek, userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
@ -163,7 +163,7 @@ func Setup(
).Methods(http.MethodGet, http.MethodOptions) ).Methods(http.MethodGet, http.MethodOptions)
r0mux.Handle("/rooms/{roomID}/join", r0mux.Handle("/rooms/{roomID}/join",
httputil.MakeAuthAPI(gomatrixserverlib.Join, userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI(gomatrixserverlib.Join, userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
@ -177,7 +177,7 @@ func Setup(
).Methods(http.MethodPost, http.MethodOptions) ).Methods(http.MethodPost, http.MethodOptions)
r0mux.Handle("/rooms/{roomID}/leave", r0mux.Handle("/rooms/{roomID}/leave",
httputil.MakeAuthAPI("membership", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI("membership", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
@ -211,7 +211,7 @@ func Setup(
).Methods(http.MethodPost, http.MethodOptions) ).Methods(http.MethodPost, http.MethodOptions)
r0mux.Handle("/rooms/{roomID}/invite", r0mux.Handle("/rooms/{roomID}/invite",
httputil.MakeAuthAPI("membership", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI("membership", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
@ -329,14 +329,14 @@ func Setup(
).Methods(http.MethodPut, http.MethodOptions) ).Methods(http.MethodPut, http.MethodOptions)
r0mux.Handle("/register", httputil.MakeExternalAPI("register", func(req *http.Request) util.JSONResponse { r0mux.Handle("/register", httputil.MakeExternalAPI("register", func(req *http.Request) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
return Register(req, userAPI, accountDB, cfg) return Register(req, userAPI, accountDB, cfg)
})).Methods(http.MethodPost, http.MethodOptions) })).Methods(http.MethodPost, http.MethodOptions)
r0mux.Handle("/register/available", httputil.MakeExternalAPI("registerAvailable", func(req *http.Request) util.JSONResponse { r0mux.Handle("/register/available", httputil.MakeExternalAPI("registerAvailable", func(req *http.Request) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
return RegisterAvailable(req, cfg, accountDB) return RegisterAvailable(req, cfg, accountDB)
@ -410,7 +410,7 @@ func Setup(
r0mux.Handle("/rooms/{roomID}/typing/{userID}", r0mux.Handle("/rooms/{roomID}/typing/{userID}",
httputil.MakeAuthAPI("rooms_typing", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI("rooms_typing", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
@ -466,7 +466,7 @@ func Setup(
r0mux.Handle("/account/whoami", r0mux.Handle("/account/whoami",
httputil.MakeAuthAPI("whoami", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI("whoami", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
return Whoami(req, device) return Whoami(req, device)
@ -475,7 +475,7 @@ func Setup(
r0mux.Handle("/account/password", r0mux.Handle("/account/password",
httputil.MakeAuthAPI("password", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI("password", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
return Password(req, userAPI, accountDB, device, cfg) return Password(req, userAPI, accountDB, device, cfg)
@ -484,7 +484,7 @@ func Setup(
r0mux.Handle("/account/deactivate", r0mux.Handle("/account/deactivate",
httputil.MakeAuthAPI("deactivate", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI("deactivate", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
return Deactivate(req, userInteractiveAuth, userAPI, device) return Deactivate(req, userInteractiveAuth, userAPI, device)
@ -495,7 +495,7 @@ func Setup(
r0mux.Handle("/login", r0mux.Handle("/login",
httputil.MakeExternalAPI("login", func(req *http.Request) util.JSONResponse { httputil.MakeExternalAPI("login", func(req *http.Request) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
return Login(req, accountDB, userAPI, cfg) return Login(req, accountDB, userAPI, cfg)
@ -552,7 +552,7 @@ func Setup(
r0mux.Handle("/profile/{userID}/avatar_url", r0mux.Handle("/profile/{userID}/avatar_url",
httputil.MakeAuthAPI("profile_avatar_url", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI("profile_avatar_url", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
@ -577,7 +577,7 @@ func Setup(
r0mux.Handle("/profile/{userID}/displayname", r0mux.Handle("/profile/{userID}/displayname",
httputil.MakeAuthAPI("profile_displayname", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI("profile_displayname", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
@ -617,7 +617,7 @@ func Setup(
// Element logs get flooded unless this is handled // Element logs get flooded unless this is handled
r0mux.Handle("/presence/{userID}/status", r0mux.Handle("/presence/{userID}/status",
httputil.MakeExternalAPI("presence", func(req *http.Request) util.JSONResponse { httputil.MakeExternalAPI("presence", func(req *http.Request) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
// TODO: Set presence (probably the responsibility of a presence server not clientapi) // TODO: Set presence (probably the responsibility of a presence server not clientapi)
@ -630,7 +630,7 @@ func Setup(
r0mux.Handle("/voip/turnServer", r0mux.Handle("/voip/turnServer",
httputil.MakeAuthAPI("turn_server", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI("turn_server", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
return RequestTurnServer(req, device, cfg) return RequestTurnServer(req, device, cfg)
@ -709,7 +709,7 @@ func Setup(
r0mux.Handle("/user/{userID}/openid/request_token", r0mux.Handle("/user/{userID}/openid/request_token",
httputil.MakeAuthAPI("openid_request_token", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI("openid_request_token", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
@ -722,7 +722,7 @@ func Setup(
r0mux.Handle("/user_directory/search", r0mux.Handle("/user_directory/search",
httputil.MakeAuthAPI("userdirectory_search", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI("userdirectory_search", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
postContent := struct { postContent := struct {
@ -767,7 +767,7 @@ func Setup(
r0mux.Handle("/rooms/{roomID}/read_markers", r0mux.Handle("/rooms/{roomID}/read_markers",
httputil.MakeAuthAPI("rooms_read_markers", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI("rooms_read_markers", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
@ -780,7 +780,7 @@ func Setup(
r0mux.Handle("/rooms/{roomID}/forget", r0mux.Handle("/rooms/{roomID}/forget",
httputil.MakeAuthAPI("rooms_forget", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI("rooms_forget", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
@ -884,7 +884,7 @@ func Setup(
r0mux.Handle("/capabilities", r0mux.Handle("/capabilities",
httputil.MakeAuthAPI("capabilities", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI("capabilities", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
return GetCapabilities(req, rsAPI) return GetCapabilities(req, rsAPI)
@ -1100,7 +1100,7 @@ func Setup(
).Methods(http.MethodPost, http.MethodOptions) ).Methods(http.MethodPost, http.MethodOptions)
r0mux.Handle("/rooms/{roomId}/receipt/{receiptType}/{eventId}", r0mux.Handle("/rooms/{roomId}/receipt/{receiptType}/{eventId}",
httputil.MakeAuthAPI(gomatrixserverlib.Join, userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { httputil.MakeAuthAPI(gomatrixserverlib.Join, userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.rateLimit(req); r != nil { if r := rateLimits.Limit(req); r != nil {
return *r return *r
} }
vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) vars, err := httputil.URLDecodeMapValues(mux.Vars(req))

View file

@ -34,7 +34,12 @@ func ConnectToPeer(pRouter *pineconeRouter.Router, peer string) error {
if parent == nil { if parent == nil {
return fmt.Errorf("failed to wrap connection") return fmt.Errorf("failed to wrap connection")
} }
_, err := pRouter.AuthenticatedConnect(parent, "static", pineconeRouter.PeerTypeRemote, true) _, err := pRouter.Connect(
parent,
pineconeRouter.ConnectionZone("static"),
pineconeRouter.ConnectionPeerType(pineconeRouter.PeerTypeRemote),
pineconeRouter.ConnectionURI(peer),
)
return err return err
} }

View file

@ -26,6 +26,7 @@ import (
"net" "net"
"net/http" "net/http"
"os" "os"
"strings"
"time" "time"
"github.com/gorilla/mux" "github.com/gorilla/mux"
@ -61,7 +62,7 @@ import (
var ( var (
instanceName = flag.String("name", "dendrite-p2p-pinecone", "the name of this P2P demo instance") instanceName = flag.String("name", "dendrite-p2p-pinecone", "the name of this P2P demo instance")
instancePort = flag.Int("port", 8008, "the port that the client API will listen on") instancePort = flag.Int("port", 8008, "the port that the client API will listen on")
instancePeer = flag.String("peer", "", "the static Pinecone peer to connect to") instancePeer = flag.String("peer", "", "the static Pinecone peers to connect to, comma separated-list")
instanceListen = flag.String("listen", ":0", "the port Pinecone peers can connect to") instanceListen = flag.String("listen", ":0", "the port Pinecone peers can connect to")
) )
@ -109,9 +110,12 @@ func main() {
continue continue
} }
port, err := pRouter.AuthenticatedConnect(conn, "", pineconeRouter.PeerTypeRemote, true) port, err := pRouter.Connect(
conn,
pineconeRouter.ConnectionPeerType(pineconeRouter.PeerTypeRemote),
)
if err != nil { if err != nil {
logrus.WithError(err).Error("pSwitch.AuthenticatedConnect failed") logrus.WithError(err).Error("pSwitch.Connect failed")
continue continue
} }
@ -124,14 +128,22 @@ func main() {
pMulticast.Start() pMulticast.Start()
connectToStaticPeer := func() { connectToStaticPeer := func() {
connected := map[string]bool{} // URI -> connected?
for _, uri := range strings.Split(*instancePeer, ",") {
connected[strings.TrimSpace(uri)] = false
}
attempt := func() { attempt := func() {
if pRouter.PeerCount(pineconeRouter.PeerTypeRemote) == 0 { for k := range connected {
uri := *instancePeer connected[k] = false
if uri == "" { }
return for _, info := range pRouter.Peers() {
} connected[info.URI] = true
if err := conn.ConnectToPeer(pRouter, uri); err != nil { }
logrus.WithError(err).Error("Failed to connect to static peer") for k, online := range connected {
if !online {
if err := conn.ConnectToPeer(pRouter, k); err != nil {
logrus.WithError(err).Error("Failed to connect to static peer")
}
} }
} }
} }
@ -229,7 +241,11 @@ func main() {
return return
} }
conn := conn.WrapWebSocketConn(c) conn := conn.WrapWebSocketConn(c)
if _, err = pRouter.AuthenticatedConnect(conn, "websocket", pineconeRouter.PeerTypeRemote, true); err != nil { if _, err = pRouter.Connect(
conn,
pineconeRouter.ConnectionZone("websocket"),
pineconeRouter.ConnectionPeerType(pineconeRouter.PeerTypeRemote),
); err != nil {
logrus.WithError(err).Error("Failed to connect WebSocket peer to Pinecone switch") logrus.WithError(err).Error("Failed to connect WebSocket peer to Pinecone switch")
} }
}) })

View file

@ -67,6 +67,7 @@ func main() {
cfg.MediaAPI.InternalAPI.Connect = httpAPIAddr cfg.MediaAPI.InternalAPI.Connect = httpAPIAddr
cfg.RoomServer.InternalAPI.Connect = httpAPIAddr cfg.RoomServer.InternalAPI.Connect = httpAPIAddr
cfg.SyncAPI.InternalAPI.Connect = httpAPIAddr cfg.SyncAPI.InternalAPI.Connect = httpAPIAddr
cfg.UserAPI.InternalAPI.Connect = httpAPIAddr
options = append(options, basepkg.UseHTTPAPIs) options = append(options, basepkg.UseHTTPAPIs)
} }
@ -102,20 +103,41 @@ 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.SetFederationAPI(fsAPI) rsImpl.SetFederationAPI(fsAPI)
keyAPI := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, fsAPI) keyImpl := keyserver.NewInternalAPI(base, &base.Cfg.KeyServer, fsAPI)
userAPI := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI) keyAPI := keyImpl
keyAPI.SetUserAPI(userAPI) if base.UseHTTPAPIs {
keyserver.AddInternalRoutes(base.InternalAPIMux, keyAPI)
keyAPI = base.KeyServerHTTPClient()
}
userImpl := userapi.NewInternalAPI(accountDB, &cfg.UserAPI, cfg.Derived.ApplicationServices, keyAPI)
userAPI := userImpl
if base.UseHTTPAPIs {
userapi.AddInternalRoutes(base.InternalAPIMux, userAPI)
userAPI = base.UserAPIClient()
}
if traceInternal { if traceInternal {
userAPI = &uapi.UserInternalAPITrace{ userAPI = &uapi.UserInternalAPITrace{
Impl: userAPI, Impl: userAPI,
} }
} }
// needs to be after the SetUserAPI call above
// TODO: This should use userAPI, not userImpl, but the appservice setup races with
// the listeners and panics at startup if it tries to create appservice accounts
// before the listeners are up.
asAPI := appservice.NewInternalAPI(base, userImpl, rsAPI)
if base.UseHTTPAPIs { if base.UseHTTPAPIs {
keyserver.AddInternalRoutes(base.InternalAPIMux, keyAPI) appservice.AddInternalRoutes(base.InternalAPIMux, asAPI)
keyAPI = base.KeyServerHTTPClient() asAPI = base.AppserviceHTTPClient()
} }
// The underlying roomserver implementation needs to be able to call the fedsender.
// This is different to rsAPI which can be the http client which doesn't need this
// dependency. Other components also need updating after their dependencies are up.
rsImpl.SetFederationAPI(fsAPI)
rsImpl.SetAppserviceAPI(asAPI)
keyImpl.SetUserAPI(userAPI)
eduInputAPI := eduserver.NewInternalAPI( eduInputAPI := eduserver.NewInternalAPI(
base, cache.New(), userAPI, base, cache.New(), userAPI,
) )
@ -124,13 +146,6 @@ func main() {
eduInputAPI = base.EDUServerClient() eduInputAPI = base.EDUServerClient()
} }
asAPI := appservice.NewInternalAPI(base, userAPI, rsAPI)
if base.UseHTTPAPIs {
appservice.AddInternalRoutes(base.InternalAPIMux, asAPI)
asAPI = base.AppserviceHTTPClient()
}
rsAPI.SetAppserviceAPI(asAPI)
monolith := setup.Monolith{ monolith := setup.Monolith{
Config: base.Cfg, Config: base.Cfg,
AccountDB: accountDB, AccountDB: accountDB,

View file

@ -35,6 +35,9 @@ func FederationAPI(base *basepkg.BaseDendrite, cfg *config.Dendrite) {
&base.Cfg.MSCs, nil, &base.Cfg.MSCs, nil,
) )
intAPI := federationapi.NewInternalAPI(base, federation, rsAPI, base.Caches, true)
federationapi.AddInternalRoutes(base.InternalAPIMux, intAPI)
base.SetupAndServeHTTP( base.SetupAndServeHTTP(
base.Cfg.FederationAPI.InternalAPI.Listen, base.Cfg.FederationAPI.InternalAPI.Listen,
base.Cfg.FederationAPI.ExternalAPI.Listen, base.Cfg.FederationAPI.ExternalAPI.Listen,

View file

@ -24,7 +24,7 @@ func MediaAPI(base *basepkg.BaseDendrite, cfg *config.Dendrite) {
userAPI := base.UserAPIClient() userAPI := base.UserAPIClient()
client := base.CreateClient() client := base.CreateClient()
mediaapi.AddPublicRoutes(base.PublicMediaAPIMux, &base.Cfg.MediaAPI, userAPI, client) mediaapi.AddPublicRoutes(base.PublicMediaAPIMux, &base.Cfg.MediaAPI, &base.Cfg.ClientAPI.RateLimiting, userAPI, client)
base.SetupAndServeHTTP( base.SetupAndServeHTTP(
base.Cfg.MediaAPI.InternalAPI.Listen, base.Cfg.MediaAPI.InternalAPI.Listen,

2
go.mod
View file

@ -40,7 +40,7 @@ require (
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-20211115192839-15a64d244aa2 github.com/matrix-org/gomatrixserverlib v0.0.0-20211115192839-15a64d244aa2
github.com/matrix-org/pinecone v0.0.0-20211116111603-febf3501584d github.com/matrix-org/pinecone v0.0.0-20211129130654-b0bf9ad6f5c7
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.8 github.com/mattn/go-sqlite3 v1.14.8
github.com/morikuni/aec v1.0.0 // indirect github.com/morikuni/aec v1.0.0 // indirect

4
go.sum
View file

@ -990,8 +990,8 @@ github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 h1:ZtO5uywdd5d
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s= github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
github.com/matrix-org/gomatrixserverlib v0.0.0-20211115192839-15a64d244aa2 h1:RFsBN3509Ql6NJ7TDVkcKoN3bb/tmqUqzur5c0AwIHQ= github.com/matrix-org/gomatrixserverlib v0.0.0-20211115192839-15a64d244aa2 h1:RFsBN3509Ql6NJ7TDVkcKoN3bb/tmqUqzur5c0AwIHQ=
github.com/matrix-org/gomatrixserverlib v0.0.0-20211115192839-15a64d244aa2/go.mod h1:rB8tBUUUo1rzUqpzklRDSooxZ6YMhoaEPx4SO5fGeUc= github.com/matrix-org/gomatrixserverlib v0.0.0-20211115192839-15a64d244aa2/go.mod h1:rB8tBUUUo1rzUqpzklRDSooxZ6YMhoaEPx4SO5fGeUc=
github.com/matrix-org/pinecone v0.0.0-20211116111603-febf3501584d h1:V1b6GZVvL95qTkjYSEWH9Pja6c0WcJKBt2MlAILlw+Q= github.com/matrix-org/pinecone v0.0.0-20211129130654-b0bf9ad6f5c7 h1:HC1TdU79ly+sxtyOuNDIpG2YBHQyGmvLux4VPQbT72I=
github.com/matrix-org/pinecone v0.0.0-20211116111603-febf3501584d/go.mod h1:r6dsL+ylE0yXe/7zh8y/Bdh6aBYI1r+u4yZni9A4iyk= github.com/matrix-org/pinecone v0.0.0-20211129130654-b0bf9ad6f5c7/go.mod h1:r6dsL+ylE0yXe/7zh8y/Bdh6aBYI1r+u4yZni9A4iyk=
github.com/matrix-org/util v0.0.0-20190711121626-527ce5ddefc7/go.mod h1:vVQlW/emklohkZnOPwD3LrZUBqdfsbiyO3p1lNV8F6U= github.com/matrix-org/util v0.0.0-20190711121626-527ce5ddefc7/go.mod h1:vVQlW/emklohkZnOPwD3LrZUBqdfsbiyO3p1lNV8F6U=
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4 h1:eCEHXWDv9Rm335MSuB49mFUK44bwZPFSDde3ORE3syk= github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4 h1:eCEHXWDv9Rm335MSuB49mFUK44bwZPFSDde3ORE3syk=
github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4/go.mod h1:vVQlW/emklohkZnOPwD3LrZUBqdfsbiyO3p1lNV8F6U= github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4/go.mod h1:vVQlW/emklohkZnOPwD3LrZUBqdfsbiyO3p1lNV8F6U=

View file

@ -23,6 +23,7 @@ import (
"net/url" "net/url"
"strings" "strings"
"github.com/matrix-org/dendrite/userapi/api"
opentracing "github.com/opentracing/opentracing-go" opentracing "github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext" "github.com/opentracing/opentracing-go/ext"
) )
@ -72,6 +73,9 @@ func PostJSON(
var errorBody struct { var errorBody struct {
Message string `json:"message"` Message string `json:"message"`
} }
if _, ok := response.(*api.PerformKeyBackupResponse); ok { // TODO: remove this, once cross-boundary errors are a thing
return nil
}
if msgerr := json.NewDecoder(res.Body).Decode(&errorBody); msgerr == nil { if msgerr := json.NewDecoder(res.Body).Decode(&errorBody); msgerr == nil {
return fmt.Errorf("internal API: %d from %s: %s", res.StatusCode, apiURL, errorBody.Message) return fmt.Errorf("internal API: %d from %s: %s", res.StatusCode, apiURL, errorBody.Message)
} }

View file

@ -1,4 +1,4 @@
package routing package httputil
import ( import (
"net/http" "net/http"
@ -10,7 +10,7 @@ import (
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
type rateLimits struct { type RateLimits struct {
limits map[string]chan struct{} limits map[string]chan struct{}
limitsMutex sync.RWMutex limitsMutex sync.RWMutex
cleanMutex sync.RWMutex cleanMutex sync.RWMutex
@ -19,8 +19,8 @@ type rateLimits struct {
cooloffDuration time.Duration cooloffDuration time.Duration
} }
func newRateLimits(cfg *config.RateLimiting) *rateLimits { func NewRateLimits(cfg *config.RateLimiting) *RateLimits {
l := &rateLimits{ l := &RateLimits{
limits: make(map[string]chan struct{}), limits: make(map[string]chan struct{}),
enabled: cfg.Enabled, enabled: cfg.Enabled,
requestThreshold: cfg.Threshold, requestThreshold: cfg.Threshold,
@ -32,7 +32,7 @@ func newRateLimits(cfg *config.RateLimiting) *rateLimits {
return l return l
} }
func (l *rateLimits) clean() { func (l *RateLimits) clean() {
for { for {
// On a 30 second interval, we'll take an exclusive write // On a 30 second interval, we'll take an exclusive write
// lock of the entire map and see if any of the channels are // lock of the entire map and see if any of the channels are
@ -52,7 +52,7 @@ func (l *rateLimits) clean() {
} }
} }
func (l *rateLimits) rateLimit(req *http.Request) *util.JSONResponse { func (l *RateLimits) Limit(req *http.Request) *util.JSONResponse {
// If rate limiting is disabled then do nothing. // If rate limiting is disabled then do nothing.
if !l.enabled { if !l.enabled {
return nil return nil

View file

@ -26,7 +26,9 @@ import (
// AddPublicRoutes sets up and registers HTTP handlers for the MediaAPI component. // AddPublicRoutes sets up and registers HTTP handlers for the MediaAPI component.
func AddPublicRoutes( func AddPublicRoutes(
router *mux.Router, cfg *config.MediaAPI, router *mux.Router,
cfg *config.MediaAPI,
rateLimit *config.RateLimiting,
userAPI userapi.UserInternalAPI, userAPI userapi.UserInternalAPI,
client *gomatrixserverlib.Client, client *gomatrixserverlib.Client,
) { ) {
@ -36,6 +38,6 @@ func AddPublicRoutes(
} }
routing.Setup( routing.Setup(
router, cfg, mediaDB, userAPI, client, router, cfg, rateLimit, mediaDB, userAPI, client,
) )
} }

View file

@ -15,16 +15,16 @@
package routing package routing
import ( import (
"encoding/json"
"net/http" "net/http"
"strings" "strings"
userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/matrix-org/dendrite/internal/httputil" "github.com/matrix-org/dendrite/internal/httputil"
"github.com/matrix-org/dendrite/mediaapi/storage" "github.com/matrix-org/dendrite/mediaapi/storage"
"github.com/matrix-org/dendrite/mediaapi/types" "github.com/matrix-org/dendrite/mediaapi/types"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util" "github.com/matrix-org/util"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
@ -32,6 +32,12 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
) )
// configResponse is the response to GET /_matrix/media/r0/config
// https://matrix.org/docs/spec/client_server/latest#get-matrix-media-r0-config
type configResponse struct {
UploadSize config.FileSizeBytes `json:"m.upload.size"`
}
// Setup registers the media API HTTP handlers // Setup registers the media API HTTP handlers
// //
// Due to Setup being used to call many other functions, a gocyclo nolint is // Due to Setup being used to call many other functions, a gocyclo nolint is
@ -40,10 +46,13 @@ import (
func Setup( func Setup(
publicAPIMux *mux.Router, publicAPIMux *mux.Router,
cfg *config.MediaAPI, cfg *config.MediaAPI,
rateLimit *config.RateLimiting,
db storage.Database, db storage.Database,
userAPI userapi.UserInternalAPI, userAPI userapi.UserInternalAPI,
client *gomatrixserverlib.Client, client *gomatrixserverlib.Client,
) { ) {
rateLimits := httputil.NewRateLimits(rateLimit)
r0mux := publicAPIMux.PathPrefix("/r0").Subrouter() r0mux := publicAPIMux.PathPrefix("/r0").Subrouter()
v1mux := publicAPIMux.PathPrefix("/v1").Subrouter() v1mux := publicAPIMux.PathPrefix("/v1").Subrouter()
@ -54,31 +63,46 @@ func Setup(
uploadHandler := httputil.MakeAuthAPI( uploadHandler := httputil.MakeAuthAPI(
"upload", userAPI, "upload", userAPI,
func(req *http.Request, dev *userapi.Device) util.JSONResponse { func(req *http.Request, dev *userapi.Device) util.JSONResponse {
if r := rateLimits.Limit(req); r != nil {
return *r
}
return Upload(req, cfg, dev, db, activeThumbnailGeneration) return Upload(req, cfg, dev, db, activeThumbnailGeneration)
}, },
) )
configHandler := httputil.MakeAuthAPI("config", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
if r := rateLimits.Limit(req); r != nil {
return *r
}
return util.JSONResponse{
Code: http.StatusOK,
JSON: configResponse{UploadSize: *cfg.MaxFileSizeBytes},
}
})
r0mux.Handle("/upload", uploadHandler).Methods(http.MethodPost, http.MethodOptions) r0mux.Handle("/upload", uploadHandler).Methods(http.MethodPost, http.MethodOptions)
r0mux.Handle("/config", configHandler).Methods(http.MethodGet, http.MethodOptions)
v1mux.Handle("/upload", uploadHandler).Methods(http.MethodPost, http.MethodOptions) v1mux.Handle("/upload", uploadHandler).Methods(http.MethodPost, http.MethodOptions)
activeRemoteRequests := &types.ActiveRemoteRequests{ activeRemoteRequests := &types.ActiveRemoteRequests{
MXCToResult: map[string]*types.RemoteRequestResult{}, MXCToResult: map[string]*types.RemoteRequestResult{},
} }
downloadHandler := makeDownloadAPI("download", cfg, db, client, activeRemoteRequests, activeThumbnailGeneration) downloadHandler := makeDownloadAPI("download", cfg, rateLimits, db, client, activeRemoteRequests, activeThumbnailGeneration)
r0mux.Handle("/download/{serverName}/{mediaId}", downloadHandler).Methods(http.MethodGet, http.MethodOptions) r0mux.Handle("/download/{serverName}/{mediaId}", downloadHandler).Methods(http.MethodGet, http.MethodOptions)
r0mux.Handle("/download/{serverName}/{mediaId}/{downloadName}", downloadHandler).Methods(http.MethodGet, http.MethodOptions) r0mux.Handle("/download/{serverName}/{mediaId}/{downloadName}", downloadHandler).Methods(http.MethodGet, http.MethodOptions)
v1mux.Handle("/download/{serverName}/{mediaId}", downloadHandler).Methods(http.MethodGet, http.MethodOptions) // TODO: remove when synapse is fixed v1mux.Handle("/download/{serverName}/{mediaId}", downloadHandler).Methods(http.MethodGet, http.MethodOptions) // TODO: remove when synapse is fixed
v1mux.Handle("/download/{serverName}/{mediaId}/{downloadName}", downloadHandler).Methods(http.MethodGet, http.MethodOptions) // TODO: remove when synapse is fixed v1mux.Handle("/download/{serverName}/{mediaId}/{downloadName}", downloadHandler).Methods(http.MethodGet, http.MethodOptions) // TODO: remove when synapse is fixed
r0mux.Handle("/thumbnail/{serverName}/{mediaId}", r0mux.Handle("/thumbnail/{serverName}/{mediaId}",
makeDownloadAPI("thumbnail", cfg, db, client, activeRemoteRequests, activeThumbnailGeneration), makeDownloadAPI("thumbnail", cfg, rateLimits, db, client, activeRemoteRequests, activeThumbnailGeneration),
).Methods(http.MethodGet, http.MethodOptions) ).Methods(http.MethodGet, http.MethodOptions)
} }
func makeDownloadAPI( func makeDownloadAPI(
name string, name string,
cfg *config.MediaAPI, cfg *config.MediaAPI,
rateLimits *httputil.RateLimits,
db storage.Database, db storage.Database,
client *gomatrixserverlib.Client, client *gomatrixserverlib.Client,
activeRemoteRequests *types.ActiveRemoteRequests, activeRemoteRequests *types.ActiveRemoteRequests,
@ -99,6 +123,16 @@ func makeDownloadAPI(
// Content-Type will be overridden in case of returning file data, else we respond with JSON-formatted errors // Content-Type will be overridden in case of returning file data, else we respond with JSON-formatted errors
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
// Ratelimit requests
if r := rateLimits.Limit(req); r != nil {
if err := json.NewEncoder(w).Encode(r); err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusTooManyRequests)
return
}
vars, _ := httputil.URLDecodeMapValues(mux.Vars(req)) vars, _ := httputil.URLDecodeMapValues(mux.Vars(req))
serverName := gomatrixserverlib.ServerName(vars["serverName"]) serverName := gomatrixserverlib.ServerName(vars["serverName"])

View file

@ -21,7 +21,6 @@ import (
"io" "io"
"net" "net"
"net/http" "net/http"
"net/url"
"os" "os"
"os/signal" "os/signal"
"syscall" "syscall"
@ -79,7 +78,6 @@ type BaseDendrite struct {
SynapseAdminMux *mux.Router SynapseAdminMux *mux.Router
UseHTTPAPIs bool UseHTTPAPIs bool
apiHttpClient *http.Client apiHttpClient *http.Client
httpClient *http.Client
Cfg *config.Dendrite Cfg *config.Dendrite
Caches *caching.Caches Caches *caching.Caches
DNSCache *gomatrixserverlib.DNSCache DNSCache *gomatrixserverlib.DNSCache
@ -181,13 +179,6 @@ func NewBaseDendrite(cfg *config.Dendrite, componentName string, options ...Base
}, },
}, },
} }
client := http.Client{Timeout: HTTPClientTimeout}
if cfg.FederationAPI.Proxy.Enabled {
client.Transport = &http.Transport{Proxy: http.ProxyURL(&url.URL{
Scheme: cfg.FederationAPI.Proxy.Protocol,
Host: fmt.Sprintf("%s:%d", cfg.FederationAPI.Proxy.Host, cfg.FederationAPI.Proxy.Port),
})}
}
// Ideally we would only use SkipClean on routes which we know can allow '/' but due to // Ideally we would only use SkipClean on routes which we know can allow '/' but due to
// https://github.com/gorilla/mux/issues/460 we have to attach this at the top router. // https://github.com/gorilla/mux/issues/460 we have to attach this at the top router.
@ -217,7 +208,6 @@ func NewBaseDendrite(cfg *config.Dendrite, componentName string, options ...Base
InternalAPIMux: mux.NewRouter().SkipClean(true).PathPrefix(httputil.InternalPathPrefix).Subrouter().UseEncodedPath(), InternalAPIMux: mux.NewRouter().SkipClean(true).PathPrefix(httputil.InternalPathPrefix).Subrouter().UseEncodedPath(),
SynapseAdminMux: mux.NewRouter().SkipClean(true).PathPrefix("/_synapse/").Subrouter().UseEncodedPath(), SynapseAdminMux: mux.NewRouter().SkipClean(true).PathPrefix("/_synapse/").Subrouter().UseEncodedPath(),
apiHttpClient: &apiClient, apiHttpClient: &apiClient,
httpClient: &client,
} }
} }

View file

@ -68,7 +68,7 @@ func (m *Monolith) AddAllPublicRoutes(process *process.ProcessContext, csMux, ss
m.KeyRing, m.RoomserverAPI, m.FederationAPI, m.KeyRing, m.RoomserverAPI, m.FederationAPI,
m.EDUInternalAPI, m.KeyAPI, &m.Config.MSCs, nil, m.EDUInternalAPI, m.KeyAPI, &m.Config.MSCs, nil,
) )
mediaapi.AddPublicRoutes(mediaMux, &m.Config.MediaAPI, m.UserAPI, m.Client) mediaapi.AddPublicRoutes(mediaMux, &m.Config.MediaAPI, &m.Config.ClientAPI.RateLimiting, m.UserAPI, m.Client)
syncapi.AddPublicRoutes( syncapi.AddPublicRoutes(
process, csMux, m.UserAPI, m.RoomserverAPI, process, csMux, m.UserAPI, m.RoomserverAPI,
m.KeyAPI, m.FedClient, &m.Config.SyncAPI, m.KeyAPI, m.FedClient, &m.Config.SyncAPI,

View file

@ -7,7 +7,6 @@ import (
"crypto/sha256" "crypto/sha256"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"sort" "sort"
@ -504,7 +503,7 @@ type testUserAPI struct {
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 {
res.Err = fmt.Errorf("unknown token") res.Err = "unknown token"
return nil return nil
} }
res.Device = &dev res.Device = &dev

View file

@ -19,7 +19,6 @@ import (
"context" "context"
"crypto/ed25519" "crypto/ed25519"
"encoding/json" "encoding/json"
"fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
@ -347,7 +346,7 @@ type testUserAPI struct {
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 {
res.Err = fmt.Errorf("unknown token") res.Err = "unknown token"
return nil return nil
} }
res.Device = &dev res.Device = &dev

View file

@ -556,6 +556,7 @@ can fetch self-signing keys over federation
Changing master key notifies local users Changing master key notifies local users
Changing user-signing key notifies local users Changing user-signing key notifies local users
Inbound federation correctly handles soft failed events as extremities Inbound federation correctly handles soft failed events as extremities
Can read configuration endpoint
User can create and send/receive messages in a room with version 7 User can create and send/receive messages in a room with version 7
local user can join room with version 7 local user can join room with version 7
User can invite local user to room with version 7 User can invite local user to room with version 7

View file

@ -33,7 +33,7 @@ type UserInternalAPI interface {
PerformDeviceUpdate(ctx context.Context, req *PerformDeviceUpdateRequest, res *PerformDeviceUpdateResponse) error PerformDeviceUpdate(ctx context.Context, req *PerformDeviceUpdateRequest, res *PerformDeviceUpdateResponse) error
PerformAccountDeactivation(ctx context.Context, req *PerformAccountDeactivationRequest, res *PerformAccountDeactivationResponse) error PerformAccountDeactivation(ctx context.Context, req *PerformAccountDeactivationRequest, res *PerformAccountDeactivationResponse) error
PerformOpenIDTokenCreation(ctx context.Context, req *PerformOpenIDTokenCreationRequest, res *PerformOpenIDTokenCreationResponse) error PerformOpenIDTokenCreation(ctx context.Context, req *PerformOpenIDTokenCreationRequest, res *PerformOpenIDTokenCreationResponse) error
PerformKeyBackup(ctx context.Context, req *PerformKeyBackupRequest, res *PerformKeyBackupResponse) PerformKeyBackup(ctx context.Context, req *PerformKeyBackupRequest, res *PerformKeyBackupResponse) error
QueryKeyBackup(ctx context.Context, req *QueryKeyBackupRequest, res *QueryKeyBackupResponse) QueryKeyBackup(ctx context.Context, req *QueryKeyBackupRequest, res *QueryKeyBackupResponse)
QueryProfile(ctx context.Context, req *QueryProfileRequest, res *QueryProfileResponse) error QueryProfile(ctx context.Context, req *QueryProfileRequest, res *QueryProfileResponse) error
QueryAccessToken(ctx context.Context, req *QueryAccessTokenRequest, res *QueryAccessTokenResponse) error QueryAccessToken(ctx context.Context, req *QueryAccessTokenRequest, res *QueryAccessTokenResponse) error
@ -181,7 +181,7 @@ type QueryAccessTokenRequest struct {
// QueryAccessTokenResponse is the response for QueryAccessToken // QueryAccessTokenResponse is the response for QueryAccessToken
type QueryAccessTokenResponse struct { type QueryAccessTokenResponse struct {
Device *Device Device *Device
Err error // e.g ErrorForbidden Err string // e.g ErrorForbidden
} }
// QueryAccountDataRequest is the request for QueryAccountData // QueryAccountDataRequest is the request for QueryAccountData
@ -290,6 +290,10 @@ type PerformDeviceCreationRequest struct {
IPAddr string IPAddr string
// Useragent for this device // Useragent for this device
UserAgent string UserAgent string
// NoDeviceListUpdate determines whether we should avoid sending a device list
// update for this account. Generally the only reason to do this is if the account
// is an appservice account.
NoDeviceListUpdate bool
} }
// PerformDeviceCreationResponse is the response for PerformDeviceCreation // PerformDeviceCreationResponse is the response for PerformDeviceCreation

View file

@ -74,11 +74,14 @@ func (t *UserInternalAPITrace) PerformOpenIDTokenCreation(ctx context.Context, r
util.GetLogger(ctx).Infof("PerformOpenIDTokenCreation req=%+v res=%+v", js(req), js(res)) util.GetLogger(ctx).Infof("PerformOpenIDTokenCreation req=%+v res=%+v", js(req), js(res))
return err return err
} }
func (t *UserInternalAPITrace) PerformKeyBackup(ctx context.Context, req *PerformKeyBackupRequest, res *PerformKeyBackupResponse) { func (t *UserInternalAPITrace) PerformKeyBackup(ctx context.Context, req *PerformKeyBackupRequest, res *PerformKeyBackupResponse) error {
t.Impl.PerformKeyBackup(ctx, req, res) err := t.Impl.PerformKeyBackup(ctx, req, res)
util.GetLogger(ctx).Infof("PerformKeyBackup req=%+v res=%+v", js(req), js(res))
return err
} }
func (t *UserInternalAPITrace) QueryKeyBackup(ctx context.Context, req *QueryKeyBackupRequest, res *QueryKeyBackupResponse) { func (t *UserInternalAPITrace) QueryKeyBackup(ctx context.Context, req *QueryKeyBackupRequest, res *QueryKeyBackupResponse) {
t.Impl.QueryKeyBackup(ctx, req, res) t.Impl.QueryKeyBackup(ctx, req, res)
util.GetLogger(ctx).Infof("QueryKeyBackup req=%+v res=%+v", js(req), js(res))
} }
func (t *UserInternalAPITrace) QueryProfile(ctx context.Context, req *QueryProfileRequest, res *QueryProfileResponse) error { func (t *UserInternalAPITrace) QueryProfile(ctx context.Context, req *QueryProfileRequest, res *QueryProfileResponse) error {
err := t.Impl.QueryProfile(ctx, req, res) err := t.Impl.QueryProfile(ctx, req, res)

View file

@ -119,6 +119,9 @@ func (a *UserInternalAPI) PerformDeviceCreation(ctx context.Context, req *api.Pe
} }
res.DeviceCreated = true res.DeviceCreated = true
res.Device = dev res.Device = dev
if req.NoDeviceListUpdate {
return nil
}
// create empty device keys and upload them to trigger device list changes // create empty device keys and upload them to trigger device list changes
return a.deviceListUpdate(dev.UserID, []string{dev.ID}) return a.deviceListUpdate(dev.UserID, []string{dev.ID})
} }
@ -358,8 +361,11 @@ func (a *UserInternalAPI) QueryAccountData(ctx context.Context, req *api.QueryAc
func (a *UserInternalAPI) QueryAccessToken(ctx context.Context, req *api.QueryAccessTokenRequest, res *api.QueryAccessTokenResponse) error { func (a *UserInternalAPI) QueryAccessToken(ctx context.Context, req *api.QueryAccessTokenRequest, res *api.QueryAccessTokenResponse) error {
if req.AppServiceUserID != "" { if req.AppServiceUserID != "" {
appServiceDevice, err := a.queryAppServiceToken(ctx, req.AccessToken, req.AppServiceUserID) appServiceDevice, err := a.queryAppServiceToken(ctx, req.AccessToken, req.AppServiceUserID)
if err != nil {
res.Err = err.Error()
}
res.Device = appServiceDevice res.Device = appServiceDevice
res.Err = err
return nil return nil
} }
device, err := a.DeviceDB.GetDeviceByAccessToken(ctx, req.AccessToken) device, err := a.DeviceDB.GetDeviceByAccessToken(ctx, req.AccessToken)
@ -455,13 +461,16 @@ func (a *UserInternalAPI) QueryOpenIDToken(ctx context.Context, req *api.QueryOp
return nil return nil
} }
func (a *UserInternalAPI) PerformKeyBackup(ctx context.Context, req *api.PerformKeyBackupRequest, res *api.PerformKeyBackupResponse) { func (a *UserInternalAPI) PerformKeyBackup(ctx context.Context, req *api.PerformKeyBackupRequest, res *api.PerformKeyBackupResponse) error {
// Delete metadata // Delete metadata
if req.DeleteBackup { if req.DeleteBackup {
if req.Version == "" { if req.Version == "" {
res.BadInput = true res.BadInput = true
res.Error = "must specify a version to delete" res.Error = "must specify a version to delete"
return if res.Error != "" {
return fmt.Errorf(res.Error)
}
return nil
} }
exists, err := a.AccountDB.DeleteKeyBackup(ctx, req.UserID, req.Version) exists, err := a.AccountDB.DeleteKeyBackup(ctx, req.UserID, req.Version)
if err != nil { if err != nil {
@ -469,7 +478,10 @@ func (a *UserInternalAPI) PerformKeyBackup(ctx context.Context, req *api.Perform
} }
res.Exists = exists res.Exists = exists
res.Version = req.Version res.Version = req.Version
return if res.Error != "" {
return fmt.Errorf(res.Error)
}
return nil
} }
// Create metadata // Create metadata
if req.Version == "" { if req.Version == "" {
@ -479,7 +491,10 @@ func (a *UserInternalAPI) PerformKeyBackup(ctx context.Context, req *api.Perform
} }
res.Exists = err == nil res.Exists = err == nil
res.Version = version res.Version = version
return if res.Error != "" {
return fmt.Errorf(res.Error)
}
return nil
} }
// Update metadata // Update metadata
if len(req.Keys.Rooms) == 0 { if len(req.Keys.Rooms) == 0 {
@ -489,10 +504,17 @@ func (a *UserInternalAPI) PerformKeyBackup(ctx context.Context, req *api.Perform
} }
res.Exists = err == nil res.Exists = err == nil
res.Version = req.Version res.Version = req.Version
return if res.Error != "" {
return fmt.Errorf(res.Error)
}
return nil
} }
// Upload Keys for a specific version metadata // Upload Keys for a specific version metadata
a.uploadBackupKeys(ctx, req, res) a.uploadBackupKeys(ctx, req, res)
if res.Error != "" {
return fmt.Errorf(res.Error)
}
return nil
} }
func (a *UserInternalAPI) uploadBackupKeys(ctx context.Context, req *api.PerformKeyBackupRequest, res *api.PerformKeyBackupResponse) { func (a *UserInternalAPI) uploadBackupKeys(ctx context.Context, req *api.PerformKeyBackupRequest, res *api.PerformKeyBackupResponse) {

View file

@ -228,7 +228,7 @@ func (h *httpUserInternalAPI) QueryOpenIDToken(ctx context.Context, req *api.Que
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, req, res) return httputil.PostJSON(ctx, span, h.httpClient, apiURL, req, res)
} }
func (h *httpUserInternalAPI) PerformKeyBackup(ctx context.Context, req *api.PerformKeyBackupRequest, res *api.PerformKeyBackupResponse) { func (h *httpUserInternalAPI) PerformKeyBackup(ctx context.Context, req *api.PerformKeyBackupRequest, res *api.PerformKeyBackupResponse) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "PerformKeyBackup") span, ctx := opentracing.StartSpanFromContext(ctx, "PerformKeyBackup")
defer span.Finish() defer span.Finish()
@ -237,6 +237,7 @@ func (h *httpUserInternalAPI) PerformKeyBackup(ctx context.Context, req *api.Per
if err != nil { if err != nil {
res.Error = err.Error() res.Error = err.Error()
} }
return nil
} }
func (h *httpUserInternalAPI) QueryKeyBackup(ctx context.Context, req *api.QueryKeyBackupRequest, res *api.QueryKeyBackupResponse) { func (h *httpUserInternalAPI) QueryKeyBackup(ctx context.Context, req *api.QueryKeyBackupRequest, res *api.QueryKeyBackupResponse) {
span, ctx := opentracing.StartSpanFromContext(ctx, "QueryKeyBackup") span, ctx := opentracing.StartSpanFromContext(ctx, "QueryKeyBackup")

View file

@ -16,6 +16,7 @@ package inthttp
import ( import (
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
@ -234,4 +235,32 @@ func AddRoutes(internalAPIMux *mux.Router, s api.UserInternalAPI) {
return util.JSONResponse{Code: http.StatusOK, JSON: &response} return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}), }),
) )
internalAPIMux.Handle(QueryKeyBackupPath,
httputil.MakeInternalAPI("queryKeyBackup", func(req *http.Request) util.JSONResponse {
request := api.QueryKeyBackupRequest{}
response := api.QueryKeyBackupResponse{}
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
return util.MessageResponse(http.StatusBadRequest, err.Error())
}
s.QueryKeyBackup(req.Context(), &request, &response)
if response.Error != "" {
return util.ErrorResponse(fmt.Errorf("QueryKeyBackup: %s", response.Error))
}
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}),
)
internalAPIMux.Handle(PerformKeyBackupPath,
httputil.MakeInternalAPI("performKeyBackup", func(req *http.Request) util.JSONResponse {
request := api.PerformKeyBackupRequest{}
response := api.PerformKeyBackupResponse{}
if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
return util.MessageResponse(http.StatusBadRequest, err.Error())
}
err := s.PerformKeyBackup(req.Context(), &request, &response)
if err != nil {
return util.JSONResponse{Code: http.StatusBadRequest, JSON: &response}
}
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}),
)
} }