// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Threading.Tasks; using EpicGames.Core; using EpicGames.Horde; using EpicGames.Horde.Server; using HordeServer.Plugins; using HordeServer.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace HordeServer.Server { /// /// Controller managing account status /// [ApiController] [Authorize] [CppApi] [Route("[controller]")] public class ServerController : HordeControllerBase { readonly IAgentVersionProvider? _agentVersionProvider; readonly IPluginCollection _pluginCollection; readonly IOptionsSnapshot _globalConfig; readonly ILogger _logger; /// /// Constructor /// public ServerController(IEnumerable agentVersionProviders, IPluginCollection pluginCollection, IOptionsSnapshot globalConfig, ILogger logger) { _agentVersionProvider = agentVersionProviders.FirstOrDefault(); _pluginCollection = pluginCollection; _globalConfig = globalConfig; _logger = logger; } /// /// Get server version /// [HttpGet] [AllowAnonymous] [Route("/api/v1/server/version")] public ActionResult GetVersion() { FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location); return Ok(fileVersionInfo.ProductVersion); } /// /// Get server information /// [HttpGet] [AllowAnonymous] [Route("/api/v1/server/info")] [ProducesResponseType(typeof(GetServerInfoResponse), 200)] public async Task> GetServerInfoAsync() { GetServerInfoResponse response = new GetServerInfoResponse(); response.ApiVersion = HordeApiVersion.Latest; FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location); response.ServerVersion = versionInfo.ProductVersion ?? String.Empty; if (_agentVersionProvider != null) { response.AgentVersion = await _agentVersionProvider.GetAsync(HttpContext.RequestAborted); } response.Plugins = _pluginCollection.LoadedPlugins.ConvertAll(plugin => { FileVersionInfo pluginVersion = FileVersionInfo.GetVersionInfo(plugin.Assembly.Location); return new ServerPluginInfoResponse(plugin.Metadata.Name.ToString(), plugin.Metadata.Description, true, pluginVersion.ProductVersion ?? String.Empty); }).ToArray(); return response; } /// /// Gets connection information /// [HttpGet] [AllowAnonymous] [Route("/api/v1/server/connection")] public ActionResult GetConnection() { GetConnectionResponse response = new GetConnectionResponse(); response.Ip = HttpContext.Connection.RemoteIpAddress?.ToString(); response.Port = HttpContext.Connection.RemotePort; return response; } /// /// Gets ports used by the server /// [HttpGet] [AllowAnonymous] [Route("/api/v1/server/ports")] public ActionResult GetPorts() { ServerSettings serverSettings = _globalConfig.Value.ServerSettings; GetPortsResponse response = new GetPortsResponse(); response.Http = serverSettings.HttpPort; response.Https = serverSettings.HttpsPort; response.UnencryptedHttp2 = serverSettings.Http2Port; return response; } private const string ServerAuthRoute = "/api/v1/server/auth"; /// /// Returns settings for automating auth against this server /// [HttpGet] [AllowAnonymous] [Route(ServerAuthRoute)] public ActionResult GetAuthConfig() { ServerSettings settings = _globalConfig.Value.ServerSettings; GetAuthConfigResponse response = new GetAuthConfigResponse(); response.Method = settings.AuthMethod; response.ProfileName = settings.OidcProfileName; if (settings.AuthMethod == AuthMethod.Horde) { response.ServerUrl = new Uri(_globalConfig.Value.ServerSettings.ServerUrl, "api/v1/oauth2").ToString(); response.ClientId = "default"; } else { if (!String.IsNullOrEmpty(settings.OidcClientSecret)) { _logger.LogWarning( "OIDC config mismatch: Command-line auth requires a public OAuth/OIDC client, but a confidential client is configured ({ClientSecret} is set). " + "This will prevent Horde's C# client from signing in and block usage of Unreal Build Accelerator. " + "To fix: Configure your OAuth/OIDC client as public (SPA/mobile/desktop) and remove the client secret", nameof(ServerSettings.OidcClientSecret) ); } response.ServerUrl = settings.OidcAuthority; response.ClientId = settings.OidcClientId; response.Scopes = settings.OidcApiRequestedScopes; } response.LocalRedirectUrls = settings.OidcLocalRedirectUrls; return response; } } }