mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-11 17:03:10 -06:00
Generate m.room.third_party_invite if the ID server doesn't know the 3PID
This commit is contained in:
parent
5d8d2d948f
commit
c207c5859e
|
|
@ -58,7 +58,7 @@ func SendMembership(
|
|||
return *reqErr
|
||||
}
|
||||
|
||||
if res := checkAndProcess3PIDInvite(req, device, &body, roomID); res != nil {
|
||||
if res := checkAndProcess3PIDInvite(req, device, &body, cfg, queryAPI, producer, roomID); res != nil {
|
||||
return *res
|
||||
}
|
||||
|
||||
|
|
@ -162,7 +162,8 @@ func getMembershipStateKey(
|
|||
|
||||
func checkAndProcess3PIDInvite(
|
||||
req *http.Request, device *authtypes.Device, body *membershipRequestBody,
|
||||
roomID string,
|
||||
cfg config.Dendrite, queryAPI api.RoomserverQueryAPI,
|
||||
producer *producers.RoomserverProducer, roomID string,
|
||||
) *util.JSONResponse {
|
||||
if body.Address == "" && body.IDServer == "" && body.Medium == "" {
|
||||
// If none of the 3PID-specific fields are supplied, it's a standard invite
|
||||
|
|
@ -177,20 +178,47 @@ func checkAndProcess3PIDInvite(
|
|||
}
|
||||
}
|
||||
|
||||
resp, _, err := queryIDServer(req, body)
|
||||
resp, err := queryIDServer(req, device, body, roomID)
|
||||
if err != nil {
|
||||
resErr := httputil.LogThenError(req, err)
|
||||
return &resErr
|
||||
}
|
||||
|
||||
if resp.MXID != "" {
|
||||
// Set the Matrix user ID from the body request and let the process
|
||||
// continue to create a "m.room.member" event
|
||||
body.UserID = resp.MXID
|
||||
if resp.Lookup.MXID == "" {
|
||||
event, err := make3PIDInviteEvent(body, resp.StoreInvite, device, roomID, cfg, queryAPI)
|
||||
if err == events.ErrRoomNoExists {
|
||||
return &util.JSONResponse{
|
||||
Code: 404,
|
||||
JSON: jsonerror.NotFound(err.Error()),
|
||||
}
|
||||
} else if err != nil {
|
||||
resErr := httputil.LogThenError(req, err)
|
||||
return &resErr
|
||||
}
|
||||
|
||||
if err := producer.SendEvents([]gomatrixserverlib.Event{*event}, cfg.Matrix.ServerName); err != nil {
|
||||
resErr := httputil.LogThenError(req, err)
|
||||
return &resErr
|
||||
}
|
||||
|
||||
return &util.JSONResponse{
|
||||
Code: 200,
|
||||
JSON: struct{}{},
|
||||
}
|
||||
}
|
||||
|
||||
// Set the Matrix user ID from the body request and let the process
|
||||
// continue to create a "m.room.member" event
|
||||
body.UserID = resp.Lookup.MXID
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type idServerResponses struct {
|
||||
Lookup *idServerLookupResponse
|
||||
StoreInvite *idServerStoreInviteResponse
|
||||
}
|
||||
|
||||
type idServerLookupResponse struct {
|
||||
TS int64 `json:"ts"`
|
||||
NotBefore int64 `json:"not_before"`
|
||||
|
|
@ -201,25 +229,36 @@ type idServerLookupResponse struct {
|
|||
Signatures map[string]map[string]string `json:"signatures"`
|
||||
}
|
||||
|
||||
func queryIDServer(req *http.Request, body *membershipRequestBody) (res *idServerLookupResponse, token string, err error) {
|
||||
res, err = queryIDServerLookup(body)
|
||||
type idServerStoreInviteResponse struct {
|
||||
PublicKey string `json:"public_key"`
|
||||
Token string `json:"token"`
|
||||
DisplayName string `json:"display_name"`
|
||||
PublicKeys []common.PublicKey `json:"public_keys"`
|
||||
}
|
||||
|
||||
func queryIDServer(
|
||||
req *http.Request, device *authtypes.Device, body *membershipRequestBody,
|
||||
roomID string,
|
||||
) (res *idServerResponses, err error) {
|
||||
res = new(idServerResponses)
|
||||
res.Lookup, err = queryIDServerLookup(body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if res.MXID == "" {
|
||||
// TODO: Store the invite and send a 3PID invite event
|
||||
if res.Lookup.MXID == "" {
|
||||
res.StoreInvite, err = queryIDServerStoreInvite(device, body, roomID)
|
||||
return
|
||||
}
|
||||
|
||||
// Get timestamp in milliseconds to compare it
|
||||
now := time.Now().UnixNano() / 1000000
|
||||
if res.NotBefore > now || now > res.NotAfter {
|
||||
if res.Lookup.NotBefore > now || now > res.Lookup.NotAfter {
|
||||
// If the current timestamp isn't in the time frame in which the association
|
||||
// is known to be valid, re-run the query
|
||||
return queryIDServer(req, body)
|
||||
return queryIDServer(req, device, body, roomID)
|
||||
}
|
||||
|
||||
ok, err := checkIDServerSignatures(body, res)
|
||||
ok, err := checkIDServerSignatures(body, res.Lookup)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -244,7 +283,7 @@ func queryIDServerLookup(body *membershipRequestBody) (res *idServerLookupRespon
|
|||
return
|
||||
}
|
||||
|
||||
func queryIDServerStoreInvite(device *authtypes.Device, body *membershipRequestBody, roomID string) (*http.Response, error) {
|
||||
func queryIDServerStoreInvite(device *authtypes.Device, body *membershipRequestBody, roomID string) (*idServerStoreInviteResponse, error) {
|
||||
client := http.Client{}
|
||||
|
||||
data := url.Values{}
|
||||
|
|
@ -260,8 +299,19 @@ func queryIDServerStoreInvite(device *authtypes.Device, body *membershipRequestB
|
|||
}
|
||||
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client.Do(req)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
errMsg := fmt.Sprintf("Identity server %s responded with a %d error code", body.IDServer, resp.StatusCode)
|
||||
return nil, errors.New(errMsg)
|
||||
}
|
||||
|
||||
idResp := new(idServerStoreInviteResponse)
|
||||
err = json.NewDecoder(resp.Body).Decode(idResp)
|
||||
return idResp, err
|
||||
}
|
||||
|
||||
func queryIDServerPubKey(body *membershipRequestBody, keyID string) (publicKey []byte, err error) {
|
||||
|
|
@ -302,3 +352,33 @@ func checkIDServerSignatures(body *membershipRequestBody, res *idServerLookupRes
|
|||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func make3PIDInviteEvent(
|
||||
body *membershipRequestBody, res *idServerStoreInviteResponse,
|
||||
device *authtypes.Device, roomID string, cfg config.Dendrite,
|
||||
queryAPI api.RoomserverQueryAPI,
|
||||
) (*gomatrixserverlib.Event, error) {
|
||||
builder := &gomatrixserverlib.EventBuilder{
|
||||
Sender: device.UserID,
|
||||
RoomID: roomID,
|
||||
Type: "m.room.third_party_invite",
|
||||
StateKey: &res.Token,
|
||||
}
|
||||
|
||||
validityURL := fmt.Sprintf("https://%s/_matrix/identity/api/v1/pubkey/isvalid", body.IDServer)
|
||||
content := common.ThirdPartyInviteContent{
|
||||
DisplayName: res.DisplayName,
|
||||
KeyValidityURL: validityURL,
|
||||
PublicKey: res.PublicKey,
|
||||
}
|
||||
|
||||
content.PublicKeys = make([]common.PublicKey, len(res.PublicKeys))
|
||||
copy(content.PublicKeys, res.PublicKeys)
|
||||
|
||||
if err := builder.SetContent(content); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var queryRes *api.QueryLatestEventsAndStateResponse
|
||||
return events.BuildEvent(builder, cfg, queryAPI, queryRes)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,20 @@ type MemberContent struct {
|
|||
// TODO: ThirdPartyInvite string `json:"third_party_invite,omitempty"`
|
||||
}
|
||||
|
||||
// ThirdPartyInviteContent is the content event for https://matrix.org/speculator/spec/HEAD/client_server/unstable.html#m-room-third-party-invite
|
||||
type ThirdPartyInviteContent struct {
|
||||
DisplayName string `json:"display_name"`
|
||||
KeyValidityURL string `json:"key_validity_url"`
|
||||
PublicKey string `json:"public_key"`
|
||||
PublicKeys []PublicKey `json:"public_keys"`
|
||||
}
|
||||
|
||||
// PublicKey is the PublicKeys structure in https://matrix.org/speculator/spec/HEAD/client_server/unstable.html#m-room-third-party-invite
|
||||
type PublicKey struct {
|
||||
KeyValidityURL string `json:"key_validity_url"`
|
||||
PublicKey string `json:"public_key"`
|
||||
}
|
||||
|
||||
// NameContent is the event content for https://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-name
|
||||
type NameContent struct {
|
||||
Name string `json:"name"`
|
||||
|
|
|
|||
Loading…
Reference in a new issue