Alias key backup endpoints onto /unstable, fix key backup bugs (#1947)
* Default /unstable requests to stable endpoints if not overridden specifically with a custom route * Rewrite URL * Try something different * Fix routing manually * Fix selectLatestVersionSQL * Don't return 0 if no backup version exists * Log more useful error * fix up replace keys check * Don't enforce uniqueness on e2e_room_keys_versions_idx Co-authored-by: kegsay <kegan@matrix.org>main
parent
3e01a88a0c
commit
9e4618000e
|
@ -898,49 +898,54 @@ func Setup(
|
||||||
|
|
||||||
// Key Backup Versions (Metadata)
|
// Key Backup Versions (Metadata)
|
||||||
|
|
||||||
r0mux.Handle("/room_keys/version/{version}",
|
getBackupKeysVersion := httputil.MakeAuthAPI("get_backup_keys_version", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
httputil.MakeAuthAPI("get_backup_keys_version", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
|
||||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return KeyBackupVersion(req, userAPI, device, vars["version"])
|
return KeyBackupVersion(req, userAPI, device, vars["version"])
|
||||||
}),
|
})
|
||||||
).Methods(http.MethodGet, http.MethodOptions)
|
|
||||||
r0mux.Handle("/room_keys/version",
|
getLatestBackupKeysVersion := httputil.MakeAuthAPI("get_latest_backup_keys_version", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
httputil.MakeAuthAPI("get_latest_backup_keys_version", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
|
||||||
return KeyBackupVersion(req, userAPI, device, "")
|
return KeyBackupVersion(req, userAPI, device, "")
|
||||||
}),
|
})
|
||||||
).Methods(http.MethodGet, http.MethodOptions)
|
|
||||||
r0mux.Handle("/room_keys/version/{version}",
|
putBackupKeysVersion := httputil.MakeAuthAPI("put_backup_keys_version", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
httputil.MakeAuthAPI("put_backup_keys_version", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
|
||||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return ModifyKeyBackupVersionAuthData(req, userAPI, device, vars["version"])
|
return ModifyKeyBackupVersionAuthData(req, userAPI, device, vars["version"])
|
||||||
}),
|
})
|
||||||
).Methods(http.MethodPut)
|
|
||||||
r0mux.Handle("/room_keys/version/{version}",
|
deleteBackupKeysVersion := httputil.MakeAuthAPI("delete_backup_keys_version", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
httputil.MakeAuthAPI("delete_backup_keys_version", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
|
||||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return DeleteKeyBackupVersion(req, userAPI, device, vars["version"])
|
return DeleteKeyBackupVersion(req, userAPI, device, vars["version"])
|
||||||
}),
|
})
|
||||||
).Methods(http.MethodDelete)
|
|
||||||
r0mux.Handle("/room_keys/version",
|
postNewBackupKeysVersion := httputil.MakeAuthAPI("post_new_backup_keys_version", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
httputil.MakeAuthAPI("post_new_backup_keys_version", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
|
||||||
return CreateKeyBackupVersion(req, userAPI, device)
|
return CreateKeyBackupVersion(req, userAPI, device)
|
||||||
}),
|
})
|
||||||
).Methods(http.MethodPost, http.MethodOptions)
|
|
||||||
|
r0mux.Handle("/room_keys/version/{version}", getBackupKeysVersion).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
r0mux.Handle("/room_keys/version", getLatestBackupKeysVersion).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
r0mux.Handle("/room_keys/version/{version}", putBackupKeysVersion).Methods(http.MethodPut)
|
||||||
|
r0mux.Handle("/room_keys/version/{version}", deleteBackupKeysVersion).Methods(http.MethodDelete)
|
||||||
|
r0mux.Handle("/room_keys/version", postNewBackupKeysVersion).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
|
unstableMux.Handle("/room_keys/version/{version}", getBackupKeysVersion).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
unstableMux.Handle("/room_keys/version", getLatestBackupKeysVersion).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
unstableMux.Handle("/room_keys/version/{version}", putBackupKeysVersion).Methods(http.MethodPut)
|
||||||
|
unstableMux.Handle("/room_keys/version/{version}", deleteBackupKeysVersion).Methods(http.MethodDelete)
|
||||||
|
unstableMux.Handle("/room_keys/version", postNewBackupKeysVersion).Methods(http.MethodPost, http.MethodOptions)
|
||||||
|
|
||||||
// Inserting E2E Backup Keys
|
// Inserting E2E Backup Keys
|
||||||
|
|
||||||
// Bulk room and session
|
// Bulk room and session
|
||||||
r0mux.Handle("/room_keys/keys",
|
putBackupKeys := httputil.MakeAuthAPI("put_backup_keys", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
httputil.MakeAuthAPI("put_backup_keys", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
|
||||||
version := req.URL.Query().Get("version")
|
version := req.URL.Query().Get("version")
|
||||||
if version == "" {
|
if version == "" {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
@ -954,11 +959,10 @@ func Setup(
|
||||||
return *resErr
|
return *resErr
|
||||||
}
|
}
|
||||||
return UploadBackupKeys(req, userAPI, device, version, &reqBody)
|
return UploadBackupKeys(req, userAPI, device, version, &reqBody)
|
||||||
}),
|
})
|
||||||
).Methods(http.MethodPut)
|
|
||||||
// Single room bulk session
|
// Single room bulk session
|
||||||
r0mux.Handle("/room_keys/keys/{roomID}",
|
putBackupKeysRoom := httputil.MakeAuthAPI("put_backup_keys_room", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
httputil.MakeAuthAPI("put_backup_keys_room", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
|
||||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
|
@ -987,11 +991,10 @@ func Setup(
|
||||||
}
|
}
|
||||||
reqBody.Rooms[roomID] = body
|
reqBody.Rooms[roomID] = body
|
||||||
return UploadBackupKeys(req, userAPI, device, version, &reqBody)
|
return UploadBackupKeys(req, userAPI, device, version, &reqBody)
|
||||||
}),
|
})
|
||||||
).Methods(http.MethodPut)
|
|
||||||
// Single room, single session
|
// Single room, single session
|
||||||
r0mux.Handle("/room_keys/keys/{roomID}/{sessionID}",
|
putBackupKeysRoomSession := httputil.MakeAuthAPI("put_backup_keys_room_session", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
httputil.MakeAuthAPI("put_backup_keys_room_session", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
|
||||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
|
@ -1021,34 +1024,45 @@ func Setup(
|
||||||
}
|
}
|
||||||
keyReq.Rooms[roomID].Sessions[sessionID] = reqBody
|
keyReq.Rooms[roomID].Sessions[sessionID] = reqBody
|
||||||
return UploadBackupKeys(req, userAPI, device, version, &keyReq)
|
return UploadBackupKeys(req, userAPI, device, version, &keyReq)
|
||||||
}),
|
})
|
||||||
).Methods(http.MethodPut)
|
|
||||||
|
r0mux.Handle("/room_keys/keys", putBackupKeys).Methods(http.MethodPut)
|
||||||
|
r0mux.Handle("/room_keys/keys/{roomID}", putBackupKeysRoom).Methods(http.MethodPut)
|
||||||
|
r0mux.Handle("/room_keys/keys/{roomID}/{sessionID}", putBackupKeysRoomSession).Methods(http.MethodPut)
|
||||||
|
|
||||||
|
unstableMux.Handle("/room_keys/keys", putBackupKeys).Methods(http.MethodPut)
|
||||||
|
unstableMux.Handle("/room_keys/keys/{roomID}", putBackupKeysRoom).Methods(http.MethodPut)
|
||||||
|
unstableMux.Handle("/room_keys/keys/{roomID}/{sessionID}", putBackupKeysRoomSession).Methods(http.MethodPut)
|
||||||
|
|
||||||
// Querying E2E Backup Keys
|
// Querying E2E Backup Keys
|
||||||
|
|
||||||
r0mux.Handle("/room_keys/keys",
|
getBackupKeys := httputil.MakeAuthAPI("get_backup_keys", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
httputil.MakeAuthAPI("get_backup_keys", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
|
||||||
return GetBackupKeys(req, userAPI, device, req.URL.Query().Get("version"), "", "")
|
return GetBackupKeys(req, userAPI, device, req.URL.Query().Get("version"), "", "")
|
||||||
}),
|
})
|
||||||
).Methods(http.MethodGet, http.MethodOptions)
|
|
||||||
r0mux.Handle("/room_keys/keys/{roomID}",
|
getBackupKeysRoom := httputil.MakeAuthAPI("get_backup_keys_room", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
httputil.MakeAuthAPI("get_backup_keys_room", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
|
||||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return GetBackupKeys(req, userAPI, device, req.URL.Query().Get("version"), vars["roomID"], "")
|
return GetBackupKeys(req, userAPI, device, req.URL.Query().Get("version"), vars["roomID"], "")
|
||||||
}),
|
})
|
||||||
).Methods(http.MethodGet, http.MethodOptions)
|
|
||||||
r0mux.Handle("/room_keys/keys/{roomID}/{sessionID}",
|
getBackupKeysRoomSession := httputil.MakeAuthAPI("get_backup_keys_room_session", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||||
httputil.MakeAuthAPI("get_backup_keys_room_session", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
|
||||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
}
|
}
|
||||||
return GetBackupKeys(req, userAPI, device, req.URL.Query().Get("version"), vars["roomID"], vars["sessionID"])
|
return GetBackupKeys(req, userAPI, device, req.URL.Query().Get("version"), vars["roomID"], vars["sessionID"])
|
||||||
}),
|
})
|
||||||
).Methods(http.MethodGet, http.MethodOptions)
|
|
||||||
|
r0mux.Handle("/room_keys/keys", getBackupKeys).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
r0mux.Handle("/room_keys/keys/{roomID}", getBackupKeysRoom).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
r0mux.Handle("/room_keys/keys/{roomID}/{sessionID}", getBackupKeysRoomSession).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
|
||||||
|
unstableMux.Handle("/room_keys/keys", getBackupKeys).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
unstableMux.Handle("/room_keys/keys/{roomID}", getBackupKeysRoom).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
unstableMux.Handle("/room_keys/keys/{roomID}/{sessionID}", getBackupKeysRoomSession).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
|
||||||
// Deleting E2E Backup Keys
|
// Deleting E2E Backup Keys
|
||||||
|
|
||||||
|
|
|
@ -72,13 +72,11 @@ func (a *KeyBackupSession) ShouldReplaceRoomKey(newKey *KeyBackupSession) bool {
|
||||||
// "if the keys have different values for is_verified, then it will keep the key that has is_verified set to true"
|
// "if the keys have different values for is_verified, then it will keep the key that has is_verified set to true"
|
||||||
if newKey.IsVerified && !a.IsVerified {
|
if newKey.IsVerified && !a.IsVerified {
|
||||||
return true
|
return true
|
||||||
}
|
} else if newKey.FirstMessageIndex < a.FirstMessageIndex {
|
||||||
// "if they have the same values for is_verified, then it will keep the key with a lower first_message_index"
|
// "if they have the same values for is_verified, then it will keep the key with a lower first_message_index"
|
||||||
if newKey.FirstMessageIndex < a.FirstMessageIndex {
|
|
||||||
return true
|
return true
|
||||||
}
|
} else if newKey.ForwardedCount < a.ForwardedCount {
|
||||||
// "and finally, is is_verified and first_message_index are equal, then it will keep the key with a lower forwarded_count"
|
// "and finally, is is_verified and first_message_index are equal, then it will keep the key with a lower forwarded_count"
|
||||||
if newKey.ForwardedCount < a.ForwardedCount {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -36,7 +36,7 @@ CREATE TABLE IF NOT EXISTS account_e2e_room_keys (
|
||||||
session_data TEXT NOT NULL
|
session_data TEXT NOT NULL
|
||||||
);
|
);
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS e2e_room_keys_idx ON account_e2e_room_keys(user_id, room_id, session_id, version);
|
CREATE UNIQUE INDEX IF NOT EXISTS e2e_room_keys_idx ON account_e2e_room_keys(user_id, room_id, session_id, version);
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS e2e_room_keys_versions_idx ON account_e2e_room_keys(user_id, version);
|
CREATE INDEX IF NOT EXISTS e2e_room_keys_versions_idx ON account_e2e_room_keys(user_id, version);
|
||||||
`
|
`
|
||||||
|
|
||||||
const insertBackupKeySQL = "" +
|
const insertBackupKeySQL = "" +
|
||||||
|
|
|
@ -146,13 +146,20 @@ func (s *keyBackupVersionStatements) selectKeyBackup(
|
||||||
) (versionResult, algorithm string, authData json.RawMessage, etag string, deleted bool, err error) {
|
) (versionResult, algorithm string, authData json.RawMessage, etag string, deleted bool, err error) {
|
||||||
var versionInt int64
|
var versionInt int64
|
||||||
if version == "" {
|
if version == "" {
|
||||||
err = txn.Stmt(s.selectLatestVersionStmt).QueryRowContext(ctx, userID).Scan(&versionInt)
|
var v *int64 // allows nulls
|
||||||
} else {
|
if err = txn.Stmt(s.selectLatestVersionStmt).QueryRowContext(ctx, userID).Scan(&v); err != nil {
|
||||||
versionInt, err = strconv.ParseInt(version, 10, 64)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if v == nil {
|
||||||
|
err = sql.ErrNoRows
|
||||||
|
return
|
||||||
|
}
|
||||||
|
versionInt = *v
|
||||||
|
} else {
|
||||||
|
if versionInt, err = strconv.ParseInt(version, 10, 64); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
versionResult = strconv.FormatInt(versionInt, 10)
|
versionResult = strconv.FormatInt(versionInt, 10)
|
||||||
var deletedInt int
|
var deletedInt int
|
||||||
var authDataStr string
|
var authDataStr string
|
||||||
|
|
|
@ -479,7 +479,7 @@ func (d *Database) UpsertBackupKeys(
|
||||||
err = d.keyBackups.updateBackupKey(ctx, txn, userID, version, newKey)
|
err = d.keyBackups.updateBackupKey(ctx, txn, userID, version, newKey)
|
||||||
changed = true
|
changed = true
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("d.keyBackups.updateBackupKey: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if we shouldn't replace the key we do nothing with it
|
// if we shouldn't replace the key we do nothing with it
|
||||||
|
@ -490,7 +490,7 @@ func (d *Database) UpsertBackupKeys(
|
||||||
err = d.keyBackups.insertBackupKey(ctx, txn, userID, version, newKey)
|
err = d.keyBackups.insertBackupKey(ctx, txn, userID, version, newKey)
|
||||||
changed = true
|
changed = true
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("d.keyBackups.insertBackupKey: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ CREATE TABLE IF NOT EXISTS account_e2e_room_keys (
|
||||||
session_data TEXT NOT NULL
|
session_data TEXT NOT NULL
|
||||||
);
|
);
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS e2e_room_keys_idx ON account_e2e_room_keys(user_id, room_id, session_id, version);
|
CREATE UNIQUE INDEX IF NOT EXISTS e2e_room_keys_idx ON account_e2e_room_keys(user_id, room_id, session_id, version);
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS e2e_room_keys_versions_idx ON account_e2e_room_keys(user_id, version);
|
CREATE INDEX IF NOT EXISTS e2e_room_keys_versions_idx ON account_e2e_room_keys(user_id, version);
|
||||||
`
|
`
|
||||||
|
|
||||||
const insertBackupKeySQL = "" +
|
const insertBackupKeySQL = "" +
|
||||||
|
|
|
@ -144,13 +144,20 @@ func (s *keyBackupVersionStatements) selectKeyBackup(
|
||||||
) (versionResult, algorithm string, authData json.RawMessage, etag string, deleted bool, err error) {
|
) (versionResult, algorithm string, authData json.RawMessage, etag string, deleted bool, err error) {
|
||||||
var versionInt int64
|
var versionInt int64
|
||||||
if version == "" {
|
if version == "" {
|
||||||
err = txn.Stmt(s.selectLatestVersionStmt).QueryRowContext(ctx, userID).Scan(&versionInt)
|
var v *int64 // allows nulls
|
||||||
} else {
|
if err = txn.Stmt(s.selectLatestVersionStmt).QueryRowContext(ctx, userID).Scan(&v); err != nil {
|
||||||
versionInt, err = strconv.ParseInt(version, 10, 64)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if v == nil {
|
||||||
|
err = sql.ErrNoRows
|
||||||
|
return
|
||||||
|
}
|
||||||
|
versionInt = *v
|
||||||
|
} else {
|
||||||
|
if versionInt, err = strconv.ParseInt(version, 10, 64); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
versionResult = strconv.FormatInt(versionInt, 10)
|
versionResult = strconv.FormatInt(versionInt, 10)
|
||||||
var deletedInt int
|
var deletedInt int
|
||||||
var authDataStr string
|
var authDataStr string
|
||||||
|
|
|
@ -520,7 +520,7 @@ func (d *Database) UpsertBackupKeys(
|
||||||
err = d.keyBackups.updateBackupKey(ctx, txn, userID, version, newKey)
|
err = d.keyBackups.updateBackupKey(ctx, txn, userID, version, newKey)
|
||||||
changed = true
|
changed = true
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("d.keyBackups.updateBackupKey: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if we shouldn't replace the key we do nothing with it
|
// if we shouldn't replace the key we do nothing with it
|
||||||
|
@ -531,7 +531,7 @@ func (d *Database) UpsertBackupKeys(
|
||||||
err = d.keyBackups.insertBackupKey(ctx, txn, userID, version, newKey)
|
err = d.keyBackups.insertBackupKey(ctx, txn, userID, version, newKey)
|
||||||
changed = true
|
changed = true
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("d.keyBackups.insertBackupKey: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue