diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite index edded18..8441856 100644 Binary files a/.vs/slnx.sqlite and b/.vs/slnx.sqlite differ diff --git a/api/common.go b/api/common.go index 1f287cd..bd4cf8a 100644 --- a/api/common.go +++ b/api/common.go @@ -70,6 +70,8 @@ func Init(mux *http.ServeMux) error { // admin mux.HandleFunc("POST /admin/account/discord-link", handleAdminDiscordLink) mux.HandleFunc("POST /admin/account/discord-unlink", handleAdminDiscordUnlink) + mux.HandleFunc("POST /admin/account/google-link", handleAdminGoogleLink) + mux.HandleFunc("POST /admin/account/google-unlink", handleAdminGoogleUnlink) mux.HandleFunc("GET /admin/account/admin-search", handleAdminSearch) return nil diff --git a/api/endpoints.go b/api/endpoints.go index 84e34d9..a1232c8 100644 --- a/api/endpoints.go +++ b/api/endpoints.go @@ -697,10 +697,10 @@ func handleAdminDiscordLink(w http.ResponseWriter, r *http.Request) { discordId := r.Form.Get("discordId") // this does a quick call to make sure the username exists on the server before allowing the rest of the code to run - // this calls error value 204 (StatusNoContent) if there's no data; this means the username does not exist in the server + // this calls error value 404 (StatusNotFound) if there's no data; this means the username does not exist in the server _, err = db.CheckUsernameExists(username) if err != nil { - httpError(w, r, fmt.Errorf("username does not exist on the server"), http.StatusNoContent) + httpError(w, r, fmt.Errorf("username does not exist on the server"), http.StatusNotFound) return } @@ -751,10 +751,10 @@ func handleAdminDiscordUnlink(w http.ResponseWriter, r *http.Request) { if username != "" { log.Printf("Username given, removing discordId") // this does a quick call to make sure the username exists on the server before allowing the rest of the code to run - // this calls error value 204 (StatusNoContent) if there's no data; this means the username does not exist in the server + // this calls error value 404 (StatusNotFound) if there's no data; this means the username does not exist in the server _, err = db.CheckUsernameExists(username) if err != nil { - httpError(w, r, fmt.Errorf("username does not exist on the server"), http.StatusNoContent) + httpError(w, r, fmt.Errorf("username does not exist on the server"), http.StatusNotFound) return } err = db.RemoveDiscordIdByUsername(username) @@ -777,6 +777,115 @@ func handleAdminDiscordUnlink(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } +func handleAdminGoogleLink(w http.ResponseWriter, r *http.Request) { + err := r.ParseForm() + if err != nil { + httpError(w, r, fmt.Errorf("failed to parse request form: %s", err), http.StatusBadRequest) + return + } + + uuid, err := uuidFromRequest(r) + if err != nil { + httpError(w, r, err, http.StatusUnauthorized) + return + } + + userDiscordId, err := db.FetchDiscordIdByUUID(uuid) + if err != nil { + httpError(w, r, err, http.StatusUnauthorized) + return + } + + hasRole, err := account.IsUserDiscordAdmin(userDiscordId, account.DiscordGuildID) + if !hasRole || err != nil { + httpError(w, r, fmt.Errorf("user does not have the required role"), http.StatusForbidden) + return + } + + username := r.Form.Get("username") + googleId := r.Form.Get("googleId") + + // this does a quick call to make sure the username exists on the server before allowing the rest of the code to run + // this calls error value 404 (StatusNotFound) if there's no data; this means the username does not exist in the server + _, err = db.CheckUsernameExists(username) + if err != nil { + httpError(w, r, fmt.Errorf("username does not exist on the server"), http.StatusNotFound) + return + } + + err = db.AddGoogleIdByUsername(googleId, username) + if err != nil { + httpError(w, r, err, http.StatusInternalServerError) + return + } + + log.Printf("%s: %s added google id %s to username %s", r.URL.Path, userDiscordId, googleId, username) + + w.WriteHeader(http.StatusOK) +} + +func handleAdminGoogleUnlink(w http.ResponseWriter, r *http.Request) { + err := r.ParseForm() + if err != nil { + httpError(w, r, fmt.Errorf("failed to parse request form: %s", err), http.StatusBadRequest) + return + } + + uuid, err := uuidFromRequest(r) + if err != nil { + httpError(w, r, err, http.StatusUnauthorized) + return + } + + userDiscordId, err := db.FetchDiscordIdByUUID(uuid) + if err != nil { + httpError(w, r, err, http.StatusUnauthorized) + return + } + + hasRole, err := account.IsUserDiscordAdmin(userDiscordId, account.DiscordGuildID) + if !hasRole || err != nil { + httpError(w, r, fmt.Errorf("user does not have the required role"), http.StatusForbidden) + return + } + + if err != nil { + httpError(w, r, err, http.StatusUnauthorized) + return + } + + username := r.Form.Get("username") + googleId := r.Form.Get("googleId") + + if username != "" { + log.Printf("Username given, removing googleId") + // this does a quick call to make sure the username exists on the server before allowing the rest of the code to run + // this calls error value 404 (StatusNotFound) if there's no data; this means the username does not exist in the server + _, err = db.CheckUsernameExists(username) + if err != nil { + httpError(w, r, fmt.Errorf("username does not exist on the server"), http.StatusNotFound) + return + } + err = db.RemoveGoogleIdByUsername(username) + if err != nil { + httpError(w, r, err, http.StatusInternalServerError) + return + } + } + if googleId != "" { + log.Printf("DiscordID given, removing googleId") + err = db.RemoveGoogleIdByDiscordId(googleId) + if err != nil { + httpError(w, r, err, http.StatusInternalServerError) + return + } + } + + log.Printf("%s: %s removed google id %s from username %s", userDiscordId, r.URL.Path, r.Form.Get("googleId"), r.Form.Get("username")) + + w.WriteHeader(http.StatusOK) +} + func handleAdminSearch(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() if err != nil { @@ -810,10 +919,10 @@ func handleAdminSearch(w http.ResponseWriter, r *http.Request) { username := r.Form.Get("username") // this does a quick call to make sure the username exists on the server before allowing the rest of the code to run - // this calls error value 204 (StatusNoContent) if there's no data; this means the username does not exist in the server + // this calls error value 404 (StatusNotFound) if there's no data; this means the username does not exist in the server _, err = db.CheckUsernameExists(username) if err != nil { - httpError(w, r, fmt.Errorf("username does not exist on the server"), http.StatusNoContent) + httpError(w, r, fmt.Errorf("username does not exist on the server"), http.StatusNotFound) return } diff --git a/db/account.go b/db/account.go index cfd9c4e..7bacf35 100644 --- a/db/account.go +++ b/db/account.go @@ -183,14 +183,15 @@ type AdminSearchResponse struct { DiscordId string `json:"discordId"` GoogleId string `json:"googleId"` LastLoggedIn string `json:"lastLoggedIn"` + Registered string `json:"registered"` } func FetchAdminDetailsByUsername(dbUsername string) (AdminSearchResponse, error) { - var resultUsername, resultDiscordId, resultGoogleId, resultLastLoggedIn sql.NullString - var username, discordId, googleId, lastLoggedIn string + var resultUsername, resultDiscordId, resultGoogleId, resultLastLoggedIn, resultRegistered sql.NullString + var username, discordId, googleId, lastLoggedIn, registered string var adminResponse AdminSearchResponse - err := handle.QueryRow("SELECT username, discordId, googleId, lastLoggedIn from accounts WHERE username = ?", dbUsername).Scan(&resultUsername, &resultDiscordId, &resultGoogleId, &resultLastLoggedIn) + err := handle.QueryRow("SELECT username, discordId, googleId, lastLoggedIn, registered from accounts WHERE username = ?", dbUsername).Scan(&resultUsername, &resultDiscordId, &resultGoogleId, &resultLastLoggedIn, &resultRegistered) if err != nil { return adminResponse, err } @@ -219,11 +220,18 @@ func FetchAdminDetailsByUsername(dbUsername string) (AdminSearchResponse, error) lastLoggedIn = "" } + if resultRegistered.Valid { + registered = resultRegistered.String + } else { + registered = "" + } + adminResponse = AdminSearchResponse{ Username: username, DiscordId: discordId, GoogleId: googleId, LastLoggedIn: lastLoggedIn, + Registered: registered, } return adminResponse, nil @@ -436,6 +444,15 @@ func RemoveGoogleIdByUUID(uuid []byte) error { return nil } +func RemoveGoogleIdByUsername(username string) error { + _, err := handle.Exec("UPDATE accounts SET googleId = NULL WHERE username = ?", username) + if err != nil { + return err + } + + return nil +} + func RemoveDiscordIdByUsername(username string) error { _, err := handle.Exec("UPDATE accounts SET discordId = NULL WHERE username = ?", username) if err != nil { @@ -451,5 +468,14 @@ func RemoveDiscordIdByDiscordId(discordId string) error { return err } + return nil +} + +func RemoveGoogleIdByDiscordId(discordId string) error { + _, err := handle.Exec("UPDATE accounts SET googleId = NULL WHERE discordId = ?", discordId) + if err != nil { + return err + } + return nil } \ No newline at end of file