Merge pull request #672 from Cyberboss/DMAPISecurityRequirement [NugetDeploy]

DMAPI security requirement
This commit is contained in:
Jordan Brown
2018-09-17 23:53:55 -04:00
committed by GitHub
33 changed files with 1650 additions and 60 deletions
+10 -1
View File
@@ -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
+7 -2
View File
@@ -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
+3
View File
@@ -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
+4 -1
View File
@@ -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
+8 -4
View File
@@ -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
@@ -23,7 +23,7 @@ namespace Tgstation.Server.Api.Models
public bool? Running { get; set; }
/// <summary>
/// The current <see cref="DreamDaemonSecurity"/> of <see cref="DreamDaemon"/>
/// The current <see cref="DreamDaemonSecurity"/> of <see cref="DreamDaemon"/>. May be downgraded due to requirements of <see cref="ActiveCompileJob"/>
/// </summary>
public DreamDaemonSecurity? CurrentSecurity { get; set; }
@@ -17,5 +17,11 @@ namespace Tgstation.Server.Api.Models
/// </summary>
[Required]
public ushort? ApiValidationPort { get; set; }
/// <summary>
/// The <see cref="DreamDaemonSecurity"/> level used to validate the DMAPI
/// </summary>
[Required]
public DreamDaemonSecurity? ApiValidationSecurityLevel { get; set; }
}
}
@@ -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
/// </summary>
public Guid? DirectoryName { get; set; }
/// <summary>
/// The minimum <see cref="DreamDaemonSecurity"/> required to run the <see cref="CompileJob"/>'s output
/// </summary>
[Required]
public DreamDaemonSecurity? MinimumSecurityLevel { get; set; }
}
}
@@ -35,6 +35,10 @@ namespace Tgstation.Server.Api.Rights
/// <summary>
/// User may list and read all <see cref="Models.CompileJob"/>s
/// </summary>
CompileJobs = 32
CompileJobs = 32,
/// <summary>
/// User may modify <see cref="Models.DreamMaker.ApiValidationSecurityLevel"/>
/// </summary>
SetSecurityLevel = 64
}
}
@@ -17,7 +17,7 @@
<FileVersion>4.0.0.0</FileVersion>
<PackageTags>json web api tgstation-server tgstation ss13 byond</PackageTags>
<PackageReleaseNotes>Prototype release</PackageReleaseNotes>
<Version>4.0.0.0-preview6003</Version>
<Version>4.0.0.0-preview6004</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
@@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<DebugType>Full</DebugType>
<Version>4.0.0.0-preview9109</Version>
<Version>4.0.0.0-preview9110</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>Cyberboss</Authors>
<Company>/tg/station 13</Company>
@@ -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)
@@ -125,8 +125,8 @@ namespace Tgstation.Server.Host.Components.Compiler
/// <param name="byondLock">The current <see cref="IByondExecutableLock"/></param>
/// <param name="portToUse">The port to use for API validation</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task{TResult}"/> resulting in <see langword="true"/> if the DMAPI was successfully validated, <see langword="false"/> otherwise</returns>
async Task<bool> VerifyApi(uint timeout, DreamDaemonSecurity securityLevel, Models.CompileJob job, IByondExecutableLock byondLock, ushort portToUse, CancellationToken cancellationToken)
/// <returns>A <see cref="Task"/> representing the running operation</returns>
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
}
/// <inheritdoc />
public async Task<Models.CompileJob> Compile(Models.RevisionInformation revisionInformation, Api.Models.DreamMaker dreamMakerSettings, DreamDaemonSecurity securityLevel, uint apiValidateTimeout, IRepository repository, CancellationToken cancellationToken)
public async Task<Models.CompileJob> 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<string> { 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...");
@@ -15,11 +15,10 @@ namespace Tgstation.Server.Host.Components.Compiler
/// </summary>
/// <param name="revisionInformation">The <see cref="Models.RevisionInformation"/> being compiled from the <paramref name="repository"/></param>
/// <param name="dreamMakerSettings">The <see cref="Api.Models.DreamMaker"/> for the compile</param>
/// <param name="securityLevel">The <see cref="DreamDaemonSecurity"/> level allowed for API validation</param>
/// <param name="apiValidateTimeout">The time in seconds to wait while validating the API</param>
/// <param name="repository">The <see cref="IRepository"/> to copy from</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task{TResult}"/> resulting in the partially populated <see cref="Models.CompileJob"/> for the operation. In particular, note the <see cref="Models.CompileJob.RevisionInformation"/> field will only have it's <see cref="Api.Models.Internal.RevisionInformation.CommitSha"/> field populated</returns>
Task<Models.CompileJob> Compile(Models.RevisionInformation revisionInformation, Api.Models.DreamMaker dreamMakerSettings, DreamDaemonSecurity securityLevel, uint apiValidateTimeout, IRepository repository, CancellationToken cancellationToken);
Task<Models.CompileJob> Compile(Models.RevisionInformation revisionInformation, Api.Models.DreamMaker dreamMakerSettings, uint apiValidateTimeout, IRepository repository, CancellationToken cancellationToken);
}
}
@@ -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;
@@ -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; }
/// <summary>
/// The <see cref="RevisionInformation"/> of the launch
/// The <see cref="Api.Models.Internal.RevisionInformation"/> of the launch
/// </summary>
public RevisionInformation Revision { get; set; }
public Api.Models.Internal.RevisionInformation Revision { get; set; }
/// <summary>
/// The <see cref="DreamDaemonSecurity"/> level of the launch
/// </summary>
public DreamDaemonSecurity SecurityLevel { get; set; }
/// <summary>
/// The <see cref="TestMerge"/>s in the launch
@@ -0,0 +1,33 @@
namespace Tgstation.Server.Host.Components.Watchdog
{
/// <summary>
/// Status of DMAPI validation
/// </summary>
enum ApiValidationStatus
{
/// <summary>
/// The DMAPI never contacted the server for validation
/// </summary>
NeverValidated,
/// <summary>
/// The server was contacted for validation but it was never requested
/// </summary>
UnaskedValidationRequest,
/// <summary>
/// The validation request was malformed
/// </summary>
BadValidationRequest,
/// <summary>
/// Valid API. The game must be run with a minimum security level of <see cref="Api.Models.DreamDaemonSecurity.Safe"/>
/// </summary>
RequiresSafe,
/// <summary>
/// Valid API. The game must be run with a security level of <see cref="Api.Models.DreamDaemonSecurity.Trusted"/>
/// </summary>
RequiresTrusted,
/// <summary>
/// Valid API. The game must be run with a minimum security level of <see cref="Api.Models.DreamDaemonSecurity.Ultrasafe"/>
/// </summary>
RequiresUltrasafe
}
}
@@ -25,9 +25,9 @@ namespace Tgstation.Server.Host.Components.Watchdog
bool TerminationWasRequested { get; }
/// <summary>
/// If the DMAPI was validated. This field may only be access once <see cref="IProcessBase.Lifetime"/> completes
/// The DMAPI <see cref="Components.Watchdog.ApiValidationStatus"/>
/// </summary>
bool ApiValidated { get; }
ApiValidationStatus ApiValidationStatus { get; }
/// <summary>
/// The <see cref="IDmbProvider"/> being used
@@ -13,7 +13,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
/// <summary>
/// Create a <see cref="ISessionController"/> from a freshly launch DreamDaemon instance
/// </summary>
/// <param name="launchParameters">The <see cref="DreamDaemonLaunchParameters"/> to use</param>
/// <param name="launchParameters">The <see cref="DreamDaemonLaunchParameters"/> to use. <see cref="DreamDaemonLaunchParameters.SecurityLevel"/> will be updated with the minumum required security level for the launch</param>
/// <param name="dmbProvider">The <see cref="IDmbProvider"/> to use</param>
/// <param name="currentByondLock">The current <see cref="IByondExecutableLock"/> if any</param>
/// <param name="primaryPort">If the <see cref="DreamDaemonLaunchParameters.PrimaryPort"/> of <paramref name="launchParameters"/> should be used</param>
@@ -56,7 +56,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
/// <summary>
/// Changes the <see cref="ActiveLaunchParameters"/>. If currently <see cref="Running"/> triggers a graceful restart
/// </summary>
/// <param name="launchParameters">The new <see cref="DreamDaemonLaunchParameters"/></param>
/// <param name="launchParameters">The new <see cref="DreamDaemonLaunchParameters"/>. May be modified</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
/// <returns>A <see cref="Task"/> representing the running operation</returns>
Task ChangeSettings(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken);
@@ -29,13 +29,13 @@ namespace Tgstation.Server.Host.Components.Watchdog
}
/// <inheritdoc />
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
/// </summary>
readonly ILogger<SessionController> logger;
/// <summary>
/// The <see cref="DreamDaemonSecurity"/> level the <see cref="process"/> was launched with
/// </summary>
readonly DreamDaemonSecurity? launchSecurityLevel;
/// <summary>
/// The <see cref="TaskCompletionSource{TResult}"/> <see cref="SetPort(ushort, CancellationToken)"/> waits on when DreamDaemon currently has it's ports closed
/// </summary>
@@ -151,9 +156,9 @@ namespace Tgstation.Server.Host.Components.Watchdog
bool disposed;
/// <summary>
/// If the DMAPI was validated
/// The <see cref="ApiValidationStatus"/> for the <see cref="SessionController"/>
/// </summary>
bool apiValidated;
ApiValidationStatus apiValidationStatus;
/// <summary>
/// If <see cref="process"/> should be kept alive instead
@@ -171,8 +176,9 @@ namespace Tgstation.Server.Host.Components.Watchdog
/// <param name="chat">The value of <see cref="chat"/></param>
/// <param name="chatJsonTrackingContext">The value of <see cref="chatJsonTrackingContext"/></param>
/// <param name="logger">The value of <see cref="logger"/></param>
/// <param name="launchSecurityLevel">The value of <see cref="launchSecurityLevel"/></param>
/// <param name="startupTimeout">The optional time to wait before failing the <see cref="LaunchResult"/></param>
public SessionController(ReattachInformation reattachInformation, IProcess process, IByondExecutableLock byondLock, IByondTopicSender byondTopicSender, IJsonTrackingContext chatJsonTrackingContext, ICommContext interopContext, IChat chat, ILogger<SessionController> logger, uint? startupTimeout)
public SessionController(ReattachInformation reattachInformation, IProcess process, IByondExecutableLock byondLock, IByondTopicSender byondTopicSender, IJsonTrackingContext chatJsonTrackingContext, ICommContext interopContext, IChat chat, ILogger<SessionController> 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<object>();
@@ -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<DreamDaemonSecurity>(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)
@@ -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<SessionController>(), launchParameters.StartupTimeout);
}, process, byondLock, byondTopicSender, chatJsonTrackingContext, context, chat, loggerFactory.CreateLogger<SessionController>(), 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<SessionController>(), null);
return new SessionController(reattachInformation, process, byondLock, byondTopicSender, chatJsonTrackingContext, context, chat, loggerFactory.CreateLogger<SessionController>(), null, null);
}
catch
{
@@ -150,7 +150,7 @@ namespace Tgstation.Server.Host.Components.Watchdog
/// <param name="byondTopicSender">The value of <see cref="byondTopicSender"/></param>
/// <param name="eventConsumer">The value of <see cref="eventConsumer"/></param>
/// <param name="jobManager">The value of <see cref="jobManager"/></param>
/// <param name="initialLaunchParameters">The initial value of <see cref="ActiveLaunchParameters"/></param>
/// <param name="initialLaunchParameters">The initial value of <see cref="ActiveLaunchParameters"/>. May be modified</param>
/// <param name="instance">The value of <see cref="instance"/></param>
/// <param name="autoStart">The value of <see cref="autoStart"/></param>
public Watchdog(IChat chat, ISessionControllerFactory sessionControllerFactory, IDmbFactory dmbFactory, IServerControl serverUpdater, ILogger<Watchdog> logger, IReattachInfoHandler reattachInfoHandler, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, IEventConsumer eventConsumer, IJobManager jobManager, DreamDaemonLaunchParameters initialLaunchParameters, Api.Models.Instance instance, bool autoStart)
@@ -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)
@@ -101,12 +101,15 @@ namespace Tgstation.Server.Host.Controllers
}
/// <inheritdoc />
[TgsAuthorize(DreamMakerRights.SetDme | DreamMakerRights.SetApiValidationPort)]
[TgsAuthorize(DreamMakerRights.SetDme | DreamMakerRights.SetApiValidationPort | DreamMakerRights.SetApiValidationPort)]
public override async Task<IActionResult> 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);
}
@@ -136,7 +136,8 @@ namespace Tgstation.Server.Host.Controllers
},
DreamMakerSettings = new DreamMakerSettings
{
ApiValidationPort = 1339
ApiValidationPort = 1339,
ApiValidationSecurityLevel = DreamDaemonSecurity.Safe
},
Name = model.Name,
Online = false,
@@ -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
{
@@ -0,0 +1,648 @@
// <auto-generated />
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
{
/// <summary>
/// Builds the target model
/// </summary>
/// <param name="modelBuilder">The <see cref="ModelBuilder"/> to use</param>
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<long>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConnectionString")
.IsRequired();
b.Property<bool?>("Enabled");
b.Property<long>("InstanceId");
b.Property<string>("Name")
.IsRequired();
b.Property<int?>("Provider");
b.HasKey("Id");
b.HasIndex("InstanceId");
b.HasIndex("Name")
.IsUnique();
b.ToTable("ChatBots");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd();
b.Property<long>("ChatSettingsId");
b.Property<ulong?>("DiscordChannelId");
b.Property<string>("IrcChannel");
b.Property<bool?>("IsAdminChannel")
.IsRequired();
b.Property<bool?>("IsUpdatesChannel")
.IsRequired();
b.Property<bool?>("IsWatchdogChannel")
.IsRequired();
b.Property<string>("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<long>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ByondVersion")
.IsRequired();
b.Property<Guid?>("DirectoryName");
b.Property<string>("DmeName");
b.Property<long?>("JobId");
b.Property<int>("MinimumSecurityLevel");
b.Property<string>("Output");
b.Property<long>("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<long>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("AccessToken");
b.Property<bool?>("AllowWebClient")
.IsRequired();
b.Property<bool?>("AutoStart")
.IsRequired();
b.Property<long>("InstanceId");
b.Property<ushort?>("PrimaryPort")
.IsRequired();
b.Property<int?>("ProcessId");
b.Property<ushort?>("SecondaryPort")
.IsRequired();
b.Property<int>("SecurityLevel");
b.Property<bool?>("SoftRestart")
.IsRequired();
b.Property<bool?>("SoftShutdown")
.IsRequired();
b.Property<uint?>("StartupTimeout")
.IsRequired();
b.HasKey("Id");
b.HasIndex("InstanceId")
.IsUnique();
b.ToTable("DreamDaemonSettings");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd();
b.Property<ushort?>("ApiValidationPort")
.IsRequired();
b.Property<int>("ApiValidationSecurityLevel");
b.Property<long>("InstanceId");
b.Property<string>("ProjectName");
b.HasKey("Id");
b.HasIndex("InstanceId")
.IsUnique();
b.ToTable("DreamMakerSettings");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd();
b.Property<uint?>("AutoUpdateInterval")
.IsRequired();
b.Property<int>("ConfigurationType");
b.Property<string>("Name")
.IsRequired();
b.Property<bool?>("Online")
.IsRequired();
b.Property<string>("Path")
.IsRequired();
b.Property<long?>("WatchdogReattachInformationId");
b.HasKey("Id");
b.HasIndex("Path")
.IsUnique();
b.HasIndex("WatchdogReattachInformationId");
b.ToTable("Instances");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ByondRights");
b.Property<ulong>("ChatBotRights");
b.Property<ulong>("ConfigurationRights");
b.Property<ulong>("DreamDaemonRights");
b.Property<ulong>("DreamMakerRights");
b.Property<long>("InstanceId");
b.Property<ulong>("InstanceUserRights");
b.Property<ulong>("RepositoryRights");
b.Property<long?>("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<long>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong?>("CancelRight");
b.Property<ulong?>("CancelRightsType");
b.Property<bool?>("Cancelled")
.IsRequired();
b.Property<long?>("CancelledById");
b.Property<string>("Description")
.IsRequired();
b.Property<string>("ExceptionDetails");
b.Property<long>("InstanceId");
b.Property<DateTimeOffset?>("StartedAt")
.IsRequired();
b.Property<long>("StartedById");
b.Property<DateTimeOffset?>("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<long>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("AccessIdentifier")
.IsRequired();
b.Property<string>("ChatChannelsJson")
.IsRequired();
b.Property<string>("ChatCommandsJson")
.IsRequired();
b.Property<long?>("CompileJobId");
b.Property<bool>("IsPrimary");
b.Property<ushort>("Port");
b.Property<int>("ProcessId");
b.Property<int>("RebootState");
b.Property<string>("ServerCommandsJson")
.IsRequired();
b.HasKey("Id");
b.HasIndex("CompileJobId");
b.ToTable("ReattachInformations");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("AccessToken");
b.Property<string>("AccessUser");
b.Property<bool?>("AutoUpdatesKeepTestMerges")
.IsRequired();
b.Property<bool?>("AutoUpdatesSynchronize")
.IsRequired();
b.Property<string>("CommitterEmail")
.IsRequired();
b.Property<string>("CommitterName")
.IsRequired();
b.Property<long>("InstanceId");
b.Property<bool?>("PushTestMergeCommits")
.IsRequired();
b.Property<bool?>("ShowTestMergeCommitters")
.IsRequired();
b.HasKey("Id");
b.HasIndex("InstanceId")
.IsUnique();
b.ToTable("RepositorySettings");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd();
b.Property<long>("RevisionInformationId");
b.Property<long>("TestMergeId");
b.HasKey("Id");
b.HasIndex("RevisionInformationId");
b.HasIndex("TestMergeId");
b.ToTable("RevInfoTestMerges");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("CommitSha")
.IsRequired()
.HasMaxLength(40);
b.Property<long>("InstanceId");
b.Property<string>("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<long>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Author")
.IsRequired();
b.Property<string>("BodyAtMerge")
.IsRequired();
b.Property<string>("Comment");
b.Property<DateTimeOffset>("MergedAt");
b.Property<long>("MergedById");
b.Property<int?>("Number")
.IsRequired();
b.Property<long?>("PrimaryRevisionInformationId");
b.Property<string>("PullRequestRevision")
.IsRequired();
b.Property<string>("TitleAtMerge")
.IsRequired();
b.Property<string>("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<long>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("AdministrationRights");
b.Property<string>("CanonicalName")
.IsRequired();
b.Property<DateTimeOffset?>("CreatedAt")
.IsRequired();
b.Property<long?>("CreatedById");
b.Property<bool?>("Enabled")
.IsRequired();
b.Property<ulong>("InstanceManagerRights");
b.Property<DateTimeOffset?>("LastPasswordUpdate");
b.Property<string>("Name")
.IsRequired();
b.Property<string>("PasswordHash");
b.Property<string>("SystemIdentifier");
b.HasKey("Id");
b.HasIndex("CanonicalName")
.IsUnique();
b.HasIndex("CreatedById");
b.ToTable("Users");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd();
b.Property<long?>("AlphaId");
b.Property<bool>("AlphaIsActive");
b.Property<long?>("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
}
}
}
@@ -0,0 +1,45 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Tgstation.Server.Api.Models;
namespace Tgstation.Server.Host.Migrations
{
/// <summary>
/// Add the <see cref="Api.Models.Internal.CompileJob.MinimumSecurityLevel"/> and <see cref="DreamMaker.ApiValidationSecurityLevel"/> columns for MySQL/MariaDB
/// </summary>
public partial class MYAddMinimumSecurity : Migration
{
/// <summary>
/// Applies the migration
/// </summary>
/// <param name="migrationBuilder">The <see cref="MigrationBuilder"/> to use</param>
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "ApiValidationSecurityLevel",
table: "DreamMakerSettings",
nullable: false,
defaultValue: (int)DreamDaemonSecurity.Safe);
migrationBuilder.AddColumn<int>(
name: "MinimumSecurityLevel",
table: "CompileJobs",
nullable: false,
defaultValue: (int)DreamDaemonSecurity.Safe);
}
/// <summary>
/// Unapplies the migration
/// </summary>
/// <param name="migrationBuilder">The <see cref="MigrationBuilder"/> to use</param>
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ApiValidationSecurityLevel",
table: "DreamMakerSettings");
migrationBuilder.DropColumn(
name: "MinimumSecurityLevel",
table: "CompileJobs");
}
}
}
@@ -0,0 +1,676 @@
// <auto-generated />
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
{
/// <summary>
/// Builds the target model
/// </summary>
/// <param name="modelBuilder">The <see cref="ModelBuilder"/> to use</param>
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<long>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ConnectionString")
.IsRequired();
b.Property<bool?>("Enabled");
b.Property<long>("InstanceId");
b.Property<string>("Name")
.IsRequired();
b.Property<int?>("Provider");
b.HasKey("Id");
b.HasIndex("InstanceId");
b.HasIndex("Name")
.IsUnique();
b.ToTable("ChatBots");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<long>("ChatSettingsId");
b.Property<decimal?>("DiscordChannelId")
.HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
b.Property<string>("IrcChannel");
b.Property<bool?>("IsAdminChannel")
.IsRequired();
b.Property<bool?>("IsUpdatesChannel")
.IsRequired();
b.Property<bool?>("IsWatchdogChannel")
.IsRequired();
b.Property<string>("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<long>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ByondVersion")
.IsRequired();
b.Property<Guid?>("DirectoryName");
b.Property<string>("DmeName");
b.Property<long?>("JobId");
b.Property<int>("MinimumSecurityLevel");
b.Property<string>("Output");
b.Property<long>("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<long>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("AccessToken");
b.Property<bool?>("AllowWebClient")
.IsRequired();
b.Property<bool?>("AutoStart")
.IsRequired();
b.Property<long>("InstanceId");
b.Property<int>("PrimaryPort");
b.Property<int?>("ProcessId");
b.Property<int>("SecondaryPort");
b.Property<int>("SecurityLevel");
b.Property<bool?>("SoftRestart")
.IsRequired();
b.Property<bool?>("SoftShutdown")
.IsRequired();
b.Property<long>("StartupTimeout");
b.HasKey("Id");
b.HasIndex("InstanceId")
.IsUnique();
b.ToTable("DreamDaemonSettings");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<int>("ApiValidationPort");
b.Property<int>("ApiValidationSecurityLevel");
b.Property<long>("InstanceId");
b.Property<string>("ProjectName");
b.HasKey("Id");
b.HasIndex("InstanceId")
.IsUnique();
b.ToTable("DreamMakerSettings");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<long>("AutoUpdateInterval");
b.Property<int>("ConfigurationType");
b.Property<string>("Name")
.IsRequired();
b.Property<bool?>("Online")
.IsRequired();
b.Property<string>("Path")
.IsRequired();
b.Property<long?>("WatchdogReattachInformationId");
b.HasKey("Id");
b.HasIndex("Path")
.IsUnique();
b.HasIndex("WatchdogReattachInformationId");
b.ToTable("Instances");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<decimal>("ByondRights")
.HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
b.Property<decimal>("ChatBotRights")
.HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
b.Property<decimal>("ConfigurationRights")
.HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
b.Property<decimal>("DreamDaemonRights")
.HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
b.Property<decimal>("DreamMakerRights")
.HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
b.Property<long>("InstanceId");
b.Property<decimal>("InstanceUserRights")
.HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
b.Property<decimal>("RepositoryRights")
.HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
b.Property<long?>("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<long>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<decimal?>("CancelRight")
.HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
b.Property<decimal?>("CancelRightsType")
.HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
b.Property<bool?>("Cancelled")
.IsRequired();
b.Property<long?>("CancelledById");
b.Property<string>("Description")
.IsRequired();
b.Property<string>("ExceptionDetails");
b.Property<long>("InstanceId");
b.Property<DateTimeOffset?>("StartedAt")
.IsRequired();
b.Property<long>("StartedById");
b.Property<DateTimeOffset?>("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<long>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("AccessIdentifier")
.IsRequired();
b.Property<string>("ChatChannelsJson")
.IsRequired();
b.Property<string>("ChatCommandsJson")
.IsRequired();
b.Property<long?>("CompileJobId");
b.Property<bool>("IsPrimary");
b.Property<int>("Port");
b.Property<int>("ProcessId");
b.Property<int>("RebootState");
b.Property<string>("ServerCommandsJson")
.IsRequired();
b.HasKey("Id");
b.HasIndex("CompileJobId");
b.ToTable("ReattachInformations");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("AccessToken");
b.Property<string>("AccessUser");
b.Property<bool?>("AutoUpdatesKeepTestMerges")
.IsRequired();
b.Property<bool?>("AutoUpdatesSynchronize")
.IsRequired();
b.Property<string>("CommitterEmail")
.IsRequired();
b.Property<string>("CommitterName")
.IsRequired();
b.Property<long>("InstanceId");
b.Property<bool?>("PushTestMergeCommits")
.IsRequired();
b.Property<bool?>("ShowTestMergeCommitters")
.IsRequired();
b.HasKey("Id");
b.HasIndex("InstanceId")
.IsUnique();
b.ToTable("RepositorySettings");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<long>("RevisionInformationId");
b.Property<long>("TestMergeId");
b.HasKey("Id");
b.HasIndex("RevisionInformationId");
b.HasIndex("TestMergeId");
b.ToTable("RevInfoTestMerges");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("CommitSha")
.IsRequired()
.HasMaxLength(40);
b.Property<long>("InstanceId");
b.Property<string>("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<long>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("Author")
.IsRequired();
b.Property<string>("BodyAtMerge")
.IsRequired();
b.Property<string>("Comment");
b.Property<DateTimeOffset>("MergedAt");
b.Property<long>("MergedById");
b.Property<int?>("Number")
.IsRequired();
b.Property<long?>("PrimaryRevisionInformationId");
b.Property<string>("PullRequestRevision")
.IsRequired();
b.Property<string>("TitleAtMerge")
.IsRequired();
b.Property<string>("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<long>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<decimal>("AdministrationRights")
.HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
b.Property<string>("CanonicalName")
.IsRequired();
b.Property<DateTimeOffset?>("CreatedAt")
.IsRequired();
b.Property<long?>("CreatedById");
b.Property<bool?>("Enabled")
.IsRequired();
b.Property<decimal>("InstanceManagerRights")
.HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
b.Property<DateTimeOffset?>("LastPasswordUpdate");
b.Property<string>("Name")
.IsRequired();
b.Property<string>("PasswordHash");
b.Property<string>("SystemIdentifier");
b.HasKey("Id");
b.HasIndex("CanonicalName")
.IsUnique();
b.HasIndex("CreatedById");
b.ToTable("Users");
});
modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<long?>("AlphaId");
b.Property<bool>("AlphaIsActive");
b.Property<long?>("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
}
}
}
@@ -0,0 +1,45 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Tgstation.Server.Api.Models;
namespace Tgstation.Server.Host.Migrations
{
/// <summary>
/// Add the <see cref="Api.Models.Internal.CompileJob.MinimumSecurityLevel"/> and <see cref="DreamMaker.ApiValidationSecurityLevel"/> columns for MSSQL
/// </summary>
public partial class MSAddMinimumSecurity : Migration
{
/// <summary>
/// Applies the migration
/// </summary>
/// <param name="migrationBuilder">The <see cref="MigrationBuilder"/> to use</param>
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "ApiValidationSecurityLevel",
table: "DreamMakerSettings",
nullable: false,
defaultValue: (int)DreamDaemonSecurity.Safe);
migrationBuilder.AddColumn<int>(
name: "MinimumSecurityLevel",
table: "CompileJobs",
nullable: false,
defaultValue: (int)DreamDaemonSecurity.Safe);
}
/// <summary>
/// Unapplies the migration
/// </summary>
/// <param name="migrationBuilder">The <see cref="MigrationBuilder"/> to use</param>
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ApiValidationSecurityLevel",
table: "DreamMakerSettings");
migrationBuilder.DropColumn(
name: "MinimumSecurityLevel",
table: "CompileJobs");
}
}
}
@@ -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<long?>("JobId");
b.Property<int>("MinimumSecurityLevel");
b.Property<string>("Output");
b.Property<long>("RevisionInformationId");
@@ -155,6 +156,8 @@ namespace Tgstation.Server.Host.Models.Migrations
b.Property<ushort?>("ApiValidationPort")
.IsRequired();
b.Property<int>("ApiValidationSecurityLevel");
b.Property<long>("InstanceId");
b.Property<string>("ProjectName");
@@ -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<long?>("JobId");
b.Property<int>("MinimumSecurityLevel");
b.Property<string>("Output");
b.Property<long>("RevisionInformationId");
@@ -161,6 +163,8 @@ namespace Tgstation.Server.Host.Models.Migrations
b.Property<int>("ApiValidationPort");
b.Property<int>("ApiValidationSecurityLevel");
b.Property<long>("InstanceId");
b.Property<string>("ProjectName");