From a3a1654030191a6db226c1075635881fddcd61f9 Mon Sep 17 00:00:00 2001 From: noe Date: Wed, 26 Mar 2025 21:08:15 -0700 Subject: [PATCH] wtf auth?? --- authmiddleware/authmiddleware.go | 130 ++++++++++++++++++++++ authmiddleware/authmiddleware_test.go | 80 +++++++++++++ authmiddleware/const.go | 10 ++ discord/clientmock/discord_client_mock.go | 4 + discord/discord_client.go | 14 +++ interactions/cmd_roleypoly_test.go | 5 + roleypoly/fiber.go | 38 ++++++- static/embed.go | 9 ++ static/favicon.ico | Bin 0 -> 103665 bytes static/main.css | 3 + static/robots.txt | 1 + templates/index.html | 6 + templates/layouts/main.html | 7 ++ testing/testing.go | 31 ++++++ 14 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 authmiddleware/authmiddleware.go create mode 100644 authmiddleware/authmiddleware_test.go create mode 100644 authmiddleware/const.go create mode 100644 static/embed.go create mode 100644 static/favicon.ico create mode 100644 static/main.css create mode 100644 static/robots.txt diff --git a/authmiddleware/authmiddleware.go b/authmiddleware/authmiddleware.go new file mode 100644 index 0000000..0146d54 --- /dev/null +++ b/authmiddleware/authmiddleware.go @@ -0,0 +1,130 @@ +package authmiddleware + +import ( + "errors" + "fmt" + "time" + + "slices" + + "git.sapphic.engineer/roleypoly/v4/discord" + "git.sapphic.engineer/roleypoly/v4/types" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/session" +) + +type AuthMiddleware struct { + Client discord.IDiscordClient + + supportIDs []string + superuserIDs []string +} + +type Session struct { + Permissions Permission + User *types.DiscordUser + AccessToken string + LastRefresh time.Time +} + +var SessionKey uint8 + +func New(discordClient discord.IDiscordClient, supportIDs []string, superuserIDs []string) func(fiber.Ctx) error { + am := AuthMiddleware{ + Client: discordClient, + supportIDs: supportIDs, + superuserIDs: superuserIDs, + } + + return am.Handle +} + +var DefaultSession *Session = &Session{Permissions: PermAnonymous} + +func (am *AuthMiddleware) Handle(c fiber.Ctx) error { + sc := session.FromContext(c) + sess := am.Init(sc) + + am.validateAccessToken(sess) // this will remove AccessToken if its no good + am.setPermissions(sess) // this captures this + am.Commit(sc, sess) // and save our first bits? + + return c.Next() +} + +func (am *AuthMiddleware) Init(sess *session.Middleware) *Session { + session, ok := sess.Get(SessionKey).(*Session) + if !ok { + am.Commit(sess, DefaultSession) + return DefaultSession + } + + return session +} + +func (am *AuthMiddleware) Commit(sc *session.Middleware, sess *Session) { + sc.Set(SessionKey, sess) +} + +func SessionFrom(c fiber.Ctx) (s *Session) { + sess := session.FromContext(c) + s, _ = sess.Get(SessionKey).(*Session) + + return s +} + +func (am *AuthMiddleware) isSupport(userID string) bool { + return slices.Contains(am.supportIDs, userID) +} + +func (am *AuthMiddleware) isSuperuser(userID string) bool { + return slices.Contains(am.superuserIDs, userID) +} + +func (am *AuthMiddleware) setPermissions(sess *Session) { + sess.Permissions = PermAnonymous + + if sess.AccessToken != "" { + sess.Permissions = PermUser + } + + if am.isSupport(sess.User.ID) { + sess.Permissions = PermSupport + } + + if am.isSuperuser(sess.User.ID) { + sess.Permissions = PermSuperuser + } +} + +func (am *AuthMiddleware) validateAccessToken(sess *Session) { + if sess.AccessToken == "" { + return + } + + if sess.LastRefresh.Add(time.Hour).Before(time.Now()) { + user, err := am.GetCurrentUser(sess.AccessToken) + if err != nil { + if errors.Is(err, discord.ErrUnauthorized) { + sess.AccessToken = "" + return + } + } + + sess.User = user + } +} + +func (am *AuthMiddleware) GetCurrentUser(accessToken string) (*types.DiscordUser, error) { + req := discord.NewRequest("GET", "/users/@me") + am.Client.ClientAuth(req, accessToken) + + resp, err := am.Client.Do(req) + if err != nil { + return nil, fmt.Errorf("authmiddleware.GetCurrentUser: request failed: %w", err) + } + + var user types.DiscordUser + err = discord.OutputResponse(resp, &user) + return &user, err +} diff --git a/authmiddleware/authmiddleware_test.go b/authmiddleware/authmiddleware_test.go new file mode 100644 index 0000000..292389e --- /dev/null +++ b/authmiddleware/authmiddleware_test.go @@ -0,0 +1,80 @@ +package authmiddleware_test + +import ( + "bytes" + "net/http" + "testing" + + "git.sapphic.engineer/roleypoly/v4/authmiddleware" + "git.sapphic.engineer/roleypoly/v4/discord" + "git.sapphic.engineer/roleypoly/v4/discord/clientmock" + "github.com/goccy/go-json" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/session" +) + +func TestAnonymous(t *testing.T) { + dc := clientmock.NewDiscordClientMock() + app := getApp(dc) + + setSession(app, dc, authmiddleware.Session{ + Permissions: authmiddleware.PermAnonymous, + }) +} + +func getApp(dc discord.IDiscordClient) *fiber.App { + app := fiber.New(fiber.Config{}) + sessionMiddleware, sessionStore := session.NewWithStore() + sessionStore.RegisterType(authmiddleware.Session{}) + + app.Use(sessionMiddleware, authmiddleware.New(dc, []string{}, []string{})) + app.Get("/", authState) + app.Post("/updateSession", updateSession) + + return app +} + +func setSession(app *fiber.App, dc *clientmock.DiscordClientMock, sess authmiddleware.Session) error { + body := bytes.Buffer{} + json.NewEncoder(&body).Encode(sess) + + // do mocks here + + req, _ := http.NewRequest("POST", "/updateSession", &body) + _, err := app.Test(req) + return err +} + +func authState(c fiber.Ctx) error { + s := authmiddleware.SessionFrom(c) + + permList := []string{} + + if s.Permissions >= authmiddleware.PermAnonymous { + permList = append(permList, "anonymous") + } + + if s.Permissions >= authmiddleware.PermUser { + permList = append(permList, "user") + } + + if s.Permissions >= authmiddleware.PermSupport { + permList = append(permList, "support") + } + + if s.Permissions >= authmiddleware.PermSuperuser { + permList = append(permList, "superuser") + } + + return c.JSON(permList) +} + +func updateSession(c fiber.Ctx) error { + var newSession *authmiddleware.Session + c.Bind().JSON(&newSession) + + sc := session.FromContext(c) + sc.Set(authmiddleware.SessionKey, newSession) + + return c.SendString("ok") +} diff --git a/authmiddleware/const.go b/authmiddleware/const.go new file mode 100644 index 0000000..ea4b93c --- /dev/null +++ b/authmiddleware/const.go @@ -0,0 +1,10 @@ +package authmiddleware + +type Permission uint8 + +const ( + PermAnonymous Permission = 1 << iota + PermUser + PermSupport + PermSuperuser +) diff --git a/discord/clientmock/discord_client_mock.go b/discord/clientmock/discord_client_mock.go index c0a640d..6d6f523 100644 --- a/discord/clientmock/discord_client_mock.go +++ b/discord/clientmock/discord_client_mock.go @@ -42,6 +42,10 @@ func (c *DiscordClientMock) BotAuth(req *http.Request) { c.Called(req) } +func (c *DiscordClientMock) ClientAuth(req *http.Request, accessToken string) { + c.Called(req, accessToken) +} + func (c *DiscordClientMock) MockResponse(method, path string, statusCode int, data any) { body := bytes.Buffer{} json.NewEncoder(&body).Encode(data) diff --git a/discord/discord_client.go b/discord/discord_client.go index d3f832f..7eb7c8b 100644 --- a/discord/discord_client.go +++ b/discord/discord_client.go @@ -1,6 +1,7 @@ package discord import ( + "errors" "fmt" "net/http" "net/url" @@ -11,9 +12,14 @@ import ( const DiscordBaseUrl = "https://discord.com/api/v10" +var ( + ErrUnauthorized = errors.New("discord: got a 401 from discord") +) + type IDiscordClient interface { Do(req *http.Request) (*http.Response, error) BotAuth(req *http.Request) + ClientAuth(req *http.Request, accessToken string) GetClientID() string } @@ -44,6 +50,10 @@ func (d *DiscordClient) BotAuth(req *http.Request) { req.Header.Set("Authorization", fmt.Sprintf("Bot %s", d.BotToken)) } +func (d *DiscordClient) ClientAuth(req *http.Request, accessToken string) { + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.BotToken)) +} + func NewRequest(method string, path string) *http.Request { url, err := url.Parse(fmt.Sprintf("%s%s", DiscordBaseUrl, path)) if err != nil { @@ -62,6 +72,10 @@ func NewRequest(method string, path string) *http.Request { } func OutputResponse(resp *http.Response, dst any) error { + if resp.StatusCode == 401 { + return ErrUnauthorized + } + // TODO: more checks? return json.NewDecoder(resp.Body).Decode(dst) } diff --git a/interactions/cmd_roleypoly_test.go b/interactions/cmd_roleypoly_test.go index 869a369..c568332 100644 --- a/interactions/cmd_roleypoly_test.go +++ b/interactions/cmd_roleypoly_test.go @@ -2,6 +2,7 @@ package interactions_test import ( "fmt" + "net/url" "testing" "git.sapphic.engineer/roleypoly/v4/interactions" @@ -28,6 +29,10 @@ func TestCmdRoleypoly(t *testing.T) { button := ir.Data.Components[0] assert.Equal(t, fmt.Sprintf("%s/s/guild-id", i.PublicBaseURL), button.URL) + u, _ := url.Parse(i.PublicBaseURL) + hostname := u.Host + assert.Equal(t, fmt.Sprintf("Pick roles on %s", hostname), button.Label) + // test the command mentions tests := map[string]string{ "See all the roles": "", diff --git a/roleypoly/fiber.go b/roleypoly/fiber.go index b02463a..33a5706 100644 --- a/roleypoly/fiber.go +++ b/roleypoly/fiber.go @@ -1,15 +1,22 @@ package roleypoly import ( + "log" "net/http" + "strings" + "time" "github.com/goccy/go-json" "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/csrf" "github.com/gofiber/fiber/v3/middleware/session" + "github.com/gofiber/fiber/v3/middleware/static" "github.com/gofiber/template/html/v2" + "git.sapphic.engineer/roleypoly/v4/authmiddleware" "git.sapphic.engineer/roleypoly/v4/discord" "git.sapphic.engineer/roleypoly/v4/interactions" + staticfs "git.sapphic.engineer/roleypoly/v4/static" "git.sapphic.engineer/roleypoly/v4/templates" "git.sapphic.engineer/roleypoly/v4/testing" ) @@ -24,11 +31,38 @@ func CreateFiberApp() *fiber.App { ViewsLayout: "layouts/main", }) - app.Use(session.New(session.Config{})) + sessionMiddleware, sessionStore := session.NewWithStore() + sessionStore.RegisterType(authmiddleware.Session{}) + + app.Use(sessionMiddleware) + app.Use(csrf.New(csrf.Config{ + Session: sessionStore, + })) + + app.Get("/static*", static.New("", static.Config{ + FS: staticfs.FS, + Compress: true, + CacheDuration: 24 * 8 * time.Hour, + })) + app.Get("/favicon.ico", oneStatic) + app.Get("/manifest.json", oneStatic) + app.Get("/robots.txt", oneStatic) + app.Get("/humans.txt", oneStatic) return app } +func oneStatic(c fiber.Ctx) error { + path := strings.Replace(c.OriginalURL(), "/", "", 1) + f, err := staticfs.FS.Open(path) + if err != nil { + log.Println("oneStatic:", c.OriginalURL(), " failed: ", err) + return c.SendStatus(500) + } + + return c.SendStream(f) +} + func SetupControllers(app *fiber.App, dc *discord.DiscordClient, publicKey string, publicBaseURL string) { gs := discord.NewGuildService(dc) @@ -37,6 +71,8 @@ func SetupControllers(app *fiber.App, dc *discord.DiscordClient, publicKey strin }).Routes(app.Group("/testing")) interactions.NewInteractions(publicKey, publicBaseURL, gs).Routes(app.Group("/interactions")) + + app.Use(authmiddleware.New(dc, []string{}, []string{})) } // func getStorageDirectory() string { diff --git a/static/embed.go b/static/embed.go new file mode 100644 index 0000000..0085f72 --- /dev/null +++ b/static/embed.go @@ -0,0 +1,9 @@ +package static + +import "embed" + +//go:embed * +var FS embed.FS + +// hi :3 +// dont look its rude :c diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..99cefdbd2a9b1242f61fd7b83f862f4eefceb28a GIT binary patch literal 103665 zcmeHQ2V4|a6F;npv0&`N0T$rspn&wIqKJSX_5$|UK!b{c$^jZlBxBF(^%$wI{-V=$mL{&uerzJ8F z#n%#v2II3-DoaZ~(-J+zcOD+{w3kR!yP=lI+FF)gZzvKu_QaD0^0c-{bTdXvq>U@R z@2g58(e!y*qUoLd+tsMnxEfwv!>4_lAi7KVjI!?i^?Kx4tAkG)&*1S_o}`9++xz~6 z z_WiB&wU5O0Vs?vrbqmUx-C^v2VE1138|zeT=vqNEYL-Z(+e{=n{+Z~o4U(H}A2kro zy3*mU*F4*$8>eXpbjaNBU+r7X66yzC=y>y7>%ZHqkTlRqJ{tJuaV62&H@+{ok8zyb zV#><{Hj}-RgEuy;d*}zVlrQftTzh8Dv#M2Z)YTF>c#A}HYKx=;Dv54D01n5#4*j*0 z*`ff6&67T^DT}l3FIevGH@nZ$@D&GtJ^OO!=$8qPO|mwwjaq%NTC#ql0j)w}o_VYxigM?3HXhD|>jtH}yr=D&)zJ z%ig7`wBo9+mR22{5;hK*@OIhSmIsGT4UUT5`2DgO`<<>~@B(S2oS_>9_Yg<84#71g%Q+ zewKYH>!E8%PS6YAm&*b}7QLzN)u-Z(`tKAlmG^B&kxSiScb~=0dbq>tgy`@xQpK<;V;F{(9+1+Pqtlv$I>JjemY2JU#11PUUxsli1tdSbG3i zdolE!cwxQYJ0EMZaA3@eY4&#yo;{KEKmY8MaKFglODooC85>&YWF7fpLBz!~sei8z z{@&PFWNrC%4~u=RCDXK5v^=)dV&Alf-JbeCn!mmG%SR)8mnQb#HqE`s)PVtUk~i(2 zw|HPNc8^~A^jhrY-$_%SwH(J8B_1v(4HLl{OPaFUYqL=9du$qwoUx` ztc1qXw_RDkUg!LB*OkugH%0XO{gGwP@81Ob*85!acwO7X`;X?DPC279pz6qTuC~*z zF0QHL9KS6k!fwa)Gq>s{duSO9wd?$Ig&Xx6X&ttZDHA7LPPc3g^nU2McVbZT&EPi& z>!}cvrR-hWzv($9 zWZupt>nn@Tp1Iw><@r_q={duux`x+%cBt8o7oOHH+7}?r=&$Z~-OfOqfj&bzXF;Ck z4;$Uqo_06GcZjpq{2wX@x15=s8qzCZ&d91mhoo;gTF)!8>{lc?%KRCUkbHa)iHMdLLI_oDi>h;Z{)wXH=F|}Ila5!rlI;-EIH6xl#cpI4H z*wC!bj-F$DS5|8>@#uQ}pvXS%Z&yBSDi%HQa!RuMZgbjV$(ZOJd+$fIdzjgD@SoT3 z6i`DtR&?rYwy1&O-GjrLOgS>C=NNa({jI|-udVxWLDZk?cc>lfB0)YK_Y1c=FJlY^y`c9OJ8oCdj`P9I5 zjrO4tv(~k3HEwo6RpD^$^$SiKFZe7?`}gf`7W>Z3te;x7-OtXVJ>TTi^ApWF=98HA zx=YV7uakeSU{L+C-hod9G;pB|d>T!9G2E4|Hn#6%DK`T32&n?2`mO?*^< z@v*6K`G{rDF)Po7TI`Fhb*<&XoTt?fH16b_usf)&+uDPEe}RfxETh+4-twmlbV~>uq>vBJTMTu~((cNs(qWRqubi5q?UI(@YMm{j?o*8>;y4RCG zZlw>dKX%)|$JMSSIgfgMFvg@+^Ddp+?$P=&Z06GgLky?1d%g3)iZ3npZTHnl^vXK$ zV(PUMt6v$MwcI&q#Op0T>RejcYtv6BCU({mwo9BTsXDIl&s{B721WJ=3xEdsrFOk` z@UI#5Q^$Ms?Y{n>!TqDtpWj$|B+2NgnfIjgjVFdy^w>ZA-13e0oi3->cz*85pI-k= z7fGtrl8*G*GizVgJngiB(jjv5#ASix<<`y@x}X*8;Iy1tqXTOL8c+MNZ7aQR_a}Pw zI=19(jQjRACy!lBtC+pA`@onXDd9ciUwx5pW1@3)=waxq*}>i~-qcUUYaERm_qF8f83>kk>Z zHF4L?EibnDWJY_x$?~juI)A%vpF%uN?3uiZ)HJC@-y;XUIyZZgwqfdG^MtH!_j_hv zOdhx6?Xg*V?;Kj`H6zXLk(F=uoWQikckUdSTr<@BdE1<|YyWIByMF5B27y6G#&;iL zX7%5_F;_R&8lOFO&7rr)e(82GGWzG&NB2nBK8iq4)Mc?hYkRfB-QN?|cHWSEW#-XK z_pjc382sk1IrSf$nf)f?bnvRK4_q(oS>EnVW}Wor$1nc!m7DR;UxaVie=ySO)PesU z8a6An(Y@EdhvfEw1n>XBvBs}Kdqej;sCl$mg4@E4Nw#U-9j>29xZi!my$%+?S4%Le zX7u@X=PdJ$gEl|dKW=^E6Bnan^@lYcaP>`+(aaZb6MJ{vH#M?XP-?^ByQ=7SPr5G} zJaE^8n%T?hGS*#~tG`6v|7pfNm*Z9={f0Gte&~P3Gk>h(S3$HtB4Xp3J;vvwUofZs z_S+JWwY5P)SPlC#X`P~0gfw}y?U#m=mq?Be8#MB(lapTG%)g47E#I`vciiPCQ=V`1 zdOr2PjF+2QIQ{qZj?M7Kwf~)K*6YNVr($dEiCb}fS%*1&w%+f#<(Huup0y2k%{p^p z`SdwkPyOVaameLT@;}F(UkmpeyR*w9*Gu{r_DqUw67BYO>W>)-fz00IGg`BPeU4Su zjqAE*^@Y{ZR~n~H%4quL-rFM`qJ01Te)hGTT`u*EK9^QJc4bNQq>XFR_5IwfB1W%T zb@IsYbE)5D{_NlBWxuWinSryI3A?@?+H1hK`P=Kw&DR_pH7lyij1b>BHE*^ue6xFW zPNT8=bFS~1J>4s2L*|y~dadP;h zT5~RbKm3_akCd2Y?bD@`wgop|^zWe%eUJHL&aY4Uvj59(0-5P+H+dgiWz%J2*NuO! zGwy1pzt?!^sfNt2ahvZwh(7ekk{-6bT7)l*yKZwaW?PEy4!1g~)7&qI-|#f8d%@;< z^3u-YRKJ{{M-x(fr=7p#UFFK^mAuKkMfTbDn))G9f;_OcOooc4d;cgDoc3x*&2p;q*W zXs^US#y#jUf3S0OjPJ0-IgWjHcbV~LjmJwDyVl#|cHvy&tA&B#!v{2VeUTleXScCy zEt>~P3nt$iv_IwZnEut%wsyUEUGJ2czUA8rc}5vr^HU|Y>zE0b%4p>KCNU4mUHewcV;Rj7=FEAPH6UbHbHi5BZ?8rSv1h^s=4Vf!yYxFyq-r?YWK(P!>^zCCEicDny|IefNL&U`t)G43)@p;VtS9(%D11gW^&EL$q75dQikuiKQ+euVW*7^`@dYDyyfJ&dbeJs zT$U{FWWB<3Uj5a#c8-4feTQk`gAYFKv;O4u_?vwa_uIEh9B<-kd+m#aU9QjT9UZgu zNyGnsdFmKtn$qWd_?zBct6q=Ij*qt6eMYPL)x9UC&NZtoviW0g!x;nJ<1Zh4sqcHS zUIW8ZKRs&lO_#(Y#@~+_FV!E}>*36fkHThrx3Bx7pqy1UyX)6YsC9M9`Ub75cNvhg z^umcNv8lap_xmGX`_7JBMY^MyFMJpKyQP|ipQLYm+WNQI>J=I%!DlIbfsr>2&1HXD zqB>uQSjfN7Tr?%1DFIChe2@|_l^R$9)&vu&fxU5@-Y5Yk8OMva39!#7UiXXu*Nx-E z=>ojOXQmK56a1E*y8)!han&eJyi$OL0y>DM?h>hifdF5AkOKS2GS$fcBNM2K>}ce# zVu6+MpTj;@04n7|TumYsSLXm*+X&F*U=pj}9_T=j#OepY&c+Ea2eyB&0KdcbU*Ldu z+yXKMc!ke}+5i6pzoqBy0Jr710o%VxfVBcTh^CRov3f2%;`A(x<8>S6Li{CuQz++}!{v2gr3F zAZ*{CBVAV8@RFp9`kI2d3Ei2HJ^BYc$Th0m|7ql3pl)mAFS7%h`p?>d@>&1UpRWjD z{4N99HzU}!U;(7~{IdWl`23Fm+4x+rosDhE7|S7%HfPKxIOk%#J7W_&w+!hU2lH{>Je@4P(AD?h8*)DiaPtTI zgk_A+I%TC}ppm~!2Q>1R*#J%bm)on4wEi1O#gzdG2jf_=JM7%I0&Kx&!MH~JqA)us zi7{eKVB(y-y9i2zhj)bZR6qGWhdhn0Q<}V_TIzy5)aSzHyGCD zlieq@d0L?Dr*^^9`yS(o_JT+IRL0Xs1`nUBjBDT#9xg!#8OL6$d9>=Yn6Vo$jYo^2 zqxrUh+6dDqM~3<@9E@Z2m9-DFc9AB>HC(A5#bFfI#Tp7wyR_N^$h{iSiAStB5_AdW zO??_=yz%#`O*f5d#lt@2JH9=qw!L+m^g?A;SbJWo_u4$+`x(^dApNlG4}Flsa&&N> z{(*696TS?JP+l|##_0lG|A6dSp9uLCfkvhCr;&dF{YQ=bIUAs<{~uZX$NEGy4u-LM z`mjI02=E6!)4Do`*Z5qZZKH8B*S6EORo_L7Q}<0g-26^4?j6tbXx%!aeAs`Yv+ZLZ z)NkPXCFq|xciq7_^j^WE#jtS3YIHXqCX-DHEHim|{);Pz2l%{BfH<@RdIG4`_e`)x ztqu8`Az!Yp)0JpaMmvAsD!dQhCQ{qz?we9hfh71C0l%pT&XJQ<4Ojgn8M90#sL5dtfTnsRCf^DFgJNFY@L4 z1CkiyEa7lUwRy7n_wq8HFI|1~YV%k&Mxb#b;MVClzt-Ryl*E`u+wv%&KR{zb z4m3Y-T!3)20lEUH&3^zoP#xg=15Q2Hj&<*tlFq@i-R&1;x!#pk+B`b^&G(z!J0=VI z0$3}FZmHP*E2b%qF--vK4+{GO>g*3{sh-=fH*G=EcG znkzw)2b*(18O8xg z0{nx|)CO?S^al#<3&5Ae`vK$JWfu?o(C_#-H1Z=-=1T_qzZH-Qps#4RODYXL5Cg*g zz<$VH*dL(2p{75mLK(REpW^R056GuIXr|B~NQUf%{Q-^qmB^p`1%z>r6fddJ-WQ&Z6^B9g9s+!(D5sK2 zL-rc^E0HhiL!os*Bmct7M_Kum_xt5s{xm<(kOS5WCIRy}KsNsYSw2^3lvKFRwE zv8Rqb6R|eXmPZlRhZ7LTKT3d_C1vlGy%&050tn{^9zv#Ee<0(Nl&^|9PqF91eR9Z` zys-WN=7Dwt!oBV5D3eo3-Pg$f{q37Z{_kIg<=M`ZbNQ410L>Aw@bbzS3yhZ`Gahou z5`g*yn*M-78Nm0NaqO{obk~-_?K5_P4P20QRnvgZ3gSto_G5iro~(kEu0&TveG1f3K1MyYf}0^BVc9US`V5 zv^?Ga$mCxgHh=}}e_iwgLUWNB1G)SwfTlk{eF2&eK%6xPyZ+M!vE(-F9&`%rK@QSf zEct81bo*c~*qVdT9)HNs(1S``Ux2RAgC3B*us=X^MVkJgLK)DWdcv4|+H+As{&~`n ze>cHefX9cFxiD{y{EMgC8u=Hm+&>@)5~j3pSt%2kH+1?Kxo0Knj^<3ZUr^pe=Ch zzJc`#Ain?twFQOQf9yd&5$BK$;~=!bW$82Ypc=rh6`(&b3bGgW2cCZ{b@+YwaO$=> z&w7BF(s&V_Siw0oqso%Zg|ukoU$k~jBmbh6)BDu1vi$7FDu1CZpwF-e+Iic9%xRE| zus!%B{RdnEf8))TA@gHnI#fPooP*&}|GP~mQ{wuai&;?zE{=hEC zT-YDTLHeWX4`2`8JbR1Nxig&C|OqVtCmuZ5g{>#or{)Fp4#hUT4=jabihP@ZY zoc3)3z}tf+3jKkOCMTQLp!E5@EjadGE#MO#3=WaLA}pV8(=**g&=;UNf^rjkuB!hRvW}OxK17$Y$ z+_A?`j8#|&57TJ7Lf8L|;&dY*dz=p=LwS^!x@p$PU)?2IzTT&ifBBM^x~YZyDZWe? zd;TRCcTQJ%f1pgno_k^c7teP0c-Zy-R@we9dD?^K@p_(3sJkpbhW9}iJ_m&T zfnf^$fe#jY?$!1&WPgW;?SLtaWU?s_X;b4x8b_3bpsJ|7CXn&ytp# zy56smzq(4Ye7;8`|MDp}bx{rZ*8qgE=Mk{~!r1f2NS8|NIh_UN1b;6M)*}Zhv;Pm! z2HX@tM_uLeslErgP#56)1Cad>3jKi+i#>Plx`QDfKMuf?$me2QSe;N-8W6x8^wxe z00RZ6uC8*>yhm07(1pf;us^U(p+8XU*mKWzxB0O?jrnZ{PbyU2iu1ijaXK9gq}tj7 zRM5yxy(OTLzk18FyuD2$|MDg=_0kLZYXidA^C;MVVeI(>q=m8PMUFkD_+IAX}o`x1MI&mY=1QYwA9PJ=52~409`Nug#CeI3jKj1%ilHdcmAwbI`>t%f0y`o z#Q5<0yW+F4B7K^4$(Ddd{v}(!W&Q0M`ImJGl`MUbKlKNM=bn#Kh&_Lb_JE5!rz;z+aGXmbIaa7bWS=4+d&gEAxlLW7na^-9H%!*025W|(fq!2OF$$4 z(k<_@mYYWYWlbWbOAqAF$DX_ToUdgwVn8Sd%Lu=5u>a(<&EdI1dvG7=BLa+s?bj8c zTIq75$?bhgz*MTE1z2-1iPiOkYz5je;V#@6QIlcXmVF)2|y330(^e} z^6v)OC*?vnO5n>!etnwxeRT@p1HVfn{|_vH51hS5{vTKXn(Q?t@ClUwqx|nJTO1Yn zA1X=T;7ZS3_JoRQ+1u$IvK&=R=l)zIstN*BP4m z;OA-3=)H(jQ$6T#Z@q8^*QLSX%ee%2)Ih(^!kGK3E+J>uoJxh4@kyu zC}xRxRpg(1Vd#6>za$O(CjzV3cf{$1p*$@CW8-+;>cAKHzA8}J0CW)4$XbzJ1>UOx zkcnO^@E;2P7ZTk7t>+2ls?N`u;5`Z00N%+Ds1ta{yr4m;tpj6@vJ#*ziPh7EJRETK zm&A(4fd4h%`!=AePm?O;^DlTm4(tT)aljzMSUq#YSh0a|oK8)k#OFuByFMTVmV;N4 z_dkFtJgD=x*~pvp?I3uEKIpg>NqJTHk-VOxe(TjhKBhn$@csj?v@U%YcnS!3(qoKc z^%+Tw5o0ph9I*^75XWZEm_<13D>COatLpPek-qZ!%Re<;Qhr~N70S4f|HS!^OlTU_#Uxm-o zX6*i~77_mJUj25Vvlxe-s}SSy3wv&-M~A-|uXcBs)@`2haR3w(;_mY|*#kh7l!tzEj4_@B) z!GC%l{yp%$NBirHyYG3%y<;-t*liP@T_=l;_-ZU;HL3?&7G_ZnY&j%Msb<}uEl_d$La)X#tG4Cu@Q#-qbk#bA86_|1(G z%U6G}PYmgUYv6Be{jeO}h2vZCN-|3a|6}EPP^*aiSA+cP;@Ne;cHk9kZ#K!CVv?;# z4q)?j>bi|_4LJS*_(y&4eh52pp0yFQFFM%=bF?dLdy%^@_AAhy1tg*j#se0xiSUv#0l6 zw%u^*wwbXVG>NrIk{Agi$*Tuc7cicpzN(%vwT(s81M=6fwi`6DvwO#sbl4y|E2zxK zUPy~qySuDz+6|a0(*u+N_!WX|VjB4Om+L_lW%dBPQ$1*C-*1|wdxyX0dA7f~0eMm! zEA4Snrpm#q?LVwtbndc~u^TXr)dLgSFF#igs9hkN9%mG%hqzh2BDah1<7LkYKrByR z@J;6imf3TH=*$4JQ7s2Y%k>~{KLO+F-SYVbu`>B{_rZT9pdR?QMtt)W@c$D)XCstR zJ-Ej>_FRL$Nd)=`F2XuMpVNQ~D1*+1abkO*D#xd?E6RY*5knbdaOXtOS)ygC9&q%y z1|DG?a9*@U0p~=6|4HB-=R=8$yic55555HdHo$n~l?YH7lsP@{Y=498BiIg#VQ4&N zGRa&~4-P>OwgL5&$zAb2%7NV{9%V2VypuinVCRG&??Uy@q4zR&ESk@LGBkdtx}c-T zpRzRguS{hi*MkwDf$Ra<#4@J`ZvH3PJr*p+_$tbu;_0(6pV3Al)inoHo-aTd)CR~V za_0o(+2cW(Y)a!k)dOk|tO^(>fd6jb-2+g0oDANnU8oK{Ae$HperPW&>LZodzQ`(< z8I70FH?T#&$ZAw~%uAW&(F54Qg}^jGX}`FjdVw-v_lbrc%m#gA4@&Kvh@zGU%_-Ro z8-X*V^6n!FzBd81k5sYek>z^uInL0sfF1;cri}odtz2gGz%Aee;}ZNU#)+--$R9S~ z4sZh~_Iw$q7bu4YfHnB$_6aXD_Rs_S%TXCvjBBUJKh1GazgQjo)A^cJpa-UOo*K$w zD?sO|eRw^pM{f8365-Txfr#r0?7Ho59!& zANo!^fc2Acz(|e{`bqv zq1gK*Y|5fVXx1e{R44MH{AGm!o=`0ns@m7>~xdbLLG5Jn7WfyGg9*xk>;bn=UWwa+GR8mYL38j=?Lrbj zZD}M!G=uK&^Vvs`pZ+CDw|`XGd87!TUGBWMwp# z^nLKJ1z2PLz!SWa9?<-O+VsG7U>su=76Kody!-1K#_0?LLQ9%IW$%~suLJ(6f3Osw zHCO5%yw_Y3ohMK8pf*Fp8S}C2;kRX&SN?Ij0nOud+bE;2wC4=}IX1N@U76w3$0&Hpd+x%;jtEYfuI%uf5ZHIO`(>A1_JHg@oux@WAwvFfUgli+nVH32|0#`91Mg%HY3QkIs%~*E|IMebj+J(ceFaK93Ig zM=VX5GyR1Y2g-rYUZA!2Xz)($LcTs2wgaZHu?C9gDS-E%0b2L0Q)otI^rl=7XziWW zTxso{+68JGSv_zGI>fZ<^8;fU(TVkoWqsyl@+V(N>^Z56xFsDApiJ96WyOzjs3X^d zY2cmofc3#3e^5GK!EC&H9(yn29qXO3I^OcL6Uv3%E2tdgdO&OMX3&FR#9Jg{Z}Df= z;eB73OtM5@ubCqMiRk-%i*nEd|A>=mST5{dLHu*yqa5m!9ys(`oH@VNpYzXpl}ApVa7-4cQO)>q~iBeyz&phZl2KJ_Ff{v>4e7 za<8%nwPf2xfB@Np+!gz;$R5nS%3dJfUjp}KFW|Oy$-T-Rl$QwY;*!Ss=|{4~s#xD>fNnMZsP1Ae-i|hvgeoaMuiZn!DyeK=j958E!)f z?s_S%KsV~wd9V2=F&TEZ4P04{)s9PaY8 zBd#scH<#cU#Nz4IL(Ix&zz6B8fcDye4d@sO+GhhlgMMmH$!{#L&>jp#BgKVL+bGmYg+^yRQoN-33{iy5ws5iid`?D zE7-%sK#ccI2S}%BJ~Xdh#5Q5qORU2BU`#cE^`CO;zL9ao7?b8#Xl|LEUxtsV3u3)p z5DVF*w%)?eIa9fi-zAMjA3(1*;o1pwsC0%QVzlc36r&go9079c zB4WfT4v=C$tVayS+-EFev{xe*U?<|ykFv2LY&?3~N4axH@DcTFcZ>1xNoC{EU4jlG zhGqq0-)}Y>Peo&CcHLasR47;4hk$f+G3rHs*ok_8(t3e7V~Vl2lE;`6-BjkX_p*B^ zNKkIBh^_vPA)g(LYm1LIkJ;F`Rw3UbezzZdz_IoRnSx#+AO9f#-$1)PpbZqNQ=q>V zem@m>1du-R@3KL8Q@PQaix=`%jQ1}dFNgz|^C63Gi(=<>1o;vTREGBu>l+ICn-o@^ zLH}p?tv`VAp!fx1`E$r8$aavFjq7&vKUMtlE=*ReAM!)gk^PWu0y{THdu1TTn97fM zpmFQZkj-e+huTy>3*rO)+lGkIb;0jb;m`IAzRhphJtnZuSG+P-O~*X2XN7a{POK-k z!+NrXa8Cw`wLFMDVp;$cb6p`n9zZ|siC!6KiTSEc?!K4aYmKj1JY!wdqvHj{3XVd( zXu<0+=%@DY8scSf&YD4ia|NKC##>*y`Tf<|qus4Jpn>M2OU0K<#xL!Ua36Bm!^XXm z93*VrX500D(Z*41OS}BaU-%v74;o?q$Q$oG4Uq3esrn^)w!6*lX<$CqC(qhEXg>su zL!4r5AYVP=_y7-VoGxf5KjUJp6XLmI*Bxp*DCW{U(o?8M^!cbf*79?X*8HxZpVoY9 zLJt0z*PaDhXkNe6ToNOu9hfH?g z8Jpq5h4jn%8L?tR#5fcx15PgBfn8?;?SBDeNJ^6xd3u;gv<9v!LIAe)3VI{V+NsAF2;jr%Oba zC|0AzS6>V1zlu4S?FG@M%J<-b{L83LrvfF?K2WR$?L{Ief9zAIzBn83!1@V-elF%% zt@Xh*;22`Z<4Tf#>hslxE_IUk`P4doC~5jReFG2FPPc^|D4vD-d}`^p(mrRjhlfCa z(x5+kxqQ`m1rKT;KR9>$ncX8#K>vH&pMrP>{iI7ZAO~OQ(hSIe#z5~S2ikK_*q4Uw zJPlv5`32FSj_>8V)Dg7r1>S4?(5la3*58{ef6#vn^vA1%&cfb7asaGB|9g!eWMi>B zG5dQ;!%rQwlP-OR_Mr`QX(sqkt$m=mRP;eDzYfM2Aa~CVl>drm@%p8<1`i&{E_DL^ z6jMPw6seu|YD;?zUXaZ(=Faz$FU5E8lP*=CG4Md+2P@D{HNXCG>{@|*UYBjZFxTH3`&FA1C9jhD4S0YrdAc2Fr`G9t^t=hJO z$NO824yH9+TDPDzeOkvo3A6$IW#;S%Mf$lk)=(N^eiO8c{|AuHTmt3-jk({H^(%Pz z9I%Gp!Vkb)xxZPN)A@qR5wz1)`RifXeNaGjkbju$`ci() zQMkS(zR3SdD!(2E-<0OA87Lo0zZ6`_Ps;%C0C+zwZPom=utr`5=e0C}eelAVx(|Gs zMxwrvF9rFqz|RN!CC7x0t(C;PyzJ~B4!}`6-^$Paob07%sm=92%^nT&og0jy<-zi`Pd~dp8 z9<)g7Vc=N{>)_;LGaEPu$nu4s2l-;q9`3Y{z8&UTX@7f%K1*@M84&QFY4KHreE(AN z*}%C6GJ479{~(oiO24Js4XhecKvFX;WyW5@hxz+V33Q-*!N^|NR##Ia{0 z_6xsQ^n4ZimQ+6Ee?tB(Y#g<0t)1j?80BC|`Yo&gu%{yUVb?K>Y88RLT^7Y)=iy87 z{Y6aEn!gLmkd150rC~Sp%mg&#Stkb#S}lisUDw0=_SeX}NaM$%&`AC`37H1`+ z;VJH0axY)T|oos1kL}C zQ3nmQ)*_4VCq2-KC@5dW??HpMTo&()r;B0>xZgPR`jPd!BpE>dYZUV-EG?rUb1dHV z6nJM|g3F_@t8?dHbH~o{;@=9(FTZ;-S!6~ckH^KK!L`%xxqC|%BY&a^G$779a{}_G z{g6~D2UqNG$M10*FFu$bk45=TPD2#(r*-Bc)8N!&1M-*ctpfS~Sd_d9`VDA6-OQYb z{O=W&2KzpXcsq=F{+R{6yQtsGX_$ok?W z<80KsiI^)awCyV{|J-wO8YW@R@3zgbk#FhjC3(C1ha^^~X=%h26AgC#r%Z&sqZk$P z4Z>Ja592#Mwdx(mbADI!t?Ni+V`k{PZY0V*-+o_y&#LP?q5<`e5gN z@J2dBoTfNvo&+-#h)c@>-ePWup{T)WG*5wP33jf6pQk{tu%L5?F*ktoCdDlv%LstR z{0D(wfdqiY?u{sR0)Bc8>M&mbm z_Pc?t@WQ=&v@amexnj?lCrTS z=E>Q!0->{{uj?^hW9guAqI-vn#;|J_0VQL^40aqVkv4mSx;`Fb9kx7BA4KTG74049 zuiK3Hit218Xy^Ug84VRSBLH%ucUvr>P7)5_a277KBXxJ=#p8E`53_-(%!swvA zP4N6>?is}^-a)*aFX$aDhm51=(-fZ*rtuC5o=?v29kic1WNs6_An|^od2Az<)|H43 z_$8dhd=$;S1i;o8lwE<}L+2!@=Oc~cbl5hU<~n5-30HaTmg3`K;06HGlh-auVS^3> JH-Jo$=>PH{+42AY literal 0 HcmV?d00001 diff --git a/static/main.css b/static/main.css new file mode 100644 index 0000000..c0c10b1 --- /dev/null +++ b/static/main.css @@ -0,0 +1,3 @@ +body { + font-family: "Atkinson Hyperlegible", sans-serif; +} diff --git a/static/robots.txt b/static/robots.txt new file mode 100644 index 0000000..2b10c71 --- /dev/null +++ b/static/robots.txt @@ -0,0 +1 @@ +# Hi cutie \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index e69de29..774fc13 100644 --- a/templates/index.html +++ b/templates/index.html @@ -0,0 +1,6 @@ +

roleypoly!

+
put a role here
+
put a role here
+
put a role here
+
put a role here
+
put a role here
diff --git a/templates/layouts/main.html b/templates/layouts/main.html index 450456d..679194f 100644 --- a/templates/layouts/main.html +++ b/templates/layouts/main.html @@ -1,7 +1,14 @@ + {{ .HeadTitle }} + + + {{embed}} diff --git a/testing/testing.go b/testing/testing.go index 607ce0e..8c68c4f 100644 --- a/testing/testing.go +++ b/testing/testing.go @@ -5,6 +5,7 @@ import ( "github.com/gofiber/fiber/v3" + "git.sapphic.engineer/roleypoly/v4/authmiddleware" "git.sapphic.engineer/roleypoly/v4/discord" "git.sapphic.engineer/roleypoly/v4/types" ) @@ -15,8 +16,14 @@ type TestingController struct { func (t *TestingController) Routes(r fiber.Router) { r.Get("/picker/:version?", t.Picker) + r.Get("/index", t.Index) r.Get("/m/:server/:user", t.GetMember) r.Get("/g/:server", t.GetGuild) + r.Get("/auth", t.AuthState, authmiddleware.New(t.Guilds.Client(), []string{}, []string{})) +} + +func (t *TestingController) Index(c fiber.Ctx) error { + return c.Render("index", fiber.Map{}) } func (t *TestingController) Picker(c fiber.Ctx) error { @@ -54,3 +61,27 @@ func (t *TestingController) GetGuild(c fiber.Ctx) error { return c.JSON(g.DiscordGuild) } + +func (t *TestingController) AuthState(c fiber.Ctx) error { + s := authmiddleware.SessionFrom(c) + + permList := []string{} + + if s.Permissions >= authmiddleware.PermAnonymous { + permList = append(permList, "anonymous") + } + + if s.Permissions >= authmiddleware.PermUser { + permList = append(permList, "user") + } + + if s.Permissions >= authmiddleware.PermSupport { + permList = append(permList, "support") + } + + if s.Permissions >= authmiddleware.PermSuperuser { + permList = append(permList, "superuser") + } + + return c.JSON(permList) +}