From d3ecdc066284ae0959315e216e99cdaba0b7ffc8 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 15 Aug 2016 10:00:08 +0100 Subject: [PATCH 1/6] Add endpoint to GET a Service config --- src/github.com/matrix-org/go-neb/api.go | 35 +++++++++++++++++++++++ src/github.com/matrix-org/go-neb/goneb.go | 1 + 2 files changed, 36 insertions(+) diff --git a/src/github.com/matrix-org/go-neb/api.go b/src/github.com/matrix-org/go-neb/api.go index ff1da59..2b68d58 100644 --- a/src/github.com/matrix-org/go-neb/api.go +++ b/src/github.com/matrix-org/go-neb/api.go @@ -1,6 +1,7 @@ package main import ( + "database/sql" "encoding/json" log "github.com/Sirupsen/logrus" "github.com/matrix-org/go-neb/clients" @@ -228,3 +229,37 @@ func (s *configureServiceHandler) OnIncomingRequest(req *http.Request) (interfac NewConfig types.Service }{body.ID, body.Type, oldService, service}, nil } + +type getServiceHandler struct { + db *database.ServiceDB +} + +func (h *getServiceHandler) OnIncomingRequest(req *http.Request) (interface{}, *errors.HTTPError) { + if req.Method != "GET" { + return nil, &errors.HTTPError{nil, "Unsupported Method", 405} + } + var body struct { + ID string + } + if err := json.NewDecoder(req.Body).Decode(&body); err != nil { + return nil, &errors.HTTPError{err, "Error parsing request JSON", 400} + } + + if body.ID == "" { + return nil, &errors.HTTPError{nil, `Must supply a "ID"`, 400} + } + + srv, err := h.db.LoadService(body.ID) + if err != nil { + if err == sql.ErrNoRows { + return nil, &errors.HTTPError{err, `Service not found`, 404} + } + return nil, &errors.HTTPError{err, `Failed to load service`, 500} + } + + return &struct { + ID string + Type string + Config types.Service + }{srv.ServiceID(), srv.ServiceType(), srv}, nil +} diff --git a/src/github.com/matrix-org/go-neb/goneb.go b/src/github.com/matrix-org/go-neb/goneb.go index 2e22ef9..2e107bd 100644 --- a/src/github.com/matrix-org/go-neb/goneb.go +++ b/src/github.com/matrix-org/go-neb/goneb.go @@ -40,6 +40,7 @@ func main() { } http.Handle("/test", server.MakeJSONAPI(&heartbeatHandler{})) + http.Handle("/admin/getService", server.MakeJSONAPI(&getServiceHandler{db: db})) 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})) From b3ff1cd54a86f3aad8ffec39067f8a43f83c1032 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 15 Aug 2016 10:10:15 +0100 Subject: [PATCH 2/6] Add endpoint for GETing AuthSession information for a user/realm tuple Add a new `AuthSession` function `Authenticated()` which returns `true` if the user has completed the auth process. This allows the caller to distinguish between: - Never done any auth (404s) - In the process of doing auth (`Authenticated == false`) - Finished doing auth (`Authenticated == true`) --- src/github.com/matrix-org/go-neb/api.go | 35 +++++++++++++++++++ src/github.com/matrix-org/go-neb/goneb.go | 1 + .../matrix-org/go-neb/realms/github/github.go | 5 +++ .../matrix-org/go-neb/realms/jira/jira.go | 5 +++ .../matrix-org/go-neb/types/types.go | 1 + 5 files changed, 47 insertions(+) diff --git a/src/github.com/matrix-org/go-neb/api.go b/src/github.com/matrix-org/go-neb/api.go index 2b68d58..6d63678 100644 --- a/src/github.com/matrix-org/go-neb/api.go +++ b/src/github.com/matrix-org/go-neb/api.go @@ -263,3 +263,38 @@ func (h *getServiceHandler) OnIncomingRequest(req *http.Request) (interface{}, * Config types.Service }{srv.ServiceID(), srv.ServiceType(), srv}, nil } + +type getSessionHandler struct { + db *database.ServiceDB +} + +func (h *getSessionHandler) OnIncomingRequest(req *http.Request) (interface{}, *errors.HTTPError) { + if req.Method != "GET" { + return nil, &errors.HTTPError{nil, "Unsupported Method", 405} + } + var body struct { + RealmID string + UserID string + } + if err := json.NewDecoder(req.Body).Decode(&body); err != nil { + return nil, &errors.HTTPError{err, "Error parsing request JSON", 400} + } + + if body.RealmID == "" || body.UserID == "" { + return nil, &errors.HTTPError{nil, `Must supply a "RealmID" and "UserID"`, 400} + } + + session, err := h.db.LoadAuthSessionByUser(body.RealmID, body.UserID) + if err != nil { + if err == sql.ErrNoRows { + return nil, &errors.HTTPError{err, `Session not found`, 404} + } + return nil, &errors.HTTPError{err, `Failed to load session`, 500} + } + + return &struct { + ID string + Authenticated bool + Session types.AuthSession + }{session.ID(), session.Authenticated(), 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 2e107bd..9a93067 100644 --- a/src/github.com/matrix-org/go-neb/goneb.go +++ b/src/github.com/matrix-org/go-neb/goneb.go @@ -41,6 +41,7 @@ func main() { http.Handle("/test", server.MakeJSONAPI(&heartbeatHandler{})) http.Handle("/admin/getService", server.MakeJSONAPI(&getServiceHandler{db: db})) + http.Handle("/admin/getSession", server.MakeJSONAPI(&getSessionHandler{db: db})) 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})) 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 7d624e4..848ec1c 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 @@ -30,6 +30,11 @@ type GithubSession struct { realmID string } +// Authenticated returns true if the user has completed the auth process +func (s *GithubSession) Authenticated() bool { + return s.AccessToken != "" +} + // UserID returns the user_id who authorised with Github func (s *GithubSession) UserID() string { return s.userID diff --git a/src/github.com/matrix-org/go-neb/realms/jira/jira.go b/src/github.com/matrix-org/go-neb/realms/jira/jira.go index 26e6fb7..27c5a02 100644 --- a/src/github.com/matrix-org/go-neb/realms/jira/jira.go +++ b/src/github.com/matrix-org/go-neb/realms/jira/jira.go @@ -46,6 +46,11 @@ type JIRASession struct { AccessSecret string } +// Authenticated returns true if the user has completed the auth process +func (s *JIRASession) Authenticated() bool { + return s.AccessToken != "" && s.AccessSecret != "" +} + // UserID returns the ID of the user performing the authentication. func (s *JIRASession) UserID() string { return s.userID 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 14febc5..5e68cfe 100644 --- a/src/github.com/matrix-org/go-neb/types/types.go +++ b/src/github.com/matrix-org/go-neb/types/types.go @@ -123,4 +123,5 @@ type AuthSession interface { ID() string UserID() string RealmID() string + Authenticated() bool } From 4c340dd016b06f310db01fdfd974157da78d1815 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 15 Aug 2016 10:46:27 +0100 Subject: [PATCH 3/6] s/GET/POST/ --- src/github.com/matrix-org/go-neb/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/github.com/matrix-org/go-neb/api.go b/src/github.com/matrix-org/go-neb/api.go index 2b68d58..c0f22b4 100644 --- a/src/github.com/matrix-org/go-neb/api.go +++ b/src/github.com/matrix-org/go-neb/api.go @@ -235,7 +235,7 @@ type getServiceHandler struct { } func (h *getServiceHandler) OnIncomingRequest(req *http.Request) (interface{}, *errors.HTTPError) { - if req.Method != "GET" { + if req.Method != "POST" { return nil, &errors.HTTPError{nil, "Unsupported Method", 405} } var body struct { From cb0fcbf2addc14fe4bc4af449a4d7043bac1aaef Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 15 Aug 2016 10:47:16 +0100 Subject: [PATCH 4/6] s/GET/POST/ --- src/github.com/matrix-org/go-neb/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/github.com/matrix-org/go-neb/api.go b/src/github.com/matrix-org/go-neb/api.go index 6d63678..598a35b 100644 --- a/src/github.com/matrix-org/go-neb/api.go +++ b/src/github.com/matrix-org/go-neb/api.go @@ -269,7 +269,7 @@ type getSessionHandler struct { } func (h *getSessionHandler) OnIncomingRequest(req *http.Request) (interface{}, *errors.HTTPError) { - if req.Method != "GET" { + if req.Method != "POST" { return nil, &errors.HTTPError{nil, "Unsupported Method", 405} } var body struct { From 44abae4977129c9fec40bd39a026b8d5bea32b8b Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 15 Aug 2016 11:25:06 +0100 Subject: [PATCH 5/6] Make StarterLink a property of the AuthRealm --- .../matrix-org/go-neb/realms/github/github.go | 19 ++++++++++--------- .../go-neb/services/github/github.go | 11 +++++++++-- 2 files changed, 19 insertions(+), 11 deletions(-) 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 7d624e4..3e54c7e 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 @@ -12,11 +12,12 @@ import ( "net/url" ) -type githubRealm struct { +type GithubRealm struct { id string redirectURL string ClientSecret string ClientID string + StarterLink string } // GithubSession represents an authenticated github session @@ -45,23 +46,23 @@ func (s *GithubSession) ID() string { return s.id } -func (r *githubRealm) ID() string { +func (r *GithubRealm) ID() string { return r.id } -func (r *githubRealm) Type() string { +func (r *GithubRealm) Type() string { return "github" } -func (r *githubRealm) Init() error { +func (r *GithubRealm) Init() error { return nil } -func (r *githubRealm) Register() error { +func (r *GithubRealm) Register() error { return nil } -func (r *githubRealm) RequestAuthSession(userID string, req json.RawMessage) interface{} { +func (r *GithubRealm) RequestAuthSession(userID string, req json.RawMessage) interface{} { state, err := randomString(10) if err != nil { log.WithError(err).Print("Failed to generate state param") @@ -90,7 +91,7 @@ func (r *githubRealm) RequestAuthSession(userID string, req json.RawMessage) int }{u.String()} } -func (r *githubRealm) OnReceiveRedirect(w http.ResponseWriter, req *http.Request) { +func (r *GithubRealm) OnReceiveRedirect(w http.ResponseWriter, req *http.Request) { // parse out params from the request code := req.URL.Query().Get("code") state := req.URL.Query().Get("state") @@ -148,7 +149,7 @@ func (r *githubRealm) OnReceiveRedirect(w http.ResponseWriter, req *http.Request w.Write([]byte("OK!")) } -func (r *githubRealm) AuthSession(id, userID, realmID string) types.AuthSession { +func (r *GithubRealm) AuthSession(id, userID, realmID string) types.AuthSession { return &GithubSession{ id: id, userID: userID, @@ -175,6 +176,6 @@ func randomString(length int) (string, error) { func init() { types.RegisterAuthRealm(func(realmID, redirectURL string) types.AuthRealm { - return &githubRealm{id: realmID, redirectURL: redirectURL} + return &GithubRealm{id: realmID, redirectURL: redirectURL} }) } diff --git a/src/github.com/matrix-org/go-neb/services/github/github.go b/src/github.com/matrix-org/go-neb/services/github/github.go index 75bc820..e363c79 100644 --- a/src/github.com/matrix-org/go-neb/services/github/github.go +++ b/src/github.com/matrix-org/go-neb/services/github/github.go @@ -28,7 +28,6 @@ type githubService struct { ClientUserID string RealmID string SecretToken string - StarterLink string Rooms map[string]struct { // room_id => {} Repos map[string]struct { // owner/repo => { events: ["push","issue","pull_request"] } Events []string @@ -50,9 +49,17 @@ func (s *githubService) RoomIDs() []string { func (s *githubService) cmdGithubCreate(roomID, userID string, args []string) (interface{}, error) { cli := s.githubClientFor(userID, false) if cli == nil { + r, err := database.GetServiceDB().LoadAuthRealm(s.RealmID) + if err != nil { + return nil, err + } + ghRealm, ok := r.(*realms.GithubRealm) + if !ok { + return nil, fmt.Errorf("Failed to cast realm %s into a GithubRealm", s.RealmID) + } return matrix.StarterLinkMessage{ Body: "You need to OAuth with Github before you can create issues.", - Link: s.StarterLink, + Link: ghRealm.StarterLink, }, nil } From 2a93bbee8220891c2c233d0bf3c735f58d8e9cd9 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 15 Aug 2016 11:27:37 +0100 Subject: [PATCH 6/6] Comments --- src/github.com/matrix-org/go-neb/realms/github/github.go | 8 ++++++++ 1 file changed, 8 insertions(+) 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 3e54c7e..f1c1021 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 @@ -12,6 +12,7 @@ import ( "net/url" ) +// GithubRealm can handle OAuth processes with github.com type GithubRealm struct { id string redirectURL string @@ -46,22 +47,27 @@ func (s *GithubSession) ID() string { return s.id } +// ID returns the realm ID func (r *GithubRealm) ID() string { return r.id } +// Type is github func (r *GithubRealm) Type() string { return "github" } +// Init does nothing. func (r *GithubRealm) Init() error { return nil } +// Register does nothing. func (r *GithubRealm) Register() error { return nil } +// RequestAuthSession generates an OAuth2 URL for this user to auth with github via. func (r *GithubRealm) RequestAuthSession(userID string, req json.RawMessage) interface{} { state, err := randomString(10) if err != nil { @@ -91,6 +97,7 @@ func (r *GithubRealm) RequestAuthSession(userID string, req json.RawMessage) int }{u.String()} } +// OnReceiveRedirect processes OAuth redirect requests from Github func (r *GithubRealm) OnReceiveRedirect(w http.ResponseWriter, req *http.Request) { // parse out params from the request code := req.URL.Query().Get("code") @@ -149,6 +156,7 @@ func (r *GithubRealm) OnReceiveRedirect(w http.ResponseWriter, req *http.Request w.Write([]byte("OK!")) } +// AuthSession returns a GithubSession for this user func (r *GithubRealm) AuthSession(id, userID, realmID string) types.AuthSession { return &GithubSession{ id: id,