Skip to content
Snippets Groups Projects
Commit e9ef5434 authored by Florent Gluck's avatar Florent Gluck
Browse files

Reworked nexus-exam a bit to be more robust to errors and added recurrent...

Reworked nexus-exam a bit to be more robust to errors and added recurrent token refresh to fix issue #145 "nexus-exam to renew its jwt token recurrently"
parent a086f66a
Branches
No related tags found
No related merge requests found
...@@ -2,6 +2,7 @@ package main ...@@ -2,6 +2,7 @@ package main
import ( import (
"os" "os"
"time"
"bytes" "bytes"
"errors" "errors"
"strings" "strings"
...@@ -13,14 +14,12 @@ import ( ...@@ -13,14 +14,12 @@ import (
e "nexus-client/exec" e "nexus-client/exec"
u "nexus-client/utils" u "nexus-client/utils"
g "nexus-client/globals" g "nexus-client/globals"
// "nexus-client/cmdVersion"
"nexus-client/defaults" "nexus-client/defaults"
"nexus-client/cmdLogin" "nexus-client/cmdLogin"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/app" "fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
...@@ -37,6 +36,7 @@ var ( ...@@ -37,6 +36,7 @@ var (
//go:embed nexus_exam_pwd.val //go:embed nexus_exam_pwd.val
nexus_exam_pwd string nexus_exam_pwd string
token string
certPath string certPath string
exitFn func() exitFn func()
) )
...@@ -48,35 +48,32 @@ func exit(code int) { ...@@ -48,35 +48,32 @@ func exit(code int) {
os.Exit(code) os.Exit(code)
} }
func errorDialog(parent fyne.Window, msg string) { func attachVM(parent fyne.Window, hostname, cert, pwd string) {
dialog.NewInformation("Connection error", msg, parent).Show()
}
func attachVM(parent fyne.Window, token, hostname, cert, pwd string) (int, error) {
client := g.GetInstance().Client client := g.GetInstance().Client
host := g.GetInstance().Host host := g.GetInstance().Host
client.SetHeader("Content-Type", "application/json")
client.SetHeader("Authorization", "Bearer "+token)
p := &params.VMAttachCreds{ Pwd: pwd } p := &params.VMAttachCreds{ Pwd: pwd }
resp, err := client.R().SetBody(p).Post(host+"/vms/spicecreds") resp, err := client.R().SetBody(p).Post(host+"/vms/spicecreds")
if err != nil { if err != nil {
errorDialog(parent, err.Error()) errorPopup(parent, "Failed attaching to VM (code 4)")
return
} }
if resp.IsSuccess() { if resp.IsSuccess() {
var creds vm.VMSpiceCredentialsSerialized var creds vm.VMSpiceCredentialsSerialized
if err := json.Unmarshal(resp.Body(), &creds); err != nil { if err := json.Unmarshal(resp.Body(), &creds); err != nil {
errorDialog(parent, err.Error()) errorPopup(parent, "Failed attaching to VM (code 5)")
return
} }
if err := validator.New(validator.WithRequiredStructEnabled()).Struct(creds); err != nil { if err := validator.New(validator.WithRequiredStructEnabled()).Struct(creds); err != nil {
errorDialog(parent, err.Error()) errorPopup(parent, "Failed attaching to VM (code 6)")
return
} }
go func(creds vm.VMSpiceCredentialsSerialized) { go func(creds vm.VMSpiceCredentialsSerialized) {
_, err := e.RunRemoteViewer(hostname, cert, creds.Name, creds.SpicePort, creds.SpicePwd, true) _, err := e.RunRemoteViewer(hostname, cert, creds.Name, creds.SpicePort, creds.SpicePwd, true)
if err != nil { if err != nil {
errorDialog(parent, err.Error()) errorPopup(parent, "Failed attaching to VM (code 7)")
return
} }
} (creds) } (creds)
} else { } else {
...@@ -85,15 +82,16 @@ func attachVM(parent fyne.Window, token, hostname, cert, pwd string) (int, error ...@@ -85,15 +82,16 @@ func attachVM(parent fyne.Window, token, hostname, cert, pwd string) (int, error
} }
var m msg var m msg
if err := json.Unmarshal(resp.Body(), &m); err != nil { if err := json.Unmarshal(resp.Body(), &m); err != nil {
errorDialog(parent, err.Error()) errorPopup(parent, "Failed attaching to VM (code 8)")
return
} }
errorDialog(parent, m.Message) errorPopup(parent, m.Message)
// errorDialog(parent, "Error: "+resp.Status()+": "+resp.String()) // errorPopup(parent, "Error: "+resp.Status()+": "+resp.String())
return
} }
return 0, nil
} }
func abortWindow(labelMsg string) { func abortWindow(msg string) {
a := app.New() a := app.New()
a.Settings().SetTheme(theme.LightTheme()) a.Settings().SetTheme(theme.LightTheme())
win := a.NewWindow(windowTitle) win := a.NewWindow(windowTitle)
...@@ -101,8 +99,8 @@ func abortWindow(labelMsg string) { ...@@ -101,8 +99,8 @@ func abortWindow(labelMsg string) {
win.Close() win.Close()
exit(1) exit(1)
}) })
label := widget.NewLabel("ERROR: "+labelMsg) label := widget.NewLabel("FATAL: "+msg)
button := widget.NewButton("OK", func() { button := widget.NewButton("Quit", func() {
win.Close() win.Close()
exit(1) exit(1)
}) })
...@@ -111,6 +109,16 @@ func abortWindow(labelMsg string) { ...@@ -111,6 +109,16 @@ func abortWindow(labelMsg string) {
win.ShowAndRun() win.ShowAndRun()
} }
func errorPopup(win fyne.Window, msg string) {
var modal *widget.PopUp
modal = widget.NewModalPopUp(
container.NewVBox(
widget.NewLabel("Error: "+msg),
widget.NewButton("Close", func() { modal.Hide() })),
win.Canvas())
modal.Show()
}
func hypervisorCheck() { func hypervisorCheck() {
cmd := exec.Command("systemd-detect-virt") cmd := exec.Command("systemd-detect-virt")
var out bytes.Buffer var out bytes.Buffer
...@@ -122,6 +130,35 @@ func hypervisorCheck() { ...@@ -122,6 +130,35 @@ func hypervisorCheck() {
} }
} }
// Recurrently obtains a new JWT token so that the user session doesn't expire.
func refreshToken(parent fyne.Window) {
client := g.GetInstance().Client
host := g.GetInstance().Host
for {
resp, err := client.R().Get(host+"/token/refresh")
if err != nil {
errorPopup(parent, "Failed refreshing token (code 1)")
} else {
if resp.IsSuccess() {
type Response struct {
Token string
}
var response Response
err = json.Unmarshal(resp.Body(), &response)
if err != nil {
errorPopup(parent, "Failed refreshing token (code 2)")
}
token = response.Token
} else {
// errorPopup(parent, resp.Status()+": "+resp.String())
errorPopup(parent, "Failed refreshing token (code 3)")
}
}
time.Sleep(4*time.Hour)
}
}
func run() int { func run() int {
hypervisorCheck() hypervisorCheck()
...@@ -166,17 +203,22 @@ func run() int { ...@@ -166,17 +203,22 @@ func run() int {
g.Init(hostname, host, certPath, client) g.Init(hostname, host, certPath, client)
client.SetTimeout(10*time.Second)
// Checks the client version is compatible with the server's API. // Checks the client version is compatible with the server's API.
// if !cmdVersion.CheckServerCompatibility("nexus-exam") { // if !cmdVersion.CheckServerCompatibility("nexus-exam") {
// abortWindow("client version is incompatible with server!") // abortWindow("client version is incompatible with server!")
// } // }
// Logins and obtains a JWT token. // Logins and obtains a JWT token.
token, err := cmdLogin.GetToken(nexus_exam_user, nexus_exam_pwd) token, err = cmdLogin.GetToken(nexus_exam_user, nexus_exam_pwd)
if err != nil { if err != nil {
abortWindow(err.Error()) abortWindow("Failed obtaining token (network issue?)")
} }
client.SetHeader("Content-Type", "application/json")
client.SetHeader("Authorization", "Bearer "+token)
app := app.New() app := app.New()
app.Settings().SetTheme(theme.LightTheme()) app.Settings().SetTheme(theme.LightTheme())
win := app.NewWindow(windowTitle) win := app.NewWindow(windowTitle)
...@@ -198,13 +240,16 @@ func run() int { ...@@ -198,13 +240,16 @@ func run() int {
{Text: "Password", Widget: pwdEntry}, {Text: "Password", Widget: pwdEntry},
}, },
OnSubmit: func() { OnSubmit: func() {
attachVM(win, token, hostname, certPath, pwdEntry.Text) attachVM(win, hostname, certPath, pwdEntry.Text)
}, },
SubmitText: "Connect", SubmitText: "Connect",
} }
win.SetContent(container.NewPadded(container.NewVBox(label, form))) win.SetContent(container.NewPadded(container.NewVBox(label, form)))
win.Resize(fyne.NewSize(600,200)) win.Resize(fyne.NewSize(600,200))
go refreshToken(win)
win.ShowAndRun() win.ShowAndRun()
return 0 return 0
} }
......
...@@ -50,7 +50,7 @@ func NewAuth(u *users.Users) (*auth, error) { ...@@ -50,7 +50,7 @@ func NewAuth(u *users.Users) (*auth, error) {
SigningKey: []byte(jwtSecretKey), SigningKey: []byte(jwtSecretKey),
Claims: &customClaims{}, Claims: &customClaims{},
ErrorHandlerWithContext: func(err error, c echo.Context) error { ErrorHandlerWithContext: func(err error, c echo.Context) error {
return echo.NewHTTPError(http.StatusUnauthorized, "access denied") return echo.NewHTTPError(http.StatusUnauthorized, "token expired: access denied")
}, },
}, },
}, nil }, nil
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment