2021-07-27 12:47:32 +01:00
|
|
|
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package routing
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
"github.com/matrix-org/dendrite/clientapi/httputil"
|
|
|
|
userapi "github.com/matrix-org/dendrite/userapi/api"
|
2023-05-09 23:46:49 +01:00
|
|
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
2021-07-27 12:47:32 +01:00
|
|
|
"github.com/matrix-org/util"
|
|
|
|
)
|
|
|
|
|
|
|
|
type keyBackupVersion struct {
|
|
|
|
Algorithm string `json:"algorithm"`
|
|
|
|
AuthData json.RawMessage `json:"auth_data"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type keyBackupVersionCreateResponse struct {
|
|
|
|
Version string `json:"version"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type keyBackupVersionResponse struct {
|
|
|
|
Algorithm string `json:"algorithm"`
|
|
|
|
AuthData json.RawMessage `json:"auth_data"`
|
2021-07-27 19:29:32 +01:00
|
|
|
Count int64 `json:"count"`
|
2021-07-27 12:47:32 +01:00
|
|
|
ETag string `json:"etag"`
|
|
|
|
Version string `json:"version"`
|
|
|
|
}
|
|
|
|
|
2021-07-27 17:08:53 +01:00
|
|
|
type keyBackupSessionRequest struct {
|
|
|
|
Rooms map[string]struct {
|
|
|
|
Sessions map[string]userapi.KeyBackupSession `json:"sessions"`
|
|
|
|
} `json:"rooms"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type keyBackupSessionResponse struct {
|
|
|
|
Count int64 `json:"count"`
|
|
|
|
ETag string `json:"etag"`
|
|
|
|
}
|
|
|
|
|
2021-07-27 12:47:32 +01:00
|
|
|
// Create a new key backup. Request must contain a `keyBackupVersion`. Returns a `keyBackupVersionCreateResponse`.
|
|
|
|
// Implements POST /_matrix/client/r0/room_keys/version
|
2022-05-05 13:17:38 +01:00
|
|
|
func CreateKeyBackupVersion(req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device) util.JSONResponse {
|
2021-07-27 12:47:32 +01:00
|
|
|
var kb keyBackupVersion
|
|
|
|
resErr := httputil.UnmarshalJSONRequest(req, &kb)
|
|
|
|
if resErr != nil {
|
|
|
|
return *resErr
|
|
|
|
}
|
2023-04-28 16:49:38 +01:00
|
|
|
if len(kb.AuthData) == 0 {
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusBadRequest,
|
2023-05-09 23:46:49 +01:00
|
|
|
JSON: spec.BadJSON("missing auth_data"),
|
2023-04-28 16:49:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
version, err := userAPI.PerformKeyBackup(req.Context(), &userapi.PerformKeyBackupRequest{
|
2021-07-27 12:47:32 +01:00
|
|
|
UserID: device.UserID,
|
|
|
|
Version: "",
|
|
|
|
AuthData: kb.AuthData,
|
|
|
|
Algorithm: kb.Algorithm,
|
2023-04-28 16:49:38 +01:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return util.ErrorResponse(fmt.Errorf("PerformKeyBackup: %w", err))
|
2021-07-27 12:47:32 +01:00
|
|
|
}
|
2023-04-28 16:49:38 +01:00
|
|
|
|
2021-07-27 12:47:32 +01:00
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 200,
|
|
|
|
JSON: keyBackupVersionCreateResponse{
|
2023-04-28 16:49:38 +01:00
|
|
|
Version: version,
|
2021-07-27 12:47:32 +01:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// KeyBackupVersion returns the key backup version specified. If `version` is empty, the latest `keyBackupVersionResponse` is returned.
|
|
|
|
// Implements GET /_matrix/client/r0/room_keys/version and GET /_matrix/client/r0/room_keys/version/{version}
|
2022-05-05 13:17:38 +01:00
|
|
|
func KeyBackupVersion(req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version string) util.JSONResponse {
|
2023-04-28 16:49:38 +01:00
|
|
|
queryResp, err := userAPI.QueryKeyBackup(req.Context(), &userapi.QueryKeyBackupRequest{
|
2021-07-27 19:29:32 +01:00
|
|
|
UserID: device.UserID,
|
|
|
|
Version: version,
|
2023-04-28 16:49:38 +01:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return util.ErrorResponse(fmt.Errorf("QueryKeyBackup: %s", err))
|
2021-07-27 12:47:32 +01:00
|
|
|
}
|
|
|
|
if !queryResp.Exists {
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 404,
|
2023-05-09 23:46:49 +01:00
|
|
|
JSON: spec.NotFound("version not found"),
|
2021-07-27 12:47:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 200,
|
|
|
|
JSON: keyBackupVersionResponse{
|
|
|
|
Algorithm: queryResp.Algorithm,
|
|
|
|
AuthData: queryResp.AuthData,
|
|
|
|
Count: queryResp.Count,
|
|
|
|
ETag: queryResp.ETag,
|
|
|
|
Version: queryResp.Version,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Modify the auth data of a key backup. Version must not be empty. Request must contain a `keyBackupVersion`
|
|
|
|
// Implements PUT /_matrix/client/r0/room_keys/version/{version}
|
2022-05-05 13:17:38 +01:00
|
|
|
func ModifyKeyBackupVersionAuthData(req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version string) util.JSONResponse {
|
2021-07-27 12:47:32 +01:00
|
|
|
var kb keyBackupVersion
|
|
|
|
resErr := httputil.UnmarshalJSONRequest(req, &kb)
|
|
|
|
if resErr != nil {
|
|
|
|
return *resErr
|
|
|
|
}
|
2023-04-28 16:49:38 +01:00
|
|
|
performKeyBackupResp, err := userAPI.UpdateBackupKeyAuthData(req.Context(), &userapi.PerformKeyBackupRequest{
|
2021-07-27 12:47:32 +01:00
|
|
|
UserID: device.UserID,
|
|
|
|
Version: version,
|
|
|
|
AuthData: kb.AuthData,
|
|
|
|
Algorithm: kb.Algorithm,
|
2023-04-28 16:49:38 +01:00
|
|
|
})
|
|
|
|
switch e := err.(type) {
|
2023-05-17 01:33:27 +01:00
|
|
|
case spec.ErrRoomKeysVersion:
|
2023-04-28 16:49:38 +01:00
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusForbidden,
|
|
|
|
JSON: e,
|
2021-07-27 12:47:32 +01:00
|
|
|
}
|
2023-04-28 16:49:38 +01:00
|
|
|
case nil:
|
|
|
|
default:
|
|
|
|
return util.ErrorResponse(fmt.Errorf("PerformKeyBackup: %w", e))
|
2021-07-27 12:47:32 +01:00
|
|
|
}
|
2023-04-28 16:49:38 +01:00
|
|
|
|
2021-07-27 12:47:32 +01:00
|
|
|
if !performKeyBackupResp.Exists {
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 404,
|
2023-05-09 23:46:49 +01:00
|
|
|
JSON: spec.NotFound("backup version not found"),
|
2021-07-27 12:47:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 200,
|
|
|
|
JSON: keyBackupVersionCreateResponse{
|
|
|
|
Version: performKeyBackupResp.Version,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete a version of key backup. Version must not be empty. If the key backup was previously deleted, will return 200 OK.
|
|
|
|
// Implements DELETE /_matrix/client/r0/room_keys/version/{version}
|
2022-05-05 13:17:38 +01:00
|
|
|
func DeleteKeyBackupVersion(req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version string) util.JSONResponse {
|
2023-04-28 16:49:38 +01:00
|
|
|
exists, err := userAPI.DeleteKeyBackup(req.Context(), device.UserID, version)
|
|
|
|
if err != nil {
|
|
|
|
return util.ErrorResponse(fmt.Errorf("DeleteKeyBackup: %s", err))
|
2021-12-03 17:18:35 +00:00
|
|
|
}
|
2023-04-28 16:49:38 +01:00
|
|
|
if !exists {
|
2021-07-27 12:47:32 +01:00
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 404,
|
2023-05-09 23:46:49 +01:00
|
|
|
JSON: spec.NotFound("backup version not found"),
|
2021-07-27 12:47:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 200,
|
2023-04-28 16:49:38 +01:00
|
|
|
JSON: struct{}{},
|
2021-07-27 12:47:32 +01:00
|
|
|
}
|
|
|
|
}
|
2021-07-27 17:08:53 +01:00
|
|
|
|
|
|
|
// Upload a bunch of session keys for a given `version`.
|
|
|
|
func UploadBackupKeys(
|
2022-05-05 13:17:38 +01:00
|
|
|
req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version string, keys *keyBackupSessionRequest,
|
2021-07-27 17:08:53 +01:00
|
|
|
) util.JSONResponse {
|
2023-04-28 16:49:38 +01:00
|
|
|
performKeyBackupResp, err := userAPI.UpdateBackupKeyAuthData(req.Context(), &userapi.PerformKeyBackupRequest{
|
2021-07-27 17:08:53 +01:00
|
|
|
UserID: device.UserID,
|
|
|
|
Version: version,
|
|
|
|
Keys: *keys,
|
2023-04-28 16:49:38 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
switch e := err.(type) {
|
2023-05-17 01:33:27 +01:00
|
|
|
case spec.ErrRoomKeysVersion:
|
2023-04-28 16:49:38 +01:00
|
|
|
return util.JSONResponse{
|
|
|
|
Code: http.StatusForbidden,
|
|
|
|
JSON: e,
|
2021-07-27 17:08:53 +01:00
|
|
|
}
|
2023-04-28 16:49:38 +01:00
|
|
|
case nil:
|
|
|
|
default:
|
|
|
|
return util.ErrorResponse(fmt.Errorf("PerformKeyBackup: %w", e))
|
2021-07-27 17:08:53 +01:00
|
|
|
}
|
|
|
|
if !performKeyBackupResp.Exists {
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 404,
|
2023-05-09 23:46:49 +01:00
|
|
|
JSON: spec.NotFound("backup version not found"),
|
2021-07-27 17:08:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 200,
|
|
|
|
JSON: keyBackupSessionResponse{
|
|
|
|
Count: performKeyBackupResp.KeyCount,
|
|
|
|
ETag: performKeyBackupResp.KeyETag,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2021-07-27 19:29:32 +01:00
|
|
|
|
|
|
|
// Get keys from a given backup version. Response returned varies depending on if roomID and sessionID are set.
|
|
|
|
func GetBackupKeys(
|
2022-05-05 13:17:38 +01:00
|
|
|
req *http.Request, userAPI userapi.ClientUserAPI, device *userapi.Device, version, roomID, sessionID string,
|
2021-07-27 19:29:32 +01:00
|
|
|
) util.JSONResponse {
|
2023-04-28 16:49:38 +01:00
|
|
|
queryResp, err := userAPI.QueryKeyBackup(req.Context(), &userapi.QueryKeyBackupRequest{
|
2021-07-27 19:29:32 +01:00
|
|
|
UserID: device.UserID,
|
|
|
|
Version: version,
|
|
|
|
ReturnKeys: true,
|
|
|
|
KeysForRoomID: roomID,
|
|
|
|
KeysForSessionID: sessionID,
|
2023-04-28 16:49:38 +01:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return util.ErrorResponse(fmt.Errorf("QueryKeyBackup: %w", err))
|
2021-07-27 19:29:32 +01:00
|
|
|
}
|
|
|
|
if !queryResp.Exists {
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 404,
|
2023-05-09 23:46:49 +01:00
|
|
|
JSON: spec.NotFound("version not found"),
|
2021-07-27 19:29:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if sessionID != "" {
|
|
|
|
// return the key itself if it was found
|
|
|
|
roomData, ok := queryResp.Keys[roomID]
|
|
|
|
if ok {
|
|
|
|
key, ok := roomData[sessionID]
|
|
|
|
if ok {
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 200,
|
|
|
|
JSON: key,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if roomID != "" {
|
|
|
|
roomData, ok := queryResp.Keys[roomID]
|
2023-04-28 16:49:38 +01:00
|
|
|
if !ok {
|
|
|
|
// If no keys are found, then an object with an empty sessions property will be returned
|
|
|
|
roomData = make(map[string]userapi.KeyBackupSession)
|
|
|
|
}
|
|
|
|
// wrap response in "sessions"
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 200,
|
|
|
|
JSON: struct {
|
|
|
|
Sessions map[string]userapi.KeyBackupSession `json:"sessions"`
|
|
|
|
}{
|
|
|
|
Sessions: roomData,
|
|
|
|
},
|
2021-07-27 19:29:32 +01:00
|
|
|
}
|
2023-04-28 16:49:38 +01:00
|
|
|
|
2021-07-27 19:29:32 +01:00
|
|
|
} else {
|
|
|
|
// response is the same as the upload request
|
|
|
|
var resp keyBackupSessionRequest
|
|
|
|
resp.Rooms = make(map[string]struct {
|
|
|
|
Sessions map[string]userapi.KeyBackupSession `json:"sessions"`
|
|
|
|
})
|
|
|
|
for roomID, roomData := range queryResp.Keys {
|
|
|
|
resp.Rooms[roomID] = struct {
|
|
|
|
Sessions map[string]userapi.KeyBackupSession `json:"sessions"`
|
|
|
|
}{
|
|
|
|
Sessions: roomData,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 200,
|
|
|
|
JSON: resp,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return util.JSONResponse{
|
|
|
|
Code: 404,
|
2023-05-09 23:46:49 +01:00
|
|
|
JSON: spec.NotFound("keys not found"),
|
2021-07-27 19:29:32 +01:00
|
|
|
}
|
|
|
|
}
|