From 51bf830632ab87d99fe5b7eb8a61f21e91f5beab Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 4 Aug 2016 16:53:22 +0100 Subject: [PATCH] Tweak the auth session API to be more transactional - Rename the path from /configureAuthSession to /requestAuthSession - Add a global getter/setter for the `ServiceDB` : this avoids cyclical deps because now the Realm wants access to the database, and due to the factory pattern it would mean `types.go` would need to import `database`, but `database` is already doing so to invoke the factory function in `schema.go`. - Modify how `AuthSession` is loaded/stored in the database. Now it is just a blunt JSON store for Public fields. It is initialised via a new Realm interface function `AuthSession(userID, realmID)` which is there to return the right `struct` so stuff can be unmarshalled into it. - Add a new Realm interface function `RequestAuthSession` which is invoked when `/requestAuthSession` is hit. It is a direct request/response mapping, a JSON blob goes in as a param, and a JSON blob comes out as the return. The Realm is free to create/load/update/delete `AuthSessions` inside the function. This allows better control over when new sessions are made (or whether to return an existing session). --- src/github.com/matrix-org/go-neb/api.go | 21 +++++---------- .../matrix-org/go-neb/database/db.go | 15 +++++++++++ .../matrix-org/go-neb/database/schema.go | 5 +++- src/github.com/matrix-org/go-neb/goneb.go | 3 ++- .../matrix-org/go-neb/realms/github/github.go | 26 ++++++++++++++++--- .../matrix-org/go-neb/types/types.go | 3 ++- 6 files changed, 51 insertions(+), 22 deletions(-) diff --git a/src/github.com/matrix-org/go-neb/api.go b/src/github.com/matrix-org/go-neb/api.go index 3088c36..cc419b2 100644 --- a/src/github.com/matrix-org/go-neb/api.go +++ b/src/github.com/matrix-org/go-neb/api.go @@ -17,11 +17,11 @@ func (*heartbeatHandler) OnIncomingRequest(req *http.Request) (interface{}, *err return &struct{}{}, nil } -type configureAuthSessionHandler struct { +type requestAuthSessionHandler struct { db *database.ServiceDB } -func (h *configureAuthSessionHandler) OnIncomingRequest(req *http.Request) (interface{}, *errors.HTTPError) { +func (h *requestAuthSessionHandler) OnIncomingRequest(req *http.Request) (interface{}, *errors.HTTPError) { if req.Method != "POST" { return nil, &errors.HTTPError{nil, "Unsupported Method", 405} } @@ -43,21 +43,12 @@ func (h *configureAuthSessionHandler) OnIncomingRequest(req *http.Request) (inte return nil, &errors.HTTPError{err, "Unknown RealmID", 400} } - session := realm.AuthSession(body.UserID, body.Config) - if session == nil { - return nil, &errors.HTTPError{nil, "Failed to create auth session", 500} + response := realm.RequestAuthSession(body.UserID, body.Config) + if response == nil { + return nil, &errors.HTTPError{nil, "Failed to request auth session", 500} } - old, err := h.db.StoreAuthSession(session) - if err != nil { - return nil, &errors.HTTPError{err, "Failed to store auth session", 500} - } - - return &struct { - RealmType string - OldSession types.AuthSession - Session types.AuthSession - }{realm.Type(), old, session}, nil + return response, nil } type configureAuthRealmHandler struct { 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 467dd7a..9ade230 100644 --- a/src/github.com/matrix-org/go-neb/database/db.go +++ b/src/github.com/matrix-org/go-neb/database/db.go @@ -13,6 +13,21 @@ type ServiceDB struct { db *sql.DB } +// A single global instance of the service DB. +// XXX: I can't think of any way of doing this without one without creating +// cyclical dependencies somewhere -- Kegan +var globalServiceDB *ServiceDB + +// SetServiceDB sets the global service DB instance. +func SetServiceDB(db *ServiceDB) { + globalServiceDB = db +} + +// GetServiceDB gets the global service DB instance. +func GetServiceDB() *ServiceDB { + return globalServiceDB +} + // Open a SQL database to use as a ServiceDB. This will automatically create // the necessary database tables if they aren't already present. func Open(databaseType, databaseURL string) (serviceDB *ServiceDB, err error) { diff --git a/src/github.com/matrix-org/go-neb/database/schema.go b/src/github.com/matrix-org/go-neb/database/schema.go index 41afea6..3ec01e6 100644 --- a/src/github.com/matrix-org/go-neb/database/schema.go +++ b/src/github.com/matrix-org/go-neb/database/schema.go @@ -318,10 +318,13 @@ func selectAuthSessionTxn(txn *sql.Tx, realmID, userID string) (types.AuthSessio if err := json.Unmarshal(realmJSON, realm); err != nil { return nil, err } - session := realm.AuthSession(userID, json.RawMessage(sessionJSON)) + session := realm.AuthSession(userID, realmID) if session == nil { return nil, fmt.Errorf("Cannot create session for given realm") } + if err := json.Unmarshal(sessionJSON, session); err != nil { + return nil, err + } return session, nil } diff --git a/src/github.com/matrix-org/go-neb/goneb.go b/src/github.com/matrix-org/go-neb/goneb.go index ae97c1b..ca066a9 100644 --- a/src/github.com/matrix-org/go-neb/goneb.go +++ b/src/github.com/matrix-org/go-neb/goneb.go @@ -23,6 +23,7 @@ func main() { if err != nil { log.Panic(err) } + database.SetServiceDB(db) clients := clients.New(db) if err := clients.Start(); err != nil { @@ -33,7 +34,7 @@ func main() { http.Handle("/admin/configureClient", server.MakeJSONAPI(&configureClientHandler{db: db, clients: clients})) http.Handle("/admin/configureService", server.MakeJSONAPI(&configureServiceHandler{db: db, clients: clients})) http.Handle("/admin/configureAuthRealm", server.MakeJSONAPI(&configureAuthRealmHandler{db: db})) - http.Handle("/admin/configureAuthSession", server.MakeJSONAPI(&configureAuthSessionHandler{db: db})) + http.Handle("/admin/requestAuthSession", server.MakeJSONAPI(&requestAuthSessionHandler{db: db})) wh := &webhookHandler{db: db, clients: clients} http.HandleFunc("/services/hooks/", wh.handle) diff --git a/src/github.com/matrix-org/go-neb/realms/github/github.go b/src/github.com/matrix-org/go-neb/realms/github/github.go index 2bb3fcc..552e059 100644 --- a/src/github.com/matrix-org/go-neb/realms/github/github.go +++ b/src/github.com/matrix-org/go-neb/realms/github/github.go @@ -2,6 +2,8 @@ package realms import ( "encoding/json" + log "github.com/Sirupsen/logrus" + "github.com/matrix-org/go-neb/database" "github.com/matrix-org/go-neb/types" "net/url" ) @@ -14,7 +16,7 @@ type githubRealm struct { } type githubSession struct { - URL string + State string userID string realmID string } @@ -35,18 +37,34 @@ func (r *githubRealm) Type() string { return "github" } -func (r *githubRealm) AuthSession(userID string, config json.RawMessage) types.AuthSession { +func (r *githubRealm) RequestAuthSession(userID string, req json.RawMessage) interface{} { u, _ := url.Parse("https://github.com/login/oauth/authorize") q := u.Query() q.Set("client_id", r.ClientID) q.Set("client_secret", r.ClientSecret) // TODO: state, scope u.RawQuery = q.Encode() - return &githubSession{ - URL: u.String(), + session := &githubSession{ + State: "TODO", userID: userID, realmID: r.ID(), } + _, err := database.GetServiceDB().StoreAuthSession(session) + if err != nil { + log.WithError(err).Print("Failed to store new auth session") + return nil + } + + return &struct { + URL string + }{u.String()} +} + +func (r *githubRealm) AuthSession(userID, realmID string) types.AuthSession { + return &githubSession{ + userID: userID, + realmID: realmID, + } } func init() { diff --git a/src/github.com/matrix-org/go-neb/types/types.go b/src/github.com/matrix-org/go-neb/types/types.go index 97edddc..47c801c 100644 --- a/src/github.com/matrix-org/go-neb/types/types.go +++ b/src/github.com/matrix-org/go-neb/types/types.go @@ -59,7 +59,8 @@ func CreateService(serviceID, serviceType string) Service { type AuthRealm interface { ID() string Type() string - AuthSession(userID string, config json.RawMessage) AuthSession + AuthSession(userID, realmID string) AuthSession + RequestAuthSession(userID string, config json.RawMessage) interface{} } var realmsByType = map[string]func(string) AuthRealm{}