// Copyright Epic Games, Inc. All Rights Reserved. using HordeServer.Api; using HordeServer.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Net.Http.Headers; using System; using System.IO; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using HordeServer.Models; using MongoDB.Bson; using HordeServer.Utilities; namespace HordeServer.Controllers { using AgentSoftwareChannelName = StringId; /// /// Controller for the /api/v1/software endpoint /// [ApiController] [Authorize] [Route("[controller]")] public class AgentSoftwareController : ControllerBase { /// /// Singleton instance of the ACL service /// private readonly AclService AclService; /// /// Singleton instance of the client service /// private readonly AgentSoftwareService AgentSoftwareService; /// /// Constructor /// /// The ACL service /// The client service public AgentSoftwareController(AclService AclService, AgentSoftwareService AgentSoftwareService) { this.AclService = AclService; this.AgentSoftwareService = AgentSoftwareService; } /// /// Finds all uploaded software matching the given criteria /// /// Filter for the properties to return /// Http response [HttpGet] [Route("/api/v1/agentsoftware")] [ProducesResponseType(typeof(List), 200)] public async Task>> FindSoftwareAsync([FromQuery] PropertyFilter? Filter = null) { if (!await AclService.AuthorizeAsync(AclAction.DownloadSoftware, User)) { return Forbid(); } List Results = await AgentSoftwareService.FindChannelsAsync(); List Responses = new List(); foreach (IAgentSoftwareChannel Result in Results) { Responses.Add(new GetAgentSoftwareChannelResponse(Result).ApplyFilter(Filter)); } return Responses; } /// /// Finds all uploaded software matching the given criteria /// /// Name of the channel to get /// Filter for the properties to return /// Http response [HttpGet] [Route("/api/v1/agentsoftware/{Name}")] [ProducesResponseType(typeof(GetAgentSoftwareChannelResponse), 200)] public async Task> FindSoftwareAsync(string Name, [FromQuery] PropertyFilter? Filter = null) { if (!await AclService.AuthorizeAsync(AclAction.DownloadSoftware, User)) { return Forbid(); } IAgentSoftwareChannel? Channel = await AgentSoftwareService.GetChannelAsync(new AgentSoftwareChannelName(Name)); if(Channel == null) { return NotFound(); } return new GetAgentSoftwareChannelResponse(Channel).ApplyFilter(Filter); } /// /// Uploads a new agent zip file /// /// Name of the channel to post to /// Zip archive containing the new client software /// Http result code [HttpPost] [Route("/api/v1/agentsoftware/{Name}/zip")] public async Task SetArchiveAsync(string Name, [FromForm] IFormFile File) { if (!await AclService.AuthorizeAsync(AclAction.UploadSoftware, User)) { return Forbid(); } byte[] Data; using (MemoryStream MemoryStream = new MemoryStream()) { using (System.IO.Stream Stream = File.OpenReadStream()) { await Stream.CopyToAsync(MemoryStream); } Data = MemoryStream.ToArray(); } await AgentSoftwareService.SetArchiveAsync(new AgentSoftwareChannelName(Name), User.Identity?.Name, Data); return Ok(); } /// /// Gets the zip file for a specific channel /// /// Name of the channel /// Http response [HttpGet] [Route("/api/v1/agentsoftware/{Name}/zip")] public async Task GetArchiveAsync(string Name) { if (!await AclService.AuthorizeAsync(AclAction.DownloadSoftware, User)) { return Forbid(); } byte[]? Data = await AgentSoftwareService.GetArchiveAsync(new AgentSoftwareChannelName(Name)); if (Data == null) { return NotFound(); } return new FileStreamResult(new MemoryStream(Data), new MediaTypeHeaderValue("application/octet-stream")) { FileDownloadName = $"HordeAgent.zip" }; } } }