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