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  }