From 64cff2bb92698f7a2ea9ee016046c37c818eb997 Mon Sep 17 00:00:00 2001 From: Daniel Aberger Date: Mon, 9 Jan 2017 21:34:37 +0100 Subject: [PATCH] WebUI sessions implemented --- main.go | 2 +- static/css/style.css | 71 +++++++++++++++++++++++++++++++ templates/home.html | 4 ++ templates/layout.html | 17 ++++++++ webserver.go | 98 +++++++++++++++++++++++++++++++++---------- 5 files changed, 169 insertions(+), 23 deletions(-) create mode 100644 static/css/style.css create mode 100644 templates/home.html create mode 100644 templates/layout.html diff --git a/main.go b/main.go index 433f778..3393ec0 100644 --- a/main.go +++ b/main.go @@ -691,6 +691,7 @@ func onMessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { // Find the collection for the command we got for _, coll := range COLLECTIONS { if scontains(parts[0], coll.Commands...) { + go deleteCommandMessage(s, m.ChannelID, m.ID) // If they passed a specific sound effect, find and select that (otherwise play nothing) var sound *Sound @@ -707,7 +708,6 @@ func onMessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { } go enqueuePlay(m.Author, guild, coll, sound) - go deleteCommandMessage(s, m.ChannelID, m.ID) return } } diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000..dda9193 --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,71 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) +*/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} + +a { + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +body { + font-size: 13px; + font-family: 'Open Sans', sans-serif; + padding: 0 30px; +} + +header { + padding: 30px 0; + font-size: 15px; +} + +header h1 { + font-size: 20px; +} diff --git a/templates/home.html b/templates/home.html new file mode 100644 index 0000000..248a28f --- /dev/null +++ b/templates/home.html @@ -0,0 +1,4 @@ +{{ define "title"}}Example{{ end }} +{{ define "content" }} +Login with Discord +{{ end }} diff --git a/templates/layout.html b/templates/layout.html new file mode 100644 index 0000000..81496a3 --- /dev/null +++ b/templates/layout.html @@ -0,0 +1,17 @@ +{{ define "base" }} + + + + + {{template "title" . }} + + +
+

Example

+
+
+ {{ template "content" . }} +
+ + +{{ end }} diff --git a/webserver.go b/webserver.go index 6265db1..46a7282 100644 --- a/webserver.go +++ b/webserver.go @@ -1,58 +1,103 @@ package main import ( + "crypto/rand" + "encoding/base64" "fmt" + "html/template" "net/http" "github.com/bwmarrin/discordgo" + "github.com/gorilla/mux" + "github.com/gorilla/sessions" "golang.org/x/oauth2" ) -const htmlIndex = ` -Log in with discord - -` +const ( + defaultLayout = "templates/layout.html" + templateDir = "templates/" +) var ( discordOauthConfig = &oauth2.Config{ RedirectURL: "", ClientID: "", ClientSecret: "", - Scopes: []string{"connections", - "email", - "identify", - "guilds"}, - Endpoint: *endp, + Scopes: []string{"identify", "guilds"}, + Endpoint: *endp, } // Some random string, random for each request - oauthStateString = "random" + oauthStateString string endp = &oauth2.Endpoint{ AuthURL: "https://discordapp.com/api/oauth2/authorize", TokenURL: "https://discordapp.com/api/oauth2/token", } + tmpls = map[string]*template.Template{} + // TODO change secret + store *sessions.CookieStore ) -// startWebServer with the port provided +// startWebServer func startWebServer(port string, ci string, cs string, redirectURL string) { + tmpls["home.html"] = template.Must(template.ParseFiles(templateDir+"home.html", defaultLayout)) + store = sessions.NewCookieStore([]byte(cs)) discordOauthConfig.ClientID = ci discordOauthConfig.ClientSecret = cs discordOauthConfig.RedirectURL = redirectURL + "/discordCallback" - http.HandleFunc("/", handleMain) - http.HandleFunc("/discordLogin", handlediscordLogin) - http.HandleFunc("/discordCallback", handlediscordCallback) - fmt.Println(http.ListenAndServe(":"+port, nil)) + + r := mux.NewRouter() + r.HandleFunc("/", handleMain) + r.HandleFunc("/logout", handleLogout) + r.HandleFunc("/discordLogin", handlediscordLogin) + r.HandleFunc("/discordCallback", handlediscordCallback) + + http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static")))) + http.Handle("/", r) + http.ListenAndServe(":"+port, nil) } func handleMain(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, htmlIndex) + session, _ := store.Get(r, "session-name") + if session.Values["discordUsername"] != nil { + fmt.Fprintf(w, "you are logged in as %s. logout", session.Values["discordUsername"]) + return + } + tmpls["home.html"].ExecuteTemplate(w, "base", map[string]interface{}{}) +} +func handleLogout(w http.ResponseWriter, r *http.Request) { + cookie := &http.Cookie{ + Name: "session-name", + Value: "", + Path: "/", + MaxAge: -1, + } + http.SetCookie(w, cookie) + http.Redirect(w, r, "/", http.StatusFound) } - func handlediscordLogin(w http.ResponseWriter, r *http.Request) { + b := make([]byte, 16) + rand.Read(b) + oauthStateString = base64.URLEncoding.EncodeToString(b) + + session, _ := store.Get(r, "session-name") + session.Values["state"] = oauthStateString + session.Save(r, w) + url := discordOauthConfig.AuthCodeURL(oauthStateString) http.Redirect(w, r, url, http.StatusTemporaryRedirect) } func handlediscordCallback(w http.ResponseWriter, r *http.Request) { + session, err := store.Get(r, "session-name") + if err != nil { + fmt.Fprintln(w, "aborted") + return + } + if r.URL.Query().Get("state") != session.Values["state"] { + fmt.Fprintln(w, "no state match; possible csrf OR cookies not enabled") + return + } + state := r.FormValue("state") if state != oauthStateString { fmt.Printf("invalid oauth state, expected '%s', got '%s'\n", oauthStateString, state) @@ -60,11 +105,14 @@ func handlediscordCallback(w http.ResponseWriter, r *http.Request) { return } - code := r.FormValue("code") - token, err := discordOauthConfig.Exchange(oauth2.NoContext, code) + token, err := discordOauthConfig.Exchange(oauth2.NoContext, r.FormValue("code")) if err != nil { - fmt.Println("Code exchange failed with ", err) - http.Redirect(w, r, "/", http.StatusTemporaryRedirect) + fmt.Fprintln(w, "Code exchange (could not get token) failed with ", err) + return + } + + if !token.Valid() { + fmt.Fprintln(w, "retrieved invalid token") return } @@ -74,5 +122,11 @@ func handlediscordCallback(w http.ResponseWriter, r *http.Request) { return } user, _ := dg.User("@me") - fmt.Fprintf(w, user.Username) + + session.Values["discordUserID"] = user.ID + session.Values["discordUsername"] = user.Username + session.Values["accessToken"] = token.AccessToken + session.Save(r, w) + + http.Redirect(w, r, "/", http.StatusTemporaryRedirect) }