Skip to content

Commit

Permalink
added file download url endpoint (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
sei-aschlackman authored May 22, 2024
1 parent 807ba03 commit 2f5e543
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 16 deletions.
4 changes: 3 additions & 1 deletion src/Player.Vm.Api/Domain/Models/Permissions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ namespace Player.Vm.Api.Domain.Models
{
public enum Permissions
{
ReadOnly
ReadOnly,
ViewAdmin,
SystemAdmin
}
}
35 changes: 21 additions & 14 deletions src/Player.Vm.Api/Domain/Services/PermissionsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,13 @@ public PermissionsService(

public async Task<IEnumerable<Permissions>> GetViewPermissions(Guid viewId, CancellationToken cancellationToken)
{
var permissions = new List<Permissions>();
var viewPermissions = await _playerService.GetPermissionsByViewIdAsync(viewId, cancellationToken);

if (viewPermissions.Any(x => x.Key.ToLower() == Permissions.ReadOnly.ToString().ToLower() && x.Value.ToLower() == "true"))
{
permissions.Add(Permissions.ReadOnly);
}

var permissions = this.ProcessPermissions(viewPermissions);
return permissions;
}

public async Task<IEnumerable<Permissions>> GetPermissions(IEnumerable<Guid> teamIds, CancellationToken cancellationToken)
{
var vmPermissions = new List<Permissions>();
var viewPermissions = new List<Permission>();
var viewIds = await _viewService.GetViewIdsForTeams(teamIds, cancellationToken);
var taskDict = new Dictionary<Guid, Task<IEnumerable<Permission>>>();
Expand Down Expand Up @@ -80,12 +73,7 @@ public async Task<IEnumerable<Permissions>> GetPermissions(IEnumerable<Guid> tea
});
}

if (viewPermissions.Any(x => x.Key.ToLower() == Permissions.ReadOnly.ToString().ToLower() && x.Value.ToLower() == "true"))
{
vmPermissions.Add(Permissions.ReadOnly);
}

return vmPermissions;
return this.ProcessPermissions(viewPermissions);
}

public async Task<bool> CanWrite(IEnumerable<Guid> teamIds, CancellationToken cancellationToken)
Expand All @@ -101,5 +89,24 @@ public async Task<bool> CanWrite(IEnumerable<Guid> teamIds, CancellationToken ca
return true;
}
}

private IEnumerable<Permissions> ProcessPermissions(IEnumerable<Permission> playerPermissions)
{
var permissionsList = new List<Permissions>();
this.ProcessPermission(playerPermissions, ref permissionsList, Permissions.ReadOnly);
this.ProcessPermission(playerPermissions, ref permissionsList, Permissions.ViewAdmin);
this.ProcessPermission(playerPermissions, ref permissionsList, Permissions.SystemAdmin);
return permissionsList;
}

private IList<Permissions> ProcessPermission(IEnumerable<Permission> playerPermissions, ref List<Permissions> permissionsList, Permissions permission)
{
if (playerPermissions.Any(x => x.Key.ToLower() == permission.ToString().ToLower() && x.Value.ToLower() == "true"))
{
permissionsList.Add(permission);
}

return permissionsList;
}
}
}
83 changes: 83 additions & 0 deletions src/Player.Vm.Api/Domain/Vsphere/Services/VsphereService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using Player.Vm.Api.Domain.Vsphere.Models;
using Player.Vm.Api.Domain.Vsphere.Extensions;
using Player.Vm.Api.Domain.Models;
using System.Web;

namespace Player.Vm.Api.Domain.Vsphere.Services
{
Expand All @@ -31,6 +32,7 @@ public interface IVsphereService
Task<TaskInfo> ReconfigureVm(Guid id, Feature feature, string label, string newvalue);
Task<VirtualMachineToolsStatus> GetVmToolsStatus(Guid id);
Task<string> UploadFileToVm(Guid id, string username, string password, string filepath, Stream fileStream);
Task<string> GetVmFileUrl(Guid id, string username, string password, string filepath);
Task<IEnumerable<IsoFile>> GetIsos(Guid vmId, string viewId, string subFolder);
Task<string> SetResolution(Guid id, int width, int height);
Task<ManagedObjectReference[]> BulkPowerOperation(Guid[] ids, PowerOperation operation);
Expand Down Expand Up @@ -746,6 +748,87 @@ public async Task<string> UploadFileToVm(Guid id, string username, string passwo
return "";
}

public async Task<string> GetVmFileUrl(Guid id, string username, string password, string filepath)
{
var aggregate = await GetVm(id);
var vmReference = aggregate.MachineReference;

if (vmReference == null)
{
var errorMessage = $"could not get file url, vmReference is null";
_logger.LogDebug(errorMessage);
return errorMessage;
}

//retrieve the properties specificied
RetrievePropertiesResponse response = await aggregate.Connection.Client.RetrievePropertiesAsync(
aggregate.Connection.Props,
VmFilter(vmReference));

VimClient.ObjectContent[] oc = response.returnval;
VimClient.ObjectContent obj = oc[0];

foreach (DynamicProperty dp in obj.propSet)
{
if (dp.val.GetType() == typeof(VirtualMachineSummary))
{
VirtualMachineSummary vmSummary = (VirtualMachineSummary)dp.val;
//check vmware tools status
var tools_status = vmSummary.guest.toolsStatus;
if (tools_status == VirtualMachineToolsStatus.toolsNotInstalled || tools_status == VirtualMachineToolsStatus.toolsNotRunning)
{
var errorMessage = $"could not get file url, VM Tools is not running";
_logger.LogDebug(errorMessage);
return errorMessage;
}

// user credentials on the VM
NamePasswordAuthentication credentialsAuth = new NamePasswordAuthentication()
{
interactiveSession = false,
username = username,
password = password
};
ManagedObjectReference fileManager = new ManagedObjectReference()
{
type = "GuestFileManager",
Value = "guestOperationsFileManager"
};

var fileTransferInfo = await aggregate.Connection.Client.InitiateFileTransferFromGuestAsync(fileManager, vmReference, credentialsAuth, filepath);
var fileTransferUrl = fileTransferInfo.url;

// Replace IP address with hostname
RetrievePropertiesResponse hostResponse = await aggregate.Connection.Client.RetrievePropertiesAsync(aggregate.Connection.Props, HostFilter(vmSummary.runtime.host, "name"));
string hostName = hostResponse.returnval[0].propSet[0].val as string;

if (!fileTransferUrl.Contains(hostName))
{
fileTransferUrl = fileTransferUrl.Replace("https://", "");
var s = fileTransferUrl.IndexOf("/");
fileTransferUrl = "https://" + hostName + fileTransferUrl.Substring(s);
}

if (_rewriteHostOptions.RewriteHost)
{
var builder = new UriBuilder(fileTransferUrl);

var query = HttpUtility.ParseQueryString(builder.Query);
query[_rewriteHostOptions.RewriteHostQueryParam] = builder.Host;
var fileName = Path.GetFileName(filepath);
query["fileName"] = fileName;
builder.Query = query.ToString();

builder.Host = _rewriteHostOptions.RewriteHostUrl;
fileTransferUrl = builder.ToString();
}

return fileTransferUrl;
}
}
return "";
}

private async Task<TaskInfo> WaitForVimTask(ManagedObjectReference task, VsphereConnection connection)
{
int i = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/Player.Vm.Api/Features/Vsphere/BaseHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class BaseHandler
{
private readonly IMapper _mapper;
private readonly IVsphereService _vsphereService;
private readonly IPlayerService _playerService;
protected readonly IPlayerService _playerService;
private readonly Guid _userId;
private readonly IPermissionsService _permissionsService;
private readonly IVmService _vmService;
Expand Down
79 changes: 79 additions & 0 deletions src/Player.Vm.Api/Features/Vsphere/Commands/GetVmFileUrl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2024 Carnegie Mellon University. All Rights Reserved.
// Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information.

using System;
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using Player.Vm.Api.Infrastructure.Exceptions;
using Player.Vm.Api.Domain.Vsphere.Services;
using Player.Vm.Api.Features.Vms;
using System.IO;
using AutoMapper;
using Player.Vm.Api.Domain.Services;
using System.Security.Principal;
using Player.Vm.Api.Domain.Models;

namespace Player.Vm.Api.Features.Vsphere
{
public class GetVmFileUrl
{
[DataContract(Name = "GetFileUrlVsphereVirtualMachine")]
public class Command : IRequest<Response>
{
[JsonIgnore]
public Guid Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string FilePath { get; set; }
}

[DataContract(Name = "FileVmUrlResponse")]
public class Response
{
public string Url { get; set; }
public string FileName { get; set; }
}

public class Handler : BaseHandler, IRequestHandler<Command, Response>
{
private readonly IVsphereService _vsphereService;

public Handler(
IVsphereService vsphereService,
IVmService vmService,
IMapper mapper,
IPlayerService playerService,
IPrincipal principal,
IPermissionsService permissionsService) :
base(mapper, vsphereService, playerService, principal, permissionsService, vmService)
{
_vsphereService = vsphereService;
}

public async Task<Response> Handle(Command request, CancellationToken cancellationToken)
{
var vm = await base.GetVm(request.Id, Permissions.ReadOnly, cancellationToken);

if (!await _playerService.CanManageTeamsAsync(vm.TeamIds, false, cancellationToken))
throw new ForbiddenException("You do not have permission to download files from this vm.");

var url = await _vsphereService.GetVmFileUrl(
request.Id,
request.Username,
request.Password,
request.FilePath);

var fileName = Path.GetFileName(request.FilePath);

return new Response()
{
FileName = fileName,
Url = url
};
}
}
}
}
12 changes: 12 additions & 0 deletions src/Player.Vm.Api/Features/Vsphere/VsphereController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,18 @@ public async Task<IActionResult> UploadFile([FromRoute] Guid id)
return Json(result);
}

/// <summary>
/// Get the url to download a file from a vsphere virtual machine
/// </summary>
[HttpPost("vms/vsphere/{id}/actions/file-url")]
[ProducesResponseType(typeof(GetVmFileUrl.Response), (int)HttpStatusCode.OK)]
[SwaggerOperation(OperationId = "getFileUrlVsphereVirtualMachine")]
public async Task<IActionResult> GetFileUrl([FromRoute] Guid id, [FromBody] GetVmFileUrl.Command command)
{
command.Id = id;
return Ok(await _mediator.Send(command));
}

/// <summary>
/// Get isos available to be mounted to a vsphere virtual machine
/// </summary>
Expand Down

0 comments on commit 2f5e543

Please sign in to comment.