diff --git a/src/client/common.mk b/src/client/common.mk index ad58f61e9c01b3b07702c3e1b9a49b87f22d13f3..7f34e2ee3aa909433eec9f6ec57c4254f73e98eb 100644 --- a/src/client/common.mk +++ b/src/client/common.mk @@ -56,7 +56,7 @@ build: @echo "[Building for $(OS) $(ARCH)]" ;\ if [ $(OS) = "linux" ]; then \ GOARCH=$(ARCH) GOOS=$(OS) CGO_ENABLED=0 go $(build_flags) -o build/$(ARCH)/$(OS)/ ;\ - strip -s build/$(ARCH)/$(OS)/$(bin) ;\ + #strip -s build/$(ARCH)/$(OS)/$(bin) ;\ #upx build/$(ARCH)/$(OS)/$(bin) ;\ else \ echo "GOARCH=$(ARCH) GOOS=$(OS) go $(build_flags) -o build/$(ARCH)/$(OS)/" ;\ @@ -76,7 +76,7 @@ build_all: echo "[Building for $$os $$arch]" ;\ if [ $$os = "linux" ]; then \ GOARCH=$$arch GOOS=$$os CGO_ENABLED=0 go $(build_flags) -o build/$$arch/$$os/ ;\ - strip -s build/$$arch/$$os/$(bin) ;\ + #strip -s build/$$arch/$$os/$(bin) ;\ #upx build/$$arch/$$os/$(bin) ;\ else \ echo "GOARCH=$$arch GOOS=$$os go $(build_flags) -o build/$$arch/$$os/" ;\ diff --git a/src/common/caps/caps.go b/src/common/caps/caps.go index e59ca2f3bc33e6f9ae15678d20a9a9829409b8f5..ffc63580b7c791ceb1c4169a319095e13bb2dfc2 100644 --- a/src/common/caps/caps.go +++ b/src/common/caps/caps.go @@ -27,6 +27,7 @@ const ( CAP_VM_EDIT = "VM_EDIT" CAP_VM_EDIT_ANY = "VM_EDIT_ANY" CAP_VM_SET_ACCESS = "VM_SET_ACCESS" + CAP_VM_SET_ACCESS_ANY = "VM_SET_ACCESS_ANY" CAP_VM_READFS = "VM_READFS" CAP_VM_READFS_ANY = "VM_READFS_ANY" CAP_VM_WRITEFS = "VM_WRITEFS" @@ -58,6 +59,7 @@ var userCaps = Capabilities { CAP_VM_REBOOT_ANY: 1, CAP_VM_LIST_ANY: 1, CAP_VM_SET_ACCESS: 1, + CAP_VM_SET_ACCESS_ANY: 1, CAP_VM_READFS_ANY: 1, CAP_VM_WRITEFS_ANY: 1, diff --git a/src/server/router/router.go b/src/server/router/router.go index 348ae345d1e0716898edc51be381f0c84b1dba7a..5941b102b860c04a83f56b7362fab83360a1f7e3 100644 --- a/src/server/router/router.go +++ b/src/server/router/router.go @@ -78,13 +78,13 @@ func (router *Router)Start(port int) { vmsGroup.GET("/stop", router.vms.GetStoppableVMs) vmsGroup.GET("/reboot", router.vms.GetRebootableVMs) vmsGroup.GET("/edit", router.vms.GetEditableVMs) - vmsGroup.GET("/editaccess", router.vms.GetEditableVMAccessVMs) + vmsGroup.GET("/editaccess", router.vms.GetModifiableVMAccessVMs) vmsGroup.GET("/exportdir", router.vms.GetDirExportableVMs) vmsGroup.GET("/importfiles", router.vms.GetFilesImportableVMs) vmsGroup.POST("", router.vms.CreateVM) - vmsGroup.DELETE("/:id", router.vms.DeleteVMByID) - vmsGroup.PUT("/:id", router.vms.EditVMByID) + vmsGroup.DELETE("/:id", router.vms.DeleteVM) + vmsGroup.PUT("/:id", router.vms.EditVM) vmsGroup.PUT("/:id/start", router.vms.StartVM) vmsGroup.PUT("/:id/startwithcreds", router.vms.StartVMWithCreds) vmsGroup.PUT("/:id/stop", router.vms.KillVM) @@ -102,8 +102,8 @@ func (router *Router)Start(port int) { templatesGroup.POST("/vm", router.tpl.CreateTemplateFromVM) templatesGroup.POST("/qcow", router.tpl.CreateTemplateFromQCOW) templatesGroup.GET("/:id/disk", router.tpl.ExportDisk) - templatesGroup.DELETE("/:id", router.tpl.DeleteTemplateByID) - templatesGroup.PUT("/:id", router.tpl.EditTemplateByID) + templatesGroup.DELETE("/:id", router.tpl.DeleteTemplate) + templatesGroup.PUT("/:id", router.tpl.EditTemplate) // Starts server in a dedicated goroutine. go func() { diff --git a/src/server/router/routerTemplates.go b/src/server/router/routerTemplates.go index 2038ae72058721041d60f17f87b0c37e5dc5fb6d..af611957b884190d1c23d67f492c1ffe9bbcf3f4 100644 --- a/src/server/router/routerTemplates.go +++ b/src/server/router/routerTemplates.go @@ -186,7 +186,7 @@ func (r *RouterTemplates)CreateTemplateFromQCOW(c echo.Context) error { // CAP_TPL_DESTROY: only a template owned by the user can be deleted. // Remark: a template can only be deleted if no VM references it! // curl --cacert ca.pem -X DELETE https://localhost:1077/templates/4913a2bb-edfe-4dfe-af53-38197a44523b -H "Authorization: Bearer <AccessToken>" -func (r *RouterTemplates)DeleteTemplateByID(c echo.Context) error { +func (r *RouterTemplates)DeleteTemplate(c echo.Context) error { // Retrieves logged user from context. user, err := getLoggedUser(r.users, c) if err != nil { @@ -226,7 +226,7 @@ func (r *RouterTemplates)DeleteTemplateByID(c echo.Context) error { // CAP_TPL_EDIT_ANY: any template can be edited. // CAP_TPL_EDIT: only a template owned by the user can be edited. // curl --cacert ca.pem -X PUT https://localhost:1077/templates/4913a2bb-edfe-4dfe-af53-38197a44523b -H "Authorization: Bearer <AccessToken>" -func (r *RouterTemplates)EditTemplateByID(c echo.Context) error { +func (r *RouterTemplates)EditTemplate(c echo.Context) error { // Retrieves logged user from context. user, err := getLoggedUser(r.users, c) if err != nil { diff --git a/src/server/router/routerVMs.go b/src/server/router/routerVMs.go index 018c56c4a9b7a2290b9a8cb61ba9063d4bf0c5b2..3f3670a411f876d9ed9b81c2d4483b4d7132f2d1 100644 --- a/src/server/router/routerVMs.go +++ b/src/server/router/routerVMs.go @@ -3,6 +3,7 @@ package router import ( "io" "os" + // "errors" "net/http" "path/filepath" "nexus-common/caps" @@ -35,7 +36,7 @@ func NewRouterVMs() *RouterVMs { // VM access cap: CAP_VM_LIST: returns all VMs with this cap for the logged user. // curl --cacert ca.pem -X GET https://localhost:1077/vms -H "Authorization: Bearer <AccessToken>" func (r *RouterVMs)GetListableVMs(c echo.Context) error { - return r.performVMsList(c, caps.CAP_VM_LIST_ANY, caps.CAP_VM_LIST, func(vm *vms.VM) bool { + return r.getFilteredVMs(c, caps.CAP_VM_LIST_ANY, caps.CAP_VM_LIST, func(vm *vms.VM) bool { return true }) } @@ -46,7 +47,7 @@ func (r *RouterVMs)GetListableVMs(c echo.Context) error { // VM access cap: CAP_VM_LIST: returns all running VMs with this cap for the logged user. // curl --cacert ca.pem -X GET https://localhost:1077/vms/attach -H "Authorization: Bearer <AccessToken>" func (r *RouterVMs)GetAttachableVMs(c echo.Context) error { - return r.performVMsList(c, caps.CAP_VM_LIST_ANY, caps.CAP_VM_LIST, func(vm *vms.VM) bool { + return r.getFilteredVMs(c, caps.CAP_VM_LIST_ANY, caps.CAP_VM_LIST, func(vm *vms.VM) bool { return vm.IsRunning() }) } @@ -57,7 +58,7 @@ func (r *RouterVMs)GetAttachableVMs(c echo.Context) error { // VM access cap: CAP_VM_DESTROY: returns all VMs with this cap for the logged user. // curl --cacert ca.pem -X GET https://localhost:1077/vms/del -H "Authorization: Bearer <AccessToken>" func (r *RouterVMs)GetDeletableVMs(c echo.Context) error { - return r.performVMsList(c, caps.CAP_VM_DESTROY_ANY, caps.CAP_VM_DESTROY, func(vm *vms.VM) bool { + return r.getFilteredVMs(c, caps.CAP_VM_DESTROY_ANY, caps.CAP_VM_DESTROY, func(vm *vms.VM) bool { return !vm.IsRunning() }) } @@ -68,7 +69,7 @@ func (r *RouterVMs)GetDeletableVMs(c echo.Context) error { // VM access cap: CAP_VM_START: returns all VMs with this cap for the logged user. // curl --cacert ca.pem -X GET https://localhost:1077/vms/start -H "Authorization: Bearer <AccessToken>" func (r *RouterVMs)GetStartableVMs(c echo.Context) error { - return r.performVMsList(c, caps.CAP_VM_START_ANY, caps.CAP_VM_START, func(vm *vms.VM) bool { + return r.getFilteredVMs(c, caps.CAP_VM_START_ANY, caps.CAP_VM_START, func(vm *vms.VM) bool { return !vm.IsRunning() }) } @@ -79,7 +80,7 @@ func (r *RouterVMs)GetStartableVMs(c echo.Context) error { // VM access cap: CAP_VM_STOP: returns all VMs with this cap for the logged user. // curl --cacert ca.pem -X GET https://localhost:1077/vms/stop -H "Authorization: Bearer <AccessToken>" func (r *RouterVMs)GetStoppableVMs(c echo.Context) error { - return r.performVMsList(c, caps.CAP_VM_STOP_ANY, caps.CAP_VM_STOP, func(vm *vms.VM) bool { + return r.getFilteredVMs(c, caps.CAP_VM_STOP_ANY, caps.CAP_VM_STOP, func(vm *vms.VM) bool { return vm.IsRunning() }) } @@ -90,7 +91,7 @@ func (r *RouterVMs)GetStoppableVMs(c echo.Context) error { // VM access cap: CAP_VM_REBOOT: returns all VMs with this cap for the logged user. // curl --cacert ca.pem -X GET https://localhost:1077/vms/reboot -H "Authorization: Bearer <AccessToken>" func (r *RouterVMs)GetRebootableVMs(c echo.Context) error { - return r.performVMsList(c, caps.CAP_VM_REBOOT_ANY, caps.CAP_VM_REBOOT, func(vm *vms.VM) bool { + return r.getFilteredVMs(c, caps.CAP_VM_REBOOT_ANY, caps.CAP_VM_REBOOT, func(vm *vms.VM) bool { return vm.IsRunning() }) } @@ -101,36 +102,41 @@ func (r *RouterVMs)GetRebootableVMs(c echo.Context) error { // VM access cap: CAP_VM_EDIT: returns all VMs with this cap for the logged user. // curl --cacert ca.pem -X GET https://localhost:1077/vms/edit -H "Authorization: Bearer <AccessToken>" func (r *RouterVMs)GetEditableVMs(c echo.Context) error { - return r.performVMsList(c, caps.CAP_VM_EDIT_ANY, caps.CAP_VM_EDIT, func(vm *vms.VM) bool { + return r.getFilteredVMs(c, caps.CAP_VM_EDIT_ANY, caps.CAP_VM_EDIT, func(vm *vms.VM) bool { return !vm.IsRunning() }) } -// Returns VMs that can have their access set or deleted. -// Requires to be the VM's owner, or BOTH capabilities: +// Returns VMs that can have their access modified (set or deleted). +// REMARK: running VMs are filtered out, even if they can be modified! +// Requires to be the VM's owner, or to have VM_SET_ACCESS_ANY capability, or to have BOTH capabilities: // User cap: CAP_VM_SET_ACCESS // VM access cap: CAP_VM_SET_ACCESS // curl --cacert ca.pem -X GET https://localhost:1077/vms/editaccess -H "Authorization: Bearer <AccessToken>" -func (r *RouterVMs)GetEditableVMAccessVMs(c echo.Context) error { +func (r *RouterVMs)GetModifiableVMAccessVMs(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()) } - return c.JSONPretty(http.StatusOK, r.vms.GetNetworkSerializedVMs( - func(vm *vms.VM) bool { - // First, checks if the user is the VM's owner + // Checks whether the logged user is allowed to modify (add/remove) a VM's VM access. + isModifyVMAccessAllowed := func(user *users.User, vm *vms.VM) bool { + // If user has VM_SET_ACCESS_ANY, modify is allowed. + if user.HasCapability(caps.CAP_VM_SET_ACCESS_ANY) { + return true + } else { + // If user is the VM's owner, modify is allowed. if vm.IsOwner(user.Email) { - return !vm.IsRunning() + return true } else { - // Then, checks that user has CAP_VM_SET_ACCESS and also that - // VM access has CAP_VM_SET_ACCESS set for the user + // If user has VM_SET_ACCESS and VM's VM access is present for the same user, + // modify is allowed. if user.HasCapability(caps.CAP_VM_SET_ACCESS) { capabilities, exists := vm.GetAccess()[user.Email] if exists { - _, visible := capabilities[caps.CAP_VM_SET_ACCESS] - return visible && !vm.IsRunning() + _, found := capabilities[caps.CAP_VM_SET_ACCESS] + return found } else { return false } @@ -138,7 +144,14 @@ func (r *RouterVMs)GetEditableVMAccessVMs(c echo.Context) error { return false } } - }), " ") + } + } + + filterFunc := func(vm *vms.VM) bool { + return isModifyVMAccessAllowed(user, vm) && !vm.IsRunning() + } + + return c.JSONPretty(http.StatusOK, r.vms.GetNetworkSerializedVMs(filterFunc), " ") } // Returns VMs that can have a directory exported. @@ -147,7 +160,7 @@ func (r *RouterVMs)GetEditableVMAccessVMs(c echo.Context) error { // VM access cap: VM_READFS: returns all VMs with this cap for the logged user. // curl --cacert ca.pem -X GET https://localhost:1077/vms/exportdir -H "Authorization: Bearer <AccessToken>" func (r *RouterVMs)GetDirExportableVMs(c echo.Context) error { - return r.performVMsList(c, caps.CAP_VM_READFS_ANY, caps.CAP_VM_READFS, func(vm *vms.VM) bool { + return r.getFilteredVMs(c, caps.CAP_VM_READFS_ANY, caps.CAP_VM_READFS, func(vm *vms.VM) bool { return true }) } @@ -158,7 +171,7 @@ func (r *RouterVMs)GetDirExportableVMs(c echo.Context) error { // VM access cap: VM_WRITEFS: returns all VMs with this cap for the logged user. // curl --cacert ca.pem -X GET https://localhost:1077/vms/importfiles -H "Authorization: Bearer <AccessToken>" func (r *RouterVMs)GetFilesImportableVMs(c echo.Context) error { - return r.performVMsList(c, caps.CAP_VM_WRITEFS_ANY, caps.CAP_VM_WRITEFS, func(vm *vms.VM) bool { + return r.getFilteredVMs(c, caps.CAP_VM_WRITEFS_ANY, caps.CAP_VM_WRITEFS, func(vm *vms.VM) bool { return true }) } @@ -202,8 +215,8 @@ func (r *RouterVMs)CreateVM(c echo.Context) error { // User cap: CAP_VM_DESTROY_ANY: any VM can be deleted. // VM access cap: CAP_VM_DESTROY: any of the VMs with this cap for the logged user can be deleted. // curl --cacert ca.pem -X DELETE https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59 -H "Authorization: Bearer <AccessToken>" -func (r *RouterVMs)DeleteVMByID(c echo.Context) error { - return r.performVMAction(c, caps.CAP_VM_DESTROY_ANY, caps.CAP_VM_DESTROY, func(c echo.Context, vm *vms.VM) error { +func (r *RouterVMs)DeleteVM(c echo.Context) error { + return r.applyOnFilteredVMs(c, caps.CAP_VM_DESTROY_ANY, caps.CAP_VM_DESTROY, func(c echo.Context, vm *vms.VM) error { if err := r.vms.DeleteVM(vm.GetID()); err != nil { return echo.NewHTTPError(http.StatusNotFound, err.Error()) } @@ -217,7 +230,7 @@ func (r *RouterVMs)DeleteVMByID(c echo.Context) error { // VM access cap: CAP_VM_START: any of the VMs with this cap for the logged user can be started. // curl --cacert ca.pem -X PUT https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59/start -H "Authorization: Bearer <AccessToken>" func (r *RouterVMs)StartVM(c echo.Context) error { - return r.performVMAction(c, caps.CAP_VM_START_ANY, caps.CAP_VM_START, func(c echo.Context, vm *vms.VM) error { + return r.applyOnFilteredVMs(c, caps.CAP_VM_START_ANY, caps.CAP_VM_START, func(c echo.Context, vm *vms.VM) error { _, _, err := r.vms.StartVM(vm.GetID()) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) @@ -232,7 +245,7 @@ func (r *RouterVMs)StartVM(c echo.Context) error { // VM access cap: CAP_VM_START: any of the VMs with this cap for the logged user can be started. // curl --cacert ca.pem -X PUT https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59/startwithcreds -H "Authorization: Bearer <AccessToken>" func (r *RouterVMs)StartVMWithCreds(c echo.Context) error { - return r.performVMAction(c, caps.CAP_VM_START_ANY, caps.CAP_VM_START, func(c echo.Context, vm *vms.VM) error { + return r.applyOnFilteredVMs(c, caps.CAP_VM_START_ANY, caps.CAP_VM_START, func(c echo.Context, vm *vms.VM) error { // Deserializes and validates client's parameters. p := new(params.VMStartWithCreds) @@ -253,7 +266,7 @@ func (r *RouterVMs)StartVMWithCreds(c echo.Context) error { // VM access cap: CAP_VM_STOP: any of the VMs with this cap for the logged user can be killed. // curl --cacert ca.pem -X PUT https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59/stop -H "Authorization: Bearer <AccessToken>" func (r *RouterVMs)KillVM(c echo.Context) error { - return r.performVMAction(c, caps.CAP_VM_STOP_ANY, caps.CAP_VM_STOP, func(c echo.Context, vm *vms.VM) error { + return r.applyOnFilteredVMs(c, caps.CAP_VM_STOP_ANY, caps.CAP_VM_STOP, func(c echo.Context, vm *vms.VM) error { if err := r.vms.KillVM(vm.GetID()); err != nil { return echo.NewHTTPError(http.StatusNotFound, err.Error()) } @@ -267,7 +280,7 @@ func (r *RouterVMs)KillVM(c echo.Context) error { // VM access cap: CAP_VM_STOP: any of the VMs with this cap for the logged user can be shutdown. // curl --cacert ca.pem -X PUT https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59/shutdown -H "Authorization: Bearer <AccessToken>" func (r *RouterVMs)ShutdownVM(c echo.Context) error { - return r.performVMAction(c, caps.CAP_VM_STOP_ANY, caps.CAP_VM_STOP, func(c echo.Context, vm *vms.VM) error { + return r.applyOnFilteredVMs(c, caps.CAP_VM_STOP_ANY, caps.CAP_VM_STOP, func(c echo.Context, vm *vms.VM) error { if err := r.vms.ShutdownVM(vm.GetID()); err != nil { return echo.NewHTTPError(http.StatusNotFound, err.Error()) } @@ -281,7 +294,7 @@ func (r *RouterVMs)ShutdownVM(c echo.Context) error { // VM access cap: CAP_VM_REBOOT: any of the VMs with this cap for the logged user can be rebooted. // curl --cacert ca.pem -X PUT https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59/stop -H "Authorization: Bearer <AccessToken>" func (r *RouterVMs)RebootVM(c echo.Context) error { - return r.performVMAction(c, caps.CAP_VM_REBOOT_ANY, caps.CAP_VM_REBOOT, func(c echo.Context, vm *vms.VM) error { + return r.applyOnFilteredVMs(c, caps.CAP_VM_REBOOT_ANY, caps.CAP_VM_REBOOT, func(c echo.Context, vm *vms.VM) error { if err := r.vms.RebootVM(vm.GetID()); err != nil { return echo.NewHTTPError(http.StatusNotFound, err.Error()) } @@ -294,8 +307,8 @@ func (r *RouterVMs)RebootVM(c echo.Context) error { // User cap: CAP_VM_EDIT_ANY: any VM can be edited. // VM access cap: CAP_VM_EDIT: any of the VMs with this cap for the logged user can be edited. // curl --cacert ca.pem -X PUT https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59 -H 'Content-Type: application/json' -d '{"name":"Edited VM","cpus":1,"ram":2048,"nic":"user","usbDevs":["1307:0165","1234:abcd"]}' -H "Authorization: Bearer <AccessToken>" -func (r *RouterVMs)EditVMByID(c echo.Context) error { - return r.performVMAction(c, caps.CAP_VM_EDIT_ANY, caps.CAP_VM_EDIT, func(c echo.Context, vm *vms.VM) error { +func (r *RouterVMs)EditVM(c echo.Context) error { + return r.applyOnFilteredVMs(c, caps.CAP_VM_EDIT_ANY, caps.CAP_VM_EDIT, func(c echo.Context, vm *vms.VM) error { // Deserializes and validates client's parameters. // Given these parameters are optional, we can't use a validator on them. // Validation is performed in vm.EditVM() instead. @@ -311,7 +324,7 @@ func (r *RouterVMs)EditVMByID(c echo.Context) error { } // Set a VM access for a given user. -// Requires to be the VM's owner, or BOTH capabilities: +// Requires to be the VM's owner, or to have VM_SET_ACCESS_ANY capability, or to have BOTH capabilities: // User cap: CAP_VM_SET_ACCESS // VM access cap: CAP_VM_SET_ACCESS // curl --cacert ca.pem -X PUT https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59/caps/janedoes@nexus.org -H 'Content-Type: application/json' -d '{"caps":{"VM_LIST":1,"VM_START":1,"VM_STOP":1}}' -H "Authorization: Bearer <AccessToken>" @@ -335,20 +348,6 @@ func (r *RouterVMs)SetVMAccessForUser(c echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } - // Retrieves the VM to modify. - vm, err := r.vms.GetVM(vmID) - if err != nil { - return echo.NewHTTPError(http.StatusNotFound, err.Error()) - } - - // First, check that the logged user is the VM's owner. - if !vm.IsOwner(user.Email) { - // Next, checks the logged user has the VM_SET_ACCESS capability. - if !user.HasCapability(caps.CAP_VM_SET_ACCESS) { - return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps) - } - } - // Deserializes and validates client's parameters. p := new(params.VMAddAccess) if err := decodeJson(c, &p); err != nil { @@ -358,7 +357,7 @@ func (r *RouterVMs)SetVMAccessForUser(c echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } - if err = r.vms.SetVMAccess(vmID, user.Email, email, p.Access); err != nil { + if err = r.vms.SetVMAccess(vmID, user, email, p.Access); err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } @@ -383,25 +382,11 @@ func (r *RouterVMs)DeleteVMAccessForUser(c echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } - // Retrieves the VM to modify. - vm, err := r.vms.GetVM(vmID) - if err != nil { - return echo.NewHTTPError(http.StatusNotFound, err.Error()) - } - - // Does not check that the user to remove the VM access for actually exists. - // Indeed, it might have been deleted. + // Purposedly does not check that the user to remove the VM access for actually + // exists as it might have been deleted. email := c.Param("email") - // First, check that the logged user is the VM's owner. - if !vm.IsOwner(user.Email) { - // Next, checks the logged user has the VM_SET_ACCESS capability. - if !user.HasCapability(caps.CAP_VM_SET_ACCESS) { - return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps) - } - } - - if err = r.vms.DeleteVMAccess(vmID, user.Email, email); err != nil { + if err = r.vms.DeleteVMAccess(vmID, user, email); err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } @@ -414,7 +399,7 @@ func (r *RouterVMs)DeleteVMAccessForUser(c echo.Context) error { // VM access cap: VM_READFS: any of the VMs with this cap for the logged user can have their filesystem read. // curl --cacert ca.pem -X GET https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59/exportdir -H 'Content-Type: application/json' -d '{"dir":"absolute_path_to_dir"}' -H "Authorization: Bearer <AccessToken>" --output dir.tar func (r *RouterVMs)ExportVMDir(c echo.Context) error { - return r.performVMAction(c, caps.CAP_VM_READFS_ANY, caps.CAP_VM_READFS, func(c echo.Context, vm *vms.VM) error { + return r.applyOnFilteredVMs(c, caps.CAP_VM_READFS_ANY, caps.CAP_VM_READFS, func(c echo.Context, vm *vms.VM) error { // Deserializes and validates the client's parameter. p := new(params.VMExportDir) if err := decodeJson(c, &p); err != nil { @@ -446,7 +431,7 @@ func (r *RouterVMs)ExportVMDir(c echo.Context) error { // VM access cap: VM_WRITEFS: any of the VMs with this cap for the logged user can import the file tree. // curl --cacert ca.pem -X POST https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59/importfiles -H 'Content-Type: multipart/form-data' -F dir="/home/nexus" -F file=@files.tar -H "Authorization: Bearer <AccessToken>" func (r *RouterVMs)ImportFilesToVM(c echo.Context) error { - return r.performVMAction(c, caps.CAP_VM_WRITEFS_ANY, caps.CAP_VM_WRITEFS, func(c echo.Context, vm *vms.VM) error { + return r.applyOnFilteredVMs(c, caps.CAP_VM_WRITEFS_ANY, caps.CAP_VM_WRITEFS, func(c echo.Context, vm *vms.VM) error { // Retrieves the various client arguments. vmDir := c.FormValue("vmDir") @@ -490,48 +475,47 @@ func (r *RouterVMs)ImportFilesToVM(c echo.Context) error { }) } -// Helper function that returns a list of serialized VMs that match either: +// Helper function that returns a list of serialized VMs if either condition is true: // - the logged user has the userCapabilityAny capability. // - the logged user is the VM's owner. // - the VM access for the logged user matches the vmAccessCapability capability. -// Also, VMs for which cond is false are filtered out. -func (r *RouterVMs)performVMsList(c echo.Context, userCapabilityAny, vmAccessCapability string, cond vms.VMKeeperFn) error { +// Additionally, VMs for which the cond function returns false are filtered out. +func (r *RouterVMs)getFilteredVMs(c echo.Context, userCapabilityAny, vmAccessCapability string, cond vms.VMKeeperFn) error { // Retrieves logged user from context. user, err := getLoggedUser(r.users, c) if err != nil { return echo.NewHTTPError(http.StatusUnauthorized, err.Error()) } - // If the logged user has the XX_ANY capability (userCapabilityAny), returns all VMs. + // If user has the XX_ANY capability, returns all VMs. if user.HasCapability(userCapabilityAny) { // Returns all VMs that pass the condition. return c.JSONPretty(http.StatusOK, r.vms.GetNetworkSerializedVMs(cond), " ") } else { // Returns all VMs: - // - owned by the logged user - // - for which the logged user has the specified capability (vmAccessCapability) in the VM's access - return c.JSONPretty(http.StatusOK, r.vms.GetNetworkSerializedVMs( - func(vm *vms.VM) bool { - if vm.IsOwner(user.Email) { - return cond(vm) + // - owned by the user + // - for which the user has the specified capability (vmAccessCapability) in the VM's access + return c.JSONPretty(http.StatusOK, r.vms.GetNetworkSerializedVMs(func(vm *vms.VM) bool { + if vm.IsOwner(user.Email) { + return cond(vm) + } else { + capabilities, exists := vm.GetAccess()[user.Email] + if exists { + _, found := capabilities[vmAccessCapability] + return found && cond(vm) } else { - capabilities, exists := vm.GetAccess()[user.Email] - if exists { - _, visible := capabilities[vmAccessCapability] - return visible && cond(vm) - } else { - return false - } + return false } - }), " ") + } + }), " ") } } -// Helper function that performs an action on a VM based either on: +// Helper function that executes an action on a VM if either condition is true: // - the logged user is the VM's owner (in this case, user has all VM access capabilities). // - the logged user has the userCapabilityAny capability. // - the VM access for the logged user matches the vmAccessCapability capability. -func (r *RouterVMs)performVMAction(c echo.Context, userCapabilityAny, vmAccessCapability string, action vmActionFn) error { +func (r *RouterVMs)applyOnFilteredVMs(c echo.Context, userCapabilityAny, vmAccessCapability string, action vmActionFn) error { // Retrieves the VM on which to perform the action. id, err := uuid.Parse(c.Param("id")) if err != nil { @@ -548,24 +532,23 @@ func (r *RouterVMs)performVMAction(c echo.Context, userCapabilityAny, vmAccessCa return echo.NewHTTPError(http.StatusUnauthorized, err.Error()) } - // First, checks if the user is the VM's owner + // If user is the VM's owner, executes the action. if vm.IsOwner(user.Email) { return action(c, vm) + // If user has the XX_ANY capability, executes the action. } else if user.HasCapability(userCapabilityAny) { - // Next, checks if the user has the XX_ANY capability return action(c, vm) + // Finally, if VM access for the logged user matches the required capability, executes the action. } else { - // Finally, check if the VM access for the logged user matches the required capability userCaps, exists := vm.GetAccess()[user.Email] if !exists { return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps) } - _, hasAccess := userCaps[vmAccessCapability] if !hasAccess { return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps) } - return action(c, vm) } } + diff --git a/src/server/version/version.go b/src/server/version/version.go index c8835ed0c38f478d2f365745e15c79fdfca06eda..befa71c8753c57cbf22cdac701b130c2f0d0b56f 100644 --- a/src/server/version/version.go +++ b/src/server/version/version.go @@ -7,7 +7,7 @@ import ( const ( major = 1 minor = 8 - bugfix = 2 + bugfix = 3 ) type Version struct { diff --git a/src/server/vms/vms.go b/src/server/vms/vms.go index 9b80bdc5cad7434d710d08c9751a3f4535b56074..cd5ef711b89d925b3c14c408bd4b3239caf8e1eb 100644 --- a/src/server/vms/vms.go +++ b/src/server/vms/vms.go @@ -12,6 +12,7 @@ import ( "nexus-server/exec" "nexus-server/paths" "nexus-server/utils" + "nexus-server/users" "nexus-server/logger" c "nexus-server/consts" "github.com/google/uuid" @@ -404,9 +405,9 @@ func (vms *VMs)EditVM(vmID uuid.UUID, name string, cpus, ram int, nic vm.NicType } // Set a VM's Access for a given user (email). -// loggedUserEmail is the email of the currently logged user -// userMail is the email of the user for which to modify the access -func (vms *VMs)SetVMAccess(vmID uuid.UUID, loggedUserEmail, userEmail string, newAccess caps.Capabilities) error { +// user is the currently logged user +// destUserEmail is the email of the user for which to modify the access +func (vms *VMs)SetVMAccess(vmID uuid.UUID, user *users.User, destUserEmail string, newAccess caps.Capabilities) error { if err := caps.ValidateVMAccessCaps(newAccess); err != nil { return err } @@ -427,17 +428,20 @@ func (vms *VMs)SetVMAccess(vmID uuid.UUID, loggedUserEmail, userEmail string, ne return errors.New("VM must be stopped") } - // First, check that the logged user is the VM's owner. - if !vm.IsOwner(loggedUserEmail) { - // Next, checks the logged user has VM_SET_ACCESS set in her/his VM access. - userCaps := vm.v.Access[loggedUserEmail] - _, exists := userCaps[caps.CAP_VM_SET_ACCESS] - if !exists { - return errors.New("Insufficient capability") + // If user has VM_SET_ACCESS_ANY, modify is allowed. + if !user.HasCapability(caps.CAP_VM_SET_ACCESS_ANY) { + // If user is the VM's owner, modify is allowed. + if !vm.IsOwner(user.Email) { + // If user has VM_SET_ACCESS and VM's VM access is present for the same user, modify is allowed. + userCaps := vm.v.Access[user.Email] + _, exists := userCaps[caps.CAP_VM_SET_ACCESS] + if !exists { + return errors.New("Insufficient capability") + } } } - vm.v.Access[userEmail] = newAccess + vm.v.Access[destUserEmail] = newAccess if err = vm.writeConfig(); err != nil { return err @@ -447,9 +451,9 @@ func (vms *VMs)SetVMAccess(vmID uuid.UUID, loggedUserEmail, userEmail string, ne } // Remove a VM's Access for a given user (email). -// loggedUserEmail is the email of the currently logged user -// userMail is the email of the user for which to remove the access -func (vms *VMs)DeleteVMAccess(vmID uuid.UUID, loggedUserEmail, userEmail string) error { +// user is the currently logged user +// destUserEmail is the email of the user for which to modify the access +func (vms *VMs)DeleteVMAccess(vmID uuid.UUID, user *users.User, destUserEmail string) error { vms.rwlock.Lock() defer vms.rwlock.Unlock() @@ -466,21 +470,24 @@ func (vms *VMs)DeleteVMAccess(vmID uuid.UUID, loggedUserEmail, userEmail string) return errors.New("VM must be stopped") } - // First, check that the logged user is the VM's owner. - if !vm.IsOwner(loggedUserEmail) { - // Next, checks the logged user has VM_SET_ACCESS set in her/his VM access. - userCaps := vm.v.Access[loggedUserEmail] - _, exists := userCaps[caps.CAP_VM_SET_ACCESS] - if !exists { - return errors.New("Insufficient capability") + // If user has VM_SET_ACCESS_ANY, modify is allowed. + if !user.HasCapability(caps.CAP_VM_SET_ACCESS_ANY) { + // If user is the VM's owner, modify is allowed. + if !vm.IsOwner(user.Email) { + // If user has VM_SET_ACCESS and VM's VM access is present for the same user, modify is allowed. + userCaps := vm.v.Access[user.Email] + _, exists := userCaps[caps.CAP_VM_SET_ACCESS] + if !exists { + return errors.New("Insufficient capability") + } } } // Only removes the user from the Access map if it actually had an access. - if _, exists := vm.v.Access[userEmail]; exists { - delete(vm.v.Access, userEmail) + if _, exists := vm.v.Access[destUserEmail]; exists { + delete(vm.v.Access, destUserEmail) } else { - return errors.New("User "+userEmail+" has no VM access") + return errors.New("User "+destUserEmail+" has no VM access") } if err = vm.writeConfig(); err != nil {