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

Added owner property in VM data structure (and on disk).

Better serialization of VMs (disk and over the network) with cleaner/more maintenable code.
This change implied quite a few changes in the permission/capabilities related code.
Bumped to version 1.5.0
parent 0e0d9fb9
No related branches found
No related tags found
No related merge requests found
...@@ -83,6 +83,9 @@ func (r *RouterTemplates)CreateTemplateFromVM(c echo.Context) error { ...@@ -83,6 +83,9 @@ func (r *RouterTemplates)CreateTemplateFromVM(c echo.Context) error {
return echo.NewHTTPError(http.StatusNotFound, err.Error()) return echo.NewHTTPError(http.StatusNotFound, err.Error())
} }
// If the user isn't the VM's owner, checks that the required capabilities are met:
// either VM_LIST_ANY or VM_LIST in the VM access
if !vm.IsOwner(user.Email) {
// Check the user has the required capabilities: either VM_LIST_ANY or VM_LIST in the VM access // Check the user has the required capabilities: either VM_LIST_ANY or VM_LIST in the VM access
if !user.HasCapability(caps.CAP_VM_LIST_ANY) { if !user.HasCapability(caps.CAP_VM_LIST_ANY) {
userCaps, exists := vm.Access[user.Email] userCaps, exists := vm.Access[user.Email]
...@@ -95,6 +98,7 @@ func (r *RouterTemplates)CreateTemplateFromVM(c echo.Context) error { ...@@ -95,6 +98,7 @@ func (r *RouterTemplates)CreateTemplateFromVM(c echo.Context) error {
return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps) return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps)
} }
} }
}
// Creates a new template from the client's parameters. // Creates a new template from the client's parameters.
template, err := vms.NewTemplateFromVM(p.Name, user.Email, p.Access, &vm) template, err := vms.NewTemplateFromVM(p.Name, user.Email, p.Access, &vm)
......
...@@ -29,7 +29,7 @@ func NewRouterVMs() *RouterVMs { ...@@ -29,7 +29,7 @@ func NewRouterVMs() *RouterVMs {
} }
// Returns VMs that can be listed. // Returns VMs that can be listed.
// Requires either capability: // Requires to be the VM's owner, or either capability:
// User cap: CAP_VM_LIST_ANY: returns all VMs. // User cap: CAP_VM_LIST_ANY: returns all VMs.
// VM access cap: CAP_VM_LIST: returns all VMs with this cap for the logged user. // 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>" // curl --cacert ca.pem -X GET https://localhost:1077/vms -H "Authorization: Bearer <AccessToken>"
...@@ -40,7 +40,7 @@ func (r *RouterVMs)GetListableVMs(c echo.Context) error { ...@@ -40,7 +40,7 @@ func (r *RouterVMs)GetListableVMs(c echo.Context) error {
} }
// Returns VMs that are running and that can be attached to. // Returns VMs that are running and that can be attached to.
// Requires either capability: // Requires to be the VM's owner, or either capability:
// User cap: CAP_VM_LIST_ANY: returns all running VMs. // User cap: CAP_VM_LIST_ANY: returns all running VMs.
// VM access cap: CAP_VM_LIST: returns all running VMs with this cap for the logged user. // 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>" // curl --cacert ca.pem -X GET https://localhost:1077/vms/attach -H "Authorization: Bearer <AccessToken>"
...@@ -51,7 +51,7 @@ func (r *RouterVMs)GetAttachableVMs(c echo.Context) error { ...@@ -51,7 +51,7 @@ func (r *RouterVMs)GetAttachableVMs(c echo.Context) error {
} }
// Returns VMs that are stopped and that can be deleted. // Returns VMs that are stopped and that can be deleted.
// Requires either capability: // Requires to be the VM's owner, or either capability:
// User cap: CAP_VM_DESTROY_ANY: returns all VMs. // User cap: CAP_VM_DESTROY_ANY: returns all VMs.
// VM access cap: CAP_VM_DESTROY: returns all VMs with this cap for the logged user. // 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>" // curl --cacert ca.pem -X GET https://localhost:1077/vms/del -H "Authorization: Bearer <AccessToken>"
...@@ -62,7 +62,7 @@ func (r *RouterVMs)GetDeletableVMs(c echo.Context) error { ...@@ -62,7 +62,7 @@ func (r *RouterVMs)GetDeletableVMs(c echo.Context) error {
} }
// Returns VMs that are stopped and that can be started. // Returns VMs that are stopped and that can be started.
// Requires either capability: // Requires to be the VM's owner, or either capability:
// User cap: CAP_VM_START_ANY: returns all VMs. // User cap: CAP_VM_START_ANY: returns all VMs.
// VM access cap: CAP_VM_START: returns all VMs with this cap for the logged user. // 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>" // curl --cacert ca.pem -X GET https://localhost:1077/vms/start -H "Authorization: Bearer <AccessToken>"
...@@ -73,7 +73,7 @@ func (r *RouterVMs)GetStartableVMs(c echo.Context) error { ...@@ -73,7 +73,7 @@ func (r *RouterVMs)GetStartableVMs(c echo.Context) error {
} }
// Returns VMs that are running and that can be stopped. // Returns VMs that are running and that can be stopped.
// Requires either capability: // Requires to be the VM's owner, or either capability:
// User cap: CAP_VM_STOP_ANY: returns all VMs. // User cap: CAP_VM_STOP_ANY: returns all VMs.
// VM access cap: CAP_VM_STOP: returns all VMs with this cap for the logged user. // 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>" // curl --cacert ca.pem -X GET https://localhost:1077/vms/stop -H "Authorization: Bearer <AccessToken>"
...@@ -84,7 +84,7 @@ func (r *RouterVMs)GetStoppableVMs(c echo.Context) error { ...@@ -84,7 +84,7 @@ func (r *RouterVMs)GetStoppableVMs(c echo.Context) error {
} }
// Returns VMs that are running and that can be rebooted. // Returns VMs that are running and that can be rebooted.
// Requires either capability: // Requires to be the VM's owner, or either capability:
// User cap: CAP_VM_REBOOT_ANY: returns all VMs. // User cap: CAP_VM_REBOOT_ANY: returns all VMs.
// VM access cap: CAP_VM_REBOOT: returns all VMs with this cap for the logged user. // 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>" // curl --cacert ca.pem -X GET https://localhost:1077/vms/reboot -H "Authorization: Bearer <AccessToken>"
...@@ -95,7 +95,7 @@ func (r *RouterVMs)GetRebootableVMs(c echo.Context) error { ...@@ -95,7 +95,7 @@ func (r *RouterVMs)GetRebootableVMs(c echo.Context) error {
} }
// Returns VMs that can be edited. // Returns VMs that can be edited.
// Requires either capability: // Requires to be the VM's owner, or either capability:
// User cap: CAP_VM_EDIT_ANY: returns all VMs. // User cap: CAP_VM_EDIT_ANY: returns all VMs.
// VM access cap: CAP_VM_EDIT: returns all VMs with this cap for the logged user. // 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>" // curl --cacert ca.pem -X GET https://localhost:1077/vms/edit -H "Authorization: Bearer <AccessToken>"
...@@ -106,7 +106,7 @@ func (r *RouterVMs)GetEditableVMs(c echo.Context) error { ...@@ -106,7 +106,7 @@ func (r *RouterVMs)GetEditableVMs(c echo.Context) error {
} }
// Returns VMs that can have their access set or deleted. // Returns VMs that can have their access set or deleted.
// Requires BOTH capabilities: // Requires to be the VM's owner, or BOTH capabilities:
// User cap: CAP_VM_SET_ACCESS // User cap: CAP_VM_SET_ACCESS
// VM access 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>" // curl --cacert ca.pem -X GET https://localhost:1077/vms/editaccess -H "Authorization: Bearer <AccessToken>"
...@@ -117,10 +117,15 @@ func (r *RouterVMs)GetEditableVMAccessVMs(c echo.Context) error { ...@@ -117,10 +117,15 @@ func (r *RouterVMs)GetEditableVMAccessVMs(c echo.Context) error {
return echo.NewHTTPError(http.StatusUnauthorized, err.Error()) return echo.NewHTTPError(http.StatusUnauthorized, err.Error())
} }
if user.HasCapability(caps.CAP_VM_SET_ACCESS) { return c.JSONPretty(http.StatusOK, r.vms.GetNetworkSerializedVMs(
// Returns all VMs for which VM access for the logged user matches CAP_VM_SET_ACCESS.
return c.JSONPretty(http.StatusOK, r.vms.GetVMs(
func(vm vms.VM) bool { func(vm vms.VM) bool {
// First, checks if the user is the VM's owner
if vm.IsOwner(user.Email) {
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.HasCapability(caps.CAP_VM_SET_ACCESS) {
capabilities, exists := vm.Access[user.Email] capabilities, exists := vm.Access[user.Email]
if exists { if exists {
_, visible := capabilities[caps.CAP_VM_SET_ACCESS] _, visible := capabilities[caps.CAP_VM_SET_ACCESS]
...@@ -128,14 +133,15 @@ func (r *RouterVMs)GetEditableVMAccessVMs(c echo.Context) error { ...@@ -128,14 +133,15 @@ func (r *RouterVMs)GetEditableVMAccessVMs(c echo.Context) error {
} else { } else {
return false return false
} }
}), " ")
} else { } else {
return c.JSONPretty(http.StatusOK, []vms.VM{}, " ") return false
} }
} }
}), " ")
}
// Returns VMs that can have a directory exported. // Returns VMs that can have a directory exported.
// Requires either capability: // Requires to be the VM's owner, or either capability:
// User cap: VM_READFS_ANY: returns all VMs. // User cap: VM_READFS_ANY: returns all VMs.
// VM access cap: VM_READFS: returns all VMs with this cap for the logged user. // 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>" // curl --cacert ca.pem -X GET https://localhost:1077/vms/exportdir -H "Authorization: Bearer <AccessToken>"
...@@ -146,7 +152,7 @@ func (r *RouterVMs)GetDirExportableVMs(c echo.Context) error { ...@@ -146,7 +152,7 @@ func (r *RouterVMs)GetDirExportableVMs(c echo.Context) error {
} }
// Returns VMs that can have files imported (i.e. copied) into their filesystem. // Returns VMs that can have files imported (i.e. copied) into their filesystem.
// Requires either capability: // Requires to be the VM's owner, or either capability:
// User cap: VM_WRITEFS_ANY: returns all VMs. // User cap: VM_WRITEFS_ANY: returns all VMs.
// VM access cap: VM_WRITEFS: returns all VMs with this cap for the logged user. // 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>" // curl --cacert ca.pem -X GET https://localhost:1077/vms/importfiles -H "Authorization: Bearer <AccessToken>"
...@@ -185,8 +191,7 @@ func (r *RouterVMs)CreateVM(c echo.Context) error { ...@@ -185,8 +191,7 @@ func (r *RouterVMs)CreateVM(c echo.Context) error {
} }
// Creates a new VM from the client's parameters. // Creates a new VM from the client's parameters.
// The user creating the VM automatically gets all the VM access capabilities (see caps.VMAccessCaps for the list). vm, err := vms.NewVM(user.Email, p.Name, p.Cpus, p.Ram, p.Nic, p.TemplateID, user.Email)
vm, err := vms.NewVM(user.Email, caps.VMAccessCaps, p.Name, p.Cpus, p.Ram, p.Nic, p.TemplateID)
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error()) return echo.NewHTTPError(http.StatusBadRequest, err.Error())
} }
...@@ -195,11 +200,11 @@ func (r *RouterVMs)CreateVM(c echo.Context) error { ...@@ -195,11 +200,11 @@ func (r *RouterVMs)CreateVM(c echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, err.Error()) return echo.NewHTTPError(http.StatusBadRequest, err.Error())
} }
return c.JSONPretty(http.StatusCreated, vm, " ") return c.JSONPretty(http.StatusCreated, vm.SerializeToNetwork(), " ")
} }
// Deletes a VM based on its ID. // Deletes a VM based on its ID.
// Requires either capability: // Requires to be the VM's owner, or either capability:
// User cap: CAP_VM_DESTROY_ANY: any VM can be deleted. // 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. // 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>" // curl --cacert ca.pem -X DELETE https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59 -H "Authorization: Bearer <AccessToken>"
...@@ -213,7 +218,7 @@ func (r *RouterVMs)DeleteVMByID(c echo.Context) error { ...@@ -213,7 +218,7 @@ func (r *RouterVMs)DeleteVMByID(c echo.Context) error {
} }
// Starts a VM based on its ID. // Starts a VM based on its ID.
// Requires either capability: // Requires to be the VM's owner, or either capability:
// User cap: CAP_VM_START_ANY: any VM can be started. // User cap: CAP_VM_START_ANY: any VM can be started.
// VM access cap: CAP_VM_START: any of the VMs with this cap for the logged user can be started. // 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>" // curl --cacert ca.pem -X PUT https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59/start -H "Authorization: Bearer <AccessToken>"
...@@ -228,7 +233,7 @@ func (r *RouterVMs)StartVM(c echo.Context) error { ...@@ -228,7 +233,7 @@ func (r *RouterVMs)StartVM(c echo.Context) error {
} }
// Kills a VM based on its ID. // Kills a VM based on its ID.
// Requires either capability: // Requires to be the VM's owner, or either capability:
// User cap: CAP_VM_STOP_ANY: any VM can be killed. // User cap: CAP_VM_STOP_ANY: any VM can be killed.
// VM access cap: CAP_VM_STOP: any of the VMs with this cap for the logged user can be killed. // 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>" // curl --cacert ca.pem -X PUT https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59/stop -H "Authorization: Bearer <AccessToken>"
...@@ -242,7 +247,7 @@ func (r *RouterVMs)KillVM(c echo.Context) error { ...@@ -242,7 +247,7 @@ func (r *RouterVMs)KillVM(c echo.Context) error {
} }
// Gracefully shutdown a VM based on its ID. // Gracefully shutdown a VM based on its ID.
// Requires either capability: // Requires to be the VM's owner, or either capability:
// User cap: CAP_VM_STOP_ANY: any VM can be shutdown. // User cap: CAP_VM_STOP_ANY: any VM can be shutdown.
// VM access cap: CAP_VM_STOP: any of the VMs with this cap for the logged user can be shutdown. // 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>" // curl --cacert ca.pem -X PUT https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59/shutdown -H "Authorization: Bearer <AccessToken>"
...@@ -256,7 +261,7 @@ func (r *RouterVMs)ShutdownVM(c echo.Context) error { ...@@ -256,7 +261,7 @@ func (r *RouterVMs)ShutdownVM(c echo.Context) error {
} }
// Reboot a VM based on its ID. // Reboot a VM based on its ID.
// Requires either capability: // Requires to be the VM's owner, or either capability:
// User cap: CAP_VM_REBOOT_ANY: any VM can be rebooted. // User cap: CAP_VM_REBOOT_ANY: any VM can be rebooted.
// VM access cap: CAP_VM_REBOOT: any of the VMs with this cap for the logged user can be rebooted. // 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>" // curl --cacert ca.pem -X PUT https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59/stop -H "Authorization: Bearer <AccessToken>"
...@@ -270,7 +275,7 @@ func (r *RouterVMs)RebootVM(c echo.Context) error { ...@@ -270,7 +275,7 @@ func (r *RouterVMs)RebootVM(c echo.Context) error {
} }
// Edit a VM' specs: name, cpus, ram, nic // Edit a VM' specs: name, cpus, ram, nic
// Requires either capability: // Requires to be the VM's owner, or either capability:
// User cap: CAP_VM_EDIT_ANY: any VM can be edited. // 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. // 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"}' -H "Authorization: Bearer <AccessToken>" // 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"}' -H "Authorization: Bearer <AccessToken>"
...@@ -297,7 +302,7 @@ func (r *RouterVMs)EditVMByID(c echo.Context) error { ...@@ -297,7 +302,7 @@ func (r *RouterVMs)EditVMByID(c echo.Context) error {
} }
// Set a VM access for a given user. // Set a VM access for a given user.
// Requires BOTH capabilities: // Requires to be the VM's owner, or BOTH capabilities:
// User cap: CAP_VM_SET_ACCESS // User cap: CAP_VM_SET_ACCESS
// VM access 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>" // 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>"
...@@ -307,11 +312,8 @@ func (r *RouterVMs)SetVMAccessForUser(c echo.Context) error { ...@@ -307,11 +312,8 @@ func (r *RouterVMs)SetVMAccessForUser(c echo.Context) error {
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusUnauthorized, err.Error()) return echo.NewHTTPError(http.StatusUnauthorized, err.Error())
} }
if !user.HasCapability(caps.CAP_VM_SET_ACCESS) {
return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps)
}
// Checks the user for which to modify the VM Access actually exists. // Checks the user for which to set the VM Access actually exists.
email := c.Param("email") email := c.Param("email")
_, err = r.users.GetUserByEmail(email) _, err = r.users.GetUserByEmail(email)
if err != nil { if err != nil {
...@@ -324,6 +326,20 @@ func (r *RouterVMs)SetVMAccessForUser(c echo.Context) error { ...@@ -324,6 +326,20 @@ func (r *RouterVMs)SetVMAccessForUser(c echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, err.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 the client's parameters. // Deserializes and validates the client's parameters.
type Parameters struct { type Parameters struct {
Access caps.Capabilities `json:"access" validate:"required"` Access caps.Capabilities `json:"access" validate:"required"`
...@@ -344,7 +360,7 @@ func (r *RouterVMs)SetVMAccessForUser(c echo.Context) error { ...@@ -344,7 +360,7 @@ func (r *RouterVMs)SetVMAccessForUser(c echo.Context) error {
} }
// Delete a VM Access for a given user. // Delete a VM Access for a given user.
// Requires BOTH capabilities: // Requires to be the VM's owner, or BOTH capabilities:
// User cap: CAP_VM_SET_ACCESS // User cap: CAP_VM_SET_ACCESS
// VM access cap: CAP_VM_SET_ACCESS // VM access cap: CAP_VM_SET_ACCESS
// curl --cacert ca.pem -X DELETE https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59/caps/janedoes@nexus.org -H 'Content-Type: application/json' -H "Authorization: Bearer <AccessToken>" // curl --cacert ca.pem -X DELETE https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59/caps/janedoes@nexus.org -H 'Content-Type: application/json' -H "Authorization: Bearer <AccessToken>"
...@@ -354,18 +370,29 @@ func (r *RouterVMs)DeleteVMAccessForUser(c echo.Context) error { ...@@ -354,18 +370,29 @@ func (r *RouterVMs)DeleteVMAccessForUser(c echo.Context) error {
if err != nil { if err != nil {
return echo.NewHTTPError(http.StatusUnauthorized, err.Error()) return echo.NewHTTPError(http.StatusUnauthorized, err.Error())
} }
if !user.HasCapability(caps.CAP_VM_SET_ACCESS) {
return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps) // Retrieves the vmID of the VM to modify.
vmID, err := uuid.Parse(c.Param("id"))
if err != nil {
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. // Does not check that the user to remove the VM access for actually exists.
// Indeed, it might have been deleted. // Indeed, it might have been deleted.
email := c.Param("email") email := c.Param("email")
// Retrieves the vmID of the VM to modify. // First, check that the logged user is the VM's owner.
vmID, err := uuid.Parse(c.Param("id")) if !vm.IsOwner(user.Email) {
if err != nil { // Next, checks the logged user has the VM_SET_ACCESS capability.
return echo.NewHTTPError(http.StatusBadRequest, err.Error()) 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, email); err != nil {
...@@ -376,7 +403,7 @@ func (r *RouterVMs)DeleteVMAccessForUser(c echo.Context) error { ...@@ -376,7 +403,7 @@ func (r *RouterVMs)DeleteVMAccessForUser(c echo.Context) error {
} }
// Exports a VM's directory into a compressed archive. // Exports a VM's directory into a compressed archive.
// Requires either capability: // Requires to be the VM's owner, or either capability:
// User cap: VM_READFS_ANY: any VM can have their filesystem read. // User cap: VM_READFS_ANY: any VM can have their filesystem read.
// VM access cap: VM_READFS: any of the VMs with this cap for the logged user can have their filesystem read. // 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 // 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
...@@ -411,7 +438,7 @@ func (r *RouterVMs)ExportVMDir(c echo.Context) error { ...@@ -411,7 +438,7 @@ func (r *RouterVMs)ExportVMDir(c echo.Context) error {
} }
// Import files into a VM's filesystem, in a specified directory. The file tree is received in a tar.gz archive. // Import files into a VM's filesystem, in a specified directory. The file tree is received in a tar.gz archive.
// Requires either capability: // Requires to be the VM's owner, or either capability:
// User cap: VM_WRITEFS_ANY: any VM can import the file tree. // User cap: VM_WRITEFS_ANY: any VM can import the file tree.
// VM access cap: VM_WRITEFS: any of the VMs with this cap for the logged user can import the file tree. // 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>" // 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>"
...@@ -460,8 +487,9 @@ func (r *RouterVMs)ImportFilesToVM(c echo.Context) error { ...@@ -460,8 +487,9 @@ func (r *RouterVMs)ImportFilesToVM(c echo.Context) error {
}) })
} }
// Helper function that returns a list of VMs that match either: // Helper function that returns a list of serialized VMs that match either:
// - the logged user has the userCapabilityAny capability. // - 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. // - the VM access for the logged user matches the vmAccessCapability capability.
// Also, VMs for which cond is false are filtered out. // Also, VMs for which cond is false are filtered out.
func (r *RouterVMs)performVMsList(c echo.Context, userCapabilityAny, vmAccessCapability string, cond vms.VMKeeperFn) error { func (r *RouterVMs)performVMsList(c echo.Context, userCapabilityAny, vmAccessCapability string, cond vms.VMKeeperFn) error {
...@@ -474,11 +502,16 @@ func (r *RouterVMs)performVMsList(c echo.Context, userCapabilityAny, vmAccessCap ...@@ -474,11 +502,16 @@ func (r *RouterVMs)performVMsList(c echo.Context, userCapabilityAny, vmAccessCap
// If the logged user has the XX_ANY capability (userCapabilityAny), returns all VMs. // If the logged user has the XX_ANY capability (userCapabilityAny), returns all VMs.
if user.HasCapability(userCapabilityAny) { if user.HasCapability(userCapabilityAny) {
// Returns all VMs that pass the condition. // Returns all VMs that pass the condition.
return c.JSONPretty(http.StatusOK, r.vms.GetVMs(cond), " ") return c.JSONPretty(http.StatusOK, r.vms.GetNetworkSerializedVMs(cond), " ")
} else { } else {
// Returns all VMs for which VM access for the logged user matches the specified the VM Access capability (vmAccessCapability). // Returns all VMs:
return c.JSONPretty(http.StatusOK, r.vms.GetVMs( // - 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 { func(vm vms.VM) bool {
if vm.IsOwner(user.Email) {
return true
} else {
capabilities, exists := vm.Access[user.Email] capabilities, exists := vm.Access[user.Email]
if exists { if exists {
_, visible := capabilities[vmAccessCapability] _, visible := capabilities[vmAccessCapability]
...@@ -486,11 +519,13 @@ func (r *RouterVMs)performVMsList(c echo.Context, userCapabilityAny, vmAccessCap ...@@ -486,11 +519,13 @@ func (r *RouterVMs)performVMsList(c echo.Context, userCapabilityAny, vmAccessCap
} else { } else {
return false return false
} }
}
}), " ") }), " ")
} }
} }
// Helper function that performs an action on a VM based either on: // Helper function that performs an action on a VM based either on:
// - 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 logged user has the userCapabilityAny capability.
// - the VM access for the logged user matches the vmAccessCapability 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)performVMAction(c echo.Context, userCapabilityAny, vmAccessCapability string, action vmActionFn) error {
...@@ -510,11 +545,14 @@ func (r *RouterVMs)performVMAction(c echo.Context, userCapabilityAny, vmAccessCa ...@@ -510,11 +545,14 @@ func (r *RouterVMs)performVMAction(c echo.Context, userCapabilityAny, vmAccessCa
return echo.NewHTTPError(http.StatusUnauthorized, err.Error()) return echo.NewHTTPError(http.StatusUnauthorized, err.Error())
} }
// First, checks if the user has the XX_ANY capability // First, checks if the user is the VM's owner
if user.HasCapability(userCapabilityAny) { if vm.IsOwner(user.Email) {
return action(c, &vm)
} else if user.HasCapability(userCapabilityAny) {
// Next, checks if the user has the XX_ANY capability
return action(c, &vm) return action(c, &vm)
} else { } else {
// Check if the VM access for the logged user matches the required capability // Finally, check if the VM access for the logged user matches the required capability
userCaps, exists := vm.Access[user.Email] userCaps, exists := vm.Access[user.Email]
if !exists { if !exists {
return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps) return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps)
......
...@@ -6,8 +6,8 @@ import ( ...@@ -6,8 +6,8 @@ import (
const ( const (
major = 1 major = 1
minor = 4 minor = 5
bugfix = 2 bugfix = 0
) )
type Version struct { type Version struct {
......
...@@ -21,8 +21,10 @@ import ( ...@@ -21,8 +21,10 @@ import (
) )
type ( type (
// Internal VM structure.
VM struct { VM struct {
ID uuid.UUID `json:"id" validate:"required"` ID uuid.UUID `json:"id" validate:"required"`
Owner string `json:"owner" validate:"required,email"`
Name string `json:"name" validate:"required,min=2,max=256"` Name string `json:"name" validate:"required,min=2,max=256"`
Cpus int `json:"cpus" validate:"required,gte=1,lte=16"` Cpus int `json:"cpus" validate:"required,gte=1,lte=16"`
Ram int `json:"ram" validate:"required,gte=512,lte=32768"` Ram int `json:"ram" validate:"required,gte=512,lte=32768"`
...@@ -48,6 +50,34 @@ type ( ...@@ -48,6 +50,34 @@ type (
VMState string VMState string
NicType string NicType string
// VM fields to be serialized to disk.
VMDiskSerialized struct {
ID uuid.UUID `json:"id"`
Owner string `json:"owner"`
Name string `json:"name"`
Cpus int `json:"cpus"`
Ram int `json:"ram"`
Nic NicType `json:"nic"`
TemplateID uuid.UUID `json:"templateID"`
Access map[string]caps.Capabilities `json:"access"`
}
// VM fields to be serialized over the network (sent to the client).
VMNetworkSerialized struct {
ID uuid.UUID
Owner string
Name string
Cpus int
Ram int
Nic NicType
TemplateID uuid.UUID
Access map[string]caps.Capabilities
State VMState
Port int
Pwd string
DiskBusy bool // If true the VM's disk is busy (cannot be modified or deleted)
}
endOfExecCallback func(vm *VM) endOfExecCallback func(vm *VM)
) )
...@@ -74,14 +104,16 @@ var passwordGen, _ = password.NewGenerator(&password.GeneratorInput{ ...@@ -74,14 +104,16 @@ var passwordGen, _ = password.NewGenerator(&password.GeneratorInput{
}) })
// Creates a VM. // Creates a VM.
func NewVM(creatorEmail string, caps caps.Capabilities, name string, cpus, ram int, nic NicType, templateID uuid.UUID) (*VM, error) { func NewVM(creatorEmail string, name string, cpus, ram int, nic NicType, templateID uuid.UUID, owner string) (*VM, error) {
vmID, err := uuid.NewRandom() vmID, err := uuid.NewRandom()
if err != nil { if err != nil {
log.Error("Failed creating VM: "+err.Error()) log.Error("Failed creating VM: "+err.Error())
return nil, err return nil, err
} }
vm := newEmptyVM() vm := newEmptyVM()
vm.ID = vmID vm.ID = vmID
vm.Owner = owner
vm.Name = name vm.Name = name
vm.Cpus = cpus vm.Cpus = cpus
vm.Ram = ram vm.Ram = ram
...@@ -90,7 +122,7 @@ func NewVM(creatorEmail string, caps caps.Capabilities, name string, cpus, ram i ...@@ -90,7 +122,7 @@ func NewVM(creatorEmail string, caps caps.Capabilities, name string, cpus, ram i
id := vmID.String() id := vmID.String()
vm.dir = filepath.Join(vms.dir, id[0:3], id[3:6], id) vm.dir = filepath.Join(vms.dir, id[0:3], id[3:6], id)
vm.qgaSock = filepath.Join(vm.dir, vmQGASockFile) vm.qgaSock = filepath.Join(vm.dir, vmQGASockFile)
vm.Access[creatorEmail] = caps vm.Access = make(map[string]caps.Capabilities)
if err = vm.validate(); err != nil { if err = vm.validate(); err != nil {
return nil, errors.New("Failed validating VM: "+err.Error()) return nil, errors.New("Failed validating VM: "+err.Error())
...@@ -99,15 +131,51 @@ func NewVM(creatorEmail string, caps caps.Capabilities, name string, cpus, ram i ...@@ -99,15 +131,51 @@ func NewVM(creatorEmail string, caps caps.Capabilities, name string, cpus, ram i
return vm, nil return vm, nil
} }
func (vm *VM)SerializeToDisk() VMDiskSerialized {
return VMDiskSerialized {
ID: vm.ID,
Owner: vm.Owner,
Name: vm.Name,
Cpus: vm.Cpus,
Ram: vm.Ram,
Nic: vm.Nic,
TemplateID: vm.TemplateID,
Access: vm.Access,
}
}
func (vm *VM)SerializeToNetwork() VMNetworkSerialized {
return VMNetworkSerialized {
ID: vm.ID,
Owner: vm.Owner,
Name: vm.Name,
Cpus: vm.Cpus,
Ram: vm.Ram,
Nic: vm.Nic,
TemplateID: vm.TemplateID,
Access: vm.Access,
State: vm.Run.State,
Port: vm.Run.Port,
Pwd: vm.Run.Pwd,
DiskBusy: vm.DiskBusy,
}
}
func (vm *VM)getDiskPath() string { func (vm *VM)getDiskPath() string {
vmDiskFile, _ := filepath.Abs(filepath.Join(vm.dir, vmDiskFile)) vmDiskFile, _ := filepath.Abs(filepath.Join(vm.dir, vmDiskFile))
return vmDiskFile return vmDiskFile
} }
// Returns true if the specified email is the VM's owner.
func (vm *VM)IsOwner(email string) bool {
return email == vm.Owner
}
// Creates an empty VM. // Creates an empty VM.
func newEmptyVM() *VM { func newEmptyVM() *VM {
return &VM { return &VM {
ID: uuid.Nil, ID: uuid.Nil,
Owner: "",
Name: "", Name: "",
Cpus: 0, Cpus: 0,
Ram: 0, Ram: 0,
...@@ -229,18 +297,7 @@ func (vm *VM)writeConfig() error { ...@@ -229,18 +297,7 @@ func (vm *VM)writeConfig() error {
encoder := json.NewEncoder(file) encoder := json.NewEncoder(file)
encoder.SetIndent("", " ") encoder.SetIndent("", " ")
// Only serializes the following fields. if err = encoder.Encode(vm.SerializeToDisk()); err != nil {
type VMConf struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Cpus int `json:"cpus"`
Ram int `json:"ram"`
Nic NicType `json:"nic"`
TemplateID uuid.UUID `json:"templateID"`
Access map[string]caps.Capabilities `json:"access"`
}
vmConf := VMConf{ vm.ID, vm.Name, vm.Cpus, vm.Ram, vm.Nic, vm.TemplateID, vm.Access }
if err = encoder.Encode(vmConf); err != nil {
log.Error("Failed encoding VM config file: "+err.Error()) log.Error("Failed encoding VM config file: "+err.Error())
return errors.New("Failed encoding VM config file: "+err.Error()) return errors.New("Failed encoding VM config file: "+err.Error())
} }
......
...@@ -86,13 +86,13 @@ func InitVMs() error { ...@@ -86,13 +86,13 @@ func InitVMs() error {
return nil return nil
} }
// Returns the list of VMs for which VMKeeperFn returns true. // Returns the list of serialized VMs for which VMKeeperFn returns true.
func (vms *VMs)GetVMs(keepFn VMKeeperFn) []VM { func (vms *VMs)GetNetworkSerializedVMs(keepFn VMKeeperFn) []VMNetworkSerialized {
vms.rwlock.RLock() vms.rwlock.RLock()
list := []VM{} list := []VMNetworkSerialized{}
for _, vm := range vms.m { for _, vm := range vms.m {
if keepFn(vm) { if keepFn(vm) {
list = append(list, vm) list = append(list, vm.SerializeToNetwork())
} }
} }
vms.rwlock.RUnlock() vms.rwlock.RUnlock()
...@@ -340,12 +340,15 @@ func (vms *VMs)SetVMAccess(vmID uuid.UUID, loggedUserEmail, userEmail string, ne ...@@ -340,12 +340,15 @@ func (vms *VMs)SetVMAccess(vmID uuid.UUID, loggedUserEmail, userEmail string, ne
vm.mutex.Lock() vm.mutex.Lock()
defer vm.mutex.Unlock() defer vm.mutex.Unlock()
// Checks the logged user has VM_SET_ACCESS set in her/his VM access. // 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.Access[loggedUserEmail] userCaps := vm.Access[loggedUserEmail]
_, exists := userCaps[caps.CAP_VM_SET_ACCESS] _, exists := userCaps[caps.CAP_VM_SET_ACCESS]
if !exists { if !exists {
return errors.New("Insufficient capability") return errors.New("Insufficient capability")
} }
}
vm.Access[userEmail] = newAccess vm.Access[userEmail] = newAccess
...@@ -372,12 +375,15 @@ func (vms *VMs)DeleteVMAccess(vmID uuid.UUID, loggedUserEmail, userEmail string) ...@@ -372,12 +375,15 @@ func (vms *VMs)DeleteVMAccess(vmID uuid.UUID, loggedUserEmail, userEmail string)
vm.mutex.Lock() vm.mutex.Lock()
defer vm.mutex.Unlock() defer vm.mutex.Unlock()
// Checks the user has VM_SET_ACCESS set in her/his VM access. // 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.Access[loggedUserEmail] userCaps := vm.Access[loggedUserEmail]
_, exists := userCaps[caps.CAP_VM_SET_ACCESS] _, exists := userCaps[caps.CAP_VM_SET_ACCESS]
if !exists { if !exists {
return errors.New("Insufficient capability") return errors.New("Insufficient capability")
} }
}
// Only removes the user from the Access map if it actually had an access. // Only removes the user from the Access map if it actually had an access.
if _, exists := vm.Access[userEmail]; exists { if _, exists := vm.Access[userEmail]; exists {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment