Use gomatrixserverlib.StateKeyTuple and helper functions for auth (#44)

* Update gomatrixserverib dep

* Use helper functions when creating rooms

* Use gomatrixserverlib.StateKeyTuple
This commit is contained in:
Kegsay 2017-03-17 16:28:15 +00:00 committed by GitHub
parent 44c63254a7
commit 346f5d6334
9 changed files with 101 additions and 107 deletions

View File

@ -14,7 +14,6 @@ import (
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/clientapi/producers" "github.com/matrix-org/dendrite/clientapi/producers"
"github.com/matrix-org/dendrite/common"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util" "github.com/matrix-org/util"
) )
@ -112,8 +111,6 @@ func createRoom(req *http.Request, cfg config.ClientAPI, roomID string, producer
"roomID": roomID, "roomID": roomID,
}).Info("Creating new room") }).Info("Creating new room")
// Remember events we've built and key off the state tuple so we can look them up easily when filling in auth_events
builtEventMap := make(map[common.StateKeyTuple]*gomatrixserverlib.Event)
var builtEvents []gomatrixserverlib.Event var builtEvents []gomatrixserverlib.Event
// send events into the room in order of: // send events into the room in order of:
@ -165,7 +162,7 @@ func createRoom(req *http.Request, cfg config.ClientAPI, roomID string, producer
if i > 0 { if i > 0 {
builder.PrevEvents = []gomatrixserverlib.EventReference{builtEvents[i-1].EventReference()} builder.PrevEvents = []gomatrixserverlib.EventReference{builtEvents[i-1].EventReference()}
} }
ev, err := buildEvent(&builder, builtEventMap, cfg) ev, err := buildEvent(&builder, &authEvents, cfg)
if err != nil { if err != nil {
return httputil.LogThenError(req, err) return httputil.LogThenError(req, err)
} }
@ -175,7 +172,6 @@ func createRoom(req *http.Request, cfg config.ClientAPI, roomID string, producer
} }
// Add the event to the list of auth events // Add the event to the list of auth events
builtEventMap[common.StateKeyTuple{e.Type, e.StateKey}] = ev
builtEvents = append(builtEvents, *ev) builtEvents = append(builtEvents, *ev)
authEvents.AddEvent(ev) authEvents.AddEvent(ev)
} }
@ -193,14 +189,18 @@ func createRoom(req *http.Request, cfg config.ClientAPI, roomID string, producer
// buildEvent fills out auth_events for the builder then builds the event // buildEvent fills out auth_events for the builder then builds the event
func buildEvent(builder *gomatrixserverlib.EventBuilder, func buildEvent(builder *gomatrixserverlib.EventBuilder,
events map[common.StateKeyTuple]*gomatrixserverlib.Event, provider gomatrixserverlib.AuthEventProvider,
cfg config.ClientAPI) (*gomatrixserverlib.Event, error) { cfg config.ClientAPI) (*gomatrixserverlib.Event, error) {
eventsNeeded, err := gomatrixserverlib.StateNeededForEventBuilder(builder) eventsNeeded, err := gomatrixserverlib.StateNeededForEventBuilder(builder)
if err != nil { if err != nil {
return nil, err return nil, err
} }
builder.AuthEvents = authEventsFromStateNeeded(eventsNeeded, events) refs, err := eventsNeeded.AuthEventReferences(provider)
if err != nil {
return nil, err
}
builder.AuthEvents = refs
eventID := fmt.Sprintf("$%s:%s", util.RandomString(16), cfg.ServerName) eventID := fmt.Sprintf("$%s:%s", util.RandomString(16), cfg.ServerName)
now := time.Now() now := time.Now()
event, err := builder.Build(eventID, now, cfg.ServerName, cfg.KeyID, cfg.PrivateKey) event, err := builder.Build(eventID, now, cfg.ServerName, cfg.KeyID, cfg.PrivateKey)
@ -209,41 +209,3 @@ func buildEvent(builder *gomatrixserverlib.EventBuilder,
} }
return &event, nil return &event, nil
} }
func authEventsFromStateNeeded(eventsNeeded gomatrixserverlib.StateNeeded,
events map[common.StateKeyTuple]*gomatrixserverlib.Event) (authEvents []gomatrixserverlib.EventReference) {
// These events are only "needed" if they exist, so if they don't exist we can safely ignore them.
if eventsNeeded.Create {
ev := events[common.StateKeyTuple{"m.room.create", ""}]
if ev != nil {
authEvents = append(authEvents, ev.EventReference())
}
}
if eventsNeeded.JoinRules {
ev := events[common.StateKeyTuple{"m.room.join_rules", ""}]
if ev != nil {
authEvents = append(authEvents, ev.EventReference())
}
}
if eventsNeeded.PowerLevels {
ev := events[common.StateKeyTuple{"m.room.power_levels", ""}]
if ev != nil {
authEvents = append(authEvents, ev.EventReference())
}
}
for _, userID := range eventsNeeded.Member {
ev := events[common.StateKeyTuple{"m.room.member", userID}]
if ev != nil {
authEvents = append(authEvents, ev.EventReference())
}
}
for _, token := range eventsNeeded.ThirdPartyInvite {
ev := events[common.StateKeyTuple{"m.room.member", token}]
if ev != nil {
authEvents = append(authEvents, ev.EventReference())
}
}
return
}

View File

@ -11,7 +11,6 @@ import (
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/clientapi/producers" "github.com/matrix-org/dendrite/clientapi/producers"
"github.com/matrix-org/dendrite/common"
"github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util" "github.com/matrix-org/util"
@ -48,7 +47,7 @@ func SendEvent(req *http.Request, roomID, eventType, txnID string, stateKey *str
builder.SetContent(r) builder.SetContent(r)
// work out what will be required in order to send this event // work out what will be required in order to send this event
requiredStateEvents, err := stateNeeded(&builder) needed, err := gomatrixserverlib.StateNeededForEventBuilder(&builder)
if err != nil { if err != nil {
return httputil.LogThenError(req, err) return httputil.LogThenError(req, err)
} }
@ -56,7 +55,7 @@ func SendEvent(req *http.Request, roomID, eventType, txnID string, stateKey *str
// Ask the roomserver for information about this room // Ask the roomserver for information about this room
queryReq := api.QueryLatestEventsAndStateRequest{ queryReq := api.QueryLatestEventsAndStateRequest{
RoomID: roomID, RoomID: roomID,
StateToFetch: requiredStateEvents, StateToFetch: needed.Tuples(),
} }
var queryRes api.QueryLatestEventsAndStateResponse var queryRes api.QueryLatestEventsAndStateResponse
if queryErr := queryAPI.QueryLatestEventsAndState(&queryReq, &queryRes); queryErr != nil { if queryErr := queryAPI.QueryLatestEventsAndState(&queryReq, &queryRes); queryErr != nil {
@ -105,26 +104,3 @@ func SendEvent(req *http.Request, roomID, eventType, txnID string, stateKey *str
JSON: sendEventResponse{e.EventID()}, JSON: sendEventResponse{e.EventID()},
} }
} }
func stateNeeded(builder *gomatrixserverlib.EventBuilder) (requiredStateEvents []common.StateKeyTuple, err error) {
authEvents, err := gomatrixserverlib.StateNeededForEventBuilder(builder)
if err != nil {
return
}
if authEvents.Create {
requiredStateEvents = append(requiredStateEvents, common.StateKeyTuple{"m.room.create", ""})
}
if authEvents.JoinRules {
requiredStateEvents = append(requiredStateEvents, common.StateKeyTuple{"m.room.join_rules", ""})
}
if authEvents.PowerLevels {
requiredStateEvents = append(requiredStateEvents, common.StateKeyTuple{"m.room.power_levels", ""})
}
for _, userID := range authEvents.Member {
requiredStateEvents = append(requiredStateEvents, common.StateKeyTuple{"m.room.member", userID})
}
for _, token := range authEvents.ThirdPartyInvite {
requiredStateEvents = append(requiredStateEvents, common.StateKeyTuple{"m.room.third_party_invite", token})
}
return
}

View File

@ -1,13 +1 @@
package common package common
// StateKeyTuple is a pair of an event type and state_key.
// This is typically used as a key in a map.
type StateKeyTuple struct {
// The "type" key of a matrix event.
EventType string
// The "state_key" of a matrix event.
// The empty string is a legitimate value for the "state_key" in matrix
// so take care to initialise this field lest you accidentally request a
// "state_key" with the go default of the empty string.
EventStateKey string
}

View File

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/matrix-org/dendrite/common"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"net/http" "net/http"
) )
@ -15,7 +14,7 @@ type QueryLatestEventsAndStateRequest struct {
RoomID string RoomID string
// The state key tuples to fetch from the room current state. // The state key tuples to fetch from the room current state.
// If this list is empty or nil then no state events are returned. // If this list is empty or nil then no state events are returned.
StateToFetch []common.StateKeyTuple StateToFetch []gomatrixserverlib.StateKeyTuple
} }
// QueryLatestEventsAndStateResponse is a response to QueryLatestEventsAndState // QueryLatestEventsAndStateResponse is a response to QueryLatestEventsAndState

View File

@ -2,7 +2,6 @@ package main
import ( import (
"fmt" "fmt"
"github.com/matrix-org/dendrite/common"
"github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"os" "os"
@ -368,7 +367,7 @@ func main() {
if err := q.QueryLatestEventsAndState( if err := q.QueryLatestEventsAndState(
&api.QueryLatestEventsAndStateRequest{ &api.QueryLatestEventsAndStateRequest{
RoomID: "!HCXfdvrfksxuYnIFiJ:matrix.org", RoomID: "!HCXfdvrfksxuYnIFiJ:matrix.org",
StateToFetch: []common.StateKeyTuple{ StateToFetch: []gomatrixserverlib.StateKeyTuple{
{"m.room.member", "@richvdh:matrix.org"}, {"m.room.member", "@richvdh:matrix.org"},
}, },
}, },

View File

@ -4,8 +4,8 @@ package state
import ( import (
"fmt" "fmt"
"github.com/matrix-org/dendrite/common"
"github.com/matrix-org/dendrite/roomserver/types" "github.com/matrix-org/dendrite/roomserver/types"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util" "github.com/matrix-org/util"
"sort" "sort"
) )
@ -200,12 +200,12 @@ func DifferenceBetweeenStateSnapshots(db RoomStateDatabase, oldStateNID, newStat
// stringTuplesToNumericTuples converts the string state key tuples into numeric IDs // stringTuplesToNumericTuples converts the string state key tuples into numeric IDs
// If there isn't a numeric ID for either the event type or the event state key then the tuple is discarded. // If there isn't a numeric ID for either the event type or the event state key then the tuple is discarded.
// Returns an error if there was a problem talking to the database. // Returns an error if there was a problem talking to the database.
func stringTuplesToNumericTuples(db RoomStateDatabase, stringTuples []common.StateKeyTuple) ([]types.StateKeyTuple, error) { func stringTuplesToNumericTuples(db RoomStateDatabase, stringTuples []gomatrixserverlib.StateKeyTuple) ([]types.StateKeyTuple, error) {
eventTypes := make([]string, len(stringTuples)) eventTypes := make([]string, len(stringTuples))
stateKeys := make([]string, len(stringTuples)) stateKeys := make([]string, len(stringTuples))
for i := range stringTuples { for i := range stringTuples {
eventTypes[i] = stringTuples[i].EventType eventTypes[i] = stringTuples[i].EventType
stateKeys[i] = stringTuples[i].EventStateKey stateKeys[i] = stringTuples[i].StateKey
} }
eventTypes = util.UniqueStrings(eventTypes) eventTypes = util.UniqueStrings(eventTypes)
eventTypeMap, err := db.EventTypeNIDs(eventTypes) eventTypeMap, err := db.EventTypeNIDs(eventTypes)
@ -223,7 +223,7 @@ func stringTuplesToNumericTuples(db RoomStateDatabase, stringTuples []common.Sta
var numericTuple types.StateKeyTuple var numericTuple types.StateKeyTuple
var ok1, ok2 bool var ok1, ok2 bool
numericTuple.EventTypeNID, ok1 = eventTypeMap[stringTuple.EventType] numericTuple.EventTypeNID, ok1 = eventTypeMap[stringTuple.EventType]
numericTuple.EventStateKeyNID, ok2 = stateKeyMap[stringTuple.EventStateKey] numericTuple.EventStateKeyNID, ok2 = stateKeyMap[stringTuple.StateKey]
// Discard the tuple if there wasn't a numeric ID for either the event type or the state key. // Discard the tuple if there wasn't a numeric ID for either the event type or the state key.
if ok1 && ok2 { if ok1 && ok2 {
result = append(result, numericTuple) result = append(result, numericTuple)
@ -239,7 +239,7 @@ func stringTuplesToNumericTuples(db RoomStateDatabase, stringTuples []common.Sta
// This is typically the state before an event or the current state of a room. // This is typically the state before an event or the current state of a room.
// Returns a sorted list of state entries or an error if there was a problem talking to the database. // Returns a sorted list of state entries or an error if there was a problem talking to the database.
func LoadStateAtSnapshotForStringTuples( func LoadStateAtSnapshotForStringTuples(
db RoomStateDatabase, stateNID types.StateSnapshotNID, stateKeyTuples []common.StateKeyTuple, db RoomStateDatabase, stateNID types.StateSnapshotNID, stateKeyTuples []gomatrixserverlib.StateKeyTuple,
) ([]types.StateEntry, error) { ) ([]types.StateEntry, error) {
numericTuples, err := stringTuplesToNumericTuples(db, stateKeyTuples) numericTuples, err := stringTuplesToNumericTuples(db, stateKeyTuples)
if err != nil { if err != nil {

2
vendor/manifest vendored
View File

@ -92,7 +92,7 @@
{ {
"importpath": "github.com/matrix-org/gomatrixserverlib", "importpath": "github.com/matrix-org/gomatrixserverlib",
"repository": "https://github.com/matrix-org/gomatrixserverlib", "repository": "https://github.com/matrix-org/gomatrixserverlib",
"revision": "775bc0b4341689c9fbb573008c3095495738f092", "revision": "e757e4f7f675c7356d4b2953059718f1b4598a08",
"branch": "master" "branch": "master"
}, },
{ {

View File

@ -22,6 +22,18 @@ import (
"time" "time"
) )
// A StateKeyTuple is the combination of an event type and an event state key.
// It is often used as a key in maps.
type StateKeyTuple struct {
// The "type" key of a matrix event.
EventType string
// The "state_key" of a matrix event.
// The empty string is a legitimate value for the "state_key" in matrix
// so take care to initialise this field lest you accidentally request a
// "state_key" with the go default of the empty string.
StateKey string
}
// An EventReference is a reference to a matrix event. // An EventReference is a reference to a matrix event.
type EventReference struct { type EventReference struct {
// The event ID of the event. // The event ID of the event.

View File

@ -44,6 +44,69 @@ type StateNeeded struct {
ThirdPartyInvite []string ThirdPartyInvite []string
} }
// Tuples returns the needed state key tuples for performing auth on an event.
func (s StateNeeded) Tuples() (res []StateKeyTuple) {
if s.Create {
res = append(res, StateKeyTuple{"m.room.create", ""})
}
if s.JoinRules {
res = append(res, StateKeyTuple{"m.room.join_rules", ""})
}
if s.PowerLevels {
res = append(res, StateKeyTuple{"m.room.power_levels", ""})
}
for _, userID := range s.Member {
res = append(res, StateKeyTuple{"m.room.member", userID})
}
for _, token := range s.ThirdPartyInvite {
res = append(res, StateKeyTuple{"m.room.third_party_invite", token})
}
return
}
// 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) {
var e *Event
if s.Create {
if e, err = provider.Create(); err != nil {
return
} else if e != nil {
refs = append(refs, e.EventReference())
}
}
if s.JoinRules {
if e, err = provider.JoinRules(); err != nil {
return
} else if e != nil {
refs = append(refs, e.EventReference())
}
}
if s.PowerLevels {
if e, err = provider.PowerLevels(); err != nil {
return
} else if e != nil {
refs = append(refs, e.EventReference())
}
}
for _, userID := range s.Member {
if e, err = provider.Member(userID); err != nil {
return
} else if e != nil {
refs = append(refs, e.EventReference())
}
}
for _, token := range s.ThirdPartyInvite {
if e, err = provider.ThirdPartyInvite(token); err != nil {
return
} else if e != nil {
refs = append(refs, e.EventReference())
}
}
return
}
// StateNeededForEventBuilder returns the event types and state_keys needed to authenticate the // StateNeededForEventBuilder returns the event types and state_keys needed to authenticate the
// event being built. These events should be put under 'auth_events' for the event being built. // event being built. These events should be put under 'auth_events' for the event being built.
// Returns an error if the state needed could not be calculated with the given builder, e.g // Returns an error if the state needed could not be calculated with the given builder, e.g
@ -175,14 +238,9 @@ type AuthEventProvider interface {
ThirdPartyInvite(stateKey string) (*Event, error) ThirdPartyInvite(stateKey string) (*Event, error)
} }
type stateKeyTuple struct {
Type string
StateKey string
}
// AuthEvents is an implementation of AuthEventProvider backed by a map. // AuthEvents is an implementation of AuthEventProvider backed by a map.
type AuthEvents struct { type AuthEvents struct {
events map[stateKeyTuple]*Event events map[StateKeyTuple]*Event
} }
// AddEvent adds an event to the provider. If an event already existed for the (type, state_key) then // AddEvent adds an event to the provider. If an event already existed for the (type, state_key) then
@ -191,39 +249,39 @@ func (a *AuthEvents) AddEvent(event *Event) error {
if event.StateKey() == nil { if event.StateKey() == nil {
return fmt.Errorf("AddEvent: event %s does not have a state key", event.Type()) return fmt.Errorf("AddEvent: event %s does not have a state key", event.Type())
} }
a.events[stateKeyTuple{event.Type(), *event.StateKey()}] = event a.events[StateKeyTuple{event.Type(), *event.StateKey()}] = event
return nil return nil
} }
// Create implements AuthEventProvider // Create implements AuthEventProvider
func (a *AuthEvents) Create() (*Event, error) { func (a *AuthEvents) Create() (*Event, error) {
return a.events[stateKeyTuple{"m.room.create", ""}], nil return a.events[StateKeyTuple{"m.room.create", ""}], nil
} }
// JoinRules implements AuthEventProvider // JoinRules implements AuthEventProvider
func (a *AuthEvents) JoinRules() (*Event, error) { func (a *AuthEvents) JoinRules() (*Event, error) {
return a.events[stateKeyTuple{"m.room.join_rules", ""}], nil return a.events[StateKeyTuple{"m.room.join_rules", ""}], nil
} }
// PowerLevels implements AuthEventProvider // PowerLevels implements AuthEventProvider
func (a *AuthEvents) PowerLevels() (*Event, error) { func (a *AuthEvents) PowerLevels() (*Event, error) {
return a.events[stateKeyTuple{"m.room.power_levels", ""}], nil return a.events[StateKeyTuple{"m.room.power_levels", ""}], nil
} }
// Member implements AuthEventProvider // Member implements AuthEventProvider
func (a *AuthEvents) Member(stateKey string) (*Event, error) { func (a *AuthEvents) Member(stateKey string) (*Event, error) {
return a.events[stateKeyTuple{"m.room.member", stateKey}], nil return a.events[StateKeyTuple{"m.room.member", stateKey}], nil
} }
// ThirdPartyInvite implements AuthEventProvider // ThirdPartyInvite implements AuthEventProvider
func (a *AuthEvents) ThirdPartyInvite(stateKey string) (*Event, error) { func (a *AuthEvents) ThirdPartyInvite(stateKey string) (*Event, error) {
return a.events[stateKeyTuple{"m.room.third_party_invite", stateKey}], nil return a.events[StateKeyTuple{"m.room.third_party_invite", stateKey}], nil
} }
// NewAuthEvents returns an AuthEventProvider backed by the given events. New events can be added by // NewAuthEvents returns an AuthEventProvider backed by the given events. New events can be added by
// calling AddEvent(). // calling AddEvent().
func NewAuthEvents(events []*Event) AuthEvents { func NewAuthEvents(events []*Event) AuthEvents {
a := AuthEvents{make(map[stateKeyTuple]*Event)} a := AuthEvents{make(map[StateKeyTuple]*Event)}
for _, e := range events { for _, e := range events {
a.AddEvent(e) a.AddEvent(e)
} }