feat(config): add box owner policy settings
Adds configuration options and environment variables to manage box owner policies, including settings for refresh counts and expiry.
This commit is contained in:
162
lib/server/account_auth.go
Normal file
162
lib/server/account_auth.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"warpbox/lib/metastore"
|
||||
)
|
||||
|
||||
const accountSessionCookie = "warpbox_account_session"
|
||||
|
||||
func (app *App) registerAccountRoutes(router *gin.Engine) {
|
||||
account := router.Group("/account")
|
||||
account.Use(noStoreAdminHeaders)
|
||||
account.GET("/login", app.handleAccountLogin)
|
||||
account.POST("/login", app.handleAccountLoginPost)
|
||||
|
||||
protected := account.Group("")
|
||||
protected.Use(app.requireAccountSession)
|
||||
protected.GET("", app.handleAccountDashboard)
|
||||
protected.GET("/", app.handleAccountDashboard)
|
||||
protected.POST("/logout", app.handleAccountLogout)
|
||||
protected.GET("/settings", app.handleAccountSettings)
|
||||
protected.POST("/settings", app.handleAccountSettingsPost)
|
||||
protected.POST("/settings/reset", app.handleAccountSettingsReset)
|
||||
protected.GET("/settings/export.json", app.handleAccountSettingsExport)
|
||||
protected.POST("/settings/import.json", app.handleAccountSettingsImport)
|
||||
}
|
||||
|
||||
func (app *App) handleAccountLogin(ctx *gin.Context) {
|
||||
if app.isAccountSessionValid(ctx) {
|
||||
ctx.Redirect(http.StatusSeeOther, "/account")
|
||||
return
|
||||
}
|
||||
app.renderAccountLogin(ctx, "")
|
||||
}
|
||||
|
||||
func (app *App) handleAccountLoginPost(ctx *gin.Context) {
|
||||
if !app.adminLoginEnabled {
|
||||
app.renderAccountLogin(ctx, "Account login is disabled.")
|
||||
return
|
||||
}
|
||||
|
||||
username := strings.TrimSpace(ctx.PostForm("username"))
|
||||
password := ctx.PostForm("password")
|
||||
user, ok, err := app.store.GetUserByUsername(username)
|
||||
if err != nil {
|
||||
ctx.String(http.StatusInternalServerError, "Could not load user")
|
||||
return
|
||||
}
|
||||
if !ok || user.Disabled || !metastore.VerifyPassword(user.PasswordHash, password) {
|
||||
app.renderAccountLogin(ctx, "The username or password was not accepted.")
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := app.permissionsForUser(user); err != nil {
|
||||
ctx.String(http.StatusInternalServerError, "Could not load permissions")
|
||||
return
|
||||
}
|
||||
|
||||
session, err := app.store.CreateSession(user.ID, time.Duration(app.config.SessionTTLSeconds)*time.Second)
|
||||
if err != nil {
|
||||
ctx.String(http.StatusInternalServerError, "Could not create session")
|
||||
return
|
||||
}
|
||||
ctx.SetSameSite(http.SameSiteLaxMode)
|
||||
ctx.SetCookie(accountSessionCookie, session.Token, int(app.config.SessionTTLSeconds), "/account", "", app.config.AdminCookieSecure, true)
|
||||
ctx.Redirect(http.StatusSeeOther, "/account")
|
||||
}
|
||||
|
||||
func (app *App) handleAccountLogout(ctx *gin.Context) {
|
||||
if token, err := ctx.Cookie(accountSessionCookie); err == nil {
|
||||
_ = app.store.DeleteSession(token)
|
||||
}
|
||||
ctx.SetSameSite(http.SameSiteLaxMode)
|
||||
ctx.SetCookie(accountSessionCookie, "", -1, "/account", "", app.config.AdminCookieSecure, true)
|
||||
ctx.Redirect(http.StatusSeeOther, "/account/login")
|
||||
}
|
||||
|
||||
func (app *App) requireAccountSession(ctx *gin.Context) {
|
||||
token, err := ctx.Cookie(accountSessionCookie)
|
||||
if err != nil {
|
||||
ctx.Redirect(http.StatusSeeOther, "/account/login")
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
session, ok, err := app.store.GetSession(token)
|
||||
if err != nil || !ok {
|
||||
ctx.Redirect(http.StatusSeeOther, "/account/login")
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
if !validAdminCSRF(ctx, session) {
|
||||
ctx.String(http.StatusForbidden, "Permission denied")
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
user, ok, err := app.store.GetUser(session.UserID)
|
||||
if err != nil || !ok || user.Disabled {
|
||||
ctx.Redirect(http.StatusSeeOther, "/account/login")
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
perms, err := app.permissionsForUser(user)
|
||||
if err != nil {
|
||||
ctx.Redirect(http.StatusSeeOther, "/account/login")
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Set("accountUser", user)
|
||||
ctx.Set("adminUser", user)
|
||||
ctx.Set("accountPerms", perms)
|
||||
ctx.Set("adminPerms", perms)
|
||||
ctx.Set("accountSession", session)
|
||||
ctx.Set("accountCSRFToken", session.CSRFToken)
|
||||
ctx.Set("adminCSRFToken", session.CSRFToken)
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
func (app *App) isAccountSessionValid(ctx *gin.Context) bool {
|
||||
token, err := ctx.Cookie(accountSessionCookie)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
session, ok, err := app.store.GetSession(token)
|
||||
if err != nil || !ok {
|
||||
return false
|
||||
}
|
||||
user, ok, err := app.store.GetUser(session.UserID)
|
||||
if err != nil || !ok || user.Disabled {
|
||||
return false
|
||||
}
|
||||
_, err = app.permissionsForUser(user)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (app *App) renderAccountLogin(ctx *gin.Context, errorMessage string) {
|
||||
ctx.HTML(http.StatusOK, "account_login.html", gin.H{
|
||||
"PageTitle": "WarpBox Account Login",
|
||||
"AdminLoginEnabled": app.adminLoginEnabled,
|
||||
"AccountLoginEnabled": app.adminLoginEnabled,
|
||||
"Error": errorMessage,
|
||||
})
|
||||
}
|
||||
|
||||
func currentAccountUser(ctx *gin.Context) (metastore.User, bool) {
|
||||
if current, ok := ctx.Get("accountUser"); ok {
|
||||
if user, ok := current.(metastore.User); ok {
|
||||
return user, true
|
||||
}
|
||||
}
|
||||
if current, ok := ctx.Get("adminUser"); ok {
|
||||
if user, ok := current.(metastore.User); ok {
|
||||
return user, true
|
||||
}
|
||||
}
|
||||
return metastore.User{}, false
|
||||
}
|
||||
Reference in New Issue
Block a user