From 8fa9d93b64daa97dd006390e61d759dad68594b6 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 13 Nov 2018 15:24:48 -0500 Subject: [PATCH] Add root routing to control panel --- .../ControlPanelConfiguration.cs | 2 +- .../Controllers/ApiController.cs | 19 +++++-- .../Controllers/HomeController.cs | 52 ++++++++++++++++--- .../Controllers/ModelController.cs | 2 +- src/Tgstation.Server.Host/Core/Application.cs | 3 ++ .../Tgstation.Server.Host.csproj | 17 ++---- 6 files changed, 68 insertions(+), 27 deletions(-) diff --git a/src/Tgstation.Server.Host/Configuration/ControlPanelConfiguration.cs b/src/Tgstation.Server.Host/Configuration/ControlPanelConfiguration.cs index 6873f0d27f..51387d7c3c 100644 --- a/src/Tgstation.Server.Host/Configuration/ControlPanelConfiguration.cs +++ b/src/Tgstation.Server.Host/Configuration/ControlPanelConfiguration.cs @@ -5,7 +5,7 @@ namespace Tgstation.Server.Host.Configuration /// /// Configuration options for the web control panel /// - sealed class ControlPanelConfiguration + public sealed class ControlPanelConfiguration { /// /// The key for the the resides in diff --git a/src/Tgstation.Server.Host/Controllers/ApiController.cs b/src/Tgstation.Server.Host/Controllers/ApiController.cs index 3b26db1875..1d6318d89d 100644 --- a/src/Tgstation.Server.Host/Controllers/ApiController.cs +++ b/src/Tgstation.Server.Host/Controllers/ApiController.cs @@ -51,6 +51,11 @@ namespace Tgstation.Server.Host.Controllers /// readonly bool requireInstance; + /// + /// If are required + /// + readonly bool requireHeaders; + /// /// Construct an /// @@ -58,7 +63,8 @@ namespace Tgstation.Server.Host.Controllers /// The for the /// The value of /// The value of - public ApiController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger logger, bool requireInstance) + /// The value of + public ApiController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger logger, bool requireInstance, bool requireHeaders) { DatabaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext)); if (authenticationContextFactory == null) @@ -67,6 +73,7 @@ namespace Tgstation.Server.Host.Controllers AuthenticationContext = authenticationContextFactory.CurrentAuthenticationContext; Instance = AuthenticationContext?.InstanceUser?.Instance; this.requireInstance = requireInstance; + this.requireHeaders = requireHeaders; } /// @@ -113,8 +120,11 @@ namespace Tgstation.Server.Host.Controllers } catch (InvalidOperationException e) { - await BadRequest(new ErrorMessage { Message = e.Message }).ExecuteResultAsync(context).ConfigureAwait(false); - return; + if (requireHeaders) + { + await BadRequest(new ErrorMessage { Message = e.Message }).ExecuteResultAsync(context).ConfigureAwait(false); + return; + } } if (ModelState?.IsValid == false) @@ -138,7 +148,8 @@ namespace Tgstation.Server.Host.Controllers } } - Logger.LogDebug("Request made by User ID {0}. Api version: {1}. User-Agent: {2}. Type: {3}. Route {4}{5} to Instance {6}", AuthenticationContext?.User.Id.ToString(CultureInfo.InvariantCulture), ApiHeaders.ApiVersion, ApiHeaders.UserAgent, Request.Method, Request.Path, Request.QueryString, ApiHeaders.InstanceId); + if (ApiHeaders != null) + Logger.LogDebug("Request made by User ID {0}. Api version: {1}. User-Agent: {2}. Type: {3}. Route {4}{5} to Instance {6}", AuthenticationContext?.User.Id.ToString(CultureInfo.InvariantCulture), ApiHeaders.ApiVersion, ApiHeaders.UserAgent, Request.Method, Request.Path, Request.QueryString, ApiHeaders.InstanceId); try { diff --git a/src/Tgstation.Server.Host/Controllers/HomeController.cs b/src/Tgstation.Server.Host/Controllers/HomeController.cs index 5b914af991..56d7a59e5a 100644 --- a/src/Tgstation.Server.Host/Controllers/HomeController.cs +++ b/src/Tgstation.Server.Host/Controllers/HomeController.cs @@ -1,14 +1,19 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using System; using System.Linq; +using System.Net.Mime; using System.Threading; using System.Threading.Tasks; using Tgstation.Server.Api; +using Tgstation.Server.Host.Configuration; using Tgstation.Server.Host.Core; using Tgstation.Server.Host.Models; using Tgstation.Server.Host.Security; +using Wangkanai.Detection; namespace Tgstation.Server.Host.Controllers { @@ -39,6 +44,16 @@ namespace Tgstation.Server.Host.Controllers /// readonly IIdentityCache identityCache; + /// + /// The for the + /// + readonly IBrowserResolver browserResolver; + + /// + /// The for the + /// + readonly ControlPanelConfiguration controlPanelConfiguration; + /// /// Construct a /// @@ -49,27 +64,45 @@ namespace Tgstation.Server.Host.Controllers /// The value of /// The value of /// The value of + /// The value of + /// The containing the value of /// The for the - public HomeController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ITokenFactory tokenFactory, ISystemIdentityFactory systemIdentityFactory, ICryptographySuite cryptographySuite, IApplication application, IIdentityCache identityCache, ILogger logger) : base(databaseContext, authenticationContextFactory, logger, false) + public HomeController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ITokenFactory tokenFactory, ISystemIdentityFactory systemIdentityFactory, ICryptographySuite cryptographySuite, IApplication application, IIdentityCache identityCache, IBrowserResolver browserResolver, IOptions controlPanelConfigurationOptions, ILogger logger) : base(databaseContext, authenticationContextFactory, logger, false, false) { this.tokenFactory = tokenFactory ?? throw new ArgumentNullException(nameof(tokenFactory)); this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory)); this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite)); this.application = application ?? throw new ArgumentNullException(nameof(application)); this.identityCache = identityCache ?? throw new ArgumentNullException(nameof(identityCache)); + this.browserResolver = browserResolver ?? throw new ArgumentNullException(nameof(browserResolver)); + controlPanelConfiguration = controlPanelConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(controlPanelConfigurationOptions)); } /// - /// Returns the version of the + /// Main page of the /// - /// + /// The of the if a properly authenticated API request, the web control panel if on a browser and enabled, otherwise [TgsAuthorize] + [AllowAnonymous] [HttpGet] - public JsonResult Home() => Json(new Api.Models.ServerInformation + public IActionResult Home(CancellationToken cancellationToken) { - Version = application.Version, - ApiVersion = ApiHeaders.Version - }); + if (AuthenticationContext != null) + return Json(new Api.Models.ServerInformation + { + Version = application.Version, + ApiVersion = ApiHeaders.Version + }); + + //if we are using a browser and the control panel, soft redirect to the app page + if (controlPanelConfiguration.Enable && browserResolver.Browser.Type != BrowserType.Generic) + { + Logger.LogDebug("Unauthorized browser request (User-Agent: \"{0}\"), loading control panel...", browserResolver.UserAgent); + return File("~/index.html", MediaTypeNames.Text.Html); + } + + return Unauthorized(); + } /// /// Attempt to authenticate a using @@ -79,6 +112,9 @@ namespace Tgstation.Server.Host.Controllers [HttpPost] public async Task CreateToken(CancellationToken cancellationToken) { + if (ApiHeaders == null) + return BadRequest(new Api.Models.ErrorMessage { Message = "Missing API headers!" }); + if (ApiHeaders.IsTokenAuthentication) return BadRequest(new Api.Models.ErrorMessage { Message = "Cannot create a token using another token!" }); diff --git a/src/Tgstation.Server.Host/Controllers/ModelController.cs b/src/Tgstation.Server.Host/Controllers/ModelController.cs index 4dc35d41c3..3434992040 100644 --- a/src/Tgstation.Server.Host/Controllers/ModelController.cs +++ b/src/Tgstation.Server.Host/Controllers/ModelController.cs @@ -20,7 +20,7 @@ namespace Tgstation.Server.Host.Controllers /// The for the /// The for the /// If the requires an - public ModelController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger logger, bool requireInstance) : base(databaseContext, authenticationContextFactory, logger, requireInstance) { } + public ModelController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger logger, bool requireInstance) : base(databaseContext, authenticationContextFactory, logger, requireInstance, true) { } /// /// Attempt to create a diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs index b3e3286712..57bfdf56d2 100644 --- a/src/Tgstation.Server.Host/Core/Application.cs +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -227,6 +227,9 @@ namespace Tgstation.Server.Host.Core options.SerializerSettings.Converters = new[] { new VersionConverter() }; }); + //enable browser detection + services.AddDetectionCore().AddBrowser(); + //enable CORS if necessary if (controlPanelConfiguration.AllowAnyOrigin || controlPanelConfiguration.AllowedOrigins?.Count > 0) services.AddCors(); diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index 34c5371901..4d79f98e84 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -26,9 +26,7 @@ ClientApp/node_modules/.install-stamp - + @@ -38,17 +36,11 @@ - - - + - + @@ -81,6 +73,7 @@ + @@ -107,8 +100,6 @@ - -