mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-12 09:23:09 -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
|
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
|
return *res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -162,7 +162,8 @@ func getMembershipStateKey(
|
||||||
|
|
||||||
func checkAndProcess3PIDInvite(
|
func checkAndProcess3PIDInvite(
|
||||||
req *http.Request, device *authtypes.Device, body *membershipRequestBody,
|
req *http.Request, device *authtypes.Device, body *membershipRequestBody,
|
||||||
roomID string,
|
cfg config.Dendrite, queryAPI api.RoomserverQueryAPI,
|
||||||
|
producer *producers.RoomserverProducer, roomID string,
|
||||||
) *util.JSONResponse {
|
) *util.JSONResponse {
|
||||||
if body.Address == "" && body.IDServer == "" && body.Medium == "" {
|
if body.Address == "" && body.IDServer == "" && body.Medium == "" {
|
||||||
// If none of the 3PID-specific fields are supplied, it's a standard invite
|
// 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 {
|
if err != nil {
|
||||||
resErr := httputil.LogThenError(req, err)
|
resErr := httputil.LogThenError(req, err)
|
||||||
return &resErr
|
return &resErr
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.MXID != "" {
|
if resp.Lookup.MXID == "" {
|
||||||
// Set the Matrix user ID from the body request and let the process
|
event, err := make3PIDInviteEvent(body, resp.StoreInvite, device, roomID, cfg, queryAPI)
|
||||||
// continue to create a "m.room.member" event
|
if err == events.ErrRoomNoExists {
|
||||||
body.UserID = resp.MXID
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type idServerResponses struct {
|
||||||
|
Lookup *idServerLookupResponse
|
||||||
|
StoreInvite *idServerStoreInviteResponse
|
||||||
|
}
|
||||||
|
|
||||||
type idServerLookupResponse struct {
|
type idServerLookupResponse struct {
|
||||||
TS int64 `json:"ts"`
|
TS int64 `json:"ts"`
|
||||||
NotBefore int64 `json:"not_before"`
|
NotBefore int64 `json:"not_before"`
|
||||||
|
|
@ -201,25 +229,36 @@ type idServerLookupResponse struct {
|
||||||
Signatures map[string]map[string]string `json:"signatures"`
|
Signatures map[string]map[string]string `json:"signatures"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func queryIDServer(req *http.Request, body *membershipRequestBody) (res *idServerLookupResponse, token string, err error) {
|
type idServerStoreInviteResponse struct {
|
||||||
res, err = queryIDServerLookup(body)
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if res.Lookup.MXID == "" {
|
||||||
if res.MXID == "" {
|
res.StoreInvite, err = queryIDServerStoreInvite(device, body, roomID)
|
||||||
// TODO: Store the invite and send a 3PID invite event
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get timestamp in milliseconds to compare it
|
// Get timestamp in milliseconds to compare it
|
||||||
now := time.Now().UnixNano() / 1000000
|
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
|
// If the current timestamp isn't in the time frame in which the association
|
||||||
// is known to be valid, re-run the query
|
// 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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -244,7 +283,7 @@ func queryIDServerLookup(body *membershipRequestBody) (res *idServerLookupRespon
|
||||||
return
|
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{}
|
client := http.Client{}
|
||||||
|
|
||||||
data := url.Values{}
|
data := url.Values{}
|
||||||
|
|
@ -260,8 +299,19 @@ func queryIDServerStoreInvite(device *authtypes.Device, body *membershipRequestB
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
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) {
|
func queryIDServerPubKey(body *membershipRequestBody, keyID string) (publicKey []byte, err error) {
|
||||||
|
|
@ -302,3 +352,33 @@ func checkIDServerSignatures(body *membershipRequestBody, res *idServerLookupRes
|
||||||
|
|
||||||
return true, nil
|
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"`
|
// 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
|
// NameContent is the event content for https://matrix.org/docs/spec/client_server/r0.2.0.html#m-room-name
|
||||||
type NameContent struct {
|
type NameContent struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue