Browse Source

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).
pull/9/head
Kegan Dougal 9 years ago
parent
commit
51bf830632
  1. 21
      src/github.com/matrix-org/go-neb/api.go
  2. 15
      src/github.com/matrix-org/go-neb/database/db.go
  3. 5
      src/github.com/matrix-org/go-neb/database/schema.go
  4. 3
      src/github.com/matrix-org/go-neb/goneb.go
  5. 26
      src/github.com/matrix-org/go-neb/realms/github/github.go
  6. 3
      src/github.com/matrix-org/go-neb/types/types.go

21
src/github.com/matrix-org/go-neb/api.go

@ -17,11 +17,11 @@ func (*heartbeatHandler) OnIncomingRequest(req *http.Request) (interface{}, *err
return &struct{}{}, nil return &struct{}{}, nil
} }
type configureAuthSessionHandler struct {
type requestAuthSessionHandler struct {
db *database.ServiceDB 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" { if req.Method != "POST" {
return nil, &errors.HTTPError{nil, "Unsupported Method", 405} 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} 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 { type configureAuthRealmHandler struct {

15
src/github.com/matrix-org/go-neb/database/db.go

@ -13,6 +13,21 @@ type ServiceDB struct {
db *sql.DB 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 // Open a SQL database to use as a ServiceDB. This will automatically create
// the necessary database tables if they aren't already present. // the necessary database tables if they aren't already present.
func Open(databaseType, databaseURL string) (serviceDB *ServiceDB, err error) { func Open(databaseType, databaseURL string) (serviceDB *ServiceDB, err error) {

5
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 { if err := json.Unmarshal(realmJSON, realm); err != nil {
return nil, err return nil, err
} }
session := realm.AuthSession(userID, json.RawMessage(sessionJSON))
session := realm.AuthSession(userID, realmID)
if session == nil { if session == nil {
return nil, fmt.Errorf("Cannot create session for given realm") return nil, fmt.Errorf("Cannot create session for given realm")
} }
if err := json.Unmarshal(sessionJSON, session); err != nil {
return nil, err
}
return session, nil return session, nil
} }

3
src/github.com/matrix-org/go-neb/goneb.go

@ -23,6 +23,7 @@ func main() {
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }
database.SetServiceDB(db)
clients := clients.New(db) clients := clients.New(db)
if err := clients.Start(); err != nil { 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/configureClient", server.MakeJSONAPI(&configureClientHandler{db: db, clients: clients}))
http.Handle("/admin/configureService", server.MakeJSONAPI(&configureServiceHandler{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/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} wh := &webhookHandler{db: db, clients: clients}
http.HandleFunc("/services/hooks/", wh.handle) http.HandleFunc("/services/hooks/", wh.handle)

26
src/github.com/matrix-org/go-neb/realms/github/github.go

@ -2,6 +2,8 @@ package realms
import ( import (
"encoding/json" "encoding/json"
log "github.com/Sirupsen/logrus"
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/types" "github.com/matrix-org/go-neb/types"
"net/url" "net/url"
) )
@ -14,7 +16,7 @@ type githubRealm struct {
} }
type githubSession struct { type githubSession struct {
URL string
State string
userID string userID string
realmID string realmID string
} }
@ -35,18 +37,34 @@ func (r *githubRealm) Type() string {
return "github" 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") u, _ := url.Parse("https://github.com/login/oauth/authorize")
q := u.Query() q := u.Query()
q.Set("client_id", r.ClientID) q.Set("client_id", r.ClientID)
q.Set("client_secret", r.ClientSecret) q.Set("client_secret", r.ClientSecret)
// TODO: state, scope // TODO: state, scope
u.RawQuery = q.Encode() u.RawQuery = q.Encode()
return &githubSession{
URL: u.String(),
session := &githubSession{
State: "TODO",
userID: userID, userID: userID,
realmID: r.ID(), 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() { func init() {

3
src/github.com/matrix-org/go-neb/types/types.go

@ -59,7 +59,8 @@ func CreateService(serviceID, serviceType string) Service {
type AuthRealm interface { type AuthRealm interface {
ID() string ID() string
Type() 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{} var realmsByType = map[string]func(string) AuthRealm{}

Loading…
Cancel
Save