github.com/glennzw/gophish@v0.8.1-0.20190824020715-24fe998a3aa0/controllers/route.go (about) 1 package controllers 2 3 import ( 4 "compress/gzip" 5 "context" 6 "html/template" 7 "net/http" 8 "net/url" 9 "time" 10 11 "github.com/NYTimes/gziphandler" 12 "github.com/gophish/gophish/auth" 13 "github.com/gophish/gophish/config" 14 ctx "github.com/gophish/gophish/context" 15 "github.com/gophish/gophish/controllers/api" 16 log "github.com/gophish/gophish/logger" 17 mid "github.com/gophish/gophish/middleware" 18 "github.com/gophish/gophish/models" 19 "github.com/gophish/gophish/util" 20 "github.com/gophish/gophish/worker" 21 "github.com/gorilla/csrf" 22 "github.com/gorilla/handlers" 23 "github.com/gorilla/mux" 24 "github.com/gorilla/sessions" 25 "github.com/jordan-wright/unindexed" 26 ) 27 28 // AdminServerOption is a functional option that is used to configure the 29 // admin server 30 type AdminServerOption func(*AdminServer) 31 32 // AdminServer is an HTTP server that implements the administrative Gophish 33 // handlers, including the dashboard and REST API. 34 type AdminServer struct { 35 server *http.Server 36 worker worker.Worker 37 config config.AdminServer 38 } 39 40 // WithWorker is an option that sets the background worker. 41 func WithWorker(w worker.Worker) AdminServerOption { 42 return func(as *AdminServer) { 43 as.worker = w 44 } 45 } 46 47 // NewAdminServer returns a new instance of the AdminServer with the 48 // provided config and options applied. 49 func NewAdminServer(config config.AdminServer, options ...AdminServerOption) *AdminServer { 50 defaultWorker, _ := worker.New() 51 defaultServer := &http.Server{ 52 ReadTimeout: 10 * time.Second, 53 Addr: config.ListenURL, 54 } 55 as := &AdminServer{ 56 worker: defaultWorker, 57 server: defaultServer, 58 config: config, 59 } 60 for _, opt := range options { 61 opt(as) 62 } 63 as.registerRoutes() 64 return as 65 } 66 67 // Start launches the admin server, listening on the configured address. 68 func (as *AdminServer) Start() error { 69 if as.worker != nil { 70 go as.worker.Start() 71 } 72 if as.config.UseTLS { 73 err := util.CheckAndCreateSSL(as.config.CertPath, as.config.KeyPath) 74 if err != nil { 75 log.Fatal(err) 76 return err 77 } 78 log.Infof("Starting admin server at https://%s", as.config.ListenURL) 79 return as.server.ListenAndServeTLS(as.config.CertPath, as.config.KeyPath) 80 } 81 // If TLS isn't configured, just listen on HTTP 82 log.Infof("Starting admin server at http://%s", as.config.ListenURL) 83 return as.server.ListenAndServe() 84 } 85 86 // Shutdown attempts to gracefully shutdown the server. 87 func (as *AdminServer) Shutdown() error { 88 ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) 89 defer cancel() 90 return as.server.Shutdown(ctx) 91 } 92 93 // SetupAdminRoutes creates the routes for handling requests to the web interface. 94 // This function returns an http.Handler to be used in http.ListenAndServe(). 95 func (as *AdminServer) registerRoutes() { 96 router := mux.NewRouter() 97 // Base Front-end routes 98 router.HandleFunc("/", mid.Use(as.Base, mid.RequireLogin)) 99 router.HandleFunc("/login", as.Login) 100 router.HandleFunc("/logout", mid.Use(as.Logout, mid.RequireLogin)) 101 router.HandleFunc("/campaigns", mid.Use(as.Campaigns, mid.RequireLogin)) 102 router.HandleFunc("/campaigns/{id:[0-9]+}", mid.Use(as.CampaignID, mid.RequireLogin)) 103 router.HandleFunc("/templates", mid.Use(as.Templates, mid.RequireLogin)) 104 router.HandleFunc("/groups", mid.Use(as.Groups, mid.RequireLogin)) 105 router.HandleFunc("/landing_pages", mid.Use(as.LandingPages, mid.RequireLogin)) 106 router.HandleFunc("/sending_profiles", mid.Use(as.SendingProfiles, mid.RequireLogin)) 107 router.HandleFunc("/settings", mid.Use(as.Settings, mid.RequireLogin)) 108 router.HandleFunc("/users", mid.Use(as.UserManagement, mid.RequirePermission(models.PermissionModifySystem), mid.RequireLogin)) 109 // Create the API routes 110 api := api.NewServer(api.WithWorker(as.worker)) 111 router.PathPrefix("/api/").Handler(api) 112 113 // Setup static file serving 114 router.PathPrefix("/").Handler(http.FileServer(unindexed.Dir("./static/"))) 115 116 // Setup CSRF Protection 117 csrfHandler := csrf.Protect([]byte(util.GenerateSecureKey()), 118 csrf.FieldName("csrf_token"), 119 csrf.Secure(as.config.UseTLS)) 120 adminHandler := csrfHandler(router) 121 adminHandler = mid.Use(adminHandler.ServeHTTP, mid.CSRFExceptions, mid.GetContext) 122 123 // Setup GZIP compression 124 gzipWrapper, _ := gziphandler.NewGzipLevelHandler(gzip.BestCompression) 125 adminHandler = gzipWrapper(adminHandler) 126 127 // Setup logging 128 adminHandler = handlers.CombinedLoggingHandler(log.Writer(), adminHandler) 129 as.server.Handler = adminHandler 130 } 131 132 type templateParams struct { 133 Title string 134 Flashes []interface{} 135 User models.User 136 Token string 137 Version string 138 ModifySystem bool 139 } 140 141 // newTemplateParams returns the default template parameters for a user and 142 // the CSRF token. 143 func newTemplateParams(r *http.Request) templateParams { 144 user := ctx.Get(r, "user").(models.User) 145 modifySystem, _ := user.HasPermission(models.PermissionModifySystem) 146 return templateParams{ 147 Token: csrf.Token(r), 148 User: user, 149 ModifySystem: modifySystem, 150 Version: config.Version, 151 } 152 } 153 154 // Base handles the default path and template execution 155 func (as *AdminServer) Base(w http.ResponseWriter, r *http.Request) { 156 params := newTemplateParams(r) 157 params.Title = "Dashboard" 158 getTemplate(w, "dashboard").ExecuteTemplate(w, "base", params) 159 } 160 161 // Campaigns handles the default path and template execution 162 func (as *AdminServer) Campaigns(w http.ResponseWriter, r *http.Request) { 163 params := newTemplateParams(r) 164 params.Title = "Campaigns" 165 getTemplate(w, "campaigns").ExecuteTemplate(w, "base", params) 166 } 167 168 // CampaignID handles the default path and template execution 169 func (as *AdminServer) CampaignID(w http.ResponseWriter, r *http.Request) { 170 params := newTemplateParams(r) 171 params.Title = "Campaign Results" 172 getTemplate(w, "campaign_results").ExecuteTemplate(w, "base", params) 173 } 174 175 // Templates handles the default path and template execution 176 func (as *AdminServer) Templates(w http.ResponseWriter, r *http.Request) { 177 params := newTemplateParams(r) 178 params.Title = "Email Templates" 179 getTemplate(w, "templates").ExecuteTemplate(w, "base", params) 180 } 181 182 // Groups handles the default path and template execution 183 func (as *AdminServer) Groups(w http.ResponseWriter, r *http.Request) { 184 params := newTemplateParams(r) 185 params.Title = "Users & Groups" 186 getTemplate(w, "groups").ExecuteTemplate(w, "base", params) 187 } 188 189 // LandingPages handles the default path and template execution 190 func (as *AdminServer) LandingPages(w http.ResponseWriter, r *http.Request) { 191 params := newTemplateParams(r) 192 params.Title = "Landing Pages" 193 getTemplate(w, "landing_pages").ExecuteTemplate(w, "base", params) 194 } 195 196 // SendingProfiles handles the default path and template execution 197 func (as *AdminServer) SendingProfiles(w http.ResponseWriter, r *http.Request) { 198 params := newTemplateParams(r) 199 params.Title = "Sending Profiles" 200 getTemplate(w, "sending_profiles").ExecuteTemplate(w, "base", params) 201 } 202 203 // Settings handles the changing of settings 204 func (as *AdminServer) Settings(w http.ResponseWriter, r *http.Request) { 205 switch { 206 case r.Method == "GET": 207 params := newTemplateParams(r) 208 params.Title = "Settings" 209 getTemplate(w, "settings").ExecuteTemplate(w, "base", params) 210 case r.Method == "POST": 211 err := auth.ChangePassword(r) 212 msg := models.Response{Success: true, Message: "Settings Updated Successfully"} 213 if err == auth.ErrInvalidPassword { 214 msg.Message = "Invalid Password" 215 msg.Success = false 216 api.JSONResponse(w, msg, http.StatusBadRequest) 217 return 218 } 219 if err != nil { 220 msg.Message = err.Error() 221 msg.Success = false 222 api.JSONResponse(w, msg, http.StatusBadRequest) 223 return 224 } 225 api.JSONResponse(w, msg, http.StatusOK) 226 } 227 } 228 229 // UserManagement is an admin-only handler that allows for the registration 230 // and management of user accounts within Gophish. 231 func (as *AdminServer) UserManagement(w http.ResponseWriter, r *http.Request) { 232 params := newTemplateParams(r) 233 params.Title = "User Management" 234 getTemplate(w, "users").ExecuteTemplate(w, "base", params) 235 } 236 237 // Login handles the authentication flow for a user. If credentials are valid, 238 // a session is created 239 func (as *AdminServer) Login(w http.ResponseWriter, r *http.Request) { 240 params := struct { 241 User models.User 242 Title string 243 Flashes []interface{} 244 Token string 245 }{Title: "Login", Token: csrf.Token(r)} 246 session := ctx.Get(r, "session").(*sessions.Session) 247 switch { 248 case r.Method == "GET": 249 params.Flashes = session.Flashes() 250 session.Save(r, w) 251 templates := template.New("template") 252 _, err := templates.ParseFiles("templates/login.html", "templates/flashes.html") 253 if err != nil { 254 log.Error(err) 255 } 256 template.Must(templates, err).ExecuteTemplate(w, "base", params) 257 case r.Method == "POST": 258 //Attempt to login 259 succ, u, err := auth.Login(r) 260 if err != nil { 261 log.Error(err) 262 } 263 //If we've logged in, save the session and redirect to the dashboard 264 if succ { 265 session.Values["id"] = u.Id 266 session.Save(r, w) 267 next := "/" 268 url, err := url.Parse(r.FormValue("next")) 269 if err == nil { 270 path := url.Path 271 if path != "" { 272 next = path 273 } 274 } 275 http.Redirect(w, r, next, 302) 276 } else { 277 Flash(w, r, "danger", "Invalid Username/Password") 278 params.Flashes = session.Flashes() 279 session.Save(r, w) 280 templates := template.New("template") 281 _, err := templates.ParseFiles("templates/login.html", "templates/flashes.html") 282 if err != nil { 283 log.Error(err) 284 } 285 w.Header().Set("Content-Type", "text/html; charset=utf-8") 286 w.WriteHeader(http.StatusUnauthorized) 287 template.Must(templates, err).ExecuteTemplate(w, "base", params) 288 } 289 } 290 } 291 292 // Logout destroys the current user session 293 func (as *AdminServer) Logout(w http.ResponseWriter, r *http.Request) { 294 session := ctx.Get(r, "session").(*sessions.Session) 295 delete(session.Values, "id") 296 Flash(w, r, "success", "You have successfully logged out") 297 session.Save(r, w) 298 http.Redirect(w, r, "/login", 302) 299 } 300 301 func getTemplate(w http.ResponseWriter, tmpl string) *template.Template { 302 templates := template.New("template") 303 _, err := templates.ParseFiles("templates/base.html", "templates/nav.html", "templates/"+tmpl+".html", "templates/flashes.html") 304 if err != nil { 305 log.Error(err) 306 } 307 return template.Must(templates, err) 308 } 309 310 // Flash handles the rendering flash messages 311 func Flash(w http.ResponseWriter, r *http.Request, t string, m string) { 312 session := ctx.Get(r, "session").(*sessions.Session) 313 session.AddFlash(models.Flash{ 314 Type: t, 315 Message: m, 316 }) 317 }