diff --git a/user.go b/user.go
index 6ccc922..733e7b2 100644
--- a/user.go
+++ b/user.go
@@ -71,10 +71,15 @@ func (s *Server) handlerUser(w http.ResponseWriter, r *http.Request) {
AvailableVotes int
UnlimitedVotes bool
- OAuth bool
- TwitchOAuth bool
- DiscordOAuth bool
- PatreonOAuth bool
+ OAuthEnabled bool
+ TwitchOAuthEnabled bool
+ DiscordOAuthEnabled bool
+ PatreonOAuthEnabled bool
+
+ HasLocal bool
+ HasTwitch bool
+ HasDiscord bool
+ HasPatreon bool
ActiveVotes []*common.Movie
WatchedVotes []*common.Movie
@@ -108,7 +113,7 @@ func (s *Server) handlerUser(w http.ResponseWriter, r *http.Request) {
s.l.Error("Unable to get ConfigTwitchOauthEnabled config value: %v", err)
return
}
- data.TwitchOAuth = twitchAuth
+ data.TwitchOAuthEnabled = twitchAuth
discordAuth, err := s.data.GetCfgBool(ConfigDiscordOauthEnabled, DefaultDiscordOauthEnabled)
if err != nil {
@@ -116,7 +121,7 @@ func (s *Server) handlerUser(w http.ResponseWriter, r *http.Request) {
s.l.Error("Unable to get ConfigDiscordOauthEnabled config value: %v", err)
return
}
- data.DiscordOAuth = discordAuth
+ data.DiscordOAuthEnabled = discordAuth
patreonAuth, err := s.data.GetCfgBool(ConfigPatreonOauthEnabled, DefaultPatreonOauthEnabled)
if err != nil {
@@ -124,9 +129,18 @@ func (s *Server) handlerUser(w http.ResponseWriter, r *http.Request) {
s.l.Error("Unable to get ConfigPatreonOauthEnabled config value: %v", err)
return
}
- data.PatreonOAuth = patreonAuth
+ data.PatreonOAuthEnabled = patreonAuth
- data.OAuth = twitchAuth || discordAuth || patreonAuth
+ data.OAuthEnabled = twitchAuth || discordAuth || patreonAuth
+
+ _, err = user.GetAuthMethod(common.AUTH_LOCAL)
+ data.HasLocal = err == nil
+ _, err = user.GetAuthMethod(common.AUTH_TWITCH)
+ data.HasTwitch = err == nil
+ _, err = user.GetAuthMethod(common.AUTH_DISCORD)
+ data.HasDiscord = err == nil
+ _, err = user.GetAuthMethod(common.AUTH_PATREON)
+ data.HasPatreon = err == nil
if r.Method == "POST" {
err := r.ParseForm()
From 25f97e7315cf66a7081b8fc83233d1980579a99d Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Mon, 4 Jan 2021 21:07:26 +0100
Subject: [PATCH 40/67] Added the ability to add/remove localauth
---
oauth.go | 66 ++++++++++++++++++++++++++++++++++++++++++
server.go | 1 +
templates/account.html | 15 +++++++++-
user.go | 54 ++++++++++++++++++++++++++++++++--
4 files changed, 133 insertions(+), 3 deletions(-)
diff --git a/oauth.go b/oauth.go
index b51b811..4aad6e9 100644
--- a/oauth.go
+++ b/oauth.go
@@ -48,6 +48,72 @@ func (s *Server) initOauth() error {
return nil
}
+func (s *Server) handlerLocalAuthRemove(w http.ResponseWriter, r *http.Request) {
+ s.l.Debug("local remove")
+
+ user := s.getSessionUser(w, r)
+
+ auth, err := user.GetAuthMethod(common.AUTH_LOCAL)
+
+ if err != nil {
+ s.l.Info("User %s does not have a password associated with him", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+
+ if len(user.AuthMethods) == 1 {
+ s.l.Info("User %v only has the local Authmethod associated with him", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+
+ user, err = s.RemoveAuthMethodFromUser(auth, user)
+
+ if err != nil {
+ s.l.Info("Could not remove password from user. %s", err.Error())
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+
+ err = s.data.UpdateUser(user)
+ if err != nil {
+ s.l.Info("Could not update user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+
+ err = s.logout(w, r)
+ if err != nil {
+ s.l.Info("Could not logout user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+
+ if _, err := user.GetAuthMethod(common.AUTH_TWITCH); err == nil {
+ err = s.login(user, common.AUTH_TWITCH, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ } else if _, err := user.GetAuthMethod(common.AUTH_DISCORD); err == nil {
+ err = s.login(user, common.AUTH_DISCORD, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ } else if _, err := user.GetAuthMethod(common.AUTH_PATREON); err == nil {
+ err = s.login(user, common.AUTH_PATREON, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ }
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+}
func (s *Server) handlerTwitchOAuthLogin(w http.ResponseWriter, r *http.Request) {
// TODO that might cause impersonation attacks (i.e. using the token of an other user)
diff --git a/server.go b/server.go
index a26241d..2c9fa42 100644
--- a/server.go
+++ b/server.go
@@ -214,6 +214,7 @@ func NewServer(options Options) (*Server, error) {
mux.HandleFunc("/user/new/twitch", server.handlerTwitchOAuthSignup)
mux.HandleFunc("/user/add/twitch", server.handlerTwitchOAuthAdd)
mux.HandleFunc("/user/remove/twitch", server.handlerTwitchOAuthRemove)
+ mux.HandleFunc("/user/remove/local", server.handlerLocalAuthRemove)
mux.HandleFunc("/vote/", server.handlerVote)
mux.HandleFunc("/", server.handlerRoot)
diff --git a/templates/account.html b/templates/account.html
index 21649bc..8a126f6 100644
--- a/templates/account.html
+++ b/templates/account.html
@@ -3,9 +3,9 @@
{{define "body"}}
{{/*
diff --git a/user.go b/user.go
index 733e7b2..5829dae 100644
--- a/user.go
+++ b/user.go
@@ -196,14 +196,64 @@ func (s *Server) handlerUser(w http.ResponseWriter, r *http.Request) {
s.doError(http.StatusInternalServerError, "Unable to update password", w, r)
return
}
-
}
}
} else if formVal == "Notifications" {
// Update notifications
+ } else if formVal == "SetPassword" {
+ pass1_raw := r.PostFormValue("Password1")
+ pass2_raw := r.PostFormValue("Password2")
+
+ _, err := user.GetAuthMethod(common.AUTH_LOCAL)
+ if err == nil {
+ data.ErrCurrentPass = true
+ data.PassError = append(data.PassError, "Existing password detected. (how did you end up here anyways?)")
+ } else {
+ localAuth := &common.AuthMethod{
+ Type: common.AUTH_LOCAL,
+ }
+
+ if pass1_raw == "" {
+ data.ErrNewPass = true
+ data.PassError = append(data.PassError, "New password cannot be blank")
+ }
+
+ if pass1_raw != pass2_raw {
+ data.ErrNewPass = true
+ data.PassError = append(data.PassError, "Passwords do not match")
+ }
+ if !(data.ErrCurrentPass || data.ErrNewPass || data.ErrEmail) {
+ // Change pass
+ data.SuccessMessage = "Password successfully changed"
+ localAuth.Password = s.hashPassword(pass1_raw)
+ localAuth.PassDate = time.Now()
+ s.l.Info("new PassDate: %s", localAuth.PassDate)
+
+ user, err = s.AddAuthMethodToUser(localAuth, user)
+
+ if err != nil {
+ s.l.Error("Unable to add AuthMethod %s to user %s", localAuth.Type, user.Name)
+ s.doError(http.StatusInternalServerError, "Unable to link password to user", w, r)
+ }
+
+ s.data.UpdateUser(user)
+
+ if err != nil {
+ s.l.Error("Unable to update user %s", user.Name)
+ s.doError(http.StatusInternalServerError, "Unable to update user", w, r)
+ }
+
+ err = s.login(user, common.AUTH_LOCAL, w, r)
+ if err != nil {
+ s.l.Error("Unable to login to session:", err)
+ s.doError(http.StatusInternalServerError, "Unable to update password", w, r)
+ }
+
+ http.Redirect(w, r, "/user", http.StatusFound)
+ }
+ }
}
}
-
if err := s.executeTemplate(w, "account", data); err != nil {
s.l.Error("Error rendering template: %v", err)
}
From da866cbf7c6b6d26252c793058dd8dee7ee0d48a Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Mon, 4 Jan 2021 21:25:00 +0100
Subject: [PATCH 41/67] Fixed user purge to delete auth methods
---
data/json.go | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/data/json.go b/data/json.go
index d4466d5..4d80146 100644
--- a/data/json.go
+++ b/data/json.go
@@ -1412,6 +1412,11 @@ func (j *jsonConnector) PurgeUser(userId int) error {
count++
}
}
+
+ for _, auth := range j.findUser(userId).AuthMethods {
+ delete(j.AuthMethods, auth.Id)
+ }
+
j.Votes = newVotes
j.l.Info("Purged %d votes", count)
From c078a4f14a9c67b0276291e7e78982454201a6c2 Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Mon, 4 Jan 2021 22:59:19 +0100
Subject: [PATCH 42/67] Implemented Discord Oauth
---
data/json.go | 22 +++-
oauth.go | 335 ++++++++++++++++++++++++++++++++++++++++++++++++++-
server.go | 6 +
3 files changed, 360 insertions(+), 3 deletions(-)
diff --git a/data/json.go b/data/json.go
index 4d80146..213672e 100644
--- a/data/json.go
+++ b/data/json.go
@@ -652,7 +652,27 @@ func (j *jsonConnector) UserLocalLogin(name, hashedPw string) (*common.User, err
}
func (j *jsonConnector) UserDiscordLogin(extid string) (*common.User, error) {
- return nil, fmt.Errorf("Not implemented")
+ j.lock.Lock()
+ defer j.lock.Unlock()
+
+ //TODO refreshing
+
+ var id int
+ for _, auth := range j.AuthMethods {
+ if auth.ExtId == extid {
+ id = auth.Id
+ break
+ }
+ }
+
+ for _, user := range j.Users {
+ for _, auth := range user.AuthMethods {
+ if auth == id {
+ return j.findUser(user.Id), nil
+ }
+ }
+ }
+ return nil, fmt.Errorf("No user found with corresponding extid")
}
func (j *jsonConnector) UserTwitchLogin(extid string) (*common.User, error) {
diff --git a/oauth.go b/oauth.go
index 4aad6e9..b261bc8 100644
--- a/oauth.go
+++ b/oauth.go
@@ -19,6 +19,12 @@ var patreonOAuthConfig = &oauth2.Config{}
// var oauthStateString string
var openStates = []string{}
+// Welp we need to do the endpoints ourself i guess ...
+var discordEndpoint = oauth2.Endpoint{
+ AuthURL: "https://discord.com/api/oauth2/authorize",
+ TokenURL: "https://discord.com/api/oauth2/token",
+}
+
func (s *Server) initOauth() error {
twitchOauthEnabled, err := s.data.GetCfgBool(ConfigTwitchOauthEnabled, DefaultTwitchOauthEnabled)
if err != nil {
@@ -40,7 +46,32 @@ func (s *Server) initOauth() error {
ClientID: twitchClientID,
ClientSecret: twitchClientSecret,
Scopes: []string{"user:read:email"},
- Endpoint: twitch.Endpoint,
+ Endpoint: twitch.Endpoint, //this endpoint is predefined in the oauth2 package
+ }
+ }
+
+ discordOAuthEnabled, err := s.data.GetCfgBool(ConfigDiscordOauthEnabled, DefaultDiscordOauthEnabled)
+ if err != nil {
+ return err
+ }
+
+ if discordOAuthEnabled {
+ discordClientID, err := s.data.GetCfgString(ConfigDiscordOauthClientID, DefaultDiscordOauthClientID)
+ if err != nil {
+ return err
+ }
+
+ discordClientSecret, err := s.data.GetCfgString(ConfigDiscordOauthClientSecret, DefaultDiscordOauthClientSecret)
+ if err != nil {
+ return err
+ }
+
+ discordOAuthConfig = &oauth2.Config{
+ RedirectURL: "http://localhost:8090/user/login/discord/callback",
+ ClientID: discordClientID,
+ ClientSecret: discordClientSecret,
+ Scopes: []string{"email", "identify"},
+ Endpoint: discordEndpoint,
}
}
// TODO cry in a corner and figure out how to do this stuff for discord and patreon
@@ -114,6 +145,7 @@ func (s *Server) handlerLocalAuthRemove(w http.ResponseWriter, r *http.Request)
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
+
func (s *Server) handlerTwitchOAuthLogin(w http.ResponseWriter, r *http.Request) {
// TODO that might cause impersonation attacks (i.e. using the token of an other user)
@@ -190,6 +222,35 @@ func (s *Server) handlerTwitchOAuthRemove(w http.ResponseWriter, r *http.Request
return
}
+ err = s.logout(w, r)
+ if err != nil {
+ s.l.Info("Could not logout user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+
+ if _, err := user.GetAuthMethod(common.AUTH_LOCAL); err == nil {
+ err = s.login(user, common.AUTH_LOCAL, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ } else if _, err := user.GetAuthMethod(common.AUTH_DISCORD); err == nil {
+ err = s.login(user, common.AUTH_DISCORD, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ } else if _, err := user.GetAuthMethod(common.AUTH_PATREON); err == nil {
+ err = s.login(user, common.AUTH_PATREON, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ }
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
@@ -358,8 +419,278 @@ func (s *Server) handlerTwitchOAuthCallback(w http.ResponseWriter, r *http.Reque
return
}
-func (s *Server) handlerDiscordOAuth() {
+func (s *Server) handlerDiscordOAuthLogin(w http.ResponseWriter, r *http.Request) {
+ // TODO that might cause impersonation attacks (i.e. using the token of an other user)
+
+ // Generate a new state string for each login attempt and store it in the state list
+ oauthStateString := "login_" + getCryptRandKey(32)
+ openStates = append(openStates, oauthStateString)
+
+ // Handle the Oauth redirect
+ url := discordOAuthConfig.AuthCodeURL(oauthStateString)
+ http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+
+ s.l.Debug("discord login")
+}
+
+func (s *Server) handlerDiscordOAuthSignup(w http.ResponseWriter, r *http.Request) {
+ // TODO that might cause impersonation attacks (i.e. using the token of an other user)
+
+ // Generate a new state string for each login attempt and store it in the state list
+ oauthStateString := "signup_" + getCryptRandKey(32)
+ openStates = append(openStates, oauthStateString)
+
+ // Handle the Oauth redirect
+ url := discordOAuthConfig.AuthCodeURL(oauthStateString)
+ http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+
+ s.l.Debug("discord signup")
+}
+
+func (s *Server) handlerDiscordOAuthAdd(w http.ResponseWriter, r *http.Request) {
+ // TODO that might cause impersonation attacks (i.e. using the token of an other user)
+
+ // Generate a new state string for each login attempt and store it in the state list
+ oauthStateString := "add_" + getCryptRandKey(32)
+ openStates = append(openStates, oauthStateString)
+ // Handle the Oauth redirect
+ url := discordOAuthConfig.AuthCodeURL(oauthStateString)
+ http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+
+ s.l.Debug("discord add")
+}
+
+func (s *Server) handlerDiscordOAuthRemove(w http.ResponseWriter, r *http.Request) {
+
+ s.l.Debug("discord remove")
+
+ user := s.getSessionUser(w, r)
+
+ auth, err := user.GetAuthMethod(common.AUTH_DISCORD)
+
+ if err != nil {
+ s.l.Info("User %s does not have Discord Oauth associated with him", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+
+ if len(user.AuthMethods) == 1 {
+ s.l.Info("User %v only has Discord Oauth associated with him", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+
+ user, err = s.RemoveAuthMethodFromUser(auth, user)
+
+ if err != nil {
+ s.l.Info("Could not remove Discord Oauth from user. %s", err.Error())
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+
+ err = s.data.UpdateUser(user)
+ if err != nil {
+ s.l.Info("Could not update user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+
+ err = s.logout(w, r)
+ if err != nil {
+ s.l.Info("Could not logout user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+
+ if _, err := user.GetAuthMethod(common.AUTH_TWITCH); err == nil {
+ err = s.login(user, common.AUTH_TWITCH, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ } else if _, err := user.GetAuthMethod(common.AUTH_LOCAL); err == nil {
+ err = s.login(user, common.AUTH_LOCAL, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ } else if _, err := user.GetAuthMethod(common.AUTH_PATREON); err == nil {
+ err = s.login(user, common.AUTH_PATREON, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ }
+
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+}
+
+func (s *Server) handlerDiscordOAuthCallback(w http.ResponseWriter, r *http.Request) {
+ state := r.FormValue("state")
+
+ ok := false
+ for _, expectedState := range openStates {
+ if state == expectedState {
+ ok = true
+ }
+ }
+ if !ok {
+ s.l.Info("Invalid/Unknown OAuth state string: '%s'", state)
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
+ }
+
+ code := r.FormValue("code")
+ token, err := discordOAuthConfig.Exchange(oauth2.NoContext, code)
+ if err != nil {
+ s.l.Info("Code exchange failed: %s", err)
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
+ }
+
+ // Request the User data from the API
+ req, err := http.NewRequest("GET", "https://discord.com/api/users/@me", nil)
+ req.Header.Add("Authorization", "Bearer "+token.AccessToken)
+
+ client := &http.Client{}
+ resp, err := client.Do(req)
+ if err != nil {
+ s.l.Info("Could not retrieve Userdata from Discord API: %s", err)
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
+
+ }
+ if resp.StatusCode != 200 {
+ s.l.Error("Status Code is not 200, its %v", resp.Status)
+ }
+
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ s.l.Error(err.Error())
+ }
+
+ var data map[string]interface{}
+
+ if err := json.Unmarshal(body, &data); err != nil {
+ s.l.Error(err.Error())
+ s.l.Debug("%v", data)
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
+ }
+
+ if strings.HasPrefix(state, "signup_") {
+
+ s.l.Debug("signup prefix")
+
+ auth := &common.AuthMethod{
+ Type: common.AUTH_DISCORD,
+ ExtId: data["id"].(string),
+ AuthToken: token.AccessToken,
+ RefreshToken: token.RefreshToken,
+ RefreshDate: token.Expiry,
+ }
+
+ if !s.data.CheckOauthUsage(auth.ExtId, auth.Type) {
+
+ newUser := &common.User{
+ Name: data["username"].(string),
+ Email: data["email"].(string),
+ NotifyCycleEnd: false,
+ NotifyVoteSelection: false,
+ }
+
+ newUser.Id, err = s.data.AddUser(newUser)
+
+ if err != nil {
+ s.l.Error(err.Error())
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
+ }
+
+ newUser, err = s.AddAuthMethodToUser(auth, newUser)
+
+ if err != nil {
+ s.l.Error(err.Error())
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
+ }
+
+ err = s.data.UpdateUser(newUser)
+
+ if err != nil {
+ s.l.Error(err.Error())
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
+ }
+
+ s.l.Debug("logging in %v", newUser.Name)
+ s.login(newUser, common.AUTH_DISCORD, w, r)
+ http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
+ } else {
+ s.l.Debug("AuthMethod already used")
+ http.Redirect(w, r, "/user/new", http.StatusTemporaryRedirect)
+ }
+ } else if strings.HasPrefix(state, "login_") {
+ s.l.Debug("login prefix")
+ user, err := s.data.UserDiscordLogin(data["id"].(string))
+ if err != nil {
+ s.l.Error(err.Error())
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
+ }
+ s.l.Debug("logging in %v", user.Name)
+ s.login(user, common.AUTH_DISCORD, w, r)
+ http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
+ } else if strings.HasPrefix(state, "add_") {
+ s.l.Debug("add prefix")
+
+ user := s.getSessionUser(w, r)
+
+ auth := &common.AuthMethod{
+ Type: common.AUTH_DISCORD,
+ ExtId: data["id"].(string),
+ AuthToken: token.AccessToken,
+ RefreshToken: token.RefreshToken,
+ RefreshDate: token.Expiry,
+ }
+
+ if !s.data.CheckOauthUsage(auth.ExtId, auth.Type) {
+ _, err = user.GetAuthMethod(auth.Type)
+ if err != nil {
+ _, err = s.AddAuthMethodToUser(auth, user)
+
+ if err != nil {
+ s.l.Error(err.Error())
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+
+ err = s.data.UpdateUser(user)
+
+ if err != nil {
+ s.l.Error(err.Error())
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ } else {
+ s.l.Error("User %s already has %s Oauth associated", user.Name, auth.Type)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ } else {
+ s.l.Error("The provided Oauth login is already used")
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ return
}
func (s *Server) handlerPatreonOAuth() {
diff --git a/server.go b/server.go
index 2c9fa42..195687f 100644
--- a/server.go
+++ b/server.go
@@ -208,12 +208,18 @@ func NewServer(options Options) (*Server, error) {
mux.HandleFunc("/user/login", server.handlerUserLogin)
mux.HandleFunc("/user/login/twitch", server.handlerTwitchOAuthLogin)
mux.HandleFunc("/user/login/twitch/callback", server.handlerTwitchOAuthCallback)
+ mux.HandleFunc("/user/login/discord", server.handlerDiscordOAuthLogin)
+ mux.HandleFunc("/user/login/discord/callback", server.handlerDiscordOAuthCallback)
mux.HandleFunc("/user/logout", server.handlerUserLogout)
mux.HandleFunc("/user/new", server.handlerUserNew)
mux.HandleFunc("/user/new/twitch", server.handlerTwitchOAuthSignup)
mux.HandleFunc("/user/add/twitch", server.handlerTwitchOAuthAdd)
mux.HandleFunc("/user/remove/twitch", server.handlerTwitchOAuthRemove)
+ mux.HandleFunc("/user/new/discord", server.handlerDiscordOAuthSignup)
+ mux.HandleFunc("/user/add/discord", server.handlerDiscordOAuthAdd)
+ mux.HandleFunc("/user/remove/discord", server.handlerDiscordOAuthRemove)
+
mux.HandleFunc("/user/remove/local", server.handlerLocalAuthRemove)
mux.HandleFunc("/vote/", server.handlerVote)
From b7c59a979a8dbbc81f4408222b1713526d053704 Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Tue, 5 Jan 2021 00:53:37 +0100
Subject: [PATCH 43/67] Implemented Patreon Oauth
also made use of the "HostAddress" config value to not hardcode
localhost
---
data/json.go | 22 +++-
oauth.go | 313 ++++++++++++++++++++++++++++++++++++++++++++++++++-
server.go | 5 +
3 files changed, 335 insertions(+), 5 deletions(-)
diff --git a/data/json.go b/data/json.go
index 213672e..967576a 100644
--- a/data/json.go
+++ b/data/json.go
@@ -700,7 +700,27 @@ func (j *jsonConnector) UserTwitchLogin(extid string) (*common.User, error) {
}
func (j *jsonConnector) UserPatreonLogin(extid string) (*common.User, error) {
- return nil, fmt.Errorf("Not implemented")
+ j.lock.Lock()
+ defer j.lock.Unlock()
+
+ //TODO refreshing
+
+ var id int
+ for _, auth := range j.AuthMethods {
+ if auth.ExtId == extid {
+ id = auth.Id
+ break
+ }
+ }
+
+ for _, user := range j.Users {
+ for _, auth := range user.AuthMethods {
+ if auth == id {
+ return j.findUser(user.Id), nil
+ }
+ }
+ }
+ return nil, fmt.Errorf("No user found with corresponding extid")
}
// Get the total number of users
diff --git a/oauth.go b/oauth.go
index b261bc8..5d50cba 100644
--- a/oauth.go
+++ b/oauth.go
@@ -4,6 +4,7 @@ import (
"encoding/json"
"io/ioutil"
"net/http"
+ u "net/url"
"strings"
"github.com/zorchenhimer/MoviePolls/common"
@@ -25,7 +26,17 @@ var discordEndpoint = oauth2.Endpoint{
TokenURL: "https://discord.com/api/oauth2/token",
}
+var patreonEndpoint = oauth2.Endpoint{
+ AuthURL: "https://www.patreon.com/oauth2/authorize",
+ TokenURL: "https://www.patreon.com/api/oauth2/token",
+}
+
func (s *Server) initOauth() error {
+ baseUrl, err := s.data.GetCfgString(ConfigHostAddress, "")
+ if err != nil {
+ return err
+ }
+
twitchOauthEnabled, err := s.data.GetCfgBool(ConfigTwitchOauthEnabled, DefaultTwitchOauthEnabled)
if err != nil {
return err
@@ -42,7 +53,7 @@ func (s *Server) initOauth() error {
}
twitchOAuthConfig = &oauth2.Config{
- RedirectURL: "http://localhost:8090/user/login/twitch/callback",
+ RedirectURL: baseUrl + "/user/login/twitch/callback",
ClientID: twitchClientID,
ClientSecret: twitchClientSecret,
Scopes: []string{"user:read:email"},
@@ -67,15 +78,37 @@ func (s *Server) initOauth() error {
}
discordOAuthConfig = &oauth2.Config{
- RedirectURL: "http://localhost:8090/user/login/discord/callback",
+ RedirectURL: baseUrl + "/user/login/discord/callback",
ClientID: discordClientID,
ClientSecret: discordClientSecret,
Scopes: []string{"email", "identify"},
Endpoint: discordEndpoint,
}
}
- // TODO cry in a corner and figure out how to do this stuff for discord and patreon
+ patreonOAuthEnabled, err := s.data.GetCfgBool(ConfigPatreonOauthEnabled, DefaultPatreonOauthEnabled)
+ if err != nil {
+ return err
+ }
+
+ if patreonOAuthEnabled {
+ patreonClientID, err := s.data.GetCfgString(ConfigPatreonOauthClientID, DefaultPatreonOauthClientID)
+ if err != nil {
+ return err
+ }
+
+ patreonClientSecret, err := s.data.GetCfgString(ConfigPatreonOauthClientSecret, DefaultPatreonOauthClientSecret)
+ if err != nil {
+ return err
+ }
+ patreonOAuthConfig = &oauth2.Config{
+ RedirectURL: baseUrl + "/user/login/patreon/callback",
+ ClientID: patreonClientID,
+ ClientSecret: patreonClientSecret,
+ Scopes: []string{"identity", "identity[email]"},
+ Endpoint: patreonEndpoint,
+ }
+ }
return nil
}
@@ -693,6 +726,278 @@ func (s *Server) handlerDiscordOAuthCallback(w http.ResponseWriter, r *http.Requ
return
}
-func (s *Server) handlerPatreonOAuth() {
+func (s *Server) handlerPatreonOAuthLogin(w http.ResponseWriter, r *http.Request) {
+ // TODO that might cause impersonation attacks (i.e. using the token of an other user)
+ // Generate a new state string for each login attempt and store it in the state list
+ oauthStateString := "login_" + getCryptRandKey(32)
+ openStates = append(openStates, oauthStateString)
+
+ // Handle the Oauth redirect
+ url := patreonOAuthConfig.AuthCodeURL(oauthStateString)
+ http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+
+ s.l.Debug("patreon login")
+}
+
+func (s *Server) handlerPatreonOAuthSignup(w http.ResponseWriter, r *http.Request) {
+ // TODO that might cause impersonation attacks (i.e. using the token of an other user)
+
+ // Generate a new state string for each login attempt and store it in the state list
+ oauthStateString := "signup_" + getCryptRandKey(32)
+ openStates = append(openStates, oauthStateString)
+
+ // Handle the Oauth redirect
+ url := patreonOAuthConfig.AuthCodeURL(oauthStateString)
+ http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+
+ s.l.Debug("patreon signup")
+}
+
+func (s *Server) handlerPatreonOAuthAdd(w http.ResponseWriter, r *http.Request) {
+ // TODO that might cause impersonation attacks (i.e. using the token of an other user)
+
+ // Generate a new state string for each login attempt and store it in the state list
+ oauthStateString := "add_" + getCryptRandKey(32)
+ openStates = append(openStates, oauthStateString)
+
+ // Handle the Oauth redirect
+ url := patreonOAuthConfig.AuthCodeURL(oauthStateString)
+ http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+
+ s.l.Debug("patreon add")
+}
+
+func (s *Server) handlerPatreonOAuthRemove(w http.ResponseWriter, r *http.Request) {
+
+ s.l.Debug("patreon remove")
+
+ user := s.getSessionUser(w, r)
+
+ auth, err := user.GetAuthMethod(common.AUTH_PATREON)
+
+ if err != nil {
+ s.l.Info("User %s does not have Patreon Oauth associated with him", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+
+ if len(user.AuthMethods) == 1 {
+ s.l.Info("User %v only has Patreon Oauth associated with him", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+
+ user, err = s.RemoveAuthMethodFromUser(auth, user)
+
+ if err != nil {
+ s.l.Info("Could not remove Patreon Oauth from user. %s", err.Error())
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+
+ err = s.data.UpdateUser(user)
+ if err != nil {
+ s.l.Info("Could not update user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+
+ err = s.logout(w, r)
+ if err != nil {
+ s.l.Info("Could not logout user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+
+ if _, err := user.GetAuthMethod(common.AUTH_TWITCH); err == nil {
+ err = s.login(user, common.AUTH_TWITCH, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ } else if _, err := user.GetAuthMethod(common.AUTH_DISCORD); err == nil {
+ err = s.login(user, common.AUTH_DISCORD, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ } else if _, err := user.GetAuthMethod(common.AUTH_LOCAL); err == nil {
+ err = s.login(user, common.AUTH_LOCAL, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ }
+
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+}
+
+func (s *Server) handlerPatreonOAuthCallback(w http.ResponseWriter, r *http.Request) {
+ state := r.FormValue("state")
+
+ ok := false
+ for _, expectedState := range openStates {
+ if state == expectedState {
+ ok = true
+ }
+ }
+ if !ok {
+ s.l.Info("Invalid/Unknown OAuth state string: '%s'", state)
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
+ }
+
+ code := r.FormValue("code")
+ token, err := patreonOAuthConfig.Exchange(oauth2.NoContext, code)
+ if err != nil {
+ s.l.Info("Code exchange failed: %s", err)
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
+ }
+
+ // Request the User data from the API
+ req, err := http.NewRequest("GET", "https://www.patreon.com/api/oauth2/v2/identity?fields"+u.QueryEscape("[user]")+"=email,first_name,full_name,last_name,vanity", nil)
+ req.Header.Add("Authorization", "Bearer "+token.AccessToken)
+
+ client := &http.Client{}
+ resp, err := client.Do(req)
+ if err != nil {
+ s.l.Info("Could not retrieve Userdata from Patreon API: %s", err)
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
+
+ }
+ if resp.StatusCode != 200 {
+ s.l.Error("Status Code is not 200, its %v", resp.Status)
+ }
+
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ s.l.Error(err.Error())
+ }
+
+ var data map[string]interface{}
+
+ if err := json.Unmarshal(body, &data); err != nil {
+ s.l.Error(err.Error())
+ s.l.Debug("%v", data)
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
+ }
+
+ data = data["data"].(map[string]interface{})
+
+ if strings.HasPrefix(state, "signup_") {
+
+ s.l.Debug("signup prefix")
+
+ auth := &common.AuthMethod{
+ Type: common.AUTH_PATREON,
+ ExtId: data["id"].(string),
+ AuthToken: token.AccessToken,
+ RefreshToken: token.RefreshToken,
+ RefreshDate: token.Expiry,
+ }
+
+ if !s.data.CheckOauthUsage(auth.ExtId, auth.Type) {
+
+ newUser := &common.User{
+ Name: data["attributes"].(map[string]interface{})["full_name"].(string),
+ Email: data["attributes"].(map[string]interface{})["email"].(string),
+ NotifyCycleEnd: false,
+ NotifyVoteSelection: false,
+ }
+
+ newUser.Id, err = s.data.AddUser(newUser)
+
+ if err != nil {
+ s.l.Error(err.Error())
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
+ }
+
+ newUser, err = s.AddAuthMethodToUser(auth, newUser)
+
+ if err != nil {
+ s.l.Error(err.Error())
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
+ }
+
+ err = s.data.UpdateUser(newUser)
+
+ if err != nil {
+ s.l.Error(err.Error())
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
+ }
+
+ s.l.Debug("logging in %v", newUser.Name)
+ s.login(newUser, common.AUTH_PATREON, w, r)
+ http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
+ } else {
+ s.l.Debug("AuthMethod already used")
+ http.Redirect(w, r, "/user/new", http.StatusTemporaryRedirect)
+ }
+ } else if strings.HasPrefix(state, "login_") {
+ s.l.Debug("login prefix")
+ user, err := s.data.UserPatreonLogin(data["id"].(string))
+ if err != nil {
+ s.l.Error(err.Error())
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
+ }
+ s.l.Debug("logging in %v", user.Name)
+ s.login(user, common.AUTH_PATREON, w, r)
+ http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
+ } else if strings.HasPrefix(state, "add_") {
+ s.l.Debug("add prefix")
+
+ user := s.getSessionUser(w, r)
+
+ auth := &common.AuthMethod{
+ Type: common.AUTH_PATREON,
+ ExtId: data["id"].(string),
+ AuthToken: token.AccessToken,
+ RefreshToken: token.RefreshToken,
+ RefreshDate: token.Expiry,
+ }
+
+ if !s.data.CheckOauthUsage(auth.ExtId, auth.Type) {
+ _, err = user.GetAuthMethod(auth.Type)
+ if err != nil {
+ _, err = s.AddAuthMethodToUser(auth, user)
+
+ if err != nil {
+ s.l.Error(err.Error())
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+
+ err = s.data.UpdateUser(user)
+
+ if err != nil {
+ s.l.Error(err.Error())
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ } else {
+ s.l.Error("User %s already has %s Oauth associated", user.Name, auth.Type)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ } else {
+ s.l.Error("The provided Oauth login is already used")
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ return
}
diff --git a/server.go b/server.go
index 195687f..d6a7174 100644
--- a/server.go
+++ b/server.go
@@ -210,6 +210,8 @@ func NewServer(options Options) (*Server, error) {
mux.HandleFunc("/user/login/twitch/callback", server.handlerTwitchOAuthCallback)
mux.HandleFunc("/user/login/discord", server.handlerDiscordOAuthLogin)
mux.HandleFunc("/user/login/discord/callback", server.handlerDiscordOAuthCallback)
+ mux.HandleFunc("/user/login/patreon", server.handlerPatreonOAuthLogin)
+ mux.HandleFunc("/user/login/patreon/callback", server.handlerPatreonOAuthCallback)
mux.HandleFunc("/user/logout", server.handlerUserLogout)
mux.HandleFunc("/user/new", server.handlerUserNew)
@@ -219,6 +221,9 @@ func NewServer(options Options) (*Server, error) {
mux.HandleFunc("/user/new/discord", server.handlerDiscordOAuthSignup)
mux.HandleFunc("/user/add/discord", server.handlerDiscordOAuthAdd)
mux.HandleFunc("/user/remove/discord", server.handlerDiscordOAuthRemove)
+ mux.HandleFunc("/user/new/patreon", server.handlerPatreonOAuthSignup)
+ mux.HandleFunc("/user/add/patreon", server.handlerPatreonOAuthAdd)
+ mux.HandleFunc("/user/remove/patreon", server.handlerPatreonOAuthRemove)
mux.HandleFunc("/user/remove/local", server.handlerLocalAuthRemove)
From f750ad36a9bad0007d0d0e4c875c4b4801ba81d5 Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Tue, 5 Jan 2021 01:01:01 +0100
Subject: [PATCH 44/67] Fixed the signup page saying login instead
---
templates/newaccount.html | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/templates/newaccount.html b/templates/newaccount.html
index 65d10ef..06c3f8c 100644
--- a/templates/newaccount.html
+++ b/templates/newaccount.html
@@ -25,13 +25,13 @@
{{if .OAuth}}
{{end}}
From f10116c9d036e4d1d5397e60f5ccd75ad2e38237 Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Tue, 5 Jan 2021 02:24:56 +0100
Subject: [PATCH 45/67] Reordered handlers
---
server.go | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/server.go b/server.go
index d6a7174..493c374 100644
--- a/server.go
+++ b/server.go
@@ -206,25 +206,27 @@ func NewServer(options Options) (*Server, error) {
mux.HandleFunc("/user", server.handlerUser)
mux.HandleFunc("/user/login", server.handlerUserLogin)
- mux.HandleFunc("/user/login/twitch", server.handlerTwitchOAuthLogin)
- mux.HandleFunc("/user/login/twitch/callback", server.handlerTwitchOAuthCallback)
- mux.HandleFunc("/user/login/discord", server.handlerDiscordOAuthLogin)
- mux.HandleFunc("/user/login/discord/callback", server.handlerDiscordOAuthCallback)
- mux.HandleFunc("/user/login/patreon", server.handlerPatreonOAuthLogin)
- mux.HandleFunc("/user/login/patreon/callback", server.handlerPatreonOAuthCallback)
- mux.HandleFunc("/user/logout", server.handlerUserLogout)
- mux.HandleFunc("/user/new", server.handlerUserNew)
+ mux.HandleFunc("/user/login/twitch", server.handlerTwitchOAuthLogin)
mux.HandleFunc("/user/new/twitch", server.handlerTwitchOAuthSignup)
mux.HandleFunc("/user/add/twitch", server.handlerTwitchOAuthAdd)
mux.HandleFunc("/user/remove/twitch", server.handlerTwitchOAuthRemove)
+ mux.HandleFunc("/user/callback/twitch", server.handlerTwitchOAuthCallback)
+
+ mux.HandleFunc("/user/login/discord", server.handlerDiscordOAuthLogin)
mux.HandleFunc("/user/new/discord", server.handlerDiscordOAuthSignup)
mux.HandleFunc("/user/add/discord", server.handlerDiscordOAuthAdd)
mux.HandleFunc("/user/remove/discord", server.handlerDiscordOAuthRemove)
+ mux.HandleFunc("/user/callback/discord", server.handlerDiscordOAuthCallback)
+
+ mux.HandleFunc("/user/login/patreon", server.handlerPatreonOAuthLogin)
mux.HandleFunc("/user/new/patreon", server.handlerPatreonOAuthSignup)
mux.HandleFunc("/user/add/patreon", server.handlerPatreonOAuthAdd)
mux.HandleFunc("/user/remove/patreon", server.handlerPatreonOAuthRemove)
+ mux.HandleFunc("/user/callback/patreon", server.handlerPatreonOAuthCallback)
+ mux.HandleFunc("/user/logout", server.handlerUserLogout)
+ mux.HandleFunc("/user/new", server.handlerUserNew)
mux.HandleFunc("/user/remove/local", server.handlerLocalAuthRemove)
mux.HandleFunc("/vote/", server.handlerVote)
From cb696ec0cab2ec8bb2e0115ef491849321c83abe Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Tue, 5 Jan 2021 02:25:25 +0100
Subject: [PATCH 46/67] Cleanup, logging improvements, comments
---
oauth.go | 154 ++++++++++++++++++++++++++++++++-----------------------
1 file changed, 90 insertions(+), 64 deletions(-)
diff --git a/oauth.go b/oauth.go
index 5d50cba..3acf8e2 100644
--- a/oauth.go
+++ b/oauth.go
@@ -31,6 +31,8 @@ var patreonEndpoint = oauth2.Endpoint{
TokenURL: "https://www.patreon.com/api/oauth2/token",
}
+// Initiate the OAuth configs, this includes loading the ConfigValues into "memory" to be used in the login methods
+// Returns: Error if a config value could not be retrieved
func (s *Server) initOauth() error {
baseUrl, err := s.data.GetCfgString(ConfigHostAddress, "")
if err != nil {
@@ -53,7 +55,7 @@ func (s *Server) initOauth() error {
}
twitchOAuthConfig = &oauth2.Config{
- RedirectURL: baseUrl + "/user/login/twitch/callback",
+ RedirectURL: baseUrl + "/user/callback/twitch",
ClientID: twitchClientID,
ClientSecret: twitchClientSecret,
Scopes: []string{"user:read:email"},
@@ -78,7 +80,7 @@ func (s *Server) initOauth() error {
}
discordOAuthConfig = &oauth2.Config{
- RedirectURL: baseUrl + "/user/login/discord/callback",
+ RedirectURL: baseUrl + "/user/callback/discord",
ClientID: discordClientID,
ClientSecret: discordClientSecret,
Scopes: []string{"email", "identify"},
@@ -102,7 +104,7 @@ func (s *Server) initOauth() error {
}
patreonOAuthConfig = &oauth2.Config{
- RedirectURL: baseUrl + "/user/login/patreon/callback",
+ RedirectURL: baseUrl + "/user/callback/patreon",
ClientID: patreonClientID,
ClientSecret: patreonClientSecret,
Scopes: []string{"identity", "identity[email]"},
@@ -112,6 +114,7 @@ func (s *Server) initOauth() error {
return nil
}
+// Removes the AuthType LOCAL AuthMethod from the currently logged in user
func (s *Server) handlerLocalAuthRemove(w http.ResponseWriter, r *http.Request) {
s.l.Debug("local remove")
@@ -146,6 +149,7 @@ func (s *Server) handlerLocalAuthRemove(w http.ResponseWriter, r *http.Request)
return
}
+ // Logging the user out to ensure that he is logged in with an existing AuthMethod
err = s.logout(w, r)
if err != nil {
s.l.Info("Could not logout user %s", user.Name)
@@ -153,6 +157,7 @@ func (s *Server) handlerLocalAuthRemove(w http.ResponseWriter, r *http.Request)
return
}
+ // Logging the user back in
if _, err := user.GetAuthMethod(common.AUTH_TWITCH); err == nil {
err = s.login(user, common.AUTH_TWITCH, w, r)
if err != nil {
@@ -179,6 +184,7 @@ func (s *Server) handlerLocalAuthRemove(w http.ResponseWriter, r *http.Request)
return
}
+// Creates an OAuth request to log the user in using the TWITCH OAuth
func (s *Server) handlerTwitchOAuthLogin(w http.ResponseWriter, r *http.Request) {
// TODO that might cause impersonation attacks (i.e. using the token of an other user)
@@ -193,6 +199,8 @@ func (s *Server) handlerTwitchOAuthLogin(w http.ResponseWriter, r *http.Request)
s.l.Debug("twitch login")
}
+// Creates an OAuth request to sign up the user using the TWITCH OAuth
+// A new user is created and a new AuthMethod struct is created and associated
func (s *Server) handlerTwitchOAuthSignup(w http.ResponseWriter, r *http.Request) {
// TODO that might cause impersonation attacks (i.e. using the token of an other user)
@@ -204,9 +212,10 @@ func (s *Server) handlerTwitchOAuthSignup(w http.ResponseWriter, r *http.Request
url := twitchOAuthConfig.AuthCodeURL(oauthStateString)
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
- s.l.Debug("twitch signup")
+ s.l.Debug("twitch sign up")
}
+// Creates an OAuth request to add a new Twitch AuthMethod to the currently logged in user
func (s *Server) handlerTwitchOAuthAdd(w http.ResponseWriter, r *http.Request) {
// TODO that might cause impersonation attacks (i.e. using the token of an other user)
@@ -221,8 +230,8 @@ func (s *Server) handlerTwitchOAuthAdd(w http.ResponseWriter, r *http.Request) {
s.l.Debug("twitch add")
}
+// Removes the Twitch AuthMethod from the currently logged in user
func (s *Server) handlerTwitchOAuthRemove(w http.ResponseWriter, r *http.Request) {
- s.l.Debug("twitch remove")
user := s.getSessionUser(w, r)
@@ -255,6 +264,7 @@ func (s *Server) handlerTwitchOAuthRemove(w http.ResponseWriter, r *http.Request
return
}
+ // Log the user out to ensure he uses an existing AuthMethod
err = s.logout(w, r)
if err != nil {
s.l.Info("Could not logout user %s", user.Name)
@@ -262,6 +272,7 @@ func (s *Server) handlerTwitchOAuthRemove(w http.ResponseWriter, r *http.Request
return
}
+ // Find a new AuthMethod to log the user back in
if _, err := user.GetAuthMethod(common.AUTH_LOCAL); err == nil {
err = s.login(user, common.AUTH_LOCAL, w, r)
if err != nil {
@@ -284,10 +295,14 @@ func (s *Server) handlerTwitchOAuthRemove(w http.ResponseWriter, r *http.Request
return
}
}
+
+ s.l.Debug("twitch remove")
+
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
+// This function handles all Twitch Callbacks (add/signup/login)
func (s *Server) handlerTwitchOAuthCallback(w http.ResponseWriter, r *http.Request) {
state := r.FormValue("state")
@@ -324,28 +339,30 @@ func (s *Server) handlerTwitchOAuthCallback(w http.ResponseWriter, r *http.Reque
return
}
+
if resp.StatusCode != 200 {
- s.l.Error("Status Code is not 200, its %v", resp.Status)
+ s.l.Info("Status Code is not 200, its %v", resp.Status)
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
}
var data map[string][]map[string]interface{}
if err := json.Unmarshal(body, &data); err != nil {
- s.l.Error(err.Error())
- s.l.Debug("%v", data)
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
return
}
if strings.HasPrefix(state, "signup_") {
-
- s.l.Debug("signup prefix")
-
+ // Handle the sign up process
auth := &common.AuthMethod{
Type: common.AUTH_TWITCH,
ExtId: data["data"][0]["id"].(string),
@@ -354,8 +371,9 @@ func (s *Server) handlerTwitchOAuthCallback(w http.ResponseWriter, r *http.Reque
RefreshDate: token.Expiry,
}
+ // check if Twitch Auth is already used
if !s.data.CheckOauthUsage(auth.ExtId, auth.Type) {
-
+ // Create a new user
newUser := &common.User{
Name: data["data"][0]["display_name"].(string),
Email: data["data"][0]["email"].(string),
@@ -363,26 +381,29 @@ func (s *Server) handlerTwitchOAuthCallback(w http.ResponseWriter, r *http.Reque
NotifyVoteSelection: false,
}
+ // add this new server to the database
newUser.Id, err = s.data.AddUser(newUser)
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
return
}
+ // add the authmethod to the user
newUser, err = s.AddAuthMethodToUser(auth, newUser)
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
return
}
+ // update the user in the DB with the user having the AuthMethod associated
err = s.data.UpdateUser(newUser)
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
return
}
@@ -395,10 +416,10 @@ func (s *Server) handlerTwitchOAuthCallback(w http.ResponseWriter, r *http.Reque
http.Redirect(w, r, "/user/new", http.StatusTemporaryRedirect)
}
} else if strings.HasPrefix(state, "login_") {
- s.l.Debug("login prefix")
+ // Handle Twitch Login
user, err := s.data.UserTwitchLogin(data["data"][0]["id"].(string))
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
return
}
@@ -406,8 +427,9 @@ func (s *Server) handlerTwitchOAuthCallback(w http.ResponseWriter, r *http.Reque
s.login(user, common.AUTH_TWITCH, w, r)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
} else if strings.HasPrefix(state, "add_") {
- s.l.Debug("add prefix")
+ // Handle adding a Twitch AuthMethod to the logged in user
+ // get the current user
user := s.getSessionUser(w, r)
auth := &common.AuthMethod{
@@ -418,13 +440,15 @@ func (s *Server) handlerTwitchOAuthCallback(w http.ResponseWriter, r *http.Reque
RefreshDate: token.Expiry,
}
+ // check if this oauth is already used
if !s.data.CheckOauthUsage(auth.ExtId, auth.Type) {
+ // check if the user already has an other Twitch OAuth connected
_, err = user.GetAuthMethod(auth.Type)
if err != nil {
_, err = s.AddAuthMethodToUser(auth, user)
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
@@ -432,17 +456,17 @@ func (s *Server) handlerTwitchOAuthCallback(w http.ResponseWriter, r *http.Reque
err = s.data.UpdateUser(user)
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
} else {
- s.l.Error("User %s already has %s Oauth associated", user.Name, auth.Type)
+ s.l.Info("User %s already has %s Oauth associated", user.Name, auth.Type)
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
} else {
- s.l.Error("The provided Oauth login is already used")
+ s.l.Info("The provided Oauth login is already used")
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
@@ -452,6 +476,7 @@ func (s *Server) handlerTwitchOAuthCallback(w http.ResponseWriter, r *http.Reque
return
}
+// Creates an OAuth query to log a user in using Discord OAuth
func (s *Server) handlerDiscordOAuthLogin(w http.ResponseWriter, r *http.Request) {
// TODO that might cause impersonation attacks (i.e. using the token of an other user)
@@ -466,6 +491,7 @@ func (s *Server) handlerDiscordOAuthLogin(w http.ResponseWriter, r *http.Request
s.l.Debug("discord login")
}
+// Creates an OAuth query to sign up a user using Discord OAuth
func (s *Server) handlerDiscordOAuthSignup(w http.ResponseWriter, r *http.Request) {
// TODO that might cause impersonation attacks (i.e. using the token of an other user)
@@ -480,6 +506,7 @@ func (s *Server) handlerDiscordOAuthSignup(w http.ResponseWriter, r *http.Reques
s.l.Debug("discord signup")
}
+// Creates an OAuth query to add an Discord AuthMethod to the currently logged in user
func (s *Server) handlerDiscordOAuthAdd(w http.ResponseWriter, r *http.Request) {
// TODO that might cause impersonation attacks (i.e. using the token of an other user)
@@ -494,10 +521,9 @@ func (s *Server) handlerDiscordOAuthAdd(w http.ResponseWriter, r *http.Request)
s.l.Debug("discord add")
}
+// Removes the Discord AuthMethod from the currently logged in user
func (s *Server) handlerDiscordOAuthRemove(w http.ResponseWriter, r *http.Request) {
- s.l.Debug("discord remove")
-
user := s.getSessionUser(w, r)
auth, err := user.GetAuthMethod(common.AUTH_DISCORD)
@@ -529,6 +555,7 @@ func (s *Server) handlerDiscordOAuthRemove(w http.ResponseWriter, r *http.Reques
return
}
+ // Log the user out to ensure he is logged in with an existing AuthMethod
err = s.logout(w, r)
if err != nil {
s.l.Info("Could not logout user %s", user.Name)
@@ -536,6 +563,7 @@ func (s *Server) handlerDiscordOAuthRemove(w http.ResponseWriter, r *http.Reques
return
}
+ // Try to log the user back in
if _, err := user.GetAuthMethod(common.AUTH_TWITCH); err == nil {
err = s.login(user, common.AUTH_TWITCH, w, r)
if err != nil {
@@ -559,10 +587,13 @@ func (s *Server) handlerDiscordOAuthRemove(w http.ResponseWriter, r *http.Reques
}
}
+ s.l.Debug("discord remove")
+
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
+// Handler for the Discord OAuth Callbacks (add/signup/login)
func (s *Server) handlerDiscordOAuthCallback(w http.ResponseWriter, r *http.Request) {
state := r.FormValue("state")
@@ -596,30 +627,31 @@ func (s *Server) handlerDiscordOAuthCallback(w http.ResponseWriter, r *http.Requ
s.l.Info("Could not retrieve Userdata from Discord API: %s", err)
http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
return
-
}
+
if resp.StatusCode != 200 {
- s.l.Error("Status Code is not 200, its %v", resp.Status)
+ s.l.Info("Status Code is not 200, its %v", resp.Status)
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
}
var data map[string]interface{}
if err := json.Unmarshal(body, &data); err != nil {
- s.l.Error(err.Error())
- s.l.Debug("%v", data)
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
return
}
if strings.HasPrefix(state, "signup_") {
- s.l.Debug("signup prefix")
-
auth := &common.AuthMethod{
Type: common.AUTH_DISCORD,
ExtId: data["id"].(string),
@@ -629,7 +661,6 @@ func (s *Server) handlerDiscordOAuthCallback(w http.ResponseWriter, r *http.Requ
}
if !s.data.CheckOauthUsage(auth.ExtId, auth.Type) {
-
newUser := &common.User{
Name: data["username"].(string),
Email: data["email"].(string),
@@ -640,7 +671,7 @@ func (s *Server) handlerDiscordOAuthCallback(w http.ResponseWriter, r *http.Requ
newUser.Id, err = s.data.AddUser(newUser)
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
return
}
@@ -648,7 +679,7 @@ func (s *Server) handlerDiscordOAuthCallback(w http.ResponseWriter, r *http.Requ
newUser, err = s.AddAuthMethodToUser(auth, newUser)
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
return
}
@@ -656,7 +687,7 @@ func (s *Server) handlerDiscordOAuthCallback(w http.ResponseWriter, r *http.Requ
err = s.data.UpdateUser(newUser)
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
return
}
@@ -669,10 +700,9 @@ func (s *Server) handlerDiscordOAuthCallback(w http.ResponseWriter, r *http.Requ
http.Redirect(w, r, "/user/new", http.StatusTemporaryRedirect)
}
} else if strings.HasPrefix(state, "login_") {
- s.l.Debug("login prefix")
user, err := s.data.UserDiscordLogin(data["id"].(string))
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
return
}
@@ -680,8 +710,6 @@ func (s *Server) handlerDiscordOAuthCallback(w http.ResponseWriter, r *http.Requ
s.login(user, common.AUTH_DISCORD, w, r)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
} else if strings.HasPrefix(state, "add_") {
- s.l.Debug("add prefix")
-
user := s.getSessionUser(w, r)
auth := &common.AuthMethod{
@@ -698,7 +726,7 @@ func (s *Server) handlerDiscordOAuthCallback(w http.ResponseWriter, r *http.Requ
_, err = s.AddAuthMethodToUser(auth, user)
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
@@ -706,17 +734,17 @@ func (s *Server) handlerDiscordOAuthCallback(w http.ResponseWriter, r *http.Requ
err = s.data.UpdateUser(user)
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
} else {
- s.l.Error("User %s already has %s Oauth associated", user.Name, auth.Type)
+ s.l.Info("User %s already has %s Oauth associated", user.Name, auth.Type)
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
} else {
- s.l.Error("The provided Oauth login is already used")
+ s.l.Info("The provided Oauth login is already used")
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
@@ -770,8 +798,6 @@ func (s *Server) handlerPatreonOAuthAdd(w http.ResponseWriter, r *http.Request)
func (s *Server) handlerPatreonOAuthRemove(w http.ResponseWriter, r *http.Request) {
- s.l.Debug("patreon remove")
-
user := s.getSessionUser(w, r)
auth, err := user.GetAuthMethod(common.AUTH_PATREON)
@@ -833,6 +859,8 @@ func (s *Server) handlerPatreonOAuthRemove(w http.ResponseWriter, r *http.Reques
}
}
+ s.l.Debug("patreon remove")
+
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
@@ -873,19 +901,22 @@ func (s *Server) handlerPatreonOAuthCallback(w http.ResponseWriter, r *http.Requ
}
if resp.StatusCode != 200 {
- s.l.Error("Status Code is not 200, its %v", resp.Status)
+ s.l.Info("Status Code is not 200, its %v", resp.Status)
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
+ http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
+ return
}
var data map[string]interface{}
if err := json.Unmarshal(body, &data); err != nil {
- s.l.Error(err.Error())
- s.l.Debug("%v", data)
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
return
}
@@ -894,8 +925,6 @@ func (s *Server) handlerPatreonOAuthCallback(w http.ResponseWriter, r *http.Requ
if strings.HasPrefix(state, "signup_") {
- s.l.Debug("signup prefix")
-
auth := &common.AuthMethod{
Type: common.AUTH_PATREON,
ExtId: data["id"].(string),
@@ -916,7 +945,7 @@ func (s *Server) handlerPatreonOAuthCallback(w http.ResponseWriter, r *http.Requ
newUser.Id, err = s.data.AddUser(newUser)
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
return
}
@@ -924,7 +953,7 @@ func (s *Server) handlerPatreonOAuthCallback(w http.ResponseWriter, r *http.Requ
newUser, err = s.AddAuthMethodToUser(auth, newUser)
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
return
}
@@ -932,7 +961,7 @@ func (s *Server) handlerPatreonOAuthCallback(w http.ResponseWriter, r *http.Requ
err = s.data.UpdateUser(newUser)
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
return
}
@@ -941,14 +970,13 @@ func (s *Server) handlerPatreonOAuthCallback(w http.ResponseWriter, r *http.Requ
s.login(newUser, common.AUTH_PATREON, w, r)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
} else {
- s.l.Debug("AuthMethod already used")
+ s.l.Info("AuthMethod already used")
http.Redirect(w, r, "/user/new", http.StatusTemporaryRedirect)
}
} else if strings.HasPrefix(state, "login_") {
- s.l.Debug("login prefix")
user, err := s.data.UserPatreonLogin(data["id"].(string))
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user/login", http.StatusTemporaryRedirect)
return
}
@@ -956,8 +984,6 @@ func (s *Server) handlerPatreonOAuthCallback(w http.ResponseWriter, r *http.Requ
s.login(user, common.AUTH_PATREON, w, r)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
} else if strings.HasPrefix(state, "add_") {
- s.l.Debug("add prefix")
-
user := s.getSessionUser(w, r)
auth := &common.AuthMethod{
@@ -974,7 +1000,7 @@ func (s *Server) handlerPatreonOAuthCallback(w http.ResponseWriter, r *http.Requ
_, err = s.AddAuthMethodToUser(auth, user)
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
@@ -982,17 +1008,17 @@ func (s *Server) handlerPatreonOAuthCallback(w http.ResponseWriter, r *http.Requ
err = s.data.UpdateUser(user)
if err != nil {
- s.l.Error(err.Error())
+ s.l.Info(err.Error())
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
} else {
- s.l.Error("User %s already has %s Oauth associated", user.Name, auth.Type)
+ s.l.Info("User %s already has %s Oauth associated", user.Name, auth.Type)
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
} else {
- s.l.Error("The provided Oauth login is already used")
+ s.l.Info("The provided Oauth login is already used")
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
From 925ef6b178cbd84df85f82fdeabd4510228ab1da Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Wed, 6 Jan 2021 20:49:24 +0100
Subject: [PATCH 47/67] Added oauth reload after changing the configuration
---
admin.go | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/admin.go b/admin.go
index 7e4783d..76f76e6 100644
--- a/admin.go
+++ b/admin.go
@@ -504,6 +504,13 @@ func (s *Server) handlerAdminConfig(w http.ResponseWriter, r *http.Request) {
// Set this down here so the Notice Banner is updated
data.dataPageBase = s.newPageBase("Admin - Config", w, r)
+ // Reload Oauth
+ err = s.initOauth()
+
+ if err != nil {
+ data.ErrorMessage = append(data.ErrorMessage, err.Error())
+ }
+
if err := s.executeTemplate(w, "adminConfig", data); err != nil {
s.l.Error("Error rendering template: %v", err)
}
From 332903087434473d85b57c074007cde1184c8efa Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Wed, 6 Jan 2021 20:50:10 +0100
Subject: [PATCH 48/67] Added some hardening against empty config fields
---
oauth.go | 56 +++++++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 45 insertions(+), 11 deletions(-)
diff --git a/oauth.go b/oauth.go
index 3acf8e2..c093c3b 100644
--- a/oauth.go
+++ b/oauth.go
@@ -2,6 +2,7 @@ package moviepoll
import (
"encoding/json"
+ "fmt"
"io/ioutil"
"net/http"
u "net/url"
@@ -34,26 +35,51 @@ var patreonEndpoint = oauth2.Endpoint{
// Initiate the OAuth configs, this includes loading the ConfigValues into "memory" to be used in the login methods
// Returns: Error if a config value could not be retrieved
func (s *Server) initOauth() error {
- baseUrl, err := s.data.GetCfgString(ConfigHostAddress, "")
+
+ twitchOauthEnabled, err := s.data.GetCfgBool(ConfigTwitchOauthEnabled, DefaultTwitchOauthEnabled)
if err != nil {
return err
}
- twitchOauthEnabled, err := s.data.GetCfgBool(ConfigTwitchOauthEnabled, DefaultTwitchOauthEnabled)
+ discordOAuthEnabled, err := s.data.GetCfgBool(ConfigDiscordOauthEnabled, DefaultDiscordOauthEnabled)
+ if err != nil {
+ return err
+ }
+
+ patreonOAuthEnabled, err := s.data.GetCfgBool(ConfigPatreonOauthEnabled, DefaultPatreonOauthEnabled)
if err != nil {
return err
}
+
+ baseUrl, err := s.data.GetCfgString(ConfigHostAddress, "")
+ if err != nil {
+ return err
+ }
+
+ if twitchOauthEnabled || discordOAuthEnabled || patreonOAuthEnabled {
+ if baseUrl == "" {
+ return fmt.Errorf("Config Value for HostAddress cannot be empty to use OAuth")
+ }
+ }
+
if twitchOauthEnabled {
twitchClientID, err := s.data.GetCfgString(ConfigTwitchOauthClientID, DefaultTwitchOauthClientID)
if err != nil {
return err
}
+ if twitchClientID == "" {
+ return fmt.Errorf("Config Value for TwitchOauthClientID cannot be empty to use OAuth")
+ }
twitchClientSecret, err := s.data.GetCfgString(ConfigTwitchOauthClientSecret, DefaultTwitchOauthClientSecret)
if err != nil {
return err
}
+ if twitchClientSecret == "" {
+ return fmt.Errorf("Config Value for TwitchOauthClientSecret cannot be empty to use OAuth")
+ }
+
twitchOAuthConfig = &oauth2.Config{
RedirectURL: baseUrl + "/user/callback/twitch",
ClientID: twitchClientID,
@@ -63,22 +89,25 @@ func (s *Server) initOauth() error {
}
}
- discordOAuthEnabled, err := s.data.GetCfgBool(ConfigDiscordOauthEnabled, DefaultDiscordOauthEnabled)
- if err != nil {
- return err
- }
-
if discordOAuthEnabled {
discordClientID, err := s.data.GetCfgString(ConfigDiscordOauthClientID, DefaultDiscordOauthClientID)
if err != nil {
return err
}
+ if discordClientID == "" {
+ return fmt.Errorf("Config Value for DiscordOauthClientID cannot be empty to use OAuth")
+ }
+
discordClientSecret, err := s.data.GetCfgString(ConfigDiscordOauthClientSecret, DefaultDiscordOauthClientSecret)
if err != nil {
return err
}
+ if discordClientSecret == "" {
+ return fmt.Errorf("Config Value for DiscordOauthClientSecret cannot be empty to use OAuth")
+ }
+
discordOAuthConfig = &oauth2.Config{
RedirectURL: baseUrl + "/user/callback/discord",
ClientID: discordClientID,
@@ -87,10 +116,6 @@ func (s *Server) initOauth() error {
Endpoint: discordEndpoint,
}
}
- patreonOAuthEnabled, err := s.data.GetCfgBool(ConfigPatreonOauthEnabled, DefaultPatreonOauthEnabled)
- if err != nil {
- return err
- }
if patreonOAuthEnabled {
patreonClientID, err := s.data.GetCfgString(ConfigPatreonOauthClientID, DefaultPatreonOauthClientID)
@@ -98,11 +123,19 @@ func (s *Server) initOauth() error {
return err
}
+ if patreonClientID == "" {
+ return fmt.Errorf("Config Value for PatreonOauthClientSecret cannot be empty to use OAuth")
+ }
+
patreonClientSecret, err := s.data.GetCfgString(ConfigPatreonOauthClientSecret, DefaultPatreonOauthClientSecret)
if err != nil {
return err
}
+ if patreonClientSecret == "" {
+ return fmt.Errorf("Config Value for PatreonOauthClientSecret cannot be empty to use OAuth")
+ }
+
patreonOAuthConfig = &oauth2.Config{
RedirectURL: baseUrl + "/user/callback/patreon",
ClientID: patreonClientID,
@@ -111,6 +144,7 @@ func (s *Server) initOauth() error {
Endpoint: patreonEndpoint,
}
}
+
return nil
}
From a0062f1cd07f4f59c8e72ae3a3012ada7f64e4b3 Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Wed, 6 Jan 2021 22:53:26 +0100
Subject: [PATCH 49/67] Reworked Oauth flow to make use of query parameters
Also changed around some paths for the endpoints
---
oauth.go | 487 ++++++++++++++++++-------------------
server.go | 25 +-
templates/account.html | 12 +-
templates/newaccount.html | 6 +-
templates/plain-login.html | 6 +-
5 files changed, 252 insertions(+), 284 deletions(-)
diff --git a/oauth.go b/oauth.go
index c093c3b..2dbdb2a 100644
--- a/oauth.go
+++ b/oauth.go
@@ -81,7 +81,7 @@ func (s *Server) initOauth() error {
}
twitchOAuthConfig = &oauth2.Config{
- RedirectURL: baseUrl + "/user/callback/twitch",
+ RedirectURL: baseUrl + "/oauth/twitch/callback",
ClientID: twitchClientID,
ClientSecret: twitchClientSecret,
Scopes: []string{"user:read:email"},
@@ -109,7 +109,7 @@ func (s *Server) initOauth() error {
}
discordOAuthConfig = &oauth2.Config{
- RedirectURL: baseUrl + "/user/callback/discord",
+ RedirectURL: baseUrl + "/oauth/discord/callback",
ClientID: discordClientID,
ClientSecret: discordClientSecret,
Scopes: []string{"email", "identify"},
@@ -137,7 +137,7 @@ func (s *Server) initOauth() error {
}
patreonOAuthConfig = &oauth2.Config{
- RedirectURL: baseUrl + "/user/callback/patreon",
+ RedirectURL: baseUrl + "/oauth/patreon/callback",
ClientID: patreonClientID,
ClientSecret: patreonClientSecret,
Scopes: []string{"identity", "identity[email]"},
@@ -218,122 +218,112 @@ func (s *Server) handlerLocalAuthRemove(w http.ResponseWriter, r *http.Request)
return
}
-// Creates an OAuth request to log the user in using the TWITCH OAuth
-func (s *Server) handlerTwitchOAuthLogin(w http.ResponseWriter, r *http.Request) {
- // TODO that might cause impersonation attacks (i.e. using the token of an other user)
+func (s *Server) handlerTwitchOAuth(w http.ResponseWriter, r *http.Request) {
+ action := r.URL.Query().Get("action")
- // Generate a new state string for each login attempt and store it in the state list
- oauthStateString := "login_" + getCryptRandKey(32)
- openStates = append(openStates, oauthStateString)
+ switch action {
+ case "login":
+ // Generate a new state string for each login attempt and store it in the state list
+ oauthStateString := "login_" + getCryptRandKey(32)
+ openStates = append(openStates, oauthStateString)
- // Handle the Oauth redirect
- url := twitchOAuthConfig.AuthCodeURL(oauthStateString)
- http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+ // Handle the Oauth redirect
+ url := twitchOAuthConfig.AuthCodeURL(oauthStateString)
+ http.Redirect(w, r, url, http.StatusTemporaryRedirect)
- s.l.Debug("twitch login")
-}
-
-// Creates an OAuth request to sign up the user using the TWITCH OAuth
-// A new user is created and a new AuthMethod struct is created and associated
-func (s *Server) handlerTwitchOAuthSignup(w http.ResponseWriter, r *http.Request) {
- // TODO that might cause impersonation attacks (i.e. using the token of an other user)
-
- // Generate a new state string for each login attempt and store it in the state list
- oauthStateString := "signup_" + getCryptRandKey(32)
- openStates = append(openStates, oauthStateString)
-
- // Handle the Oauth redirect
- url := twitchOAuthConfig.AuthCodeURL(oauthStateString)
- http.Redirect(w, r, url, http.StatusTemporaryRedirect)
-
- s.l.Debug("twitch sign up")
-}
-
-// Creates an OAuth request to add a new Twitch AuthMethod to the currently logged in user
-func (s *Server) handlerTwitchOAuthAdd(w http.ResponseWriter, r *http.Request) {
- // TODO that might cause impersonation attacks (i.e. using the token of an other user)
+ s.l.Debug("twitch login")
- // Generate a new state string for each login attempt and store it in the state list
- oauthStateString := "add_" + getCryptRandKey(32)
- openStates = append(openStates, oauthStateString)
+ case "signup":
+ // Generate a new state string for each login attempt and store it in the state list
+ oauthStateString := "signup_" + getCryptRandKey(32)
+ openStates = append(openStates, oauthStateString)
- // Handle the Oauth redirect
- url := twitchOAuthConfig.AuthCodeURL(oauthStateString)
- http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+ // Handle the Oauth redirect
+ url := twitchOAuthConfig.AuthCodeURL(oauthStateString)
+ http.Redirect(w, r, url, http.StatusTemporaryRedirect)
- s.l.Debug("twitch add")
-}
-
-// Removes the Twitch AuthMethod from the currently logged in user
-func (s *Server) handlerTwitchOAuthRemove(w http.ResponseWriter, r *http.Request) {
+ s.l.Debug("twitch sign up")
- user := s.getSessionUser(w, r)
+ case "add":
+ // Generate a new state string for each login attempt and store it in the state list
+ oauthStateString := "add_" + getCryptRandKey(32)
+ openStates = append(openStates, oauthStateString)
- auth, err := user.GetAuthMethod(common.AUTH_TWITCH)
+ // Handle the Oauth redirect
+ url := twitchOAuthConfig.AuthCodeURL(oauthStateString)
+ http.Redirect(w, r, url, http.StatusTemporaryRedirect)
- if err != nil {
- s.l.Info("User %s does not have Twitch Oauth associated with him", user.Name)
- http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
- return
- }
+ s.l.Debug("twitch add")
- if len(user.AuthMethods) == 1 {
- s.l.Info("User %v only has Twitch Oauth associated with him", user.Name)
- http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
- return
- }
+ case "remove":
+ user := s.getSessionUser(w, r)
- user, err = s.RemoveAuthMethodFromUser(auth, user)
+ auth, err := user.GetAuthMethod(common.AUTH_TWITCH)
- if err != nil {
- s.l.Info("Could not remove Twitch Oauth from user. %s", err.Error())
- http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
- return
- }
+ if err != nil {
+ s.l.Info("User %s does not have Twitch Oauth associated with him", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
- err = s.data.UpdateUser(user)
- if err != nil {
- s.l.Info("Could not update user %s", user.Name)
- http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
- return
- }
+ if len(user.AuthMethods) == 1 {
+ s.l.Info("User %v only has Twitch Oauth associated with him", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
- // Log the user out to ensure he uses an existing AuthMethod
- err = s.logout(w, r)
- if err != nil {
- s.l.Info("Could not logout user %s", user.Name)
- http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
- return
- }
+ user, err = s.RemoveAuthMethodFromUser(auth, user)
- // Find a new AuthMethod to log the user back in
- if _, err := user.GetAuthMethod(common.AUTH_LOCAL); err == nil {
- err = s.login(user, common.AUTH_LOCAL, w, r)
if err != nil {
- s.l.Info("Could not login user %s", user.Name)
+ s.l.Info("Could not remove Twitch Oauth from user. %s", err.Error())
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
- } else if _, err := user.GetAuthMethod(common.AUTH_DISCORD); err == nil {
- err = s.login(user, common.AUTH_DISCORD, w, r)
+
+ err = s.data.UpdateUser(user)
if err != nil {
- s.l.Info("Could not login user %s", user.Name)
+ s.l.Info("Could not update user %s", user.Name)
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
- } else if _, err := user.GetAuthMethod(common.AUTH_PATREON); err == nil {
- err = s.login(user, common.AUTH_PATREON, w, r)
+
+ // Log the user out to ensure he uses an existing AuthMethod
+ err = s.logout(w, r)
if err != nil {
- s.l.Info("Could not login user %s", user.Name)
+ s.l.Info("Could not logout user %s", user.Name)
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
- }
- s.l.Debug("twitch remove")
+ // Find a new AuthMethod to log the user back in
+ if _, err := user.GetAuthMethod(common.AUTH_LOCAL); err == nil {
+ err = s.login(user, common.AUTH_LOCAL, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ } else if _, err := user.GetAuthMethod(common.AUTH_DISCORD); err == nil {
+ err = s.login(user, common.AUTH_DISCORD, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ } else if _, err := user.GetAuthMethod(common.AUTH_PATREON); err == nil {
+ err = s.login(user, common.AUTH_PATREON, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ }
+
+ s.l.Debug("twitch remove")
- http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
- return
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
}
// This function handles all Twitch Callbacks (add/signup/login)
@@ -510,121 +500,113 @@ func (s *Server) handlerTwitchOAuthCallback(w http.ResponseWriter, r *http.Reque
return
}
-// Creates an OAuth query to log a user in using Discord OAuth
-func (s *Server) handlerDiscordOAuthLogin(w http.ResponseWriter, r *http.Request) {
- // TODO that might cause impersonation attacks (i.e. using the token of an other user)
+func (s *Server) handlerDiscordOAuth(w http.ResponseWriter, r *http.Request) {
- // Generate a new state string for each login attempt and store it in the state list
- oauthStateString := "login_" + getCryptRandKey(32)
- openStates = append(openStates, oauthStateString)
+ action := r.URL.Query().Get("action")
- // Handle the Oauth redirect
- url := discordOAuthConfig.AuthCodeURL(oauthStateString)
- http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+ switch action {
+ case "login":
+ // Generate a new state string for each login attempt and store it in the state list
+ oauthStateString := "login_" + getCryptRandKey(32)
+ openStates = append(openStates, oauthStateString)
- s.l.Debug("discord login")
-}
+ // Handle the Oauth redirect
+ url := discordOAuthConfig.AuthCodeURL(oauthStateString)
+ http.Redirect(w, r, url, http.StatusTemporaryRedirect)
-// Creates an OAuth query to sign up a user using Discord OAuth
-func (s *Server) handlerDiscordOAuthSignup(w http.ResponseWriter, r *http.Request) {
- // TODO that might cause impersonation attacks (i.e. using the token of an other user)
+ s.l.Debug("discord login")
- // Generate a new state string for each login attempt and store it in the state list
- oauthStateString := "signup_" + getCryptRandKey(32)
- openStates = append(openStates, oauthStateString)
+ case "signup":
+ // Generate a new state string for each login attempt and store it in the state list
+ oauthStateString := "signup_" + getCryptRandKey(32)
+ openStates = append(openStates, oauthStateString)
- // Handle the Oauth redirect
- url := discordOAuthConfig.AuthCodeURL(oauthStateString)
- http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+ // Handle the Oauth redirect
+ url := discordOAuthConfig.AuthCodeURL(oauthStateString)
+ http.Redirect(w, r, url, http.StatusTemporaryRedirect)
- s.l.Debug("discord signup")
-}
+ s.l.Debug("discord signup")
-// Creates an OAuth query to add an Discord AuthMethod to the currently logged in user
-func (s *Server) handlerDiscordOAuthAdd(w http.ResponseWriter, r *http.Request) {
- // TODO that might cause impersonation attacks (i.e. using the token of an other user)
+ case "add":
+ // Generate a new state string for each login attempt and store it in the state list
+ oauthStateString := "add_" + getCryptRandKey(32)
+ openStates = append(openStates, oauthStateString)
- // Generate a new state string for each login attempt and store it in the state list
- oauthStateString := "add_" + getCryptRandKey(32)
- openStates = append(openStates, oauthStateString)
+ // Handle the Oauth redirect
+ url := discordOAuthConfig.AuthCodeURL(oauthStateString)
+ http.Redirect(w, r, url, http.StatusTemporaryRedirect)
- // Handle the Oauth redirect
- url := discordOAuthConfig.AuthCodeURL(oauthStateString)
- http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+ s.l.Debug("discord add")
- s.l.Debug("discord add")
-}
-
-// Removes the Discord AuthMethod from the currently logged in user
-func (s *Server) handlerDiscordOAuthRemove(w http.ResponseWriter, r *http.Request) {
-
- user := s.getSessionUser(w, r)
-
- auth, err := user.GetAuthMethod(common.AUTH_DISCORD)
-
- if err != nil {
- s.l.Info("User %s does not have Discord Oauth associated with him", user.Name)
- http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
- return
- }
+ case "remove":
+ user := s.getSessionUser(w, r)
- if len(user.AuthMethods) == 1 {
- s.l.Info("User %v only has Discord Oauth associated with him", user.Name)
- http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
- return
- }
+ auth, err := user.GetAuthMethod(common.AUTH_DISCORD)
- user, err = s.RemoveAuthMethodFromUser(auth, user)
+ if err != nil {
+ s.l.Info("User %s does not have Discord Oauth associated with him", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
- if err != nil {
- s.l.Info("Could not remove Discord Oauth from user. %s", err.Error())
- http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
- return
- }
+ if len(user.AuthMethods) == 1 {
+ s.l.Info("User %v only has Discord Oauth associated with him", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
- err = s.data.UpdateUser(user)
- if err != nil {
- s.l.Info("Could not update user %s", user.Name)
- http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
- return
- }
+ user, err = s.RemoveAuthMethodFromUser(auth, user)
- // Log the user out to ensure he is logged in with an existing AuthMethod
- err = s.logout(w, r)
- if err != nil {
- s.l.Info("Could not logout user %s", user.Name)
- http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
- return
- }
-
- // Try to log the user back in
- if _, err := user.GetAuthMethod(common.AUTH_TWITCH); err == nil {
- err = s.login(user, common.AUTH_TWITCH, w, r)
if err != nil {
- s.l.Info("Could not login user %s", user.Name)
+ s.l.Info("Could not remove Discord Oauth from user. %s", err.Error())
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
- } else if _, err := user.GetAuthMethod(common.AUTH_LOCAL); err == nil {
- err = s.login(user, common.AUTH_LOCAL, w, r)
+
+ err = s.data.UpdateUser(user)
if err != nil {
- s.l.Info("Could not login user %s", user.Name)
+ s.l.Info("Could not update user %s", user.Name)
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
- } else if _, err := user.GetAuthMethod(common.AUTH_PATREON); err == nil {
- err = s.login(user, common.AUTH_PATREON, w, r)
+
+ // Log the user out to ensure he is logged in with an existing AuthMethod
+ err = s.logout(w, r)
if err != nil {
- s.l.Info("Could not login user %s", user.Name)
+ s.l.Info("Could not logout user %s", user.Name)
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
- }
- s.l.Debug("discord remove")
+ // Try to log the user back in
+ if _, err := user.GetAuthMethod(common.AUTH_TWITCH); err == nil {
+ err = s.login(user, common.AUTH_TWITCH, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ } else if _, err := user.GetAuthMethod(common.AUTH_LOCAL); err == nil {
+ err = s.login(user, common.AUTH_LOCAL, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ } else if _, err := user.GetAuthMethod(common.AUTH_PATREON); err == nil {
+ err = s.login(user, common.AUTH_PATREON, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ }
+
+ s.l.Debug("discord remove")
- http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
- return
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
}
// Handler for the Discord OAuth Callbacks (add/signup/login)
@@ -788,115 +770,110 @@ func (s *Server) handlerDiscordOAuthCallback(w http.ResponseWriter, r *http.Requ
return
}
-func (s *Server) handlerPatreonOAuthLogin(w http.ResponseWriter, r *http.Request) {
- // TODO that might cause impersonation attacks (i.e. using the token of an other user)
-
- // Generate a new state string for each login attempt and store it in the state list
- oauthStateString := "login_" + getCryptRandKey(32)
- openStates = append(openStates, oauthStateString)
+func (s *Server) handlerPatreonOAuth(w http.ResponseWriter, r *http.Request) {
+ action := r.URL.Query().Get("action")
- // Handle the Oauth redirect
- url := patreonOAuthConfig.AuthCodeURL(oauthStateString)
- http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+ switch action {
+ case "login":
+ // Generate a new state string for each login attempt and store it in the state list
+ oauthStateString := "login_" + getCryptRandKey(32)
+ openStates = append(openStates, oauthStateString)
- s.l.Debug("patreon login")
-}
+ // Handle the Oauth redirect
+ url := patreonOAuthConfig.AuthCodeURL(oauthStateString)
+ http.Redirect(w, r, url, http.StatusTemporaryRedirect)
-func (s *Server) handlerPatreonOAuthSignup(w http.ResponseWriter, r *http.Request) {
- // TODO that might cause impersonation attacks (i.e. using the token of an other user)
+ s.l.Debug("patreon login")
- // Generate a new state string for each login attempt and store it in the state list
- oauthStateString := "signup_" + getCryptRandKey(32)
- openStates = append(openStates, oauthStateString)
+ case "signup":
+ // Generate a new state string for each login attempt and store it in the state list
+ oauthStateString := "signup_" + getCryptRandKey(32)
+ openStates = append(openStates, oauthStateString)
- // Handle the Oauth redirect
- url := patreonOAuthConfig.AuthCodeURL(oauthStateString)
- http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+ // Handle the Oauth redirect
+ url := patreonOAuthConfig.AuthCodeURL(oauthStateString)
+ http.Redirect(w, r, url, http.StatusTemporaryRedirect)
- s.l.Debug("patreon signup")
-}
+ s.l.Debug("patreon signup")
-func (s *Server) handlerPatreonOAuthAdd(w http.ResponseWriter, r *http.Request) {
- // TODO that might cause impersonation attacks (i.e. using the token of an other user)
+ case "add":
+ // Generate a new state string for each login attempt and store it in the state list
+ oauthStateString := "add_" + getCryptRandKey(32)
+ openStates = append(openStates, oauthStateString)
- // Generate a new state string for each login attempt and store it in the state list
- oauthStateString := "add_" + getCryptRandKey(32)
- openStates = append(openStates, oauthStateString)
-
- // Handle the Oauth redirect
- url := patreonOAuthConfig.AuthCodeURL(oauthStateString)
- http.Redirect(w, r, url, http.StatusTemporaryRedirect)
-
- s.l.Debug("patreon add")
-}
+ // Handle the Oauth redirect
+ url := patreonOAuthConfig.AuthCodeURL(oauthStateString)
+ http.Redirect(w, r, url, http.StatusTemporaryRedirect)
-func (s *Server) handlerPatreonOAuthRemove(w http.ResponseWriter, r *http.Request) {
+ s.l.Debug("patreon add")
- user := s.getSessionUser(w, r)
-
- auth, err := user.GetAuthMethod(common.AUTH_PATREON)
-
- if err != nil {
- s.l.Info("User %s does not have Patreon Oauth associated with him", user.Name)
- http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
- return
- }
+ case "remove":
+ user := s.getSessionUser(w, r)
- if len(user.AuthMethods) == 1 {
- s.l.Info("User %v only has Patreon Oauth associated with him", user.Name)
- http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
- return
- }
+ auth, err := user.GetAuthMethod(common.AUTH_PATREON)
- user, err = s.RemoveAuthMethodFromUser(auth, user)
-
- if err != nil {
- s.l.Info("Could not remove Patreon Oauth from user. %s", err.Error())
- http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
- return
- }
+ if err != nil {
+ s.l.Info("User %s does not have Patreon Oauth associated with him", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
- err = s.data.UpdateUser(user)
- if err != nil {
- s.l.Info("Could not update user %s", user.Name)
- http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
- return
- }
+ if len(user.AuthMethods) == 1 {
+ s.l.Info("User %v only has Patreon Oauth associated with him", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
- err = s.logout(w, r)
- if err != nil {
- s.l.Info("Could not logout user %s", user.Name)
- http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
- return
- }
+ user, err = s.RemoveAuthMethodFromUser(auth, user)
- if _, err := user.GetAuthMethod(common.AUTH_TWITCH); err == nil {
- err = s.login(user, common.AUTH_TWITCH, w, r)
if err != nil {
- s.l.Info("Could not login user %s", user.Name)
+ s.l.Info("Could not remove Patreon Oauth from user. %s", err.Error())
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
- } else if _, err := user.GetAuthMethod(common.AUTH_DISCORD); err == nil {
- err = s.login(user, common.AUTH_DISCORD, w, r)
+
+ err = s.data.UpdateUser(user)
if err != nil {
- s.l.Info("Could not login user %s", user.Name)
+ s.l.Info("Could not update user %s", user.Name)
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
- } else if _, err := user.GetAuthMethod(common.AUTH_LOCAL); err == nil {
- err = s.login(user, common.AUTH_LOCAL, w, r)
+
+ err = s.logout(w, r)
if err != nil {
- s.l.Info("Could not login user %s", user.Name)
+ s.l.Info("Could not logout user %s", user.Name)
http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
return
}
- }
- s.l.Debug("patreon remove")
+ if _, err := user.GetAuthMethod(common.AUTH_TWITCH); err == nil {
+ err = s.login(user, common.AUTH_TWITCH, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ } else if _, err := user.GetAuthMethod(common.AUTH_DISCORD); err == nil {
+ err = s.login(user, common.AUTH_DISCORD, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ } else if _, err := user.GetAuthMethod(common.AUTH_LOCAL); err == nil {
+ err = s.login(user, common.AUTH_LOCAL, w, r)
+ if err != nil {
+ s.l.Info("Could not login user %s", user.Name)
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
+ }
+
+ s.l.Debug("patreon remove")
- http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
- return
+ http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
+ return
+ }
}
func (s *Server) handlerPatreonOAuthCallback(w http.ResponseWriter, r *http.Request) {
diff --git a/server.go b/server.go
index 493c374..dd0f7c7 100644
--- a/server.go
+++ b/server.go
@@ -207,23 +207,14 @@ func NewServer(options Options) (*Server, error) {
mux.HandleFunc("/user", server.handlerUser)
mux.HandleFunc("/user/login", server.handlerUserLogin)
- mux.HandleFunc("/user/login/twitch", server.handlerTwitchOAuthLogin)
- mux.HandleFunc("/user/new/twitch", server.handlerTwitchOAuthSignup)
- mux.HandleFunc("/user/add/twitch", server.handlerTwitchOAuthAdd)
- mux.HandleFunc("/user/remove/twitch", server.handlerTwitchOAuthRemove)
- mux.HandleFunc("/user/callback/twitch", server.handlerTwitchOAuthCallback)
-
- mux.HandleFunc("/user/login/discord", server.handlerDiscordOAuthLogin)
- mux.HandleFunc("/user/new/discord", server.handlerDiscordOAuthSignup)
- mux.HandleFunc("/user/add/discord", server.handlerDiscordOAuthAdd)
- mux.HandleFunc("/user/remove/discord", server.handlerDiscordOAuthRemove)
- mux.HandleFunc("/user/callback/discord", server.handlerDiscordOAuthCallback)
-
- mux.HandleFunc("/user/login/patreon", server.handlerPatreonOAuthLogin)
- mux.HandleFunc("/user/new/patreon", server.handlerPatreonOAuthSignup)
- mux.HandleFunc("/user/add/patreon", server.handlerPatreonOAuthAdd)
- mux.HandleFunc("/user/remove/patreon", server.handlerPatreonOAuthRemove)
- mux.HandleFunc("/user/callback/patreon", server.handlerPatreonOAuthCallback)
+ mux.HandleFunc("/oauth/twitch", server.handlerTwitchOAuth)
+ mux.HandleFunc("/oauth/twitch/callback", server.handlerTwitchOAuthCallback)
+
+ mux.HandleFunc("/oauth/discord", server.handlerDiscordOAuth)
+ mux.HandleFunc("/oauth/discord/callback", server.handlerDiscordOAuthCallback)
+
+ mux.HandleFunc("/oauth/patreon", server.handlerPatreonOAuth)
+ mux.HandleFunc("/oauth/patreon/callback", server.handlerPatreonOAuthCallback)
mux.HandleFunc("/user/logout", server.handlerUserLogout)
mux.HandleFunc("/user/new", server.handlerUserNew)
diff --git a/templates/account.html b/templates/account.html
index 8a126f6..6e50756 100644
--- a/templates/account.html
+++ b/templates/account.html
@@ -76,9 +76,9 @@
{{if .TwitchOAuthEnabled}}
{{ end }}
@@ -87,9 +87,9 @@
{{if .DiscordOAuthEnabled}}
{{end}}
@@ -98,9 +98,9 @@
{{if .PatreonOAuthEnabled}}
{{end}}
diff --git a/templates/newaccount.html b/templates/newaccount.html
index 06c3f8c..595407d 100644
--- a/templates/newaccount.html
+++ b/templates/newaccount.html
@@ -25,13 +25,13 @@
{{if .OAuth}}
{{end}}
diff --git a/templates/plain-login.html b/templates/plain-login.html
index a1a2057..18f5447 100644
--- a/templates/plain-login.html
+++ b/templates/plain-login.html
@@ -28,13 +28,13 @@
{{if .OAuth}}
{{end}}
From f7727b4c286ef486498212b93e88a36e332db8a7 Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Wed, 6 Jan 2021 22:56:10 +0100
Subject: [PATCH 50/67] Moved handlers around (no functional changes)
---
server.go | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/server.go b/server.go
index dd0f7c7..e331e35 100644
--- a/server.go
+++ b/server.go
@@ -204,9 +204,6 @@ func NewServer(options Options) (*Server, error) {
// list of past cycles
mux.HandleFunc("/history", server.handlerHistory)
- mux.HandleFunc("/user", server.handlerUser)
- mux.HandleFunc("/user/login", server.handlerUserLogin)
-
mux.HandleFunc("/oauth/twitch", server.handlerTwitchOAuth)
mux.HandleFunc("/oauth/twitch/callback", server.handlerTwitchOAuthCallback)
@@ -216,6 +213,8 @@ func NewServer(options Options) (*Server, error) {
mux.HandleFunc("/oauth/patreon", server.handlerPatreonOAuth)
mux.HandleFunc("/oauth/patreon/callback", server.handlerPatreonOAuthCallback)
+ mux.HandleFunc("/user", server.handlerUser)
+ mux.HandleFunc("/user/login", server.handlerUserLogin)
mux.HandleFunc("/user/logout", server.handlerUserLogout)
mux.HandleFunc("/user/new", server.handlerUserNew)
mux.HandleFunc("/user/remove/local", server.handlerLocalAuthRemove)
From dc3c85c25e8d1eb35015d789b2d33451f7228aca Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Sat, 16 Jan 2021 02:03:08 +0100
Subject: [PATCH 51/67] Removed the field RateLimitOverwrite field from the
user
This closes #79.
---
admin.go | 1 -
common/user.go | 4 +---
data/json.go | 5 +----
3 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/admin.go b/admin.go
index 0b2a888..661719e 100644
--- a/admin.go
+++ b/admin.go
@@ -103,7 +103,6 @@ func (s *Server) adminDeleteUser(w http.ResponseWriter, r *http.Request, user *c
user.NotifyCycleEnd = false
user.NotifyVoteSelection = false
user.Privilege = 0
- user.RateLimitOverride = false
err := s.data.UpdateUser(user)
if err != nil {
diff --git a/common/user.go b/common/user.go
index 9fd4605..528a850 100644
--- a/common/user.go
+++ b/common/user.go
@@ -24,9 +24,7 @@ type User struct {
AuthMethods []*AuthMethod
- // Does this user ignore rate limit? (default true for mod/admin)
- RateLimitOverride bool
- LastMovieAdd time.Time
+ LastMovieAdd time.Time
}
func (u User) CheckPriv(lvl string) bool {
diff --git a/data/json.go b/data/json.go
index f4ae1c2..f9fcd75 100644
--- a/data/json.go
+++ b/data/json.go
@@ -110,8 +110,7 @@ type jsonUser struct {
AuthMethods []int
- RateLimitOverride bool
- LastMovieAdd *time.Time
+ LastMovieAdd *time.Time
}
func (j *jsonConnector) newJsonUser(user *common.User) jsonUser {
@@ -132,7 +131,6 @@ func (j *jsonConnector) newJsonUser(user *common.User) jsonUser {
NotifyVoteSelection: user.NotifyVoteSelection,
Privilege: int(user.Privilege),
AuthMethods: authMethods,
- RateLimitOverride: user.RateLimitOverride,
LastMovieAdd: &user.LastMovieAdd,
}
@@ -609,7 +607,6 @@ func (j *jsonConnector) userFromJson(jUser jsonUser) *common.User {
NotifyCycleEnd: jUser.NotifyCycleEnd,
NotifyVoteSelection: jUser.NotifyVoteSelection,
AuthMethods: authMethods,
- RateLimitOverride: jUser.RateLimitOverride,
LastMovieAdd: *jUser.LastMovieAdd,
Privilege: common.PrivilegeLevel(jUser.Privilege),
}
From 3d19f027c279a8182df366b9819079b8d28a0f4e Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Sat, 16 Jan 2021 03:54:22 +0100
Subject: [PATCH 52/67] Added a forgotten "ensurance" of the AuthMethods map
---
data/json.go | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/data/json.go b/data/json.go
index f9fcd75..87fb06b 100644
--- a/data/json.go
+++ b/data/json.go
@@ -273,6 +273,10 @@ func loadJson(filename string, l *common.Logger) (*jsonConnector, error) {
data.Links = make(map[int]*common.Link)
}
+ if data.AuthMethods == nil {
+ data.AuthMethods = make(map[int]*common.AuthMethod)
+ }
+
return data, nil
}
From 87d6d146440b5048fecb84db5643c3d775c2648a Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Sat, 16 Jan 2021 04:00:48 +0100
Subject: [PATCH 53/67] Removed the "LastMovieAdd" field from the user.
While i was at it i also removed it in the (outdated) tests.
---
common/user.go | 2 --
data/connectors_test.go | 1 -
data/helpers_test.go | 3 ---
data/json.go | 4 ----
4 files changed, 10 deletions(-)
diff --git a/common/user.go b/common/user.go
index 528a850..0245d2e 100644
--- a/common/user.go
+++ b/common/user.go
@@ -23,8 +23,6 @@ type User struct {
Privilege PrivilegeLevel
AuthMethods []*AuthMethod
-
- LastMovieAdd time.Time
}
func (u User) CheckPriv(lvl string) bool {
diff --git a/data/connectors_test.go b/data/connectors_test.go
index 74ba9b8..f7999b0 100644
--- a/data/connectors_test.go
+++ b/data/connectors_test.go
@@ -171,7 +171,6 @@ func Test_AddUser(t *testing.T) {
Privilege: common.PRIV_MOD,
PassDate: passDate,
RateLimitOverride: true,
- LastMovieAdd: passDate.Add(time.Hour),
}
uid, err := conn.AddUser(testUser)
diff --git a/data/helpers_test.go b/data/helpers_test.go
index ced5405..8d704e7 100644
--- a/data/helpers_test.go
+++ b/data/helpers_test.go
@@ -130,9 +130,6 @@ func compareUsers(a, b *common.User, t *testing.T) {
// t.Fatalf("RateLimitOverride mismatch: %t vs %t", user.RateLimitOverride, loggedIn.RateLimitOverride)
//}
- //if user.LastMovieAdd != loggedIn.LastMovieAdd {
- // t.Fatalf("LastMovieAdd mismatch: %s vs %s", user.LastMovieAdd, loggedIn.LastMovieAdd)
- //}
}
func compareMovies(a, b *common.Movie, t *testing.T) {
diff --git a/data/json.go b/data/json.go
index 87fb06b..2e3782e 100644
--- a/data/json.go
+++ b/data/json.go
@@ -109,8 +109,6 @@ type jsonUser struct {
Privilege int
AuthMethods []int
-
- LastMovieAdd *time.Time
}
func (j *jsonConnector) newJsonUser(user *common.User) jsonUser {
@@ -131,7 +129,6 @@ func (j *jsonConnector) newJsonUser(user *common.User) jsonUser {
NotifyVoteSelection: user.NotifyVoteSelection,
Privilege: int(user.Privilege),
AuthMethods: authMethods,
- LastMovieAdd: &user.LastMovieAdd,
}
return ju
@@ -611,7 +608,6 @@ func (j *jsonConnector) userFromJson(jUser jsonUser) *common.User {
NotifyCycleEnd: jUser.NotifyCycleEnd,
NotifyVoteSelection: jUser.NotifyVoteSelection,
AuthMethods: authMethods,
- LastMovieAdd: *jUser.LastMovieAdd,
Privilege: common.PrivilegeLevel(jUser.Privilege),
}
From 2a169310dd04599c52998dfd114987826d1d5a4a Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Sat, 16 Jan 2021 04:03:37 +0100
Subject: [PATCH 54/67] Added new migration script "migrateUsers.go"
---
cmd/migrateUsers.go | 126 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 126 insertions(+)
create mode 100644 cmd/migrateUsers.go
diff --git a/cmd/migrateUsers.go b/cmd/migrateUsers.go
new file mode 100644
index 0000000..c825cd5
--- /dev/null
+++ b/cmd/migrateUsers.go
@@ -0,0 +1,126 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "time"
+
+ "github.com/mitchellh/mapstructure"
+ mpc "github.com/zorchenhimer/MoviePolls/common"
+ mpd "github.com/zorchenhimer/MoviePolls/data"
+)
+
+const jsonFilename string = "db/data.json"
+
+func main() {
+ err := loadOldDB()
+ if err != nil {
+ fmt.Printf("%v\n", err)
+ }
+}
+
+type OldUser struct {
+ Id int
+ Name string
+ Password string
+ OAuthToken string
+ Email string
+ NotifyCycleEnd bool
+ NotifyVoteSelection bool
+ Priviledge int
+ PassDate time.Time
+ RateLimitOverride bool
+ LastMovieAdd time.Time
+}
+
+func loadOldDB() error {
+
+ data, err := ioutil.ReadFile(jsonFilename)
+ if err != nil {
+ fmt.Println(err)
+ }
+
+ var fullData map[string]interface{}
+
+ json.Unmarshal(data, &fullData)
+
+ // fullData now contains the whole db
+ jUsers := fullData["Users"].(map[string]interface{})
+ oldUsers := []OldUser{}
+ for _, user := range jUsers {
+ ou := &OldUser{}
+ mapstructure.Decode(user, &ou)
+ ou.PassDate, _ = time.Parse(time.RFC3339, user.(map[string]interface{})["PassDate"].(string))
+ oldUsers = append(oldUsers, *ou)
+ }
+
+ delete(fullData, "Users")
+
+ data, err = json.MarshalIndent(fullData, "", " ")
+
+ // write a temporary file which contains the db without the "users"
+ err = ioutil.WriteFile("db/temp.json", data, 0666)
+ if err != nil {
+ return err
+ }
+
+ l, err := mpc.NewLogger(mpc.LLDebug, "")
+
+ if err != nil {
+ return err
+ }
+
+ dc, err := mpd.GetDataConnector("json", "db/temp.json", l)
+
+ if err != nil {
+ return err
+ }
+
+ for _, oldUser := range oldUsers {
+
+ l.Debug("Old User Passdate: %s", oldUser.PassDate)
+
+ // convert old movies to new ones
+ newUser := mpc.User{
+ Id: oldUser.Id,
+ Name: oldUser.Name,
+ Email: oldUser.Email,
+ NotifyCycleEnd: oldUser.NotifyCycleEnd,
+ NotifyVoteSelection: oldUser.NotifyVoteSelection,
+ Privilege: mpc.PrivilegeLevel(oldUser.Priviledge),
+ }
+
+ // Do NOT build new entries for users with empty passwords (i.e. Deleted users)
+ if oldUser.Password != "" {
+ authMethod := &mpc.AuthMethod{
+ Type: mpc.AUTH_LOCAL,
+ Password: oldUser.Password,
+ PassDate: oldUser.PassDate,
+ }
+
+ l.Debug("New User Passdate: %v", authMethod.PassDate)
+
+ newUser.AuthMethods = []*mpc.AuthMethod{}
+
+ id, err := dc.AddAuthMethod(authMethod)
+
+ if err != nil {
+ return err
+ }
+
+ authMethod.Id = id
+
+ newUser.AuthMethods = append(newUser.AuthMethods, authMethod)
+ }
+ dc.UpdateUser(&newUser)
+ }
+
+ err = os.Rename("db/temp.json", jsonFilename)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
From 2965e7ef7ae03cbc80ceea0ef396ab686813e550 Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Sat, 16 Jan 2021 04:05:39 +0100
Subject: [PATCH 55/67] Remove unused import
---
common/user.go | 1 -
1 file changed, 1 deletion(-)
diff --git a/common/user.go b/common/user.go
index 0245d2e..a3773b7 100644
--- a/common/user.go
+++ b/common/user.go
@@ -2,7 +2,6 @@ package common
import (
"fmt"
- "time"
)
type PrivilegeLevel int
From 67274830d0fbcdf689a12ff9ca4ea900b6fd099a Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Sat, 16 Jan 2021 04:48:08 +0100
Subject: [PATCH 56/67] Fixed a stacktrace when logging in with a non existin
username
---
data/json.go | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/data/json.go b/data/json.go
index 2e3782e..d46af60 100644
--- a/data/json.go
+++ b/data/json.go
@@ -645,16 +645,18 @@ func (j *jsonConnector) UserLocalLogin(name, hashedPw string) (*common.User, err
user := j.findUserByName(name)
- for _, auth := range user.AuthMethods {
- if auth.Type == "Local" {
- if hashedPw == auth.Password {
- return user, nil
+ if user != nil {
+ for _, auth := range user.AuthMethods {
+ if auth.Type == "Local" {
+ if hashedPw == auth.Password {
+ return user, nil
+ }
+ j.l.Info("Bad password for user %s\n", name)
+ return nil, fmt.Errorf("Invalid login credentials")
}
- j.l.Info("Bad password for user %s\n", name)
- return nil, fmt.Errorf("Invalid login credentials")
}
- }
+ }
j.l.Info("User with name %s not found\n", name)
return nil, fmt.Errorf("Invalid login credentials")
}
From 5f0a28b33f805c57f50ad086958eb66b284735c7 Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Sat, 16 Jan 2021 04:48:58 +0100
Subject: [PATCH 57/67] Fixed a typo and parse the time.Time by hand for
REASONS
---
cmd/migrateUsers.go | 28 +++++++++++++++++++++-------
1 file changed, 21 insertions(+), 7 deletions(-)
diff --git a/cmd/migrateUsers.go b/cmd/migrateUsers.go
index c825cd5..7282226 100644
--- a/cmd/migrateUsers.go
+++ b/cmd/migrateUsers.go
@@ -29,7 +29,7 @@ type OldUser struct {
Email string
NotifyCycleEnd bool
NotifyVoteSelection bool
- Priviledge int
+ Privilege int
PassDate time.Time
RateLimitOverride bool
LastMovieAdd time.Time
@@ -52,7 +52,16 @@ func loadOldDB() error {
for _, user := range jUsers {
ou := &OldUser{}
mapstructure.Decode(user, &ou)
- ou.PassDate, _ = time.Parse(time.RFC3339, user.(map[string]interface{})["PassDate"].(string))
+ // lets parse the fucking time by hand
+ if user.(map[string]interface{})["PassDate"] == nil {
+ fmt.Println("[ERROR] Could not parse field 'PassDate', was the database already converted?")
+ return nil
+ }
+ ou.PassDate, err = time.Parse(time.RFC3339, user.(map[string]interface{})["PassDate"].(string))
+ if err != nil {
+ fmt.Println("[ERROR] Could not parse field 'PassDate', was the database already converted?")
+ return nil
+ }
oldUsers = append(oldUsers, *ou)
}
@@ -80,8 +89,6 @@ func loadOldDB() error {
for _, oldUser := range oldUsers {
- l.Debug("Old User Passdate: %s", oldUser.PassDate)
-
// convert old movies to new ones
newUser := mpc.User{
Id: oldUser.Id,
@@ -89,7 +96,16 @@ func loadOldDB() error {
Email: oldUser.Email,
NotifyCycleEnd: oldUser.NotifyCycleEnd,
NotifyVoteSelection: oldUser.NotifyVoteSelection,
- Privilege: mpc.PrivilegeLevel(oldUser.Priviledge),
+ Privilege: mpc.PrivilegeLevel(oldUser.Privilege),
+ }
+
+ switch oldUser.Privilege {
+ case 0:
+ newUser.Privilege = mpc.PRIV_USER
+ case 1:
+ newUser.Privilege = mpc.PRIV_MOD
+ case 2:
+ newUser.Privilege = mpc.PRIV_ADMIN
}
// Do NOT build new entries for users with empty passwords (i.e. Deleted users)
@@ -100,8 +116,6 @@ func loadOldDB() error {
PassDate: oldUser.PassDate,
}
- l.Debug("New User Passdate: %v", authMethod.PassDate)
-
newUser.AuthMethods = []*mpc.AuthMethod{}
id, err := dc.AddAuthMethod(authMethod)
From a06a6209206e90d4f47b3cecb32d63ee5a4d55c6 Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Mon, 18 Jan 2021 02:23:52 +0100
Subject: [PATCH 58/67] Minor changes found in my review
---
cmd/migrateUsers.go | 9 ---------
oauth.go | 3 +--
user.go | 2 +-
3 files changed, 2 insertions(+), 12 deletions(-)
diff --git a/cmd/migrateUsers.go b/cmd/migrateUsers.go
index 7282226..42f67b3 100644
--- a/cmd/migrateUsers.go
+++ b/cmd/migrateUsers.go
@@ -99,15 +99,6 @@ func loadOldDB() error {
Privilege: mpc.PrivilegeLevel(oldUser.Privilege),
}
- switch oldUser.Privilege {
- case 0:
- newUser.Privilege = mpc.PRIV_USER
- case 1:
- newUser.Privilege = mpc.PRIV_MOD
- case 2:
- newUser.Privilege = mpc.PRIV_ADMIN
- }
-
// Do NOT build new entries for users with empty passwords (i.e. Deleted users)
if oldUser.Password != "" {
authMethod := &mpc.AuthMethod{
diff --git a/oauth.go b/oauth.go
index 2dbdb2a..2eac4e6 100644
--- a/oauth.go
+++ b/oauth.go
@@ -18,7 +18,6 @@ var twitchOAuthConfig = &oauth2.Config{}
var discordOAuthConfig = &oauth2.Config{}
var patreonOAuthConfig = &oauth2.Config{}
-// var oauthStateString string
var openStates = []string{}
// Welp we need to do the endpoints ourself i guess ...
@@ -405,7 +404,7 @@ func (s *Server) handlerTwitchOAuthCallback(w http.ResponseWriter, r *http.Reque
NotifyVoteSelection: false,
}
- // add this new server to the database
+ // add this new user to the database
newUser.Id, err = s.data.AddUser(newUser)
if err != nil {
diff --git a/user.go b/user.go
index 5829dae..2c58580 100644
--- a/user.go
+++ b/user.go
@@ -224,7 +224,7 @@ func (s *Server) handlerUser(w http.ResponseWriter, r *http.Request) {
}
if !(data.ErrCurrentPass || data.ErrNewPass || data.ErrEmail) {
// Change pass
- data.SuccessMessage = "Password successfully changed"
+ data.SuccessMessage = "Password successfully set"
localAuth.Password = s.hashPassword(pass1_raw)
localAuth.PassDate = time.Now()
s.l.Info("new PassDate: %s", localAuth.PassDate)
From 3f5bbf690035e3294ef51d326a1e8ce5b233e154 Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Mon, 18 Jan 2021 02:56:01 +0100
Subject: [PATCH 59/67] Added new config values to en/disable oauth signup
---
admin.go | 3 +++
auth.go | 1 -
server.go | 42 ++++++++++++++++++++++-----------------
templates/newaccount.html | 6 +++---
user.go | 35 ++++++++++++++++++++++++++++----
5 files changed, 61 insertions(+), 26 deletions(-)
diff --git a/admin.go b/admin.go
index 661719e..f5d5231 100644
--- a/admin.go
+++ b/admin.go
@@ -390,12 +390,15 @@ func (s *Server) handlerAdminConfig(w http.ResponseWriter, r *http.Request) {
configValue{Key: ConfigUnlimitedVotes, Default: DefaultUnlimitedVotes, Type: ConfigBool},
configValue{Key: ConfigTwitchOauthEnabled, Default: DefaultTwitchOauthEnabled, Type: ConfigBool},
+ configValue{Key: ConfigTwitchOauthSignupEnabled, Default: DefaultTwitchOauthSignupEnabled, Type: ConfigBool},
configValue{Key: ConfigTwitchOauthClientID, Default: DefaultTwitchOauthClientID, Type: ConfigString},
configValue{Key: ConfigTwitchOauthClientSecret, Default: DefaultTwitchOauthClientSecret, Type: ConfigString},
configValue{Key: ConfigDiscordOauthEnabled, Default: DefaultDiscordOauthEnabled, Type: ConfigBool},
+ configValue{Key: ConfigDiscordOauthSignupEnabled, Default: DefaultDiscordOauthSignupEnabled, Type: ConfigBool},
configValue{Key: ConfigDiscordOauthClientID, Default: DefaultDiscordOauthClientID, Type: ConfigString},
configValue{Key: ConfigDiscordOauthClientSecret, Default: DefaultDiscordOauthClientSecret, Type: ConfigString},
configValue{Key: ConfigPatreonOauthEnabled, Default: DefaultPatreonOauthEnabled, Type: ConfigBool},
+ configValue{Key: ConfigPatreonOauthSignupEnabled, Default: DefaultPatreonOauthSignupEnabled, Type: ConfigBool},
configValue{Key: ConfigPatreonOauthClientID, Default: DefaultPatreonOauthClientID, Type: ConfigString},
configValue{Key: ConfigPatreonOauthClientSecret, Default: DefaultPatreonOauthClientSecret, Type: ConfigString},
},
diff --git a/auth.go b/auth.go
index e805e6c..e22a41a 100644
--- a/auth.go
+++ b/auth.go
@@ -4,7 +4,6 @@ import (
"fmt"
"net/http"
"regexp"
- //"strconv"
"strings"
"time"
diff --git a/server.go b/server.go
index 72422e9..a7618dc 100644
--- a/server.go
+++ b/server.go
@@ -38,15 +38,18 @@ const (
DefaultMaxLinkLength int = 500 // length of all links combined
DefaultMaxRemarksLength int = 200
- DefaultTwitchOauthEnabled bool = false
- DefaultTwitchOauthClientID string = ""
- DefaultTwitchOauthClientSecret string = ""
- DefaultDiscordOauthEnabled bool = false
- DefaultDiscordOauthClientID string = ""
- DefaultDiscordOauthClientSecret string = ""
- DefaultPatreonOauthEnabled bool = false
- DefaultPatreonOauthClientID string = ""
- DefaultPatreonOauthClientSecret string = ""
+ DefaultTwitchOauthEnabled bool = false
+ DefaultTwitchOauthSignupEnabled bool = false
+ DefaultTwitchOauthClientID string = ""
+ DefaultTwitchOauthClientSecret string = ""
+ DefaultDiscordOauthEnabled bool = false
+ DefaultDiscordOauthSignupEnabled bool = false
+ DefaultDiscordOauthClientID string = ""
+ DefaultDiscordOauthClientSecret string = ""
+ DefaultPatreonOauthEnabled bool = false
+ DefaultPatreonOauthSignupEnabled bool = false
+ DefaultPatreonOauthClientID string = ""
+ DefaultPatreonOauthClientSecret string = ""
)
// configuration keys
@@ -71,15 +74,18 @@ const (
ConfigMaxLinkLength string = "MaxLinkLength"
ConfigMaxRemarksLength string = "MaxRemarksLength"
- ConfigTwitchOauthEnabled string = "TwitchOauthEnabled"
- ConfigTwitchOauthClientID string = "TwitchOauthClientID"
- ConfigTwitchOauthClientSecret string = "TwitchOauthSecret"
- ConfigDiscordOauthEnabled string = "DiscordOauthEnabled"
- ConfigDiscordOauthClientID string = "DiscordOauthClientID"
- ConfigDiscordOauthClientSecret string = "DiscordOauthClientSecret"
- ConfigPatreonOauthEnabled string = "PatreonOauthEnabled"
- ConfigPatreonOauthClientID string = "PatreonOauthClientID"
- ConfigPatreonOauthClientSecret string = "PatreonOauthClientSecret"
+ ConfigTwitchOauthEnabled string = "TwitchOauthEnabled"
+ ConfigTwitchOauthSignupEnabled string = "TwitchOauthSignupEnabled"
+ ConfigTwitchOauthClientID string = "TwitchOauthClientID"
+ ConfigTwitchOauthClientSecret string = "TwitchOauthSecret"
+ ConfigDiscordOauthEnabled string = "DiscordOauthEnabled"
+ ConfigDiscordOauthSignupEnabled string = "DiscordOauthSignupEnabled"
+ ConfigDiscordOauthClientID string = "DiscordOauthClientID"
+ ConfigDiscordOauthClientSecret string = "DiscordOauthClientSecret"
+ ConfigPatreonOauthEnabled string = "PatreonOauthEnabled"
+ ConfigPatreonOauthSignupEnabled string = "PatreonOauthSignupEnabled"
+ ConfigPatreonOauthClientID string = "PatreonOauthClientID"
+ ConfigPatreonOauthClientSecret string = "PatreonOauthClientSecret"
)
type Options struct {
diff --git a/templates/newaccount.html b/templates/newaccount.html
index 595407d..edb6cd0 100644
--- a/templates/newaccount.html
+++ b/templates/newaccount.html
@@ -24,13 +24,13 @@
diff --git a/user.go b/user.go
index 2c58580..2c55760 100644
--- a/user.go
+++ b/user.go
@@ -413,10 +413,13 @@ func (s *Server) handlerUserNew(w http.ResponseWriter, r *http.Request) {
ErrPass bool
ErrEmail bool
- OAuth bool
- TwitchOAuth bool
- DiscordOAuth bool
- PatreonOAuth bool
+ OAuth bool
+ TwitchOAuth bool
+ TwitchSignup bool
+ DiscordOAuth bool
+ DiscordSignup bool
+ PatreonOAuth bool
+ PatreonSignup bool
ValName string
ValEmail string
@@ -436,6 +439,14 @@ func (s *Server) handlerUserNew(w http.ResponseWriter, r *http.Request) {
}
data.TwitchOAuth = twitchAuth
+ twitchSignup, err := s.data.GetCfgBool(ConfigTwitchOauthSignupEnabled, DefaultTwitchOauthSignupEnabled)
+ if err != nil {
+ s.doError(http.StatusInternalServerError, "Something went wrong :C", w, r)
+ s.l.Error("Unable to get ConfigTwitchOauthEnabled config value: %v", err)
+ return
+ }
+ data.TwitchSignup = twitchSignup
+
discordAuth, err := s.data.GetCfgBool(ConfigDiscordOauthEnabled, DefaultDiscordOauthEnabled)
if err != nil {
s.doError(http.StatusInternalServerError, "Something went wrong :C", w, r)
@@ -444,6 +455,14 @@ func (s *Server) handlerUserNew(w http.ResponseWriter, r *http.Request) {
}
data.DiscordOAuth = discordAuth
+ discordSignup, err := s.data.GetCfgBool(ConfigDiscordOauthSignupEnabled, DefaultDiscordOauthSignupEnabled)
+ if err != nil {
+ s.doError(http.StatusInternalServerError, "Something went wrong :C", w, r)
+ s.l.Error("Unable to get ConfigDiscordOauthEnabled config value: %v", err)
+ return
+ }
+ data.DiscordSignup = discordSignup
+
patreonAuth, err := s.data.GetCfgBool(ConfigPatreonOauthEnabled, DefaultPatreonOauthEnabled)
if err != nil {
s.doError(http.StatusInternalServerError, "Something went wrong :C", w, r)
@@ -452,6 +471,14 @@ func (s *Server) handlerUserNew(w http.ResponseWriter, r *http.Request) {
}
data.PatreonOAuth = patreonAuth
+ patreonSignup, err := s.data.GetCfgBool(ConfigPatreonOauthSignupEnabled, DefaultPatreonOauthSignupEnabled)
+ if err != nil {
+ s.doError(http.StatusInternalServerError, "Something went wrong :C", w, r)
+ s.l.Error("Unable to get ConfigPatreonOauthEnabled config value: %v", err)
+ return
+ }
+ data.PatreonSignup = patreonSignup
+
data.OAuth = twitchAuth || discordAuth || patreonAuth
if r.Method == "POST" {
From 04f9a6ecdd2792262a895b1986d89b2f44f4009d Mon Sep 17 00:00:00 2001
From: CptPie <23438606+CptPie@users.noreply.github.com>
Date: Mon, 18 Jan 2021 03:42:56 +0100
Subject: [PATCH 60/67] Added an other config value to en/disable local signup
Changed the signup page accordingly
---
admin.go | 42 +++++++++++++++++++++++++++++++++++++++
server.go | 2 ++
templates/newaccount.html | 2 ++
user.go | 15 +++++++++++---
4 files changed, 58 insertions(+), 3 deletions(-)
diff --git a/admin.go b/admin.go
index f5d5231..680e673 100644
--- a/admin.go
+++ b/admin.go
@@ -389,6 +389,7 @@ func (s *Server) handlerAdminConfig(w http.ResponseWriter, r *http.Request) {
configValue{Key: ConfigUnlimitedVotes, Default: DefaultUnlimitedVotes, Type: ConfigBool},
+ configValue{Key: ConfigLocalSignupEnabled, Default: DefaultLocalSignupEnabled, Type: ConfigBool},
configValue{Key: ConfigTwitchOauthEnabled, Default: DefaultTwitchOauthEnabled, Type: ConfigBool},
configValue{Key: ConfigTwitchOauthSignupEnabled, Default: DefaultTwitchOauthSignupEnabled, Type: ConfigBool},
configValue{Key: ConfigTwitchOauthClientID, Default: DefaultTwitchOauthClientID, Type: ConfigString},
@@ -512,6 +513,47 @@ func (s *Server) handlerAdminConfig(w http.ResponseWriter, r *http.Request) {
data.ErrorMessage = append(data.ErrorMessage, err.Error())
}
+ // getting ALL the booleans
+ var localSignup, twitchSignup, patreonSignup, discordSignup, twitchOauth, patreonOauth, discordOauth bool
+
+ // lets better hope that THIS NEVER FAILS to assign a valid boolean
+ for _, val := range data.Values {
+ switch val.Key {
+ case ConfigLocalSignupEnabled:
+ localSignup = val.Value.(bool)
+ case ConfigTwitchOauthSignupEnabled:
+ twitchSignup = val.Value.(bool)
+ case ConfigTwitchOauthEnabled:
+ twitchOauth = val.Value.(bool)
+ case ConfigDiscordOauthSignupEnabled:
+ discordSignup = val.Value.(bool)
+ case ConfigDiscordOauthEnabled:
+ discordOauth = val.Value.(bool)
+ case ConfigPatreonOauthSignupEnabled:
+ patreonSignup = val.Value.(bool)
+ case ConfigPatreonOauthEnabled:
+ patreonOauth = val.Value.(bool)
+ }
+ }
+
+ // Check that we have atleast ONE signup method enabled
+ if !(localSignup || twitchSignup || discordSignup || patreonSignup) {
+ data.ErrorMessage = append(data.ErrorMessage, "No Signup method is currently enabled, please ensure to enable atleast one method")
+ }
+
+ // Check that the corresponding oauth for the signup is enabled
+ if twitchSignup && !twitchOauth {
+ data.ErrorMessage = append(data.ErrorMessage, "To enable twitch signup you need to also enable twitch Oauth (and fill the token/secret)")
+ }
+
+ if discordSignup && !discordOauth {
+ data.ErrorMessage = append(data.ErrorMessage, "To enable discord signup you need to also enable discord Oauth (and fill the token/secret)")
+ }
+
+ if patreonSignup && !patreonOauth {
+ data.ErrorMessage = append(data.ErrorMessage, "To enable patreon signup you need to also enable patreon Oauth (and fill the token/secret)")
+ }
+
if err := s.executeTemplate(w, "adminConfig", data); err != nil {
s.l.Error("Error rendering template: %v", err)
}
diff --git a/server.go b/server.go
index a7618dc..3ae4872 100644
--- a/server.go
+++ b/server.go
@@ -38,6 +38,7 @@ const (
DefaultMaxLinkLength int = 500 // length of all links combined
DefaultMaxRemarksLength int = 200
+ DefaultLocalSignupEnabled bool = true
DefaultTwitchOauthEnabled bool = false
DefaultTwitchOauthSignupEnabled bool = false
DefaultTwitchOauthClientID string = ""
@@ -74,6 +75,7 @@ const (
ConfigMaxLinkLength string = "MaxLinkLength"
ConfigMaxRemarksLength string = "MaxRemarksLength"
+ ConfigLocalSignupEnabled string = "LocalSignupEnabled"
ConfigTwitchOauthEnabled string = "TwitchOauthEnabled"
ConfigTwitchOauthSignupEnabled string = "TwitchOauthSignupEnabled"
ConfigTwitchOauthClientID string = "TwitchOauthClientID"
diff --git a/templates/newaccount.html b/templates/newaccount.html
index edb6cd0..65d28ed 100644
--- a/templates/newaccount.html
+++ b/templates/newaccount.html
@@ -10,6 +10,7 @@