add listusers and replay subcommand

This commit is contained in:
Zhi Wang 2022-01-22 09:29:01 -05:00
parent 8dfac2d026
commit a76beab83a
5 changed files with 136 additions and 18 deletions

View File

@ -7,7 +7,7 @@ This program allows you to use terminal in the browser. Simply run the program a
2. WiTTY allows users to **easily record, replay, and share console sessions** with just a few clicks. This make it a breeze to answer course-related questions, espeically with the source code. Instead of wall of text to describe their questions, students can just send a recorded session. 2. WiTTY allows users to **easily record, replay, and share console sessions** with just a few clicks. This make it a breeze to answer course-related questions, espeically with the source code. Instead of wall of text to describe their questions, students can just send a recorded session.
This repository contains a recorded session in the ```assets/extra``` directory ([M1NXZvHdvA8vSCKp_61e5d60f.rec](extra/M1NXZvHdvA8vSCKp_61e5d60f.rec)) that shows me upgrading pihole. Just put the file under the ```records``` directory, run the server, you should find the recording in the ```Recorded Session``` tab. This repository contains a recorded session in the ```assets/extra``` directory ([M1NXZvHdvA8vSCKp_61e5d60f.scr](extra/M1NXZvHdvA8vSCKp_61e5d60f.scr)) that shows me upgrading pihole. Just put the file under the ```records``` directory, run the server, you should find the recording in the ```Recorded Session``` tab.
3. More features are planned, suggestions are welcome. 3. More features are planned, suggestions are welcome.
@ -80,4 +80,8 @@ Most icons were provided by [fontawesome](https://fontawesome.com/) under this [
```https://<witty_server_ip>:8080``` ```https://<witty_server_ip>:8080```
8. You can also replay the recorded sessions with witty
```./witty replay -w 500 records/<recorded file>.scr```
The program has been tested on Linux, WSL2, Raspberry Pi 3B (Debian), and MacOSX using Google Chrome, Firefox, and Safari. The program has been tested on Linux, WSL2, Raspberry Pi 3B (Debian), and MacOSX using Google Chrome, Firefox, and Safari.

37
main.go
View File

@ -6,19 +6,13 @@ import (
"log" "log"
"os" "os"
"github.com/syssecfsu/witty/term_conn"
"github.com/syssecfsu/witty/web" "github.com/syssecfsu/witty/web"
) )
func main() { func main() {
fp, err := os.OpenFile("witty.log", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err == nil {
defer fp.Close()
log.SetOutput(fp)
}
if len(os.Args) < 2 { if len(os.Args) < 2 {
fmt.Println("witty (adduser|deluser|run)") fmt.Println("witty (adduser|deluser|replay|run)")
return return
} }
@ -27,6 +21,11 @@ func main() {
runCmd.BoolVar(&naked, "n", false, "Run WiTTY without user authentication") runCmd.BoolVar(&naked, "n", false, "Run WiTTY without user authentication")
runCmd.BoolVar(&naked, "naked", false, "Run WiTTY without user authentication") runCmd.BoolVar(&naked, "naked", false, "Run WiTTY without user authentication")
var wait uint
replayCmd := flag.NewFlagSet("replay", flag.ExitOnError)
replayCmd.UintVar(&wait, "w", 2000, "Max wait time between outputs")
replayCmd.UintVar(&wait, "wait", 2000, "Max wait time between outputs")
switch os.Args[1] { switch os.Args[1] {
case "adduser": case "adduser":
if len(os.Args) != 3 { if len(os.Args) != 3 {
@ -42,7 +41,27 @@ func main() {
} }
web.DelUser(os.Args[2]) web.DelUser(os.Args[2])
case "listusers":
web.ListUsers()
case "replay":
replayCmd.Parse(os.Args[2:])
if len(replayCmd.Args()) != 1 {
fmt.Println("witty replay <recorded>")
return
}
term_conn.Replay(replayCmd.Arg(0), wait)
case "run": case "run":
fp, err := os.OpenFile("witty.log", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err == nil {
defer fp.Close()
log.SetOutput(fp)
}
runCmd.Parse(os.Args[2:]) runCmd.Parse(os.Args[2:])
var cmdToExec []string var cmdToExec []string
@ -57,7 +76,7 @@ func main() {
web.StartWeb(fp, cmdToExec, naked) web.StartWeb(fp, cmdToExec, naked)
default: default:
fmt.Println("witty (adduser|deluser|run)") fmt.Println("witty (adduser|deluser|replay|run)")
return return
} }

71
term_conn/replay.go Normal file
View File

@ -0,0 +1,71 @@
package term_conn
import (
"encoding/json"
"io"
"log"
"os"
"time"
"golang.org/x/term"
)
type writeRecord struct {
Dur time.Duration `json:"Duration"`
Data []byte `json:"Data"`
}
func Replay(fname string, wait uint) {
fp, err := os.Open(fname)
if err != nil {
log.Fatalln("Failed to open record file", err)
}
screen := struct {
io.Reader
io.Writer
}{os.Stdin, os.Stdout}
t := term.NewTerminal(screen, "$")
if t == nil {
log.Fatalln("Failed to create terminal")
}
w, h, _ := term.GetSize(int(os.Stdout.Fd()))
if (w != 120) || (h != 36) {
log.Println("Set terminal window to 120x36 before continue")
}
decoder := json.NewDecoder(fp)
if decoder == nil {
log.Fatalln("Failed to create JSON decoder")
}
// To work with javascript decoder, we organize the file as
// an array of writeRecord. golang decode instead decode
// as individual record. Call decoder.Token to skip opening [
t.Write([]byte("\n\n---beginning of replay---\n\n"))
decoder.Token()
for decoder.More() {
var record writeRecord
if err := decoder.Decode(&record); err != nil {
log.Println("Failed to decode record", err)
continue
}
if record.Dur > time.Duration(wait)*time.Millisecond {
record.Dur = time.Duration(wait) * time.Millisecond
}
time.Sleep(record.Dur)
t.Write(record.Data)
}
t.Write([]byte("\n\n---end of replay---\n\n"))
}

View File

@ -5,6 +5,7 @@ import (
"crypto/sha256" "crypto/sha256"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log"
"os" "os"
"github.com/dchest/uniuri" "github.com/dchest/uniuri"
@ -54,7 +55,7 @@ func addUser(username []byte, passwd []byte) {
output, err := json.Marshal(users) output, err := json.Marshal(users)
if err != nil { if err != nil {
fmt.Println("Failed to marshal passwords", err) log.Println("Failed to marshal passwords", err)
return return
} }
@ -66,7 +67,7 @@ func AddUser(username string) {
passwd, err := term.ReadPassword(int(os.Stdin.Fd())) passwd, err := term.ReadPassword(int(os.Stdin.Fd()))
if err != nil { if err != nil {
fmt.Println("Failed to read password", err) log.Println("Failed to read password", err)
return return
} }
@ -74,7 +75,7 @@ func AddUser(username string) {
passwd2, err := term.ReadPassword(int(os.Stdin.Fd())) passwd2, err := term.ReadPassword(int(os.Stdin.Fd()))
if err != nil { if err != nil {
fmt.Println("Failed to read password", err) log.Println("Failed to read password", err)
return return
} }
@ -93,14 +94,14 @@ func DelUser(username string) {
exist := false exist := false
file, err := os.ReadFile(userFileName) file, err := os.ReadFile(userFileName)
if err != nil { if err != nil {
fmt.Println("Failed to read users file", err) log.Println("Failed to read users file", err)
return return
} }
err = json.Unmarshal(file, &users) err = json.Unmarshal(file, &users)
if err != nil { if err != nil {
fmt.Println("Failed to parse json format", err) log.Println("Failed to parse json format", err)
return return
} }
// update the existing user if it exists // update the existing user if it exists
@ -115,7 +116,7 @@ func DelUser(username string) {
if exist { if exist {
output, err := json.Marshal(users) output, err := json.Marshal(users)
if err != nil { if err != nil {
fmt.Println("Failed to marshal passwords", err) log.Println("Failed to marshal passwords", err)
return return
} }
@ -123,20 +124,43 @@ func DelUser(username string) {
} }
} }
func ListUsers() {
var users []UserRecord
var err error
file, err := os.ReadFile(userFileName)
if err != nil {
log.Println("Failed to read users file", err)
return
}
err = json.Unmarshal(file, &users)
if err != nil {
log.Println("Failed to parse json format", err)
return
}
// update the existing user if it exists
fmt.Println("Users of the system:")
for _, u := range users {
fmt.Println(" ", string(u.User))
}
}
func ValidateUser(username []byte, passwd []byte) bool { func ValidateUser(username []byte, passwd []byte) bool {
var users []UserRecord var users []UserRecord
var err error var err error
file, err := os.ReadFile(userFileName) file, err := os.ReadFile(userFileName)
if err != nil { if err != nil {
fmt.Println("Failed to read users file", err) log.Println("Failed to read users file", err)
return false return false
} }
err = json.Unmarshal(file, &users) err = json.Unmarshal(file, &users)
if err != nil { if err != nil {
fmt.Println("Failed to parse json format", err) log.Println("Failed to parse json format", err)
return false return false
} }