From fd5fe28a0190c67aed2c9272466085a69070a655 Mon Sep 17 00:00:00 2001 From: Florent Gluck <florent.gluck@hesge.ch> Date: Fri, 29 Mar 2024 08:51:48 +0100 Subject: [PATCH] server: - added route to retrieve a single VM from its UUID - added route to retrieve a single template from its UUID client: - added vmlistsingle to test above route (but then commented out) - added tpllistsingle to test above route (but then commented out) --- docs/README_server.md | 17 +++++++------- src/client/cmdTemplate/helper.go | 17 ++++++++++---- src/client/cmdVM/helper.go | 10 ++++---- src/client/cmdVM/vmCreate.go | 2 +- src/client/cmdVM/vmListSingle.go | 2 +- src/client/nexush/nexush.go | 3 ++- src/server/router/router.go | 1 + src/server/router/routerTemplates.go | 35 ++++++++++++++++++++++++++++ src/server/router/routerVMs.go | 15 ++++++------ 9 files changed, 74 insertions(+), 28 deletions(-) diff --git a/docs/README_server.md b/docs/README_server.md index 814f0e5..8b4fbc5 100644 --- a/docs/README_server.md +++ b/docs/README_server.md @@ -366,14 +366,15 @@ These capabilities are called "VM access capabilities": ### Template management -| Route | Description | Method | Parameters | Req. user cap. | -|--- |--- |--- |--- |--- | -| `/templates/vm` | create a template | POST | vmID,name,access | `TPL_CREATE` | -| `/templates/qcow` | create a template | POST | qcow,name,access | `TPL_CREATE` | -| `/templates/{id}` | edit a template | PUT | name,access | `TPL_EDIT_ANY` OR `TPL_EDIT` | -| `/templates/{id}` | delete a template | DELETE | | `TPL_DESTROY_ANY` OR `TPL_DESTROY` | -| `/templates/{id}/disk` | download a template's disk | GET | | `TPL_READFS_ANY` OR `TPL_READFS` | -| `/templates` | list templates | GET | | `TPL_LIST_ANY` OR `TPL_LIST` | +| Route | Description | Method | Parameters | Req. user cap. | +|--- |--- |--- |--- |--- | +| `/templates` | returns templates that can be listed | GET | | `TPL_LIST_ANY` OR `TPL_LIST` | +| `/templates/{id}` | returns a template | GET | | `TPL_LIST_ANY` OR `TPL_LIST` | +| `/templates/vm` | create a template | POST | vmID,name,access | `TPL_CREATE` | +| `/templates/qcow` | create a template | POST | qcow,name,access | `TPL_CREATE` | +| `/templates/{id}` | edit a template | PUT | name,access | `TPL_EDIT_ANY` OR `TPL_EDIT` | +| `/templates/{id}` | delete a template | DELETE | | `TPL_DESTROY_ANY` OR `TPL_DESTROY` | +| `/templates/{id}/disk` | download a template's disk | GET | | `TPL_READFS_ANY` OR `TPL_READFS` | Remarks: diff --git a/src/client/cmdTemplate/helper.go b/src/client/cmdTemplate/helper.go index 6f34648..2fa149b 100644 --- a/src/client/cmdTemplate/helper.go +++ b/src/client/cmdTemplate/helper.go @@ -141,7 +141,7 @@ func getFilteredTemplates(route string, patterns []string) ([]t.TemplateSerializ templatesList := []t.TemplateSerialized{} if resp.IsSuccess() { - templates, err := getTemplates(resp) + templates, err := deserializeTemplates(resp) if err != nil { return nil, err } @@ -176,11 +176,20 @@ func getFilteredTemplates(route string, patterns []string) ([]t.TemplateSerializ } } -// Retrieves all templates (no filtering). -func getTemplates(resp *resty.Response) ([]t.TemplateSerialized, error) { +// Deserialize a list of templates from an http response (no filtering). +func deserializeTemplates(resp *resty.Response) ([]t.TemplateSerialized, error) { templates := []t.TemplateSerialized{} if err := json.Unmarshal(resp.Body(), &templates); err != nil { return nil, err } return templates, nil -} \ No newline at end of file +} + +// Deserialize a single template from an http response. +func deserializeTemplate(resp *resty.Response) (*t.TemplateSerialized, error) { + var tpl *t.TemplateSerialized = &t.TemplateSerialized{} + if err := json.Unmarshal(resp.Body(), &tpl); err != nil { + return tpl, err + } + return tpl, nil +} diff --git a/src/client/cmdVM/helper.go b/src/client/cmdVM/helper.go index 306245a..779c860 100644 --- a/src/client/cmdVM/helper.go +++ b/src/client/cmdVM/helper.go @@ -134,7 +134,7 @@ func getFilteredVMs(route string, patterns []string) ([]vm.VMNetworkSerialized, vmsList := []vm.VMNetworkSerialized{} if resp.IsSuccess() { - vms, err := getVMs(resp) + vms, err := deserializeVMs(resp) if err != nil { return nil, err } @@ -169,8 +169,8 @@ func getFilteredVMs(route string, patterns []string) ([]vm.VMNetworkSerialized, } } -// Retrieves all VMs (no filtering). -func getVMs(resp *resty.Response) ([]vm.VMNetworkSerialized, error) { +// Deserialize a list of VMs from an http response (no filtering). +func deserializeVMs(resp *resty.Response) ([]vm.VMNetworkSerialized, error) { vms := []vm.VMNetworkSerialized{} if err := json.Unmarshal(resp.Body(), &vms); err != nil { return nil, err @@ -178,8 +178,8 @@ func getVMs(resp *resty.Response) ([]vm.VMNetworkSerialized, error) { return vms, nil } -// Retrieve a single VM. -func getVM(resp *resty.Response) (*vm.VMNetworkSerialized, error) { +// Deserialize a single VM from an http response. +func deserializeVM(resp *resty.Response) (*vm.VMNetworkSerialized, error) { var vm *vm.VMNetworkSerialized = &vm.VMNetworkSerialized{} if err := json.Unmarshal(resp.Body(), vm); err != nil { return vm, err diff --git a/src/client/cmdVM/vmCreate.go b/src/client/cmdVM/vmCreate.go index a034f94..55c29ba 100644 --- a/src/client/cmdVM/vmCreate.go +++ b/src/client/cmdVM/vmCreate.go @@ -138,7 +138,7 @@ func (cmd *Create)Run(args []string) int { statusCode = 1 } else { if resp.IsSuccess() { - vm, err := getVM(resp) + vm, err := deserializeVM(resp) if err != nil { u.PrintlnErr("Failed retrieving server's response: "+err.Error()) statusCode = 1 diff --git a/src/client/cmdVM/vmListSingle.go b/src/client/cmdVM/vmListSingle.go index aa032c1..2c970eb 100644 --- a/src/client/cmdVM/vmListSingle.go +++ b/src/client/cmdVM/vmListSingle.go @@ -45,7 +45,7 @@ func (cmd *ListSingle)Run(args []string) int { return 1 } else { if resp.IsSuccess() { - vm, err := getVM(resp) + vm, err := deserializeVM(resp) if err != nil { u.PrintlnErr("Failed retrieving server's response: "+err.Error()) return 1 diff --git a/src/client/nexush/nexush.go b/src/client/nexush/nexush.go index 5787b35..a6bb391 100644 --- a/src/client/nexush/nexush.go +++ b/src/client/nexush/nexush.go @@ -49,6 +49,7 @@ var cmdList = []cmd.Command { &cmdTemplate.Edit{"tpledit"}, &cmdTemplate.ExportDisk{"tplexportdisk"}, &cmdTemplate.List{"tpllist"}, + // &cmdTemplate.ListSingle{"tpllistsingle"}, // for testing the route only &cmdMisc.HelpHeader{"!═════╡ VM commands ╞═════════════════════════════════════════════════════════════════"}, &cmdVM.AddAccess{"vmaddaccess"}, @@ -63,7 +64,7 @@ var cmdList = []cmd.Command { &cmdVM.ImportDir{"vmimportdir"}, &cmdVM.Stop{"vmkill"}, &cmdVM.List{"vmlist"}, - &cmdVM.ListSingle{"vmlistsingle"}, + // &cmdVM.ListSingle{"vmlistsingle"}, // for testing the route only &cmdVM.Reboot{"vmreboot"}, &cmdVM.Shutdown{"vmshutdown"}, &cmdVM.Start{"vmstart"}, diff --git a/src/server/router/router.go b/src/server/router/router.go index e35b910..103baca 100644 --- a/src/server/router/router.go +++ b/src/server/router/router.go @@ -99,6 +99,7 @@ func (router *Router)Start(port int) { // Template management. templatesGroup := router.echo.Group("/templates") templatesGroup.Use(middleware.JWTWithConfig(auth.GetTokenAccess())) + templatesGroup.GET("/:id", router.tpl.GetTemplate) templatesGroup.GET("", router.tpl.GetTemplates) templatesGroup.POST("/vm", router.tpl.CreateTemplateFromVM) templatesGroup.POST("/qcow", router.tpl.CreateTemplateFromQCOW) diff --git a/src/server/router/routerTemplates.go b/src/server/router/routerTemplates.go index 6c7f51f..ebeacd8 100644 --- a/src/server/router/routerTemplates.go +++ b/src/server/router/routerTemplates.go @@ -52,6 +52,41 @@ func (r *RouterTemplates)GetTemplates(c echo.Context) error { } } +// Returns a template that can be listed based on its UUID. +// Requires either capability: +// CAP_TPL_LIST_ANY: any templates can be listed. +// CAP_TPL_LIST: only templates owned by the user as well as public templates can be listed. +// curl --cacert ca.pem -X GET https://localhost:1077/templates/62ae8791-c108-4235-a7d6-074e9b6a9017 -H "Authorization: Bearer <AccessToken>" +func (r *RouterTemplates)GetTemplate(c echo.Context) error { + // Retrieves logged user from context. + user, err := getLoggedUser(r.users, c) + if err != nil { + return echo.NewHTTPError(http.StatusUnauthorized, err.Error()) + } + + // Retrieves the template based on its UUID. + id, err := uuid.Parse(c.Param("id")) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, err.Error()) + } + tpl, err := r.tpl.GetTemplate(id) + if err != nil { + return echo.NewHTTPError(http.StatusNotFound, err.Error()) + } + + // If user has CAP_TPL_LIST_ANY, returns the template. + if user.HasCapability(caps.CAP_TPL_LIST_ANY) { + return c.JSONPretty(http.StatusOK, tpl.SerializeToNetwork(), " ") + } else if user.HasCapability(caps.CAP_TPL_LIST) { + // Returns template if owned by the user or template is public. + if tpl.GetOwner() == user.Email || tpl.IsPublic() { + return c.JSONPretty(http.StatusOK, tpl.SerializeToNetwork(), " ") + } + } + + return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps) +} + // Creates a template from a VM. // Requires CAP_TPL_CREATE. // curl --cacert ca.pem -X POST https://localhost:1077/templates/vm -H 'Content-Type: application/json' -d '{"vmID":"e41f3556-ca24-4658-bd79-8c85bd6bff59","name":"Xubuntu_22.04","access":"public"}' -H "Authorization: Bearer <AccessToken>" diff --git a/src/server/router/routerVMs.go b/src/server/router/routerVMs.go index d687db8..2d28876 100644 --- a/src/server/router/routerVMs.go +++ b/src/server/router/routerVMs.go @@ -3,7 +3,6 @@ package router import ( "io" "os" - // "errors" "net/http" "path/filepath" "nexus-common/caps" @@ -44,8 +43,14 @@ func (r *RouterVMs)GetListableVMs(c echo.Context) error { // Requires to be the VM's owner, or either capability: // User cap: CAP_VM_LIST_ANY: returns the VM. // VM access cap: CAP_VM_LIST: returns the VM with this cap for the logged user. -// curl --cacert ca.pem -X GET https://localhost:1077/vm/62ae8791-c108-4235-a7d6-074e9b6a9017 -H "Authorization: Bearer <AccessToken>" +// curl --cacert ca.pem -X GET https://localhost:1077/vms/62ae8791-c108-4235-a7d6-074e9b6a9017 -H "Authorization: Bearer <AccessToken>" func (r *RouterVMs)GetListableVM(c echo.Context) error { + // Retrieves logged user from context. + user, err := getLoggedUser(r.users, c) + if err != nil { + return echo.NewHTTPError(http.StatusUnauthorized, err.Error()) + } + // Retrieves the VM based on its UUID. id, err := uuid.Parse(c.Param("id")) if err != nil { @@ -56,12 +61,6 @@ func (r *RouterVMs)GetListableVM(c echo.Context) error { return echo.NewHTTPError(http.StatusNotFound, err.Error()) } - // Retrieves logged user from context. - user, err := getLoggedUser(r.users, c) - if err != nil { - return echo.NewHTTPError(http.StatusUnauthorized, err.Error()) - } - // If user has CAP_VM_LIST_ANY capability, returns the VM. if user.HasCapability(caps.CAP_VM_LIST_ANY) { return c.JSONPretty(http.StatusOK, vm.SerializeToNetwork(), " ") -- GitLab