From f9ba711bc2b336fd614d4bf762dbe019f1ff3931 Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Mon, 7 May 2018 14:11:00 -0400 Subject: [PATCH] Heavy WIP --- .../Internal/DreamDaemonLaunchParameters.cs | 15 ++- .../Models/Internal/DreamDaemonSettings.cs | 12 +- .../Components/IDreamDaemon.cs | 4 + .../Controllers/DreamDaemonController.cs | 121 ++++++++++++++++++ .../Controllers/DreamMakerController.cs | 7 +- .../Controllers/JobController.cs | 14 +- .../Controllers/ModelController.cs | 8 ++ 7 files changed, 168 insertions(+), 13 deletions(-) create mode 100644 src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs diff --git a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs index 8602b302b5..62e0f7eb87 100644 --- a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs +++ b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs @@ -1,4 +1,5 @@ -using Tgstation.Server.Api.Rights; +using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Api.Rights; namespace Tgstation.Server.Api.Models.Internal { @@ -12,24 +13,28 @@ namespace Tgstation.Server.Api.Models.Internal /// If the BYOND web client can be used to connect to the game server /// [Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SetWebClient)] - public bool AllowWebClient { get; set; } + [Required] + public bool? AllowWebClient { get; set; } /// /// The level of /// [Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SetSecurity)] - public DreamDaemonSecurity SecurityLevel { get; set; } + [Required] + public DreamDaemonSecurity? SecurityLevel { get; set; } /// /// The first port uses. This should be the publically advertised port /// [Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SetPorts)] - public ushort PrimaryPort { get; set; } + [Required] + public ushort? PrimaryPort { get; set; } /// /// The second port uses /// [Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SetPorts)] - public ushort SecondaryPort { get; set; } + [Required] + public ushort? SecondaryPort { get; set; } } } \ No newline at end of file diff --git a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonSettings.cs b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonSettings.cs index b4fd279881..72260ee5fc 100644 --- a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonSettings.cs +++ b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonSettings.cs @@ -1,4 +1,5 @@ -using Tgstation.Server.Api.Rights; +using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Api.Rights; namespace Tgstation.Server.Api.Models.Internal { @@ -11,18 +12,21 @@ namespace Tgstation.Server.Api.Models.Internal /// If starts when it's starts /// [Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SetAutoStart)] - public bool AutoStart { get; set; } + [Required] + public bool? AutoStart { get; set; } /// /// If the server is undergoing a soft reset. This may be automatically set by changes to other fields /// [Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SoftRestart)] - public bool SoftRestart { get; set; } + [Required] + public bool? SoftRestart { get; set; } /// /// If the server is undergoing a soft shutdown /// [Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SoftShutdown)] - public bool SoftShutdown { get; set; } + [Required] + public bool? SoftShutdown { get; set; } } } diff --git a/src/Tgstation.Server.Host/Components/IDreamDaemon.cs b/src/Tgstation.Server.Host/Components/IDreamDaemon.cs index dbd32b9d2a..ae7dee5038 100644 --- a/src/Tgstation.Server.Host/Components/IDreamDaemon.cs +++ b/src/Tgstation.Server.Host/Components/IDreamDaemon.cs @@ -41,6 +41,10 @@ namespace Tgstation.Server.Host.Components /// string AccessToken { get; } + DreamDaemonLaunchParameters LastLaunchParameters { get; } + + Host.Models.CompileJob LastCompileJob { get; } + /// /// Launch DreamDaemon /// diff --git a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs new file mode 100644 index 0000000000..d8f082abb6 --- /dev/null +++ b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs @@ -0,0 +1,121 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.Components; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.Models; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Controllers +{ + [Route("/" + nameof(DreamDaemon))] + public sealed class DreamDaemonController : ModelController + { + /// + /// The for the + /// + readonly IJobManager jobManager; + /// + /// The for the + /// + readonly IInstanceManager instanceManager; + + /// + /// Construct a + /// + /// The for the + /// The for the + public DreamDaemonController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager) : base(databaseContext, authenticationContextFactory) + { + this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager)); + this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager)); + } + + /// + [TgsAuthorize(DreamDaemonRights.Start)] + public override async Task Create([FromBody] Api.Models.DreamDaemon model, CancellationToken cancellationToken) + { + var instance = instanceManager.GetInstance(Instance); + + if (instance.DreamDaemon.Running) + return StatusCode(HttpStatusCode.Gone); + + var launchParams = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Select(x => new DreamDaemonLaunchParameters + { + AllowWebClient = x.DreamDaemonSettings.AllowWebClient, + PrimaryPort = x.DreamDaemonSettings.PrimaryPort, + SecondaryPort = x.DreamDaemonSettings.SecondaryPort, + SecurityLevel = x.DreamDaemonSettings.SecurityLevel + }).FirstAsync(cancellationToken).ConfigureAwait(false); + + await jobManager.RegisterOperation(new Models.Job + { + Description = "Launch DreamDaemon", + CancelRight = (int)DreamDaemonRights.Shutdown, + CancelRightsType = RightsType.DreamDaemon, + Instance = Instance, + StartedBy = AuthenticationContext.User + }, (job, serviceProvider, innerCt) => instance.DreamDaemon.Launch(launchParams, innerCt), cancellationToken).ConfigureAwait(false); + return Ok(); + } + + /// + [TgsAuthorize(DreamDaemonRights.ReadMetadata | DreamDaemonRights.ReadRevision)] + public override async Task Read(CancellationToken cancellationToken) + { + var dd = instanceManager.GetInstance(Instance).DreamDaemon; + + var metadata = (AuthenticationContext.GetRight(RightsType.DreamDaemon) & (int)DreamDaemonRights.ReadMetadata) != 0; + var revision = (AuthenticationContext.GetRight(RightsType.DreamDaemon) & (int)DreamDaemonRights.ReadRevision) != 0; + + var settings = metadata ? await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Select(x => x.DreamDaemonSettings).FirstAsync(cancellationToken).ConfigureAwait(false) : null; + Api.Models.DreamDaemon result = new Api.Models.DreamDaemon(); + if(metadata) + { + result.AutoStart = settings.AutoStart; + result.CurrentPort = dd.CurrentPort; + result.CurrentSecurity = dd.CurrentSecurity; + result.PrimaryPort = dd.LastLaunchParameters.PrimaryPort; + result.AllowWebClient = dd.LastLaunchParameters.AllowWebClient; + result.Running = dd.Running; + result.SecondaryPort = dd.LastLaunchParameters.SecondaryPort; + result.SecurityLevel = dd.LastLaunchParameters.SecurityLevel; + result.SoftRestart = dd.SoftRebooting; + result.SoftShutdown = dd.SoftStopping; + }; + if (revision) + result.CompileJob = dd.LastCompileJob.ToApi(); + + return Json(result); + } + + /// + [TgsAuthorize(DreamDaemonRights.Shutdown)] + public override async Task Delete([FromBody] Api.Models.DreamDaemon model, CancellationToken cancellationToken) + { + var instance = instanceManager.GetInstance(Instance); + + if (!instance.DreamDaemon.Running) + return StatusCode(HttpStatusCode.Gone); + + await instance.DreamDaemon.Terminate(false, cancellationToken).ConfigureAwait(false); + return Ok(); + } + + [TgsAuthorize(DreamDaemonRights.SetAutoStart | DreamDaemonRights.SetPorts | DreamDaemonRights.SetSecurity | DreamDaemonRights.SetWebClient | DreamDaemonRights.SoftRestart | DreamDaemonRights.SoftShutdown)] + public override async Task Update([FromBody] Api.Models.DreamDaemon model, CancellationToken cancellationToken) + { + var current = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Select(x => x.DreamDaemonSettings).FirstAsync(cancellationToken).ConfigureAwait(false); + + var userRights = (DreamDaemonRights)AuthenticationContext.GetRight(RightsType.DreamDaemon) + if() + } + } +} diff --git a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs index f94a51537e..c13cb31e11 100644 --- a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs +++ b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs @@ -17,7 +17,7 @@ namespace Tgstation.Server.Host.Controllers /// /// Controller for managing the compiler /// - [Route("/DreamMaker")] + [Route("/" + nameof(DreamMaker))] public sealed class DreamMakerController : ModelController { /// @@ -30,7 +30,7 @@ namespace Tgstation.Server.Host.Controllers readonly IInstanceManager instanceManager; /// - /// Construct a + /// Construct a /// /// The for the /// The for the @@ -66,7 +66,8 @@ namespace Tgstation.Server.Host.Controllers Description = "Compile active repository code", StartedBy = AuthenticationContext.User, CancelRightsType = RightsType.DreamMaker, - CancelRight = (int)DreamMakerRights.CancelCompile + CancelRight = (int)DreamMakerRights.CancelCompile, + Instance = Instance }; await jobManager.RegisterOperation(job, (paramJob, serviceProvider, ct) => RunCompile(paramJob, serviceProvider, Instance, ct), cancellationToken).ConfigureAwait(false); return Json(job); diff --git a/src/Tgstation.Server.Host/Controllers/JobController.cs b/src/Tgstation.Server.Host/Controllers/JobController.cs index 982fc72cd8..4be80670a2 100644 --- a/src/Tgstation.Server.Host/Controllers/JobController.cs +++ b/src/Tgstation.Server.Host/Controllers/JobController.cs @@ -14,7 +14,7 @@ namespace Tgstation.Server.Host.Controllers /// /// for s /// - [Route("/Job")] + [Route("/" + nameof(Job))] public sealed class JobController : ModelController { /// @@ -60,11 +60,23 @@ namespace Tgstation.Server.Host.Controllers if (job == default(Job)) return NotFound(); + if (job.CancelRight.HasValue && job.CancelRightsType.HasValue && (AuthenticationContext.GetRight(job.CancelRightsType.Value) & job.CancelRight.Value) == 0) + return Forbid(); + if(job.StoppedAt != null) return StatusCode(HttpStatusCode.Gone); await jobManager.CancelJob(job, AuthenticationContext.User, cancellationToken).ConfigureAwait(false); return Ok(); } + + [TgsAuthorize] + public override async Task GetId(long id, CancellationToken cancellationToken) + { + var job = await DatabaseContext.Jobs.Where(x => x.Id == id).Include(x => x.StartedBy).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + if (job == default(Job)) + return NotFound(); + return Json(job.ToApi()); + } } } diff --git a/src/Tgstation.Server.Host/Controllers/ModelController.cs b/src/Tgstation.Server.Host/Controllers/ModelController.cs index 59d2b5c19a..54d01677da 100644 --- a/src/Tgstation.Server.Host/Controllers/ModelController.cs +++ b/src/Tgstation.Server.Host/Controllers/ModelController.cs @@ -43,6 +43,14 @@ namespace Tgstation.Server.Host.Controllers [HttpGet] public virtual Task Read(CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound()); + /// + /// Attempt to get a specific a + /// + /// The for the operation + /// A resulting in the of the operation + [HttpGet("/{0}")] + public virtual Task GetId(long id, CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound()); + /// /// Attempt to update a ///