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

Added support for new user capability VM_SET_ACCESS_ANY, which allows such a...

Added support for new user capability VM_SET_ACCESS_ANY, which allows such a user to modify any VM's VM access.
Minor fix to clients' Makefile
Bumped server version to 1.8.3
parent 8759388f
Branches
No related tags found
No related merge requests found
......@@ -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/" ;\
......
......@@ -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,
......
......@@ -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() {
......
......@@ -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 {
......
......@@ -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,35 +475,34 @@ 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 {
// - 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 {
_, visible := capabilities[vmAccessCapability]
return visible && cond(vm)
_, found := capabilities[vmAccessCapability]
return found && cond(vm)
} else {
return false
}
......@@ -527,11 +511,11 @@ func (r *RouterVMs)performVMsList(c echo.Context, userCapabilityAny, vmAccessCap
}
}
// 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)
}
}
......@@ -7,7 +7,7 @@ import (
const (
major = 1
minor = 8
bugfix = 2
bugfix = 3
)
type Version struct {
......
......@@ -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]
// 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]
// 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 {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment