From fce28b5b3798a709ce9547a023d2318693ee9a13 Mon Sep 17 00:00:00 2001 From: Katalina Okano Date: Wed, 23 Sep 2020 23:44:57 -0400 Subject: [PATCH] feat: add skeleton of discord-auth, resolve gazelle issues.. again --- deps.bzl | 6 +++ go.mod | 2 + src/db/ent/BUILD.bazel | 10 ++--- src/db/ent/challenge/BUILD.bazel | 2 +- src/db/ent/enttest/BUILD.bazel | 2 +- src/db/ent/guild/BUILD.bazel | 2 +- src/db/ent/migrate/BUILD.bazel | 6 +-- src/db/ent/predicate/BUILD.bazel | 2 +- src/db/ent/schema/BUILD.bazel | 6 +-- src/db/ent/session/BUILD.bazel | 2 +- src/discord-auth/BUILD.bazel | 8 +++- src/discord-auth/README.md | 21 ++++++++++ src/discord-auth/discord-auth.go | 47 ++++++++++++++++++++++- src/discord-auth/http/BUILD.bazel | 25 ++++++++++++ src/discord-auth/http/httpservice.go | 34 ++++++++++++++++ src/discord-auth/http/httpservice_test.go | 43 +++++++++++++++++++++ src/discord-auth/http/oauth.go | 25 ++++++++++++ src/discord-auth/http/oauth_test.go | 29 ++++++++++++++ src/discord-bot/BUILD.bazel | 8 ++-- 19 files changed, 257 insertions(+), 23 deletions(-) create mode 100644 src/discord-auth/README.md create mode 100644 src/discord-auth/http/BUILD.bazel create mode 100644 src/discord-auth/http/httpservice.go create mode 100644 src/discord-auth/http/httpservice_test.go create mode 100644 src/discord-auth/http/oauth.go create mode 100644 src/discord-auth/http/oauth_test.go diff --git a/deps.bzl b/deps.bzl index 45697b0..3fa0210 100644 --- a/deps.bzl +++ b/deps.bzl @@ -499,6 +499,12 @@ def go_repositories(): sum = "h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=", version = "v2.0.1", ) + go_repository( + name = "com_github_segmentio_ksuid", + importpath = "github.com/segmentio/ksuid", + sum = "h1:FoResxvleQwYiPAVKe1tMUlEirodZqlqglIuFsdDntY=", + version = "v1.0.3", + ) go_repository( name = "com_github_shurcool_sanitized_anchor_name", importpath = "github.com/shurcooL/sanitized_anchor_name", diff --git a/go.mod b/go.mod index da22707..1c3b518 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,10 @@ require ( github.com/bwmarrin/discordgo v0.22.0 github.com/facebook/ent v0.4.3 github.com/joho/godotenv v1.3.0 + github.com/julienschmidt/httprouter v1.2.0 github.com/lampjaw/discordclient v0.0.0-20200923011548-6558fc9e89df github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect + github.com/segmentio/ksuid v1.0.3 go.uber.org/fx v1.13.1 go.uber.org/multierr v1.6.0 // indirect golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a // indirect diff --git a/src/db/ent/BUILD.bazel b/src/db/ent/BUILD.bazel index 1f8459c..b25b2b1 100644 --- a/src/db/ent/BUILD.bazel +++ b/src/db/ent/BUILD.bazel @@ -36,10 +36,10 @@ go_library( "//src/db/ent/predicate", "//src/db/ent/schema", "//src/db/ent/session", - "@com_github_facebook_ent//:go_default_library", - "@com_github_facebook_ent//dialect:go_default_library", - "@com_github_facebook_ent//dialect/sql:go_default_library", - "@com_github_facebook_ent//dialect/sql/sqlgraph:go_default_library", - "@com_github_facebook_ent//schema/field:go_default_library", + "@com_github_facebook_ent//:ent", + "@com_github_facebook_ent//dialect", + "@com_github_facebook_ent//dialect/sql", + "@com_github_facebook_ent//dialect/sql/sqlgraph", + "@com_github_facebook_ent//schema/field", ], ) diff --git a/src/db/ent/challenge/BUILD.bazel b/src/db/ent/challenge/BUILD.bazel index 45c8b13..7868d92 100644 --- a/src/db/ent/challenge/BUILD.bazel +++ b/src/db/ent/challenge/BUILD.bazel @@ -10,6 +10,6 @@ go_library( visibility = ["//visibility:public"], deps = [ "//src/db/ent/predicate", - "@com_github_facebook_ent//dialect/sql:go_default_library", + "@com_github_facebook_ent//dialect/sql", ], ) diff --git a/src/db/ent/enttest/BUILD.bazel b/src/db/ent/enttest/BUILD.bazel index b583d35..4583471 100644 --- a/src/db/ent/enttest/BUILD.bazel +++ b/src/db/ent/enttest/BUILD.bazel @@ -8,6 +8,6 @@ go_library( deps = [ "//src/db/ent", "//src/db/ent/runtime", - "@com_github_facebook_ent//dialect/sql/schema:go_default_library", + "@com_github_facebook_ent//dialect/sql/schema", ], ) diff --git a/src/db/ent/guild/BUILD.bazel b/src/db/ent/guild/BUILD.bazel index 54bc909..c427ae2 100644 --- a/src/db/ent/guild/BUILD.bazel +++ b/src/db/ent/guild/BUILD.bazel @@ -10,6 +10,6 @@ go_library( visibility = ["//visibility:public"], deps = [ "//src/db/ent/predicate", - "@com_github_facebook_ent//dialect/sql:go_default_library", + "@com_github_facebook_ent//dialect/sql", ], ) diff --git a/src/db/ent/migrate/BUILD.bazel b/src/db/ent/migrate/BUILD.bazel index 8d55c74..5fb935e 100644 --- a/src/db/ent/migrate/BUILD.bazel +++ b/src/db/ent/migrate/BUILD.bazel @@ -9,8 +9,8 @@ go_library( importpath = "github.com/roleypoly/roleypoly/src/db/ent/migrate", visibility = ["//visibility:public"], deps = [ - "@com_github_facebook_ent//dialect:go_default_library", - "@com_github_facebook_ent//dialect/sql/schema:go_default_library", - "@com_github_facebook_ent//schema/field:go_default_library", + "@com_github_facebook_ent//dialect", + "@com_github_facebook_ent//dialect/sql/schema", + "@com_github_facebook_ent//schema/field", ], ) diff --git a/src/db/ent/predicate/BUILD.bazel b/src/db/ent/predicate/BUILD.bazel index a060901..edfebc4 100644 --- a/src/db/ent/predicate/BUILD.bazel +++ b/src/db/ent/predicate/BUILD.bazel @@ -5,5 +5,5 @@ go_library( srcs = ["predicate.go"], importpath = "github.com/roleypoly/roleypoly/src/db/ent/predicate", visibility = ["//visibility:public"], - deps = ["@com_github_facebook_ent//dialect/sql:go_default_library"], + deps = ["@com_github_facebook_ent//dialect/sql"], ) diff --git a/src/db/ent/schema/BUILD.bazel b/src/db/ent/schema/BUILD.bazel index c87afb1..f667fcf 100644 --- a/src/db/ent/schema/BUILD.bazel +++ b/src/db/ent/schema/BUILD.bazel @@ -10,8 +10,8 @@ go_library( importpath = "github.com/roleypoly/roleypoly/src/db/ent/schema", visibility = ["//visibility:public"], deps = [ - "@com_github_facebook_ent//:go_default_library", - "@com_github_facebook_ent//schema/field:go_default_library", - "@com_github_facebook_ent//schema/mixin:go_default_library", + "@com_github_facebook_ent//:ent", + "@com_github_facebook_ent//schema/field", + "@com_github_facebook_ent//schema/mixin", ], ) diff --git a/src/db/ent/session/BUILD.bazel b/src/db/ent/session/BUILD.bazel index 458366c..f11c826 100644 --- a/src/db/ent/session/BUILD.bazel +++ b/src/db/ent/session/BUILD.bazel @@ -10,6 +10,6 @@ go_library( visibility = ["//visibility:public"], deps = [ "//src/db/ent/predicate", - "@com_github_facebook_ent//dialect/sql:go_default_library", + "@com_github_facebook_ent//dialect/sql", ], ) diff --git a/src/discord-auth/BUILD.bazel b/src/discord-auth/BUILD.bazel index 5eff71c..87af097 100644 --- a/src/discord-auth/BUILD.bazel +++ b/src/discord-auth/BUILD.bazel @@ -7,7 +7,13 @@ go_library( srcs = ["discord-auth.go"], importpath = "github.com/roleypoly/roleypoly/src/discord-auth", visibility = ["//visibility:private"], - deps = ["@org_uber_go_fx//:go_default_library"], + deps = [ + "//src/common/version", + "//src/discord-auth/http", + "@com_github_julienschmidt_httprouter//:httprouter", + "@io_k8s_klog//:klog", + "@org_uber_go_fx//:fx", + ], ) go_binary( diff --git a/src/discord-auth/README.md b/src/discord-auth/README.md new file mode 100644 index 0000000..4149b3d --- /dev/null +++ b/src/discord-auth/README.md @@ -0,0 +1,21 @@ +# Discord Auth + +Service for handling Discord OAuth flow. + +## Responsibilities + +- Redirect users to relevant Discord OAuth page w/ state +- Handle redirect from Discord OAuth flow and process the token +- Modify active session to include relevant data + - v3: for parity, this is just user data + - _vNext: get guilds from oauth and cache_ +- _vNext: Source of truth for user guilds_ + +## Boundaries & Services + +- **Inbound** + - HTTP: /discord-auth/\* + - gRPC: DiscordAuthService +- **Outbound** + - Redis + - gRPC: SessionService diff --git a/src/discord-auth/discord-auth.go b/src/discord-auth/discord-auth.go index 1c629bc..4e6fb5f 100644 --- a/src/discord-auth/discord-auth.go +++ b/src/discord-auth/discord-auth.go @@ -1,11 +1,54 @@ package main -import "go.uber.org/fx" +import ( + "context" + "net/http" + + "github.com/julienschmidt/httprouter" + "go.uber.org/fx" + "k8s.io/klog" + + "github.com/roleypoly/roleypoly/src/common/version" + httpservice "github.com/roleypoly/roleypoly/src/discord-auth/http" +) func main() { + klog.Info(version.StartupInfo("discord-auth")) + app := fx.New( - // fx.Invoke(StartDiscordAuthService), + fx.Provide( + httprouter.New, + httpservice.NewHTTPService, + ), + fx.Invoke( + httpservice.RegisterRoutes, + newHTTPServer, + ), ) app.Run() } + +func newHTTPServer(lc fx.Lifecycle, router *httprouter.Router) *http.Server { + server := &http.Server{ + Addr: ":8080", + Handler: router, + } + + lc.Append(fx.Hook{ + OnStart: func(context.Context) error { + klog.Info("Starting HTTP Server") + go server.ListenAndServe() + + return nil + }, + OnStop: func(ctx context.Context) error { + klog.Info("Stopping HTTP Server") + return server.Shutdown(ctx) + }, + }) + + return server +} + +func startupHTTP() diff --git a/src/discord-auth/http/BUILD.bazel b/src/discord-auth/http/BUILD.bazel new file mode 100644 index 0000000..f43e0f4 --- /dev/null +++ b/src/discord-auth/http/BUILD.bazel @@ -0,0 +1,25 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "http", + srcs = [ + "httpservice.go", + "oauth.go", + ], + importpath = "github.com/roleypoly/roleypoly/src/discord-auth/http", + visibility = ["//visibility:public"], + deps = [ + "@com_github_julienschmidt_httprouter//:httprouter", + "@com_github_segmentio_ksuid//:ksuid", + ], +) + +go_test( + name = "http_test", + srcs = [ + "httpservice_test.go", + "oauth_test.go", + ], + embed = [":http"], + deps = ["@com_github_julienschmidt_httprouter//:httprouter"], +) diff --git a/src/discord-auth/http/httpservice.go b/src/discord-auth/http/httpservice.go new file mode 100644 index 0000000..e75da94 --- /dev/null +++ b/src/discord-auth/http/httpservice.go @@ -0,0 +1,34 @@ +package httpservice + +import "github.com/julienschmidt/httprouter" + +type HTTPService struct { + config struct { + ClientID string + PublicURL string + } +} + +func NewHTTPService() *HTTPService { + return &HTTPService{} +} + +func RegisterRoutes(s *HTTPService, router *httprouter.Router) { + s.RegisterRoutes(router) +} + +func (h *HTTPService) RegisterRoutes(router *httprouter.Router) { + router.GET(h.v3("/oauth-handoff"), h.oauthHandoffv3) +} + +func (*HTTPService) v3(path string) string { + return `/discord-auth/v3` + path +} + +func (*HTTPService) v4(path string) string { + return `/discord-auth/v4` + path +} + +func (h *HTTPService) getOauthRedirectURL() string { + return h.config.PublicURL + h.v3("/oauth-callback") +} diff --git a/src/discord-auth/http/httpservice_test.go b/src/discord-auth/http/httpservice_test.go new file mode 100644 index 0000000..fd1710d --- /dev/null +++ b/src/discord-auth/http/httpservice_test.go @@ -0,0 +1,43 @@ +package httpservice + +import ( + "testing" + + "github.com/julienschmidt/httprouter" +) + +func TestV3Route(t *testing.T) { + s := &HTTPService{} + url := s.v3("/test") + if url != "/discord-auth/v3/test" { + t.FailNow() + } +} + +func TestV4Route(t *testing.T) { + s := &HTTPService{} + url := s.v4("/test") + if url != "/discord-auth/v4/test" { + t.FailNow() + } +} + +func TestRegisterRoutes(t *testing.T) { + s := NewHTTPService() + r := httprouter.New() + RegisterRoutes(s, r) +} + +func TestOauthRedirectURL(t *testing.T) { + s := &HTTPService{ + config: struct{ ClientID, PublicURL string }{ + ClientID: "", + PublicURL: "https://roleypoly.local", + }, + } + + url := s.getOauthRedirectURL() + if url != "https://roleypoly.local/discord-auth/v3/oauth-callback" { + t.FailNow() + } +} diff --git a/src/discord-auth/http/oauth.go b/src/discord-auth/http/oauth.go new file mode 100644 index 0000000..415a4cc --- /dev/null +++ b/src/discord-auth/http/oauth.go @@ -0,0 +1,25 @@ +package httpservice + +import ( + "fmt" + "net/http" + + "github.com/julienschmidt/httprouter" + "github.com/segmentio/ksuid" +) + +// Handles flow start request by redirecting the user to Discord OAuth page +func (h *HTTPService) oauthHandoffv3(rw http.ResponseWriter, r *http.Request, _ httprouter.Params) { + // TODO: actually create and store this state + requestState := ksuid.New().String() + + redirectURL := fmt.Sprintf( + `https://discord.com/oauth2/authorize?client_id=%s&redirect_uri=%s&response_type=code&scope=identify&state=%s`, + h.config.ClientID, + h.getOauthRedirectURL(), + requestState, + ) + + rw.Header().Add("location", redirectURL) + rw.WriteHeader(303) +} diff --git a/src/discord-auth/http/oauth_test.go b/src/discord-auth/http/oauth_test.go new file mode 100644 index 0000000..ef982d7 --- /dev/null +++ b/src/discord-auth/http/oauth_test.go @@ -0,0 +1,29 @@ +package httpservice + +import ( + "net/http/httptest" + "strings" + "testing" + + "github.com/julienschmidt/httprouter" +) + +func TestOauthHandoffV3(t *testing.T) { + s := NewHTTPService() + s.config.ClientID = "test1234" + s.config.PublicURL = "https://roleypoly.test" + + rw := httptest.NewRecorder() + r := httptest.NewRequest("GET", s.v3("/oauth-handoff"), nil) + ps := httprouter.Params{} + s.oauthHandoffv3(rw, r, ps) + + if rw.Result().StatusCode != 303 { + t.Error("Status code was not 303, got ", rw.Result().StatusCode) + } + + if !strings.Contains(rw.Result().Header.Get("location"), s.config.ClientID) && + !strings.Contains(rw.Result().Header.Get("location"), s.getOauthRedirectURL()) { + t.Error("Location was not correct, got ", rw.Result().Header.Get("location")) + } +} diff --git a/src/discord-bot/BUILD.bazel b/src/discord-bot/BUILD.bazel index a03ff64..04fedc1 100644 --- a/src/discord-bot/BUILD.bazel +++ b/src/discord-bot/BUILD.bazel @@ -13,10 +13,10 @@ go_library( deps = [ "//src/common/version", "//src/discord-bot/internal/strings", - "@com_github_bwmarrin_discordgo//:go_default_library", - "@com_github_joho_godotenv//autoload:go_default_library", - "@com_github_lampjaw_discordclient//:go_default_library", - "@io_k8s_klog//:go_default_library", + "@com_github_bwmarrin_discordgo//:discordgo", + "@com_github_joho_godotenv//autoload", + "@com_github_lampjaw_discordclient//:discordclient", + "@io_k8s_klog//:klog", ], )