mirror of
https://github.com/element-hq/dendrite.git
synced 2025-09-13 21:02:25 +03:00
mas: added "admin's replacement without uia" endpoint
i.e. /_synapse/admin/v1/users/{userID}/_allow_cross_signing_replacement_without_uia
This commit is contained in:
parent
63a199cec3
commit
9d9841d02e
10 changed files with 168 additions and 29 deletions
|
@ -2,6 +2,7 @@ package routing
|
|||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -30,6 +31,8 @@ import (
|
|||
userapi "github.com/element-hq/dendrite/userapi/api"
|
||||
)
|
||||
|
||||
const replacementPeriod = 10 * time.Minute
|
||||
|
||||
var validRegistrationTokenRegex = regexp.MustCompile("^[[:ascii:][:digit:]_]*$")
|
||||
|
||||
func AdminCreateNewRegistrationToken(req *http.Request, cfg *config.ClientAPI, userAPI userapi.ClientUserAPI) util.JSONResponse {
|
||||
|
@ -607,6 +610,56 @@ func AdminHandleUserDeviceByUserID(
|
|||
|
||||
}
|
||||
|
||||
func AdminAllowCrossSigningReplacementWithoutUIA(
|
||||
req *http.Request,
|
||||
userAPI userapi.ClientUserAPI,
|
||||
) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.MessageResponse(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
userIDstr, ok := vars["userID"]
|
||||
userID, err := spec.NewUserID(userIDstr, false)
|
||||
if !ok || err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusNotFound,
|
||||
JSON: spec.MissingParam("User not found."),
|
||||
}
|
||||
}
|
||||
|
||||
switch req.Method {
|
||||
case http.MethodPost:
|
||||
rq := userapi.PerformAllowingMasterCrossSigningKeyReplacementWithoutUIARequest{
|
||||
UserID: userID.String(),
|
||||
Duration: replacementPeriod,
|
||||
}
|
||||
var rs userapi.PerformAllowingMasterCrossSigningKeyReplacementWithoutUIAResponse
|
||||
err = userAPI.PerformAllowingMasterCrossSigningKeyReplacementWithoutUIA(req.Context(), &rq, &rs)
|
||||
if err == sql.ErrNoRows {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusNotFound,
|
||||
JSON: spec.MissingParam("User has no master cross-signing key"),
|
||||
}
|
||||
} else if err != nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Error("userAPI.PerformAllowingMasterCrossSigningKeyReplacementWithoutUIA")
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: spec.Unknown(err.Error()),
|
||||
}
|
||||
}
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: map[string]int64{"updatable_without_uia_before_ms": rs.Timestamp},
|
||||
}
|
||||
default:
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusMethodNotAllowed,
|
||||
JSON: spec.Unknown("Method not allowed."),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type adminExternalID struct {
|
||||
AuthProvider string `json:"auth_provider"`
|
||||
ExternalID string `json:"external_id"`
|
||||
|
|
|
@ -176,25 +176,25 @@ func UploadCrossSigningDeviceSignatures(req *http.Request, keyserverAPI api.Clie
|
|||
|
||||
func convertKeyError(err *api.KeyError) util.JSONResponse {
|
||||
switch {
|
||||
case err.IsInvalidSignature:
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.InvalidSignature(err.Error()),
|
||||
}
|
||||
case err.IsMissingParam:
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.MissingParam(err.Error()),
|
||||
}
|
||||
case err.IsInvalidParam:
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.InvalidParam(err.Error()),
|
||||
}
|
||||
default:
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.Unknown(err.Error()),
|
||||
}
|
||||
case err.IsInvalidSignature:
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.InvalidSignature(err.Error()),
|
||||
}
|
||||
case err.IsMissingParam:
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.MissingParam(err.Error()),
|
||||
}
|
||||
case err.IsInvalidParam:
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.InvalidParam(err.Error()),
|
||||
}
|
||||
default:
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.Unknown(err.Error()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -352,6 +352,11 @@ func Setup(
|
|||
httputil.MakeServiceAdminAPI("admin_user_devices", m.AdminToken, func(r *http.Request) util.JSONResponse {
|
||||
return AdminHandleUserDeviceByUserID(r, userAPI)
|
||||
})).Methods(http.MethodPost, http.MethodGet)
|
||||
|
||||
synapseAdminRouter.Handle("/admin/v1/users/{userID}/_allow_cross_signing_replacement_without_uia",
|
||||
httputil.MakeServiceAdminAPI("admin_allow_cross_signing_replacement_without_uia", m.AdminToken, func(r *http.Request) util.JSONResponse {
|
||||
return AdminAllowCrossSigningReplacementWithoutUIA(r, userAPI)
|
||||
})).Methods(http.MethodPost)
|
||||
}
|
||||
|
||||
if mscCfg.Enabled("msc2753") {
|
||||
|
|
|
@ -680,6 +680,11 @@ type ClientKeyAPI interface {
|
|||
QueryKeys(ctx context.Context, req *QueryKeysRequest, res *QueryKeysResponse)
|
||||
QueryMasterKeys(ctx context.Context, req *QueryMasterKeysRequest, res *QueryMasterKeysResponse)
|
||||
PerformUploadKeys(ctx context.Context, req *PerformUploadKeysRequest, res *PerformUploadKeysResponse) error
|
||||
PerformAllowingMasterCrossSigningKeyReplacementWithoutUIA(
|
||||
ctx context.Context,
|
||||
req *PerformAllowingMasterCrossSigningKeyReplacementWithoutUIARequest,
|
||||
res *PerformAllowingMasterCrossSigningKeyReplacementWithoutUIAResponse,
|
||||
) error
|
||||
|
||||
PerformUploadDeviceSignatures(ctx context.Context, req *PerformUploadDeviceSignaturesRequest, res *PerformUploadDeviceSignaturesResponse)
|
||||
// PerformClaimKeys claims one-time keys for use in pre-key messages
|
||||
|
@ -908,6 +913,15 @@ type PerformUploadDeviceKeysResponse struct {
|
|||
Error *KeyError
|
||||
}
|
||||
|
||||
type PerformAllowingMasterCrossSigningKeyReplacementWithoutUIARequest struct {
|
||||
UserID string
|
||||
Duration time.Duration
|
||||
}
|
||||
|
||||
type PerformAllowingMasterCrossSigningKeyReplacementWithoutUIAResponse struct {
|
||||
Timestamp int64
|
||||
}
|
||||
|
||||
type PerformUploadDeviceSignaturesRequest struct {
|
||||
Signatures map[string]map[gomatrixserverlib.KeyID]fclient.CrossSigningForKeyOrDevice
|
||||
// The user that uploaded the sig, should be populated by the clientapi.
|
||||
|
|
|
@ -96,6 +96,16 @@ func sanityCheckKey(key fclient.CrossSigningKey, userID string, purpose fclient.
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a *UserInternalAPI) PerformAllowingMasterCrossSigningKeyReplacementWithoutUIA(
|
||||
ctx context.Context,
|
||||
req *api.PerformAllowingMasterCrossSigningKeyReplacementWithoutUIARequest,
|
||||
res *api.PerformAllowingMasterCrossSigningKeyReplacementWithoutUIAResponse,
|
||||
) error {
|
||||
var err error
|
||||
res.Timestamp, err = a.KeyDatabase.UpdateMasterCrossSigningKeyAllowReplacementWithoutUIA(ctx, req.UserID, req.Duration)
|
||||
return err
|
||||
}
|
||||
|
||||
// nolint:gocyclo
|
||||
func (a *UserInternalAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.PerformUploadDeviceKeysRequest, res *api.PerformUploadDeviceKeysResponse) {
|
||||
// Find the keys to store.
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||
|
@ -231,6 +232,7 @@ type KeyDatabase interface {
|
|||
|
||||
StoreCrossSigningKeysForUser(ctx context.Context, userID string, keyMap types.CrossSigningKeyMap, updatableWithoutUIABeforeMs *int64) error
|
||||
StoreCrossSigningSigsForTarget(ctx context.Context, originUserID string, originKeyID gomatrixserverlib.KeyID, targetUserID string, targetKeyID gomatrixserverlib.KeyID, signature spec.Base64Bytes) error
|
||||
UpdateMasterCrossSigningKeyAllowReplacementWithoutUIA(ctx context.Context, userID string, duration time.Duration) (int64, error)
|
||||
|
||||
DeleteStaleDeviceLists(
|
||||
ctx context.Context,
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/element-hq/dendrite/internal"
|
||||
"github.com/element-hq/dendrite/internal/sqlutil"
|
||||
|
@ -42,11 +43,17 @@ const upsertCrossSigningKeysForUserSQL = "" +
|
|||
" VALUES($1, $2, $3)" +
|
||||
" ON CONFLICT (user_id, key_type) DO UPDATE SET key_data = $3"
|
||||
|
||||
const updateMasterCrossSigningKeyAllowReplacementWithoutUiaSQL = "" +
|
||||
"UPDATE keyserver_cross_signing_keys" +
|
||||
" SET updatable_without_uia_before_ms = $3" +
|
||||
" WHERE user_id = $1 AND key_type = $2"
|
||||
|
||||
type crossSigningKeysStatements struct {
|
||||
db *sql.DB
|
||||
selectCrossSigningKeysForUserStmt *sql.Stmt
|
||||
selectCrossSigningKeysForUserAndKeyTypeStmt *sql.Stmt
|
||||
upsertCrossSigningKeysForUserStmt *sql.Stmt
|
||||
db *sql.DB
|
||||
selectCrossSigningKeysForUserStmt *sql.Stmt
|
||||
selectCrossSigningKeysForUserAndKeyTypeStmt *sql.Stmt
|
||||
upsertCrossSigningKeysForUserStmt *sql.Stmt
|
||||
updateMasterCrossSigningKeyAllowReplacementWithoutUiaStmt *sql.Stmt
|
||||
}
|
||||
|
||||
func NewPostgresCrossSigningKeysTable(db *sql.DB) (tables.CrossSigningKeys, error) {
|
||||
|
@ -61,6 +68,7 @@ func NewPostgresCrossSigningKeysTable(db *sql.DB) (tables.CrossSigningKeys, erro
|
|||
{&s.selectCrossSigningKeysForUserStmt, selectCrossSigningKeysForUserSQL},
|
||||
{&s.selectCrossSigningKeysForUserAndKeyTypeStmt, selectCrossSigningKeysForUserAndKeyTypeSQL},
|
||||
{&s.upsertCrossSigningKeysForUserStmt, upsertCrossSigningKeysForUserSQL},
|
||||
{&s.updateMasterCrossSigningKeyAllowReplacementWithoutUiaStmt, updateMasterCrossSigningKeyAllowReplacementWithoutUiaSQL},
|
||||
}.Prepare(db)
|
||||
}
|
||||
|
||||
|
@ -138,3 +146,16 @@ func (s *crossSigningKeysStatements) UpsertCrossSigningKeysForUser(
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *crossSigningKeysStatements) UpdateMasterCrossSigningKeyAllowReplacementWithoutUIA(ctx context.Context, txn *sql.Tx, userID string, duration time.Duration) (int64, error) {
|
||||
keyTypeInt, _ := types.KeyTypePurposeToInt[fclient.CrossSigningKeyPurposeMaster]
|
||||
ts := time.Now().Add(duration).UnixMilli()
|
||||
result, err := sqlutil.TxStmt(txn, s.updateMasterCrossSigningKeyAllowReplacementWithoutUiaStmt).ExecContext(ctx, userID, keyTypeInt, ts)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if n, _ := result.RowsAffected(); n == 0 {
|
||||
return -1, sql.ErrNoRows
|
||||
}
|
||||
return ts, nil
|
||||
}
|
||||
|
|
|
@ -1159,7 +1159,19 @@ func (d *KeyDatabase) StoreCrossSigningKeysForUser(ctx context.Context, userID s
|
|||
})
|
||||
}
|
||||
|
||||
// StoreCrossSigningSigsForTarget stores a signature for a target user ID and key/dvice.
|
||||
// UpdateMasterCrossSigningKeyAllowReplacementWithoutUIA updates the 'updatable_without_uia_before_ms' attribute of the master cross-signing key.
|
||||
// Normally this attribute depending on its value marks the master key as replaceable without UIA.
|
||||
func (d *KeyDatabase) UpdateMasterCrossSigningKeyAllowReplacementWithoutUIA(ctx context.Context, userID string, duration time.Duration) (int64, error) {
|
||||
var ts int64
|
||||
err := d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||
var err error
|
||||
ts, err = d.CrossSigningKeysTable.UpdateMasterCrossSigningKeyAllowReplacementWithoutUIA(ctx, txn, userID, duration)
|
||||
return err
|
||||
})
|
||||
return ts, err
|
||||
}
|
||||
|
||||
// StoreCrossSigningSigsForTarget stores a signature for a target user ID and key/device.
|
||||
func (d *KeyDatabase) StoreCrossSigningSigsForTarget(
|
||||
ctx context.Context,
|
||||
originUserID string, originKeyID gomatrixserverlib.KeyID,
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/element-hq/dendrite/internal"
|
||||
"github.com/element-hq/dendrite/internal/sqlutil"
|
||||
|
@ -41,11 +42,17 @@ const upsertCrossSigningKeysForUserSQL = "" +
|
|||
"INSERT OR REPLACE INTO keyserver_cross_signing_keys (user_id, key_type, key_data, updatable_without_uia_before_ms)" +
|
||||
" VALUES($1, $2, $3, $4)"
|
||||
|
||||
const updateMasterCrossSigningKeyAllowReplacementWithoutUiaSQL = "" +
|
||||
"UPDATE keyserver_cross_signing_keys" +
|
||||
" SET updatable_without_uia_before_ms = $3" +
|
||||
" WHERE user_id = $1 AND key_type = $2"
|
||||
|
||||
type crossSigningKeysStatements struct {
|
||||
db *sql.DB
|
||||
selectCrossSigningKeysForUserStmt *sql.Stmt
|
||||
selectCrossSigningKeysForUserAndKeyTypeStmt *sql.Stmt
|
||||
upsertCrossSigningKeysForUserStmt *sql.Stmt
|
||||
db *sql.DB
|
||||
selectCrossSigningKeysForUserStmt *sql.Stmt
|
||||
selectCrossSigningKeysForUserAndKeyTypeStmt *sql.Stmt
|
||||
upsertCrossSigningKeysForUserStmt *sql.Stmt
|
||||
updateMasterCrossSigningKeyAllowReplacementWithoutUiaStmt *sql.Stmt
|
||||
}
|
||||
|
||||
func NewSqliteCrossSigningKeysTable(db *sql.DB) (tables.CrossSigningKeys, error) {
|
||||
|
@ -60,6 +67,7 @@ func NewSqliteCrossSigningKeysTable(db *sql.DB) (tables.CrossSigningKeys, error)
|
|||
{&s.selectCrossSigningKeysForUserStmt, selectCrossSigningKeysForUserSQL},
|
||||
{&s.selectCrossSigningKeysForUserAndKeyTypeStmt, selectCrossSigningKeysForUserAndKeyTypeSQL},
|
||||
{&s.upsertCrossSigningKeysForUserStmt, upsertCrossSigningKeysForUserSQL},
|
||||
{&s.updateMasterCrossSigningKeyAllowReplacementWithoutUiaStmt, updateMasterCrossSigningKeyAllowReplacementWithoutUiaSQL},
|
||||
}.Prepare(db)
|
||||
}
|
||||
|
||||
|
@ -137,3 +145,16 @@ func (s *crossSigningKeysStatements) UpsertCrossSigningKeysForUser(
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *crossSigningKeysStatements) UpdateMasterCrossSigningKeyAllowReplacementWithoutUIA(ctx context.Context, txn *sql.Tx, userID string, duration time.Duration) (int64, error) {
|
||||
keyTypeInt, _ := types.KeyTypePurposeToInt[fclient.CrossSigningKeyPurposeMaster]
|
||||
ts := time.Now().Add(duration).UnixMilli()
|
||||
result, err := sqlutil.TxStmt(txn, s.updateMasterCrossSigningKeyAllowReplacementWithoutUiaStmt).ExecContext(ctx, userID, keyTypeInt, ts)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if n, _ := result.RowsAffected(); n == 0 {
|
||||
return -1, sql.ErrNoRows
|
||||
}
|
||||
return ts, nil
|
||||
}
|
||||
|
|
|
@ -200,6 +200,7 @@ type CrossSigningKeys interface {
|
|||
SelectCrossSigningKeysForUser(ctx context.Context, txn *sql.Tx, userID string) (r types.CrossSigningKeyMap, err error)
|
||||
SelectCrossSigningKeysForUserAndKeyType(ctx context.Context, txn *sql.Tx, userID string, keyType fclient.CrossSigningKeyPurpose) (r types.CrossSigningKeyMap, err error)
|
||||
UpsertCrossSigningKeysForUser(ctx context.Context, txn *sql.Tx, userID string, keyType fclient.CrossSigningKeyPurpose, keyData spec.Base64Bytes, updatableWithoutUIABeforeMs *int64) error
|
||||
UpdateMasterCrossSigningKeyAllowReplacementWithoutUIA(ctx context.Context, txn *sql.Tx, userID string, duration time.Duration) (int64, error)
|
||||
}
|
||||
|
||||
type CrossSigningSigs interface {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue