diff --git a/src/DMAPI/tgs.dm b/src/DMAPI/tgs.dm index 6e55567544..6645ce51ff 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_SECURITY_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_SECURITY_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..b2f9b19cdd 100644 --- a/src/DMAPI/tgs/core/datum.dm +++ b/src/DMAPI/tgs/core/datum.dm @@ -46,6 +46,9 @@ TGS_PROTECT_DATUM(/datum/tgs_api) /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..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 @@ -48,7 +49,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!") @@ -63,14 +64,14 @@ return access_identifier = cached_json["accessIdentifier"] - instance_name = text2num(cached_json["instanceName"]) server_commands_json_path = cached_json["serverCommandsJson"] 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) + security_level = cached_json["securityLevel"] chat_channels_json_path = cached_json["chatChannelsJson"] chat_commands_json_path = cached_json["chatCommandsJson"] src.event_handler = event_handler @@ -187,7 +188,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]") @@ -295,6 +296,9 @@ channel.custom_tag = channel_json["tag"] return channel +/datum/tgs_api/v4/SecurityLevel() + return security_level + /* The MIT License 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.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.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.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 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) diff --git a/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs index 15c46067f8..adaf2a55ef 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 @@ -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); @@ -154,15 +156,36 @@ namespace Tgstation.Server.Host.Components.Compiler cancellationToken.ThrowIfCancellationRequested(); } - if (!controller.Lifetime.IsCompleted) + 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.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.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!"); } } @@ -243,7 +266,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)); @@ -254,8 +277,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"); @@ -356,13 +379,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, dreamMakerSettings.ApiValidationSecurityLevel.Value, 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/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/Interop/JsonFile.cs b/src/Tgstation.Server.Host/Components/Interop/JsonFile.cs index 4c318c21c3..10eebe5c13 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 @@ -39,9 +40,14 @@ namespace Tgstation.Server.Host.Components.Interop public string ServerCommandsJson { get; set; } /// - /// The of the launch + /// 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/ApiValidationStatus.cs b/src/Tgstation.Server.Host/Components/Watchdog/ApiValidationStatus.cs new file mode 100644 index 0000000000..46e282802d --- /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 validation request was malformed + /// + BadValidationRequest, + /// + /// Valid API. The game must be run with a minimum security level of + /// + 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/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/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/SessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs index 20b1115531..b71d7f0c62 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 = ApiValidationStatus.RequiresSafe; + break; + case DreamDaemonSecurity.Ultrasafe: + apiValidationStatus = ApiValidationStatus.RequiresUltrasafe; + break; + case DreamDaemonSecurity.Trusted: + apiValidationStatus = 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..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, @@ -182,7 +199,7 @@ namespace Tgstation.Server.Host.Components.Watchdog dmbProvider.DmbName, primaryPort ? launchParameters.PrimaryPort : launchParameters.SecondaryPort, launchParameters.AllowWebClient.Value ? "-webclient " : String.Empty, - SecurityWord(launchParameters.SecurityLevel.Value), + SecurityWord(securityLevelToUse), parameters); //launch dd @@ -190,7 +207,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, @@ -200,7 +217,12 @@ 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); + + //writeback launch parameter's fixed security level + launchParameters.SecurityLevel = securityLevelToUse; + + return result; } catch { @@ -247,7 +269,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 { 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) 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); } 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, 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");