tgstation-server 6.9.2
The /tg/station 13 server suite
Loading...
Searching...
No Matches
Application.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Frozen;
3using System.Collections.Generic;
4using System.Globalization;
5using System.Threading.Tasks;
6
7using Cyberboss.AspNetCore.AsyncInitializer;
8
9using Elastic.CommonSchema.Serilog;
10
11using Microsoft.AspNetCore.Authentication;
12using Microsoft.AspNetCore.Authentication.JwtBearer;
13using Microsoft.AspNetCore.Builder;
14using Microsoft.AspNetCore.Cors.Infrastructure;
15using Microsoft.AspNetCore.Hosting;
16using Microsoft.AspNetCore.Http;
17using Microsoft.AspNetCore.Http.Connections;
18using Microsoft.AspNetCore.Identity;
19using Microsoft.AspNetCore.Mvc.Infrastructure;
20using Microsoft.AspNetCore.SignalR;
21using Microsoft.Extensions.Configuration;
22using Microsoft.Extensions.DependencyInjection;
23using Microsoft.Extensions.Hosting;
24using Microsoft.Extensions.Logging;
25using Microsoft.Extensions.Options;
26
27using Newtonsoft.Json;
28
29using Serilog;
30using Serilog.Events;
31using Serilog.Formatting.Display;
32using Serilog.Sinks.Elasticsearch;
33
64
66{
70#pragma warning disable CA1506
71 public sealed class Application : SetupApplication
72 {
76 readonly IWebHostEnvironment hostingEnvironment;
77
82
88 {
89 var assemblyInformationProvider = new AssemblyInformationProvider();
90 var ioManager = new DefaultIOManager();
91 return new ServerFactory(
92 assemblyInformationProvider,
93 ioManager);
94 }
95
102 static void AddWatchdog<TSystemWatchdogFactory>(IServiceCollection services, IPostSetupServices postSetupServices)
103 where TSystemWatchdogFactory : class, IWatchdogFactory
104 {
105 if (postSetupServices.GeneralConfiguration.UseBasicWatchdog)
106 services.AddSingleton<IWatchdogFactory, WatchdogFactory>();
107 else
108 services.AddSingleton<IWatchdogFactory, TSystemWatchdogFactory>();
109 }
110
117 IConfiguration configuration,
118 IWebHostEnvironment hostingEnvironment)
119 : base(configuration)
120 {
121 this.hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
122 }
123
131 public void ConfigureServices(
132 IServiceCollection services,
133 IAssemblyInformationProvider assemblyInformationProvider,
134 IIOManager ioManager,
135 IPostSetupServices postSetupServices)
136 {
137 ConfigureServices(services, assemblyInformationProvider, ioManager);
138
139 ArgumentNullException.ThrowIfNull(postSetupServices);
140
141 // configure configuration
142 services.UseStandardConfig<UpdatesConfiguration>(Configuration);
143 services.UseStandardConfig<ControlPanelConfiguration>(Configuration);
144 services.UseStandardConfig<SwarmConfiguration>(Configuration);
145 services.UseStandardConfig<SessionConfiguration>(Configuration);
146 services.UseStandardConfig<TelemetryConfiguration>(Configuration);
147
148 // enable options which give us config reloading
149 services.AddOptions();
150
151 // Set the timeout for IHostedService.StopAsync
152 services.Configure<HostOptions>(
153 opts => opts.ShutdownTimeout = TimeSpan.FromMinutes(postSetupServices.GeneralConfiguration.RestartTimeoutMinutes));
154
155 static LogEventLevel? ConvertSeriLogLevel(LogLevel logLevel) =>
156 logLevel switch
157 {
158 LogLevel.Critical => LogEventLevel.Fatal,
159 LogLevel.Debug => LogEventLevel.Debug,
160 LogLevel.Error => LogEventLevel.Error,
161 LogLevel.Information => LogEventLevel.Information,
162 LogLevel.Trace => LogEventLevel.Verbose,
163 LogLevel.Warning => LogEventLevel.Warning,
164 LogLevel.None => null,
165 _ => throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid log level {0}", logLevel)),
166 };
167
168 var microsoftEventLevel = ConvertSeriLogLevel(postSetupServices.FileLoggingConfiguration.MicrosoftLogLevel);
169 var elasticsearchConfiguration = postSetupServices.ElasticsearchConfiguration;
170 services.SetupLogging(
171 config =>
172 {
173 if (microsoftEventLevel.HasValue)
174 {
175 config.MinimumLevel.Override("Microsoft", microsoftEventLevel.Value);
176 config.MinimumLevel.Override("System.Net.Http.HttpClient", microsoftEventLevel.Value);
177 }
178 },
179 sinkConfig =>
180 {
181 if (postSetupServices.FileLoggingConfiguration.Disable)
182 return;
183
184 var logPath = postSetupServices.FileLoggingConfiguration.GetFullLogDirectory(
185 ioManager,
186 assemblyInformationProvider,
187 postSetupServices.PlatformIdentifier);
188
189 var logEventLevel = ConvertSeriLogLevel(postSetupServices.FileLoggingConfiguration.LogLevel);
190
191 var formatter = new MessageTemplateTextFormatter(
192 "{Timestamp:o} "
194 + "): [{Level:u3}] {SourceContext:l}: {Message} ({EventId:x8}){NewLine}{Exception}",
195 null);
196
197 logPath = ioManager.ConcatPath(logPath, "tgs-.log");
198 var rollingFileConfig = sinkConfig.File(
199 formatter,
200 logPath,
201 logEventLevel ?? LogEventLevel.Verbose,
202 50 * 1024 * 1024, // 50MB max size
203 flushToDiskInterval: TimeSpan.FromSeconds(2),
204 rollingInterval: RollingInterval.Day,
205 rollOnFileSizeLimit: true);
206 },
207 elasticsearchConfiguration.Enable
208 ? new ElasticsearchSinkOptions(elasticsearchConfiguration.Host ?? throw new InvalidOperationException($"Missing {ElasticsearchConfiguration.Section}:{nameof(elasticsearchConfiguration.Host)}!"))
209 {
210 // Yes I know this means they cannot use a self signed cert unless they also have authentication, but lets be real here
211 // No one is going to be doing one of those but not the other
212 ModifyConnectionSettings = connectionConfigration => (!String.IsNullOrWhiteSpace(elasticsearchConfiguration.Username) && !String.IsNullOrWhiteSpace(elasticsearchConfiguration.Password))
213 ? connectionConfigration
214 .BasicAuthentication(
215 elasticsearchConfiguration.Username,
216 elasticsearchConfiguration.Password)
217 .ServerCertificateValidationCallback((o, certificate, chain, errors) => true)
218 : null,
219 CustomFormatter = new EcsTextFormatter(),
220 AutoRegisterTemplate = true,
221 AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv7,
222 IndexFormat = "tgs-logs",
223 }
224 : null,
225 postSetupServices.InternalConfiguration,
226 postSetupServices.FileLoggingConfiguration);
227
228 // configure authentication pipeline
230
231 // add mvc, configure the json serializer settings
232 var jsonVersionConverterList = new List<JsonConverter>
233 {
234 new VersionConverter(),
235 };
236
237 void ConfigureNewtonsoftJsonSerializerSettingsForApi(JsonSerializerSettings settings)
238 {
239 settings.NullValueHandling = NullValueHandling.Ignore;
240 settings.CheckAdditionalContent = true;
241 settings.MissingMemberHandling = MissingMemberHandling.Error;
242 settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
243 settings.Converters = jsonVersionConverterList;
244 }
245
246 services
247 .AddMvc(options =>
248 {
249 options.ReturnHttpNotAcceptable = true;
250 options.RespectBrowserAcceptHeader = true;
251 })
252 .AddNewtonsoftJson(options =>
253 {
254 options.AllowInputFormatterExceptionMessages = true;
255 ConfigureNewtonsoftJsonSerializerSettingsForApi(options.SerializerSettings);
256 });
257
258 services.AddSignalR(
259 options =>
260 {
261 options.AddFilter<AuthorizationContextHubFilter>();
262 })
263 .AddNewtonsoftJsonProtocol(options =>
264 {
265 ConfigureNewtonsoftJsonSerializerSettingsForApi(options.PayloadSerializerSettings);
266 });
267
268 services.AddHub<JobsHub, IJobsHub>();
269
270 if (postSetupServices.GeneralConfiguration.HostApiDocumentation)
271 {
272 string GetDocumentationFilePath(string assemblyLocation) => ioManager.ConcatPath(ioManager.GetDirectoryName(assemblyLocation), String.Concat(ioManager.GetFileNameWithoutExtension(assemblyLocation), ".xml"));
273 var assemblyDocumentationPath = GetDocumentationFilePath(GetType().Assembly.Location);
274 var apiDocumentationPath = GetDocumentationFilePath(typeof(ApiHeaders).Assembly.Location);
275 services.AddSwaggerGen(genOptions => SwaggerConfiguration.Configure(genOptions, assemblyDocumentationPath, apiDocumentationPath));
276 services.AddSwaggerGenNewtonsoftSupport();
277 }
278
279 // CORS conditionally enabled later
280 services.AddCors();
281
282 // Enable managed HTTP clients
283 services.AddHttpClient();
285
286 void AddTypedContext<TContext>()
287 where TContext : DatabaseContext
288 {
289 var configureAction = DatabaseContext.GetConfigureAction<TContext>();
290
291 services.AddDbContextPool<TContext>((serviceProvider, builder) =>
292 {
293 if (hostingEnvironment.IsDevelopment())
294 builder.EnableSensitiveDataLogging();
295
296 var databaseConfigOptions = serviceProvider.GetRequiredService<IOptions<DatabaseConfiguration>>();
297 var databaseConfig = databaseConfigOptions.Value ?? throw new InvalidOperationException("DatabaseConfiguration missing!");
298 configureAction(builder, databaseConfig);
299 });
300 services.AddScoped<IDatabaseContext>(x => x.GetRequiredService<TContext>());
301 }
302
303 // add the correct database context type
304 var dbType = postSetupServices.DatabaseConfiguration.DatabaseType;
305 switch (dbType)
306 {
307 case DatabaseType.MySql:
308 case DatabaseType.MariaDB:
309 AddTypedContext<MySqlDatabaseContext>();
310 break;
311 case DatabaseType.SqlServer:
312 AddTypedContext<SqlServerDatabaseContext>();
313 break;
314 case DatabaseType.Sqlite:
315 AddTypedContext<SqliteDatabaseContext>();
316 break;
317 case DatabaseType.PostgresSql:
318 AddTypedContext<PostgresSqlDatabaseContext>();
319 break;
320 default:
321 throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid {0}: {1}!", nameof(DatabaseType), dbType));
322 }
323
324 // configure other database services
325 services.AddSingleton<IDatabaseContextFactory, DatabaseContextFactory>();
326 services.AddSingleton<IDatabaseSeeder, DatabaseSeeder>();
327
328 // configure other security services
329 services.AddSingleton<IOAuthProviders, OAuthProviders>();
330 services.AddSingleton<IIdentityCache, IdentityCache>();
331 services.AddSingleton<ICryptographySuite, CryptographySuite>();
332 services.AddSingleton<ITokenFactory, TokenFactory>();
333 services.AddSingleton<IPasswordHasher<Models.User>, PasswordHasher<Models.User>>();
334
335 // configure platform specific services
336 if (postSetupServices.PlatformIdentifier.IsWindows)
337 {
338 AddWatchdog<WindowsWatchdogFactory>(services, postSetupServices);
341 services.AddSingleton<ByondInstallerBase, WindowsByondInstaller>();
342 services.AddSingleton<OpenDreamInstaller, WindowsOpenDreamInstaller>();
343 services.AddSingleton<IPostWriteHandler, WindowsPostWriteHandler>();
344 services.AddSingleton<IProcessFeatures, WindowsProcessFeatures>();
345
346 services.AddSingleton<WindowsNetworkPromptReaper>();
347 services.AddSingleton<INetworkPromptReaper>(x => x.GetRequiredService<WindowsNetworkPromptReaper>());
348 services.AddSingleton<IHostedService>(x => x.GetRequiredService<WindowsNetworkPromptReaper>());
349 }
350 else
351 {
352 AddWatchdog<PosixWatchdogFactory>(services, postSetupServices);
353 services.AddSingleton<ISystemIdentityFactory, PosixSystemIdentityFactory>();
354 services.AddSingleton<IFilesystemLinkFactory, PosixFilesystemLinkFactory>();
355 services.AddSingleton<ByondInstallerBase, PosixByondInstaller>();
356 services.AddSingleton<OpenDreamInstaller>();
357 services.AddSingleton<IPostWriteHandler, PosixPostWriteHandler>();
358
359 services.AddSingleton<IProcessFeatures, PosixProcessFeatures>();
360 services.AddHostedService<PosixProcessFeatures>();
361
362 // PosixProcessFeatures also needs a IProcessExecutor for gcore
363 services.AddSingleton(x => new Lazy<IProcessExecutor>(() => x.GetRequiredService<IProcessExecutor>(), true));
364 services.AddSingleton<INetworkPromptReaper, PosixNetworkPromptReaper>();
365
366 services.AddHostedService<PosixSignalHandler>();
367 }
368
369 // only global repo manager should be for the OD repo
370 // god help me if we need more
371 var openDreamRepositoryDirectory = ioManager.ConcatPath(
372 ioManager.GetPathInLocalDirectory(assemblyInformationProvider),
373 "OpenDreamRepository");
374 services.AddSingleton(
375 services => services
376 .GetRequiredService<IRepositoryManagerFactory>()
377 .CreateRepositoryManager(
379 services.GetRequiredService<IIOManager>(),
380 openDreamRepositoryDirectory),
381 new NoopEventConsumer()));
382
383 services.AddSingleton(
384 serviceProvider => new Dictionary<EngineType, IEngineInstaller>
385 {
386 { EngineType.Byond, serviceProvider.GetRequiredService<ByondInstallerBase>() },
387 { EngineType.OpenDream, serviceProvider.GetRequiredService<OpenDreamInstaller>() },
388 }
389 .ToFrozenDictionary());
390 services.AddSingleton<IEngineInstaller, DelegatingEngineInstaller>();
391
392 if (postSetupServices.InternalConfiguration.UsingSystemD)
393 services.AddHostedService<SystemDManager>();
394
395 // configure file transfer services
396 services.AddSingleton<FileTransferService>();
397 services.AddSingleton<IFileTransferStreamHandler>(x => x.GetRequiredService<FileTransferService>());
398 services.AddSingleton<IFileTransferTicketProvider>(x => x.GetRequiredService<FileTransferService>());
400
401 // configure swarm service
402 services.AddSingleton<SwarmService>();
403 services.AddSingleton<ISwarmService>(x => x.GetRequiredService<SwarmService>());
404 services.AddSingleton<ISwarmOperations>(x => x.GetRequiredService<SwarmService>());
405 services.AddSingleton<ISwarmServiceController>(x => x.GetRequiredService<SwarmService>());
406
407 // configure component services
408 services.AddSingleton<IPortAllocator, PortAllocator>();
409 services.AddSingleton<IInstanceFactory, InstanceFactory>();
410 services.AddSingleton<IGitRemoteFeaturesFactory, GitRemoteFeaturesFactory>();
411 services.AddSingleton<ILibGit2RepositoryFactory, LibGit2RepositoryFactory>();
412 services.AddSingleton<ILibGit2Commands, LibGit2Commands>();
413 services.AddSingleton<IRepositoryManagerFactory, RepostoryManagerFactory>();
415 services.AddChatProviderFactory();
416 services.AddSingleton<IChatManagerFactory, ChatManagerFactory>();
417 services.AddSingleton<IServerUpdater, ServerUpdater>();
418 services.AddSingleton<IServerUpdateInitiator, ServerUpdateInitiator>();
419 services.AddSingleton<IDotnetDumpService, DotnetDumpService>();
420
421 // configure misc services
422 services.AddSingleton<IProcessExecutor, ProcessExecutor>();
423 services.AddSingleton<ISynchronousIOManager, SynchronousIOManager>();
424 services.AddSingleton<IServerPortProvider, ServerPortProivder>();
425 services.AddSingleton<ITopicClientFactory, TopicClientFactory>();
426 services.AddHostedService<CommandPipeManager>();
427 services.AddHostedService<VersionReportingService>();
428
429 services.AddFileDownloader();
430 services.AddGitHub();
431
432 // configure root services
433 services.AddSingleton<JobService>();
434 services.AddSingleton<IJobService>(provider => provider.GetRequiredService<JobService>());
435 services.AddSingleton<IJobsHubUpdater>(provider => provider.GetRequiredService<JobService>());
436 services.AddSingleton<IJobManager>(x => x.GetRequiredService<IJobService>());
437 services.AddSingleton<JobsHubGroupMapper>();
438 services.AddSingleton<IPermissionsUpdateNotifyee>(provider => provider.GetRequiredService<JobsHubGroupMapper>());
439 services.AddSingleton<IHostedService>(x => x.GetRequiredService<JobsHubGroupMapper>()); // bit of a hack, but we need this to load immediated
440
441 services.AddSingleton<InstanceManager>();
442 services.AddSingleton<IBridgeDispatcher>(x => x.GetRequiredService<InstanceManager>());
443 services.AddSingleton<IInstanceManager>(x => x.GetRequiredService<InstanceManager>());
444 }
445
458 public void Configure(
459 IApplicationBuilder applicationBuilder,
460 IServerControl serverControl,
462 IServerPortProvider serverPortProvider,
463 IAssemblyInformationProvider assemblyInformationProvider,
464 IOptions<ControlPanelConfiguration> controlPanelConfigurationOptions,
465 IOptions<GeneralConfiguration> generalConfigurationOptions,
466 IOptions<SwarmConfiguration> swarmConfigurationOptions,
467 ILogger<Application> logger)
468 {
469 ArgumentNullException.ThrowIfNull(applicationBuilder);
470 ArgumentNullException.ThrowIfNull(serverControl);
471
472 this.tokenFactory = tokenFactory ?? throw new ArgumentNullException(nameof(tokenFactory));
473
474 ArgumentNullException.ThrowIfNull(serverPortProvider);
475 ArgumentNullException.ThrowIfNull(assemblyInformationProvider);
476
477 var controlPanelConfiguration = controlPanelConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(controlPanelConfigurationOptions));
478 var generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
479 var swarmConfiguration = swarmConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(swarmConfigurationOptions));
480
481 ArgumentNullException.ThrowIfNull(logger);
482
483 logger.LogDebug("Content Root: {contentRoot}", hostingEnvironment.ContentRootPath);
484 logger.LogTrace("Web Root: {webRoot}", hostingEnvironment.WebRootPath);
485
486 // setup the HTTP request pipeline
487 // Add additional logging context to the request
488 applicationBuilder.UseAdditionalRequestLoggingContext(swarmConfiguration);
489
490 // Wrap exceptions in a 500 (ErrorMessage) response
491 applicationBuilder.UseServerErrorHandling();
492
493 // Add the X-Powered-By response header
494 applicationBuilder.UseServerBranding(assemblyInformationProvider);
495
496 // Add the X-Accel-Buffering response header
497 applicationBuilder.UseDisabledNginxProxyBuffering();
498
499 // suppress OperationCancelledExceptions, they are just aborted HTTP requests
500 applicationBuilder.UseCancelledRequestSuppression();
501
502 // 503 requests made while the application is starting
503 applicationBuilder.UseAsyncInitialization<IInstanceManager>(
504 (instanceManager, cancellationToken) => instanceManager.Ready.WaitAsync(cancellationToken));
505
506 if (generalConfiguration.HostApiDocumentation)
507 {
508 var siteDocPath = Routes.ApiRoot + $"doc/{SwaggerConfiguration.DocumentName}.json";
509 if (!String.IsNullOrWhiteSpace(controlPanelConfiguration.PublicPath))
510 siteDocPath = controlPanelConfiguration.PublicPath.TrimEnd('/') + siteDocPath;
511
512 applicationBuilder.UseSwagger(options =>
513 {
514 options.RouteTemplate = Routes.ApiRoot + "doc/{documentName}.{json|yaml}";
515 });
516 applicationBuilder.UseSwaggerUI(options =>
517 {
519 options.SwaggerEndpoint(siteDocPath, "TGS API");
520 });
521 logger.LogTrace("Swagger API generation enabled");
522 }
523
524 // spa loading if necessary
525 if (controlPanelConfiguration.Enable)
526 {
527 logger.LogInformation("Web control panel enabled.");
528 applicationBuilder.UseFileServer(new FileServerOptions
529 {
531 EnableDefaultFiles = true,
532 EnableDirectoryBrowsing = false,
533 RedirectToAppendTrailingSlash = false,
534 });
535 }
536 else
537#if NO_WEBPANEL
538 logger.LogDebug("Web control panel was not included in TGS build!");
539#else
540 logger.LogTrace("Web control panel disabled!");
541#endif
542
543 // Enable endpoint routing
544 applicationBuilder.UseRouting();
545
546 // Set up CORS based on configuration if necessary
547 Action<CorsPolicyBuilder>? corsBuilder = null;
548 if (controlPanelConfiguration.AllowAnyOrigin)
549 {
550 logger.LogTrace("Access-Control-Allow-Origin: *");
551 corsBuilder = builder => builder.SetIsOriginAllowed(_ => true);
552 }
553 else if (controlPanelConfiguration.AllowedOrigins?.Count > 0)
554 {
555 logger.LogTrace("Access-Control-Allow-Origin: {allowedOrigins}", String.Join(',', controlPanelConfiguration.AllowedOrigins));
556 corsBuilder = builder => builder.WithOrigins([.. controlPanelConfiguration.AllowedOrigins]);
557 }
558
559 var originalBuilder = corsBuilder;
560 corsBuilder = builder =>
561 {
562 builder
563 .AllowAnyHeader()
564 .AllowAnyMethod()
565 .AllowCredentials()
566 .SetPreflightMaxAge(TimeSpan.FromDays(1));
567 originalBuilder?.Invoke(builder);
568 };
569 applicationBuilder.UseCors(corsBuilder);
570
571 // validate the API version
572 applicationBuilder.UseApiCompatibility();
573
574 // authenticate JWT tokens using our security pipeline if present, returns 401 if bad
575 applicationBuilder.UseAuthentication();
576
577 // enable authorization on endpoints
578 applicationBuilder.UseAuthorization();
579
580 // suppress and log database exceptions
581 applicationBuilder.UseDbConflictHandling();
582
583 // setup endpoints
584 applicationBuilder.UseEndpoints(endpoints =>
585 {
586 // access to the signalR jobs hub
587 endpoints.MapHub<JobsHub>(
589 options =>
590 {
591 options.Transports = HttpTransportType.ServerSentEvents;
592 options.CloseOnAuthenticationExpiration = true;
593 })
594 .RequireAuthorization()
595 .RequireCors(corsBuilder);
596
597 // majority of handling is done in the controllers
598 endpoints.MapControllers();
599 });
600
601 // 404 anything that gets this far
602 // End of request pipeline setup
603 logger.LogTrace("Configuration version: {configVersion}", GeneralConfiguration.CurrentConfigVersion);
604 logger.LogTrace("DMAPI Interop version: {interopVersion}", DMApiConstants.InteropVersion);
605 if (controlPanelConfiguration.Enable)
606 logger.LogTrace("Webpanel version: {webCPVersion}", MasterVersionsAttribute.Instance.RawWebpanelVersion);
607
608 logger.LogDebug("Starting hosting on port {httpApiPort}...", serverPortProvider.HttpApiPort);
609 }
610
612 protected override void ConfigureHostedService(IServiceCollection services)
613 => services.AddSingleton<IHostedService>(x => x.GetRequiredService<InstanceManager>());
614
619 void ConfigureAuthenticationPipeline(IServiceCollection services)
620 {
621 services.AddHttpContextAccessor();
622 services.AddScoped<IApiHeadersProvider, ApiHeadersProvider>();
623 services.AddScoped<AuthenticationContextFactory>();
624 services.AddScoped<IAuthenticationContextFactory>(provider => provider.GetRequiredService<AuthenticationContextFactory>());
625
626 // what if you
627 // wanted to just do this:
628 // return provider.GetRequiredService<AuthenticationContextFactory>().CurrentAuthenticationContext
629 // But M$ said
630 // https://stackoverflow.com/questions/56792917/scoped-services-in-asp-net-core-with-signalr-hubs
631 services.AddScoped(provider => (provider
632 .GetRequiredService<IHttpContextAccessor>()
633 .HttpContext ?? throw new InvalidOperationException($"Unable to resolve {nameof(IAuthenticationContext)} due to no HttpContext being available!"))
634 .RequestServices
635 .GetRequiredService<AuthenticationContextFactory>()
636 .CurrentAuthenticationContext);
638
639 services
640 .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
641 .AddJwtBearer(jwtBearerOptions =>
642 {
643 // this line isn't actually run until the first request is made
644 // at that point tokenFactory will be populated
645 jwtBearerOptions.TokenValidationParameters = tokenFactory?.ValidationParameters ?? throw new InvalidOperationException("tokenFactory not initialized!");
646 jwtBearerOptions.MapInboundClaims = false;
647 jwtBearerOptions.Events = new JwtBearerEvents
648 {
649 OnMessageReceived = context =>
650 {
651 if (String.IsNullOrWhiteSpace(context.Token))
652 {
653 var accessToken = context.Request.Query["access_token"];
654 var path = context.HttpContext.Request.Path;
655
656 if (!String.IsNullOrWhiteSpace(accessToken) &&
657 path.StartsWithSegments(Routes.HubsRoot, StringComparison.OrdinalIgnoreCase))
658 {
659 context.Token = accessToken;
660 }
661 }
662
663 return Task.CompletedTask;
664 },
665 };
666 });
667 }
668 }
669}
Represents the header that must be present for every server request.
Definition ApiHeaders.cs:25
Routes to a server actions.
Definition Routes.cs:9
const string HubsRoot
The root route of all hubs.
Definition Routes.cs:18
const string JobsHub
The root route of all hubs.
Definition Routes.cs:113
Base implementation of IEngineInstaller for EngineType.Byond.
Implementation of IEngineInstaller that forwards calls to different IEngineInstaller based on their a...
Implementation of IEngineInstaller for EngineType.OpenDream.
Implementation of OpenDreamInstaller for Windows systems.
Constants used for communication with the DMAPI.
static readonly Version InteropVersion
The DMAPI InteropVersion being used.
DatabaseType DatabaseType
The Configuration.DatabaseType to create.
LogLevel MicrosoftLogLevel
The minimum Microsoft.Extensions.Logging.LogLevel to display in logs for Microsoft library sources.
string GetFullLogDirectory(IIOManager ioManager, IAssemblyInformationProvider assemblyInformationProvider, IPlatformIdentifier platformIdentifier)
Gets the evaluated log Directory.
LogLevel LogLevel
The minimum Microsoft.Extensions.Logging.LogLevel to display in logs.
static readonly Version CurrentConfigVersion
The current ConfigVersion.
bool HostApiDocumentation
If the swagger documentation and UI should be made avaiable.
uint RestartTimeoutMinutes
The timeout minutes for restarting the server.
bool UsingSystemD
If the server is running under SystemD.
Configuration options for the game sessions.
Configuration for the server swarm system.
Configuration for the automatic update system.
const string ControlPanelRoute
Route to the ControlPanelController.
Sets up dependency injection.
override void ConfigureHostedService(IServiceCollection services)
Configures the IHostedService.
static void AddWatchdog< TSystemWatchdogFactory >(IServiceCollection services, IPostSetupServices postSetupServices)
Adds the IWatchdogFactory implementation.
void ConfigureServices(IServiceCollection services, IAssemblyInformationProvider assemblyInformationProvider, IIOManager ioManager, IPostSetupServices postSetupServices)
Configure the Application's services .
static IServerFactory CreateDefaultServerFactory()
Create the default IServerFactory.
void ConfigureAuthenticationPipeline(IServiceCollection services)
Configure the services for the authentication pipeline.
ITokenFactory? tokenFactory
The ITokenFactory for the Application.
void Configure(IApplicationBuilder applicationBuilder, IServerControl serverControl, ITokenFactory tokenFactory, IServerPortProvider serverPortProvider, IAssemblyInformationProvider assemblyInformationProvider, IOptions< ControlPanelConfiguration > controlPanelConfigurationOptions, IOptions< GeneralConfiguration > generalConfigurationOptions, IOptions< SwarmConfiguration > swarmConfigurationOptions, ILogger< Application > logger)
Configure the Application.
Application(IConfiguration configuration, IWebHostEnvironment hostingEnvironment)
Initializes a new instance of the Application class.
readonly IWebHostEnvironment hostingEnvironment
The IWebHostEnvironment for the Application.
Reads from the command pipe opened by the host watchdog.
Handles TGS version reporting, if enabled.
Backend abstract implementation of IDatabaseContext.
IIOManager that resolves paths to Environment.CurrentDirectory.
IPostWriteHandler for POSIX systems.
An IIOManager that resolve relative paths from another IIOManager to a subdirectory of that.
IPostWriteHandler for Windows systems.
Handles mapping groups for the JobsHub.
A SignalR Hub for pushing job updates.
Definition JobsHub.cs:16
Attribute for bringing in the master versions list from MSBuild that aren't embedded into assemblies ...
string RawWebpanelVersion
The Version string of the control panel version built.
static MasterVersionsAttribute Instance
Return the Assembly's instance of the MasterVersionsAttribute.
A IClaimsTransformation that maps Claims using an IAuthenticationContext.
An IHubFilter that denies method calls and connections if the IAuthenticationContext is not valid for...
ISystemIdentityFactory for windows systems. Uses long running tasks due to potential networked domain...
Implementation of IServerFactory.
DI root for configuring a SetupWizard.
IConfiguration Configuration
The IConfiguration for the SetupApplication.
Helps keep servers connected to the same database in sync by coordinating updates.
Implements the SystemD notify service protocol.
Implementation of the file transfer service.
Helpers for manipulating the Serilog.Context.LogContext.
static string Template
Common template used for adding our custom log context to serilog.
Implements various filters for Swashbuckle.
const string DocumentationSiteRouteExtension
The path to the hosted documentation site.
static void Configure(SwaggerGenOptions swaggerGenOptions, string assemblyDocumentationPath, string apiDocumentationPath)
Configure the swagger settings.
JsonConverter and IYamlTypeConverter for serializing global::System.Versions in semver format.
SignalR client methods for receiving JobResponses.
Definition IJobsHub.cs:12
For downloading and installing game engines for a given system.
Task Ready
Task that completes when the IInstanceManager finishes initializing.
For low level interactions with a LibGit2Sharp.IRepository.
Represents a service that may take an updated Host assembly and run it, stopping the current assembly...
Provides access to the server's HttpApiPort.
ushort HttpApiPort
The port the server listens on.
Factory for scoping usage of IDatabaseContexts. Meant for use by Components.
For initially setting up a database.
Interface for using filesystems.
Definition IIOManager.cs:13
string ConcatPath(params string[] paths)
Combines an array of strings into a path.
string GetDirectoryName(string path)
Gets the directory portion of a given path .
string GetFileNameWithoutExtension(string path)
Gets the file name portion of a path with.
Handles changing file modes/permissions after writing.
For accessing the disk in a synchronous manner.
Manages the runtime of Jobs.
The service that manages everything to do with jobs.
Definition IJobService.cs:9
Allows manually triggering jobs hub updates.
Contains various cryptographic functions.
Receives notifications about permissions updates.
TokenValidationParameters ValidationParameters
The TokenValidationParameters for the ITokenFactory.
Set of objects needed to configure an Core.Application.
GeneralConfiguration GeneralConfiguration
The Configuration.GeneralConfiguration.
ElasticsearchConfiguration ElasticsearchConfiguration
The Configuration.ElasticsearchConfiguration.
FileLoggingConfiguration FileLoggingConfiguration
The Configuration.FileLoggingConfiguration.
DatabaseConfiguration DatabaseConfiguration
The Configuration.DatabaseConfiguration.
InternalConfiguration InternalConfiguration
The Configuration.InternalConfiguration.
IPlatformIdentifier PlatformIdentifier
The IPlatformIdentifier.
Swarm service operations for the Controllers.SwarmController.
Start and stop controllers for a swarm service.
Used for swarm operations. Functions may be no-op based on configuration.
Service for managing the dotnet-dump installation.
On Windows, DreamDaemon will show an unskippable prompt when using /world/proc/OpenPort()....
bool IsWindows
If the current platform is a Windows platform.
Abstraction for suspending and resuming processes.
Reads and writes to Streams associated with FileTicketResponses.
Service for temporarily storing files to be downloaded or uploaded.
Gets unassigned ports for use by TGS.
EngineType
The type of engine the codebase is using.
Definition EngineType.cs:7
DatabaseType
Type of database to user.