From 49658a64e8f9af2b283ccc4d5970a6e93ea72d47 Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Mon, 17 Sep 2018 15:45:52 -0400 Subject: [PATCH 01/16] Adds security level stuff to the DMAPI --- src/DMAPI/tgs.dm | 11 ++++++++++- src/DMAPI/tgs/core/core.dm | 9 +++++++-- src/DMAPI/tgs/core/datum.dm | 5 ++++- src/DMAPI/tgs/v3210/api.dm | 5 ++++- src/DMAPI/tgs/v4/api.dm | 6 +++--- 5 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/DMAPI/tgs.dm b/src/DMAPI/tgs.dm index 6e55567544..7571d7f9a9 100644 --- a/src/DMAPI/tgs.dm +++ b/src/DMAPI/tgs.dm @@ -55,11 +55,16 @@ #define TGS_REBOOT_MODE_SHUTDOWN 1 #define TGS_REBOOT_MODE_RESTART 2 +#define TGS_SECURTY_TRUSTED 0 +#define TGS_SECURITY_SAFE 1 +#define TGS_SECURITY_ULTRASAFE 2 + //REQUIRED HOOKS //Call this somewhere in /world/New() that is always run //event_handler: optional user defined event handler. The default behaviour is to broadcast the event in english to all connected admin channels -/world/proc/TgsNew(datum/tgs_event_handler/event_handler) +//minimum_required_security_level: The minimum required security level to run the game in which the DMAPI is integrated +/world/proc/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SERCURITY_ULTRASAFE) return //Call this when your initializations are complete and your game is ready to play before any player interactions happen @@ -155,6 +160,10 @@ /world/proc/TgsRevision() return +//Get the current BYOND security level +/world/proc/TgsSecurityLevel() + return + //Gets a list of active `/datum/tgs_revision_information/test_merge`s /world/proc/TgsTestMerges() return diff --git a/src/DMAPI/tgs/core/core.dm b/src/DMAPI/tgs/core/core.dm index 1158fdbd34..e0495aba4e 100644 --- a/src/DMAPI/tgs/core/core.dm +++ b/src/DMAPI/tgs/core/core.dm @@ -1,4 +1,4 @@ -/world/TgsNew(datum/tgs_event_handler/event_handler) +/world/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE) var/current_api = TGS_READ_GLOBAL(tgs) if(current_api) TGS_ERROR_LOG("TgsNew(): TGS API datum already set ([current_api])!") @@ -18,7 +18,7 @@ TGS_WRITE_GLOBAL(tgs, new_api) - var/result = new_api.OnWorldNew(event_handler ? event_handler : new /datum/tgs_event_handler/tgs_default) + var/result = new_api.OnWorldNew(event_handler ? event_handler : new /datum/tgs_event_handler/tgs_default, minimum_required_security_level) if(!result || result == TGS_UNIMPLEMENTED) TGS_WRITE_GLOBAL(tgs, null) TGS_ERROR_LOG("Failed to activate API!") @@ -127,6 +127,11 @@ if(api) api.ChatPrivateMessage(message, user) +/world/TgsSecurityLevel() + var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs) + if(api) + api.SecurityLevel() + /* The MIT License diff --git a/src/DMAPI/tgs/core/datum.dm b/src/DMAPI/tgs/core/datum.dm index f81569136c..9ae5247989 100644 --- a/src/DMAPI/tgs/core/datum.dm +++ b/src/DMAPI/tgs/core/datum.dm @@ -42,10 +42,13 @@ TGS_PROTECT_DATUM(/datum/tgs_api) /datum/tgs_api/proc/ChatTargetedBroadcast(message, admin_only) return TGS_UNIMPLEMENTED - + /datum/tgs_api/proc/ChatPrivateMessage(message, admin_only) return TGS_UNIMPLEMENTED +/datum/tgs_api/proc/SecurityLevel() + return TGS_UNIMPLEMENTED + /* The MIT License diff --git a/src/DMAPI/tgs/v3210/api.dm b/src/DMAPI/tgs/v3210/api.dm index 1c04be9212..63bc0beb2b 100644 --- a/src/DMAPI/tgs/v3210/api.dm +++ b/src/DMAPI/tgs/v3210/api.dm @@ -56,7 +56,7 @@ /datum/tgs_api/v3210/proc/file2list(filename) return splittext(trim_left(trim_right(file2text(filename))), "\n") -/datum/tgs_api/v3210/OnWorldNew(datum/tgs_event_handler/event_handler) //don't use event handling in this version +/datum/tgs_api/v3210/OnWorldNew(datum/tgs_event_handler/event_handler, minimum_required_security_level) //don't use event handling in this version . = FALSE comms_key = world.params[SERVICE_WORLD_PARAM] @@ -191,6 +191,9 @@ /datum/tgs_api/v3210/ChatPrivateMessage(message, datum/tgs_chat_user/user) return TGS_UNIMPLEMENTED +/datum/tgs_api/v3210/SecurityLevel() + return TGS_SECURITY_TRUSTED + #undef REBOOT_MODE_NORMAL #undef REBOOT_MODE_HARD #undef REBOOT_MODE_SHUTDOWN diff --git a/src/DMAPI/tgs/v4/api.dm b/src/DMAPI/tgs/v4/api.dm index 92251d1e43..4fb8afc385 100644 --- a/src/DMAPI/tgs/v4/api.dm +++ b/src/DMAPI/tgs/v4/api.dm @@ -48,7 +48,7 @@ /datum/tgs_api/v4/ApiVersion() return "4.0.0.0" -/datum/tgs_api/v4/OnWorldNew(datum/tgs_event_handler/event_handler) +/datum/tgs_api/v4/OnWorldNew(datum/tgs_event_handler/event_handler, minimum_required_security_level) json_path = world.params[TGS4_PARAM_INFO_JSON] if(!json_path) TGS_ERROR_LOG("Missing [TGS4_PARAM_INFO_JSON] world parameter!") @@ -68,7 +68,7 @@ if(cached_json["apiValidateOnly"]) TGS_INFO_LOG("Validating API and exiting...") - Export(TGS4_COMM_VALIDATE) + Export(TGS4_COMM_VALIDATE, list(TGS4_PARAMETER_DATA = minimum_required_security_level)) del(world) chat_channels_json_path = cached_json["chatChannelsJson"] @@ -187,7 +187,7 @@ //request a new port export_lock = FALSE - var/list/new_port_json = Export(TGS4_COMM_NEW_PORT, list("current_port" = "[world.port]")) //stringify this on purpose + var/list/new_port_json = Export(TGS4_COMM_NEW_PORT, list(TGS4_PARAMETER_DATA = "[world.port]")) //stringify this on purpose if(!new_port_json) TGS_ERROR_LOG("No new port response from server![TGS4_PORT_CRITFAIL_MESSAGE]") From 5a0acf7266cff7a40c731bdbd1189feb0126db4c Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Mon, 17 Sep 2018 16:21:43 -0400 Subject: [PATCH 02/16] Add minimum security level validation to the compile step. Only serves as a one off warning at the moment. To be integrated with compile jobs and expanded upon at a later date. At the moment, this also allows for more verbosity during DMAPI validation. --- src/DMAPI/tgs/v4/api.dm | 2 +- .../Components/Compiler/DreamMaker.cs | 39 ++++++++++++---- .../Watchdog/ApiValidationStatus.cs | 33 +++++++++++++ .../Components/Watchdog/ISessionController.cs | 4 +- .../Components/Watchdog/SessionController.cs | 46 ++++++++++++++++--- .../Watchdog/SessionControllerFactory.cs | 4 +- 6 files changed, 106 insertions(+), 22 deletions(-) create mode 100644 src/Tgstation.Server.Host/Components/Watchdog/ApiValidationStatus.cs diff --git a/src/DMAPI/tgs/v4/api.dm b/src/DMAPI/tgs/v4/api.dm index 4fb8afc385..bf1f506f0a 100644 --- a/src/DMAPI/tgs/v4/api.dm +++ b/src/DMAPI/tgs/v4/api.dm @@ -68,7 +68,7 @@ if(cached_json["apiValidateOnly"]) TGS_INFO_LOG("Validating API and exiting...") - Export(TGS4_COMM_VALIDATE, list(TGS4_PARAMETER_DATA = minimum_required_security_level)) + Export(TGS4_COMM_VALIDATE, list(TGS4_PARAMETER_DATA = "[minimum_required_security_level]")) del(world) chat_channels_json_path = cached_json["chatChannelsJson"] diff --git a/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs index 15c46067f8..e8dd38d401 100644 --- a/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs +++ b/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs @@ -125,8 +125,8 @@ namespace Tgstation.Server.Host.Components.Compiler /// The current /// The port to use for API validation /// The for the operation - /// A resulting in if the DMAPI was successfully validated, otherwise - async Task VerifyApi(uint timeout, DreamDaemonSecurity securityLevel, Models.CompileJob job, IByondExecutableLock byondLock, ushort portToUse, CancellationToken cancellationToken) + /// A representing the running operation + async Task VerifyApi(uint timeout, DreamDaemonSecurity securityLevel, Models.CompileJob job, IByondExecutableLock byondLock, ushort portToUse, CancellationToken cancellationToken) { logger.LogTrace("Verifying DMAPI..."); var launchParameters = new DreamDaemonLaunchParameters @@ -156,13 +156,27 @@ namespace Tgstation.Server.Host.Components.Compiler if (!controller.Lifetime.IsCompleted) { - logger.LogDebug("API validation timed out!"); - return false; + var validationStatus = controller.ApiValidationStatus; + logger.LogTrace("API validation status: {0}", validationStatus); + switch (validationStatus) + { + case ApiValidationStatus.Validated: + return; + case ApiValidationStatus.NeverValidated: + break; + case ApiValidationStatus.BadValidationRequest: + throw new JobException("Recieved an unrecognized API validation request from DreamDaemon!"); + case ApiValidationStatus.RequiresSafe: + throw new JobException("This game must be run with at least the 'Safe' DreamDaemon security level!"); + case ApiValidationStatus.RequiresTrusted: + throw new JobException("This game must be run with at least the 'Trusted' DreamDaemon security level!"); + case ApiValidationStatus.UnaskedValidationRequest: + default: + throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Session controller returned unexpected ApiValidationStatus: {0}", validationStatus)); + } } - var validated = controller.ApiValidated; - logger.LogTrace("API valid: {0}", validated); - return validated; + throw new JobException("DMAPI validation timed out!"); } } @@ -356,13 +370,18 @@ namespace Tgstation.Server.Host.Components.Compiler var exitCode = await RunDreamMaker(byondLock.DreamMakerPath, job, cancellationToken).ConfigureAwait(false); - var apiValidated = exitCode == 0 && await VerifyApi(apiValidateTimeout, securityLevel, job, byondLock, dreamMakerSettings.ApiValidationPort.Value, cancellationToken).ConfigureAwait(false); + try + { + if (exitCode != 0) + throw new JobException(String.Format(CultureInfo.InvariantCulture, "DM exited with a non-zero code: {0}{1}{2}", exitCode, Environment.NewLine, job.Output)); - if (!apiValidated) + await VerifyApi(apiValidateTimeout, securityLevel, job, byondLock, dreamMakerSettings.ApiValidationPort.Value, cancellationToken).ConfigureAwait(false); + } + catch (JobException) { //server never validated or compile failed await eventConsumer.HandleEvent(EventType.CompileFailure, new List { resolvedGameDirectory, exitCode == 0 ? "1" : "0" }, cancellationToken).ConfigureAwait(false); - throw new JobException(exitCode == 0 ? "Validation of the TGS api failed!" : String.Format(CultureInfo.InvariantCulture, "DM exited with a non-zero code: {0}{1}{2}", exitCode, Environment.NewLine, job.Output)); + throw; } logger.LogTrace("Running post compile event..."); diff --git a/src/Tgstation.Server.Host/Components/Watchdog/ApiValidationStatus.cs b/src/Tgstation.Server.Host/Components/Watchdog/ApiValidationStatus.cs new file mode 100644 index 0000000000..cc179c5271 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/ApiValidationStatus.cs @@ -0,0 +1,33 @@ +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + /// Status of DMAPI validation + /// + enum ApiValidationStatus + { + /// + /// The DMAPI never contacted the server for validation + /// + NeverValidated, + /// + /// The server was contacted for validation but it was never requested + /// + UnaskedValidationRequest, + /// + /// The game must be run with a minimum security level of + /// + RequiresSafe, + /// + /// The game must be run with a security level of + /// + RequiresTrusted, + /// + /// The validation request was malformed + /// + BadValidationRequest, + /// + /// The DMAPI validated successfully + /// + Validated + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/ISessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/ISessionController.cs index 3f2e245eba..97e19b271d 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/ISessionController.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/ISessionController.cs @@ -25,9 +25,9 @@ namespace Tgstation.Server.Host.Components.Watchdog bool TerminationWasRequested { get; } /// - /// If the DMAPI was validated. This field may only be access once completes + /// The DMAPI /// - bool ApiValidated { get; } + ApiValidationStatus ApiValidationStatus { get; } /// /// The being used diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs index 20b1115531..969aed7866 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs @@ -29,13 +29,13 @@ namespace Tgstation.Server.Host.Components.Watchdog } /// - public bool ApiValidated + public ApiValidationStatus ApiValidationStatus { get { if (!Lifetime.IsCompleted) throw new InvalidOperationException("ApiValidated cannot be checked while Lifetime is incomplete!"); - return apiValidated; + return apiValidationStatus; } } @@ -126,6 +126,11 @@ namespace Tgstation.Server.Host.Components.Watchdog /// readonly ILogger logger; + /// + /// The level the was launched with + /// + readonly DreamDaemonSecurity? launchSecurityLevel; + /// /// The waits on when DreamDaemon currently has it's ports closed /// @@ -151,9 +156,9 @@ namespace Tgstation.Server.Host.Components.Watchdog bool disposed; /// - /// If the DMAPI was validated + /// The for the /// - bool apiValidated; + ApiValidationStatus apiValidationStatus; /// /// If should be kept alive instead @@ -171,8 +176,9 @@ namespace Tgstation.Server.Host.Components.Watchdog /// The value of /// The value of /// The value of + /// The value of /// The optional time to wait before failing the - public SessionController(ReattachInformation reattachInformation, IProcess process, IByondExecutableLock byondLock, IByondTopicSender byondTopicSender, IJsonTrackingContext chatJsonTrackingContext, ICommContext interopContext, IChat chat, ILogger logger, uint? startupTimeout) + public SessionController(ReattachInformation reattachInformation, IProcess process, IByondExecutableLock byondLock, IByondTopicSender byondTopicSender, IJsonTrackingContext chatJsonTrackingContext, ICommContext interopContext, IChat chat, ILogger logger, DreamDaemonSecurity? launchSecurityLevel, uint? startupTimeout) { this.chatJsonTrackingContext = chatJsonTrackingContext; //null valid this.reattachInformation = reattachInformation ?? throw new ArgumentNullException(nameof(reattachInformation)); @@ -183,11 +189,13 @@ namespace Tgstation.Server.Host.Components.Watchdog this.chat = chat ?? throw new ArgumentNullException(nameof(chat)); this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + this.launchSecurityLevel = launchSecurityLevel; + interopContext.RegisterHandler(this); portClosedForReboot = false; disposed = false; - apiValidated = false; + apiValidationStatus = ApiValidationStatus.NeverValidated; released = false; rebootTcs = new TaskCompletionSource(); @@ -289,6 +297,7 @@ namespace Tgstation.Server.Host.Components.Watchdog { /////UHHHH logger.LogWarning("DreamDaemon sent new port command without providing it's own!"); + content = new ErrorMessage { Message = "Missing stringified port as data parameter!" }; break; } @@ -314,7 +323,30 @@ namespace Tgstation.Server.Host.Components.Watchdog } break; case Constants.DMCommandApiValidate: - apiValidated = true; + if (!launchSecurityLevel.HasValue) + { + logger.LogWarning("DreamDaemon requested API validation but no intial security level was passed to the session controller!"); + apiValidationStatus = ApiValidationStatus.UnaskedValidationRequest; + content = new ErrorMessage { Message = "Invalid API validation request!" }; + break; + } + if (!query.TryGetValue(Constants.DMParameterData, out var stringMinimumSecurityLevel) || !Enum.TryParse(stringMinimumSecurityLevel, out var minimumSecurityLevel)) + apiValidationStatus = ApiValidationStatus.BadValidationRequest; + else + switch (minimumSecurityLevel) + { + case DreamDaemonSecurity.Safe: + apiValidationStatus = launchSecurityLevel == DreamDaemonSecurity.Ultrasafe ? ApiValidationStatus.RequiresSafe : ApiValidationStatus.Validated; + break; + case DreamDaemonSecurity.Ultrasafe: + apiValidationStatus = ApiValidationStatus.Validated; + break; + case DreamDaemonSecurity.Trusted: + apiValidationStatus = launchSecurityLevel == DreamDaemonSecurity.Trusted ? ApiValidationStatus.Validated : ApiValidationStatus.RequiresTrusted; + break; + default: + throw new InvalidOperationException("Enum.TryParse failed to validate the DreamDaemonSecurity range!"); + } break; case Constants.DMCommandWorldReboot: if (ClosePortOnReboot) diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs index fad899d6e7..b2f4399c13 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs @@ -200,7 +200,7 @@ namespace Tgstation.Server.Host.Components.Watchdog ChatChannelsJson = interopInfo.ChatChannelsJson, ChatCommandsJson = interopInfo.ChatCommandsJson, ServerCommandsJson = interopInfo.ServerCommandsJson, - }, process, byondLock, byondTopicSender, chatJsonTrackingContext, context, chat, loggerFactory.CreateLogger(), launchParameters.StartupTimeout); + }, process, byondLock, byondTopicSender, chatJsonTrackingContext, context, chat, loggerFactory.CreateLogger(), launchParameters.SecurityLevel, launchParameters.StartupTimeout); } catch { @@ -247,7 +247,7 @@ namespace Tgstation.Server.Host.Components.Watchdog var process = processExecutor.GetProcess(reattachInformation.ProcessId); try { - return new SessionController(reattachInformation, process, byondLock, byondTopicSender, chatJsonTrackingContext, context, chat, loggerFactory.CreateLogger(), null); + return new SessionController(reattachInformation, process, byondLock, byondTopicSender, chatJsonTrackingContext, context, chat, loggerFactory.CreateLogger(), null, null); } catch { From f3c8a91f5e6104852dacbd06f74004c5cf6877be Mon Sep 17 00:00:00 2001 From: Cyberboss Date: Mon, 17 Sep 2018 16:24:06 -0400 Subject: [PATCH 03/16] Remove stray whitespace --- src/DMAPI/tgs/core/datum.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DMAPI/tgs/core/datum.dm b/src/DMAPI/tgs/core/datum.dm index 9ae5247989..b2f9b19cdd 100644 --- a/src/DMAPI/tgs/core/datum.dm +++ b/src/DMAPI/tgs/core/datum.dm @@ -42,7 +42,7 @@ TGS_PROTECT_DATUM(/datum/tgs_api) /datum/tgs_api/proc/ChatTargetedBroadcast(message, admin_only) return TGS_UNIMPLEMENTED - + /datum/tgs_api/proc/ChatPrivateMessage(message, admin_only) return TGS_UNIMPLEMENTED From ad9667bcc371b29431224efc580fffb8159ecb0b Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 17 Sep 2018 18:03:25 -0400 Subject: [PATCH 04/16] Fix misspelling of TGS_SECURITY_ULTRASAFE --- src/DMAPI/tgs.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DMAPI/tgs.dm b/src/DMAPI/tgs.dm index 7571d7f9a9..c3a4937495 100644 --- a/src/DMAPI/tgs.dm +++ b/src/DMAPI/tgs.dm @@ -64,7 +64,7 @@ //Call this somewhere in /world/New() that is always run //event_handler: optional user defined event handler. The default behaviour is to broadcast the event in english to all connected admin channels //minimum_required_security_level: The minimum required security level to run the game in which the DMAPI is integrated -/world/proc/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SERCURITY_ULTRASAFE) +/world/proc/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE) return //Call this when your initializations are complete and your game is ready to play before any player interactions happen From 23956b7bdead8cb3b0b0901b045432110232550c Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 17 Sep 2018 18:11:03 -0400 Subject: [PATCH 05/16] Fix misspelling of TGS_SECURITY_TRUSTED --- src/DMAPI/tgs.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DMAPI/tgs.dm b/src/DMAPI/tgs.dm index c3a4937495..6645ce51ff 100644 --- a/src/DMAPI/tgs.dm +++ b/src/DMAPI/tgs.dm @@ -55,7 +55,7 @@ #define TGS_REBOOT_MODE_SHUTDOWN 1 #define TGS_REBOOT_MODE_RESTART 2 -#define TGS_SECURTY_TRUSTED 0 +#define TGS_SECURITY_TRUSTED 0 #define TGS_SECURITY_SAFE 1 #define TGS_SECURITY_ULTRASAFE 2 From 965ce32064f18912642935f39c626fa203e24c74 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 17 Sep 2018 21:40:41 -0400 Subject: [PATCH 06/16] Set minimum security level in CompileJobs --- src/Tgstation.Server.Api/Models/DreamMaker.cs | 6 +++++ .../Models/Internal/CompileJob.cs | 7 +++++ .../Components/Compiler/DreamMaker.cs | 27 ++++++++++++------- .../Components/Compiler/IDreamMaker.cs | 3 +-- .../Components/Instance.cs | 3 +-- .../Watchdog/ApiValidationStatus.cs | 20 +++++++------- .../Components/Watchdog/SessionController.cs | 6 ++--- .../Controllers/InstanceController.cs | 3 ++- 8 files changed, 47 insertions(+), 28 deletions(-) diff --git a/src/Tgstation.Server.Api/Models/DreamMaker.cs b/src/Tgstation.Server.Api/Models/DreamMaker.cs index 690cc90e09..bb821fdc63 100644 --- a/src/Tgstation.Server.Api/Models/DreamMaker.cs +++ b/src/Tgstation.Server.Api/Models/DreamMaker.cs @@ -17,5 +17,11 @@ namespace Tgstation.Server.Api.Models /// [Required] public ushort? ApiValidationPort { get; set; } + + /// + /// The level used to validate the DMAPI + /// + [Required] + public DreamDaemonSecurity? ApiValidationSecurityLevel { get; set; } } } diff --git a/src/Tgstation.Server.Api/Models/Internal/CompileJob.cs b/src/Tgstation.Server.Api/Models/Internal/CompileJob.cs index bcd2801a51..e101ff7f23 100644 --- a/src/Tgstation.Server.Api/Models/Internal/CompileJob.cs +++ b/src/Tgstation.Server.Api/Models/Internal/CompileJob.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.DataAnnotations; namespace Tgstation.Server.Api.Models.Internal { @@ -26,5 +27,11 @@ namespace Tgstation.Server.Api.Models.Internal /// The Game folder the results were compiled into /// public Guid? DirectoryName { get; set; } + + /// + /// The minimum required to run the 's output + /// + [Required] + public DreamDaemonSecurity? MinimumSecurityLevel { get; set; } } } diff --git a/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs index e8dd38d401..8b0304625f 100644 --- a/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs +++ b/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs @@ -160,22 +160,29 @@ namespace Tgstation.Server.Host.Components.Compiler logger.LogTrace("API validation status: {0}", validationStatus); switch (validationStatus) { - case ApiValidationStatus.Validated: + case ApiValidationStatus.RequiresUltrasafe: + job.MinimumSecurityLevel = DreamDaemonSecurity.Ultrasafe; + return; + case ApiValidationStatus.RequiresSafe: + if (securityLevel == DreamDaemonSecurity.Ultrasafe) + throw new JobException("This game must be run with at least the 'Safe' DreamDaemon security level!"); + job.MinimumSecurityLevel = DreamDaemonSecurity.Safe; + return; + case ApiValidationStatus.RequiresTrusted: + if (securityLevel != DreamDaemonSecurity.Trusted) + throw new JobException("This game must be run with at least the 'Trusted' DreamDaemon security level!"); + job.MinimumSecurityLevel = DreamDaemonSecurity.Trusted; return; case ApiValidationStatus.NeverValidated: break; case ApiValidationStatus.BadValidationRequest: throw new JobException("Recieved an unrecognized API validation request from DreamDaemon!"); - case ApiValidationStatus.RequiresSafe: - throw new JobException("This game must be run with at least the 'Safe' DreamDaemon security level!"); - case ApiValidationStatus.RequiresTrusted: - throw new JobException("This game must be run with at least the 'Trusted' DreamDaemon security level!"); case ApiValidationStatus.UnaskedValidationRequest: default: throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Session controller returned unexpected ApiValidationStatus: {0}", validationStatus)); } } - + throw new JobException("DMAPI validation timed out!"); } } @@ -257,7 +264,7 @@ namespace Tgstation.Server.Host.Components.Compiler } /// - public async Task Compile(Models.RevisionInformation revisionInformation, Api.Models.DreamMaker dreamMakerSettings, DreamDaemonSecurity securityLevel, uint apiValidateTimeout, IRepository repository, CancellationToken cancellationToken) + public async Task Compile(Models.RevisionInformation revisionInformation, Api.Models.DreamMaker dreamMakerSettings, uint apiValidateTimeout, IRepository repository, CancellationToken cancellationToken) { if (revisionInformation == null) throw new ArgumentNullException(nameof(revisionInformation)); @@ -268,8 +275,8 @@ namespace Tgstation.Server.Host.Components.Compiler if (repository == null) throw new ArgumentNullException(nameof(repository)); - if (securityLevel == DreamDaemonSecurity.Ultrasafe) - throw new ArgumentOutOfRangeException(nameof(securityLevel), securityLevel, "Cannot compile with ultrasafe security!"); + if (dreamMakerSettings.ApiValidationSecurityLevel == DreamDaemonSecurity.Ultrasafe) + throw new ArgumentOutOfRangeException(nameof(dreamMakerSettings), dreamMakerSettings, "Cannot compile with ultrasafe security!"); logger.LogTrace("Begin Compile"); @@ -375,7 +382,7 @@ namespace Tgstation.Server.Host.Components.Compiler if (exitCode != 0) throw new JobException(String.Format(CultureInfo.InvariantCulture, "DM exited with a non-zero code: {0}{1}{2}", exitCode, Environment.NewLine, job.Output)); - await VerifyApi(apiValidateTimeout, securityLevel, job, byondLock, dreamMakerSettings.ApiValidationPort.Value, cancellationToken).ConfigureAwait(false); + await VerifyApi(apiValidateTimeout, dreamMakerSettings.ApiValidationSecurityLevel.Value, job, byondLock, dreamMakerSettings.ApiValidationPort.Value, cancellationToken).ConfigureAwait(false); } catch (JobException) { diff --git a/src/Tgstation.Server.Host/Components/Compiler/IDreamMaker.cs b/src/Tgstation.Server.Host/Components/Compiler/IDreamMaker.cs index dce21e8194..83f83849fa 100644 --- a/src/Tgstation.Server.Host/Components/Compiler/IDreamMaker.cs +++ b/src/Tgstation.Server.Host/Components/Compiler/IDreamMaker.cs @@ -15,11 +15,10 @@ namespace Tgstation.Server.Host.Components.Compiler /// /// The being compiled from the /// The for the compile - /// The level allowed for API validation /// The time in seconds to wait while validating the API /// The to copy from /// The for the operation /// A resulting in the partially populated for the operation. In particular, note the field will only have it's field populated - Task Compile(Models.RevisionInformation revisionInformation, Api.Models.DreamMaker dreamMakerSettings, DreamDaemonSecurity securityLevel, uint apiValidateTimeout, IRepository repository, CancellationToken cancellationToken); + Task Compile(Models.RevisionInformation revisionInformation, Api.Models.DreamMaker dreamMakerSettings, uint apiValidateTimeout, IRepository repository, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Instance.cs b/src/Tgstation.Server.Host/Components/Instance.cs index 8f7b0e7700..79faad2b45 100644 --- a/src/Tgstation.Server.Host/Components/Instance.cs +++ b/src/Tgstation.Server.Host/Components/Instance.cs @@ -132,7 +132,6 @@ namespace Tgstation.Server.Host.Components var ddSettingsTask = databaseContext.DreamDaemonSettings.Where(x => x.InstanceId == metadata.Id).Select(x => new DreamDaemonSettings { StartupTimeout = x.StartupTimeout, - SecurityLevel = x.SecurityLevel }).FirstOrDefaultAsync(cancellationToken); var dreamMakerSettings = await databaseContext.DreamMakerSettings.Where(x => x.InstanceId == metadata.Id).FirstAsync(cancellationToken).ConfigureAwait(false); @@ -167,7 +166,7 @@ namespace Tgstation.Server.Host.Components databaseContext.Instances.Attach(revInfo.Instance); } - compileJob = await DreamMaker.Compile(revInfo, dreamMakerSettings, ddSettings.SecurityLevel.Value, ddSettings.StartupTimeout.Value, repo, cancellationToken).ConfigureAwait(false); + compileJob = await DreamMaker.Compile(revInfo, dreamMakerSettings, ddSettings.StartupTimeout.Value, repo, cancellationToken).ConfigureAwait(false); } compileJob.Job = job; diff --git a/src/Tgstation.Server.Host/Components/Watchdog/ApiValidationStatus.cs b/src/Tgstation.Server.Host/Components/Watchdog/ApiValidationStatus.cs index cc179c5271..46e282802d 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/ApiValidationStatus.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/ApiValidationStatus.cs @@ -14,20 +14,20 @@ /// UnaskedValidationRequest, /// - /// The game must be run with a minimum security level of - /// - RequiresSafe, - /// - /// The game must be run with a security level of - /// - RequiresTrusted, - /// /// The validation request was malformed /// BadValidationRequest, /// - /// The DMAPI validated successfully + /// Valid API. The game must be run with a minimum security level of /// - Validated + RequiresSafe, + /// + /// Valid API. The game must be run with a security level of + /// + RequiresTrusted, + /// + /// Valid API. The game must be run with a minimum security level of + /// + RequiresUltrasafe } } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs index 969aed7866..b71d7f0c62 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs @@ -336,13 +336,13 @@ namespace Tgstation.Server.Host.Components.Watchdog switch (minimumSecurityLevel) { case DreamDaemonSecurity.Safe: - apiValidationStatus = launchSecurityLevel == DreamDaemonSecurity.Ultrasafe ? ApiValidationStatus.RequiresSafe : ApiValidationStatus.Validated; + apiValidationStatus = ApiValidationStatus.RequiresSafe; break; case DreamDaemonSecurity.Ultrasafe: - apiValidationStatus = ApiValidationStatus.Validated; + apiValidationStatus = ApiValidationStatus.RequiresUltrasafe; break; case DreamDaemonSecurity.Trusted: - apiValidationStatus = launchSecurityLevel == DreamDaemonSecurity.Trusted ? ApiValidationStatus.Validated : ApiValidationStatus.RequiresTrusted; + apiValidationStatus = ApiValidationStatus.RequiresTrusted; break; default: throw new InvalidOperationException("Enum.TryParse failed to validate the DreamDaemonSecurity range!"); diff --git a/src/Tgstation.Server.Host/Controllers/InstanceController.cs b/src/Tgstation.Server.Host/Controllers/InstanceController.cs index 3545f1ce8a..aafd0da61e 100644 --- a/src/Tgstation.Server.Host/Controllers/InstanceController.cs +++ b/src/Tgstation.Server.Host/Controllers/InstanceController.cs @@ -136,7 +136,8 @@ namespace Tgstation.Server.Host.Controllers }, DreamMakerSettings = new DreamMakerSettings { - ApiValidationPort = 1339 + ApiValidationPort = 1339, + ApiValidationSecurityLevel = DreamDaemonSecurity.Safe }, Name = model.Name, Online = false, From 11c594843ddb343b9140e1f13a7f3c3479a311a8 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 17 Sep 2018 21:59:26 -0400 Subject: [PATCH 07/16] Limit the maximum DreamDaemonSecurity level to that of the CompileJob's in SessionControllerFactory --- .../Components/Compiler/DreamMaker.cs | 2 ++ .../Watchdog/SessionControllerFactory.cs | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs index 8b0304625f..97cdcc8519 100644 --- a/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs +++ b/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs @@ -138,6 +138,8 @@ namespace Tgstation.Server.Host.Components.Compiler }; var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName); + + job.MinimumSecurityLevel = securityLevel; //needed for the TempDmbProvider var provider = new TemporaryDmbProvider(ioManager.ResolvePath(dirA), String.Concat(job.DmeName, DmbExtension), job); var timeoutAt = DateTimeOffset.Now.AddSeconds(timeout); diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs index b2f4399c13..b5ec781ba0 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs @@ -177,12 +177,28 @@ namespace Tgstation.Server.Host.Components.Watchdog //more sanitization here cause it uses the same scheme var parameters = String.Format(CultureInfo.InvariantCulture, "{2}={0}&{3}={1}", byondTopicSender.SanitizeString(application.Version.ToString()), byondTopicSender.SanitizeString(interopJsonFile), byondTopicSender.SanitizeString(Constants.DMParamHostVersion), byondTopicSender.SanitizeString(Constants.DMParamInfoJson)); + var securityLevelToUse = launchParameters.SecurityLevel.Value; + switch (dmbProvider.CompileJob.MinimumSecurityLevel) + { + case DreamDaemonSecurity.Ultrasafe: + break; + case DreamDaemonSecurity.Safe: + if (securityLevelToUse == DreamDaemonSecurity.Ultrasafe) + securityLevelToUse = DreamDaemonSecurity.Safe; + break; + case DreamDaemonSecurity.Trusted: + securityLevelToUse = DreamDaemonSecurity.Trusted; + break; + default: + throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid DreamDaemonSecurity value: {0}", dmbProvider.CompileJob.MinimumSecurityLevel)); + } + //important to run on all ports to allow port changing var arguments = String.Format(CultureInfo.InvariantCulture, "{0} -port {1} -ports 1-65535 {2}-close -{3} -verbose -public -params \"{4}\"", dmbProvider.DmbName, primaryPort ? launchParameters.PrimaryPort : launchParameters.SecondaryPort, launchParameters.AllowWebClient.Value ? "-webclient " : String.Empty, - SecurityWord(launchParameters.SecurityLevel.Value), + SecurityWord(securityLevelToUse), parameters); //launch dd From 4d89dcdbfee290f97bde099f8ea3f7de758cbfef Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 17 Sep 2018 22:15:52 -0400 Subject: [PATCH 08/16] Adds AddMinimumSecurity migration --- ...20180906143029_MYInitialCreate.Designer.cs | 1 - ...918020726_MYAddMinimumSecurity.Designer.cs | 648 +++++++++++++++++ .../20180918020726_MYAddMinimumSecurity.cs | 45 ++ ...918021228_MSAddMinimumSecurity.Designer.cs | 676 ++++++++++++++++++ .../20180918021228_MSAddMinimumSecurity.cs | 45 ++ .../MySqlDatabaseContextModelSnapshot.cs | 7 +- .../SqlServerDatabaseContextModelSnapshot.cs | 6 +- 7 files changed, 1424 insertions(+), 4 deletions(-) create mode 100644 src/Tgstation.Server.Host/Models/Migrations/20180918020726_MYAddMinimumSecurity.Designer.cs create mode 100644 src/Tgstation.Server.Host/Models/Migrations/20180918020726_MYAddMinimumSecurity.cs create mode 100644 src/Tgstation.Server.Host/Models/Migrations/20180918021228_MSAddMinimumSecurity.Designer.cs create mode 100644 src/Tgstation.Server.Host/Models/Migrations/20180918021228_MSAddMinimumSecurity.cs diff --git a/src/Tgstation.Server.Host/Models/Migrations/20180906143029_MYInitialCreate.Designer.cs b/src/Tgstation.Server.Host/Models/Migrations/20180906143029_MYInitialCreate.Designer.cs index 73566bba2d..3c1a424266 100644 --- a/src/Tgstation.Server.Host/Models/Migrations/20180906143029_MYInitialCreate.Designer.cs +++ b/src/Tgstation.Server.Host/Models/Migrations/20180906143029_MYInitialCreate.Designer.cs @@ -3,7 +3,6 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Tgstation.Server.Host.Models.Migrations { diff --git a/src/Tgstation.Server.Host/Models/Migrations/20180918020726_MYAddMinimumSecurity.Designer.cs b/src/Tgstation.Server.Host/Models/Migrations/20180918020726_MYAddMinimumSecurity.Designer.cs new file mode 100644 index 0000000000..bf112bf7b2 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/Migrations/20180918020726_MYAddMinimumSecurity.Designer.cs @@ -0,0 +1,648 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Migrations +{ + [DbContext(typeof(MySqlDatabaseContext))] + [Migration("20180918020726_MYAddMinimumSecurity")] + partial class MYAddMinimumSecurity + { + /// + /// Builds the target model + /// + /// The to use + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConnectionString") + .IsRequired(); + + b.Property("Enabled"); + + b.Property("InstanceId"); + + b.Property("Name") + .IsRequired(); + + b.Property("Provider"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ChatBots"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChatSettingsId"); + + b.Property("DiscordChannelId"); + + b.Property("IrcChannel"); + + b.Property("IsAdminChannel") + .IsRequired(); + + b.Property("IsUpdatesChannel") + .IsRequired(); + + b.Property("IsWatchdogChannel") + .IsRequired(); + + b.Property("Tag"); + + b.HasKey("Id"); + + b.HasIndex("ChatSettingsId", "DiscordChannelId") + .IsUnique(); + + b.HasIndex("ChatSettingsId", "IrcChannel") + .IsUnique(); + + b.ToTable("ChatChannels"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ByondVersion") + .IsRequired(); + + b.Property("DirectoryName"); + + b.Property("DmeName"); + + b.Property("JobId"); + + b.Property("MinimumSecurityLevel"); + + b.Property("Output"); + + b.Property("RevisionInformationId"); + + b.HasKey("Id"); + + b.HasIndex("DirectoryName"); + + b.HasIndex("JobId"); + + b.HasIndex("RevisionInformationId"); + + b.ToTable("CompileJobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessToken"); + + b.Property("AllowWebClient") + .IsRequired(); + + b.Property("AutoStart") + .IsRequired(); + + b.Property("InstanceId"); + + b.Property("PrimaryPort") + .IsRequired(); + + b.Property("ProcessId"); + + b.Property("SecondaryPort") + .IsRequired(); + + b.Property("SecurityLevel"); + + b.Property("SoftRestart") + .IsRequired(); + + b.Property("SoftShutdown") + .IsRequired(); + + b.Property("StartupTimeout") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamDaemonSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ApiValidationPort") + .IsRequired(); + + b.Property("ApiValidationSecurityLevel"); + + b.Property("InstanceId"); + + b.Property("ProjectName"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamMakerSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AutoUpdateInterval") + .IsRequired(); + + b.Property("ConfigurationType"); + + b.Property("Name") + .IsRequired(); + + b.Property("Online") + .IsRequired(); + + b.Property("Path") + .IsRequired(); + + b.Property("WatchdogReattachInformationId"); + + b.HasKey("Id"); + + b.HasIndex("Path") + .IsUnique(); + + b.HasIndex("WatchdogReattachInformationId"); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ByondRights"); + + b.Property("ChatBotRights"); + + b.Property("ConfigurationRights"); + + b.Property("DreamDaemonRights"); + + b.Property("DreamMakerRights"); + + b.Property("InstanceId"); + + b.Property("InstanceUserRights"); + + b.Property("RepositoryRights"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("UserId", "InstanceId") + .IsUnique(); + + b.ToTable("InstanceUsers"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CancelRight"); + + b.Property("CancelRightsType"); + + b.Property("Cancelled") + .IsRequired(); + + b.Property("CancelledById"); + + b.Property("Description") + .IsRequired(); + + b.Property("ExceptionDetails"); + + b.Property("InstanceId"); + + b.Property("StartedAt") + .IsRequired(); + + b.Property("StartedById"); + + b.Property("StoppedAt"); + + b.HasKey("Id"); + + b.HasIndex("CancelledById"); + + b.HasIndex("InstanceId"); + + b.HasIndex("StartedById"); + + b.ToTable("Jobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessIdentifier") + .IsRequired(); + + b.Property("ChatChannelsJson") + .IsRequired(); + + b.Property("ChatCommandsJson") + .IsRequired(); + + b.Property("CompileJobId"); + + b.Property("IsPrimary"); + + b.Property("Port"); + + b.Property("ProcessId"); + + b.Property("RebootState"); + + b.Property("ServerCommandsJson") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("CompileJobId"); + + b.ToTable("ReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessToken"); + + b.Property("AccessUser"); + + b.Property("AutoUpdatesKeepTestMerges") + .IsRequired(); + + b.Property("AutoUpdatesSynchronize") + .IsRequired(); + + b.Property("CommitterEmail") + .IsRequired(); + + b.Property("CommitterName") + .IsRequired(); + + b.Property("InstanceId"); + + b.Property("PushTestMergeCommits") + .IsRequired(); + + b.Property("ShowTestMergeCommitters") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("RepositorySettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RevisionInformationId"); + + b.Property("TestMergeId"); + + b.HasKey("Id"); + + b.HasIndex("RevisionInformationId"); + + b.HasIndex("TestMergeId"); + + b.ToTable("RevInfoTestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CommitSha") + .IsRequired() + .HasMaxLength(40); + + b.Property("InstanceId"); + + b.Property("OriginCommitSha") + .IsRequired() + .HasMaxLength(40); + + b.HasKey("Id"); + + b.HasIndex("CommitSha") + .IsUnique(); + + b.HasIndex("InstanceId"); + + b.ToTable("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Author") + .IsRequired(); + + b.Property("BodyAtMerge") + .IsRequired(); + + b.Property("Comment"); + + b.Property("MergedAt"); + + b.Property("MergedById"); + + b.Property("Number") + .IsRequired(); + + b.Property("PrimaryRevisionInformationId"); + + b.Property("PullRequestRevision") + .IsRequired(); + + b.Property("TitleAtMerge") + .IsRequired(); + + b.Property("Url") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("MergedById"); + + b.HasIndex("PrimaryRevisionInformationId") + .IsUnique(); + + b.ToTable("TestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AdministrationRights"); + + b.Property("CanonicalName") + .IsRequired(); + + b.Property("CreatedAt") + .IsRequired(); + + b.Property("CreatedById"); + + b.Property("Enabled") + .IsRequired(); + + b.Property("InstanceManagerRights"); + + b.Property("LastPasswordUpdate"); + + b.Property("Name") + .IsRequired(); + + b.Property("PasswordHash"); + + b.Property("SystemIdentifier"); + + b.HasKey("Id"); + + b.HasIndex("CanonicalName") + .IsUnique(); + + b.HasIndex("CreatedById"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AlphaId"); + + b.Property("AlphaIsActive"); + + b.Property("BravoId"); + + b.HasKey("Id"); + + b.HasIndex("AlphaId"); + + b.HasIndex("BravoId"); + + b.ToTable("WatchdogReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("ChatSettings") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings") + .WithMany("Channels") + .HasForeignKey("ChatSettingsId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.HasOne("Tgstation.Server.Host.Models.Job", "Job") + .WithMany() + .HasForeignKey("JobId"); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("CompileJobs") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamDaemonSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamMakerSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.HasOne("Tgstation.Server.Host.Models.WatchdogReattachInformation", "WatchdogReattachInformation") + .WithMany() + .HasForeignKey("WatchdogReattachInformationId"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("InstanceUsers") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.User") + .WithMany("InstanceUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy") + .WithMany() + .HasForeignKey("CancelledById"); + + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("Jobs") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy") + .WithMany() + .HasForeignKey("StartedById") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob") + .WithMany() + .HasForeignKey("CompileJobId"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("RepositorySettings") + .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("ActiveTestMerges") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge") + .WithMany("RevisonInformations") + .HasForeignKey("TestMergeId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("RevisionInformations") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy") + .WithMany("TestMerges") + .HasForeignKey("MergedById") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation") + .WithOne("PrimaryTestMerge") + .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy") + .WithMany("CreatedUsers") + .HasForeignKey("CreatedById"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha") + .WithMany() + .HasForeignKey("AlphaId"); + + b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo") + .WithMany() + .HasForeignKey("BravoId"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Tgstation.Server.Host/Models/Migrations/20180918020726_MYAddMinimumSecurity.cs b/src/Tgstation.Server.Host/Models/Migrations/20180918020726_MYAddMinimumSecurity.cs new file mode 100644 index 0000000000..13a103ac32 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/Migrations/20180918020726_MYAddMinimumSecurity.cs @@ -0,0 +1,45 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Host.Migrations +{ + /// + /// Add the and columns for MySQL/MariaDB + /// + public partial class MYAddMinimumSecurity : Migration + { + /// + /// Applies the migration + /// + /// The to use + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ApiValidationSecurityLevel", + table: "DreamMakerSettings", + nullable: false, + defaultValue: (int)DreamDaemonSecurity.Safe); + + migrationBuilder.AddColumn( + name: "MinimumSecurityLevel", + table: "CompileJobs", + nullable: false, + defaultValue: (int)DreamDaemonSecurity.Safe); + } + + /// + /// Unapplies the migration + /// + /// The to use + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ApiValidationSecurityLevel", + table: "DreamMakerSettings"); + + migrationBuilder.DropColumn( + name: "MinimumSecurityLevel", + table: "CompileJobs"); + } + } +} diff --git a/src/Tgstation.Server.Host/Models/Migrations/20180918021228_MSAddMinimumSecurity.Designer.cs b/src/Tgstation.Server.Host/Models/Migrations/20180918021228_MSAddMinimumSecurity.Designer.cs new file mode 100644 index 0000000000..0abec67697 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/Migrations/20180918021228_MSAddMinimumSecurity.Designer.cs @@ -0,0 +1,676 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Migrations +{ + [DbContext(typeof(SqlServerDatabaseContext))] + [Migration("20180918021228_MSAddMinimumSecurity")] + partial class MSAddMinimumSecurity + { + /// + /// Builds the target model + /// + /// The to use + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ConnectionString") + .IsRequired(); + + b.Property("Enabled"); + + b.Property("InstanceId"); + + b.Property("Name") + .IsRequired(); + + b.Property("Provider"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("ChatBots"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ChatSettingsId"); + + b.Property("DiscordChannelId") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("IrcChannel"); + + b.Property("IsAdminChannel") + .IsRequired(); + + b.Property("IsUpdatesChannel") + .IsRequired(); + + b.Property("IsWatchdogChannel") + .IsRequired(); + + b.Property("Tag"); + + b.HasKey("Id"); + + b.HasIndex("ChatSettingsId", "DiscordChannelId") + .IsUnique() + .HasFilter("[DiscordChannelId] IS NOT NULL"); + + b.HasIndex("ChatSettingsId", "IrcChannel") + .IsUnique() + .HasFilter("[IrcChannel] IS NOT NULL"); + + b.ToTable("ChatChannels"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ByondVersion") + .IsRequired(); + + b.Property("DirectoryName"); + + b.Property("DmeName"); + + b.Property("JobId"); + + b.Property("MinimumSecurityLevel"); + + b.Property("Output"); + + b.Property("RevisionInformationId"); + + b.HasKey("Id"); + + b.HasIndex("DirectoryName"); + + b.HasIndex("JobId"); + + b.HasIndex("RevisionInformationId"); + + b.ToTable("CompileJobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AccessToken"); + + b.Property("AllowWebClient") + .IsRequired(); + + b.Property("AutoStart") + .IsRequired(); + + b.Property("InstanceId"); + + b.Property("PrimaryPort"); + + b.Property("ProcessId"); + + b.Property("SecondaryPort"); + + b.Property("SecurityLevel"); + + b.Property("SoftRestart") + .IsRequired(); + + b.Property("SoftShutdown") + .IsRequired(); + + b.Property("StartupTimeout"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamDaemonSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ApiValidationPort"); + + b.Property("ApiValidationSecurityLevel"); + + b.Property("InstanceId"); + + b.Property("ProjectName"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("DreamMakerSettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AutoUpdateInterval"); + + b.Property("ConfigurationType"); + + b.Property("Name") + .IsRequired(); + + b.Property("Online") + .IsRequired(); + + b.Property("Path") + .IsRequired(); + + b.Property("WatchdogReattachInformationId"); + + b.HasKey("Id"); + + b.HasIndex("Path") + .IsUnique(); + + b.HasIndex("WatchdogReattachInformationId"); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("ByondRights") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("ChatBotRights") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("ConfigurationRights") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("DreamDaemonRights") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("DreamMakerRights") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("InstanceId"); + + b.Property("InstanceUserRights") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("RepositoryRights") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.HasIndex("UserId", "InstanceId") + .IsUnique(); + + b.ToTable("InstanceUsers"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CancelRight") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("CancelRightsType") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("Cancelled") + .IsRequired(); + + b.Property("CancelledById"); + + b.Property("Description") + .IsRequired(); + + b.Property("ExceptionDetails"); + + b.Property("InstanceId"); + + b.Property("StartedAt") + .IsRequired(); + + b.Property("StartedById"); + + b.Property("StoppedAt"); + + b.HasKey("Id"); + + b.HasIndex("CancelledById"); + + b.HasIndex("InstanceId"); + + b.HasIndex("StartedById"); + + b.ToTable("Jobs"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AccessIdentifier") + .IsRequired(); + + b.Property("ChatChannelsJson") + .IsRequired(); + + b.Property("ChatCommandsJson") + .IsRequired(); + + b.Property("CompileJobId"); + + b.Property("IsPrimary"); + + b.Property("Port"); + + b.Property("ProcessId"); + + b.Property("RebootState"); + + b.Property("ServerCommandsJson") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("CompileJobId"); + + b.ToTable("ReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AccessToken"); + + b.Property("AccessUser"); + + b.Property("AutoUpdatesKeepTestMerges") + .IsRequired(); + + b.Property("AutoUpdatesSynchronize") + .IsRequired(); + + b.Property("CommitterEmail") + .IsRequired(); + + b.Property("CommitterName") + .IsRequired(); + + b.Property("InstanceId"); + + b.Property("PushTestMergeCommits") + .IsRequired(); + + b.Property("ShowTestMergeCommitters") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("InstanceId") + .IsUnique(); + + b.ToTable("RepositorySettings"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("RevisionInformationId"); + + b.Property("TestMergeId"); + + b.HasKey("Id"); + + b.HasIndex("RevisionInformationId"); + + b.HasIndex("TestMergeId"); + + b.ToTable("RevInfoTestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("CommitSha") + .IsRequired() + .HasMaxLength(40); + + b.Property("InstanceId"); + + b.Property("OriginCommitSha") + .IsRequired() + .HasMaxLength(40); + + b.HasKey("Id"); + + b.HasIndex("CommitSha") + .IsUnique(); + + b.HasIndex("InstanceId"); + + b.ToTable("RevisionInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Author") + .IsRequired(); + + b.Property("BodyAtMerge") + .IsRequired(); + + b.Property("Comment"); + + b.Property("MergedAt"); + + b.Property("MergedById"); + + b.Property("Number") + .IsRequired(); + + b.Property("PrimaryRevisionInformationId"); + + b.Property("PullRequestRevision") + .IsRequired(); + + b.Property("TitleAtMerge") + .IsRequired(); + + b.Property("Url") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("MergedById"); + + b.HasIndex("PrimaryRevisionInformationId") + .IsUnique() + .HasFilter("[PrimaryRevisionInformationId] IS NOT NULL"); + + b.ToTable("TestMerges"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AdministrationRights") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("CanonicalName") + .IsRequired(); + + b.Property("CreatedAt") + .IsRequired(); + + b.Property("CreatedById"); + + b.Property("Enabled") + .IsRequired(); + + b.Property("InstanceManagerRights") + .HasConversion(new ValueConverter(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0))); + + b.Property("LastPasswordUpdate"); + + b.Property("Name") + .IsRequired(); + + b.Property("PasswordHash"); + + b.Property("SystemIdentifier"); + + b.HasKey("Id"); + + b.HasIndex("CanonicalName") + .IsUnique(); + + b.HasIndex("CreatedById"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("AlphaId"); + + b.Property("AlphaIsActive"); + + b.Property("BravoId"); + + b.HasKey("Id"); + + b.HasIndex("AlphaId"); + + b.HasIndex("BravoId"); + + b.ToTable("WatchdogReattachInformations"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("ChatSettings") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b => + { + b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings") + .WithMany("Channels") + .HasForeignKey("ChatSettingsId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b => + { + b.HasOne("Tgstation.Server.Host.Models.Job", "Job") + .WithMany() + .HasForeignKey("JobId"); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("CompileJobs") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamDaemonSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("DreamMakerSettings") + .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b => + { + b.HasOne("Tgstation.Server.Host.Models.WatchdogReattachInformation", "WatchdogReattachInformation") + .WithMany() + .HasForeignKey("WatchdogReattachInformationId"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("InstanceUsers") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.User") + .WithMany("InstanceUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy") + .WithMany() + .HasForeignKey("CancelledById"); + + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("Jobs") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy") + .WithMany() + .HasForeignKey("StartedById") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob") + .WithMany() + .HasForeignKey("CompileJobId"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithOne("RepositorySettings") + .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation") + .WithMany("ActiveTestMerges") + .HasForeignKey("RevisionInformationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge") + .WithMany("RevisonInformations") + .HasForeignKey("TestMergeId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance") + .WithMany("RevisionInformations") + .HasForeignKey("InstanceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy") + .WithMany("TestMerges") + .HasForeignKey("MergedById") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation") + .WithOne("PrimaryTestMerge") + .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.User", b => + { + b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy") + .WithMany("CreatedUsers") + .HasForeignKey("CreatedById"); + }); + + modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b => + { + b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha") + .WithMany() + .HasForeignKey("AlphaId"); + + b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo") + .WithMany() + .HasForeignKey("BravoId"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Tgstation.Server.Host/Models/Migrations/20180918021228_MSAddMinimumSecurity.cs b/src/Tgstation.Server.Host/Models/Migrations/20180918021228_MSAddMinimumSecurity.cs new file mode 100644 index 0000000000..78c8da9d83 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/Migrations/20180918021228_MSAddMinimumSecurity.cs @@ -0,0 +1,45 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Host.Migrations +{ + /// + /// Add the and columns for MSSQL + /// + public partial class MSAddMinimumSecurity : Migration + { + /// + /// Applies the migration + /// + /// The to use + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ApiValidationSecurityLevel", + table: "DreamMakerSettings", + nullable: false, + defaultValue: (int)DreamDaemonSecurity.Safe); + + migrationBuilder.AddColumn( + name: "MinimumSecurityLevel", + table: "CompileJobs", + nullable: false, + defaultValue: (int)DreamDaemonSecurity.Safe); + } + + /// + /// Unapplies the migration + /// + /// The to use + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ApiValidationSecurityLevel", + table: "DreamMakerSettings"); + + migrationBuilder.DropColumn( + name: "MinimumSecurityLevel", + table: "CompileJobs"); + } + } +} diff --git a/src/Tgstation.Server.Host/Models/Migrations/MySqlDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Models/Migrations/MySqlDatabaseContextModelSnapshot.cs index 7c82e23050..7658150957 100644 --- a/src/Tgstation.Server.Host/Models/Migrations/MySqlDatabaseContextModelSnapshot.cs +++ b/src/Tgstation.Server.Host/Models/Migrations/MySqlDatabaseContextModelSnapshot.cs @@ -2,7 +2,6 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Tgstation.Server.Host.Models.Migrations { @@ -13,7 +12,7 @@ namespace Tgstation.Server.Host.Models.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.1.2-rtm-30932") + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065") .HasAnnotation("Relational:MaxIdentifierLength", 64); modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b => @@ -90,6 +89,8 @@ namespace Tgstation.Server.Host.Models.Migrations b.Property("JobId"); + b.Property("MinimumSecurityLevel"); + b.Property("Output"); b.Property("RevisionInformationId"); @@ -155,6 +156,8 @@ namespace Tgstation.Server.Host.Models.Migrations b.Property("ApiValidationPort") .IsRequired(); + b.Property("ApiValidationSecurityLevel"); + b.Property("InstanceId"); b.Property("ProjectName"); diff --git a/src/Tgstation.Server.Host/Models/Migrations/SqlServerDatabaseContextModelSnapshot.cs b/src/Tgstation.Server.Host/Models/Migrations/SqlServerDatabaseContextModelSnapshot.cs index a17096d672..d8ea3e4706 100644 --- a/src/Tgstation.Server.Host/Models/Migrations/SqlServerDatabaseContextModelSnapshot.cs +++ b/src/Tgstation.Server.Host/Models/Migrations/SqlServerDatabaseContextModelSnapshot.cs @@ -14,7 +14,7 @@ namespace Tgstation.Server.Host.Models.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.1.2-rtm-30932") + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); @@ -98,6 +98,8 @@ namespace Tgstation.Server.Host.Models.Migrations b.Property("JobId"); + b.Property("MinimumSecurityLevel"); + b.Property("Output"); b.Property("RevisionInformationId"); @@ -161,6 +163,8 @@ namespace Tgstation.Server.Host.Models.Migrations b.Property("ApiValidationPort"); + b.Property("ApiValidationSecurityLevel"); + b.Property("InstanceId"); b.Property("ProjectName"); From b8f797b8e51e710b83ebb5768c2faf649b17bd83 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 17 Sep 2018 22:22:19 -0400 Subject: [PATCH 09/16] Handle pushing clamped DreamDaemonSecurity levels back to the user without modifying the db --- src/Tgstation.Server.Api/Models/DreamDaemon.cs | 2 +- .../Components/Watchdog/ISessionControllerFactory.cs | 2 +- src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs | 2 +- .../Components/Watchdog/SessionControllerFactory.cs | 7 ++++++- src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs | 2 +- .../Controllers/DreamDaemonController.cs | 7 ++++--- 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/Tgstation.Server.Api/Models/DreamDaemon.cs b/src/Tgstation.Server.Api/Models/DreamDaemon.cs index f78ffd2f15..ac982cf3d0 100644 --- a/src/Tgstation.Server.Api/Models/DreamDaemon.cs +++ b/src/Tgstation.Server.Api/Models/DreamDaemon.cs @@ -23,7 +23,7 @@ namespace Tgstation.Server.Api.Models public bool? Running { get; set; } /// - /// The current of + /// The current of . May be downgraded due to requirements of /// public DreamDaemonSecurity? CurrentSecurity { get; set; } diff --git a/src/Tgstation.Server.Host/Components/Watchdog/ISessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/ISessionControllerFactory.cs index 645af10171..4873ce5b21 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/ISessionControllerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/ISessionControllerFactory.cs @@ -13,7 +13,7 @@ namespace Tgstation.Server.Host.Components.Watchdog /// /// Create a from a freshly launch DreamDaemon instance /// - /// The to use + /// The to use. will be updated with the minumum required security level for the launch /// The to use /// The current if any /// If the of should be used diff --git a/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs index 6c8b65b05f..2002597284 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs @@ -56,7 +56,7 @@ namespace Tgstation.Server.Host.Components.Watchdog /// /// Changes the . If currently triggers a graceful restart /// - /// The new + /// The new . May be modified /// The for the operation /// A representing the running operation Task ChangeSettings(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken); diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs index b5ec781ba0..dae993db92 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs @@ -206,7 +206,7 @@ namespace Tgstation.Server.Host.Components.Watchdog try { //return the session controller for it - return new SessionController(new ReattachInformation + var result = new SessionController(new ReattachInformation { AccessIdentifier = accessIdentifier, Dmb = dmbProvider, @@ -217,6 +217,11 @@ namespace Tgstation.Server.Host.Components.Watchdog ChatCommandsJson = interopInfo.ChatCommandsJson, ServerCommandsJson = interopInfo.ServerCommandsJson, }, process, byondLock, byondTopicSender, chatJsonTrackingContext, context, chat, loggerFactory.CreateLogger(), launchParameters.SecurityLevel, launchParameters.StartupTimeout); + + //writeback launch parameter's fixed security level + launchParameters.SecurityLevel = securityLevelToUse; + + return result; } catch { diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs index 0a852be342..28a1eeb60c 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs @@ -150,7 +150,7 @@ namespace Tgstation.Server.Host.Components.Watchdog /// The value of /// The value of /// The value of - /// The initial value of + /// The initial value of . May be modified /// The value of /// The value of public Watchdog(IChat chat, ISessionControllerFactory sessionControllerFactory, IDmbFactory dmbFactory, IServerControl serverUpdater, ILogger logger, IReattachInfoHandler reattachInfoHandler, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, IEventConsumer eventConsumer, IJobManager jobManager, DreamDaemonLaunchParameters initialLaunchParameters, Api.Models.Instance instance, bool autoStart) diff --git a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs index 34d8158a05..85d7841d8c 100644 --- a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs +++ b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs @@ -196,10 +196,11 @@ namespace Tgstation.Server.Host.Controllers return BadRequest(new ErrorMessage { Message = "Primary port and secondary port cannot be the same!" }); var wd = instanceManager.GetInstance(Instance).Watchdog; - - //run these in parallel because they are equally as important - await Task.WhenAll(DatabaseContext.Save(cancellationToken), wd.ChangeSettings(current, cancellationToken)).ConfigureAwait(false); + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); + //run this second because current may be modified by it + await wd.ChangeSettings(current, cancellationToken).ConfigureAwait(false); + if (!oldSoftRestart.Value && current.SoftRestart.Value) await wd.Restart(true, cancellationToken).ConfigureAwait(false); else if (!oldSoftShutdown.Value && current.SoftShutdown.Value) From 486008cc4e6b433fd42b678ef600b80203a10afc Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 17 Sep 2018 22:26:44 -0400 Subject: [PATCH 10/16] Add launch security level to interop JSON --- .../Components/Interop/JsonFile.cs | 8 ++++- .../Watchdog/SessionControllerFactory.cs | 33 ++++++++++--------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Interop/JsonFile.cs b/src/Tgstation.Server.Host/Components/Interop/JsonFile.cs index 4c318c21c3..b7e5da7d3a 100644 --- a/src/Tgstation.Server.Host/Components/Interop/JsonFile.cs +++ b/src/Tgstation.Server.Host/Components/Interop/JsonFile.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Tgstation.Server.Api.Models; using Tgstation.Server.Api.Models.Internal; namespace Tgstation.Server.Host.Components.Interop @@ -41,7 +42,12 @@ namespace Tgstation.Server.Host.Components.Interop /// /// The of the launch /// - public RevisionInformation Revision { get; set; } + public Api.Models.Internal.RevisionInformation Revision { get; set; } + + /// + /// The level of the launch + /// + public DreamDaemonSecurity SecurityLevel { get; set; } /// /// The s in the launch diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs index dae993db92..9e4601e314 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs @@ -131,6 +131,22 @@ namespace Tgstation.Server.Host.Components.Watchdog //i changed this back from guids, hopefully i don't regret that string JsonFile(string name) => String.Format(CultureInfo.InvariantCulture, "{0}.{1}", name, JsonPostfix); + var securityLevelToUse = launchParameters.SecurityLevel.Value; + switch (dmbProvider.CompileJob.MinimumSecurityLevel) + { + case DreamDaemonSecurity.Ultrasafe: + break; + case DreamDaemonSecurity.Safe: + if (securityLevelToUse == DreamDaemonSecurity.Ultrasafe) + securityLevelToUse = DreamDaemonSecurity.Safe; + break; + case DreamDaemonSecurity.Trusted: + securityLevelToUse = DreamDaemonSecurity.Trusted; + break; + default: + throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid DreamDaemonSecurity value: {0}", dmbProvider.CompileJob.MinimumSecurityLevel)); + } + //setup interop files var interopInfo = new JsonFile { @@ -140,6 +156,7 @@ namespace Tgstation.Server.Host.Components.Watchdog ChatCommandsJson = JsonFile("chat_commands"), ServerCommandsJson = JsonFile("server_commands"), InstanceName = instance.Name, + SecurityLevel = securityLevelToUse, Revision = new Api.Models.Internal.RevisionInformation { CommitSha = dmbProvider.CompileJob.RevisionInformation.CommitSha, @@ -177,22 +194,6 @@ namespace Tgstation.Server.Host.Components.Watchdog //more sanitization here cause it uses the same scheme var parameters = String.Format(CultureInfo.InvariantCulture, "{2}={0}&{3}={1}", byondTopicSender.SanitizeString(application.Version.ToString()), byondTopicSender.SanitizeString(interopJsonFile), byondTopicSender.SanitizeString(Constants.DMParamHostVersion), byondTopicSender.SanitizeString(Constants.DMParamInfoJson)); - var securityLevelToUse = launchParameters.SecurityLevel.Value; - switch (dmbProvider.CompileJob.MinimumSecurityLevel) - { - case DreamDaemonSecurity.Ultrasafe: - break; - case DreamDaemonSecurity.Safe: - if (securityLevelToUse == DreamDaemonSecurity.Ultrasafe) - securityLevelToUse = DreamDaemonSecurity.Safe; - break; - case DreamDaemonSecurity.Trusted: - securityLevelToUse = DreamDaemonSecurity.Trusted; - break; - default: - throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid DreamDaemonSecurity value: {0}", dmbProvider.CompileJob.MinimumSecurityLevel)); - } - //important to run on all ports to allow port changing var arguments = String.Format(CultureInfo.InvariantCulture, "{0} -port {1} -ports 1-65535 {2}-close -{3} -verbose -public -params \"{4}\"", dmbProvider.DmbName, From db186b4457591ed9503b2b4031732e1898f7a5ce Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 17 Sep 2018 22:28:50 -0400 Subject: [PATCH 11/16] Add security level support to the V4 DMAPI --- src/DMAPI/tgs/v4/api.dm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/DMAPI/tgs/v4/api.dm b/src/DMAPI/tgs/v4/api.dm index bf1f506f0a..b5bc692f11 100644 --- a/src/DMAPI/tgs/v4/api.dm +++ b/src/DMAPI/tgs/v4/api.dm @@ -32,6 +32,7 @@ var/chat_commands_json_path var/server_commands_json_path var/reboot_mode = TGS_REBOOT_MODE_NORMAL + var/security_level var/list/intercepted_message_queue @@ -63,7 +64,6 @@ return access_identifier = cached_json["accessIdentifier"] - instance_name = text2num(cached_json["instanceName"]) server_commands_json_path = cached_json["serverCommandsJson"] if(cached_json["apiValidateOnly"]) @@ -71,6 +71,7 @@ Export(TGS4_COMM_VALIDATE, list(TGS4_PARAMETER_DATA = "[minimum_required_security_level]")) del(world) + security_level = cached_json["securityLevel"] chat_channels_json_path = cached_json["chatChannelsJson"] chat_commands_json_path = cached_json["chatCommandsJson"] src.event_handler = event_handler @@ -295,6 +296,9 @@ channel.custom_tag = channel_json["tag"] return channel +/datum/tgs_api/v4/SecurityLevel() + return security_level + /* The MIT License From 3b4aa9a162faea58023c1130e691236b3660bca1 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 17 Sep 2018 23:07:16 -0400 Subject: [PATCH 12/16] Fix off by shebang --- src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs index 97cdcc8519..adaf2a55ef 100644 --- a/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs +++ b/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs @@ -156,7 +156,7 @@ namespace Tgstation.Server.Host.Components.Compiler cancellationToken.ThrowIfCancellationRequested(); } - if (!controller.Lifetime.IsCompleted) + if (controller.Lifetime.IsCompleted) { var validationStatus = controller.ApiValidationStatus; logger.LogTrace("API validation status: {0}", validationStatus); From cd658c7b291063dc0b802f916015c3b8a5de6993 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 17 Sep 2018 23:08:06 -0400 Subject: [PATCH 13/16] Remove redundant log --- src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs b/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs index 0625499ac9..1c3a909b98 100644 --- a/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs +++ b/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs @@ -125,8 +125,6 @@ namespace Tgstation.Server.Host.Components.Compiler if (job == null) throw new ArgumentNullException(nameof(job)); - logger.LogTrace("Loading compile job {0}...", job.Id); - CompileJob finalCompileJob = null; //now load the entire compile job tree await databaseContextFactory.UseContext(async db => finalCompileJob = await db.CompileJobs.Where(x => x.Id == job.Id) From ce42715f22179f27b39bec064b0d41eec83169d0 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 17 Sep 2018 23:11:57 -0400 Subject: [PATCH 14/16] Buff nuget preview versions --- src/Tgstation.Server.Api/Tgstation.Server.Api.csproj | 2 +- src/Tgstation.Server.Client/Tgstation.Server.Client.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj b/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj index ea5523602b..d3a57d45bd 100644 --- a/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj +++ b/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj @@ -17,7 +17,7 @@ 4.0.0.0 json web api tgstation-server tgstation ss13 byond Prototype release - 4.0.0.0-preview6003 + 4.0.0.0-preview6004 diff --git a/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj b/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj index 6bb5253884..97763f5f38 100644 --- a/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj +++ b/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj @@ -3,7 +3,7 @@ netstandard2.0 Full - 4.0.0.0-preview9109 + 4.0.0.0-preview9110 true Cyberboss /tg/station 13 From ce70e8bcabe8121633a8b627e1c1604add48dc61 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 17 Sep 2018 23:22:36 -0400 Subject: [PATCH 15/16] Add SetSecurityLevel right and prevent setting ultrasafe mode --- src/Tgstation.Server.Api/Rights/DreamMakerRights.cs | 6 +++++- .../Controllers/DreamMakerController.cs | 12 +++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs b/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs index 0c4612b834..e9b04645f4 100644 --- a/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs +++ b/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs @@ -35,6 +35,10 @@ namespace Tgstation.Server.Api.Rights /// /// User may list and read all s /// - CompileJobs = 32 + CompileJobs = 32, + /// + /// User may modify + /// + SetSecurityLevel = 64 } } diff --git a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs index 1f4a9bb81c..cb147a5e79 100644 --- a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs +++ b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs @@ -101,12 +101,15 @@ namespace Tgstation.Server.Host.Controllers } /// - [TgsAuthorize(DreamMakerRights.SetDme | DreamMakerRights.SetApiValidationPort)] + [TgsAuthorize(DreamMakerRights.SetDme | DreamMakerRights.SetApiValidationPort | DreamMakerRights.SetApiValidationPort)] public override async Task Update([FromBody] DreamMaker model, CancellationToken cancellationToken) { if (model.ApiValidationPort == 0) return BadRequest(new ErrorMessage { Message = "API Validation port cannot be 0!" }); + if (model.ApiValidationSecurityLevel == DreamDaemonSecurity.Ultrasafe) + return BadRequest(new ErrorMessage { Message = "This version of TGS does not support the ultrasafe DreamDaemon configuration!" }); + var hostModel = await DatabaseContext.DreamMakerSettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); if (hostModel == null) return StatusCode((int)HttpStatusCode.Gone); @@ -128,6 +131,13 @@ namespace Tgstation.Server.Host.Controllers hostModel.ApiValidationPort = model.ApiValidationPort; } + if (model.ApiValidationSecurityLevel.HasValue) + { + if (!AuthenticationContext.InstanceUser.DreamMakerRights.Value.HasFlag(DreamMakerRights.SetSecurityLevel)) + return Forbid(); + hostModel.ApiValidationSecurityLevel = model.ApiValidationSecurityLevel; + } + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); return await Read(cancellationToken).ConfigureAwait(false); } From 5fe97a9a06da256268f3e60c49bdd50b9961b280 Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Mon, 17 Sep 2018 23:35:34 -0400 Subject: [PATCH 16/16] Fix build --- src/Tgstation.Server.Host/Components/Interop/JsonFile.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tgstation.Server.Host/Components/Interop/JsonFile.cs b/src/Tgstation.Server.Host/Components/Interop/JsonFile.cs index b7e5da7d3a..10eebe5c13 100644 --- a/src/Tgstation.Server.Host/Components/Interop/JsonFile.cs +++ b/src/Tgstation.Server.Host/Components/Interop/JsonFile.cs @@ -40,7 +40,7 @@ namespace Tgstation.Server.Host.Components.Interop public string ServerCommandsJson { get; set; } /// - /// The of the launch + /// The of the launch /// public Api.Models.Internal.RevisionInformation Revision { get; set; }