mirror of
https://github.com/roleypoly/roleypoly.git
synced 2025-04-25 11:59:11 +00:00
feat: add skeleton of discord-auth, resolve gazelle issues.. again
This commit is contained in:
parent
deb6c194e9
commit
fce28b5b37
19 changed files with 257 additions and 23 deletions
6
deps.bzl
6
deps.bzl
|
@ -499,6 +499,12 @@ def go_repositories():
|
||||||
sum = "h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=",
|
sum = "h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=",
|
||||||
version = "v2.0.1",
|
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(
|
go_repository(
|
||||||
name = "com_github_shurcool_sanitized_anchor_name",
|
name = "com_github_shurcool_sanitized_anchor_name",
|
||||||
importpath = "github.com/shurcooL/sanitized_anchor_name",
|
importpath = "github.com/shurcooL/sanitized_anchor_name",
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -6,8 +6,10 @@ require (
|
||||||
github.com/bwmarrin/discordgo v0.22.0
|
github.com/bwmarrin/discordgo v0.22.0
|
||||||
github.com/facebook/ent v0.4.3
|
github.com/facebook/ent v0.4.3
|
||||||
github.com/joho/godotenv v1.3.0
|
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/lampjaw/discordclient v0.0.0-20200923011548-6558fc9e89df
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
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/fx v1.13.1
|
||||||
go.uber.org/multierr v1.6.0 // indirect
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a // indirect
|
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a // indirect
|
||||||
|
|
|
@ -36,10 +36,10 @@ go_library(
|
||||||
"//src/db/ent/predicate",
|
"//src/db/ent/predicate",
|
||||||
"//src/db/ent/schema",
|
"//src/db/ent/schema",
|
||||||
"//src/db/ent/session",
|
"//src/db/ent/session",
|
||||||
"@com_github_facebook_ent//:go_default_library",
|
"@com_github_facebook_ent//:ent",
|
||||||
"@com_github_facebook_ent//dialect:go_default_library",
|
"@com_github_facebook_ent//dialect",
|
||||||
"@com_github_facebook_ent//dialect/sql:go_default_library",
|
"@com_github_facebook_ent//dialect/sql",
|
||||||
"@com_github_facebook_ent//dialect/sql/sqlgraph:go_default_library",
|
"@com_github_facebook_ent//dialect/sql/sqlgraph",
|
||||||
"@com_github_facebook_ent//schema/field:go_default_library",
|
"@com_github_facebook_ent//schema/field",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -10,6 +10,6 @@ go_library(
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"//src/db/ent/predicate",
|
"//src/db/ent/predicate",
|
||||||
"@com_github_facebook_ent//dialect/sql:go_default_library",
|
"@com_github_facebook_ent//dialect/sql",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -8,6 +8,6 @@ go_library(
|
||||||
deps = [
|
deps = [
|
||||||
"//src/db/ent",
|
"//src/db/ent",
|
||||||
"//src/db/ent/runtime",
|
"//src/db/ent/runtime",
|
||||||
"@com_github_facebook_ent//dialect/sql/schema:go_default_library",
|
"@com_github_facebook_ent//dialect/sql/schema",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -10,6 +10,6 @@ go_library(
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"//src/db/ent/predicate",
|
"//src/db/ent/predicate",
|
||||||
"@com_github_facebook_ent//dialect/sql:go_default_library",
|
"@com_github_facebook_ent//dialect/sql",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,8 +9,8 @@ go_library(
|
||||||
importpath = "github.com/roleypoly/roleypoly/src/db/ent/migrate",
|
importpath = "github.com/roleypoly/roleypoly/src/db/ent/migrate",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"@com_github_facebook_ent//dialect:go_default_library",
|
"@com_github_facebook_ent//dialect",
|
||||||
"@com_github_facebook_ent//dialect/sql/schema:go_default_library",
|
"@com_github_facebook_ent//dialect/sql/schema",
|
||||||
"@com_github_facebook_ent//schema/field:go_default_library",
|
"@com_github_facebook_ent//schema/field",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,5 +5,5 @@ go_library(
|
||||||
srcs = ["predicate.go"],
|
srcs = ["predicate.go"],
|
||||||
importpath = "github.com/roleypoly/roleypoly/src/db/ent/predicate",
|
importpath = "github.com/roleypoly/roleypoly/src/db/ent/predicate",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = ["@com_github_facebook_ent//dialect/sql:go_default_library"],
|
deps = ["@com_github_facebook_ent//dialect/sql"],
|
||||||
)
|
)
|
||||||
|
|
|
@ -10,8 +10,8 @@ go_library(
|
||||||
importpath = "github.com/roleypoly/roleypoly/src/db/ent/schema",
|
importpath = "github.com/roleypoly/roleypoly/src/db/ent/schema",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"@com_github_facebook_ent//:go_default_library",
|
"@com_github_facebook_ent//:ent",
|
||||||
"@com_github_facebook_ent//schema/field:go_default_library",
|
"@com_github_facebook_ent//schema/field",
|
||||||
"@com_github_facebook_ent//schema/mixin:go_default_library",
|
"@com_github_facebook_ent//schema/mixin",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -10,6 +10,6 @@ go_library(
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
"//src/db/ent/predicate",
|
"//src/db/ent/predicate",
|
||||||
"@com_github_facebook_ent//dialect/sql:go_default_library",
|
"@com_github_facebook_ent//dialect/sql",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -7,7 +7,13 @@ go_library(
|
||||||
srcs = ["discord-auth.go"],
|
srcs = ["discord-auth.go"],
|
||||||
importpath = "github.com/roleypoly/roleypoly/src/discord-auth",
|
importpath = "github.com/roleypoly/roleypoly/src/discord-auth",
|
||||||
visibility = ["//visibility:private"],
|
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(
|
go_binary(
|
||||||
|
|
21
src/discord-auth/README.md
Normal file
21
src/discord-auth/README.md
Normal file
|
@ -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
|
|
@ -1,11 +1,54 @@
|
||||||
package main
|
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() {
|
func main() {
|
||||||
|
klog.Info(version.StartupInfo("discord-auth"))
|
||||||
|
|
||||||
app := fx.New(
|
app := fx.New(
|
||||||
// fx.Invoke(StartDiscordAuthService),
|
fx.Provide(
|
||||||
|
httprouter.New,
|
||||||
|
httpservice.NewHTTPService,
|
||||||
|
),
|
||||||
|
fx.Invoke(
|
||||||
|
httpservice.RegisterRoutes,
|
||||||
|
newHTTPServer,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
app.Run()
|
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()
|
||||||
|
|
25
src/discord-auth/http/BUILD.bazel
Normal file
25
src/discord-auth/http/BUILD.bazel
Normal file
|
@ -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"],
|
||||||
|
)
|
34
src/discord-auth/http/httpservice.go
Normal file
34
src/discord-auth/http/httpservice.go
Normal file
|
@ -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")
|
||||||
|
}
|
43
src/discord-auth/http/httpservice_test.go
Normal file
43
src/discord-auth/http/httpservice_test.go
Normal file
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
25
src/discord-auth/http/oauth.go
Normal file
25
src/discord-auth/http/oauth.go
Normal file
|
@ -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)
|
||||||
|
}
|
29
src/discord-auth/http/oauth_test.go
Normal file
29
src/discord-auth/http/oauth_test.go
Normal file
|
@ -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"))
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,10 +13,10 @@ go_library(
|
||||||
deps = [
|
deps = [
|
||||||
"//src/common/version",
|
"//src/common/version",
|
||||||
"//src/discord-bot/internal/strings",
|
"//src/discord-bot/internal/strings",
|
||||||
"@com_github_bwmarrin_discordgo//:go_default_library",
|
"@com_github_bwmarrin_discordgo//:discordgo",
|
||||||
"@com_github_joho_godotenv//autoload:go_default_library",
|
"@com_github_joho_godotenv//autoload",
|
||||||
"@com_github_lampjaw_discordclient//:go_default_library",
|
"@com_github_lampjaw_discordclient//:discordclient",
|
||||||
"@io_k8s_klog//:go_default_library",
|
"@io_k8s_klog//:klog",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue