Skip to content
This repository has been archived by the owner on Nov 25, 2024. It is now read-only.

3PID invite exchange over federation #222

Merged
merged 16 commits into from
Sep 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,9 @@ func Setup(
},
))

v1fedmux.Handle("/3pid/onbind", common.MakeFedAPI(
"3pid_onbind", cfg.Matrix.ServerName, keys,
func(httpReq *http.Request, request *gomatrixserverlib.FederationRequest) util.JSONResponse {
return writers.CreateInvitesFrom3PIDInvites(httpReq, query, cfg, producer)
v1fedmux.Handle("/3pid/onbind", common.MakeAPI("3pid_onbind",
func(req *http.Request) util.JSONResponse {
return writers.CreateInvitesFrom3PIDInvites(req, query, cfg, producer, federation)
},
))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package writers

import (
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
Expand All @@ -28,6 +29,8 @@ import (

"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util"

"github.com/Sirupsen/logrus"
)

type invite struct {
Expand All @@ -48,7 +51,7 @@ type invites struct {
// CreateInvitesFrom3PIDInvites implements POST /_matrix/federation/v1/3pid/onbind
func CreateInvitesFrom3PIDInvites(
req *http.Request, queryAPI api.RoomserverQueryAPI, cfg config.Dendrite,
producer *producers.RoomserverProducer,
producer *producers.RoomserverProducer, federation *gomatrixserverlib.FederationClient,
) util.JSONResponse {
var body invites
if reqErr := httputil.UnmarshalJSONRequest(req, &body); reqErr != nil {
Expand All @@ -57,7 +60,7 @@ func CreateInvitesFrom3PIDInvites(

evs := []gomatrixserverlib.Event{}
for _, inv := range body.Invites {
event, err := createInviteFrom3PIDInvite(queryAPI, cfg, inv)
event, err := createInviteFrom3PIDInvite(queryAPI, cfg, inv, federation)
if err != nil {
return httputil.LogThenError(req, err)
}
Expand All @@ -83,6 +86,7 @@ func CreateInvitesFrom3PIDInvites(
// necessary data to do so.
func createInviteFrom3PIDInvite(
queryAPI api.RoomserverQueryAPI, cfg config.Dendrite, inv invite,
federation *gomatrixserverlib.FederationClient,
) (*gomatrixserverlib.Event, error) {
// Build the event
builder := &gomatrixserverlib.EventBuilder{
Expand Down Expand Up @@ -120,11 +124,11 @@ func createInviteFrom3PIDInvite(
}

if !queryRes.RoomExists {
// TODO: Use federation to auth the event
return nil, nil
// Use federation to auth the event
return nil, sendToRemoteServer(inv, federation, cfg, *builder)
}

// Finish building the event
// Auth the event locally
builder.Depth = queryRes.Depth
builder.PrevEvents = queryRes.LatestEvents

Expand Down Expand Up @@ -154,6 +158,38 @@ func createInviteFrom3PIDInvite(
return &event, nil
}

// sendToRemoteServer uses federation to send an invite provided by an identity
// server to a remote server in case the current server isn't in the room the
// invite is for.
// Returns an error if it couldn't get the server names to reach or if all of
// them responded with an error.
func sendToRemoteServer(
inv invite, federation *gomatrixserverlib.FederationClient, cfg config.Dendrite,
builder gomatrixserverlib.EventBuilder,
) (err error) {
remoteServers := make([]gomatrixserverlib.ServerName, 2)
_, remoteServers[0], err = gomatrixserverlib.SplitID('@', inv.Sender)
if err != nil {
return
}
// Fallback to the room's server if the sender's domain is the same as
// the current server's
_, remoteServers[1], err = gomatrixserverlib.SplitID('!', inv.RoomID)
if err != nil {
return
}

for _, server := range remoteServers {
err = federation.ExchangeThirdPartyInvite(server, builder)
if err == nil {
return
}
logrus.WithError(err).Warn("failed to send 3PID invite via %s", server)
}

return errors.New("failed to send 3PID invite via any server")
}

// fillDisplayName looks in a list of auth events for a m.room.third_party_invite
// event with the state key matching a given m.room.member event's content's token.
// If such an event is found, fills the "display_name" attribute of the
Expand Down
2 changes: 1 addition & 1 deletion vendor/manifest
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
{
"importpath": "github.com/matrix-org/gomatrixserverlib",
"repository": "https://github.com/matrix-org/gomatrixserverlib",
"revision": "2e9caead882bcdeb999cf4677cfff47e39d3271e",
"revision": "fe45d482f2280c9f92f09eb6650e7aa3cca051c5",
"branch": "master"
},
{
Expand Down
6 changes: 3 additions & 3 deletions vendor/src/github.com/matrix-org/gomatrixserverlib/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func (fc *Client) LookupUserInfo(matrixServer ServerName, token string) (u UserI
var response *http.Response
response, err = fc.client.Get(url.String())
if response != nil {
defer response.Body.Close()
defer response.Body.Close() // nolint: errcheck
}
if err != nil {
return
Expand Down Expand Up @@ -152,7 +152,7 @@ func (fc *Client) LookupUserInfo(matrixServer ServerName, token string) (u UserI
// return a cached copy of the keys or whether they will need to retrieve a fresh
// copy of the keys.
// Returns the keys or an error if there was a problem talking to the server.
func (fc *Client) LookupServerKeys(
func (fc *Client) LookupServerKeys( // nolint: gocyclo
matrixServer ServerName, keyRequests map[PublicKeyRequest]Timestamp,
) (map[PublicKeyRequest]ServerKeys, error) {
url := url.URL{
Expand Down Expand Up @@ -185,7 +185,7 @@ func (fc *Client) LookupServerKeys(

response, err := fc.client.Post(url.String(), "application/json", bytes.NewBuffer(requestBytes))
if response != nil {
defer response.Body.Close()
defer response.Body.Close() // nolint: errcheck
}
if err != nil {
return nil, err
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"testing"
)

func TestToClientEvent(t *testing.T) {
func TestToClientEvent(t *testing.T) { // nolint: gocyclo
ev, err := NewEventFromTrustedJSON([]byte(`{
"type": "m.room.name",
"state_key": "",
Expand Down
3 changes: 2 additions & 1 deletion vendor/src/github.com/matrix-org/gomatrixserverlib/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ func (e Event) Sign(signingName string, keyID KeyID, privateKey ed25519.PrivateK
return Event{
redacted: e.redacted,
eventJSON: eventJSON,
fields: e.fields,
}
}

Expand Down Expand Up @@ -361,7 +362,7 @@ const (
// Returns an error if the total length of the event JSON is too long.
// Returns an error if the event ID doesn't match the origin of the event.
// https://matrix.org/docs/spec/client_server/r0.2.0.html#size-limits
func (e Event) CheckFields() error {
func (e Event) CheckFields() error { // nolint: gocyclo
if len(e.eventJSON) > maxEventLength {
return fmt.Errorf(
"gomatrixserverlib: event is too long, length %d > maximum %d",
Expand Down
12 changes: 6 additions & 6 deletions vendor/src/github.com/matrix-org/gomatrixserverlib/eventauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func (s StateNeeded) Tuples() (res []StateKeyTuple) {
// AuthEventReferences returns the auth_events references for the StateNeeded. Returns an error if the
// provider returns an error. If an event is missing from the provider but is required in StateNeeded, it
// is skipped over: no error is returned.
func (s StateNeeded) AuthEventReferences(provider AuthEventProvider) (refs []EventReference, err error) {
func (s StateNeeded) AuthEventReferences(provider AuthEventProvider) (refs []EventReference, err error) { // nolint: gocyclo
var e *Event
if s.Create {
if e, err = provider.Create(); err != nil {
Expand Down Expand Up @@ -294,7 +294,7 @@ func (a *AuthEvents) ThirdPartyInvite(stateKey string) (*Event, error) {
func NewAuthEvents(events []*Event) AuthEvents {
a := AuthEvents{make(map[StateKeyTuple]*Event)}
for _, e := range events {
a.AddEvent(e)
a.AddEvent(e) // nolint: errcheck
}
return a
}
Expand Down Expand Up @@ -775,7 +775,7 @@ type membershipAllower struct {

// newMembershipAllower loads the information needed to authenticate the m.room.member event
// from the auth events.
func newMembershipAllower(authEvents AuthEventProvider, event Event) (m membershipAllower, err error) {
func newMembershipAllower(authEvents AuthEventProvider, event Event) (m membershipAllower, err error) { // nolint: gocyclo
stateKey := event.StateKey()
if stateKey == nil {
err = errorf("m.room.member must be a state event")
Expand Down Expand Up @@ -816,7 +816,7 @@ func newMembershipAllower(authEvents AuthEventProvider, event Event) (m membersh
}

// membershipAllowed checks whether the membership event is allowed
func (m *membershipAllower) membershipAllowed(event Event) error {
func (m *membershipAllower) membershipAllowed(event Event) error { // nolint: gocyclo
if m.create.roomID != event.RoomID() {
return errorf("create event has different roomID: %q != %q", event.RoomID(), m.create.roomID)
}
Expand Down Expand Up @@ -896,7 +896,7 @@ func (m *membershipAllower) membershipAllowedFromThirdPartyInvite() error {
}

// membershipAllowedSelf determines if the change made by the user to their own membership is allowed.
func (m *membershipAllower) membershipAllowedSelf() error {
func (m *membershipAllower) membershipAllowedSelf() error { // nolint: gocyclo
if m.newMember.Membership == join {
// A user that is not in the room is allowed to join if the room
// join rules are "public".
Expand Down Expand Up @@ -930,7 +930,7 @@ func (m *membershipAllower) membershipAllowedSelf() error {
}

// membershipAllowedOther determines if the user is allowed to change the membership of another user.
func (m *membershipAllower) membershipAllowedOther() error {
func (m *membershipAllower) membershipAllowedOther() error { // nolint: gocyclo
senderLevel := m.powerLevels.userLevel(m.senderID)
targetLevel := m.powerLevels.userLevel(m.targetID)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ func TestStateNeededForJoin(t *testing.T) {
StateKey: &skey,
Sender: "@u1:a",
}
b.SetContent(memberContent{"join", nil})
if err := b.SetContent(memberContent{"join", nil}); err != nil {
t.Fatal(err)
}
testStateNeededForAuth(t, `[{
"type": "m.room.member",
"state_key": "@u1:a",
Expand All @@ -149,7 +151,9 @@ func TestStateNeededForInvite(t *testing.T) {
StateKey: &skey,
Sender: "@u1:a",
}
b.SetContent(memberContent{"invite", nil})
if err := b.SetContent(memberContent{"invite", nil}); err != nil {
t.Fatal(err)
}
testStateNeededForAuth(t, `[{
"type": "m.room.member",
"state_key": "@u2:b",
Expand All @@ -170,11 +174,13 @@ func TestStateNeededForInvite3PID(t *testing.T) {
Sender: "@u1:a",
}

b.SetContent(memberContent{"invite", &memberThirdPartyInvite{
if err := b.SetContent(memberContent{"invite", &memberThirdPartyInvite{
Signed: memberThirdPartyInviteSigned{
Token: "my_token",
},
}})
}}); err != nil {
t.Fatal(err)
}
testStateNeededForAuth(t, `[{
"type": "m.room.member",
"state_key": "@u2:b",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"crypto/sha256"
"encoding/json"
"fmt"

"golang.org/x/crypto/ed25519"
)

Expand Down Expand Up @@ -98,7 +99,7 @@ func checkEventContentHash(eventJSON []byte) error {

sha256Hash := sha256.Sum256(hashableEventJSON)

if bytes.Compare(sha256Hash[:], []byte(hashes.Sha256)) != 0 {
if !bytes.Equal(sha256Hash[:], []byte(hashes.Sha256)) {
return fmt.Errorf("Invalid Sha256 content hash: %v != %v", sha256Hash[:], []byte(hashes.Sha256))
}

Expand Down Expand Up @@ -187,7 +188,7 @@ func verifyEventSignature(signingName string, keyID KeyID, publicKey ed25519.Pub

// VerifyEventSignatures checks that each event in a list of events has valid
// signatures from the server that sent it.
func VerifyEventSignatures(events []Event, keyRing KeyRing) error {
func VerifyEventSignatures(events []Event, keyRing KeyRing) error { // nolint: gocyclo
var toVerify []VerifyJSONRequest
for _, event := range events {
redactedJSON, err := redactEvent(event.eventJSON)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ package gomatrixserverlib
import (
"bytes"
"encoding/base64"
"golang.org/x/crypto/ed25519"
"testing"

"golang.org/x/crypto/ed25519"
)

func TestVerifyEventSignatureTestVectors(t *testing.T) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package gomatrixserverlib

import (
"encoding/json"
"github.com/matrix-org/gomatrix"
"golang.org/x/crypto/ed25519"
"io/ioutil"
"net/http"
"net/url"

"github.com/matrix-org/gomatrix"
"golang.org/x/crypto/ed25519"
)

// An FederationClient is a matrix federation client that adds
Expand Down Expand Up @@ -42,7 +43,7 @@ func (ac *FederationClient) doRequest(r FederationRequest, resBody interface{})

res, err := ac.client.Do(req)
if res != nil {
defer res.Body.Close()
defer res.Body.Close() // nolint: errcheck
}

if err != nil {
Expand Down Expand Up @@ -76,6 +77,10 @@ func (ac *FederationClient) doRequest(r FederationRequest, resBody interface{})
return err
}

if resBody == nil {
return nil
}

return json.Unmarshal(contents, resBody)
}

Expand Down Expand Up @@ -124,6 +129,22 @@ func (ac *FederationClient) SendJoin(s ServerName, event Event) (res RespSendJoi
return
}

// ExchangeThirdPartyInvite sends the builder of a m.room.member event of
// "invite" membership derived from a response from invites sent by an identity
// server.
// This is used to exchange a m.room.third_party_invite event for a m.room.member
// one in a room the local server isn't a member of.
func (ac *FederationClient) ExchangeThirdPartyInvite(s ServerName, builder EventBuilder) (err error) {
path := "/_matrix/federation/v1/exchange_third_party_invite/" +
url.PathEscape(builder.RoomID)
req := NewFederationRequest("PUT", s, path)
if err = req.SetContent(builder); err != nil {
return
}
err = ac.doRequest(req, nil)
return
}

// LookupState retrieves the room state for a room at an event from a
// remote matrix server as full matrix events.
func (ac *FederationClient) LookupState(s ServerName, roomID, eventID string) (res RespState, err error) {
Expand Down
Loading