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
No related branches found
No related tags found
No related merge requests found
......@@ -2,6 +2,7 @@ package main
import (
"os"
"time"
"bytes"
"errors"
"strings"
......@@ -13,14 +14,12 @@ import (
e "nexus-client/exec"
u "nexus-client/utils"
g "nexus-client/globals"
// "nexus-client/cmdVersion"
"nexus-client/defaults"
"nexus-client/cmdLogin"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/container"
"github.com/go-resty/resty/v2"
"github.com/go-playground/validator/v10"
......@@ -37,6 +36,7 @@ var (
//go:embed nexus_exam_pwd.val
nexus_exam_pwd string
token string
certPath string
exitFn func()
)
......@@ -48,35 +48,32 @@ func exit(code int) {
os.Exit(code)
}
func errorDialog(parent fyne.Window, msg string) {
dialog.NewInformation("Connection error", msg, parent).Show()
}
func attachVM(parent fyne.Window, token, hostname, cert, pwd string) (int, error) {
func attachVM(parent fyne.Window, hostname, cert, pwd string) {
client := g.GetInstance().Client
host := g.GetInstance().Host
client.SetHeader("Content-Type", "application/json")
client.SetHeader("Authorization", "Bearer "+token)
p := &params.VMAttachCreds{ Pwd: pwd }
resp, err := client.R().SetBody(p).Post(host+"/vms/spicecreds")
if err != nil {
errorDialog(parent, err.Error())
errorPopup(parent, "Failed attaching to VM (code 4)")
return
}
if resp.IsSuccess() {
var creds vm.VMSpiceCredentialsSerialized
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 {
errorDialog(parent, err.Error())
errorPopup(parent, "Failed attaching to VM (code 6)")
return
}
go func(creds vm.VMSpiceCredentialsSerialized) {
_, err := e.RunRemoteViewer(hostname, cert, creds.Name, creds.SpicePort, creds.SpicePwd, true)
if err != nil {
errorDialog(parent, err.Error())
errorPopup(parent, "Failed attaching to VM (code 7)")
return
}
} (creds)
} else {
......@@ -85,15 +82,16 @@ func attachVM(parent fyne.Window, token, hostname, cert, pwd string) (int, error
}
var m msg
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)
// errorDialog(parent, "Error: "+resp.Status()+": "+resp.String())
errorPopup(parent, m.Message)
// errorPopup(parent, "Error: "+resp.Status()+": "+resp.String())
return
}
return 0, nil
}
func abortWindow(labelMsg string) {
func abortWindow(msg string) {
a := app.New()
a.Settings().SetTheme(theme.LightTheme())
win := a.NewWindow(windowTitle)
......@@ -101,8 +99,8 @@ func abortWindow(labelMsg string) {
win.Close()
exit(1)
})
label := widget.NewLabel("ERROR: "+labelMsg)
button := widget.NewButton("OK", func() {
label := widget.NewLabel("FATAL: "+msg)
button := widget.NewButton("Quit", func() {
win.Close()
exit(1)
})
......@@ -111,6 +109,16 @@ func abortWindow(labelMsg string) {
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() {
cmd := exec.Command("systemd-detect-virt")
var out bytes.Buffer
......@@ -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 {
hypervisorCheck()
......@@ -166,17 +203,22 @@ func run() int {
g.Init(hostname, host, certPath, client)
client.SetTimeout(10*time.Second)
// Checks the client version is compatible with the server's API.
// if !cmdVersion.CheckServerCompatibility("nexus-exam") {
// abortWindow("client version is incompatible with server!")
// }
// 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 {
abortWindow(err.Error())
abortWindow("Failed obtaining token (network issue?)")
}
client.SetHeader("Content-Type", "application/json")
client.SetHeader("Authorization", "Bearer "+token)
app := app.New()
app.Settings().SetTheme(theme.LightTheme())
win := app.NewWindow(windowTitle)
......@@ -198,13 +240,16 @@ func run() int {
{Text: "Password", Widget: pwdEntry},
},
OnSubmit: func() {
attachVM(win, token, hostname, certPath, pwdEntry.Text)
attachVM(win, hostname, certPath, pwdEntry.Text)
},
SubmitText: "Connect",
}
win.SetContent(container.NewPadded(container.NewVBox(label, form)))
win.Resize(fyne.NewSize(600,200))
go refreshToken(win)
win.ShowAndRun()
return 0
}
......
......@@ -50,7 +50,7 @@ func NewAuth(u *users.Users) (*auth, error) {
SigningKey: []byte(jwtSecretKey),
Claims: &customClaims{},
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
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment