From 0fce8aea09041851457a30eba1b5493edf711304 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 21 Oct 2016 17:37:41 +0100 Subject: [PATCH] Glue everything together --- src/github.com/matrix-org/go-neb/api/api.go | 26 ++++++++- .../matrix-org/go-neb/database/db.go | 50 +++++++++++++++++ src/github.com/matrix-org/go-neb/goneb.go | 55 ++++++++++++++++--- src/github.com/matrix-org/go-neb/handlers.go | 10 ++-- 4 files changed, 126 insertions(+), 15 deletions(-) diff --git a/src/github.com/matrix-org/go-neb/api/api.go b/src/github.com/matrix-org/go-neb/api/api.go index ed97a70..51c2beb 100644 --- a/src/github.com/matrix-org/go-neb/api/api.go +++ b/src/github.com/matrix-org/go-neb/api/api.go @@ -6,12 +6,14 @@ import ( "net/url" ) +// ConfigureAuthRealmRequest is a request to /configureAuthRealm type ConfigureAuthRealmRequest struct { ID string Type string Config json.RawMessage } +// ConfigureServiceRequest is a request to /configureService type ConfigureServiceRequest struct { ID string Type string @@ -19,7 +21,8 @@ type ConfigureServiceRequest struct { Config json.RawMessage } -// A ClientConfig is the configuration for a matrix client for a bot to use. +// A ClientConfig is the configuration for a matrix client for a bot to use. It is +// a request to /configureClient type ClientConfig struct { UserID string // The matrix UserId to connect with. HomeserverURL string // A URL with the host and port of the matrix server. E.g. https://matrix.org:8448 @@ -45,6 +48,27 @@ type ConfigFile struct { Sessions []SessionRequest } +func (c *ConfigureServiceRequest) Check() error { + if c.ID == "" || c.Type == "" || c.UserID == "" || c.Config == nil { + return errors.New(`Must supply an "ID", a "Type", a "UserID" and a "Config"`) + } + return nil +} + +func (c *ConfigureAuthRealmRequest) Check() error { + if c.ID == "" || c.Type == "" || c.Config == nil { + return errors.New(`Must supply a "ID", a "Type" and a "Config"`) + } + return nil +} + +func (c *SessionRequest) Check() error { + if c.SessionID == "" || c.UserID == "" || c.RealmID == "" || c.Config == nil { + return errors.New(`Must supply a "SessionID", a "RealmID", a "UserID" and a "Config"`) + } + return nil +} + // Check that the client has the correct fields. func (c *ClientConfig) Check() error { if c.UserID == "" || c.HomeserverURL == "" || c.AccessToken == "" { diff --git a/src/github.com/matrix-org/go-neb/database/db.go b/src/github.com/matrix-org/go-neb/database/db.go index 5b8dad6..db23b6d 100644 --- a/src/github.com/matrix-org/go-neb/database/db.go +++ b/src/github.com/matrix-org/go-neb/database/db.go @@ -2,6 +2,8 @@ package database import ( "database/sql" + "encoding/json" + "fmt" "github.com/matrix-org/go-neb/api" "github.com/matrix-org/go-neb/types" "time" @@ -273,7 +275,55 @@ func (d *ServiceDB) StoreBotOptions(opts types.BotOptions) (oldOpts types.BotOpt return } +// InsertFromConfig inserts entries from the config file into the database. This only really +// makes sense for in-memory databases. func (d *ServiceDB) InsertFromConfig(cfg *api.ConfigFile) error { + // Insert clients + for _, cli := range cfg.Clients { + if _, err := d.StoreMatrixClientConfig(cli); err != nil { + return err + } + } + + // Keep a map of realms for inserting sessions + realms := map[string]types.AuthRealm{} // by realm ID + + // Insert realms + for _, r := range cfg.Realms { + if err := r.Check(); err != nil { + return err + } + realm, err := types.CreateAuthRealm(r.ID, r.Type, r.Config) + if err != nil { + return err + } + if _, err := d.StoreAuthRealm(realm); err != nil { + return err + } + realms[realm.ID()] = realm + } + + // Insert sessions + for _, s := range cfg.Sessions { + if err := s.Check(); err != nil { + return err + } + r := realms[s.RealmID] + if r == nil { + return fmt.Errorf("Session %s specifies an unknown realm ID %s", s.SessionID, s.RealmID) + } + session := r.AuthSession(s.SessionID, s.UserID, s.RealmID) + // dump the raw JSON config directly into the session. This is what + // selectAuthSessionByUserTxn does. + if err := json.Unmarshal(s.Config, session); err != nil { + return err + } + if _, err := d.StoreAuthSession(session); err != nil { + return err + } + } + + // Do not insert services yet, they require more work to set up. return nil } diff --git a/src/github.com/matrix-org/go-neb/goneb.go b/src/github.com/matrix-org/go-neb/goneb.go index 21f5803..10e0b0b 100644 --- a/src/github.com/matrix-org/go-neb/goneb.go +++ b/src/github.com/matrix-org/go-neb/goneb.go @@ -99,6 +99,33 @@ func convertKeysToStrings(iface interface{}) interface{} { return iface // base type like string or number } +func insertServicesFromConfig(clis *clients.Clients, services []api.ConfigureServiceRequest) error { + for i, s := range services { + if err := s.Check(); err != nil { + return fmt.Errorf("config: Service[%d] : %s", i, err) + } + service, err := types.CreateService(s.ID, s.Type, s.UserID, s.Config) + if err != nil { + return fmt.Errorf("config: Service[%d] : %s", i, err) + } + + // Fetch the client for this service and register/poll + c, err := clis.Client(s.UserID) + if err != nil { + return fmt.Errorf("config: Service[%d] : %s", i, err) + } + + if err = service.Register(nil, c); err != nil { + return fmt.Errorf("config: Service[%d] : %s", i, err) + } + if _, err := database.GetServiceDB().StoreService(service); err != nil { + return fmt.Errorf("config: Service[%d] : %s", i, err) + } + service.PostRegister(nil) + } + return nil +} + func main() { bindAddress := os.Getenv("BIND_ADDRESS") databaseType := os.Getenv("DATABASE_TYPE") @@ -136,14 +163,17 @@ func main() { } database.SetServiceDB(db) + var cfg *api.ConfigFile if configYAML != "" { - var cfg *api.ConfigFile if cfg, err = loadFromConfig(db, configYAML); err != nil { log.WithError(err).WithField("config_file", configYAML).Panic("Failed to load config file") } if err := db.InsertFromConfig(cfg); err != nil { log.WithError(err).Panic("Failed to persist config data into in-memory DB") } + log.Info("Inserted ", len(cfg.Clients), " clients") + log.Info("Inserted ", len(cfg.Realms), " realms") + log.Info("Inserted ", len(cfg.Sessions), " sessions") } clients := clients.New(db) @@ -151,20 +181,29 @@ func main() { log.WithError(err).Panic("Failed to start up clients") } + // Handle non-admin paths for normal NEB functioning http.Handle("/metrics", prometheus.Handler()) http.Handle("/test", prometheus.InstrumentHandler("test", server.MakeJSONAPI(&heartbeatHandler{}))) - http.Handle("/admin/getService", prometheus.InstrumentHandler("getService", server.MakeJSONAPI(&getServiceHandler{db: db}))) - http.Handle("/admin/getSession", prometheus.InstrumentHandler("getSession", server.MakeJSONAPI(&getSessionHandler{db: db}))) - http.Handle("/admin/configureClient", prometheus.InstrumentHandler("configureClient", server.MakeJSONAPI(&configureClientHandler{db: db, clients: clients}))) - http.Handle("/admin/configureService", prometheus.InstrumentHandler("configureService", server.MakeJSONAPI(newConfigureServiceHandler(db, clients)))) - http.Handle("/admin/configureAuthRealm", prometheus.InstrumentHandler("configureAuthRealm", server.MakeJSONAPI(&configureAuthRealmHandler{db: db}))) - http.Handle("/admin/requestAuthSession", prometheus.InstrumentHandler("requestAuthSession", server.MakeJSONAPI(&requestAuthSessionHandler{db: db}))) - http.Handle("/admin/removeAuthSession", prometheus.InstrumentHandler("removeAuthSession", server.MakeJSONAPI(&removeAuthSessionHandler{db: db}))) wh := &webhookHandler{db: db, clients: clients} http.HandleFunc("/services/hooks/", prometheus.InstrumentHandlerFunc("webhookHandler", wh.handle)) rh := &realmRedirectHandler{db: db} http.HandleFunc("/realms/redirects/", prometheus.InstrumentHandlerFunc("realmRedirectHandler", rh.handle)) + if configYAML != "" { + if err := insertServicesFromConfig(clients, cfg.Services); err != nil { + log.WithError(err).Panic("Failed to insert services") + } + + log.Info("Inserted ", len(cfg.Services), " services") + } else { + http.Handle("/admin/getService", prometheus.InstrumentHandler("getService", server.MakeJSONAPI(&getServiceHandler{db: db}))) + http.Handle("/admin/getSession", prometheus.InstrumentHandler("getSession", server.MakeJSONAPI(&getSessionHandler{db: db}))) + http.Handle("/admin/configureClient", prometheus.InstrumentHandler("configureClient", server.MakeJSONAPI(&configureClientHandler{db: db, clients: clients}))) + http.Handle("/admin/configureService", prometheus.InstrumentHandler("configureService", server.MakeJSONAPI(newConfigureServiceHandler(db, clients)))) + http.Handle("/admin/configureAuthRealm", prometheus.InstrumentHandler("configureAuthRealm", server.MakeJSONAPI(&configureAuthRealmHandler{db: db}))) + http.Handle("/admin/requestAuthSession", prometheus.InstrumentHandler("requestAuthSession", server.MakeJSONAPI(&requestAuthSessionHandler{db: db}))) + http.Handle("/admin/removeAuthSession", prometheus.InstrumentHandler("removeAuthSession", server.MakeJSONAPI(&removeAuthSessionHandler{db: db}))) + } polling.SetClients(clients) if err := polling.Start(); err != nil { log.WithError(err).Panic("Failed to start polling") diff --git a/src/github.com/matrix-org/go-neb/handlers.go b/src/github.com/matrix-org/go-neb/handlers.go index e89fac5..2cec684 100644 --- a/src/github.com/matrix-org/go-neb/handlers.go +++ b/src/github.com/matrix-org/go-neb/handlers.go @@ -142,8 +142,8 @@ func (h *configureAuthRealmHandler) OnIncomingRequest(req *http.Request) (interf return nil, &errors.HTTPError{err, "Error parsing request JSON", 400} } - if body.ID == "" || body.Type == "" || body.Config == nil { - return nil, &errors.HTTPError{nil, `Must supply a "ID", a "Type" and a "Config"`, 400} + if err := body.Check(); err != nil { + return nil, &errors.HTTPError{err, err.Error(), 400} } realm, err := types.CreateAuthRealm(body.ID, body.Type, body.Config) @@ -336,10 +336,8 @@ func (s *configureServiceHandler) createService(req *http.Request) (types.Servic return nil, &errors.HTTPError{err, "Error parsing request JSON", 400} } - if body.ID == "" || body.Type == "" || body.UserID == "" || body.Config == nil { - return nil, &errors.HTTPError{ - nil, `Must supply an "ID", a "Type", a "UserID" and a "Config"`, 400, - } + if err := body.Check(); err != nil { + return nil, &errors.HTTPError{err, err.Error(), 400} } service, err := types.CreateService(body.ID, body.Type, body.UserID, body.Config)