diff --git a/src/router/routerTemplates.go b/src/router/routerTemplates.go index 5a38d6f655a2d1fe159a9b4f0e9fdc2bec3fd14c..33c7d4c9f80e31eaaf9f5fc4679edc128dbed83a 100644 --- a/src/router/routerTemplates.go +++ b/src/router/routerTemplates.go @@ -83,16 +83,20 @@ func (r *RouterTemplates)CreateTemplateFromVM(c echo.Context) error { return echo.NewHTTPError(http.StatusNotFound, err.Error()) } - // 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) { - userCaps, exists := vm.Access[user.Email] - if !exists { - return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps) - } + // 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 + if !user.HasCapability(caps.CAP_VM_LIST_ANY) { + userCaps, exists := vm.Access[user.Email] + if !exists { + return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps) + } - _, hasAccess := userCaps[caps.CAP_VM_LIST] - if !hasAccess { - return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps) + _, hasAccess := userCaps[caps.CAP_VM_LIST] + if !hasAccess { + return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps) + } } } diff --git a/src/router/routerVMs.go b/src/router/routerVMs.go index bd94f5cd8d03274ddcef34aca2b700b4fc3152d2..09d7cc9aee76960e399cf85552871d1bdde9b8f5 100644 --- a/src/router/routerVMs.go +++ b/src/router/routerVMs.go @@ -29,7 +29,7 @@ func NewRouterVMs() *RouterVMs { } // 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. // 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>" @@ -40,7 +40,7 @@ func (r *RouterVMs)GetListableVMs(c echo.Context) error { } // 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. // 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>" @@ -51,7 +51,7 @@ func (r *RouterVMs)GetAttachableVMs(c echo.Context) error { } // 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. // 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>" @@ -62,7 +62,7 @@ func (r *RouterVMs)GetDeletableVMs(c echo.Context) error { } // 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. // 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>" @@ -73,7 +73,7 @@ func (r *RouterVMs)GetStartableVMs(c echo.Context) error { } // 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. // 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>" @@ -84,7 +84,7 @@ func (r *RouterVMs)GetStoppableVMs(c echo.Context) error { } // 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. // 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>" @@ -95,7 +95,7 @@ func (r *RouterVMs)GetRebootableVMs(c echo.Context) error { } // 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. // 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>" @@ -106,7 +106,7 @@ func (r *RouterVMs)GetEditableVMs(c echo.Context) error { } // 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 // VM access cap: CAP_VM_SET_ACCESS // curl --cacert ca.pem -X GET https://localhost:1077/vms/editaccess -H "Authorization: Bearer <AccessToken>" @@ -117,25 +117,31 @@ func (r *RouterVMs)GetEditableVMAccessVMs(c echo.Context) error { return echo.NewHTTPError(http.StatusUnauthorized, err.Error()) } - if user.HasCapability(caps.CAP_VM_SET_ACCESS) { - // 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 { - capabilities, exists := vm.Access[user.Email] - if exists { - _, visible := capabilities[caps.CAP_VM_SET_ACCESS] - return visible + return c.JSONPretty(http.StatusOK, r.vms.GetNetworkSerializedVMs( + 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] + if exists { + _, visible := capabilities[caps.CAP_VM_SET_ACCESS] + return visible + } else { + return false + } } else { return false } - }), " ") - } else { - return c.JSONPretty(http.StatusOK, []vms.VM{}, " ") - } + } + }), " ") } // 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. // 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>" @@ -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. -// Requires either capability: +// Requires to be the VM's owner, or either capability: // User cap: VM_WRITEFS_ANY: returns all VMs. // 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>" @@ -185,8 +191,7 @@ func (r *RouterVMs)CreateVM(c echo.Context) error { } // 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, caps.VMAccessCaps, p.Name, p.Cpus, p.Ram, p.Nic, p.TemplateID) + vm, err := vms.NewVM(user.Email, p.Name, p.Cpus, p.Ram, p.Nic, p.TemplateID, user.Email) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } @@ -195,11 +200,11 @@ func (r *RouterVMs)CreateVM(c echo.Context) 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. -// Requires either capability: +// Requires to be the VM's owner, or either capability: // 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>" @@ -213,7 +218,7 @@ func (r *RouterVMs)DeleteVMByID(c echo.Context) error { } // 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. // 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>" @@ -228,7 +233,7 @@ func (r *RouterVMs)StartVM(c echo.Context) error { } // 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. // 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>" @@ -242,7 +247,7 @@ func (r *RouterVMs)KillVM(c echo.Context) error { } // 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. // 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>" @@ -256,7 +261,7 @@ func (r *RouterVMs)ShutdownVM(c echo.Context) error { } // 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. // 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>" @@ -270,7 +275,7 @@ func (r *RouterVMs)RebootVM(c echo.Context) error { } // 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. // 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>" @@ -297,7 +302,7 @@ func (r *RouterVMs)EditVMByID(c echo.Context) error { } // 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 // 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>" @@ -307,11 +312,8 @@ func (r *RouterVMs)SetVMAccessForUser(c echo.Context) error { if err != nil { 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") _, err = r.users.GetUserByEmail(email) if err != nil { @@ -324,6 +326,20 @@ 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 the client's parameters. type Parameters struct { Access caps.Capabilities `json:"access" validate:"required"` @@ -344,7 +360,7 @@ func (r *RouterVMs)SetVMAccessForUser(c echo.Context) error { } // 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 // 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>" @@ -354,18 +370,29 @@ func (r *RouterVMs)DeleteVMAccessForUser(c echo.Context) error { if err != nil { 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. // Indeed, it might have been deleted. email := c.Param("email") - // 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()) + // 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 { @@ -376,7 +403,7 @@ func (r *RouterVMs)DeleteVMAccessForUser(c echo.Context) error { } // 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. // 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 @@ -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. -// 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. // 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>" @@ -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 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 { @@ -474,23 +502,30 @@ func (r *RouterVMs)performVMsList(c echo.Context, userCapabilityAny, vmAccessCap // If the logged user has the XX_ANY capability (userCapabilityAny), returns all VMs. if user.HasCapability(userCapabilityAny) { // 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 { - // Returns all VMs for which VM access for the logged user matches the specified the VM Access capability (vmAccessCapability). - return c.JSONPretty(http.StatusOK, r.vms.GetVMs( + // 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 { - capabilities, exists := vm.Access[user.Email] - if exists { - _, visible := capabilities[vmAccessCapability] - return visible && cond(vm) + if vm.IsOwner(user.Email) { + return true } else { - return false + capabilities, exists := vm.Access[user.Email] + if exists { + _, visible := capabilities[vmAccessCapability] + return visible && cond(vm) + } else { + return false + } } }), " ") } } // 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 VM access for the logged user matches the vmAccessCapability capability. 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 return echo.NewHTTPError(http.StatusUnauthorized, err.Error()) } - // First, checks if the user has the XX_ANY capability - if user.HasCapability(userCapabilityAny) { + // First, checks if the user is the VM's owner + 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) } 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] if !exists { return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps) diff --git a/src/version/version.go b/src/version/version.go index 50f6dee85d9cc6e98d6a1547e2e4c70732e6fe8a..804b0773863e7d8dc074c99231acaa433a71bffd 100644 --- a/src/version/version.go +++ b/src/version/version.go @@ -6,8 +6,8 @@ import ( const ( major = 1 - minor = 4 - bugfix = 2 + minor = 5 + bugfix = 0 ) type Version struct { diff --git a/src/vms/template.go b/src/vms/template.go index e330b7415cc9c3981d4a1a30d7bc58fedb665aeb..52b4584d632551c2975f030abe717d03a42681e7 100644 --- a/src/vms/template.go +++ b/src/vms/template.go @@ -13,9 +13,9 @@ import ( ) type Template struct { - ID uuid.UUID `json:"id" validate:"required"` - Name string `json:"name" validate:"required,min=2,max=256"` - Owner string `json:"owner" validate:"required,email"` + ID uuid.UUID `json:"id" validate:"required"` + Name string `json:"name" validate:"required,min=2,max=256"` + Owner string `json:"owner" validate:"required,email"` Access string `json:"access" validate:"required,min=4,max=16"` // private or public CreationTime time.Time `json:"creationTime" validate:"required"` } diff --git a/src/vms/vm.go b/src/vms/vm.go index bd693d2b85dd7d07136a4a0f390f1417846870f8..0e555e0e52bec772cce331e62a94c6ced9888e57 100644 --- a/src/vms/vm.go +++ b/src/vms/vm.go @@ -21,8 +21,10 @@ import ( ) type ( + // Internal VM structure. VM struct { ID uuid.UUID `json:"id" validate:"required"` + Owner string `json:"owner" validate:"required,email"` Name string `json:"name" validate:"required,min=2,max=256"` Cpus int `json:"cpus" validate:"required,gte=1,lte=16"` Ram int `json:"ram" validate:"required,gte=512,lte=32768"` @@ -48,6 +50,34 @@ type ( VMState 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) ) @@ -74,14 +104,16 @@ var passwordGen, _ = password.NewGenerator(&password.GeneratorInput{ }) // 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() + if err != nil { log.Error("Failed creating VM: "+err.Error()) return nil, err } vm := newEmptyVM() vm.ID = vmID + vm.Owner = owner vm.Name = name vm.Cpus = cpus vm.Ram = ram @@ -90,7 +122,7 @@ func NewVM(creatorEmail string, caps caps.Capabilities, name string, cpus, ram i id := vmID.String() vm.dir = filepath.Join(vms.dir, id[0:3], id[3:6], id) vm.qgaSock = filepath.Join(vm.dir, vmQGASockFile) - vm.Access[creatorEmail] = caps + vm.Access = make(map[string]caps.Capabilities) if err = vm.validate(); err != nil { 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 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 { vmDiskFile, _ := filepath.Abs(filepath.Join(vm.dir, 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. func newEmptyVM() *VM { return &VM { ID: uuid.Nil, + Owner: "", Name: "", Cpus: 0, Ram: 0, @@ -229,18 +297,7 @@ func (vm *VM)writeConfig() error { encoder := json.NewEncoder(file) encoder.SetIndent("", " ") - // Only serializes the following fields. - 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 { + if err = encoder.Encode(vm.SerializeToDisk()); err != nil { log.Error("Failed encoding VM config file: "+err.Error()) return errors.New("Failed encoding VM config file: "+err.Error()) } diff --git a/src/vms/vms.go b/src/vms/vms.go index 7a1dc0124604552d7b3de8472bf291c2cf25e1fa..03a001611e03afa9a38fba360c423acb894cba0f 100644 --- a/src/vms/vms.go +++ b/src/vms/vms.go @@ -86,13 +86,13 @@ func InitVMs() error { return nil } -// Returns the list of VMs for which VMKeeperFn returns true. -func (vms *VMs)GetVMs(keepFn VMKeeperFn) []VM { +// Returns the list of serialized VMs for which VMKeeperFn returns true. +func (vms *VMs)GetNetworkSerializedVMs(keepFn VMKeeperFn) []VMNetworkSerialized { vms.rwlock.RLock() - list := []VM{} + list := []VMNetworkSerialized{} for _, vm := range vms.m { if keepFn(vm) { - list = append(list, vm) + list = append(list, vm.SerializeToNetwork()) } } vms.rwlock.RUnlock() @@ -340,11 +340,14 @@ func (vms *VMs)SetVMAccess(vmID uuid.UUID, loggedUserEmail, userEmail string, ne vm.mutex.Lock() defer vm.mutex.Unlock() - // Checks the logged user has VM_SET_ACCESS set in her/his VM access. - userCaps := vm.Access[loggedUserEmail] - _, exists := userCaps[caps.CAP_VM_SET_ACCESS] - if !exists { - return errors.New("Insufficient capability") + // 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] + _, exists := userCaps[caps.CAP_VM_SET_ACCESS] + if !exists { + return errors.New("Insufficient capability") + } } vm.Access[userEmail] = newAccess @@ -372,11 +375,14 @@ func (vms *VMs)DeleteVMAccess(vmID uuid.UUID, loggedUserEmail, userEmail string) vm.mutex.Lock() defer vm.mutex.Unlock() - // Checks the user has VM_SET_ACCESS set in her/his VM access. - userCaps := vm.Access[loggedUserEmail] - _, exists := userCaps[caps.CAP_VM_SET_ACCESS] - if !exists { - return errors.New("Insufficient capability") + // 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] + _, 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.