mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-29 16:11:05 +01:00
Adds Oauth framework
This commit is contained in:
+4
-4
@@ -3,10 +3,10 @@
|
||||
<!-- Integration tests will ensure they match across the board -->
|
||||
<Import Project="ControlPanelVersion.props" />
|
||||
<PropertyGroup>
|
||||
<TgsCoreVersion>4.6.0</TgsCoreVersion>
|
||||
<TgsConfigVersion>2.1.1</TgsConfigVersion>
|
||||
<TgsApiVersion>7.4.0</TgsApiVersion>
|
||||
<TgsClientVersion>8.4.0</TgsClientVersion>
|
||||
<TgsCoreVersion>4.7.0</TgsCoreVersion>
|
||||
<TgsConfigVersion>2.2.0</TgsConfigVersion>
|
||||
<TgsApiVersion>8.0.0</TgsApiVersion>
|
||||
<TgsClientVersion>9.0.0</TgsClientVersion>
|
||||
<TgsDmapiVersion>5.2.8</TgsDmapiVersion>
|
||||
<TgsHostWatchdogVersion>1.1.0</TgsHostWatchdogVersion>
|
||||
<TgsContainerScriptVersion>1.1.1</TgsContainerScriptVersion>
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Net.Http.Headers;
|
||||
using System.Net.Mime;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Tgstation.Server.Api.Models;
|
||||
|
||||
namespace Tgstation.Server.Api
|
||||
{
|
||||
@@ -17,25 +18,35 @@ namespace Tgstation.Server.Api
|
||||
public sealed class ApiHeaders
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="ApiVersion"/> header key
|
||||
/// The <see cref="ApiVersion"/> header key.
|
||||
/// </summary>
|
||||
public const string ApiVersionHeader = "Api";
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="InstanceId"/> header key
|
||||
/// The <see cref="InstanceId"/> header key.
|
||||
/// </summary>
|
||||
public const string InstanceIdHeader = "Instance";
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="OAuthProvider"/> header key.
|
||||
/// </summary>
|
||||
public const string OAuthProviderHeader = "OAuthProvider";
|
||||
|
||||
/// <summary>
|
||||
/// The JWT authentication header scheme
|
||||
/// </summary>
|
||||
public const string JwtAuthenticationScheme = "bearer";
|
||||
public const string BearerAuthenticationScheme = "bearer";
|
||||
|
||||
/// <summary>
|
||||
/// The JWT authentication header scheme
|
||||
/// </summary>
|
||||
public const string BasicAuthenticationScheme = "basic";
|
||||
|
||||
/// <summary>
|
||||
/// The JWT authentication header scheme
|
||||
/// </summary>
|
||||
public const string OAuthAuthenticationScheme = "oauth";
|
||||
|
||||
/// <summary>
|
||||
/// The current <see cref="System.Reflection.AssemblyName"/>
|
||||
/// </summary>
|
||||
@@ -47,7 +58,7 @@ namespace Tgstation.Server.Api
|
||||
public static readonly Version Version = AssemblyName.Version.Semver();
|
||||
|
||||
/// <summary>
|
||||
/// The instance <see cref="Models.EntityId.Id"/> being accessed
|
||||
/// The instance <see cref="EntityId.Id"/> being accessed
|
||||
/// </summary>
|
||||
public long? InstanceId { get; set; }
|
||||
|
||||
@@ -82,9 +93,14 @@ namespace Tgstation.Server.Api
|
||||
public string? Password { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If the header uses password or JWT authentication
|
||||
/// The <see cref="Models.OAuthProvider"/> the <see cref="Token"/> is for, if any.
|
||||
/// </summary>
|
||||
public bool IsTokenAuthentication => Token != null;
|
||||
public OAuthProvider? OAuthProvider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If the header uses password or TGS JWT authentication.
|
||||
/// </summary>
|
||||
public bool IsTokenAuthentication => Token != null && !OAuthProvider.HasValue;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a given <paramref name="otherVersion"/> is compatible with our own
|
||||
@@ -98,12 +114,15 @@ namespace Tgstation.Server.Api
|
||||
/// </summary>
|
||||
/// <param name="userAgent">The value of <see cref="UserAgent"/></param>
|
||||
/// <param name="token">The value of <see cref="Token"/></param>
|
||||
public ApiHeaders(ProductHeaderValue userAgent, string token) : this(userAgent, token, null, null)
|
||||
/// <param name="oauthProvider">The value of <see cref="OAuthProvider"/>.</param>
|
||||
public ApiHeaders(ProductHeaderValue userAgent, string token, OAuthProvider? oauthProvider = null) : this(userAgent, token, null, null)
|
||||
{
|
||||
if (userAgent == null)
|
||||
throw new ArgumentNullException(nameof(userAgent));
|
||||
if (token == null)
|
||||
throw new ArgumentNullException(nameof(token));
|
||||
|
||||
OAuthProvider = oauthProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -191,7 +210,20 @@ namespace Tgstation.Server.Api
|
||||
switch (scheme.ToLowerInvariant())
|
||||
#pragma warning restore CA1308 // Normalize strings to uppercase
|
||||
{
|
||||
case JwtAuthenticationScheme:
|
||||
case OAuthAuthenticationScheme:
|
||||
if (requestHeaders.Headers.TryGetValue(OAuthProviderHeader, out StringValues oauthProviderValues))
|
||||
{
|
||||
var oauthProviderString = oauthProviderValues.First();
|
||||
if (Enum.TryParse<OAuthProvider>(oauthProviderString, out var oauthProvider))
|
||||
OAuthProvider = oauthProvider;
|
||||
else
|
||||
AddError(HeaderTypes.OAuthProvider, "Invalid OAuth provider!");
|
||||
}
|
||||
else
|
||||
AddError(HeaderTypes.OAuthProvider, $"Missing {OAuthProviderHeader} header!");
|
||||
|
||||
goto case BearerAuthenticationScheme;
|
||||
case BearerAuthenticationScheme:
|
||||
Token = parameter;
|
||||
break;
|
||||
case BasicAuthenticationScheme:
|
||||
@@ -253,7 +285,7 @@ namespace Tgstation.Server.Api
|
||||
/// Set <see cref="HttpRequestHeaders"/> using the <see cref="ApiHeaders"/>. This initially clears <paramref name="headers"/>
|
||||
/// </summary>
|
||||
/// <param name="headers">The <see cref="HttpRequestHeaders"/> to set</param>
|
||||
/// <param name="instanceId">The instance <see cref="Models.EntityId.Id"/> for the request</param>
|
||||
/// <param name="instanceId">The instance <see cref="EntityId.Id"/> for the request</param>
|
||||
public void SetRequestHeaders(HttpRequestHeaders headers, long? instanceId = null)
|
||||
{
|
||||
if (headers == null)
|
||||
@@ -263,12 +295,16 @@ namespace Tgstation.Server.Api
|
||||
|
||||
headers.Clear();
|
||||
headers.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
|
||||
if (IsTokenAuthentication)
|
||||
headers.Authorization = new AuthenticationHeaderValue(JwtAuthenticationScheme, Token);
|
||||
else
|
||||
if (!IsTokenAuthentication)
|
||||
headers.Authorization = new AuthenticationHeaderValue(
|
||||
BasicAuthenticationScheme,
|
||||
Convert.ToBase64String(Encoding.UTF8.GetBytes($"{Username}:{Password}")));
|
||||
else
|
||||
{
|
||||
headers.Authorization = new AuthenticationHeaderValue(BearerAuthenticationScheme, Token);
|
||||
if (OAuthProvider.HasValue)
|
||||
headers.Add(OAuthProviderHeader, OAuthProvider.ToString());
|
||||
}
|
||||
|
||||
headers.UserAgent.Add(new ProductInfoHeaderValue(UserAgent));
|
||||
headers.Add(ApiVersionHeader, new ProductHeaderValue(AssemblyName.Name, ApiVersion.ToString()).ToString());
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace Tgstation.Server.Api
|
||||
{
|
||||
@@ -24,13 +24,18 @@ namespace Tgstation.Server.Api
|
||||
Accept = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Api header.
|
||||
/// <see cref="ApiHeaders.ApiVersionHeader"/>.
|
||||
/// </summary>
|
||||
Api = 4,
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="Microsoft.Net.Http.Headers.HeaderNames.Authorization"/>
|
||||
/// </summary>
|
||||
Authorization = 8
|
||||
Authorization = 8,
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="ApiHeaders.OAuthProviderHeader"/>.
|
||||
/// </summary>
|
||||
OAuthProvider,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -582,5 +582,11 @@ namespace Tgstation.Server.Api.Models
|
||||
/// </summary>
|
||||
[Description("The requested port is either already in use by TGS or could not be allocated!")]
|
||||
PortNotAvailable,
|
||||
|
||||
/// <summary>
|
||||
/// Attempted to set <see cref="User.OAuthConnections"/> for the admin user.
|
||||
/// </summary>
|
||||
[Description("The admin user cannot use OAuth connections!")]
|
||||
AdminUserCannotOAuth,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Tgstation.Server.Api.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a valid OAuth connection.
|
||||
/// </summary>
|
||||
public class OAuthConnection
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="OAuthProvider"/> of the <see cref="OAuthConnection"/>.
|
||||
/// </summary>]
|
||||
[EnumDataType(typeof(OAuthProvider))]
|
||||
public OAuthProvider Provider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the user in the <see cref="Provider"/>.
|
||||
/// </summary>
|
||||
public ulong ExternalUserId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Tgstation.Server.Api.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// List of OAuth providers supported by TGS
|
||||
/// </summary>
|
||||
public enum OAuthProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// https://github.com
|
||||
/// </summary>
|
||||
GitHub,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Tgstation.Server.Api.Models
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Tgstation.Server.Api.Models
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class User : Internal.User
|
||||
@@ -17,5 +19,10 @@
|
||||
/// The <see cref="User"/> who created this <see cref="User"/>
|
||||
/// </summary>
|
||||
public Internal.User? CreatedBy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of <see cref="OAuthConnection"/>s associated with the <see cref="User"/>.
|
||||
/// </summary>
|
||||
public ICollection<OAuthConnection>? OAuthConnections { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,5 +42,10 @@ namespace Tgstation.Server.Api.Rights
|
||||
/// User can list and download <see cref="Models.LogFile"/>s.
|
||||
/// </summary>
|
||||
DownloadLogs = 32,
|
||||
|
||||
/// <summary>
|
||||
/// User can modify their own <see cref="Models.User.OAuthConnections"/>.
|
||||
/// </summary>
|
||||
EditOwnOAuthConnections = 64,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-ef": {
|
||||
"version": "3.1.7",
|
||||
"version": "3.1.10",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
]
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace Tgstation.Server.Host.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// OAuth options for GitHub
|
||||
/// </summary>
|
||||
sealed class GitHubOAuthConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// The GitHub client ID.
|
||||
/// </summary>
|
||||
public string ClientId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The GitHub client secret.
|
||||
/// </summary>
|
||||
public string ClientSecret { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using Tgstation.Server.Api.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
@@ -15,6 +17,11 @@ namespace Tgstation.Server.Host.Configuration
|
||||
/// </summary>
|
||||
const uint DefaultTokenExpiryMinutes = 15;
|
||||
|
||||
/// <summary>
|
||||
/// Default value of <see cref="OAuthTokenExpiryMinutes"/>.
|
||||
/// </summary>
|
||||
const uint DefaultOAuthTokenExpiryMinutes = 60 * 24; // 1 day
|
||||
|
||||
/// <summary>
|
||||
/// Default value of <see cref="TokenClockSkewMinutes"/>.
|
||||
/// </summary>
|
||||
@@ -26,12 +33,12 @@ namespace Tgstation.Server.Host.Configuration
|
||||
const uint DefaultTokenSigningKeyByteAmount = 256;
|
||||
|
||||
/// <summary>
|
||||
/// Amount of minutes until generated <see cref="Api.Models.Token"/>s expire.
|
||||
/// Amount of minutes until <see cref="Token"/>s generated from passwords expire.
|
||||
/// </summary>
|
||||
public uint TokenExpiryMinutes { get; set; } = DefaultTokenExpiryMinutes;
|
||||
|
||||
/// <summary>
|
||||
/// Amount of minutes to skew the clock for <see cref="Api.Models.Token"/> validation.
|
||||
/// Amount of minutes to skew the clock for <see cref="Token"/> validation.
|
||||
/// </summary>
|
||||
public uint TokenClockSkewMinutes { get; set; } = DefaultTokenClockSkewMinutes;
|
||||
|
||||
@@ -40,9 +47,19 @@ namespace Tgstation.Server.Host.Configuration
|
||||
/// </summary>
|
||||
public uint TokenSigningKeyByteCount { get; set; } = DefaultTokenSigningKeyByteAmount;
|
||||
|
||||
/// <summary>
|
||||
/// Amount of minutes until <see cref="Token"/>s generated from OAuth logins expire.
|
||||
/// </summary>
|
||||
public uint OAuthTokenExpiryMinutes { get; set; } = DefaultOAuthTokenExpiryMinutes;
|
||||
|
||||
/// <summary>
|
||||
/// A custom token signing key. Overrides <see cref="TokenSigningKeyByteCount"/>.
|
||||
/// </summary>
|
||||
public string CustomTokenSigningKeyBase64 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// OAuth options for GitHub.
|
||||
/// </summary>
|
||||
public GitHubOAuthConfiguration GitHubOAuth { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Octokit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -115,14 +113,6 @@ namespace Tgstation.Server.Host.Controllers
|
||||
fileLoggingConfiguration = fileLoggingConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(fileLoggingConfigurationOptions));
|
||||
}
|
||||
|
||||
ObjectResult RateLimit(RateLimitExceededException exception)
|
||||
{
|
||||
Logger.LogWarning(exception, "Exceeded GitHub rate limit!");
|
||||
var secondsString = Math.Ceiling((exception.Reset - DateTimeOffset.Now).TotalSeconds).ToString(CultureInfo.InvariantCulture);
|
||||
Response.Headers.Add("Retry-After", new StringValues(secondsString));
|
||||
return StatusCode(HttpStatusCode.TooManyRequests, new ErrorMessage(ErrorCode.GitHubApiRateLimit));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to download and apply an update with a given <paramref name="newVersion"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -2,8 +2,11 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Octokit;
|
||||
using Serilog.Context;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Mime;
|
||||
@@ -60,7 +63,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// <param name="authenticationContextFactory">The <see cref="IAuthenticationContextFactory"/> for the <see cref="ApiController"/></param>
|
||||
/// <param name="logger">The value of <see cref="Logger"/></param>
|
||||
/// <param name="requireHeaders">The value of <see cref="requireHeaders"/></param>
|
||||
public ApiController(
|
||||
protected ApiController(
|
||||
IDatabaseContext databaseContext,
|
||||
IAuthenticationContextFactory authenticationContextFactory,
|
||||
ILogger<ApiController> logger,
|
||||
@@ -115,6 +118,22 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// <returns>A <see cref="HttpStatusCode.Created"/> <see cref="ObjectResult"/> with the given <paramref name="payload"/>.</returns>
|
||||
protected ObjectResult Created(object payload) => StatusCode((int)HttpStatusCode.Created, payload);
|
||||
|
||||
/// <summary>
|
||||
/// 429 response for a given <paramref name="rateLimitException"/>.
|
||||
/// </summary>
|
||||
/// <param name="rateLimitException">The <see cref="RateLimitExceededException"/> that occurred.</param>
|
||||
/// <returns>A <see cref="HttpStatusCode.TooManyRequests"/> <see cref="ObjectResult"/>.</returns>
|
||||
protected ObjectResult RateLimit(RateLimitExceededException rateLimitException)
|
||||
{
|
||||
if (rateLimitException == null)
|
||||
throw new ArgumentNullException(nameof(rateLimitException));
|
||||
|
||||
Logger.LogWarning(rateLimitException, "Exceeded GitHub rate limit!");
|
||||
var secondsString = Math.Ceiling((rateLimitException.Reset - DateTimeOffset.Now).TotalSeconds).ToString(CultureInfo.InvariantCulture);
|
||||
Response.Headers.Add(HeaderNames.RetryAfter, secondsString);
|
||||
return StatusCode(HttpStatusCode.TooManyRequests, new ErrorMessage(ErrorCode.GitHubApiRateLimit));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs validation a request.
|
||||
/// </summary>
|
||||
|
||||
@@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Octokit;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -17,6 +18,7 @@ using Tgstation.Server.Host.Core;
|
||||
using Tgstation.Server.Host.Database;
|
||||
using Tgstation.Server.Host.Models;
|
||||
using Tgstation.Server.Host.Security;
|
||||
using Tgstation.Server.Host.Security.OAuth;
|
||||
using Tgstation.Server.Host.System;
|
||||
using Wangkanai.Detection;
|
||||
|
||||
@@ -53,6 +55,11 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// </summary>
|
||||
readonly IIdentityCache identityCache;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IOAuthProviders"/> for the <see cref="HomeController"/>.
|
||||
/// </summary>
|
||||
readonly IOAuthProviders oAuthProviders;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IBrowserResolver"/> for the <see cref="HomeController"/>
|
||||
/// </summary>
|
||||
@@ -78,6 +85,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// <param name="cryptographySuite">The value of <see cref="cryptographySuite"/></param>
|
||||
/// <param name="assemblyInformationProvider">The value of <see cref="assemblyInformationProvider"/></param>
|
||||
/// <param name="identityCache">The value of <see cref="identityCache"/></param>
|
||||
/// <param name="oAuthProviders">The value of <see cref="oAuthProviders"/>.</param>
|
||||
/// <param name="browserResolver">The value of <see cref="browserResolver"/></param>
|
||||
/// <param name="generalConfigurationOptions">The <see cref="IOptions{TOptions}"/> containing the value of <see cref="generalConfiguration"/>.</param>
|
||||
/// <param name="controlPanelConfigurationOptions">The <see cref="IOptions{TOptions}"/> containing the value of <see cref="controlPanelConfiguration"/></param>
|
||||
@@ -90,6 +98,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
ICryptographySuite cryptographySuite,
|
||||
IAssemblyInformationProvider assemblyInformationProvider,
|
||||
IIdentityCache identityCache,
|
||||
IOAuthProviders oAuthProviders,
|
||||
IBrowserResolver browserResolver,
|
||||
IOptions<GeneralConfiguration> generalConfigurationOptions,
|
||||
IOptions<ControlPanelConfiguration> controlPanelConfigurationOptions,
|
||||
@@ -106,6 +115,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
|
||||
this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
|
||||
this.identityCache = identityCache ?? throw new ArgumentNullException(nameof(identityCache));
|
||||
this.oAuthProviders = oAuthProviders ?? throw new ArgumentNullException(nameof(oAuthProviders));
|
||||
this.browserResolver = browserResolver;
|
||||
generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
|
||||
controlPanelConfiguration = controlPanelConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(controlPanelConfigurationOptions));
|
||||
@@ -140,7 +150,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
if (controlPanelConfiguration.Enable && browserResolver.Browser.Type != BrowserType.Generic)
|
||||
{
|
||||
Logger.LogDebug("Unauthorized browser request (User-Agent: \"{0}\"), redirecting to control panel...", browserResolver.UserAgent);
|
||||
return Redirect(Application.ControlPanelRoute);
|
||||
return Redirect(Core.Application.ControlPanelRoute);
|
||||
}
|
||||
|
||||
return ApiHeaders == null ? HeadersIssue() : Unauthorized();
|
||||
@@ -168,26 +178,56 @@ namespace Tgstation.Server.Host.Controllers
|
||||
if (ApiHeaders.IsTokenAuthentication)
|
||||
return BadRequest(new ErrorMessage(ErrorCode.TokenWithToken));
|
||||
|
||||
ISystemIdentity systemIdentity;
|
||||
try
|
||||
{
|
||||
// trust the system over the database because a user's name can change while still having the same SID
|
||||
systemIdentity = await systemIdentityFactory.CreateSystemIdentity(ApiHeaders.Username, ApiHeaders.Password, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
{
|
||||
systemIdentity = null;
|
||||
}
|
||||
var oAuthLogin = ApiHeaders.OAuthProvider.HasValue;
|
||||
|
||||
ISystemIdentity systemIdentity = null;
|
||||
if (!oAuthLogin)
|
||||
try
|
||||
{
|
||||
// trust the system over the database because a user's name can change while still having the same SID
|
||||
systemIdentity = await systemIdentityFactory.CreateSystemIdentity(ApiHeaders.Username, ApiHeaders.Password, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (NotImplementedException ex)
|
||||
{
|
||||
Logger.LogTrace(ex, "System identities not implemented!");
|
||||
}
|
||||
|
||||
using (systemIdentity)
|
||||
{
|
||||
// Get the user from the database
|
||||
IQueryable<Models.User> query = DatabaseContext.Users.AsQueryable();
|
||||
string canonicalName = Models.User.CanonicalizeName(ApiHeaders.Username);
|
||||
if (systemIdentity == null)
|
||||
query = query.Where(x => x.CanonicalName == canonicalName);
|
||||
if (oAuthLogin)
|
||||
{
|
||||
ulong? externalUserId;
|
||||
try
|
||||
{
|
||||
externalUserId = await oAuthProviders
|
||||
.GetValidator(ApiHeaders.OAuthProvider.Value)
|
||||
.ValidateResponseCode(ApiHeaders.Token, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (RateLimitExceededException ex)
|
||||
{
|
||||
return RateLimit(ex);
|
||||
}
|
||||
|
||||
if (!externalUserId.HasValue)
|
||||
return Unauthorized();
|
||||
|
||||
query = query.Where(
|
||||
x => x.OAuthConnections.Any(
|
||||
y => y.Provider == ApiHeaders.OAuthProvider.Value
|
||||
&& y.ExternalUserId == externalUserId.Value));
|
||||
}
|
||||
else
|
||||
query = query.Where(x => x.CanonicalName == canonicalName || x.SystemIdentifier == systemIdentity.Uid);
|
||||
{
|
||||
string canonicalName = Models.User.CanonicalizeName(ApiHeaders.Username);
|
||||
if (systemIdentity == null)
|
||||
query = query.Where(x => x.CanonicalName == canonicalName);
|
||||
else
|
||||
query = query.Where(x => x.CanonicalName == canonicalName || x.SystemIdentifier == systemIdentity.Uid);
|
||||
}
|
||||
|
||||
var users = await query.Select(x => new Models.User
|
||||
{
|
||||
Id = x.Id,
|
||||
@@ -213,32 +253,33 @@ namespace Tgstation.Server.Host.Controllers
|
||||
var originalHash = user.PasswordHash;
|
||||
var isDbUser = originalHash != null;
|
||||
bool usingSystemIdentity = systemIdentity != null && !isDbUser;
|
||||
if (!usingSystemIdentity)
|
||||
{
|
||||
// DB User password check and update
|
||||
if (!cryptographySuite.CheckUserPassword(user, ApiHeaders.Password))
|
||||
return Unauthorized();
|
||||
if (user.PasswordHash != originalHash)
|
||||
if (!oAuthLogin)
|
||||
if (!usingSystemIdentity)
|
||||
{
|
||||
Logger.LogDebug("User ID {0}'s password hash needs a refresh, updating database.", user.Id);
|
||||
var updatedUser = new Models.User
|
||||
// DB User password check and update
|
||||
if (!cryptographySuite.CheckUserPassword(user, ApiHeaders.Password))
|
||||
return Unauthorized();
|
||||
if (user.PasswordHash != originalHash)
|
||||
{
|
||||
Id = user.Id
|
||||
};
|
||||
DatabaseContext.Users.Attach(updatedUser);
|
||||
updatedUser.PasswordHash = user.PasswordHash;
|
||||
Logger.LogDebug("User ID {0}'s password hash needs a refresh, updating database.", user.Id);
|
||||
var updatedUser = new Models.User
|
||||
{
|
||||
Id = user.Id
|
||||
};
|
||||
DatabaseContext.Users.Attach(updatedUser);
|
||||
updatedUser.PasswordHash = user.PasswordHash;
|
||||
await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else if (systemIdentity.Username != user.Name)
|
||||
{
|
||||
// System identity username change update
|
||||
Logger.LogDebug("User ID {0}'s system identity needs a refresh, updating database.", user.Id);
|
||||
DatabaseContext.Users.Attach(user);
|
||||
user.Name = systemIdentity.Username;
|
||||
user.CanonicalName = Models.User.CanonicalizeName(user.Name);
|
||||
await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else if (systemIdentity.Username != user.Name)
|
||||
{
|
||||
// System identity username change update
|
||||
Logger.LogDebug("User ID {0}'s system identity needs a refresh, updating database.", user.Id);
|
||||
DatabaseContext.Users.Attach(user);
|
||||
user.Name = systemIdentity.Username;
|
||||
user.CanonicalName = Models.User.CanonicalizeName(user.Name);
|
||||
await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// Now that the bookeeping is done, tell them to fuck off if necessary
|
||||
if (!user.Enabled.Value)
|
||||
@@ -247,7 +288,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
return Forbid();
|
||||
}
|
||||
|
||||
var token = await tokenFactory.CreateToken(user, cancellationToken).ConfigureAwait(false);
|
||||
var token = await tokenFactory.CreateToken(user, oAuthLogin, cancellationToken).ConfigureAwait(false);
|
||||
if (usingSystemIdentity)
|
||||
{
|
||||
// expire the identity slightly after the auth token in case of lag
|
||||
@@ -262,6 +303,6 @@ namespace Tgstation.Server.Host.Controllers
|
||||
return Json(token);
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1506
|
||||
#pragma warning restore CA1506
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +281,9 @@ namespace Tgstation.Server.Host.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CA1508 // Avoid dead conditional code
|
||||
if (earlyOut != null)
|
||||
#pragma warning restore CA1508 // Avoid dead conditional code
|
||||
return earlyOut;
|
||||
|
||||
// Last test, ensure it's in the list of valid paths
|
||||
@@ -420,9 +422,9 @@ namespace Tgstation.Server.Host.Controllers
|
||||
|
||||
var moveJob = await InstanceQuery()
|
||||
.SelectMany(x => x.Jobs).
|
||||
#pragma warning disable CA1307 // Specify StringComparison
|
||||
#pragma warning disable CA1310 // Specify StringComparison
|
||||
Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix))
|
||||
#pragma warning restore CA1307 // Specify StringComparison
|
||||
#pragma warning restore CA1310 // Specify StringComparison
|
||||
.Select(x => new Models.Job
|
||||
{
|
||||
Id = x.Id
|
||||
@@ -614,9 +616,9 @@ namespace Tgstation.Server.Host.Controllers
|
||||
|
||||
var moveJobs = await GetBaseQuery()
|
||||
.SelectMany(x => x.Jobs)
|
||||
#pragma warning disable CA1307 // Specify StringComparison
|
||||
#pragma warning disable CA1310 // Specify StringComparison
|
||||
.Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix))
|
||||
#pragma warning restore CA1307 // Specify StringComparison
|
||||
#pragma warning restore CA1310 // Specify StringComparison
|
||||
.Include(x => x.StartedBy).ThenInclude(x => x.CreatedBy)
|
||||
.Include(x => x.Instance)
|
||||
.ToListAsync(cancellationToken)
|
||||
@@ -687,9 +689,9 @@ namespace Tgstation.Server.Host.Controllers
|
||||
|
||||
var moveJob = await QueryForUser()
|
||||
.SelectMany(x => x.Jobs)
|
||||
#pragma warning disable CA1307 // Specify StringComparison
|
||||
#pragma warning disable CA1310 // Specify StringComparison
|
||||
.Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix))
|
||||
#pragma warning restore CA1307 // Specify StringComparison
|
||||
#pragma warning restore CA1310 // Specify StringComparison
|
||||
.Include(x => x.StartedBy).ThenInclude(x => x.CreatedBy)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
@@ -426,7 +426,9 @@ namespace Tgstation.Server.Host.Controllers
|
||||
|| (model.UpdateFromOrigin == true && !userRights.HasFlag(RepositoryRights.UpdateBranch)))
|
||||
return Forbid();
|
||||
|
||||
if (currentModel.AccessToken?.Length == 0 && currentModel.AccessUser?.Length == 0)
|
||||
#pragma warning disable CA1508 // Avoid dead conditional code
|
||||
if (model.AccessToken?.Length == 0 && model.AccessUser?.Length == 0)
|
||||
#pragma warning restore CA1508 // Avoid dead conditional code
|
||||
{
|
||||
// setting an empty string clears everything
|
||||
currentModel.AccessUser = null;
|
||||
|
||||
@@ -74,7 +74,9 @@ namespace Tgstation.Server.Host.Controllers
|
||||
BadRequestObjectResult CheckValidName(UserUpdate model, bool newUser)
|
||||
{
|
||||
var userInvalidWithNullName = newUser && model.Name == null && model.SystemIdentifier == null;
|
||||
#pragma warning disable CA1508 // https://github.com/dotnet/roslyn-analyzers/issues/3685
|
||||
if (userInvalidWithNullName || (model.Name != null && String.IsNullOrWhiteSpace(model.Name)))
|
||||
#pragma warning restore CA1508
|
||||
return BadRequest(new ErrorMessage(ErrorCode.UserMissingName));
|
||||
|
||||
model.Name = model.Name?.Trim();
|
||||
@@ -118,6 +120,9 @@ namespace Tgstation.Server.Host.Controllers
|
||||
if (model == null)
|
||||
throw new ArgumentNullException(nameof(model));
|
||||
|
||||
if (model.OAuthConnections?.Any(x => x == null) == true)
|
||||
return BadRequest(new ErrorMessage(ErrorCode.ModelValidationFailure));
|
||||
|
||||
if (!(model.Password == null ^ model.SystemIdentifier == null))
|
||||
return BadRequest(new ErrorMessage(ErrorCode.UserMismatchPasswordSid));
|
||||
|
||||
@@ -132,17 +137,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
if (fail != null)
|
||||
return fail;
|
||||
|
||||
var dbUser = new Models.User
|
||||
{
|
||||
AdministrationRights = RightsHelper.Clamp(model.AdministrationRights ?? AdministrationRights.None),
|
||||
CreatedAt = DateTimeOffset.Now,
|
||||
CreatedBy = AuthenticationContext.User,
|
||||
Enabled = model.Enabled ?? false,
|
||||
InstanceManagerRights = RightsHelper.Clamp(model.InstanceManagerRights ?? InstanceManagerRights.None),
|
||||
Name = model.Name,
|
||||
SystemIdentifier = model.SystemIdentifier,
|
||||
InstanceUsers = new List<Models.InstanceUser>()
|
||||
};
|
||||
var dbUser = CreateNewUserFromModel(model);
|
||||
|
||||
if (model.SystemIdentifier != null)
|
||||
try
|
||||
@@ -157,7 +152,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
{
|
||||
return RequiresPosixSystemIdentity();
|
||||
}
|
||||
else
|
||||
else if (!(model.Password?.Length == 0 && model.OAuthConnections.Count != 0))
|
||||
{
|
||||
var result = TrySetPassword(dbUser, model.Password, true);
|
||||
if (result != null)
|
||||
@@ -170,6 +165,8 @@ namespace Tgstation.Server.Host.Controllers
|
||||
|
||||
await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
Logger.LogInformation("Created new user {0} ({1})", dbUser.Name, dbUser.Id);
|
||||
|
||||
return Created(dbUser.ToApi(true));
|
||||
}
|
||||
|
||||
@@ -182,7 +179,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// <response code="200"><see cref="Api.Models.User"/> updated successfully.</response>
|
||||
/// <response code="404">Requested <see cref="Api.Models.Internal.User.Id"/> does not exist.</response>
|
||||
[HttpPost]
|
||||
[TgsAuthorize(AdministrationRights.WriteUsers | AdministrationRights.EditOwnPassword)]
|
||||
[TgsAuthorize(AdministrationRights.WriteUsers | AdministrationRights.EditOwnPassword | AdministrationRights.EditOwnOAuthConnections)]
|
||||
[ProducesResponseType(typeof(Api.Models.User), 200)]
|
||||
[ProducesResponseType(typeof(ErrorMessage), 404)]
|
||||
#pragma warning disable CA1502 // TODO: Decomplexify
|
||||
@@ -192,19 +189,22 @@ namespace Tgstation.Server.Host.Controllers
|
||||
if (model == null)
|
||||
throw new ArgumentNullException(nameof(model));
|
||||
|
||||
if (!model.Id.HasValue)
|
||||
if (!model.Id.HasValue || model.OAuthConnections?.Any(x => x == null) == true)
|
||||
return BadRequest(new ErrorMessage(ErrorCode.ModelValidationFailure));
|
||||
|
||||
var callerAdministrationRights = (AdministrationRights)AuthenticationContext.GetRight(RightsType.Administration);
|
||||
var passwordEditOnly = !callerAdministrationRights.HasFlag(AdministrationRights.WriteUsers);
|
||||
var canEditAllUsers = callerAdministrationRights.HasFlag(AdministrationRights.WriteUsers);
|
||||
var passwordEdit = canEditAllUsers || callerAdministrationRights.HasFlag(AdministrationRights.EditOwnPassword);
|
||||
var oAuthEdit = canEditAllUsers || callerAdministrationRights.HasFlag(AdministrationRights.EditOwnOAuthConnections);
|
||||
|
||||
var originalUser = passwordEditOnly
|
||||
var originalUser = passwordEdit
|
||||
? AuthenticationContext.User
|
||||
: await DatabaseContext
|
||||
.Users
|
||||
.AsQueryable()
|
||||
.Where(x => x.Id == model.Id)
|
||||
.Include(x => x.CreatedBy)
|
||||
.Include(x => x.OAuthConnections)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
@@ -214,13 +214,15 @@ namespace Tgstation.Server.Host.Controllers
|
||||
if (originalUser.CanonicalName == Models.User.CanonicalizeName(Models.User.TgsSystemUserName))
|
||||
return Forbid();
|
||||
|
||||
// Ensure they are only trying to edit password (system identity change will trigger a bad request)
|
||||
if (passwordEditOnly
|
||||
// Ensure they are only trying to edit things they have perms for (system identity change will trigger a bad request)
|
||||
if ((!canEditAllUsers
|
||||
&& (model.Id != originalUser.Id
|
||||
|| model.InstanceManagerRights.HasValue
|
||||
|| model.AdministrationRights.HasValue
|
||||
|| model.Enabled.HasValue
|
||||
|| model.Name != null))
|
||||
|| (!passwordEdit && model.Password != null)
|
||||
|| (!oAuthEdit && model.OAuthConnections != null))
|
||||
return Forbid();
|
||||
|
||||
if (model.SystemIdentifier != null && model.SystemIdentifier != originalUser.SystemIdentifier)
|
||||
@@ -246,6 +248,22 @@ namespace Tgstation.Server.Host.Controllers
|
||||
originalUser.Enabled = model.Enabled.Value;
|
||||
}
|
||||
|
||||
if (model.OAuthConnections != null
|
||||
&& (model.OAuthConnections.Count != originalUser.OAuthConnections.Count
|
||||
|| !model.OAuthConnections.All(x => originalUser.OAuthConnections.Any(y => y.Provider == x.Provider && y.ExternalUserId == x.ExternalUserId))))
|
||||
{
|
||||
if (originalUser.CanonicalName == Models.User.CanonicalizeName(Api.Models.User.AdminName))
|
||||
return BadRequest(new ErrorMessage(ErrorCode.AdminUserCannotOAuth));
|
||||
|
||||
originalUser.OAuthConnections.Clear();
|
||||
foreach (var updatedConnection in model.OAuthConnections)
|
||||
originalUser.OAuthConnections.Add(new Models.OAuthConnection
|
||||
{
|
||||
Provider = updatedConnection.Provider,
|
||||
ExternalUserId = updatedConnection.ExternalUserId
|
||||
});
|
||||
}
|
||||
|
||||
var fail = CheckValidName(model, false);
|
||||
if (fail != null)
|
||||
return fail;
|
||||
@@ -254,6 +272,8 @@ namespace Tgstation.Server.Host.Controllers
|
||||
|
||||
await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
Logger.LogInformation("Updated user {0} ({1})", originalUser.Name, originalUser.Id);
|
||||
|
||||
// return id only if not a self update and cannot read users
|
||||
return Json(
|
||||
model.Id == originalUser.Id
|
||||
@@ -293,6 +313,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.AsQueryable()
|
||||
.Where(x => x.CanonicalName != Models.User.CanonicalizeName(Models.User.TgsSystemUserName))
|
||||
.Include(x => x.CreatedBy)
|
||||
.Include(x => x.OAuthConnections)
|
||||
.ToListAsync(cancellationToken).ConfigureAwait(false);
|
||||
return Json(users.Select(x => x.ToApi(true)));
|
||||
}
|
||||
@@ -321,6 +342,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
.AsQueryable()
|
||||
.Where(x => x.Id == id)
|
||||
.Include(x => x.CreatedBy)
|
||||
.Include(x => x.OAuthConnections)
|
||||
.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (user == default)
|
||||
return NotFound();
|
||||
@@ -330,5 +352,30 @@ namespace Tgstation.Server.Host.Controllers
|
||||
|
||||
return Json(user.ToApi(true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="User"/> from a given <paramref name="model"/>.
|
||||
/// </summary>
|
||||
/// <param name="model">The <see cref="Api.Models.User"/> to use as a template.</param>
|
||||
/// <returns>A new <see cref="User"/>.</returns>
|
||||
Models.User CreateNewUserFromModel(Api.Models.User model) => new Models.User
|
||||
{
|
||||
AdministrationRights = RightsHelper.Clamp(model.AdministrationRights ?? AdministrationRights.None),
|
||||
CreatedAt = DateTimeOffset.Now,
|
||||
CreatedBy = AuthenticationContext.User,
|
||||
Enabled = model.Enabled ?? false,
|
||||
InstanceManagerRights = RightsHelper.Clamp(model.InstanceManagerRights ?? InstanceManagerRights.None),
|
||||
Name = model.Name,
|
||||
SystemIdentifier = model.SystemIdentifier,
|
||||
InstanceUsers = new List<Models.InstanceUser>(),
|
||||
OAuthConnections = model
|
||||
.OAuthConnections
|
||||
?.Select(x => new Models.OAuthConnection
|
||||
{
|
||||
Provider = x.Provider,
|
||||
ExternalUserId = x.ExternalUserId
|
||||
})
|
||||
.ToList()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ using Tgstation.Server.Host.IO;
|
||||
using Tgstation.Server.Host.Jobs;
|
||||
using Tgstation.Server.Host.Properties;
|
||||
using Tgstation.Server.Host.Security;
|
||||
using Tgstation.Server.Host.Security.OAuth;
|
||||
using Tgstation.Server.Host.Setup;
|
||||
using Tgstation.Server.Host.System;
|
||||
|
||||
@@ -100,7 +101,6 @@ namespace Tgstation.Server.Host.Core
|
||||
// configure configuration
|
||||
services.UseStandardConfig<UpdatesConfiguration>(Configuration);
|
||||
services.UseStandardConfig<ControlPanelConfiguration>(Configuration);
|
||||
services.UseStandardConfig<SecurityConfiguration>(Configuration);
|
||||
|
||||
// enable options which give us config reloading
|
||||
services.AddOptions();
|
||||
@@ -159,24 +159,29 @@ namespace Tgstation.Server.Host.Core
|
||||
});
|
||||
|
||||
// configure bearer token validation
|
||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(jwtBearerOptions =>
|
||||
{
|
||||
// this line isn't actually run until the first request is made
|
||||
// at that point tokenFactory will be populated
|
||||
jwtBearerOptions.TokenValidationParameters = tokenFactory.ValidationParameters;
|
||||
jwtBearerOptions.Events = new JwtBearerEvents
|
||||
var authenticationBuilder = services
|
||||
.AddAuthentication(options =>
|
||||
{
|
||||
// Application is our composition root so this monstrosity of a line is okay
|
||||
// At least, that's what I tell myself to sleep at night
|
||||
OnTokenValidated = ctx => ctx
|
||||
.HttpContext
|
||||
.RequestServices
|
||||
.GetRequiredService<IClaimsInjector>()
|
||||
.InjectClaimsIntoContext(
|
||||
ctx,
|
||||
ctx.HttpContext.RequestAborted)
|
||||
};
|
||||
});
|
||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddJwtBearer(jwtBearerOptions =>
|
||||
{
|
||||
// this line isn't actually run until the first request is made
|
||||
// at that point tokenFactory will be populated
|
||||
jwtBearerOptions.TokenValidationParameters = tokenFactory.ValidationParameters;
|
||||
jwtBearerOptions.Events = new JwtBearerEvents
|
||||
{
|
||||
// Application is our composition root so this monstrosity of a line is okay
|
||||
// At least, that's what I tell myself to sleep at night
|
||||
OnTokenValidated = ctx => ctx
|
||||
.HttpContext
|
||||
.RequestServices
|
||||
.GetRequiredService<IClaimsInjector>()
|
||||
.InjectClaimsIntoContext(
|
||||
ctx,
|
||||
ctx.HttpContext.RequestAborted)
|
||||
};
|
||||
});
|
||||
|
||||
// WARNING: STATIC CODE
|
||||
// fucking prevents converting 'sub' to M$ bs
|
||||
@@ -260,6 +265,7 @@ namespace Tgstation.Server.Host.Core
|
||||
// configure security services
|
||||
services.AddScoped<IAuthenticationContextFactory, AuthenticationContextFactory>();
|
||||
services.AddScoped<IClaimsInjector, ClaimsInjector>();
|
||||
services.AddScoped<IOAuthProviders, OAuthProviders>();
|
||||
services.AddSingleton<IIdentityCache, IdentityCache>();
|
||||
services.AddSingleton<ICryptographySuite, CryptographySuite>();
|
||||
services.AddSingleton<ITokenFactory, TokenFactory>();
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace Tgstation.Server.Host.Core
|
||||
In = ParameterLocation.Header,
|
||||
Type = SecuritySchemeType.Http,
|
||||
Name = HeaderNames.Authorization,
|
||||
Scheme = ApiHeaders.JwtAuthenticationScheme
|
||||
Scheme = ApiHeaders.BearerAuthenticationScheme
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -87,10 +87,15 @@ namespace Tgstation.Server.Host.Database
|
||||
public DbSet<TestMerge> TestMerges { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="RevInfoTestMerge"/>s om the <see cref="DatabaseContext"/>
|
||||
/// The <see cref="RevInfoTestMerge"/>s in the <see cref="DatabaseContext"/>
|
||||
/// </summary>
|
||||
public DbSet<RevInfoTestMerge> RevInfoTestMerges { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="OAuthConnection"/>s in the <see cref="DatabaseContext"/>
|
||||
/// </summary>
|
||||
public DbSet<OAuthConnection> OAuthConnections { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="DeleteBehavior"/> for the <see cref="CompileJob"/>/<see cref="RevisionInformation"/> foreign key.
|
||||
/// </summary>
|
||||
@@ -132,6 +137,9 @@ namespace Tgstation.Server.Host.Database
|
||||
/// <inheritdoc />
|
||||
IDatabaseCollection<ReattachInformation> IDatabaseContext.ReattachInformations => reattachInformationsCollection;
|
||||
|
||||
/// <inheritdoc />
|
||||
IDatabaseCollection<OAuthConnection> IDatabaseContext.OAuthConnections => oAuthConnections;
|
||||
|
||||
/// <summary>
|
||||
/// Backing field for <see cref="IDatabaseContext.Users"/>.
|
||||
/// </summary>
|
||||
@@ -192,6 +200,11 @@ namespace Tgstation.Server.Host.Database
|
||||
/// </summary>
|
||||
readonly IDatabaseCollection<ReattachInformation> reattachInformationsCollection;
|
||||
|
||||
/// <summary>
|
||||
/// Backing field for <see cref="IDatabaseContext.OAuthConnections"/>.
|
||||
/// </summary>
|
||||
readonly IDatabaseCollection<OAuthConnection> oAuthConnections;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configure action for a given <typeparamref name="TDatabaseContext"/>.
|
||||
/// </summary>
|
||||
@@ -216,7 +229,7 @@ namespace Tgstation.Server.Host.Database
|
||||
/// Construct a <see cref="DatabaseContext"/>
|
||||
/// </summary>
|
||||
/// <param name="dbContextOptions">The <see cref="DbContextOptions"/> for the <see cref="DatabaseContext"/>.</param>
|
||||
public DatabaseContext(DbContextOptions dbContextOptions) : base(dbContextOptions)
|
||||
protected DatabaseContext(DbContextOptions dbContextOptions) : base(dbContextOptions)
|
||||
{
|
||||
usersCollection = new DatabaseCollection<User>(Users);
|
||||
instancesCollection = new DatabaseCollection<Instance>(Instances);
|
||||
@@ -230,6 +243,7 @@ namespace Tgstation.Server.Host.Database
|
||||
revisionInformationsCollection = new DatabaseCollection<RevisionInformation>(RevisionInformations);
|
||||
jobsCollection = new DatabaseCollection<Job>(Jobs);
|
||||
reattachInformationsCollection = new DatabaseCollection<ReattachInformation>(ReattachInformations);
|
||||
oAuthConnections = new DatabaseCollection<OAuthConnection>(OAuthConnections);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -244,6 +258,9 @@ namespace Tgstation.Server.Host.Database
|
||||
userModel.HasIndex(x => x.CanonicalName).IsUnique();
|
||||
userModel.HasIndex(x => x.SystemIdentifier).IsUnique();
|
||||
userModel.HasMany(x => x.TestMerges).WithOne(x => x.MergedBy).OnDelete(DeleteBehavior.Restrict);
|
||||
userModel.HasMany(x => x.OAuthConnections).WithOne(x => x.User).OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<OAuthConnection>().HasIndex(x => new { x.Provider, x.ExternalUserId }).IsUnique();
|
||||
|
||||
modelBuilder.Entity<InstanceUser>().HasIndex(x => new { x.UserId, x.InstanceId }).IsUnique();
|
||||
|
||||
@@ -316,6 +333,7 @@ namespace Tgstation.Server.Host.Database
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
#pragma warning disable CA1502 // Cyclomatic complexity
|
||||
public async Task SchemaDowngradeForServerVersion(
|
||||
ILogger<DatabaseContext> logger,
|
||||
Version version,
|
||||
@@ -338,7 +356,43 @@ namespace Tgstation.Server.Host.Database
|
||||
if (version < new Version(4, 1, 0))
|
||||
throw new NotSupportedException("Cannot migrate below version 4.1.0!");
|
||||
|
||||
if(version < new Version(4, 4, 0))
|
||||
if (version < new Version(4, 6, 0))
|
||||
switch (currentDatabaseType)
|
||||
{
|
||||
case DatabaseType.MariaDB:
|
||||
case DatabaseType.MySql:
|
||||
targetMigration = nameof(MYAddAdditionalDDParameters);
|
||||
break;
|
||||
case DatabaseType.PostgresSql:
|
||||
targetMigration = nameof(PGAddAdditionalDDParameters);
|
||||
break;
|
||||
case DatabaseType.SqlServer:
|
||||
case DatabaseType.Sqlite:
|
||||
targetMigration = nameof(MSAddAdditionalDDParameters);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"Invalid DatabaseType: {currentDatabaseType}", nameof(currentDatabaseType));
|
||||
}
|
||||
|
||||
if (version < new Version(4, 5, 0))
|
||||
switch (currentDatabaseType)
|
||||
{
|
||||
case DatabaseType.MariaDB:
|
||||
case DatabaseType.MySql:
|
||||
targetMigration = nameof(MYAddDeploymentColumns);
|
||||
break;
|
||||
case DatabaseType.PostgresSql:
|
||||
targetMigration = nameof(PGAddDeploymentColumns);
|
||||
break;
|
||||
case DatabaseType.SqlServer:
|
||||
case DatabaseType.Sqlite:
|
||||
targetMigration = nameof(MSAddDeploymentColumns);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"Invalid DatabaseType: {currentDatabaseType}", nameof(currentDatabaseType));
|
||||
}
|
||||
|
||||
if (version < new Version(4, 4, 0))
|
||||
switch (currentDatabaseType)
|
||||
{
|
||||
case DatabaseType.MariaDB:
|
||||
@@ -403,5 +457,6 @@ namespace Tgstation.Server.Host.Database
|
||||
logger.LogCritical(e, "Failed to migrate!");
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1502 // Cyclomatic complexity
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Threading;
|
||||
@@ -14,65 +14,70 @@ namespace Tgstation.Server.Host.Database
|
||||
public interface IDatabaseContext
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="User"/>s in the <see cref="IDatabaseContext"/>
|
||||
/// The <see cref="User"/>s in the <see cref="IDatabaseContext"/>.
|
||||
/// </summary>
|
||||
IDatabaseCollection<User> Users { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Instance"/>s in the <see cref="IDatabaseContext"/>
|
||||
/// The <see cref="Instance"/>s in the <see cref="IDatabaseContext"/>.
|
||||
/// </summary>
|
||||
IDatabaseCollection<Instance> Instances { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="InstanceUser"/>s in the <see cref="IDatabaseContext"/>
|
||||
/// The <see cref="InstanceUser"/>s in the <see cref="IDatabaseContext"/>.
|
||||
/// </summary>
|
||||
IDatabaseCollection<InstanceUser> InstanceUsers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Job"/>s in the <see cref="IDatabaseContext"/>
|
||||
/// The <see cref="Job"/>s in the <see cref="IDatabaseContext"/>.
|
||||
/// </summary>
|
||||
IDatabaseCollection<Job> Jobs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="CompileJob"/>s in the <see cref="IDatabaseContext"/>
|
||||
/// The <see cref="CompileJob"/>s in the <see cref="IDatabaseContext"/>.
|
||||
/// </summary>
|
||||
IDatabaseCollection<CompileJob> CompileJobs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="RevisionInformation"/>s in the <see cref="IDatabaseContext"/>
|
||||
/// The <see cref="RevisionInformation"/>s in the <see cref="IDatabaseContext"/>.
|
||||
/// </summary>
|
||||
IDatabaseCollection<RevisionInformation> RevisionInformations { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Models.DreamMakerSettings"/> in the <see cref="IDatabaseContext"/>
|
||||
/// The <see cref="Models.DreamMakerSettings"/> in the <see cref="IDatabaseContext"/>.
|
||||
/// </summary>
|
||||
IDatabaseCollection<DreamMakerSettings> DreamMakerSettings { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Models.DreamDaemonSettings"/> in the <see cref="IDatabaseContext"/>
|
||||
/// The <see cref="Models.DreamDaemonSettings"/> in the <see cref="IDatabaseContext"/>.
|
||||
/// </summary>
|
||||
IDatabaseCollection<DreamDaemonSettings> DreamDaemonSettings { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ChatBot"/>s in the <see cref="IDatabaseContext"/>
|
||||
/// The <see cref="ChatBot"/>s in the <see cref="IDatabaseContext"/>.
|
||||
/// </summary>
|
||||
IDatabaseCollection<ChatBot> ChatBots { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ChatChannel"/> in the <see cref="IDatabaseContext"/>
|
||||
/// The <see cref="ChatChannel"/> in the <see cref="IDatabaseContext"/>.
|
||||
/// </summary>
|
||||
IDatabaseCollection<ChatChannel> ChatChannels { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Models.RepositorySettings"/> in the <see cref="IDatabaseContext"/>
|
||||
/// The <see cref="Models.RepositorySettings"/> in the <see cref="IDatabaseContext"/>.
|
||||
/// </summary>
|
||||
IDatabaseCollection<RepositorySettings> RepositorySettings { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="DbSet{TEntity}"/> for <see cref="ReattachInformation"/>s
|
||||
/// The <see cref="DbSet{TEntity}"/> for <see cref="ReattachInformation"/>s.
|
||||
/// </summary>
|
||||
IDatabaseCollection<ReattachInformation> ReattachInformations { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="DbSet{TEntity}"/> for <see cref="OAuthConnection"/>s.
|
||||
/// </summary>
|
||||
IDatabaseCollection<OAuthConnection> OAuthConnections { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Saves changes made to the <see cref="IDatabaseContext"/>
|
||||
/// </summary>
|
||||
|
||||
Generated
+820
@@ -0,0 +1,820 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
[DbContext(typeof(SqlServerDatabaseContext))]
|
||||
[Migration("20201122231219_MSAddOAuthConnections")]
|
||||
partial class MSAddOAuthConnections
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.10")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ChannelLimit")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ConnectionString")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("Enabled")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(100)")
|
||||
.HasMaxLength(100);
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long>("ReconnectionInterval")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId", "Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ChatBots");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<long>("ChatSettingsId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<decimal?>("DiscordChannelId")
|
||||
.HasColumnType("decimal(20,0)");
|
||||
|
||||
b.Property<string>("IrcChannel")
|
||||
.HasColumnType("nvarchar(100)")
|
||||
.HasMaxLength(100);
|
||||
|
||||
b.Property<bool?>("IsAdminChannel")
|
||||
.IsRequired()
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool?>("IsUpdatesChannel")
|
||||
.IsRequired()
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool?>("IsWatchdogChannel")
|
||||
.IsRequired()
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Tag")
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
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()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("ByondVersion")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int?>("DMApiMajorVersion")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("DMApiMinorVersion")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("DMApiPatchVersion")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<Guid?>("DirectoryName")
|
||||
.IsRequired()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("DmeName")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int?>("GitHubDeploymentId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long?>("GitHubRepoId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("JobId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int?>("MinimumSecurityLevel")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Output")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<long>("RevisionInformationId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DirectoryName");
|
||||
|
||||
b.HasIndex("JobId")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("RevisionInformationId");
|
||||
|
||||
b.ToTable("CompileJobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AdditionalParameters")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("AllowWebClient")
|
||||
.IsRequired()
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool?>("AutoStart")
|
||||
.IsRequired()
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<long>("HeartbeatSeconds")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("Port")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SecurityLevel")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long>("StartupTimeout")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("TopicRequestTimeout")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("DreamDaemonSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<int>("ApiValidationPort")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("ApiValidationSecurityLevel")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("ProjectName")
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("RequireDMApiValidation")
|
||||
.IsRequired()
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("DreamMakerSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<long>("AutoUpdateInterval")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("ChatBotLimit")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("ConfigurationType")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("Online")
|
||||
.IsRequired()
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Path")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Path")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Instances");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<decimal>("ByondRights")
|
||||
.HasColumnType("decimal(20,0)");
|
||||
|
||||
b.Property<decimal>("ChatBotRights")
|
||||
.HasColumnType("decimal(20,0)");
|
||||
|
||||
b.Property<decimal>("ConfigurationRights")
|
||||
.HasColumnType("decimal(20,0)");
|
||||
|
||||
b.Property<decimal>("DreamDaemonRights")
|
||||
.HasColumnType("decimal(20,0)");
|
||||
|
||||
b.Property<decimal>("DreamMakerRights")
|
||||
.HasColumnType("decimal(20,0)");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<decimal>("InstanceUserRights")
|
||||
.HasColumnType("decimal(20,0)");
|
||||
|
||||
b.Property<decimal>("RepositoryRights")
|
||||
.HasColumnType("decimal(20,0)");
|
||||
|
||||
b.Property<long>("UserId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
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()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<decimal?>("CancelRight")
|
||||
.HasColumnType("decimal(20,0)");
|
||||
|
||||
b.Property<decimal?>("CancelRightsType")
|
||||
.HasColumnType("decimal(20,0)");
|
||||
|
||||
b.Property<bool?>("Cancelled")
|
||||
.IsRequired()
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<long?>("CancelledById")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<long?>("ErrorCode")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("ExceptionDetails")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTimeOffset?>("StartedAt")
|
||||
.IsRequired()
|
||||
.HasColumnType("datetimeoffset");
|
||||
|
||||
b.Property<long>("StartedById")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTimeOffset?>("StoppedAt")
|
||||
.HasColumnType("datetimeoffset");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CancelledById");
|
||||
|
||||
b.HasIndex("InstanceId");
|
||||
|
||||
b.HasIndex("StartedById");
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<decimal>("ExternalUserId")
|
||||
.HasColumnType("decimal(20,0)");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long?>("UserId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("Provider", "ExternalUserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("OAuthConnections");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AccessIdentifier")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<long>("CompileJobId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("LaunchSecurityLevel")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Port")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("ProcessId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("RebootState")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CompileJobId");
|
||||
|
||||
b.ToTable("ReattachInformations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("AccessToken")
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<string>("AccessUser")
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("AutoUpdatesKeepTestMerges")
|
||||
.IsRequired()
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool?>("AutoUpdatesSynchronize")
|
||||
.IsRequired()
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("CommitterEmail")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<string>("CommitterName")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("CreateGitHubDeployments")
|
||||
.IsRequired()
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool?>("PostTestMergeComment")
|
||||
.IsRequired()
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool?>("PushTestMergeCommits")
|
||||
.IsRequired()
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool?>("ShowTestMergeCommitters")
|
||||
.IsRequired()
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("RepositorySettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<long>("RevisionInformationId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("TestMergeId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
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()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("CommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(40)")
|
||||
.HasMaxLength(40);
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("OriginCommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(40)")
|
||||
.HasMaxLength(40);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId", "CommitSha")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("RevisionInformations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("Author")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("BodyAtMerge")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<DateTimeOffset>("MergedAt")
|
||||
.HasColumnType("datetimeoffset");
|
||||
|
||||
b.Property<long>("MergedById")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("Number")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long?>("PrimaryRevisionInformationId")
|
||||
.IsRequired()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("PullRequestRevision")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(40)")
|
||||
.HasMaxLength(40);
|
||||
|
||||
b.Property<string>("TitleAtMerge")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
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()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<decimal>("AdministrationRights")
|
||||
.HasColumnType("decimal(20,0)");
|
||||
|
||||
b.Property<string>("CanonicalName")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<DateTimeOffset?>("CreatedAt")
|
||||
.IsRequired()
|
||||
.HasColumnType("datetimeoffset");
|
||||
|
||||
b.Property<long?>("CreatedById")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool?>("Enabled")
|
||||
.IsRequired()
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("InstanceManagerRights")
|
||||
.HasColumnType("decimal(20,0)");
|
||||
|
||||
b.Property<DateTimeOffset?>("LastPasswordUpdate")
|
||||
.HasColumnType("datetimeoffset");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("SystemIdentifier")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CanonicalName")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("CreatedById");
|
||||
|
||||
b.HasIndex("SystemIdentifier")
|
||||
.IsUnique()
|
||||
.HasFilter("[SystemIdentifier] IS NOT NULL");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
|
||||
.WithMany("ChatSettings")
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
|
||||
.WithMany("Channels")
|
||||
.HasForeignKey("ChatSettingsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
|
||||
.WithOne()
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
|
||||
.WithMany("CompileJobs")
|
||||
.HasForeignKey("RevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.ClientNoAction)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
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)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
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)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
|
||||
.WithMany("InstanceUsers")
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", null)
|
||||
.WithMany("InstanceUsers")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
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)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("StartedById")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "User")
|
||||
.WithMany("OAuthConnections")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
|
||||
.WithMany()
|
||||
.HasForeignKey("CompileJobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
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)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
|
||||
.WithMany("ActiveTestMerges")
|
||||
.HasForeignKey("RevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
|
||||
.WithMany("RevisonInformations")
|
||||
.HasForeignKey("TestMergeId")
|
||||
.OnDelete(DeleteBehavior.ClientNoAction)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
|
||||
.WithMany("RevisionInformations")
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
|
||||
.WithMany("TestMerges")
|
||||
.HasForeignKey("MergedById")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
|
||||
.WithOne("PrimaryTestMerge")
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
|
||||
.WithMany("CreatedUsers")
|
||||
.HasForeignKey("CreatedById");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using System;
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the OAuthConnections table for MSSQL.
|
||||
/// </summary>
|
||||
public partial class MSAddOAuthConnections : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder == null)
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OAuthConnections",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
Provider = table.Column<int>(nullable: false),
|
||||
ExternalUserId = table.Column<decimal>(nullable: false),
|
||||
UserId = table.Column<long>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_OAuthConnections", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_OAuthConnections_Users_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OAuthConnections_UserId",
|
||||
table: "OAuthConnections",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OAuthConnections_Provider_ExternalUserId",
|
||||
table: "OAuthConnections",
|
||||
columns: new[] { "Provider", "ExternalUserId" },
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder == null)
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "OAuthConnections");
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+809
@@ -0,0 +1,809 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
[DbContext(typeof(MySqlDatabaseContext))]
|
||||
[Migration("20201122231327_MYAddOAuthConnections")]
|
||||
partial class MYAddOAuthConnections
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.10")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<ushort?>("ChannelLimit")
|
||||
.IsRequired()
|
||||
.HasColumnType("smallint unsigned");
|
||||
|
||||
b.Property<string>("ConnectionString")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("Enabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(100) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(100);
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<uint?>("ReconnectionInterval")
|
||||
.IsRequired()
|
||||
.HasColumnType("int unsigned");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId", "Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ChatBots");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("ChatSettingsId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<ulong?>("DiscordChannelId")
|
||||
.HasColumnType("bigint unsigned");
|
||||
|
||||
b.Property<string>("IrcChannel")
|
||||
.HasColumnType("varchar(100) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(100);
|
||||
|
||||
b.Property<bool?>("IsAdminChannel")
|
||||
.IsRequired()
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool?>("IsUpdatesChannel")
|
||||
.IsRequired()
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool?>("IsWatchdogChannel")
|
||||
.IsRequired()
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Tag")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
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()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("ByondVersion")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<int?>("DMApiMajorVersion")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("DMApiMinorVersion")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("DMApiPatchVersion")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<Guid?>("DirectoryName")
|
||||
.IsRequired()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<string>("DmeName")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<int?>("GitHubDeploymentId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long?>("GitHubRepoId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("JobId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int?>("MinimumSecurityLevel")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Output")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<long>("RevisionInformationId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DirectoryName");
|
||||
|
||||
b.HasIndex("JobId")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("RevisionInformationId");
|
||||
|
||||
b.ToTable("CompileJobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("AdditionalParameters")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("AllowWebClient")
|
||||
.IsRequired()
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool?>("AutoStart")
|
||||
.IsRequired()
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<uint?>("HeartbeatSeconds")
|
||||
.IsRequired()
|
||||
.HasColumnType("int unsigned");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<ushort?>("Port")
|
||||
.IsRequired()
|
||||
.HasColumnType("smallint unsigned");
|
||||
|
||||
b.Property<int>("SecurityLevel")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<uint?>("StartupTimeout")
|
||||
.IsRequired()
|
||||
.HasColumnType("int unsigned");
|
||||
|
||||
b.Property<uint?>("TopicRequestTimeout")
|
||||
.IsRequired()
|
||||
.HasColumnType("int unsigned");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("DreamDaemonSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<ushort?>("ApiValidationPort")
|
||||
.IsRequired()
|
||||
.HasColumnType("smallint unsigned");
|
||||
|
||||
b.Property<int>("ApiValidationSecurityLevel")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("ProjectName")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("RequireDMApiValidation")
|
||||
.IsRequired()
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("DreamMakerSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<uint?>("AutoUpdateInterval")
|
||||
.IsRequired()
|
||||
.HasColumnType("int unsigned");
|
||||
|
||||
b.Property<ushort?>("ChatBotLimit")
|
||||
.IsRequired()
|
||||
.HasColumnType("smallint unsigned");
|
||||
|
||||
b.Property<int>("ConfigurationType")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("Online")
|
||||
.IsRequired()
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Path")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Path")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Instances");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<ulong>("ByondRights")
|
||||
.HasColumnType("bigint unsigned");
|
||||
|
||||
b.Property<ulong>("ChatBotRights")
|
||||
.HasColumnType("bigint unsigned");
|
||||
|
||||
b.Property<ulong>("ConfigurationRights")
|
||||
.HasColumnType("bigint unsigned");
|
||||
|
||||
b.Property<ulong>("DreamDaemonRights")
|
||||
.HasColumnType("bigint unsigned");
|
||||
|
||||
b.Property<ulong>("DreamMakerRights")
|
||||
.HasColumnType("bigint unsigned");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<ulong>("InstanceUserRights")
|
||||
.HasColumnType("bigint unsigned");
|
||||
|
||||
b.Property<ulong>("RepositoryRights")
|
||||
.HasColumnType("bigint unsigned");
|
||||
|
||||
b.Property<long>("UserId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
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()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<ulong?>("CancelRight")
|
||||
.HasColumnType("bigint unsigned");
|
||||
|
||||
b.Property<ulong?>("CancelRightsType")
|
||||
.HasColumnType("bigint unsigned");
|
||||
|
||||
b.Property<bool?>("Cancelled")
|
||||
.IsRequired()
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<long?>("CancelledById")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<uint?>("ErrorCode")
|
||||
.HasColumnType("int unsigned");
|
||||
|
||||
b.Property<string>("ExceptionDetails")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTimeOffset?>("StartedAt")
|
||||
.IsRequired()
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<long>("StartedById")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTimeOffset?>("StoppedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CancelledById");
|
||||
|
||||
b.HasIndex("InstanceId");
|
||||
|
||||
b.HasIndex("StartedById");
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<ulong>("ExternalUserId")
|
||||
.HasColumnType("bigint unsigned");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long?>("UserId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("Provider", "ExternalUserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("OAuthConnections");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("AccessIdentifier")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<long>("CompileJobId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("LaunchSecurityLevel")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<ushort>("Port")
|
||||
.HasColumnType("smallint unsigned");
|
||||
|
||||
b.Property<int>("ProcessId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("RebootState")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CompileJobId");
|
||||
|
||||
b.ToTable("ReattachInformations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("AccessToken")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<string>("AccessUser")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("AutoUpdatesKeepTestMerges")
|
||||
.IsRequired()
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool?>("AutoUpdatesSynchronize")
|
||||
.IsRequired()
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("CommitterEmail")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<string>("CommitterName")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("CreateGitHubDeployments")
|
||||
.IsRequired()
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool?>("PostTestMergeComment")
|
||||
.IsRequired()
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool?>("PushTestMergeCommits")
|
||||
.IsRequired()
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool?>("ShowTestMergeCommitters")
|
||||
.IsRequired()
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("RepositorySettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("RevisionInformationId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("TestMergeId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
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()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("CommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(40) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(40);
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("OriginCommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(40) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(40);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId", "CommitSha")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("RevisionInformations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Author")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<string>("BodyAtMerge")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<DateTimeOffset>("MergedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<long>("MergedById")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("Number")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long?>("PrimaryRevisionInformationId")
|
||||
.IsRequired()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("PullRequestRevision")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(40) CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(40);
|
||||
|
||||
b.Property<string>("TitleAtMerge")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
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()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<ulong>("AdministrationRights")
|
||||
.HasColumnType("bigint unsigned");
|
||||
|
||||
b.Property<string>("CanonicalName")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<DateTimeOffset?>("CreatedAt")
|
||||
.IsRequired()
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<long?>("CreatedById")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool?>("Enabled")
|
||||
.IsRequired()
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<ulong>("InstanceManagerRights")
|
||||
.HasColumnType("bigint unsigned");
|
||||
|
||||
b.Property<DateTimeOffset?>("LastPasswordUpdate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("longtext CHARACTER SET utf8mb4");
|
||||
|
||||
b.Property<string>("SystemIdentifier")
|
||||
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CanonicalName")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("CreatedById");
|
||||
|
||||
b.HasIndex("SystemIdentifier")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
|
||||
.WithMany("ChatSettings")
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
|
||||
.WithMany("Channels")
|
||||
.HasForeignKey("ChatSettingsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
|
||||
.WithOne()
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
|
||||
.WithMany("CompileJobs")
|
||||
.HasForeignKey("RevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
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)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
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)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
|
||||
.WithMany("InstanceUsers")
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", null)
|
||||
.WithMany("InstanceUsers")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
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)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("StartedById")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "User")
|
||||
.WithMany("OAuthConnections")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
|
||||
.WithMany()
|
||||
.HasForeignKey("CompileJobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
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)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
|
||||
.WithMany("ActiveTestMerges")
|
||||
.HasForeignKey("RevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
|
||||
.WithMany("RevisonInformations")
|
||||
.HasForeignKey("TestMergeId")
|
||||
.OnDelete(DeleteBehavior.ClientNoAction)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
|
||||
.WithMany("RevisionInformations")
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
|
||||
.WithMany("TestMerges")
|
||||
.HasForeignKey("MergedById")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
|
||||
.WithOne("PrimaryTestMerge")
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
|
||||
.WithMany("CreatedUsers")
|
||||
.HasForeignKey("CreatedById");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using System;
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the OAuthConnections table for MYSQL.
|
||||
/// </summary>
|
||||
public partial class MYAddOAuthConnections : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder == null)
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OAuthConnections",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Provider = table.Column<int>(nullable: false),
|
||||
ExternalUserId = table.Column<ulong>(nullable: false),
|
||||
UserId = table.Column<long>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_OAuthConnections", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_OAuthConnections_Users_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OAuthConnections_UserId",
|
||||
table: "OAuthConnections",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OAuthConnections_Provider_ExternalUserId",
|
||||
table: "OAuthConnections",
|
||||
columns: new[] { "Provider", "ExternalUserId" },
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder == null)
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "OAuthConnections");
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+817
@@ -0,0 +1,817 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
[DbContext(typeof(PostgresSqlDatabaseContext))]
|
||||
[Migration("20201122231443_PGAddOAuthConnections")]
|
||||
partial class PGAddOAuthConnections
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
|
||||
.HasAnnotation("ProductVersion", "3.1.10")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<int>("ChannelLimit")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("ConnectionString")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("Enabled")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasMaxLength(100);
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long>("ReconnectionInterval")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId", "Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ChatBots");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<long>("ChatSettingsId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<decimal?>("DiscordChannelId")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
|
||||
b.Property<string>("IrcChannel")
|
||||
.HasColumnType("character varying(100)")
|
||||
.HasMaxLength(100);
|
||||
|
||||
b.Property<bool?>("IsAdminChannel")
|
||||
.IsRequired()
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool?>("IsUpdatesChannel")
|
||||
.IsRequired()
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool?>("IsWatchdogChannel")
|
||||
.IsRequired()
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("Tag")
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
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()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<string>("ByondVersion")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int?>("DMApiMajorVersion")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int?>("DMApiMinorVersion")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int?>("DMApiPatchVersion")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<Guid?>("DirectoryName")
|
||||
.IsRequired()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("DmeName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int?>("GitHubDeploymentId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long?>("GitHubRepoId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("JobId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int?>("MinimumSecurityLevel")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Output")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long>("RevisionInformationId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DirectoryName");
|
||||
|
||||
b.HasIndex("JobId")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("RevisionInformationId");
|
||||
|
||||
b.ToTable("CompileJobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<string>("AdditionalParameters")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("AllowWebClient")
|
||||
.IsRequired()
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool?>("AutoStart")
|
||||
.IsRequired()
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<long>("HeartbeatSeconds")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("Port")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("SecurityLevel")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long>("StartupTimeout")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("TopicRequestTimeout")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("DreamDaemonSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<int>("ApiValidationPort")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("ApiValidationSecurityLevel")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("ProjectName")
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("RequireDMApiValidation")
|
||||
.IsRequired()
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("DreamMakerSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<long>("AutoUpdateInterval")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("ChatBotLimit")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("ConfigurationType")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("Online")
|
||||
.IsRequired()
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("Path")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Path")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Instances");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<decimal>("ByondRights")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
|
||||
b.Property<decimal>("ChatBotRights")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
|
||||
b.Property<decimal>("ConfigurationRights")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
|
||||
b.Property<decimal>("DreamDaemonRights")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
|
||||
b.Property<decimal>("DreamMakerRights")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<decimal>("InstanceUserRights")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
|
||||
b.Property<decimal>("RepositoryRights")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
|
||||
b.Property<long>("UserId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
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()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<decimal?>("CancelRight")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
|
||||
b.Property<decimal?>("CancelRightsType")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
|
||||
b.Property<bool?>("Cancelled")
|
||||
.IsRequired()
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<long?>("CancelledById")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long?>("ErrorCode")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("ExceptionDetails")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTimeOffset?>("StartedAt")
|
||||
.IsRequired()
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("StartedById")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTimeOffset?>("StoppedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CancelledById");
|
||||
|
||||
b.HasIndex("InstanceId");
|
||||
|
||||
b.HasIndex("StartedById");
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<decimal>("ExternalUserId")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long?>("UserId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("Provider", "ExternalUserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("OAuthConnections");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<string>("AccessIdentifier")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long>("CompileJobId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("LaunchSecurityLevel")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Port")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("ProcessId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("RebootState")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CompileJobId");
|
||||
|
||||
b.ToTable("ReattachInformations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<string>("AccessToken")
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<string>("AccessUser")
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("AutoUpdatesKeepTestMerges")
|
||||
.IsRequired()
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool?>("AutoUpdatesSynchronize")
|
||||
.IsRequired()
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("CommitterEmail")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<string>("CommitterName")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("CreateGitHubDeployments")
|
||||
.IsRequired()
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool?>("PostTestMergeComment")
|
||||
.IsRequired()
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool?>("PushTestMergeCommits")
|
||||
.IsRequired()
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<bool?>("ShowTestMergeCommitters")
|
||||
.IsRequired()
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("RepositorySettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<long>("RevisionInformationId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("TestMergeId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
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()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<string>("CommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(40)")
|
||||
.HasMaxLength(40);
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("OriginCommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(40)")
|
||||
.HasMaxLength(40);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId", "CommitSha")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("RevisionInformations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<string>("Author")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("BodyAtMerge")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<DateTimeOffset>("MergedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("MergedById")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("Number")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long?>("PrimaryRevisionInformationId")
|
||||
.IsRequired()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("PullRequestRevision")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(40)")
|
||||
.HasMaxLength(40);
|
||||
|
||||
b.Property<string>("TitleAtMerge")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
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()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<decimal>("AdministrationRights")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
|
||||
b.Property<string>("CanonicalName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset?>("CreatedAt")
|
||||
.IsRequired()
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long?>("CreatedById")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool?>("Enabled")
|
||||
.IsRequired()
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<decimal>("InstanceManagerRights")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
|
||||
b.Property<DateTimeOffset?>("LastPasswordUpdate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("character varying(10000)")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("SystemIdentifier")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CanonicalName")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("CreatedById");
|
||||
|
||||
b.HasIndex("SystemIdentifier")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
|
||||
.WithMany("ChatSettings")
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
|
||||
.WithMany("Channels")
|
||||
.HasForeignKey("ChatSettingsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
|
||||
.WithOne()
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
|
||||
.WithMany("CompileJobs")
|
||||
.HasForeignKey("RevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
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)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
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)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
|
||||
.WithMany("InstanceUsers")
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", null)
|
||||
.WithMany("InstanceUsers")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
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)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("StartedById")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "User")
|
||||
.WithMany("OAuthConnections")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
|
||||
.WithMany()
|
||||
.HasForeignKey("CompileJobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
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)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
|
||||
.WithMany("ActiveTestMerges")
|
||||
.HasForeignKey("RevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
|
||||
.WithMany("RevisonInformations")
|
||||
.HasForeignKey("TestMergeId")
|
||||
.OnDelete(DeleteBehavior.ClientNoAction)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
|
||||
.WithMany("RevisionInformations")
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
|
||||
.WithMany("TestMerges")
|
||||
.HasForeignKey("MergedById")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
|
||||
.WithOne("PrimaryTestMerge")
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
|
||||
.WithMany("CreatedUsers")
|
||||
.HasForeignKey("CreatedById");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using System;
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the OAuthConnections table for PostgresSQL.
|
||||
/// </summary>
|
||||
public partial class PGAddOAuthConnections : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder == null)
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OAuthConnections",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Provider = table.Column<int>(nullable: false),
|
||||
ExternalUserId = table.Column<decimal>(nullable: false),
|
||||
UserId = table.Column<long>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_OAuthConnections", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_OAuthConnections_Users_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OAuthConnections_UserId",
|
||||
table: "OAuthConnections",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OAuthConnections_Provider_ExternalUserId",
|
||||
table: "OAuthConnections",
|
||||
columns: new[] { "Provider", "ExternalUserId" },
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder == null)
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "OAuthConnections");
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+808
@@ -0,0 +1,808 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
[DbContext(typeof(SqliteDatabaseContext))]
|
||||
[Migration("20201122231546_SLAddOAuthConnections")]
|
||||
partial class SLAddOAuthConnections
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.10");
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ushort?>("ChannelLimit")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ConnectionString")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("Enabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(100);
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint?>("ReconnectionInterval")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId", "Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ChatBots");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("ChatSettingsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong?>("DiscordChannelId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("IrcChannel")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(100);
|
||||
|
||||
b.Property<bool?>("IsAdminChannel")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool?>("IsUpdatesChannel")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool?>("IsWatchdogChannel")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Tag")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
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()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ByondVersion")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("DMApiMajorVersion")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("DMApiMinorVersion")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("DMApiPatchVersion")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid?>("DirectoryName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DmeName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("GitHubDeploymentId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long?>("GitHubRepoId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("JobId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("MinimumSecurityLevel")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Output")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("RevisionInformationId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DirectoryName");
|
||||
|
||||
b.HasIndex("JobId")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("RevisionInformationId");
|
||||
|
||||
b.ToTable("CompileJobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AdditionalParameters")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("AllowWebClient")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool?>("AutoStart")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint?>("HeartbeatSeconds")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ushort?>("Port")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SecurityLevel")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint?>("StartupTimeout")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint?>("TopicRequestTimeout")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("DreamDaemonSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ushort?>("ApiValidationPort")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ApiValidationSecurityLevel")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ProjectName")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("RequireDMApiValidation")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("DreamMakerSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint?>("AutoUpdateInterval")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ushort?>("ChatBotLimit")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ConfigurationType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("Online")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Path")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Path")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Instances");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("ByondRights")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("ChatBotRights")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("ConfigurationRights")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("DreamDaemonRights")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("DreamMakerRights")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("InstanceUserRights")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("RepositoryRights")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
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()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong?>("CancelRight")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong?>("CancelRightsType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool?>("Cancelled")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long?>("CancelledById")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint?>("ErrorCode")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ExceptionDetails")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset?>("StartedAt")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("StartedById")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset?>("StoppedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CancelledById");
|
||||
|
||||
b.HasIndex("InstanceId");
|
||||
|
||||
b.HasIndex("StartedById");
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("ExternalUserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long?>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("Provider", "ExternalUserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("OAuthConnections");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AccessIdentifier")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("CompileJobId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("LaunchSecurityLevel")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ushort>("Port")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ProcessId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("RebootState")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CompileJobId");
|
||||
|
||||
b.ToTable("ReattachInformations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AccessToken")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<string>("AccessUser")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("AutoUpdatesKeepTestMerges")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool?>("AutoUpdatesSynchronize")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CommitterEmail")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<string>("CommitterName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<bool?>("CreateGitHubDeployments")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool?>("PostTestMergeComment")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool?>("PushTestMergeCommits")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool?>("ShowTestMergeCommitters")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("RepositorySettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("RevisionInformationId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("TestMergeId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
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()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(40);
|
||||
|
||||
b.Property<long>("InstanceId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("OriginCommitSha")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(40);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstanceId", "CommitSha")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("RevisionInformations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Author")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("BodyAtMerge")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<DateTimeOffset>("MergedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("MergedById")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Number")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long?>("PrimaryRevisionInformationId")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("PullRequestRevision")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(40);
|
||||
|
||||
b.Property<string>("TitleAtMerge")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
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()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("AdministrationRights")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CanonicalName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("CreatedAt")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long?>("CreatedById")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool?>("Enabled")
|
||||
.IsRequired()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("InstanceManagerRights")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset?>("LastPasswordUpdate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(10000);
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SystemIdentifier")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CanonicalName")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("CreatedById");
|
||||
|
||||
b.HasIndex("SystemIdentifier")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
|
||||
.WithMany("ChatSettings")
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
|
||||
.WithMany("Channels")
|
||||
.HasForeignKey("ChatSettingsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
|
||||
.WithOne()
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
|
||||
.WithMany("CompileJobs")
|
||||
.HasForeignKey("RevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.ClientNoAction)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
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)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
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)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
|
||||
.WithMany("InstanceUsers")
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", null)
|
||||
.WithMany("InstanceUsers")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
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)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("StartedById")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "User")
|
||||
.WithMany("OAuthConnections")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
|
||||
.WithMany()
|
||||
.HasForeignKey("CompileJobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
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)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
|
||||
.WithMany("ActiveTestMerges")
|
||||
.HasForeignKey("RevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
|
||||
.WithMany("RevisonInformations")
|
||||
.HasForeignKey("TestMergeId")
|
||||
.OnDelete(DeleteBehavior.ClientNoAction)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
|
||||
.WithMany("RevisionInformations")
|
||||
.HasForeignKey("InstanceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
|
||||
.WithMany("TestMerges")
|
||||
.HasForeignKey("MergedById")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
|
||||
.WithOne("PrimaryTestMerge")
|
||||
.HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
|
||||
.WithMany("CreatedUsers")
|
||||
.HasForeignKey("CreatedById");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using System;
|
||||
|
||||
namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the OAuthConnections table for SQLite.
|
||||
/// </summary>
|
||||
public partial class SLAddOAuthConnections : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder == null)
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OAuthConnections",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Provider = table.Column<int>(nullable: false),
|
||||
ExternalUserId = table.Column<ulong>(nullable: false),
|
||||
UserId = table.Column<long>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_OAuthConnections", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_OAuthConnections_Users_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OAuthConnections_UserId",
|
||||
table: "OAuthConnections",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OAuthConnections_Provider_ExternalUserId",
|
||||
table: "OAuthConnections",
|
||||
columns: new[] { "Provider", "ExternalUserId" },
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder == null)
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "OAuthConnections");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.7")
|
||||
.HasAnnotation("ProductVersion", "3.1.10")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
@@ -373,6 +373,31 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<ulong>("ExternalUserId")
|
||||
.HasColumnType("bigint unsigned");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long?>("UserId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("Provider", "ExternalUserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("OAuthConnections");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
@@ -705,6 +730,14 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "User")
|
||||
.WithMany("OAuthConnections")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
|
||||
|
||||
+35
-1
@@ -15,7 +15,7 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
|
||||
.HasAnnotation("ProductVersion", "3.1.7")
|
||||
.HasAnnotation("ProductVersion", "3.1.10")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
@@ -374,6 +374,32 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<decimal>("ExternalUserId")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long?>("UserId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("Provider", "ExternalUserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("OAuthConnections");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
@@ -712,6 +738,14 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "User")
|
||||
.WithMany("OAuthConnections")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
|
||||
|
||||
+35
-1
@@ -14,7 +14,7 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.7")
|
||||
.HasAnnotation("ProductVersion", "3.1.10")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
@@ -376,6 +376,32 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<decimal>("ExternalUserId")
|
||||
.HasColumnType("decimal(20,0)");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<long?>("UserId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("Provider", "ExternalUserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("OAuthConnections");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
@@ -715,6 +741,14 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "User")
|
||||
.WithMany("OAuthConnections")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
|
||||
|
||||
+34
-1
@@ -13,7 +13,7 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.7");
|
||||
.HasAnnotation("ProductVersion", "3.1.10");
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
|
||||
{
|
||||
@@ -372,6 +372,31 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("ExternalUserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long?>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("Provider", "ExternalUserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("OAuthConnections");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
@@ -704,6 +729,14 @@ namespace Tgstation.Server.Host.Database.Migrations
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.OAuthConnection", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.User", "User")
|
||||
.WithMany("OAuthConnections")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
|
||||
{
|
||||
b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace Tgstation.Server.Host.Models
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public sealed class OAuthConnection : Api.Models.OAuthConnection
|
||||
{
|
||||
/// <summary>
|
||||
/// The row Id.
|
||||
/// </summary>
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The owning <see cref="Models.User"/>.
|
||||
/// </summary>
|
||||
public User User { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Convert the <see cref="OAuthConnection"/> to it's API form.
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="Api.Models.OAuthConnection"/>.</returns>
|
||||
public Api.Models.OAuthConnection ToApi() => new Api.Models.OAuthConnection
|
||||
{
|
||||
Provider = Provider,
|
||||
ExternalUserId = ExternalUserId
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
@@ -48,6 +48,11 @@ namespace Tgstation.Server.Host.Models
|
||||
/// </summary>
|
||||
public ICollection<TestMerge> TestMerges { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="TestMerge"/>s made by the <see cref="User"/>
|
||||
/// </summary>
|
||||
public ICollection<OAuthConnection> OAuthConnections { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Change a <see cref="Api.Models.Internal.User.Name"/> into a <see cref="CanonicalName"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api.Models;
|
||||
@@ -19,8 +19,9 @@ namespace Tgstation.Server.Host.Security
|
||||
/// Create a <see cref="Token"/> for a given <paramref name="user"/>
|
||||
/// </summary>
|
||||
/// <param name="user">The <see cref="Models.User"/> to create the token for. Must have the <see cref="Api.Models.Internal.User.Id"/> field available</param>
|
||||
/// <param name="oAuth">Whether or not this is an OAuth login.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in a new <see cref="Token"/></returns>
|
||||
Task<Token> CreateToken(Models.User user, CancellationToken cancellationToken);
|
||||
Task<Token> CreateToken(Models.User user, bool oAuth, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Octokit;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.Core;
|
||||
|
||||
namespace Tgstation.Server.Host.Security.OAuth
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IOAuthValidator"/> for GitHub.
|
||||
/// </summary>
|
||||
sealed class GitHubOAuthValidator : IOAuthValidator
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public OAuthProvider Provider => OAuthProvider.GitHub;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IGitHubClientFactory"/> for the <see cref="GitHubOAuthValidator"/>.
|
||||
/// </summary>
|
||||
readonly IGitHubClientFactory gitHubClientFactory;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ILogger"/> for the <see cref="GitHubOAuthValidator"/>.
|
||||
/// </summary>
|
||||
readonly ILogger<GitHubOAuthValidator> logger;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="GitHubOAuthConfiguration"/> for the <see cref="GitHubOAuthValidator"/>.
|
||||
/// </summary>
|
||||
readonly GitHubOAuthConfiguration gitHubOAuthConfiguration;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GitHubOAuthValidator"/> <see langword="class"/>.
|
||||
/// </summary>
|
||||
/// <param name="gitHubClientFactory">The value of <see cref="gitHubClientFactory"/>.</param>
|
||||
/// <param name="logger">The value of <see cref="logger"/>.</param>
|
||||
/// <param name="gitHubOAuthConfiguration">The value of <see cref="gitHubOAuthConfiguration"/>.</param>
|
||||
public GitHubOAuthValidator(
|
||||
IGitHubClientFactory gitHubClientFactory,
|
||||
ILogger<GitHubOAuthValidator> logger,
|
||||
GitHubOAuthConfiguration gitHubOAuthConfiguration)
|
||||
{
|
||||
this.gitHubClientFactory = gitHubClientFactory ?? throw new ArgumentNullException(nameof(gitHubClientFactory));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
this.gitHubOAuthConfiguration = gitHubOAuthConfiguration ?? throw new ArgumentNullException(nameof(gitHubOAuthConfiguration));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ulong?> ValidateResponseCode(string code, CancellationToken cancellationToken)
|
||||
{
|
||||
if (code == null)
|
||||
throw new ArgumentNullException(nameof(code));
|
||||
|
||||
var client = gitHubClientFactory.CreateClient();
|
||||
try
|
||||
{
|
||||
logger.LogTrace("Validating response code...");
|
||||
var response = await client
|
||||
.Oauth
|
||||
.CreateAccessToken(
|
||||
new OauthTokenRequest(
|
||||
gitHubOAuthConfiguration.ClientId,
|
||||
gitHubOAuthConfiguration.ClientSecret,
|
||||
code))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var token = response.AccessToken;
|
||||
if (token == null)
|
||||
return null;
|
||||
|
||||
var authenticatedClient = gitHubClientFactory.CreateClient(token);
|
||||
|
||||
logger.LogTrace("Getting user details...");
|
||||
var userDetails = await authenticatedClient
|
||||
.User
|
||||
.Current()
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return (ulong)userDetails.Id;
|
||||
}
|
||||
catch (RateLimitExceededException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch(ApiException ex)
|
||||
{
|
||||
logger.LogWarning(ex, "API error while completing OAuth handshake!");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using Tgstation.Server.Api.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Security.OAuth
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains <see cref="IOAuthValidator"/>s
|
||||
/// </summary>
|
||||
public interface IOAuthProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IOAuthValidator"/> for a given <paramref name="oAuthProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="oAuthProvider">The <see cref="OAuthProvider"/> to get the validator for.</param>
|
||||
/// <returns>The <see cref="IOAuthValidator"/> for <paramref name="oAuthProvider"/>.</returns>
|
||||
IOAuthValidator GetValidator(OAuthProvider oAuthProvider);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Security.OAuth
|
||||
{
|
||||
/// <summary>
|
||||
/// Validates OAuth responses for a given <see cref="Provider"/>.
|
||||
/// </summary>
|
||||
public interface IOAuthValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="OAuthProvider"/> this validator is for.
|
||||
/// </summary>
|
||||
OAuthProvider Provider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Validate a given OAuth response <paramref name="code"/>.
|
||||
/// </summary>
|
||||
/// <param name="code">The OAuth response string from web application.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in <see langword="null"/> if authentication failed, <see cref="global::System.UInt64.MaxValue"/> if a rate limit occurred, and the validated <see cref="OAuthConnection.ExternalUserId"/> otherwise.</returns>
|
||||
Task<ulong?> ValidateResponseCode(string code, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.Core;
|
||||
|
||||
namespace Tgstation.Server.Host.Security.OAuth
|
||||
{
|
||||
/// <inheritdoc />
|
||||
sealed class OAuthProviders : IOAuthProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IReadOnlyCollection{T}"/> of <see cref="IOAuthValidator"/>s.
|
||||
/// </summary>
|
||||
readonly IReadOnlyCollection<IOAuthValidator> validators;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OAuthProviders"/> <see langword="class"/>.
|
||||
/// </summary>
|
||||
/// <param name="gitHubClientFactory">The <see cref="IGitHubClientFactory"/> to use.</param>
|
||||
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/> to use.</param>
|
||||
/// <param name="securityConfigurationOptions">The <see cref="IOptions{TOptions}"/> containing the <see cref="SecurityConfiguration"/> to use.</param>
|
||||
public OAuthProviders(
|
||||
IGitHubClientFactory gitHubClientFactory,
|
||||
ILoggerFactory loggerFactory,
|
||||
IOptions<SecurityConfiguration> securityConfigurationOptions)
|
||||
{
|
||||
if (loggerFactory == null)
|
||||
throw new ArgumentNullException(nameof(loggerFactory));
|
||||
|
||||
var securityConfiguration = securityConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(securityConfigurationOptions));
|
||||
|
||||
var validatorsBuilder = new List<IOAuthValidator>();
|
||||
|
||||
if (securityConfiguration.GitHubOAuth != null)
|
||||
validatorsBuilder.Add(
|
||||
new GitHubOAuthValidator(
|
||||
gitHubClientFactory,
|
||||
loggerFactory.CreateLogger<GitHubOAuthValidator>(),
|
||||
securityConfiguration.GitHubOAuth));
|
||||
|
||||
validators = validatorsBuilder;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IOAuthValidator GetValidator(OAuthProvider oAuthProvider) => validators.First(x => x.Provider == oAuthProvider);
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ namespace Tgstation.Server.Host.Security
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Token> CreateToken(Models.User user, CancellationToken cancellationToken)
|
||||
public async Task<Token> CreateToken(Models.User user, bool oAuth, CancellationToken cancellationToken)
|
||||
{
|
||||
if (user == null)
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
@@ -93,7 +93,9 @@ namespace Tgstation.Server.Host.Security
|
||||
if (nowUnix == lpuUnix)
|
||||
await asyncDelayer.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var expiry = now.AddMinutes(securityConfiguration.TokenExpiryMinutes);
|
||||
var expiry = now.AddMinutes(oAuth
|
||||
? securityConfiguration.OAuthTokenExpiryMinutes
|
||||
: securityConfiguration.TokenExpiryMinutes);
|
||||
var claims = new Claim[]
|
||||
{
|
||||
new Claim(JwtRegisteredClaimNames.Sub, user.Id.Value.ToString(CultureInfo.InvariantCulture)),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.Configuration;
|
||||
using Tgstation.Server.Host.System;
|
||||
|
||||
namespace Tgstation.Server.Host.Setup
|
||||
@@ -18,6 +18,11 @@ namespace Tgstation.Server.Host.Setup
|
||||
/// </summary>
|
||||
DatabaseConfiguration DatabaseConfiguration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Configuration.SecurityConfiguration"/>.
|
||||
/// </summary>
|
||||
SecurityConfiguration SecurityConfiguration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Configuration.FileLoggingConfiguration"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -18,6 +18,9 @@ namespace Tgstation.Server.Host.Setup
|
||||
/// <inheritdoc />
|
||||
public DatabaseConfiguration DatabaseConfiguration => databaseConfigurationOptions.Value;
|
||||
|
||||
/// <inheritdoc />
|
||||
public SecurityConfiguration SecurityConfiguration => securityConfigurationOptions.Value;
|
||||
|
||||
/// <inheritdoc />
|
||||
public FileLoggingConfiguration FileLoggingConfiguration => fileLoggingConfigurationOptions.Value;
|
||||
|
||||
@@ -34,6 +37,11 @@ namespace Tgstation.Server.Host.Setup
|
||||
/// </summary>
|
||||
readonly IOptions<DatabaseConfiguration> databaseConfigurationOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Backing <see cref="IOptions{TOptions}"/> for <see cref="SecurityConfiguration"/>.
|
||||
/// </summary>
|
||||
readonly IOptions<SecurityConfiguration> securityConfigurationOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Backing <see cref="IOptions{TOptions}"/> for <see cref="FileLoggingConfiguration"/>.
|
||||
/// </summary>
|
||||
@@ -46,12 +54,14 @@ namespace Tgstation.Server.Host.Setup
|
||||
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/> used to create <see cref="Logger"/>.</param>
|
||||
/// <param name="generalConfigurationOptions">The <see cref="IOptions{TOptions}"/> containing the value of <see cref="GeneralConfiguration"/>.</param>
|
||||
/// <param name="databaseConfigurationOptions">The <see cref="IOptions{TOptions}"/> containing the value of <see cref="DatabaseConfiguration"/>.</param>
|
||||
/// <param name="securityConfigurationOptions">The <see cref="IOptions{TOptions}"/> containing the value of <see cref="SecurityConfiguration"/>.</param>
|
||||
/// <param name="fileLoggingConfigurationOptions">The <see cref="IOptions{TOptions}"/> containing the value of <see cref="FileLoggingConfiguration"/>.</param>
|
||||
public PostSetupServices(
|
||||
IPlatformIdentifier platformIdentifier,
|
||||
ILoggerFactory loggerFactory,
|
||||
IOptions<GeneralConfiguration> generalConfigurationOptions,
|
||||
IOptions<DatabaseConfiguration> databaseConfigurationOptions,
|
||||
IOptions<SecurityConfiguration> securityConfigurationOptions,
|
||||
IOptions<FileLoggingConfiguration> fileLoggingConfigurationOptions)
|
||||
{
|
||||
PlatformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier));
|
||||
@@ -61,6 +71,7 @@ namespace Tgstation.Server.Host.Setup
|
||||
Logger = loggerFactory.CreateLogger<TLoggerType>();
|
||||
this.generalConfigurationOptions = generalConfigurationOptions ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
|
||||
this.databaseConfigurationOptions = databaseConfigurationOptions ?? throw new ArgumentNullException(nameof(databaseConfigurationOptions));
|
||||
this.securityConfigurationOptions = securityConfigurationOptions ?? throw new ArgumentNullException(nameof(securityConfigurationOptions));
|
||||
this.fileLoggingConfigurationOptions = fileLoggingConfigurationOptions ?? throw new ArgumentNullException(nameof(fileLoggingConfigurationOptions));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ namespace Tgstation.Server.Host.Setup
|
||||
|
||||
services.UseStandardConfig<GeneralConfiguration>(Configuration);
|
||||
services.UseStandardConfig<DatabaseConfiguration>(Configuration);
|
||||
services.UseStandardConfig<SecurityConfiguration>(Configuration);
|
||||
services.UseStandardConfig<FileLoggingConfiguration>(Configuration);
|
||||
|
||||
ConfigureHostedService(services);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Import Project="../../build/Version.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
@@ -66,20 +66,20 @@
|
||||
<PackageReference Include="Cyberboss.SmartIrc4net.Standard" Version="0.4.6" />
|
||||
<PackageReference Include="Discord.Net.WebSocket" Version="2.2.0" />
|
||||
<PackageReference Include="LibGit2Sharp" Version="0.27.0-preview-0034" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.0">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.10" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.7">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.10">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.7">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.10">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@@ -87,7 +87,7 @@
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.4" />
|
||||
<PackageReference Include="Octokit" Version="0.48.0" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.1.2" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.2.4" />
|
||||
<!-- If this is updated, be sure to update the reference in the README.md -->
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Async" Version="1.4.0" />
|
||||
@@ -97,13 +97,13 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.5.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="5.5.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="5.6.3" />
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.8.2" />
|
||||
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="4.7.0" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.7.1" />
|
||||
<PackageReference Include="System.Management" Version="4.7.0" />
|
||||
<PackageReference Include="Wangkanai.Detection.Browser" Version="2.0.0" />
|
||||
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="5.0.0" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.8.0" />
|
||||
<PackageReference Include="System.Management" Version="5.0.0" />
|
||||
<PackageReference Include="Wangkanai.Detection.Browser" Version="2.0.1" />
|
||||
<PackageReference Include="Z.EntityFramework.Plus.EFCore" Version="3.0.57" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
"TokenExpiryMinutes": 15,
|
||||
"TokenClockSkewMinutes": 1,
|
||||
"TokenSigningKeyByteAmount": 256,
|
||||
"CustomTokenSigningKeyBase64": null
|
||||
"CustomTokenSigningKeyBase64": null,
|
||||
"GitHubOAuth": null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Headers;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Mime;
|
||||
using Tgstation.Server.Api.Models;
|
||||
|
||||
namespace Tgstation.Server.Api.Tests
|
||||
{
|
||||
@@ -21,6 +22,7 @@ namespace Tgstation.Server.Api.Tests
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new ApiHeaders(null, null));
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new ApiHeaders(productHeaderValue, null));
|
||||
var headers = new ApiHeaders(productHeaderValue, String.Empty);
|
||||
headers = new ApiHeaders(productHeaderValue, String.Empty, OAuthProvider.GitHub);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
||||
Reference in New Issue
Block a user