// 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 { /// /// Controller for the /api/v1/software endpoint /// [ApiController] [Authorize] [Route("[controller]")] public class SoftwareController : ControllerBase { /// /// Singleton instance of the ACL service /// private readonly AclService AclService; /// /// Singleton instance of the client service /// private readonly SoftwareService SoftwareService; /// /// Constructor /// /// The ACL service /// The client service public SoftwareController(AclService AclService, SoftwareService SoftwareService) { this.AclService = AclService; this.SoftwareService = SoftwareService; } /// /// Uploads a new agent zip file /// /// Zip archive containing the new client software /// Whether the client should immediately be made the default /// Http result code [HttpPost] [Route("/api/v1/software")] public async Task> CreateSoftwareAsync([FromForm] IFormFile File, [FromForm] bool Default = false) { if (!await AclService.AuthorizeAsync(AclAction.UploadSoftware, User)) { return Forbid(); } ObjectId Id; using (System.IO.Stream Stream = File.OpenReadStream()) { Id = await SoftwareService.CreateSoftwareAsync(Stream, User.Identity.Name, Default); } return new CreateSoftwareResponse(Id.ToString()); } /// /// Finds all uploaded software matching the given criteria /// /// The user that created the software /// The user that made it the default /// Whether the software was made the default /// Index of the first result to return /// Number of results to return /// Filter for the properties to return /// Http response [HttpGet] [Route("/api/v1/software")] [ProducesResponseType(typeof(List), 200)] public async Task>> FindSoftwareAsync([FromQuery] string? CreatedByUser = null, [FromQuery] string? MadeDefaultByUser = null, [FromQuery] bool? MadeDefault = null, [FromQuery] PropertyFilter? Filter = null, [FromQuery] int Offset = 0, [FromQuery] int Count = 200) { if (!await AclService.AuthorizeAsync(AclAction.DownloadSoftware, User)) { return Forbid(); } List Results = await SoftwareService.FindSoftwareAsync(CreatedByUser, MadeDefaultByUser, MadeDefault, Offset, Count); List Responses = new List(); foreach (ISoftware Result in Results) { Responses.Add(new GetSoftwareResponse(Result).ApplyFilter(Filter)); } return Responses; } /// /// Gets information about a specific client revision /// /// Unique id of the client software /// Filter for the properties to return /// Http response [HttpGet] [Route("/api/v1/software/{Id}")] [ProducesResponseType(typeof(GetSoftwareResponse), 200)] public async Task> GetSoftwareAsync(string Id, [FromQuery] PropertyFilter? Filter = null) { if (!await AclService.AuthorizeAsync(AclAction.DownloadSoftware, User)) { return Forbid(); } ISoftware? Client = await GetSoftwareInternalAsync(Id, false); if (Client == null) { return NotFound(); } return new GetSoftwareResponse(Client).ApplyFilter(Filter); } /// /// Gets the zip file for a specific client revision /// /// Unique id of the client /// Http response [HttpGet] [Route("/api/v1/software/{Id}/download")] public async Task> DownloadSoftwareAsync(string Id) { if (!await AclService.AuthorizeAsync(AclAction.DownloadSoftware, User)) { return Forbid(); } ISoftware? Software = await GetSoftwareInternalAsync(Id, true); if (Software == null) { return NotFound(); } return new FileStreamResult(new MemoryStream(Software.Data!), new MediaTypeHeaderValue("application/octet-stream")); } /// /// Find the software matching the given identifier /// /// The identifier to match. May be an object id or "latest" /// Whether to include data in the response /// Software interface async Task GetSoftwareInternalAsync(string Id, bool bIncludeData) { if (Id.Equals("latest", StringComparison.OrdinalIgnoreCase)) { return await SoftwareService.GetDefaultSoftwareAsync(bIncludeData); } else { return await SoftwareService.GetSoftwareAsync(Id.ToObjectId(), bIncludeData); } } /// /// Deletes a version of the client /// /// Unique id of the client to delete /// Http response [HttpDelete] [Route("/api/v1/software/{Id}")] public async Task DeleteSoftwareAsync(string Id) { if (!await AclService.AuthorizeAsync(AclAction.DeleteSoftware, User)) { return Forbid(); } await SoftwareService.DeleteSoftwareAsync(Id.ToObjectId()); return new OkResult(); } } }