diff --git a/src/client/cmdVM/attachHelper.go b/src/client/cmdVM/attachHelper.go index f607e0dbbe05b93b5fd322298242dfcbb97bc1e4..d4ad3d44f26e79512ad99d9384d290d48bc3e47b 100644 --- a/src/client/cmdVM/attachHelper.go +++ b/src/client/cmdVM/attachHelper.go @@ -1,71 +1,37 @@ package cmdVM import ( - "encoding/json" - "errors" "fmt" "nexus-client/exec" u "nexus-client/utils" "nexus-common/params" "nexus-common/vm" g "nexus-libclient/globals" - "sync" - - "github.com/go-playground/validator/v10" + libclient "nexus-libclient/vm" ) -// The boolean indicates whether attaching is blocking (synchronous) or non-blocking (asynchronous). -func AttachToVMs(vms []vm.VMAttachCredentialsSerialized, synchronous bool) (int, error) { +func AttachToVMs(vms []vm.VMAttachCredentialsSerialized) (int, error) { statusCode := 0 hostname := g.GetInstance().Hostname cert := g.GetInstance().PubCert - client := g.GetInstance().Client - host := g.GetInstance().Host - - var wg sync.WaitGroup - - if synchronous { - // Use wait groups to wait until all viewers threads have completed. - wg.Add(len(vms)) - } - for _, v := range vms { - uuid := v.ID.String() - p := ¶ms.VMAttachCreds{Pwd: v.Pwd} - resp, err := client.R().SetBody(p).Post(host + "/vms/" + uuid + "/spicecreds") + p := params.VMAttachCreds{Pwd: v.Pwd} + + creds, err := libclient.VMGetSpiceCreds(v.ID.String(), p) if err != nil { + u.PrintlnErr(err) return 1, err } - if resp.IsSuccess() { - var creds vm.VMSpiceCredentialsSerialized - if err := json.Unmarshal(resp.Body(), &creds); err != nil { - return 1, err + go func(v vm.VMAttachCredentialsSerialized, creds vm.VMSpiceCredentialsSerialized) { + stdoutStderr, err := exec.RunRemoteViewer(hostname, cert, creds.Name, creds.SpicePort, creds.SpicePwd, false) + if err != nil { + u.PrintlnErr("Failed attaching to VM ", v.ID, ": ", fmt.Sprintf("%s", stdoutStderr)) + statusCode |= 1 } - if err := validator.New(validator.WithRequiredStructEnabled()).Struct(creds); err != nil { - return 1, err - } - - go func(v vm.VMAttachCredentialsSerialized, creds vm.VMSpiceCredentialsSerialized) { - stdoutStderr, err := exec.RunRemoteViewer(hostname, cert, creds.Name, creds.SpicePort, creds.SpicePwd, false) - if err != nil { - u.PrintlnErr("Failed attaching to VM ", v.ID, ": ", fmt.Sprintf("%s", stdoutStderr)) - statusCode |= 1 - } - if synchronous { - wg.Done() - } - }(v, creds) - - } else { - return 1, errors.New(resp.Status() + ": " + resp.String()) - } - } - - if synchronous { - wg.Wait() + }(v, *creds) } return statusCode, nil diff --git a/src/client/cmdVM/helper.go b/src/client/cmdVM/helper.go index a20df134f367b89efb5e60984fafe8d05e58f55a..9259f42dccf1ef15243c9b39cb4a5e894d08ef59 100644 --- a/src/client/cmdVM/helper.go +++ b/src/client/cmdVM/helper.go @@ -108,7 +108,7 @@ func getFilteredVMs(filteredVMsFunc GetVMs, patterns []string) ([]vm.VMNetworkSe // // "." -> matches everything // "bla" -> matches any VM name containing "bla" -func getFilteredVMCredentials(route string, patterns []string) ([]vm.VMAttachCredentialsSerialized, error) { +func getFilteredVMCredentials(patterns []string) ([]vm.VMAttachCredentialsSerialized, error) { if len(patterns) < 1 { return nil, errors.New("At least one ID or regex must be specified") } diff --git a/src/client/cmdVM/vmAttachAsync.go b/src/client/cmdVM/vmAttachAsync.go index d147716b78bb3e0a0b835fbdeaed84325f5b99db..748a9417b9847ea36f99c7a3465a399dceae2e81 100644 --- a/src/client/cmdVM/vmAttachAsync.go +++ b/src/client/cmdVM/vmAttachAsync.go @@ -36,7 +36,7 @@ func (cmd *AttachAsync) Run(args []string) int { return 1 } - creds, err := getFilteredVMCredentials("/vms/attach", args) + creds, err := getFilteredVMCredentials(args) if err != nil { u.PrintlnErr(err) return 1 @@ -47,7 +47,7 @@ func (cmd *AttachAsync) Run(args []string) int { return 1 } - statusCode, err := AttachToVMs(creds, false) + statusCode, err := AttachToVMs(creds) if err != nil { u.PrintlnErr(err) } diff --git a/src/client/cmdVM/vmAttachAsyncSingle.go b/src/client/cmdVM/vmAttachAsyncSingle.go index 6fce7a941879a75e30f5af7b090c1fb7fb173882..872013bd090493d70ee05ff85c9bcf5498db4946 100644 --- a/src/client/cmdVM/vmAttachAsyncSingle.go +++ b/src/client/cmdVM/vmAttachAsyncSingle.go @@ -50,7 +50,7 @@ func (cmd *AttachAsyncSingle) Run(args []string) int { return 1 } - statusCode, err := AttachToVMs([]vm.VMAttachCredentialsSerialized{*creds}, false) + statusCode, err := AttachToVMs([]vm.VMAttachCredentialsSerialized{*creds}) if err != nil { u.PrintlnErr(err) return 1 diff --git a/src/client/cmdVM/vmAttachFromPwd.go b/src/client/cmdVM/vmAttachFromPwd.go index 48d75f33eeb91a3520c9acb31fcc21753e01eaf2..1bbf3f4726c6b418fe7181aef39020986c476d49 100644 --- a/src/client/cmdVM/vmAttachFromPwd.go +++ b/src/client/cmdVM/vmAttachFromPwd.go @@ -1,15 +1,13 @@ package cmdVM import ( - "encoding/json" "nexus-client/exec" e "nexus-client/exec" u "nexus-client/utils" "nexus-common/params" "nexus-common/vm" g "nexus-libclient/globals" - - "github.com/go-playground/validator/v10" + libclient "nexus-libclient/vm" ) type AttachFromPwd struct { @@ -23,7 +21,7 @@ func (cmd *AttachFromPwd) GetName() string { func (cmd *AttachFromPwd) GetDesc() []string { return []string{ "Attaches to the VM that matches the specified password.", - "Requires the VM_ATTACH_ANY user capability."} + "Requires either EXAM_ATTACH or VM_ATTACH_ANY user capability."} } func (cmd *AttachFromPwd) PrintUsage() { @@ -47,47 +45,23 @@ func (cmd *AttachFromPwd) Run(args []string) int { return 1 } - client := g.GetInstance().Client - host := g.GetInstance().Host - hostname := g.GetInstance().Hostname cert := g.GetInstance().PubCert pwd := args[0] - p := ¶ms.VMAttachCreds{Pwd: pwd} - resp, err := client.R().SetBody(p).Post(host + "/vms/spicecreds") + p := params.VMAttachCreds{Pwd: pwd} + + creds, err := libclient.VMGetAnySpiceCreds(p) if err != nil { - u.PrintlnErr("Failed retrieving VM credentials: " + err.Error()) + u.PrintlnErr(err) return 1 - } - - if resp.IsSuccess() { - var creds vm.VMSpiceCredentialsSerialized - if err := json.Unmarshal(resp.Body(), &creds); err != nil { - u.PrintlnErr("Failed deserializing VM spice credentials: " + err.Error()) - return 1 - } - if err := validator.New(validator.WithRequiredStructEnabled()).Struct(creds); err != nil { - u.PrintlnErr("Failed validating VM spice credentials: " + err.Error()) - return 1 - } + } else { go func(creds vm.VMSpiceCredentialsSerialized) { _, err := e.RunRemoteViewer(hostname, cert, creds.Name, creds.SpicePort, creds.SpicePwd, true) if err != nil { u.PrintlnErr("Failed executing remote-viewer: " + err.Error()) } - }(creds) - } else { - type msg struct { - Message string `json:"message"` - } - var m msg - if err := json.Unmarshal(resp.Body(), &m); err != nil { - u.PrintlnErr("Failed retrieving VM credentials: " + err.Error()) - return 1 - } - u.PrintlnErr("Failed retrieving VM credentials: " + m.Message) - return 1 + }(*creds) } return 0 diff --git a/src/client/cmdVM/vmCreds2csv.go b/src/client/cmdVM/vmCreds2csv.go index 5474cd93ecd6fb582128322905e91698b3c275a0..f11e49601f4af922ab1f4952f8764427713cbfb4 100644 --- a/src/client/cmdVM/vmCreds2csv.go +++ b/src/client/cmdVM/vmCreds2csv.go @@ -41,7 +41,7 @@ func (cmd *Creds2csv) Run(args []string) int { csvFile := args[argc-1] - credsList, err := getFilteredVMCredentials("/vms/attach", args[:argc-1]) + credsList, err := getFilteredVMCredentials(args[:argc-1]) if err != nil { u.PrintlnErr("Error: " + err.Error()) return 1 diff --git a/src/client/cmdVM/vmCreds2pdf.go b/src/client/cmdVM/vmCreds2pdf.go index 77d60cda6a70dfaa6d5490b193b6fde3d31e6354..7a2c1b9c501c166342431c87976c66d1762780d5 100644 --- a/src/client/cmdVM/vmCreds2pdf.go +++ b/src/client/cmdVM/vmCreds2pdf.go @@ -44,7 +44,7 @@ func (cmd *Creds2pdf) Run(args []string) int { pdfFile := args[argc-1] - credsList, err := getFilteredVMCredentials("/vms/attach", args[:argc-1]) + credsList, err := getFilteredVMCredentials(args[:argc-1]) if err != nil { u.PrintlnErr("Error: " + err.Error()) return 1 diff --git a/src/client/cmdVM/vmListSingle.go b/src/client/cmdVM/vmListSingle.go index be8ef4b579d06466a55f3d4b5db6689a178bd234..8d06c5d71dfafc12cb1fffa8e0b5512949b3168a 100644 --- a/src/client/cmdVM/vmListSingle.go +++ b/src/client/cmdVM/vmListSingle.go @@ -36,7 +36,7 @@ func (cmd *ListSingle) Run(args []string) int { } vmID := args[0] - vm, err := libclient.GetListableVM(vmID) + vm, err := libclient.GetListVM(vmID) if err != nil { u.PrintlnErr(err) return 1 diff --git a/src/client/cmdVM/vmStartAttach.go b/src/client/cmdVM/vmStartAttach.go index 4cca859438ff75e810d36043b3f4a1dd7b1a4351..d3ae95bc6ba6c080dbc97c5e0dd4ad0ba64bb04d 100644 --- a/src/client/cmdVM/vmStartAttach.go +++ b/src/client/cmdVM/vmStartAttach.go @@ -73,7 +73,7 @@ func (cmd *StartAttach) Run(args []string) int { } // at this point, the returned filtered credentials only works for VMs that started successfully - creds, err := getFilteredVMCredentials("/vms/attach", args) + creds, err := getFilteredVMCredentials(args) if err != nil { u.PrintlnErr(err) return 1 @@ -84,7 +84,7 @@ func (cmd *StartAttach) Run(args []string) int { return 1 } - attachStatusCode, err := AttachToVMs(creds, false) + attachStatusCode, err := AttachToVMs(creds) if err != nil { u.PrintlnErr(err) } diff --git a/src/client/nexush/nexush.go b/src/client/nexush/nexush.go index 3a12c03e4d9cf4b1799f1047c3a7727880f06b46..181f453b03fd1631183f8e5fa699a1fe448a4abd 100644 --- a/src/client/nexush/nexush.go +++ b/src/client/nexush/nexush.go @@ -60,7 +60,7 @@ var cmdList = []cmd.Command{ &cmdVM.AddAccess{"vmaddaccess"}, &cmdVM.AttachAsync{"vmattach"}, // &cmdVM.AttachAsyncSingle{"vmattachsingle"}, // for testing the route only - // &cmdVM.AttachFromPwd{"vmattachfrompwd"}, // for testing the route only + &cmdVM.AttachFromPwd{"vmattachfrompwd"}, // for testing the route only &cmdVM.Create{"vmcreate"}, &cmdVM.Creds2pdf{"vmcreds2pdf"}, &cmdVM.Creds2csv{"vmcreds2csv"}, diff --git a/src/libclient/vm/vmGet.go b/src/libclient/vm/vmGet.go index 2b2eaf3de2ed665cd83736ff8dda71ba0b529c08..34bac139c1e645cd67601d2268ee0b78e62b319f 100644 --- a/src/libclient/vm/vmGet.go +++ b/src/libclient/vm/vmGet.go @@ -69,31 +69,31 @@ func getFilteredVMs(route string) ([]vm.VMNetworkSerialized, error) { } if resp.IsSuccess() { - templates, err := deserializeVMs(resp) + vms, err := deserializeVMs(resp) if err != nil { return nil, err } - return templates, nil + return vms, nil } else { return nil, response.ErrorToMsg(resp) } } -func GetListableVM(uuid string) (*vm.VMNetworkSerialized, error) { +func GetListVM(vmID string) (*vm.VMNetworkSerialized, error) { client := g.GetInstance().Client host := g.GetInstance().Host - resp, err := client.R().Get(host + "/vms/" + uuid) + resp, err := client.R().Get(host + "/vms/" + vmID) if err != nil { return nil, err } if resp.IsSuccess() { - template, err := deserializeVM(resp) + vm, err := deserializeVM(resp) if err != nil { return nil, err } - return template, nil + return vm, nil } else { return nil, response.ErrorToMsg(resp) } diff --git a/src/libclient/vm/vmGetSpiceCreds.go b/src/libclient/vm/vmGetSpiceCreds.go new file mode 100644 index 0000000000000000000000000000000000000000..d97ac60c447c5e3335c97a89641b552ac8c320f0 --- /dev/null +++ b/src/libclient/vm/vmGetSpiceCreds.go @@ -0,0 +1,52 @@ +package vm + +import ( + "encoding/json" + "errors" + "nexus-common/params" + "nexus-common/vm" + g "nexus-libclient/globals" + "nexus-libclient/response" + + "github.com/go-playground/validator/v10" + "github.com/go-resty/resty/v2" +) + +func VMGetSpiceCreds(vmID string, p params.VMAttachCreds) (*vm.VMSpiceCredentialsSerialized, error) { + client := g.GetInstance().Client + host := g.GetInstance().Host + + resp, err := client.R().SetBody(p).Post(host + "/vms/" + vmID + "/spicecreds") + if err != nil { + return nil, err + } + + return handleResponse(resp) +} + +func VMGetAnySpiceCreds(p params.VMAttachCreds) (*vm.VMSpiceCredentialsSerialized, error) { + client := g.GetInstance().Client + host := g.GetInstance().Host + + resp, err := client.R().SetBody(p).Post(host + "/vms/spicecreds") + if err != nil { + return nil, err + } + + return handleResponse(resp) +} + +func handleResponse(resp *resty.Response) (*vm.VMSpiceCredentialsSerialized, error) { + if resp.IsSuccess() { + var creds vm.VMSpiceCredentialsSerialized + if err := json.Unmarshal(resp.Body(), &creds); err != nil { + return nil, errors.New("Failed deserializing VM spice credentials: " + err.Error()) + } + if err := validator.New(validator.WithRequiredStructEnabled()).Struct(creds); err != nil { + return nil, errors.New("Failed validating VM spice credentials: " + err.Error()) + } + return &creds, nil + } else { + return nil, response.ErrorToMsg(resp) + } +} diff --git a/src/server/router/routerVMs.go b/src/server/router/routerVMs.go index e328202f0f4c220b8c0a21c4f3efa12a8c6af474..51d59d42215879574fb97054e0d4b846ecfa5369 100644 --- a/src/server/router/routerVMs.go +++ b/src/server/router/routerVMs.go @@ -205,7 +205,8 @@ func (r *RouterVMs) VMSpiceCreds(c echo.Context) error { // Returns the Spice credentials for the VM matching the specific VM attach password. // Output: nexus-common/vm.VMSpiceCredentialsSerialized -// Requires either CAP_EXAM_ATTACH or CAP_VM_ATTACH_ANY user capability: +// Requires either to be the VM's owner, or either capability: +// CAP_EXAM_ATTACH or CAP_VM_ATTACH_ANY user capability: // curl --cacert ca.pem -X POST https://localhost:1077/vms/spicecreds -H 'Content-Type: application/json' -d '{"pwd":"46L8drgZ5Dx"}' -H "Authorization: Bearer <AccessToken>" func (r *RouterVMs) VMSpiceCredsAny(c echo.Context) error { // Retrieves logged user from context.