//Root is used to show the login page of the app //when a user browses to the page (usually just the domain minus any path), the user is checked for a session //if a session exists, the app attempts to auto-login the user //otherwise a user is shown the log in prompt //this also handles the "first run" of the app in which no users exist yet...it forces creation of the "super admin" func Root(w http.ResponseWriter, r *http.Request) { //check that session store was initialized correctly if err := sessionutils.CheckSession(); err != nil { notificationPage(w, "panel-danger", sessionInitError, err, "btn-default", "/", "Go Back") return } //check that stripe private key and statement desecriptor were read correctly if err := card.CheckStripe(); err != nil { notificationPage(w, "panel-danger", sessionInitError, err, "btn-default", "/", "Go Back") return } //check if the admin user exists //redirect user to create admin if it does not exist err := users.DoesAdminExist(r) if err == users.ErrAdminDoesNotExist { http.Redirect(w, r, "/setup/", http.StatusFound) return } else if err != nil { notificationPage(w, "panel-danger", adminInitError, err, "btn-default", "/", "Go Back") return } //check if user is already signed in //if user is already logged in, redirect to /main/ page session := sessionutils.Get(r) if session.IsNew == false { uId := session.Values["user_id"].(int64) c := appengine.NewContext(r) u, err := users.Find(c, uId) if err != nil { sessionutils.Destroy(w, r) notificationPage(w, "panel-danger", "Autologin Error", "There was an issue looking up your user account. Please go back and try logging in.", "btn-default", "/", "Go Back") return } //user data was found //check if user is allowed access if users.AllowedAccess(u) == false { sessionutils.Destroy(w, r) notificationPage(w, "panel-danger", "Autologin Error", "You are not allowed access. Please contact an administrator.", "btn-default", "/", "Go Back") } //user account is found an allowed access //redirect user http.Redirect(w, r, "/main/", http.StatusFound) return } //load the login page templates.Load(w, "root", nil) return }
//Auth checks if a user is logged in and is allowed access to the app //this is done on every page load and every endpoint func Auth(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { //get user data from session session := sessionutils.Get(r) //session data does not exist yet //this is a new session //redirect user to log in page if session.IsNew { http.Redirect(w, r, "/", http.StatusFound) return } //check if user id is in session //it *should* be! //otherwise show user a notice and force user to log in again userId, ok := session.Values["user_id"].(int64) if ok == false { sessionutils.Destroy(w, r) notificationPage(w, "panel-danger", "Session Expired", "Your session has expired. Please log back in or contact an administrator if this problem persists.", "btn-default", "/", "Log In") return } //look up user in memcache and/or datastore c := appengine.NewContext(r) data, err := users.Find(c, userId) if err != nil { sessionutils.Destroy(w, r) notificationPage(w, "panel-danger", "Application Error", "The app encountered an error in the middleware while trying to authenticate you as a legitimate user. Please try logging in again or contact an administrator.", "btn-default", "/", "Log In") return } //check if user is allowed access to the app //this is a setting the app's administrators can toggle for each user if users.AllowedAccess(data) == false { sessionutils.Destroy(w, r) http.Redirect(w, r, "/", http.StatusFound) return } //user is allowed access //extend expiration of session cookie to allow user to stay "logged in" sessionutils.ExtendExpiration(session, w, r) //move to next middleware or handler next.ServeHTTP(w, r) }) }
//Logout handles logging out of the app //this removes the session data so a user must log back in before using the app func Logout(w http.ResponseWriter, r *http.Request) { //destroy session sessionutils.Destroy(w, r) //redirect to root page http.Redirect(w, r, "/", http.StatusFound) return }
//Login verifies a username and password combo //this makes sure the user exists, that the password is correct, and that the user is active //if user is allowed access, their data is saved to the session and they are redirected into the app func Login(w http.ResponseWriter, r *http.Request) { //get form values username := r.FormValue("username") password := r.FormValue("password") //get user data c := appengine.NewContext(r) id, data, err := exists(c, username) if err == ErrUserDoesNotExist { notificationPage(w, "panel-danger", "Cannot Log In", "The username you provided does not exist.", "btn-default", "/", "Try Again") return } //is user allowed access if AllowedAccess(data) == false { notificationPage(w, "panel-danger", "Cannot Log In", "You are not allowed access. Please contact an administrator.", "btn-default", "/", "Go Back") return } //validate password _, err = pwds.Verify(password, data.Password) if err != nil { notificationPage(w, "panel-danger", "Cannot Log In", "The password you provided is invalid.", "btn-default", "/", "Try Again") return } //user validated //save session data session := sessionutils.Get(r) if session.IsNew == false { sessionutils.Destroy(w, r) session = sessionutils.Get(r) } sessionutils.AddValue(session, "username", username) sessionutils.AddValue(session, "user_id", id) sessionutils.Save(session, w, r) //show user main page http.Redirect(w, r, "/main/", http.StatusFound) return }