mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 21:56:50 -04:00
Persist user sessions across server restarts.
This commit is contained in:
52
web/login.go
52
web/login.go
@@ -8,27 +8,31 @@ package web
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Team254/cheesy-arena/model"
|
||||
"github.com/google/uuid"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Shows the login form.
|
||||
func (web *Web) loginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var errorMessage string
|
||||
if username := web.cookieAuth.Authorize(r); username != "" {
|
||||
// If redirected here but already logged in, the user must have insufficient privileges; show a useful message.
|
||||
errorMessage = fmt.Sprintf("User '%s' has insufficient privileges for the requested page. Try logging in as a"+
|
||||
" different user.", username)
|
||||
}
|
||||
web.renderLogin(w, r, errorMessage)
|
||||
web.renderLogin(w, r, "")
|
||||
}
|
||||
|
||||
// Processes the login request.
|
||||
func (web *Web) loginPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if err := web.cookieAuth.Login(w, r.PostFormValue("username"), r.PostFormValue("password")); err != nil {
|
||||
username := r.PostFormValue("username")
|
||||
if err := web.checkAuthPassword(username, r.PostFormValue("password")); err != nil {
|
||||
web.renderLogin(w, r, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
session := model.UserSession{Token: uuid.New().String(), Username: username, CreatedAt: time.Now()}
|
||||
if err := web.arena.Database.CreateUserSession(&session); err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
http.SetCookie(w, &http.Cookie{Name: sessionTokenCookie, Value: session.Token})
|
||||
redirectUrl := r.URL.Query().Get("redirect")
|
||||
if redirectUrl == "" {
|
||||
redirectUrl = "/"
|
||||
@@ -52,3 +56,35 @@ func (web *Web) renderLogin(w http.ResponseWriter, r *http.Request, errorMessage
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the given user is authorized for admin operations. Used for HTTP cookie authentication.
|
||||
func (web *Web) userIsAdmin(w http.ResponseWriter, r *http.Request) bool {
|
||||
if web.arena.EventSettings.AdminPassword == "" {
|
||||
// Disable auth if there is no password configured.
|
||||
return true
|
||||
}
|
||||
session := web.getUserSessionFromCookie(r)
|
||||
if session != nil && session.Username == adminUser {
|
||||
return true
|
||||
} else {
|
||||
http.Redirect(w, r, "/login?redirect="+r.URL.Path, 307)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (web *Web) getUserSessionFromCookie(r *http.Request) *model.UserSession {
|
||||
token, err := r.Cookie(sessionTokenCookie)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
session, _ := web.arena.Database.GetUserSessionByToken(token.Value)
|
||||
return session
|
||||
}
|
||||
|
||||
func (web *Web) checkAuthPassword(user, password string) error {
|
||||
if user == adminUser && password == web.arena.EventSettings.AdminPassword {
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("Invalid login credentials.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,19 +24,19 @@ func TestLoginDisplay(t *testing.T) {
|
||||
// Check logging in with the wrong username and right password.
|
||||
recorder = web.postHttpResponse("/login?redirect=/match_play", "username=blorpy&password=reader")
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), "Bad username or password")
|
||||
assert.Contains(t, recorder.Body.String(), "Invalid login credentials.")
|
||||
|
||||
// Check logging in with the right username and wrong password.
|
||||
recorder = web.postHttpResponse("/login?redirect=/match_play", "username=admin&password=blorpy")
|
||||
assert.Equal(t, 200, recorder.Code)
|
||||
assert.Contains(t, recorder.Body.String(), "Bad username or password")
|
||||
assert.Contains(t, recorder.Body.String(), "Invalid login credentials.")
|
||||
|
||||
// Check logging in with the right username and password.
|
||||
recorder = web.postHttpResponse("/login?redirect=/match_play", "username=admin&password=admin")
|
||||
assert.Equal(t, 303, recorder.Code)
|
||||
assert.Equal(t, "/match_play", recorder.Header().Get("Location"))
|
||||
cookie := recorder.Header().Get("Set-Cookie")
|
||||
assert.Contains(t, cookie, "Authorization=")
|
||||
assert.Contains(t, cookie, "session_token=")
|
||||
|
||||
// Check that hitting the reader-level protected page works now.
|
||||
recorder = web.getHttpResponseWithHeaders("/match_play", map[string]string{"Cookie": cookie})
|
||||
|
||||
@@ -39,6 +39,7 @@ func (web *Web) settingsPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if len(eventSettings.Name) < 1 && eventSettings.Name != previousEventName {
|
||||
eventSettings.Name = previousEventName
|
||||
}
|
||||
previousAdminPassword := eventSettings.AdminPassword
|
||||
|
||||
numAlliances, _ := strconv.Atoi(r.PostFormValue("numElimAlliances"))
|
||||
if numAlliances < 2 || numAlliances > 16 {
|
||||
@@ -80,6 +81,14 @@ func (web *Web) settingsPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if eventSettings.AdminPassword != previousAdminPassword {
|
||||
// Delete any existing user sessions to force a logout.
|
||||
if err := web.arena.Database.TruncateUserSessions(); err != nil {
|
||||
handleWebErr(w, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/setup/settings", 303)
|
||||
}
|
||||
|
||||
|
||||
24
web/web.go
24
web/web.go
@@ -6,7 +6,6 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"bitbucket.org/rj/httpauth-go"
|
||||
"fmt"
|
||||
"github.com/Team254/cheesy-arena/field"
|
||||
"github.com/Team254/cheesy-arena/model"
|
||||
@@ -18,18 +17,17 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
adminUser = "admin"
|
||||
sessionTokenCookie = "session_token"
|
||||
adminUser = "admin"
|
||||
)
|
||||
|
||||
type Web struct {
|
||||
arena *field.Arena
|
||||
cookieAuth *httpauth.Cookie
|
||||
templateHelpers template.FuncMap
|
||||
}
|
||||
|
||||
func NewWeb(arena *field.Arena) *Web {
|
||||
web := &Web{arena: arena}
|
||||
web.cookieAuth = httpauth.NewCookie("Cheesy Arena", "", web.checkAuthPassword)
|
||||
|
||||
// Helper functions that can be used inside templates.
|
||||
web.templateHelpers = template.FuncMap{
|
||||
@@ -93,24 +91,6 @@ func (web *Web) indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the given user is authorized for admin operations. Used for HTTP cookie authentication.
|
||||
func (web *Web) userIsAdmin(w http.ResponseWriter, r *http.Request) bool {
|
||||
if web.arena.EventSettings.AdminPassword == "" {
|
||||
// Disable auth if there is no password configured.
|
||||
return true
|
||||
}
|
||||
if web.cookieAuth.Authorize(r) == adminUser {
|
||||
return true
|
||||
} else {
|
||||
http.Redirect(w, r, "/login?redirect="+r.URL.Path, 307)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (web *Web) checkAuthPassword(user, password string) bool {
|
||||
return user == adminUser && password == web.arena.EventSettings.AdminPassword
|
||||
}
|
||||
|
||||
// Sets up the mapping between URLs and handlers.
|
||||
func (web *Web) newHandler() http.Handler {
|
||||
router := mux.NewRouter()
|
||||
|
||||
Reference in New Issue
Block a user