From a8cf6aa54f6a8c5df1f06e2c9476180a99bff63d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20R=C3=B6ger?= <jonas.roeger@tu-dortmund.de> Date: Sun, 13 Oct 2019 17:52:20 +0200 Subject: [PATCH] Added server side ability for reporting bugs --- internal/bugreport.go | 91 +++++++++++++++++++++++++++++++++++++++++++ internal/server.go | 46 ++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 internal/bugreport.go diff --git a/internal/bugreport.go b/internal/bugreport.go new file mode 100644 index 0000000..68e5d84 --- /dev/null +++ b/internal/bugreport.go @@ -0,0 +1,91 @@ +package wtfd + +import ( + "errors" + "fmt" + "time" + "strconv" + "net/smtp" +) + +var ( + serviceDeskDomain = "jroeger.de" + serviceDeskUser = "noreply" + serviceDeskPort = 25 // server to server smtp port + serviceDeskEnabled = true + rateLimitInterval float64 = 180 // 3 Minutes + rateLimitReports = 2 // 2 Reports during interval before beeing rate limited + + userAccess map[string]access = make(map[string]access) +) + +type access struct { + lastBlock time.Time // Currently unused + lastAccess []time.Time +} + +/** + * Check if user is rate limited + */ +func BRIsUserRateLimited(u *User) bool { + record, ok := userAccess[u.Name] + if !ok { + return false + } + + /* Ok if no critical ammount of records */ + if len(record.lastAccess) < rateLimitReports { + return false + } + + /* Check if earliest record is in interval, then block */ + if time.Since(record.lastAccess[0]).Seconds() < rateLimitInterval { + return true + } + + return false +} + +/** + * Register a user access + */ +func registerUserAccess(u *User) { + record, ok := userAccess[u.Name] + + if !ok { + /* New record */ + record = access{ + lastBlock: time.Time{}, + lastAccess: []time.Time{time.Now()}, + } + } else if len(record.lastAccess) < rateLimitReports { + /* No critical ammount of records */ + record.lastAccess = append(record.lastAccess, time.Now()) + } else if !BRIsUserRateLimited(u) { + /* Cycle access */ + record.lastAccess = record.lastAccess[1:] + record.lastAccess = append(record.lastAccess, time.Now()) + } + userAccess[u.Name] = record +} + +/** + * Send bugreport + */ +func BRDispatchBugreport(u *User, subject string, content string) error { + if !serviceDeskEnabled { + return errors.New("Service Desk is disabled") + } + + recipient := serviceDeskUser + "@" + serviceDeskDomain + recipients := []string{recipient} + formatContent := fmt.Sprintf("From: %s\nSubject: %s\n\n%s", u.Name, subject, content) + + + err := smtp.SendMail(serviceDeskDomain+":"+strconv.Itoa(serviceDeskPort), + nil, u.Name, recipients, []byte(formatContent)) + if err == nil { + registerUserAccess(u) + } + return err +} diff --git a/internal/server.go b/internal/server.go index 3f709fc..18c0d36 100644 --- a/internal/server.go +++ b/internal/server.go @@ -300,6 +300,51 @@ func logout(w http.ResponseWriter, r *http.Request) { } +func reportBug(w http.ResponseWriter, r *http.Request) { + var err error + + if r.Method != "POST" { + w.WriteHeader(http.StatusBadRequest) + _, _ = fmt.Fprintf(w, "Invalid Request") + return + } + + /* Check user login */ + user, ok := getUser(r) + if !ok { + w.WriteHeader(http.StatusInternalServerError) + _, _ = fmt.Fprintf(w, "Server Error: %v", "Not logged in") + return + } + + /* Check if user is rate limited */ + if BRIsUserRateLimited(&user) { + w.WriteHeader(http.StatusTooManyRequests) + _, _ = fmt.Fprint(w, "Too many requsets") + return + } + + /* Read and Check form */ + if err = r.ParseForm() ; err != nil { + w.WriteHeader(http.StatusInternalServerError) + _, _ = fmt.Fprintf(w, "Server Error: %v", "Not logged in") + return + } + subject := r.FormValue("subject") + content := r.FormValue("content") + if subject == "" || content == "" { + w.WriteHeader(http.StatusBadRequest) + fmt.Fprint(w, "Invaild Request") + return + } + + /* Try to dispatch bugreport */ + if err = BRDispatchBugreport(&user, subject , content); err != nil { + w.WriteHeader(http.StatusInternalServerError) + _, _ = fmt.Fprintf(w, "Server Error: %v", err) + } +} + func solutionview(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) chall, err := challs.Find(vars["chall"]) @@ -493,6 +538,7 @@ func Server() error { r.HandleFunc("/register", register) r.HandleFunc("/submitflag", submitFlag) r.HandleFunc("/ws", leaderboardWS) + r.HandleFunc("/reportbug", reportBug) r.HandleFunc("/{chall}", mainpage) r.HandleFunc("/detailview/{chall}", detailview) r.HandleFunc("/solutionview/{chall}", solutionview) -- GitLab