From cdee3d1a518348d6763c97b881b4ea53875f463c Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 2 Aug 2016 16:13:01 +0100 Subject: [PATCH 1/4] Add ThirdPartyAuth struct and third_party_auth table with CRU operations --- .../matrix-org/go-neb/database/schema.go | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) 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 420766f..f85a6dd 100644 --- a/src/github.com/matrix-org/go-neb/database/schema.go +++ b/src/github.com/matrix-org/go-neb/database/schema.go @@ -34,6 +34,16 @@ CREATE TABLE IF NOT EXISTS matrix_clients ( time_updated_ms BIGINT NOT NULL, UNIQUE(user_id) ); + +CREATE TABLE IF NOT EXISTS third_party_auth ( + user_id TEXT NOT NULL, + service_type TEXT NOT NULL, + resource TEXT NOT NULL, + auth_json TEXT NOT NULL, + time_added_ms BIGINT NOT NULL, + time_updated_ms BIGINT NOT NULL, + UNIQUE(user_id, resource) +); ` const selectServiceUserIDsSQL = ` @@ -201,3 +211,69 @@ func selectRoomServicesTxn(txn *sql.Tx, serviceUserID, roomID string) (serviceID } return } + +type ThirdPartyAuth struct { + // The ID of the matrix user who has authed with the third party + UserID string + // The type of third party. This determines which code gets loaded to + // handle parsing of the AuthJSON. + ServiceType string + // The location of the third party resource e.g. "github.com". + // This is mainly relevant for decentralised services like JIRA which + // may have many different locations (e.g. "matrix.org/jira") for the + // same ServiceType ("jira"). + Resource string + // An opaque JSON blob of stored auth data. Only the service defined in + // ServiceType knows how to parse this data. + AuthJSON []byte + // When the row was initially inserted. + TimeAddedMs int + // When the row was last updated. + TimeUpdatedMs int +} + +const selectThirdPartyAuthSQL = ` +SELECT resource, auth_json, time_added_ms, time_updated_ms FROM third_party_auth +WHERE user_id=$1 AND service_type=$2 +` + +func selectThirdPartyAuthsForUserTxn(txn *sql.Tx, service types.Service, userID string) (auths []ThirdPartyAuth, err error) { + rows, err := txn.Query(selectThirdPartyAuthSQL, userID, service.ServiceType()) + if err != nil { + return + } + defer rows.Close() + for rows.Next() { + var tpa ThirdPartyAuth + if err = rows.Scan(&tpa.Resource, &tpa.AuthJSON, &tpa.TimeAddedMs, &tpa.TimeUpdatedMs); err != nil { + return + } + tpa.UserID = userID + tpa.ServiceType = service.ServiceType() + auths = append(auths, tpa) + } + return +} + +const insertThirdPartyAuthSQL = ` +INSERT INTO third_party_auth( + user_id, service_type, resource, auth_json, time_added_ms, time_updated_ms +) VALUES($1, $2, $3, $4, $5, $6) +` + +func insertThirdPartyAuthTxn(txn *sql.Tx, tpa ThirdPartyAuth) (err error) { + _, err = txn.Exec(insertThirdPartyAuthSQL, tpa.UserID, tpa.ServiceType, tpa.Resource, + tpa.AuthJSON, tpa.TimeAddedMs, tpa.TimeUpdatedMs) + return +} + +const updateThirdPartyAuthSQL = ` +UPDATE third_party_auth SET auth_json=$1, time_updated_ms=$2 + WHERE user_id=$3 AND resource=$4 +` + +func updateThirdPartyAuthTxn(txn *sql.Tx, tpa ThirdPartyAuth) (err error) { + _, err = txn.Exec(updateThirdPartyAuthSQL, tpa.AuthJSON, tpa.TimeUpdatedMs, + tpa.UserID, tpa.Resource) + return err +} From 1d08fcbb18a52035abd179ba9fde9419307a13c6 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 2 Aug 2016 16:51:12 +0100 Subject: [PATCH 2/4] Add LoadThirdPartyAuthsForUser and StoreThirdPartyAuth --- .../matrix-org/go-neb/database/db.go | 45 +++++++++++++++++++ .../matrix-org/go-neb/database/schema.go | 11 ++--- 2 files changed, 51 insertions(+), 5 deletions(-) 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 ece6d23..ef8b09f 100644 --- a/src/github.com/matrix-org/go-neb/database/db.go +++ b/src/github.com/matrix-org/go-neb/database/db.go @@ -95,6 +95,51 @@ func (d *ServiceDB) LoadServicesInRoom(serviceUserID, roomID string) (services [ return } +// LoadThirdPartyAuthsForUser loads all the third-party credentials that the given userID +// has linked to the given Service. Returns an empty list if there are no credentials. +func (d *ServiceDB) LoadThirdPartyAuthsForUser(srv types.Service, userID string) (tpas []ThirdPartyAuth, err error) { + err = runTransaction(d.db, func(txn *sql.Tx) error { + tpas, err = selectThirdPartyAuthsForUserTxn(txn, srv.ServiceType(), userID) + if err != nil { + return err + } + return nil + }) + return +} + +// StoreThirdPartyAuth stores the ThirdPartyAuth for the given Service. Updates the +// time added/updated values. +// If the auth already exists then it will be updated, otherwise a new auth +// will be inserted. The previous auth is returned. +func (d *ServiceDB) StoreThirdPartyAuth(tpa ThirdPartyAuth) (old ThirdPartyAuth, err error) { + err = runTransaction(d.db, func(txn *sql.Tx) error { + var olds []ThirdPartyAuth + var hasOld bool + olds, err = selectThirdPartyAuthsForUserTxn(txn, tpa.ServiceType, tpa.UserID) + for _, o := range olds { + if o.UserID == tpa.UserID && o.Resource == tpa.Resource { + old = o + hasOld = true + break + } + } + now := time.Now().UnixNano() / 1000000 + + if err != nil { + return err + } else if hasOld { + tpa.TimeUpdatedMs = now + return updateThirdPartyAuthTxn(txn, tpa) + } else { + tpa.TimeAddedMs = now + tpa.TimeUpdatedMs = now + return insertThirdPartyAuthTxn(txn, tpa) + } + }) + return +} + // StoreService stores a service into the database either by inserting a new // service or updating an existing service. Returns the old service if there // was one. 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 f85a6dd..78cf4d3 100644 --- a/src/github.com/matrix-org/go-neb/database/schema.go +++ b/src/github.com/matrix-org/go-neb/database/schema.go @@ -212,6 +212,7 @@ func selectRoomServicesTxn(txn *sql.Tx, serviceUserID, roomID string) (serviceID return } +// ThirdPartyAuth represents a third_party_auth data row. type ThirdPartyAuth struct { // The ID of the matrix user who has authed with the third party UserID string @@ -227,9 +228,9 @@ type ThirdPartyAuth struct { // ServiceType knows how to parse this data. AuthJSON []byte // When the row was initially inserted. - TimeAddedMs int + TimeAddedMs int64 // When the row was last updated. - TimeUpdatedMs int + TimeUpdatedMs int64 } const selectThirdPartyAuthSQL = ` @@ -237,8 +238,8 @@ SELECT resource, auth_json, time_added_ms, time_updated_ms FROM third_party_auth WHERE user_id=$1 AND service_type=$2 ` -func selectThirdPartyAuthsForUserTxn(txn *sql.Tx, service types.Service, userID string) (auths []ThirdPartyAuth, err error) { - rows, err := txn.Query(selectThirdPartyAuthSQL, userID, service.ServiceType()) +func selectThirdPartyAuthsForUserTxn(txn *sql.Tx, serviceType, userID string) (auths []ThirdPartyAuth, err error) { + rows, err := txn.Query(selectThirdPartyAuthSQL, userID, serviceType) if err != nil { return } @@ -249,7 +250,7 @@ func selectThirdPartyAuthsForUserTxn(txn *sql.Tx, service types.Service, userID return } tpa.UserID = userID - tpa.ServiceType = service.ServiceType() + tpa.ServiceType = serviceType auths = append(auths, tpa) } return From 514d59e4d559569c4e467fd255445a2da93f2a30 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 2 Aug 2016 17:48:02 +0100 Subject: [PATCH 3/4] Remove Service ID from ThirdPartyAuth; query off resource instead. This de-couples ThirdPartyAuth from Services so we can do auth without having to instantiate Services. --- .../matrix-org/go-neb/database/db.go | 32 ++++++---------- .../matrix-org/go-neb/database/schema.go | 37 ++++++------------- 2 files changed, 23 insertions(+), 46 deletions(-) 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 ef8b09f..c7b84e8 100644 --- a/src/github.com/matrix-org/go-neb/database/db.go +++ b/src/github.com/matrix-org/go-neb/database/db.go @@ -95,11 +95,12 @@ func (d *ServiceDB) LoadServicesInRoom(serviceUserID, roomID string) (services [ return } -// LoadThirdPartyAuthsForUser loads all the third-party credentials that the given userID -// has linked to the given Service. Returns an empty list if there are no credentials. -func (d *ServiceDB) LoadThirdPartyAuthsForUser(srv types.Service, userID string) (tpas []ThirdPartyAuth, err error) { +// LoadThirdPartyAuth loads third-party credentials that the given userID +// has linked to the given resource. Returns sql.ErrNoRows if there are no +// credentials for the given resource/user combination. +func (d *ServiceDB) LoadThirdPartyAuth(resource, userID string) (tpa ThirdPartyAuth, err error) { err = runTransaction(d.db, func(txn *sql.Tx) error { - tpas, err = selectThirdPartyAuthsForUserTxn(txn, srv.ServiceType(), userID) + tpa, err = selectThirdPartyAuthTxn(txn, resource, userID) if err != nil { return err } @@ -114,27 +115,18 @@ func (d *ServiceDB) LoadThirdPartyAuthsForUser(srv types.Service, userID string) // will be inserted. The previous auth is returned. func (d *ServiceDB) StoreThirdPartyAuth(tpa ThirdPartyAuth) (old ThirdPartyAuth, err error) { err = runTransaction(d.db, func(txn *sql.Tx) error { - var olds []ThirdPartyAuth - var hasOld bool - olds, err = selectThirdPartyAuthsForUserTxn(txn, tpa.ServiceType, tpa.UserID) - for _, o := range olds { - if o.UserID == tpa.UserID && o.Resource == tpa.Resource { - old = o - hasOld = true - break - } - } + old, err = selectThirdPartyAuthTxn(txn, tpa.Resource, tpa.UserID) now := time.Now().UnixNano() / 1000000 - if err != nil { - return err - } else if hasOld { - tpa.TimeUpdatedMs = now - return updateThirdPartyAuthTxn(txn, tpa) - } else { + if err == sql.ErrNoRows { tpa.TimeAddedMs = now tpa.TimeUpdatedMs = now return insertThirdPartyAuthTxn(txn, tpa) + } else if err != nil { + return err + } else { + tpa.TimeUpdatedMs = now + return updateThirdPartyAuthTxn(txn, tpa) } }) return 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 78cf4d3..187d411 100644 --- a/src/github.com/matrix-org/go-neb/database/schema.go +++ b/src/github.com/matrix-org/go-neb/database/schema.go @@ -37,7 +37,6 @@ CREATE TABLE IF NOT EXISTS matrix_clients ( CREATE TABLE IF NOT EXISTS third_party_auth ( user_id TEXT NOT NULL, - service_type TEXT NOT NULL, resource TEXT NOT NULL, auth_json TEXT NOT NULL, time_added_ms BIGINT NOT NULL, @@ -216,16 +215,12 @@ func selectRoomServicesTxn(txn *sql.Tx, serviceUserID, roomID string) (serviceID type ThirdPartyAuth struct { // The ID of the matrix user who has authed with the third party UserID string - // The type of third party. This determines which code gets loaded to - // handle parsing of the AuthJSON. - ServiceType string // The location of the third party resource e.g. "github.com". // This is mainly relevant for decentralised services like JIRA which // may have many different locations (e.g. "matrix.org/jira") for the // same ServiceType ("jira"). Resource string - // An opaque JSON blob of stored auth data. Only the service defined in - // ServiceType knows how to parse this data. + // An opaque JSON blob of stored auth data. AuthJSON []byte // When the row was initially inserted. TimeAddedMs int64 @@ -234,36 +229,26 @@ type ThirdPartyAuth struct { } const selectThirdPartyAuthSQL = ` -SELECT resource, auth_json, time_added_ms, time_updated_ms FROM third_party_auth -WHERE user_id=$1 AND service_type=$2 +SELECT auth_json, time_added_ms, time_updated_ms FROM third_party_auth +WHERE user_id=$1 AND resource=$2 ` -func selectThirdPartyAuthsForUserTxn(txn *sql.Tx, serviceType, userID string) (auths []ThirdPartyAuth, err error) { - rows, err := txn.Query(selectThirdPartyAuthSQL, userID, serviceType) - if err != nil { - return - } - defer rows.Close() - for rows.Next() { - var tpa ThirdPartyAuth - if err = rows.Scan(&tpa.Resource, &tpa.AuthJSON, &tpa.TimeAddedMs, &tpa.TimeUpdatedMs); err != nil { - return - } - tpa.UserID = userID - tpa.ServiceType = serviceType - auths = append(auths, tpa) - } +func selectThirdPartyAuthTxn(txn *sql.Tx, resource, userID string) (tpa ThirdPartyAuth, err error) { + tpa.Resource = resource + tpa.UserID = userID + err = txn.QueryRow(selectThirdPartyAuthSQL, userID, resource).Scan( + &tpa.AuthJSON, &tpa.TimeAddedMs, &tpa.TimeUpdatedMs) return } const insertThirdPartyAuthSQL = ` INSERT INTO third_party_auth( - user_id, service_type, resource, auth_json, time_added_ms, time_updated_ms -) VALUES($1, $2, $3, $4, $5, $6) + user_id, resource, auth_json, time_added_ms, time_updated_ms +) VALUES($1, $2, $3, $4, $5) ` func insertThirdPartyAuthTxn(txn *sql.Tx, tpa ThirdPartyAuth) (err error) { - _, err = txn.Exec(insertThirdPartyAuthSQL, tpa.UserID, tpa.ServiceType, tpa.Resource, + _, err = txn.Exec(insertThirdPartyAuthSQL, tpa.UserID, tpa.Resource, tpa.AuthJSON, tpa.TimeAddedMs, tpa.TimeUpdatedMs) return } From 8fea4bb0ab2320534e6527ab1560c8f6a8d9d3da Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Aug 2016 13:07:49 +0100 Subject: [PATCH 4/4] Add AuthModule concept - Register them like we are with Services. - Add `/configureAuth` endpoint to create/update auth. - Move ThirdPartyAuth out of the database layer since they are passed as params to `/admin/configureAuth` --- src/github.com/matrix-org/go-neb/api.go | 26 ++++++++++++ src/github.com/matrix-org/go-neb/auth/auth.go | 13 ++++++ .../matrix-org/go-neb/auth/github/github.go | 22 ++++++++++ .../matrix-org/go-neb/database/db.go | 9 +--- .../matrix-org/go-neb/database/schema.go | 42 ++++++------------- src/github.com/matrix-org/go-neb/goneb.go | 4 ++ .../matrix-org/go-neb/types/types.go | 36 ++++++++++++++++ 7 files changed, 116 insertions(+), 36 deletions(-) create mode 100644 src/github.com/matrix-org/go-neb/auth/auth.go create mode 100644 src/github.com/matrix-org/go-neb/auth/github/github.go diff --git a/src/github.com/matrix-org/go-neb/api.go b/src/github.com/matrix-org/go-neb/api.go index 8a07874..d6e055d 100644 --- a/src/github.com/matrix-org/go-neb/api.go +++ b/src/github.com/matrix-org/go-neb/api.go @@ -16,6 +16,32 @@ func (*heartbeatHandler) OnIncomingRequest(req *http.Request) (interface{}, *err return &struct{}{}, nil } +type configureAuthHandler struct { + db *database.ServiceDB +} + +func (*configureAuthHandler) OnIncomingRequest(req *http.Request) (interface{}, *errors.HTTPError) { + if req.Method != "POST" { + return nil, &errors.HTTPError{nil, "Unsupported Method", 405} + } + var tpa types.ThirdPartyAuth + if err := json.NewDecoder(req.Body).Decode(&tpa); err != nil { + return nil, &errors.HTTPError{err, "Error parsing request JSON", 400} + } + + am := types.GetAuthModule(tpa.Type) + if am == nil { + return nil, &errors.HTTPError{nil, "Bad auth type: " + tpa.Type, 400} + } + + err := am.Process(tpa) + if err != nil { + return nil, &errors.HTTPError{err, "Failed to persist auth", 500} + } + + return nil, nil +} + func handleWebhook(w http.ResponseWriter, req *http.Request) { segments := strings.Split(req.URL.Path, "/") // last path segment is the service type which we will pass the incoming request to diff --git a/src/github.com/matrix-org/go-neb/auth/auth.go b/src/github.com/matrix-org/go-neb/auth/auth.go new file mode 100644 index 0000000..eb9338d --- /dev/null +++ b/src/github.com/matrix-org/go-neb/auth/auth.go @@ -0,0 +1,13 @@ +package auth + +import ( + "github.com/matrix-org/go-neb/auth/github" + "github.com/matrix-org/go-neb/database" + "github.com/matrix-org/go-neb/types" +) + +// RegisterModules registers all known modules so they can be retrieved via +// type.GetAuthModule +func RegisterModules(db *database.ServiceDB) { + types.RegisterAuthModule(&github.AuthModule{Database: db}) +} diff --git a/src/github.com/matrix-org/go-neb/auth/github/github.go b/src/github.com/matrix-org/go-neb/auth/github/github.go new file mode 100644 index 0000000..3661417 --- /dev/null +++ b/src/github.com/matrix-org/go-neb/auth/github/github.go @@ -0,0 +1,22 @@ +package github + +import ( + "github.com/matrix-org/go-neb/database" + "github.com/matrix-org/go-neb/types" +) + +// AuthModule for github +type AuthModule struct { + Database *database.ServiceDB +} + +// Type of the auth module +func (*AuthModule) Type() string { + return "github" +} + +// Process a third-party auth request +func (am *AuthModule) Process(tpa types.ThirdPartyAuth) (err error) { + _, err = am.Database.StoreThirdPartyAuth(tpa) + return +} 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 c7b84e8..2259534 100644 --- a/src/github.com/matrix-org/go-neb/database/db.go +++ b/src/github.com/matrix-org/go-neb/database/db.go @@ -98,7 +98,7 @@ func (d *ServiceDB) LoadServicesInRoom(serviceUserID, roomID string) (services [ // LoadThirdPartyAuth loads third-party credentials that the given userID // has linked to the given resource. Returns sql.ErrNoRows if there are no // credentials for the given resource/user combination. -func (d *ServiceDB) LoadThirdPartyAuth(resource, userID string) (tpa ThirdPartyAuth, err error) { +func (d *ServiceDB) LoadThirdPartyAuth(resource, userID string) (tpa types.ThirdPartyAuth, err error) { err = runTransaction(d.db, func(txn *sql.Tx) error { tpa, err = selectThirdPartyAuthTxn(txn, resource, userID) if err != nil { @@ -113,19 +113,14 @@ func (d *ServiceDB) LoadThirdPartyAuth(resource, userID string) (tpa ThirdPartyA // time added/updated values. // If the auth already exists then it will be updated, otherwise a new auth // will be inserted. The previous auth is returned. -func (d *ServiceDB) StoreThirdPartyAuth(tpa ThirdPartyAuth) (old ThirdPartyAuth, err error) { +func (d *ServiceDB) StoreThirdPartyAuth(tpa types.ThirdPartyAuth) (old types.ThirdPartyAuth, err error) { err = runTransaction(d.db, func(txn *sql.Tx) error { old, err = selectThirdPartyAuthTxn(txn, tpa.Resource, tpa.UserID) - now := time.Now().UnixNano() / 1000000 - if err == sql.ErrNoRows { - tpa.TimeAddedMs = now - tpa.TimeUpdatedMs = now return insertThirdPartyAuthTxn(txn, tpa) } else if err != nil { return err } else { - tpa.TimeUpdatedMs = now return updateThirdPartyAuthTxn(txn, tpa) } }) 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 187d411..70275d0 100644 --- a/src/github.com/matrix-org/go-neb/database/schema.go +++ b/src/github.com/matrix-org/go-neb/database/schema.go @@ -37,6 +37,7 @@ CREATE TABLE IF NOT EXISTS matrix_clients ( CREATE TABLE IF NOT EXISTS third_party_auth ( user_id TEXT NOT NULL, + type TEXT NOT NULL, resource TEXT NOT NULL, auth_json TEXT NOT NULL, time_added_ms BIGINT NOT NULL, @@ -211,45 +212,27 @@ func selectRoomServicesTxn(txn *sql.Tx, serviceUserID, roomID string) (serviceID return } -// ThirdPartyAuth represents a third_party_auth data row. -type ThirdPartyAuth struct { - // The ID of the matrix user who has authed with the third party - UserID string - // The location of the third party resource e.g. "github.com". - // This is mainly relevant for decentralised services like JIRA which - // may have many different locations (e.g. "matrix.org/jira") for the - // same ServiceType ("jira"). - Resource string - // An opaque JSON blob of stored auth data. - AuthJSON []byte - // When the row was initially inserted. - TimeAddedMs int64 - // When the row was last updated. - TimeUpdatedMs int64 -} - const selectThirdPartyAuthSQL = ` -SELECT auth_json, time_added_ms, time_updated_ms FROM third_party_auth -WHERE user_id=$1 AND resource=$2 +SELECT type, auth_json FROM third_party_auth WHERE user_id=$1 AND resource=$2 ` -func selectThirdPartyAuthTxn(txn *sql.Tx, resource, userID string) (tpa ThirdPartyAuth, err error) { +func selectThirdPartyAuthTxn(txn *sql.Tx, resource, userID string) (tpa types.ThirdPartyAuth, err error) { tpa.Resource = resource tpa.UserID = userID - err = txn.QueryRow(selectThirdPartyAuthSQL, userID, resource).Scan( - &tpa.AuthJSON, &tpa.TimeAddedMs, &tpa.TimeUpdatedMs) + err = txn.QueryRow(selectThirdPartyAuthSQL, userID, resource).Scan(&tpa.Type, &tpa.AuthJSON) return } const insertThirdPartyAuthSQL = ` INSERT INTO third_party_auth( - user_id, resource, auth_json, time_added_ms, time_updated_ms -) VALUES($1, $2, $3, $4, $5) + user_id, type, resource, auth_json, time_added_ms, time_updated_ms +) VALUES($1, $2, $3, $4, $5, $6) ` -func insertThirdPartyAuthTxn(txn *sql.Tx, tpa ThirdPartyAuth) (err error) { - _, err = txn.Exec(insertThirdPartyAuthSQL, tpa.UserID, tpa.Resource, - tpa.AuthJSON, tpa.TimeAddedMs, tpa.TimeUpdatedMs) +func insertThirdPartyAuthTxn(txn *sql.Tx, tpa types.ThirdPartyAuth) (err error) { + timeAddedMs := time.Now().UnixNano() / 1000000 + _, err = txn.Exec(insertThirdPartyAuthSQL, tpa.UserID, tpa.Type, tpa.Resource, + []byte(tpa.AuthJSON), timeAddedMs, timeAddedMs) return } @@ -258,8 +241,9 @@ UPDATE third_party_auth SET auth_json=$1, time_updated_ms=$2 WHERE user_id=$3 AND resource=$4 ` -func updateThirdPartyAuthTxn(txn *sql.Tx, tpa ThirdPartyAuth) (err error) { - _, err = txn.Exec(updateThirdPartyAuthSQL, tpa.AuthJSON, tpa.TimeUpdatedMs, +func updateThirdPartyAuthTxn(txn *sql.Tx, tpa types.ThirdPartyAuth) (err error) { + timeUpdatedMs := time.Now().UnixNano() / 1000000 + _, err = txn.Exec(updateThirdPartyAuthSQL, []byte(tpa.AuthJSON), timeUpdatedMs, tpa.UserID, tpa.Resource) return err } diff --git a/src/github.com/matrix-org/go-neb/goneb.go b/src/github.com/matrix-org/go-neb/goneb.go index 7ba75c2..72e6f24 100644 --- a/src/github.com/matrix-org/go-neb/goneb.go +++ b/src/github.com/matrix-org/go-neb/goneb.go @@ -2,6 +2,7 @@ package main import ( log "github.com/Sirupsen/logrus" + "github.com/matrix-org/go-neb/auth" "github.com/matrix-org/go-neb/clients" "github.com/matrix-org/go-neb/database" "github.com/matrix-org/go-neb/server" @@ -28,9 +29,12 @@ func main() { log.Panic(err) } + auth.RegisterModules(db) + http.Handle("/test", server.MakeJSONAPI(&heartbeatHandler{})) 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/configureAuth", server.MakeJSONAPI(&configureAuthHandler{db: db})) http.HandleFunc("/services/hooks/", handleWebhook) http.ListenAndServe(bindAddress, nil) 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 dcf42e6..1f0f6c9 100644 --- a/src/github.com/matrix-org/go-neb/types/types.go +++ b/src/github.com/matrix-org/go-neb/types/types.go @@ -1,6 +1,7 @@ package types import ( + "encoding/json" "errors" "github.com/matrix-org/go-neb/plugin" "net/http" @@ -51,3 +52,38 @@ func CreateService(serviceID, serviceType string) Service { } return f(serviceID) } + +// AuthModule represents a thing which can handle auth requests of a given type. +type AuthModule interface { + Type() string + Process(tpa ThirdPartyAuth) error +} + +var authModulesByType = map[string]AuthModule{} + +// ThirdPartyAuth represents an individual authorisation entry between +// a third party and the Matrix user. +type ThirdPartyAuth struct { + // The ID of the matrix user who has authed with the third party + UserID string + // The type of auth (e.g. "jira", "github"). This determines which + // auth module is loaded to process the auth. + Type string + // The location of the third party resource e.g. "github.com". + // This is mainly relevant for decentralised services like JIRA which + // may have many different locations (e.g. "matrix.org/jira") for the + // same ServiceType ("jira"). + Resource string + // An opaque JSON blob of stored auth data. + AuthJSON json.RawMessage +} + +// RegisterAuthModule so it can be used by other parts of NEB. +func RegisterAuthModule(am AuthModule) { + authModulesByType[am.Type()] = am +} + +// GetAuthModule for the given auth type. Returns nil if no match. +func GetAuthModule(authType string) AuthModule { + return authModulesByType[authType] +}