package server import ( "net/http" "time" "github.com/gin-gonic/gin" "warpbox/lib/boxstore" "warpbox/lib/models" ) const boxAuthCookiePrefix = "warpbox_box_" func handleBoxLogin(ctx *gin.Context) { boxID := ctx.Param("id") if !boxstore.ValidBoxID(boxID) { ctx.String(http.StatusBadRequest, "Invalid box id") return } manifest, err := boxstore.ReadManifest(boxID) if err != nil { ctx.String(http.StatusNotFound, "Box not found") return } if boxstore.IsExpired(manifest) { boxstore.DeleteBox(boxID) ctx.String(http.StatusGone, "Box expired") return } if !boxstore.IsPasswordProtected(manifest) || isBoxAuthorized(ctx, boxID, manifest) { ctx.Redirect(http.StatusSeeOther, "/box/"+boxID) return } renderBoxLogin(ctx, boxID, "") } func handleBoxLoginPost(ctx *gin.Context) { boxID := ctx.Param("id") if !boxstore.ValidBoxID(boxID) { ctx.String(http.StatusBadRequest, "Invalid box id") return } manifest, err := boxstore.ReadManifest(boxID) if err != nil { ctx.String(http.StatusNotFound, "Box not found") return } if boxstore.IsExpired(manifest) { boxstore.DeleteBox(boxID) ctx.String(http.StatusGone, "Box expired") return } if !boxstore.VerifyPassword(manifest, ctx.PostForm("password")) { renderBoxLogin(ctx, boxID, "The password was not accepted.") return } maxAge := 24 * 60 * 60 if !manifest.ExpiresAt.IsZero() { seconds := int(time.Until(manifest.ExpiresAt).Seconds()) if seconds > 0 { maxAge = seconds } } ctx.SetCookie(boxAuthCookieName(boxID), manifest.AuthToken, maxAge, "/box/"+boxID, "", false, true) ctx.Redirect(http.StatusSeeOther, "/box/"+boxID) } func (app *App) authorizeBoxRequest(ctx *gin.Context, boxID string, wantsHTML bool) (models.BoxManifest, bool, bool) { manifest, err := boxstore.ReadManifest(boxID) if err != nil { return models.BoxManifest{}, false, true } if boxstore.IsExpired(manifest) { boxstore.DeleteBox(boxID) if wantsHTML { ctx.String(http.StatusGone, "Box expired") } else { ctx.JSON(http.StatusGone, gin.H{"error": "Box expired"}) } return manifest, true, false } if manifest.OneTimeDownload && manifest.Consumed { if wantsHTML { ctx.String(http.StatusGone, "Box already consumed") } else { ctx.JSON(http.StatusGone, gin.H{"error": "Box already consumed"}) } return manifest, true, false } if boxstore.IsPasswordProtected(manifest) && !isBoxAuthorized(ctx, boxID, manifest) { if wantsHTML { ctx.Redirect(http.StatusSeeOther, "/box/"+boxID+"/login") } else { ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Password required"}) } return manifest, true, false } if app.config.RenewOnAccessEnabled { if renewed, err := boxstore.RenewManifest(boxID, manifest.RetentionSecs); err == nil { manifest = renewed } } return manifest, true, true } func isBoxAuthorized(ctx *gin.Context, boxID string, manifest models.BoxManifest) bool { token, err := ctx.Cookie(boxAuthCookieName(boxID)) return err == nil && boxstore.VerifyAuthToken(manifest, token) } func boxAuthCookieName(boxID string) string { return boxAuthCookiePrefix + boxID } func renderBoxLogin(ctx *gin.Context, boxID string, errorMessage string) { ctx.HTML(http.StatusOK, "box_login.html", gin.H{ "BoxID": boxID, "BoxUser": "WarpBox\\" + boxID, "ErrorMessage": errorMessage, }) }