Browse Source

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`
kegan/auth
Kegan Dougal 8 years ago
parent
commit
8fea4bb0ab
  1. 26
      src/github.com/matrix-org/go-neb/api.go
  2. 13
      src/github.com/matrix-org/go-neb/auth/auth.go
  3. 22
      src/github.com/matrix-org/go-neb/auth/github/github.go
  4. 9
      src/github.com/matrix-org/go-neb/database/db.go
  5. 42
      src/github.com/matrix-org/go-neb/database/schema.go
  6. 4
      src/github.com/matrix-org/go-neb/goneb.go
  7. 36
      src/github.com/matrix-org/go-neb/types/types.go

26
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

13
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})
}

22
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
}

9
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)
}
})

42
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
}

4
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)

36
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]
}
Loading…
Cancel
Save