mirror of
https://github.com/1f349/dendrite.git
synced 2024-11-24 12:41:34 +00:00
d357615452
Fixes https://github.com/matrix-org/dendrite/issues/3273 As we otherwise send down device list updates which are merely useful for the user and causes tests to be flakey: ``` ❌ TestPushSync/Adding_a_push_rule_wakes_up_an_incremental_/sync (10ms) push_test.go:57: no pushrules found in sync response: {"next_batch":"s0_0_0_0_0_1_1_0_1","device_lists":{"changed":["@user-1:hs1"]}} ``` What this does: If a `PerformDeviceCreation` request is coming from registering an account, it does **not** send device list updates, as they are merely useful (no joined rooms, no one to inform) . In all other cases, the behavior is unchanged and device list updates are sent as usual.
966 lines
34 KiB
Go
966 lines
34 KiB
Go
// Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/matrix-org/dendrite/syncapi/synctypes"
|
|
"github.com/matrix-org/dendrite/userapi/types"
|
|
"github.com/matrix-org/gomatrixserverlib"
|
|
"github.com/matrix-org/gomatrixserverlib/fclient"
|
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
|
|
|
clientapi "github.com/matrix-org/dendrite/clientapi/api"
|
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
|
"github.com/matrix-org/dendrite/internal/pushrules"
|
|
)
|
|
|
|
// UserInternalAPI is the internal API for information about users and devices.
|
|
type UserInternalAPI interface {
|
|
SyncUserAPI
|
|
ClientUserAPI
|
|
FederationUserAPI
|
|
|
|
QuerySearchProfilesAPI // used by p2p demos
|
|
QueryAccountByLocalpart(ctx context.Context, req *QueryAccountByLocalpartRequest, res *QueryAccountByLocalpartResponse) (err error)
|
|
}
|
|
|
|
// api functions required by the appservice api
|
|
type AppserviceUserAPI interface {
|
|
PerformAccountCreation(ctx context.Context, req *PerformAccountCreationRequest, res *PerformAccountCreationResponse) error
|
|
PerformDeviceCreation(ctx context.Context, req *PerformDeviceCreationRequest, res *PerformDeviceCreationResponse) error
|
|
}
|
|
|
|
type RoomserverUserAPI interface {
|
|
QueryAccountData(ctx context.Context, req *QueryAccountDataRequest, res *QueryAccountDataResponse) error
|
|
QueryAccountByLocalpart(ctx context.Context, req *QueryAccountByLocalpartRequest, res *QueryAccountByLocalpartResponse) (err error)
|
|
}
|
|
|
|
// api functions required by the media api
|
|
type MediaUserAPI interface {
|
|
QueryAcccessTokenAPI
|
|
}
|
|
|
|
// api functions required by the federation api
|
|
type FederationUserAPI interface {
|
|
UploadDeviceKeysAPI
|
|
QueryOpenIDToken(ctx context.Context, req *QueryOpenIDTokenRequest, res *QueryOpenIDTokenResponse) error
|
|
QueryProfile(ctx context.Context, userID string) (*authtypes.Profile, error)
|
|
QueryDevices(ctx context.Context, req *QueryDevicesRequest, res *QueryDevicesResponse) error
|
|
QueryKeys(ctx context.Context, req *QueryKeysRequest, res *QueryKeysResponse)
|
|
QuerySignatures(ctx context.Context, req *QuerySignaturesRequest, res *QuerySignaturesResponse)
|
|
QueryDeviceMessages(ctx context.Context, req *QueryDeviceMessagesRequest, res *QueryDeviceMessagesResponse) error
|
|
PerformClaimKeys(ctx context.Context, req *PerformClaimKeysRequest, res *PerformClaimKeysResponse)
|
|
}
|
|
|
|
// api functions required by the sync api
|
|
type SyncUserAPI interface {
|
|
QueryAcccessTokenAPI
|
|
SyncKeyAPI
|
|
QueryAccountData(ctx context.Context, req *QueryAccountDataRequest, res *QueryAccountDataResponse) error
|
|
PerformLastSeenUpdate(ctx context.Context, req *PerformLastSeenUpdateRequest, res *PerformLastSeenUpdateResponse) error
|
|
PerformDeviceUpdate(ctx context.Context, req *PerformDeviceUpdateRequest, res *PerformDeviceUpdateResponse) error
|
|
QueryDevices(ctx context.Context, req *QueryDevicesRequest, res *QueryDevicesResponse) error
|
|
QueryDeviceInfos(ctx context.Context, req *QueryDeviceInfosRequest, res *QueryDeviceInfosResponse) error
|
|
}
|
|
|
|
// api functions required by the client api
|
|
type ClientUserAPI interface {
|
|
QueryAcccessTokenAPI
|
|
LoginTokenInternalAPI
|
|
UserLoginAPI
|
|
ClientKeyAPI
|
|
ProfileAPI
|
|
KeyBackupAPI
|
|
QueryNumericLocalpart(ctx context.Context, req *QueryNumericLocalpartRequest, res *QueryNumericLocalpartResponse) error
|
|
QueryDevices(ctx context.Context, req *QueryDevicesRequest, res *QueryDevicesResponse) error
|
|
QueryAccountData(ctx context.Context, req *QueryAccountDataRequest, res *QueryAccountDataResponse) error
|
|
QueryPushers(ctx context.Context, req *QueryPushersRequest, res *QueryPushersResponse) error
|
|
QueryPushRules(ctx context.Context, userID string) (*pushrules.AccountRuleSets, error)
|
|
QueryAccountAvailability(ctx context.Context, req *QueryAccountAvailabilityRequest, res *QueryAccountAvailabilityResponse) error
|
|
PerformAdminCreateRegistrationToken(ctx context.Context, registrationToken *clientapi.RegistrationToken) (bool, error)
|
|
PerformAdminListRegistrationTokens(ctx context.Context, returnAll bool, valid bool) ([]clientapi.RegistrationToken, error)
|
|
PerformAdminGetRegistrationToken(ctx context.Context, tokenString string) (*clientapi.RegistrationToken, error)
|
|
PerformAdminDeleteRegistrationToken(ctx context.Context, tokenString string) error
|
|
PerformAdminUpdateRegistrationToken(ctx context.Context, tokenString string, newAttributes map[string]interface{}) (*clientapi.RegistrationToken, error)
|
|
PerformAccountCreation(ctx context.Context, req *PerformAccountCreationRequest, res *PerformAccountCreationResponse) error
|
|
PerformDeviceCreation(ctx context.Context, req *PerformDeviceCreationRequest, res *PerformDeviceCreationResponse) error
|
|
PerformDeviceUpdate(ctx context.Context, req *PerformDeviceUpdateRequest, res *PerformDeviceUpdateResponse) error
|
|
PerformDeviceDeletion(ctx context.Context, req *PerformDeviceDeletionRequest, res *PerformDeviceDeletionResponse) error
|
|
PerformPasswordUpdate(ctx context.Context, req *PerformPasswordUpdateRequest, res *PerformPasswordUpdateResponse) error
|
|
PerformPusherDeletion(ctx context.Context, req *PerformPusherDeletionRequest, res *struct{}) error
|
|
PerformPusherSet(ctx context.Context, req *PerformPusherSetRequest, res *struct{}) error
|
|
PerformPushRulesPut(ctx context.Context, userID string, ruleSets *pushrules.AccountRuleSets) error
|
|
PerformAccountDeactivation(ctx context.Context, req *PerformAccountDeactivationRequest, res *PerformAccountDeactivationResponse) error
|
|
PerformOpenIDTokenCreation(ctx context.Context, req *PerformOpenIDTokenCreationRequest, res *PerformOpenIDTokenCreationResponse) error
|
|
QueryNotifications(ctx context.Context, req *QueryNotificationsRequest, res *QueryNotificationsResponse) error
|
|
InputAccountData(ctx context.Context, req *InputAccountDataRequest, res *InputAccountDataResponse) error
|
|
|
|
QueryThreePIDsForLocalpart(ctx context.Context, req *QueryThreePIDsForLocalpartRequest, res *QueryThreePIDsForLocalpartResponse) error
|
|
QueryLocalpartForThreePID(ctx context.Context, req *QueryLocalpartForThreePIDRequest, res *QueryLocalpartForThreePIDResponse) error
|
|
PerformForgetThreePID(ctx context.Context, req *PerformForgetThreePIDRequest, res *struct{}) error
|
|
PerformSaveThreePIDAssociation(ctx context.Context, req *PerformSaveThreePIDAssociationRequest, res *struct{}) error
|
|
}
|
|
|
|
type KeyBackupAPI interface {
|
|
DeleteKeyBackup(ctx context.Context, userID, version string) (bool, error)
|
|
PerformKeyBackup(ctx context.Context, req *PerformKeyBackupRequest) (string, error)
|
|
QueryKeyBackup(ctx context.Context, req *QueryKeyBackupRequest) (*QueryKeyBackupResponse, error)
|
|
UpdateBackupKeyAuthData(ctx context.Context, req *PerformKeyBackupRequest) (*PerformKeyBackupResponse, error)
|
|
}
|
|
|
|
type ProfileAPI interface {
|
|
QueryProfile(ctx context.Context, userID string) (*authtypes.Profile, error)
|
|
SetAvatarURL(ctx context.Context, localpart string, serverName spec.ServerName, avatarURL string) (*authtypes.Profile, bool, error)
|
|
SetDisplayName(ctx context.Context, localpart string, serverName spec.ServerName, displayName string) (*authtypes.Profile, bool, error)
|
|
}
|
|
|
|
// custom api functions required by pinecone / p2p demos
|
|
type QuerySearchProfilesAPI interface {
|
|
QuerySearchProfiles(ctx context.Context, req *QuerySearchProfilesRequest, res *QuerySearchProfilesResponse) error
|
|
}
|
|
|
|
// common function for creating authenticated endpoints (used in client/media/sync api)
|
|
type QueryAcccessTokenAPI interface {
|
|
QueryAccessToken(ctx context.Context, req *QueryAccessTokenRequest, res *QueryAccessTokenResponse) error
|
|
}
|
|
|
|
type UserLoginAPI interface {
|
|
QueryAccountByPassword(ctx context.Context, req *QueryAccountByPasswordRequest, res *QueryAccountByPasswordResponse) error
|
|
}
|
|
|
|
type PerformKeyBackupRequest struct {
|
|
UserID string
|
|
Version string // optional if modifying a key backup
|
|
AuthData json.RawMessage
|
|
Algorithm string
|
|
|
|
// The keys to upload, if any. If blank, creates/updates/deletes key version metadata only.
|
|
Keys struct {
|
|
Rooms map[string]struct {
|
|
Sessions map[string]KeyBackupSession `json:"sessions"`
|
|
} `json:"rooms"`
|
|
}
|
|
}
|
|
|
|
// KeyBackupData in https://spec.matrix.org/unstable/client-server-api/#get_matrixclientr0room_keyskeysroomidsessionid
|
|
type KeyBackupSession struct {
|
|
FirstMessageIndex int `json:"first_message_index"`
|
|
ForwardedCount int `json:"forwarded_count"`
|
|
IsVerified bool `json:"is_verified"`
|
|
SessionData json.RawMessage `json:"session_data"`
|
|
}
|
|
|
|
func (a *KeyBackupSession) ShouldReplaceRoomKey(newKey *KeyBackupSession) bool {
|
|
// https://spec.matrix.org/unstable/client-server-api/#backup-algorithm-mmegolm_backupv1curve25519-aes-sha2
|
|
// "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 {
|
|
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"
|
|
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"
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Internal KeyBackupData for passing to/from the storage layer
|
|
type InternalKeyBackupSession struct {
|
|
KeyBackupSession
|
|
RoomID string
|
|
SessionID string
|
|
}
|
|
|
|
type PerformKeyBackupResponse struct {
|
|
Exists bool // set to true if the Version exists
|
|
Version string // the newly created version
|
|
|
|
KeyCount int64 // only set if Keys were given in the request
|
|
KeyETag string // only set if Keys were given in the request
|
|
}
|
|
|
|
type QueryKeyBackupRequest struct {
|
|
UserID string
|
|
Version string // the version to query, if blank it means the latest
|
|
|
|
ReturnKeys bool // whether to return keys in the backup response or just the metadata
|
|
KeysForRoomID string // optional string to return keys which belong to this room
|
|
KeysForSessionID string // optional string to return keys which belong to this (room, session)
|
|
}
|
|
|
|
type QueryKeyBackupResponse struct {
|
|
Exists bool
|
|
|
|
Algorithm string `json:"algorithm"`
|
|
AuthData json.RawMessage `json:"auth_data"`
|
|
Count int64 `json:"count"`
|
|
ETag string `json:"etag"`
|
|
Version string `json:"version"`
|
|
|
|
Keys map[string]map[string]KeyBackupSession // the keys if ReturnKeys=true
|
|
}
|
|
|
|
// InputAccountDataRequest is the request for InputAccountData
|
|
type InputAccountDataRequest struct {
|
|
UserID string // required: the user to set account data for
|
|
RoomID string // optional: the room to associate the account data with
|
|
DataType string // required: the data type of the data
|
|
AccountData json.RawMessage // required: the message content
|
|
}
|
|
|
|
// InputAccountDataResponse is the response for InputAccountData
|
|
type InputAccountDataResponse struct {
|
|
}
|
|
|
|
type PerformDeviceUpdateRequest struct {
|
|
RequestingUserID string
|
|
DeviceID string
|
|
DisplayName *string
|
|
}
|
|
type PerformDeviceUpdateResponse struct {
|
|
DeviceExists bool
|
|
}
|
|
|
|
type PerformDeviceDeletionRequest struct {
|
|
UserID string
|
|
// The devices to delete. An empty slice means delete all devices.
|
|
DeviceIDs []string
|
|
// The requesting device ID to exclude from deletion. This is needed
|
|
// so that a password change doesn't cause that client to be logged
|
|
// out. Only specify when DeviceIDs is empty.
|
|
ExceptDeviceID string
|
|
}
|
|
|
|
type PerformDeviceDeletionResponse struct {
|
|
}
|
|
|
|
// QueryDeviceInfosRequest is the request to QueryDeviceInfos
|
|
type QueryDeviceInfosRequest struct {
|
|
DeviceIDs []string
|
|
}
|
|
|
|
// QueryDeviceInfosResponse is the response to QueryDeviceInfos
|
|
type QueryDeviceInfosResponse struct {
|
|
DeviceInfo map[string]struct {
|
|
DisplayName string
|
|
UserID string
|
|
}
|
|
}
|
|
|
|
// QueryAccessTokenRequest is the request for QueryAccessToken
|
|
type QueryAccessTokenRequest struct {
|
|
AccessToken string
|
|
// optional user ID, valid only if the token is an appservice.
|
|
// https://matrix.org/docs/spec/application_service/r0.1.2#using-sync-and-events
|
|
AppServiceUserID string
|
|
}
|
|
|
|
// QueryAccessTokenResponse is the response for QueryAccessToken
|
|
type QueryAccessTokenResponse struct {
|
|
Device *Device
|
|
Err string // e.g ErrorForbidden
|
|
}
|
|
|
|
// QueryAccountDataRequest is the request for QueryAccountData
|
|
type QueryAccountDataRequest struct {
|
|
UserID string // required: the user to get account data for.
|
|
RoomID string // optional: the room ID, or global account data if not specified.
|
|
DataType string // optional: the data type, or all types if not specified.
|
|
}
|
|
|
|
// QueryAccountDataResponse is the response for QueryAccountData
|
|
type QueryAccountDataResponse struct {
|
|
GlobalAccountData map[string]json.RawMessage // type -> data
|
|
RoomAccountData map[string]map[string]json.RawMessage // room -> type -> data
|
|
}
|
|
|
|
// QueryDevicesRequest is the request for QueryDevices
|
|
type QueryDevicesRequest struct {
|
|
UserID string
|
|
}
|
|
|
|
// QueryDevicesResponse is the response for QueryDevices
|
|
type QueryDevicesResponse struct {
|
|
UserExists bool
|
|
Devices []Device
|
|
}
|
|
|
|
// QuerySearchProfilesRequest is the request for QueryProfile
|
|
type QuerySearchProfilesRequest struct {
|
|
// The search string to match
|
|
SearchString string
|
|
// How many results to return
|
|
Limit int
|
|
}
|
|
|
|
// QuerySearchProfilesResponse is the response for QuerySearchProfilesRequest
|
|
type QuerySearchProfilesResponse struct {
|
|
// Profiles matching the search
|
|
Profiles []authtypes.Profile
|
|
}
|
|
|
|
// PerformAccountCreationRequest is the request for PerformAccountCreation
|
|
type PerformAccountCreationRequest struct {
|
|
AccountType AccountType // Required: whether this is a guest or user account
|
|
Localpart string // Required: The localpart for this account. Ignored if account type is guest.
|
|
ServerName spec.ServerName // optional: if not specified, default server name used instead
|
|
|
|
AppServiceID string // optional: the application service ID (not user ID) creating this account, if any.
|
|
Password string // optional: if missing then this account will be a passwordless account
|
|
OnConflict Conflict
|
|
}
|
|
|
|
// PerformAccountCreationResponse is the response for PerformAccountCreation
|
|
type PerformAccountCreationResponse struct {
|
|
AccountCreated bool
|
|
Account *Account
|
|
}
|
|
|
|
// PerformAccountCreationRequest is the request for PerformAccountCreation
|
|
type PerformPasswordUpdateRequest struct {
|
|
Localpart string // Required: The localpart for this account.
|
|
ServerName spec.ServerName // Required: The domain for this account.
|
|
Password string // Required: The new password to set.
|
|
LogoutDevices bool // Optional: Whether to log out all user devices.
|
|
}
|
|
|
|
// PerformAccountCreationResponse is the response for PerformAccountCreation
|
|
type PerformPasswordUpdateResponse struct {
|
|
PasswordUpdated bool
|
|
Account *Account
|
|
}
|
|
|
|
// PerformLastSeenUpdateRequest is the request for PerformLastSeenUpdate.
|
|
type PerformLastSeenUpdateRequest struct {
|
|
UserID string
|
|
DeviceID string
|
|
RemoteAddr string
|
|
UserAgent string
|
|
}
|
|
|
|
// PerformLastSeenUpdateResponse is the response for PerformLastSeenUpdate.
|
|
type PerformLastSeenUpdateResponse struct {
|
|
}
|
|
|
|
// PerformDeviceCreationRequest is the request for PerformDeviceCreation
|
|
type PerformDeviceCreationRequest struct {
|
|
Localpart string
|
|
ServerName spec.ServerName // optional: if blank, default server name used
|
|
AccessToken string // optional: if blank one will be made on your behalf
|
|
// optional: if nil an ID is generated for you. If set, replaces any existing device session,
|
|
// which will generate a new access token and invalidate the old one.
|
|
DeviceID *string
|
|
// optional: if nil no display name will be associated with this device.
|
|
DeviceDisplayName *string
|
|
// IP address of this device
|
|
IPAddr string
|
|
// Useragent for this device
|
|
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
|
|
|
|
// FromRegistration determines if this request comes from registering a new account
|
|
// and is in most cases false.
|
|
FromRegistration bool
|
|
}
|
|
|
|
// PerformDeviceCreationResponse is the response for PerformDeviceCreation
|
|
type PerformDeviceCreationResponse struct {
|
|
DeviceCreated bool
|
|
Device *Device
|
|
}
|
|
|
|
// PerformAccountDeactivationRequest is the request for PerformAccountDeactivation
|
|
type PerformAccountDeactivationRequest struct {
|
|
Localpart string
|
|
ServerName spec.ServerName // optional: if blank, default server name used
|
|
}
|
|
|
|
// PerformAccountDeactivationResponse is the response for PerformAccountDeactivation
|
|
type PerformAccountDeactivationResponse struct {
|
|
AccountDeactivated bool
|
|
}
|
|
|
|
// PerformOpenIDTokenCreationRequest is the request for PerformOpenIDTokenCreation
|
|
type PerformOpenIDTokenCreationRequest struct {
|
|
UserID string
|
|
}
|
|
|
|
// PerformOpenIDTokenCreationResponse is the response for PerformOpenIDTokenCreation
|
|
type PerformOpenIDTokenCreationResponse struct {
|
|
Token OpenIDToken
|
|
}
|
|
|
|
// QueryOpenIDTokenRequest is the request for QueryOpenIDToken
|
|
type QueryOpenIDTokenRequest struct {
|
|
Token string
|
|
}
|
|
|
|
// QueryOpenIDTokenResponse is the response for QueryOpenIDToken
|
|
type QueryOpenIDTokenResponse struct {
|
|
Sub string // The Matrix User ID that generated the token
|
|
ExpiresAtMS int64
|
|
}
|
|
|
|
// Device represents a client's device (mobile, web, etc)
|
|
type Device struct {
|
|
ID string
|
|
UserID string
|
|
// The access_token granted to this device.
|
|
// This uniquely identifies the device from all other devices and clients.
|
|
AccessToken string
|
|
// The unique ID of the session identified by the access token.
|
|
// Can be used as a secure substitution in places where data needs to be
|
|
// associated with access tokens.
|
|
SessionID int64
|
|
DisplayName string
|
|
LastSeenTS int64
|
|
LastSeenIP string
|
|
UserAgent string
|
|
// If the device is for an appservice user,
|
|
// this is the appservice ID.
|
|
AppserviceID string
|
|
AccountType AccountType
|
|
}
|
|
|
|
func (d *Device) UserDomain() spec.ServerName {
|
|
_, domain, err := gomatrixserverlib.SplitID('@', d.UserID)
|
|
if err != nil {
|
|
// This really is catastrophic because it means that someone
|
|
// managed to forge a malformed user ID for a device during
|
|
// login.
|
|
// TODO: Is there a better way to deal with this than panic?
|
|
panic(err)
|
|
}
|
|
return domain
|
|
}
|
|
|
|
// Account represents a Matrix account on this home server.
|
|
type Account struct {
|
|
UserID string
|
|
Localpart string
|
|
ServerName spec.ServerName
|
|
AppServiceID string
|
|
AccountType AccountType
|
|
// TODO: Associations (e.g. with application services)
|
|
}
|
|
|
|
// OpenIDToken represents an OpenID token
|
|
type OpenIDToken struct {
|
|
Token string
|
|
UserID string
|
|
ExpiresAtMS int64
|
|
}
|
|
|
|
// OpenIDTokenInfo represents the attributes associated with an issued OpenID token
|
|
type OpenIDTokenAttributes struct {
|
|
UserID string
|
|
ExpiresAtMS int64
|
|
}
|
|
|
|
// UserInfo is for returning information about the user an OpenID token was issued for
|
|
type UserInfo struct {
|
|
Sub string // The Matrix user's ID who generated the token
|
|
}
|
|
|
|
// ErrorForbidden is an error indicating that the supplied access token is forbidden
|
|
type ErrorForbidden struct {
|
|
Message string
|
|
}
|
|
|
|
func (e *ErrorForbidden) Error() string {
|
|
return "Forbidden: " + e.Message
|
|
}
|
|
|
|
// ErrorConflict is an error indicating that there was a conflict which resulted in the request being aborted.
|
|
type ErrorConflict struct {
|
|
Message string
|
|
}
|
|
|
|
func (e *ErrorConflict) Error() string {
|
|
return "Conflict: " + e.Message
|
|
}
|
|
|
|
// Conflict is an enum representing what to do when encountering conflicting when creating profiles/devices
|
|
type Conflict int
|
|
|
|
// AccountType is an enum representing the kind of account
|
|
type AccountType int
|
|
|
|
const (
|
|
// ConflictUpdate will update matching records returning no error
|
|
ConflictUpdate Conflict = 1
|
|
// ConflictAbort will reject the request with ErrorConflict
|
|
ConflictAbort Conflict = 2
|
|
|
|
// AccountTypeUser indicates this is a user account
|
|
AccountTypeUser AccountType = 1
|
|
// AccountTypeGuest indicates this is a guest account
|
|
AccountTypeGuest AccountType = 2
|
|
// AccountTypeAdmin indicates this is an admin account
|
|
AccountTypeAdmin AccountType = 3
|
|
// AccountTypeAppService indicates this is an appservice account
|
|
AccountTypeAppService AccountType = 4
|
|
)
|
|
|
|
type QueryPushersRequest struct {
|
|
Localpart string
|
|
ServerName spec.ServerName
|
|
}
|
|
|
|
type QueryPushersResponse struct {
|
|
Pushers []Pusher `json:"pushers"`
|
|
}
|
|
|
|
type PerformPusherSetRequest struct {
|
|
Pusher // Anonymous field because that's how clientapi unmarshals it.
|
|
Localpart string
|
|
ServerName spec.ServerName
|
|
Append bool `json:"append"`
|
|
}
|
|
|
|
type PerformPusherDeletionRequest struct {
|
|
Localpart string
|
|
ServerName spec.ServerName
|
|
SessionID int64
|
|
}
|
|
|
|
// Pusher represents a push notification subscriber
|
|
type Pusher struct {
|
|
SessionID int64 `json:"session_id,omitempty"`
|
|
PushKey string `json:"pushkey"`
|
|
PushKeyTS int64 `json:"pushkey_ts,omitempty"`
|
|
Kind PusherKind `json:"kind"`
|
|
AppID string `json:"app_id"`
|
|
AppDisplayName string `json:"app_display_name"`
|
|
DeviceDisplayName string `json:"device_display_name"`
|
|
ProfileTag string `json:"profile_tag"`
|
|
Language string `json:"lang"`
|
|
Data map[string]interface{} `json:"data"`
|
|
}
|
|
|
|
type PusherKind string
|
|
|
|
const (
|
|
EmailKind PusherKind = "email"
|
|
HTTPKind PusherKind = "http"
|
|
)
|
|
|
|
type QueryNotificationsRequest struct {
|
|
Localpart string `json:"localpart"` // Required.
|
|
ServerName spec.ServerName `json:"server_name"` // Required.
|
|
From string `json:"from,omitempty"`
|
|
Limit int `json:"limit,omitempty"`
|
|
Only string `json:"only,omitempty"`
|
|
}
|
|
|
|
type QueryNotificationsResponse struct {
|
|
NextToken string `json:"next_token"`
|
|
Notifications []*Notification `json:"notifications"` // Required.
|
|
}
|
|
|
|
type Notification struct {
|
|
Actions []*pushrules.Action `json:"actions"` // Required.
|
|
Event synctypes.ClientEvent `json:"event"` // Required.
|
|
ProfileTag string `json:"profile_tag"` // Required by Sytest, but actually optional.
|
|
Read bool `json:"read"` // Required.
|
|
RoomID string `json:"room_id"` // Required.
|
|
TS spec.Timestamp `json:"ts"` // Required.
|
|
}
|
|
|
|
type QueryNumericLocalpartRequest struct {
|
|
ServerName spec.ServerName
|
|
}
|
|
|
|
type QueryNumericLocalpartResponse struct {
|
|
ID int64
|
|
}
|
|
|
|
type QueryAccountAvailabilityRequest struct {
|
|
Localpart string
|
|
ServerName spec.ServerName
|
|
}
|
|
|
|
type QueryAccountAvailabilityResponse struct {
|
|
Available bool
|
|
}
|
|
|
|
type QueryAccountByPasswordRequest struct {
|
|
Localpart string
|
|
ServerName spec.ServerName
|
|
PlaintextPassword string
|
|
}
|
|
|
|
type QueryAccountByPasswordResponse struct {
|
|
Account *Account
|
|
Exists bool
|
|
}
|
|
|
|
type QueryLocalpartForThreePIDRequest struct {
|
|
ThreePID, Medium string
|
|
}
|
|
|
|
type QueryLocalpartForThreePIDResponse struct {
|
|
Localpart string
|
|
ServerName spec.ServerName
|
|
}
|
|
|
|
type QueryThreePIDsForLocalpartRequest struct {
|
|
Localpart string
|
|
ServerName spec.ServerName
|
|
}
|
|
|
|
type QueryThreePIDsForLocalpartResponse struct {
|
|
ThreePIDs []authtypes.ThreePID
|
|
}
|
|
|
|
type PerformForgetThreePIDRequest QueryLocalpartForThreePIDRequest
|
|
|
|
type PerformSaveThreePIDAssociationRequest struct {
|
|
ThreePID string
|
|
Localpart string
|
|
ServerName spec.ServerName
|
|
Medium string
|
|
}
|
|
|
|
type QueryAccountByLocalpartRequest struct {
|
|
Localpart string
|
|
ServerName spec.ServerName
|
|
}
|
|
|
|
type QueryAccountByLocalpartResponse struct {
|
|
Account *Account
|
|
}
|
|
|
|
// API functions required by the clientapi
|
|
type ClientKeyAPI interface {
|
|
UploadDeviceKeysAPI
|
|
QueryKeys(ctx context.Context, req *QueryKeysRequest, res *QueryKeysResponse)
|
|
PerformUploadKeys(ctx context.Context, req *PerformUploadKeysRequest, res *PerformUploadKeysResponse) error
|
|
|
|
PerformUploadDeviceSignatures(ctx context.Context, req *PerformUploadDeviceSignaturesRequest, res *PerformUploadDeviceSignaturesResponse)
|
|
// PerformClaimKeys claims one-time keys for use in pre-key messages
|
|
PerformClaimKeys(ctx context.Context, req *PerformClaimKeysRequest, res *PerformClaimKeysResponse)
|
|
PerformMarkAsStaleIfNeeded(ctx context.Context, req *PerformMarkAsStaleRequest, res *struct{}) error
|
|
}
|
|
|
|
type UploadDeviceKeysAPI interface {
|
|
PerformUploadDeviceKeys(ctx context.Context, req *PerformUploadDeviceKeysRequest, res *PerformUploadDeviceKeysResponse)
|
|
}
|
|
|
|
// API functions required by the syncapi
|
|
type SyncKeyAPI interface {
|
|
QueryKeyChanges(ctx context.Context, req *QueryKeyChangesRequest, res *QueryKeyChangesResponse) error
|
|
QueryOneTimeKeys(ctx context.Context, req *QueryOneTimeKeysRequest, res *QueryOneTimeKeysResponse) error
|
|
PerformMarkAsStaleIfNeeded(ctx context.Context, req *PerformMarkAsStaleRequest, res *struct{}) error
|
|
}
|
|
|
|
type FederationKeyAPI interface {
|
|
UploadDeviceKeysAPI
|
|
QueryKeys(ctx context.Context, req *QueryKeysRequest, res *QueryKeysResponse)
|
|
QuerySignatures(ctx context.Context, req *QuerySignaturesRequest, res *QuerySignaturesResponse)
|
|
QueryDeviceMessages(ctx context.Context, req *QueryDeviceMessagesRequest, res *QueryDeviceMessagesResponse) error
|
|
PerformClaimKeys(ctx context.Context, req *PerformClaimKeysRequest, res *PerformClaimKeysResponse)
|
|
}
|
|
|
|
// KeyError is returned if there was a problem performing/querying the server
|
|
type KeyError struct {
|
|
Err string `json:"error"`
|
|
IsInvalidSignature bool `json:"is_invalid_signature,omitempty"` // M_INVALID_SIGNATURE
|
|
IsMissingParam bool `json:"is_missing_param,omitempty"` // M_MISSING_PARAM
|
|
IsInvalidParam bool `json:"is_invalid_param,omitempty"` // M_INVALID_PARAM
|
|
}
|
|
|
|
func (k *KeyError) Error() string {
|
|
return k.Err
|
|
}
|
|
|
|
type DeviceMessageType int
|
|
|
|
const (
|
|
TypeDeviceKeyUpdate DeviceMessageType = iota
|
|
TypeCrossSigningUpdate
|
|
)
|
|
|
|
// DeviceMessage represents the message produced into Kafka by the key server.
|
|
type DeviceMessage struct {
|
|
Type DeviceMessageType `json:"Type,omitempty"`
|
|
*DeviceKeys `json:"DeviceKeys,omitempty"`
|
|
*OutputCrossSigningKeyUpdate `json:"CrossSigningKeyUpdate,omitempty"`
|
|
// A monotonically increasing number which represents device changes for this user.
|
|
StreamID int64
|
|
DeviceChangeID int64
|
|
}
|
|
|
|
// OutputCrossSigningKeyUpdate is an entry in the signing key update output kafka log
|
|
type OutputCrossSigningKeyUpdate struct {
|
|
CrossSigningKeyUpdate `json:"signing_keys"`
|
|
}
|
|
|
|
type CrossSigningKeyUpdate struct {
|
|
MasterKey *fclient.CrossSigningKey `json:"master_key,omitempty"`
|
|
SelfSigningKey *fclient.CrossSigningKey `json:"self_signing_key,omitempty"`
|
|
UserID string `json:"user_id"`
|
|
}
|
|
|
|
// DeviceKeysEqual returns true if the device keys updates contain the
|
|
// same display name and key JSON. This will return false if either of
|
|
// the updates is not a device keys update, or if the user ID/device ID
|
|
// differ between the two.
|
|
func (m1 *DeviceMessage) DeviceKeysEqual(m2 *DeviceMessage) bool {
|
|
if m1.DeviceKeys == nil || m2.DeviceKeys == nil {
|
|
return false
|
|
}
|
|
if m1.UserID != m2.UserID || m1.DeviceID != m2.DeviceID {
|
|
return false
|
|
}
|
|
if m1.DisplayName != m2.DisplayName {
|
|
return false // different display names
|
|
}
|
|
if len(m1.KeyJSON) == 0 || len(m2.KeyJSON) == 0 {
|
|
return false // either is empty
|
|
}
|
|
return bytes.Equal(m1.KeyJSON, m2.KeyJSON)
|
|
}
|
|
|
|
// DeviceKeys represents a set of device keys for a single device
|
|
// https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-keys-upload
|
|
type DeviceKeys struct {
|
|
// The user who owns this device
|
|
UserID string
|
|
// The device ID of this device
|
|
DeviceID string
|
|
// The device display name
|
|
DisplayName string
|
|
// The raw device key JSON
|
|
KeyJSON []byte
|
|
}
|
|
|
|
// WithStreamID returns a copy of this device message with the given stream ID
|
|
func (k *DeviceKeys) WithStreamID(streamID int64) DeviceMessage {
|
|
return DeviceMessage{
|
|
DeviceKeys: k,
|
|
StreamID: streamID,
|
|
}
|
|
}
|
|
|
|
// OneTimeKeys represents a set of one-time keys for a single device
|
|
// https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-keys-upload
|
|
type OneTimeKeys struct {
|
|
// The user who owns this device
|
|
UserID string
|
|
// The device ID of this device
|
|
DeviceID string
|
|
// A map of algorithm:key_id => key JSON
|
|
KeyJSON map[string]json.RawMessage
|
|
}
|
|
|
|
// Split a key in KeyJSON into algorithm and key ID
|
|
func (k *OneTimeKeys) Split(keyIDWithAlgo string) (algo string, keyID string) {
|
|
segments := strings.Split(keyIDWithAlgo, ":")
|
|
return segments[0], segments[1]
|
|
}
|
|
|
|
// OneTimeKeysCount represents the counts of one-time keys for a single device
|
|
type OneTimeKeysCount struct {
|
|
// The user who owns this device
|
|
UserID string
|
|
// The device ID of this device
|
|
DeviceID string
|
|
// algorithm to count e.g:
|
|
// {
|
|
// "curve25519": 10,
|
|
// "signed_curve25519": 20
|
|
// }
|
|
KeyCount map[string]int
|
|
}
|
|
|
|
// PerformUploadKeysRequest is the request to PerformUploadKeys
|
|
type PerformUploadKeysRequest struct {
|
|
UserID string // Required - User performing the request
|
|
DeviceID string // Optional - Device performing the request, for fetching OTK count
|
|
DeviceKeys []DeviceKeys
|
|
OneTimeKeys []OneTimeKeys
|
|
// OnlyDisplayNameUpdates should be `true` if ALL the DeviceKeys are present to update
|
|
// the display name for their respective device, and NOT to modify the keys. The key
|
|
// itself doesn't change but it's easier to pretend upload new keys and reuse the same code paths.
|
|
// Without this flag, requests to modify device display names would delete device keys.
|
|
OnlyDisplayNameUpdates bool
|
|
|
|
// FromRegistration is set if this key upload comes right after creating an account
|
|
// and determines if we need to inform downstream components.
|
|
FromRegistration bool
|
|
}
|
|
|
|
// PerformUploadKeysResponse is the response to PerformUploadKeys
|
|
type PerformUploadKeysResponse struct {
|
|
// A fatal error when processing e.g database failures
|
|
Error *KeyError
|
|
// A map of user_id -> device_id -> Error for tracking failures.
|
|
KeyErrors map[string]map[string]*KeyError
|
|
OneTimeKeyCounts []OneTimeKeysCount
|
|
}
|
|
|
|
// PerformDeleteKeysRequest asks the keyserver to forget about certain
|
|
// keys, and signatures related to those keys.
|
|
type PerformDeleteKeysRequest struct {
|
|
UserID string
|
|
KeyIDs []gomatrixserverlib.KeyID
|
|
}
|
|
|
|
// PerformDeleteKeysResponse is the response to PerformDeleteKeysRequest.
|
|
type PerformDeleteKeysResponse struct {
|
|
Error *KeyError
|
|
}
|
|
|
|
// KeyError sets a key error field on KeyErrors
|
|
func (r *PerformUploadKeysResponse) KeyError(userID, deviceID string, err *KeyError) {
|
|
if r.KeyErrors[userID] == nil {
|
|
r.KeyErrors[userID] = make(map[string]*KeyError)
|
|
}
|
|
r.KeyErrors[userID][deviceID] = err
|
|
}
|
|
|
|
type PerformClaimKeysRequest struct {
|
|
// Map of user_id to device_id to algorithm name
|
|
OneTimeKeys map[string]map[string]string
|
|
Timeout time.Duration
|
|
}
|
|
|
|
type PerformClaimKeysResponse struct {
|
|
// Map of user_id to device_id to algorithm:key_id to key JSON
|
|
OneTimeKeys map[string]map[string]map[string]json.RawMessage
|
|
// Map of remote server domain to error JSON
|
|
Failures map[string]interface{}
|
|
// Set if there was a fatal error processing this action
|
|
Error *KeyError
|
|
}
|
|
|
|
type PerformUploadDeviceKeysRequest struct {
|
|
fclient.CrossSigningKeys
|
|
// The user that uploaded the key, should be populated by the clientapi.
|
|
UserID string
|
|
}
|
|
|
|
type PerformUploadDeviceKeysResponse struct {
|
|
Error *KeyError
|
|
}
|
|
|
|
type PerformUploadDeviceSignaturesRequest struct {
|
|
Signatures map[string]map[gomatrixserverlib.KeyID]fclient.CrossSigningForKeyOrDevice
|
|
// The user that uploaded the sig, should be populated by the clientapi.
|
|
UserID string
|
|
}
|
|
|
|
type PerformUploadDeviceSignaturesResponse struct {
|
|
Error *KeyError
|
|
}
|
|
|
|
type QueryKeysRequest struct {
|
|
// The user ID asking for the keys, e.g. if from a client API request.
|
|
// Will not be populated if the key request came from federation.
|
|
UserID string
|
|
// Maps user IDs to a list of devices
|
|
UserToDevices map[string][]string
|
|
Timeout time.Duration
|
|
}
|
|
|
|
type QueryKeysResponse struct {
|
|
// Map of remote server domain to error JSON
|
|
Failures map[string]interface{}
|
|
// Map of user_id to device_id to device_key
|
|
DeviceKeys map[string]map[string]json.RawMessage
|
|
// Maps of user_id to cross signing key
|
|
MasterKeys map[string]fclient.CrossSigningKey
|
|
SelfSigningKeys map[string]fclient.CrossSigningKey
|
|
UserSigningKeys map[string]fclient.CrossSigningKey
|
|
// Set if there was a fatal error processing this query
|
|
Error *KeyError
|
|
}
|
|
|
|
type QueryKeyChangesRequest struct {
|
|
// The offset of the last received key event, or sarama.OffsetOldest if this is from the beginning
|
|
Offset int64
|
|
// The inclusive offset where to track key changes up to. Messages with this offset are included in the response.
|
|
// Use types.OffsetNewest if the offset is unknown (then check the response Offset to avoid racing).
|
|
ToOffset int64
|
|
}
|
|
|
|
type QueryKeyChangesResponse struct {
|
|
// The set of users who have had their keys change.
|
|
UserIDs []string
|
|
// The latest offset represented in this response.
|
|
Offset int64
|
|
// Set if there was a problem handling the request.
|
|
Error *KeyError
|
|
}
|
|
|
|
type QueryOneTimeKeysRequest struct {
|
|
// The local user to query OTK counts for
|
|
UserID string
|
|
// The device to query OTK counts for
|
|
DeviceID string
|
|
}
|
|
|
|
type QueryOneTimeKeysResponse struct {
|
|
// OTK key counts, in the extended /sync form described by https://matrix.org/docs/spec/client_server/r0.6.1#id84
|
|
Count OneTimeKeysCount
|
|
Error *KeyError
|
|
}
|
|
|
|
type QueryDeviceMessagesRequest struct {
|
|
UserID string
|
|
}
|
|
|
|
type QueryDeviceMessagesResponse struct {
|
|
// The latest stream ID
|
|
StreamID int64
|
|
Devices []DeviceMessage
|
|
Error *KeyError
|
|
}
|
|
|
|
type QuerySignaturesRequest struct {
|
|
// A map of target user ID -> target key/device IDs to retrieve signatures for
|
|
TargetIDs map[string][]gomatrixserverlib.KeyID `json:"target_ids"`
|
|
}
|
|
|
|
type QuerySignaturesResponse struct {
|
|
// A map of target user ID -> target key/device ID -> origin user ID -> origin key/device ID -> signatures
|
|
Signatures map[string]map[gomatrixserverlib.KeyID]types.CrossSigningSigMap
|
|
// A map of target user ID -> cross-signing master key
|
|
MasterKeys map[string]fclient.CrossSigningKey
|
|
// A map of target user ID -> cross-signing self-signing key
|
|
SelfSigningKeys map[string]fclient.CrossSigningKey
|
|
// A map of target user ID -> cross-signing user-signing key
|
|
UserSigningKeys map[string]fclient.CrossSigningKey
|
|
// The request error, if any
|
|
Error *KeyError
|
|
}
|
|
|
|
type PerformMarkAsStaleRequest struct {
|
|
UserID string
|
|
Domain spec.ServerName
|
|
DeviceID string
|
|
}
|