mirror of
https://github.com/syssecfsu/witty.git
synced 2025-01-11 20:52:43 +01:00
add toast messages
This commit is contained in:
parent
481e5473aa
commit
033d9b3f14
@ -14,8 +14,19 @@
|
||||
</head>
|
||||
|
||||
<body class="text-center">
|
||||
<div class="toast bg-primary text-white border-0" role="alert" aria-live="assertive" aria-atomic="true" id="authMsg"
|
||||
style="position: absolute;top: 0px; right: 10px; z-index:1;">
|
||||
<div class="d-flex">
|
||||
<div class="toast-body">
|
||||
{{.msg}}
|
||||
</div>
|
||||
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"
|
||||
aria-label="Close"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<main class="form-signin">
|
||||
<form action="/login" method="post">
|
||||
<form action="/login" method="post">
|
||||
<img class="mb-4" src="/assets/img/keyboard.svg" alt="" width="64">
|
||||
|
||||
<div class="form-floating">
|
||||
@ -31,6 +42,19 @@
|
||||
<p class="mt-5 mb-3 text-muted">WiTTY: Web-based Interactive TTY</p>
|
||||
</form>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
var element = document.getElementById("authMsg");
|
||||
var toast = new bootstrap.Toast(element);
|
||||
|
||||
toast.show()
|
||||
setTimeout(() => {
|
||||
toast.hide()
|
||||
}, 1500)
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
@ -248,8 +248,8 @@ out:
|
||||
if err != nil {
|
||||
log.Println("Failed to marshal record", err)
|
||||
} else {
|
||||
tc.record.Write(jbuf)
|
||||
tc.record.Write([]byte(",")) // write a deliminator
|
||||
tc.record.Write(jbuf)
|
||||
}
|
||||
|
||||
tc.lastRecTime = time.Now()
|
||||
@ -259,7 +259,7 @@ out:
|
||||
var err error
|
||||
if cmd == recordCmd {
|
||||
// use the session ID and current as file name
|
||||
fname := "./records/" + tc.Name + "_" + strconv.FormatInt(time.Now().Unix(), 16) + ".rec"
|
||||
fname := "./records/" + tc.Name + "_" + strconv.FormatInt(time.Now().Unix(), 16) + ".scr"
|
||||
|
||||
tc.record, err = os.OpenFile(fname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
@ -268,16 +268,14 @@ out:
|
||||
}
|
||||
|
||||
tc.record.Write([]byte("[")) // write a [ for an array of json objs
|
||||
|
||||
// write a dummy record to clear the screen.
|
||||
tc.lastRecTime = time.Now()
|
||||
jbuf, _ := json.Marshal(WriteRecord{Dur: time.Since(tc.lastRecTime), Data: []byte("\033[2J\033[H")})
|
||||
tc.record.Write(jbuf)
|
||||
|
||||
} else {
|
||||
fsinfo, err := tc.record.Stat()
|
||||
|
||||
if err == nil {
|
||||
tc.record.Truncate(fsinfo.Size() - 1)
|
||||
tc.record.Seek(0, 2) // truncate does not change read/write location
|
||||
tc.record.Write([]byte("]"))
|
||||
}
|
||||
|
||||
tc.record.Write([]byte("]"))
|
||||
tc.record.Close()
|
||||
tc.record = nil
|
||||
}
|
||||
|
55
web/auth.go
55
web/auth.go
@ -9,9 +9,16 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
userkey = "user"
|
||||
userkey = "authorized_user"
|
||||
loginKey = "login_msg"
|
||||
)
|
||||
|
||||
func leftLoginMsg(c *gin.Context, msg string) {
|
||||
session := sessions.Default(c)
|
||||
session.Set(loginKey, msg)
|
||||
session.Save()
|
||||
}
|
||||
|
||||
func login(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
|
||||
@ -20,64 +27,66 @@ func login(c *gin.Context) {
|
||||
|
||||
// Validate form input
|
||||
if strings.Trim(username, " ") == "" || strings.Trim(passwd, " ") == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Username/password can't be empty"})
|
||||
leftLoginMsg(c, "User name or password cannot be empty")
|
||||
c.Redirect(http.StatusSeeOther, "/login")
|
||||
return
|
||||
}
|
||||
|
||||
// Check for username and password match, usually from a database
|
||||
if username != "hello" || passwd != "world" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication failed"})
|
||||
leftLoginMsg(c, "Username/password does not match")
|
||||
c.Redirect(http.StatusSeeOther, "/login")
|
||||
return
|
||||
}
|
||||
|
||||
// Save the username in the session
|
||||
session.Set(userkey, username) // In real world usage you'd set this to the users ID
|
||||
session.Set(userkey, username)
|
||||
|
||||
if err := session.Save(); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save session"})
|
||||
leftLoginMsg(c, "Failed to save session data")
|
||||
c.Redirect(http.StatusSeeOther, "/login")
|
||||
return
|
||||
}
|
||||
|
||||
host = &c.Request.Host
|
||||
|
||||
c.Redirect(http.StatusSeeOther, "/")
|
||||
}
|
||||
|
||||
func logout(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
|
||||
user := session.Get(userkey)
|
||||
if user == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid session token"})
|
||||
return
|
||||
}
|
||||
session.Delete(userkey)
|
||||
if err := session.Save(); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save session"})
|
||||
return
|
||||
if user != nil {
|
||||
session.Delete(userkey)
|
||||
session.Save()
|
||||
}
|
||||
|
||||
leftLoginMsg(c, "Welcome to WiTTY")
|
||||
c.Redirect(http.StatusFound, "/login")
|
||||
}
|
||||
|
||||
// AuthRequired is a simple middleware to check the session
|
||||
func AuthRequired(c *gin.Context) {
|
||||
if (c.Request.URL.String() == "/login") ||
|
||||
strings.HasPrefix(c.Request.URL.String(), "/assets") {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
session := sessions.Default(c)
|
||||
user := session.Get(userkey)
|
||||
|
||||
if user == nil {
|
||||
// Abort the request with the appropriate error code
|
||||
leftLoginMsg(c, "Not authorized, login first")
|
||||
c.Redirect(http.StatusTemporaryRedirect, "/login")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
// Continue down the chain to handler etc
|
||||
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func loginPage(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "login.html", gin.H{})
|
||||
session := sessions.Default(c)
|
||||
msg := session.Get(loginKey)
|
||||
|
||||
if msg == nil {
|
||||
msg = "Login first"
|
||||
}
|
||||
|
||||
c.HTML(http.StatusOK, "login.html", gin.H{"msg": msg})
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ func collectRecords(c *gin.Context, cmd string) (records []RecordedSession) {
|
||||
if err == nil {
|
||||
for _, finfo := range files {
|
||||
fname := finfo.Name()
|
||||
if !strings.HasSuffix(fname, ".rec") {
|
||||
if !strings.HasSuffix(fname, ".scr") {
|
||||
continue
|
||||
}
|
||||
fsize := finfo.Size() / 1024
|
||||
|
@ -44,44 +44,44 @@ func StartWeb(fp *os.File, cmd []string) {
|
||||
// so login can survive server reboot
|
||||
store := sessions.NewCookieStore([]byte(uniuri.NewLen(32)))
|
||||
rt.Use(sessions.Sessions("witty-session", store))
|
||||
rt.Use(AuthRequired)
|
||||
|
||||
rt.SetTrustedProxies(nil)
|
||||
rt.LoadHTMLGlob("./assets/template/*")
|
||||
|
||||
// Fill in the index page
|
||||
rt.GET("/", indexPage)
|
||||
rt.GET("/login", loginPage)
|
||||
|
||||
rt.POST("/login", login)
|
||||
rt.GET("/logout", logout)
|
||||
|
||||
// to update the tabs of current interactive and saved sessions
|
||||
rt.GET("/update/:active", updateIndex)
|
||||
|
||||
// create a new interactive session
|
||||
rt.GET("/new", newInteractive)
|
||||
rt.GET("/ws_new/:id", newTermConn)
|
||||
|
||||
// create a viewer of an interactive session
|
||||
rt.GET("/view/:id", viewPage)
|
||||
rt.GET("/ws_view/:id", newViewWS)
|
||||
|
||||
// start/stop recording the session
|
||||
rt.GET("/record/:id", startRecord)
|
||||
rt.GET("/stop/:id", stopRecord)
|
||||
|
||||
// create a viewer of an interactive session
|
||||
rt.GET("/replay/:id", replayPage)
|
||||
|
||||
// delete a recording
|
||||
rt.GET("/delete/:fname", delRec)
|
||||
|
||||
// handle static files
|
||||
rt.Static("/assets", "./assets")
|
||||
rt.Static("/records", "./records")
|
||||
rt.GET("/favicon.ico", favIcon)
|
||||
|
||||
rt.GET("/login", loginPage)
|
||||
rt.POST("/login", login)
|
||||
|
||||
g1 := rt.Group("/", AuthRequired)
|
||||
|
||||
// Fill in the index page
|
||||
g1.GET("/", indexPage)
|
||||
g1.GET("/logout", logout)
|
||||
|
||||
// to update the tabs of current interactive and saved sessions
|
||||
g1.GET("/update/:active", updateIndex)
|
||||
|
||||
// create a new interactive session
|
||||
g1.GET("/new", newInteractive)
|
||||
g1.GET("/ws_new/:id", newTermConn)
|
||||
|
||||
// create a viewer of an interactive session
|
||||
g1.GET("/view/:id", viewPage)
|
||||
g1.GET("/ws_view/:id", newViewWS)
|
||||
|
||||
// start/stop recording the session
|
||||
g1.GET("/record/:id", startRecord)
|
||||
g1.GET("/stop/:id", stopRecord)
|
||||
|
||||
// create a viewer of an interactive session
|
||||
g1.GET("/replay/:id", replayPage)
|
||||
|
||||
// delete a recording
|
||||
g1.GET("/delete/:fname", delRec)
|
||||
|
||||
term_conn.Init(checkOrigin)
|
||||
rt.RunTLS(":8080", "./tls/cert.pem", "./tls/private-key.pem")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user