// API_SMTP handles requests for the /api/smtp/ endpoint func API_SMTP(w http.ResponseWriter, r *http.Request) { switch { case r.Method == "GET": ss, err := models.GetSMTPs(ctx.Get(r, "user_id").(int64)) if err != nil { Logger.Println(err) } JSONResponse(w, ss, http.StatusOK) //POST: Create a new SMTP and return it as JSON case r.Method == "POST": s := models.SMTP{} // Put the request into a page err := json.NewDecoder(r.Body).Decode(&s) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "Invalid request"}, http.StatusBadRequest) return } // Check to make sure the name is unique _, err = models.GetSMTPByName(s.Name, ctx.Get(r, "user_id").(int64)) if err != gorm.ErrRecordNotFound { JSONResponse(w, models.Response{Success: false, Message: "SMTP name already in use"}, http.StatusConflict) Logger.Println(err) return } s.ModifiedDate = time.Now() s.UserId = ctx.Get(r, "user_id").(int64) err = models.PostSMTP(&s) if err != nil { JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError) return } JSONResponse(w, s, http.StatusCreated) } }
// API_Groups returns a list of groups if requested via GET. // If requested via POST, API_Groups creates a new group and returns a reference to it. func API_Groups(w http.ResponseWriter, r *http.Request) { switch { case r.Method == "GET": gs, err := models.GetGroups(ctx.Get(r, "user_id").(int64)) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "No groups found"}, http.StatusNotFound) return } JSONResponse(w, gs, http.StatusOK) //POST: Create a new group and return it as JSON case r.Method == "POST": g := models.Group{} // Put the request into a group err := json.NewDecoder(r.Body).Decode(&g) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "Invalid JSON structure"}, http.StatusBadRequest) return } _, err = models.GetGroupByName(g.Name, ctx.Get(r, "user_id").(int64)) if err != gorm.ErrRecordNotFound { JSONResponse(w, models.Response{Success: false, Message: "Group name already in use"}, http.StatusConflict) return } g.ModifiedDate = time.Now() g.UserId = ctx.Get(r, "user_id").(int64) err = models.PostGroup(&g) if err != nil { JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) return } w.Header().Set("Location", "http://localhost:3333/api/groups/"+string(g.Id)) JSONResponse(w, g, http.StatusCreated) } }
// API_Campaigns returns a list of campaigns if requested via GET. // If requested via POST, API_Campaigns creates a new campaign and returns a reference to it. func API_Campaigns(w http.ResponseWriter, r *http.Request) { switch { case r.Method == "GET": cs, err := models.GetCampaigns(ctx.Get(r, "user_id").(int64)) if err != nil { Logger.Println(err) } JSONResponse(w, cs, http.StatusOK) //POST: Create a new campaign and return it as JSON case r.Method == "POST": c := models.Campaign{} // Put the request into a campaign err := json.NewDecoder(r.Body).Decode(&c) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "Invalid JSON structure"}, http.StatusBadRequest) return } err = models.PostCampaign(&c, ctx.Get(r, "user_id").(int64)) if err != nil { JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) return } JSONResponse(w, c, http.StatusCreated) } }
func ChangePassword(r *http.Request) error { u := ctx.Get(r, "user").(models.User) currentPw := r.FormValue("current_password") newPassword := r.FormValue("new_password") confirmPassword := r.FormValue("confirm_new_password") // Check the current password err := bcrypt.CompareHashAndPassword([]byte(u.Hash), []byte(currentPw)) if err != nil { return ErrInvalidPassword } // Check that the new password isn't blank if newPassword == "" { return ErrEmptyPassword } // Check that new passwords match if newPassword != confirmPassword { return ErrPasswordMismatch } // Generate the new hash h, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost) if err != nil { return err } u.Hash = string(h) if err = models.PutUser(&u); err != nil { return err } return nil }
// Login handles the authentication flow for a user. If credentials are valid, // a session is created func Login(w http.ResponseWriter, r *http.Request) { params := struct { User models.User Title string Flashes []interface{} Token string }{Title: "Login", Token: csrf.Token(r)} session := ctx.Get(r, "session").(*sessions.Session) switch { case r.Method == "GET": params.Flashes = session.Flashes() session.Save(r, w) templates := template.New("template") _, err := templates.ParseFiles("templates/login.html", "templates/flashes.html") if err != nil { Logger.Println(err) } template.Must(templates, err).ExecuteTemplate(w, "base", params) case r.Method == "POST": //Attempt to login succ, u, err := auth.Login(r) if err != nil { Logger.Println(err) } //If we've logged in, save the session and redirect to the dashboard if succ { session.Values["id"] = u.Id session.Save(r, w) http.Redirect(w, r, "/", 302) } else { Flash(w, r, "danger", "Invalid Username/Password") http.Redirect(w, r, "/login", 302) } } }
// Settings handles the changing of settings func Settings(w http.ResponseWriter, r *http.Request) { switch { case r.Method == "GET": params := struct { User models.User Title string Flashes []interface{} Token string Version string }{Title: "Settings", Version: config.Version, User: ctx.Get(r, "user").(models.User), Token: csrf.Token(r)} getTemplate(w, "settings").ExecuteTemplate(w, "base", params) case r.Method == "POST": err := auth.ChangePassword(r) msg := models.Response{Success: true, Message: "Settings Updated Successfully"} if err == auth.ErrInvalidPassword { msg.Message = "Invalid Password" msg.Success = false JSONResponse(w, msg, http.StatusBadRequest) return } if err != nil { msg.Message = err.Error() msg.Success = false JSONResponse(w, msg, http.StatusBadRequest) return } JSONResponse(w, msg, http.StatusOK) } }
// Logout destroys the current user session func Logout(w http.ResponseWriter, r *http.Request) { // If it is a post request, attempt to register the account // Now that we are all registered, we can log the user in session := ctx.Get(r, "session").(*sessions.Session) delete(session.Values, "id") Flash(w, r, "success", "You have successfully logged out") http.Redirect(w, r, "/login", 302) }
// Flash handles the rendering flash messages func Flash(w http.ResponseWriter, r *http.Request, t string, m string) { session := ctx.Get(r, "session").(*sessions.Session) session.AddFlash(models.Flash{ Type: t, Message: m, }) session.Save(r, w) }
// Base handles the default path and template execution func Base(w http.ResponseWriter, r *http.Request) { params := struct { User models.User Title string Flashes []interface{} Token string }{Title: "Dashboard", User: ctx.Get(r, "user").(models.User), Token: csrf.Token(r)} getTemplate(w, "dashboard").ExecuteTemplate(w, "base", params) }
// RequireLogin is a simple middleware which checks to see if the user is currently logged in. // If not, the function returns a 302 redirect to the login page. func RequireLogin(handler http.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if u := ctx.Get(r, "user"); u != nil { handler.ServeHTTP(w, r) } else { http.Redirect(w, r, "/login", 302) } } }
// SendingProfiles handles the default path and template execution func SendingProfiles(w http.ResponseWriter, r *http.Request) { // Example of using session - will be removed. params := struct { User models.User Title string Flashes []interface{} Token string }{Title: "Sending Profiles", User: ctx.Get(r, "user").(models.User), Token: csrf.Token(r)} getTemplate(w, "sending_profiles").ExecuteTemplate(w, "base", params) }
// API_SMTP_Id contains functions to handle the GET'ing, DELETE'ing, and PUT'ing // of a SMTP object func API_SMTP_Id(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id, _ := strconv.ParseInt(vars["id"], 0, 64) s, err := models.GetSMTP(id, ctx.Get(r, "user_id").(int64)) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "SMTP not found"}, http.StatusNotFound) return } switch { case r.Method == "GET": JSONResponse(w, s, http.StatusOK) case r.Method == "DELETE": err = models.DeleteSMTP(id, ctx.Get(r, "user_id").(int64)) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "Error deleting SMTP"}, http.StatusInternalServerError) return } JSONResponse(w, models.Response{Success: true, Message: "SMTP Deleted Successfully"}, http.StatusOK) case r.Method == "PUT": s = models.SMTP{} err = json.NewDecoder(r.Body).Decode(&s) if err != nil { Logger.Println(err) } if s.Id != id { JSONResponse(w, models.Response{Success: false, Message: "/:id and /:smtp_id mismatch"}, http.StatusBadRequest) return } err = s.Validate() if err != nil { JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) return } s.ModifiedDate = time.Now() s.UserId = ctx.Get(r, "user_id").(int64) err = models.PutSMTP(&s) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "Error updating page"}, http.StatusInternalServerError) return } JSONResponse(w, s, http.StatusOK) } }
// API_Templates handles the functionality for the /api/templates endpoint func API_Templates(w http.ResponseWriter, r *http.Request) { switch { case r.Method == "GET": ts, err := models.GetTemplates(ctx.Get(r, "user_id").(int64)) if err != nil { Logger.Println(err) } JSONResponse(w, ts, http.StatusOK) //POST: Create a new template and return it as JSON case r.Method == "POST": t := models.Template{} // Put the request into a template err := json.NewDecoder(r.Body).Decode(&t) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "Invalid JSON structure"}, http.StatusBadRequest) return } _, err = models.GetTemplateByName(t.Name, ctx.Get(r, "user_id").(int64)) if err != gorm.ErrRecordNotFound { JSONResponse(w, models.Response{Success: false, Message: "Template name already in use"}, http.StatusConflict) return } t.ModifiedDate = time.Now() t.UserId = ctx.Get(r, "user_id").(int64) err = models.PostTemplate(&t) if err == models.ErrTemplateNameNotSpecified { JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) return } if err == models.ErrTemplateMissingParameter { JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) return } if err != nil { JSONResponse(w, models.Response{Success: false, Message: "Error inserting template into database"}, http.StatusInternalServerError) Logger.Println(err) return } JSONResponse(w, t, http.StatusCreated) } }
// API (/api/reset) resets a user's API key func API_Reset(w http.ResponseWriter, r *http.Request) { switch { case r.Method == "POST": u := ctx.Get(r, "user").(models.User) u.ApiKey = auth.GenerateSecureKey() err := models.PutUser(&u) if err != nil { http.Error(w, "Error setting API Key", http.StatusInternalServerError) } else { JSONResponse(w, models.Response{Success: true, Message: "API Key successfully reset!", Data: u.ApiKey}, http.StatusOK) } } }
// API_Campaigns_Id_Complete effectively "ends" a campaign. // Future phishing emails clicked will return a simple "404" page. func API_Campaigns_Id_Complete(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id, _ := strconv.ParseInt(vars["id"], 0, 64) switch { case r.Method == "GET": err := models.CompleteCampaign(id, ctx.Get(r, "user_id").(int64)) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "Error completing campaign"}, http.StatusInternalServerError) return } JSONResponse(w, models.Response{Success: true, Message: "Campaign completed successfully!"}, http.StatusOK) } }
// API_Pages_Id contains functions to handle the GET'ing, DELETE'ing, and PUT'ing // of a Page object func API_Pages_Id(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id, _ := strconv.ParseInt(vars["id"], 0, 64) p, err := models.GetPage(id, ctx.Get(r, "user_id").(int64)) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "Page not found"}, http.StatusNotFound) return } switch { case r.Method == "GET": JSONResponse(w, p, http.StatusOK) case r.Method == "DELETE": err = models.DeletePage(id, ctx.Get(r, "user_id").(int64)) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "Error deleting page"}, http.StatusInternalServerError) return } JSONResponse(w, models.Response{Success: true, Message: "Page Deleted Successfully"}, http.StatusOK) case r.Method == "PUT": p = models.Page{} err = json.NewDecoder(r.Body).Decode(&p) if err != nil { Logger.Println(err) } if p.Id != id { JSONResponse(w, models.Response{Success: false, Message: "/:id and /:page_id mismatch"}, http.StatusBadRequest) return } p.ModifiedDate = time.Now() p.UserId = ctx.Get(r, "user_id").(int64) err = models.PutPage(&p) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "Error updating page: " + err.Error()}, http.StatusInternalServerError) return } JSONResponse(w, p, http.StatusOK) } }
// API_Templates_Id handles the functions for the /api/templates/:id endpoint func API_Templates_Id(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id, _ := strconv.ParseInt(vars["id"], 0, 64) t, err := models.GetTemplate(id, ctx.Get(r, "user_id").(int64)) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "Template not found"}, http.StatusNotFound) return } switch { case r.Method == "GET": JSONResponse(w, t, http.StatusOK) case r.Method == "DELETE": err = models.DeleteTemplate(id, ctx.Get(r, "user_id").(int64)) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "Error deleting template"}, http.StatusInternalServerError) return } JSONResponse(w, models.Response{Success: true, Message: "Template deleted successfully!"}, http.StatusOK) case r.Method == "PUT": t = models.Template{} err = json.NewDecoder(r.Body).Decode(&t) if err != nil { Logger.Println(err) } if t.Id != id { JSONResponse(w, models.Response{Success: false, Message: "Error: /:id and template_id mismatch"}, http.StatusBadRequest) return } t.ModifiedDate = time.Now() t.UserId = ctx.Get(r, "user_id").(int64) err = models.PutTemplate(&t) if err != nil { JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) return } JSONResponse(w, t, http.StatusOK) } }
// API_Campaigns_Id_Results returns just the results for a given campaign to // significantly reduce the information returned. func API_Campaigns_Id_Results(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id, _ := strconv.ParseInt(vars["id"], 0, 64) cr, err := models.GetCampaignResults(id, ctx.Get(r, "user_id").(int64)) if err != nil { Logger.Println(err) JSONResponse(w, models.Response{Success: false, Message: "Campaign not found"}, http.StatusNotFound) return } if r.Method == "GET" { JSONResponse(w, cr, http.StatusOK) return } }
// API_Groups_Id returns details about the requested group. // If the group is not valid, API_Groups_Id returns null. func API_Groups_Id(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id, _ := strconv.ParseInt(vars["id"], 0, 64) g, err := models.GetGroup(id, ctx.Get(r, "user_id").(int64)) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "Group not found"}, http.StatusNotFound) return } switch { case r.Method == "GET": JSONResponse(w, g, http.StatusOK) case r.Method == "DELETE": err = models.DeleteGroup(&g) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "Error deleting group"}, http.StatusInternalServerError) return } JSONResponse(w, models.Response{Success: true, Message: "Group deleted successfully!"}, http.StatusOK) case r.Method == "PUT": // Change this to get from URL and uid (don't bother with id in r.Body) g = models.Group{} err = json.NewDecoder(r.Body).Decode(&g) if g.Id != id { JSONResponse(w, models.Response{Success: false, Message: "Error: /:id and group_id mismatch"}, http.StatusInternalServerError) return } g.ModifiedDate = time.Now() g.UserId = ctx.Get(r, "user_id").(int64) err = models.PutGroup(&g) if err != nil { JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) return } JSONResponse(w, g, http.StatusOK) } }
// Register creates a new user func Register(w http.ResponseWriter, r *http.Request) { // If it is a post request, attempt to register the account // Now that we are all registered, we can log the user in params := struct { Title string Flashes []interface{} User models.User Token string }{Title: "Register", Token: csrf.Token(r)} session := ctx.Get(r, "session").(*sessions.Session) switch { case r.Method == "GET": params.Flashes = session.Flashes() session.Save(r, w) templates := template.New("template") _, err := templates.ParseFiles("templates/register.html", "templates/flashes.html") if err != nil { Logger.Println(err) } template.Must(templates, err).ExecuteTemplate(w, "base", params) case r.Method == "POST": //Attempt to register succ, err := auth.Register(r) //If we've registered, redirect to the login page if succ { session.AddFlash(models.Flash{ Type: "success", Message: "Registration successful!.", }) session.Save(r, w) http.Redirect(w, r, "/login", 302) return } // Check the error m := err.Error() Logger.Println(err) session.AddFlash(models.Flash{ Type: "danger", Message: m, }) session.Save(r, w) http.Redirect(w, r, "/register", 302) return } }
// API_Campaigns_Id returns details about the requested campaign. If the campaign is not // valid, API_Campaigns_Id returns null. func API_Campaigns_Id(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id, _ := strconv.ParseInt(vars["id"], 0, 64) c, err := models.GetCampaign(id, ctx.Get(r, "user_id").(int64)) if err != nil { Logger.Println(err) JSONResponse(w, models.Response{Success: false, Message: "Campaign not found"}, http.StatusNotFound) return } switch { case r.Method == "GET": JSONResponse(w, c, http.StatusOK) case r.Method == "DELETE": err = models.DeleteCampaign(id) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "Error deleting campaign"}, http.StatusInternalServerError) return } JSONResponse(w, models.Response{Success: true, Message: "Campaign deleted successfully!"}, http.StatusOK) } }
// API_Send_Test_Email sends a test email using the template name // and Target given. func API_Send_Test_Email(w http.ResponseWriter, r *http.Request) { s := &models.SendTestEmailRequest{} if r.Method != "POST" { JSONResponse(w, models.Response{Success: false, Message: "Method not allowed"}, http.StatusBadRequest) return } err := json.NewDecoder(r.Body).Decode(s) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "Error decoding JSON Request"}, http.StatusBadRequest) return } // Validate the given request if err = s.Validate(); err != nil { JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) return } // If a Template is not specified use a default if s.Template.Name == "" { //default message body text := "It works!\n\nThis is an email letting you know that your gophish\nconfiguration was successful.\n" + "Here are the details:\n\nWho you sent from: {{.From}}\n\nWho you sent to: \n" + "{{if .FirstName}} First Name: {{.FirstName}}\n{{end}}" + "{{if .LastName}} Last Name: {{.LastName}}\n{{end}}" + "{{if .Position}} Position: {{.Position}}\n{{end}}" + "{{if .TrackingURL}} Tracking URL: {{.TrackingURL}}\n{{end}}" + "\nNow go send some phish!" t := models.Template{ Subject: "Default Email from Gophish", Text: text, } s.Template = t // Try to lookup the Template by name } else { // Get the Template requested by name s.Template, err = models.GetTemplateByName(s.Template.Name, ctx.Get(r, "user_id").(int64)) if err == gorm.ErrRecordNotFound { Logger.Printf("Error - Template %s does not exist", s.Template.Name) JSONResponse(w, models.Response{Success: false, Message: models.ErrTemplateNotFound.Error()}, http.StatusBadRequest) } else if err != nil { Logger.Println(err) JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) return } } // If a complete sending profile is provided use it if err := s.SMTP.Validate(); err != nil { // Otherwise get the SMTP requested by name s.SMTP, err = models.GetSMTPByName(s.SMTP.Name, ctx.Get(r, "user_id").(int64)) if err == gorm.ErrRecordNotFound { Logger.Printf("Error - Sending profile %s does not exist", s.SMTP.Name) JSONResponse(w, models.Response{Success: false, Message: models.ErrSMTPNotFound.Error()}, http.StatusBadRequest) } else if err != nil { Logger.Println(err) JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) return } } // Send the test email err = worker.SendTestEmail(s) if err != nil { JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError) return } JSONResponse(w, models.Response{Success: true, Message: "Email Sent"}, http.StatusOK) return }