diff --git a/build/ControlPanelVersion.props b/build/ControlPanelVersion.props index ee18d7302c..801c4295d0 100644 --- a/build/ControlPanelVersion.props +++ b/build/ControlPanelVersion.props @@ -1,6 +1,6 @@ - 2.4.0 + 3.0.0 diff --git a/build/Dockerfile b/build/Dockerfile index fb870efdfb..7b6227ab71 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -19,6 +19,9 @@ RUN . $NVM_DIR/nvm.sh \ && rm -rf /var/lib/apt/lists/* \ && npm install -g npm +#You may wonder why this needs to be in a seperate step. I don't know... It just works(tm) +RUN npm install -g yarn + # Build web control panel WORKDIR /repo/build @@ -32,7 +35,7 @@ COPY src/Tgstation.Server.Host/Tgstation.Server.Host.csproj ./ # I cant figure out how to run npm as non root so eh RUN npm set unsafe-perm true RUN dotnet msbuild -target:NpmBuild -RUN npm set unsafe-perm false +RUN npm set unsafe-perm fals WORKDIR /repo diff --git a/build/OpenApiValidationSettings.json b/build/OpenApiValidationSettings.json index 4ddb553e57..58f088c0d1 100644 --- a/build/OpenApiValidationSettings.json +++ b/build/OpenApiValidationSettings.json @@ -48,8 +48,7 @@ "inconsistent_property_type": "error", "property_case_convention": "off", "property_case_collision": "error", - "enum_case_convention": "off", - "undefined_required_properties": "error" + "enum_case_convention": "off" }, "walker": { "no_empty_descriptions": "error", diff --git a/build/Version.props b/build/Version.props index 014fb98062..2fa65b2f96 100644 --- a/build/Version.props +++ b/build/Version.props @@ -3,7 +3,7 @@ - 4.15.6 + 4.15.7 4.1.0 9.3.0 9.3.1 diff --git a/docs/API.dox b/docs/API.dox index bb0167c9d1..49779e1f2d 100644 --- a/docs/API.dox +++ b/docs/API.dox @@ -146,7 +146,7 @@ You will be granted a bearer token as in basic auth. This will have an extended - GitHub: https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps - Discord: https://discord.com/developers/docs/topics/oauth2 -- TGForums: https://tgstation13.org/phpBB/viewtopic.php?f=45&t=9922 +- TGForums: https://tgstation13.org/phpBB/viewtopic.php?f=45&t=30155 - Keycloak: https://plugins.miniorange.com/keycloak-single-sign-on-wordpress-sso-oauth-openid-connect @section api_perms Permissions diff --git a/src/Tgstation.Server.Host/Security/OAuth/BaseOAuthValidator.cs b/src/Tgstation.Server.Host/Security/OAuth/BaseOAuthValidator.cs deleted file mode 100644 index 9040b6dd5a..0000000000 --- a/src/Tgstation.Server.Host/Security/OAuth/BaseOAuthValidator.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Net.Mime; -using System.Threading; -using System.Threading.Tasks; - -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; - -using Tgstation.Server.Api.Models; -using Tgstation.Server.Host.Configuration; -using Tgstation.Server.Host.System; - -namespace Tgstation.Server.Host.Security.OAuth -{ - /// - /// Base for s. - /// - abstract class BaseOAuthValidator : IOAuthValidator - { - /// - public abstract OAuthProvider Provider { get; } - - /// - /// The for the . - /// - protected ILogger Logger { get; } - - /// - /// The for the . - /// - protected OAuthConfiguration OAuthConfiguration { get; } - - /// - /// The for the . - /// - readonly IHttpClientFactory httpClientFactory; - - /// - /// The for the . - /// - readonly IAssemblyInformationProvider assemblyInformationProvider; - - /// - /// Gets that should be used. - /// - /// A new . - protected static JsonSerializerSettings SerializerSettings() => new JsonSerializerSettings - { - ContractResolver = new DefaultContractResolver - { - NamingStrategy = new SnakeCaseNamingStrategy(), - }, - }; - - /// - /// Initializes a new instance of the class. - /// - /// The value of . - /// The value of . - /// The value of . - /// The value of . - public BaseOAuthValidator( - IHttpClientFactory httpClientFactory, - IAssemblyInformationProvider assemblyInformationProvider, - ILogger logger, - OAuthConfiguration oAuthConfiguration) - { - this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory)); - this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider)); - Logger = logger ?? throw new ArgumentNullException(nameof(logger)); - OAuthConfiguration = oAuthConfiguration ?? throw new ArgumentNullException(nameof(oAuthConfiguration)); - } - - /// - public abstract Task GetProviderInfo(CancellationToken cancellationToken); - - /// - public abstract Task ValidateResponseCode(string code, CancellationToken cancellationToken); - - /// - /// Create a new configured . - /// - /// A new configured . - protected HttpClient CreateHttpClient() - { - var httpClient = httpClientFactory.CreateClient(); - try - { - httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json)); - httpClient.DefaultRequestHeaders.UserAgent.Add(assemblyInformationProvider.ProductInfoHeaderValue); - return httpClient; - } - catch - { - httpClient.Dispose(); - throw; - } - } - } -} diff --git a/src/Tgstation.Server.Host/Security/OAuth/DiscordOAuthValidator.cs b/src/Tgstation.Server.Host/Security/OAuth/DiscordOAuthValidator.cs index d28bc9d04f..e74a6c9d4b 100644 --- a/src/Tgstation.Server.Host/Security/OAuth/DiscordOAuthValidator.cs +++ b/src/Tgstation.Server.Host/Security/OAuth/DiscordOAuthValidator.cs @@ -14,6 +14,15 @@ namespace Tgstation.Server.Host.Security.OAuth /// sealed class DiscordOAuthValidator : GenericOAuthValidator { + /// + public override OAuthProvider Provider => OAuthProvider.Discord; + + /// + protected override Uri TokenUrl => new Uri("https://discord.com/api/oauth2/token"); + + /// + protected override Uri UserInformationUrl => new Uri("https://discord.com/api/users/@me"); + /// /// Initializes a new instance of the class. /// @@ -30,15 +39,6 @@ namespace Tgstation.Server.Host.Security.OAuth { } - /// - public override OAuthProvider Provider => OAuthProvider.Discord; - - /// - protected override Uri TokenUrl => new Uri("https://discord.com/api/oauth2/token"); - - /// - protected override Uri UserInformationUrl => new Uri("https://discord.com/api/users/@me"); - /// protected override OAuthTokenRequest CreateTokenRequest(string code) => new OAuthTokenRequest(OAuthConfiguration, code, "identify"); diff --git a/src/Tgstation.Server.Host/Security/OAuth/GenericOAuthValidator.cs b/src/Tgstation.Server.Host/Security/OAuth/GenericOAuthValidator.cs index aa834e38a7..9582d915f3 100644 --- a/src/Tgstation.Server.Host/Security/OAuth/GenericOAuthValidator.cs +++ b/src/Tgstation.Server.Host/Security/OAuth/GenericOAuthValidator.cs @@ -2,12 +2,14 @@ using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Headers; +using System.Net.Mime; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; using Tgstation.Server.Api; using Tgstation.Server.Api.Models; @@ -19,8 +21,21 @@ namespace Tgstation.Server.Host.Security.OAuth /// /// for generic OAuth2 endpoints. /// - abstract class GenericOAuthValidator : BaseOAuthValidator + abstract class GenericOAuthValidator : IOAuthValidator { + /// + public abstract OAuthProvider Provider { get; } + + /// + /// The for the . + /// + protected ILogger Logger { get; } + + /// + /// The for the . + /// + protected OAuthConfiguration OAuthConfiguration { get; } + /// /// to to to get the access token. /// @@ -31,28 +46,49 @@ namespace Tgstation.Server.Host.Security.OAuth /// protected abstract Uri UserInformationUrl { get; } + /// + /// The for the . + /// + readonly IHttpClientFactory httpClientFactory; + + /// + /// The for the . + /// + readonly IAssemblyInformationProvider assemblyInformationProvider; + + /// + /// Gets that should be used. + /// + /// A new . + protected static JsonSerializerSettings SerializerSettings() => new JsonSerializerSettings + { + ContractResolver = new DefaultContractResolver + { + NamingStrategy = new SnakeCaseNamingStrategy(), + }, + }; + /// /// Initializes a new instance of the class. /// - /// The for the . - /// The for the . - /// The for the . - /// The for the . + /// The value of . + /// The value of . + /// The value of . + /// The value of . public GenericOAuthValidator( IHttpClientFactory httpClientFactory, IAssemblyInformationProvider assemblyInformationProvider, ILogger logger, OAuthConfiguration oAuthConfiguration) - : base( - httpClientFactory, - assemblyInformationProvider, - logger, - oAuthConfiguration) { + this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory)); + this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider)); + Logger = logger ?? throw new ArgumentNullException(nameof(logger)); + OAuthConfiguration = oAuthConfiguration ?? throw new ArgumentNullException(nameof(oAuthConfiguration)); } /// - public override async Task ValidateResponseCode(string code, CancellationToken cancellationToken) + public async Task ValidateResponseCode(string code, CancellationToken cancellationToken) { using var httpClient = CreateHttpClient(); string tokenResponsePayload = null; @@ -110,7 +146,7 @@ namespace Tgstation.Server.Host.Security.OAuth } /// - public override Task GetProviderInfo(CancellationToken cancellationToken) => Task.FromResult( + public Task GetProviderInfo(CancellationToken cancellationToken) => Task.FromResult( new OAuthProviderInfo { ClientId = OAuthConfiguration.ClientId, @@ -138,5 +174,25 @@ namespace Tgstation.Server.Host.Security.OAuth /// The OAuth code from the browser. /// The to send to . protected abstract OAuthTokenRequest CreateTokenRequest(string code); + + /// + /// Create a new configured . + /// + /// A new configured . + HttpClient CreateHttpClient() + { + var httpClient = httpClientFactory.CreateClient(); + try + { + httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json)); + httpClient.DefaultRequestHeaders.UserAgent.Add(assemblyInformationProvider.ProductInfoHeaderValue); + return httpClient; + } + catch + { + httpClient.Dispose(); + throw; + } + } } } diff --git a/src/Tgstation.Server.Host/Security/OAuth/TGBaseResponse.cs b/src/Tgstation.Server.Host/Security/OAuth/TGBaseResponse.cs deleted file mode 100644 index 0dd50cf814..0000000000 --- a/src/Tgstation.Server.Host/Security/OAuth/TGBaseResponse.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Tgstation.Server.Host.Security.OAuth -{ - /// - /// Base for tgstation forum responses. - /// - abstract class TGBaseResponse - { - /// - /// Expected value of . - /// - public const string OkStatus = "OK"; - - /// - /// The response status. - /// - public string Status { get; set; } - - /// - /// The response error, if any. - /// - public string Error { get; set; } - } -} diff --git a/src/Tgstation.Server.Host/Security/OAuth/TGCreateSessionResponse.cs b/src/Tgstation.Server.Host/Security/OAuth/TGCreateSessionResponse.cs deleted file mode 100644 index a012ada04f..0000000000 --- a/src/Tgstation.Server.Host/Security/OAuth/TGCreateSessionResponse.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Tgstation.Server.Host.Security.OAuth -{ - /// - /// Response when creating a tgstation forums session. - /// - sealed class TGCreateSessionResponse : TGBaseResponse - { - /// - /// The session's private token. Similar to OAuth authorization response code. - /// - public string SessionPrivateToken { get; set; } - - /// - /// The session's public token. Barely similar to OAuth client ID. - /// - public string SessionPublicToken { get; set; } - } -} diff --git a/src/Tgstation.Server.Host/Security/OAuth/TGForumsOAuthValidator.cs b/src/Tgstation.Server.Host/Security/OAuth/TGForumsOAuthValidator.cs index 86902f89db..1783a03993 100644 --- a/src/Tgstation.Server.Host/Security/OAuth/TGForumsOAuthValidator.cs +++ b/src/Tgstation.Server.Host/Security/OAuth/TGForumsOAuthValidator.cs @@ -1,14 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Net.Http; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Web; using Microsoft.Extensions.Logging; -using Newtonsoft.Json; using Tgstation.Server.Api.Models; using Tgstation.Server.Host.Configuration; @@ -33,10 +26,10 @@ namespace Tgstation.Server.Host.Security.OAuth /// /// Initializes a new instance of the class. /// - /// The for the . - /// The for the . - /// The for the . - /// The for the . + /// The for the . + /// The for the . + /// The for the . + /// The for the . public TGForumsOAuthValidator( IHttpClientFactory httpClientFactory, IAssemblyInformationProvider assemblyInformationProvider, @@ -51,10 +44,7 @@ namespace Tgstation.Server.Host.Security.OAuth } /// - protected override string DecodeTokenPayload(dynamic responseJson) - { - throw new NotImplementedException(); - } + protected override string DecodeTokenPayload(dynamic responseJson) => responseJson.access_token; /// protected override string DecodeUserInformationPayload(dynamic responseJson) => responseJson.phpbb_username; diff --git a/src/Tgstation.Server.Host/Security/OAuth/TGGetSessionInfoResponse.cs b/src/Tgstation.Server.Host/Security/OAuth/TGGetSessionInfoResponse.cs deleted file mode 100644 index 62a630a38b..0000000000 --- a/src/Tgstation.Server.Host/Security/OAuth/TGGetSessionInfoResponse.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Tgstation.Server.Host.Security.OAuth -{ - /// - /// Response when getting tgstation forum user's info. - /// - sealed class TGGetSessionInfoResponse : TGBaseResponse - { - /// - /// The user's forum account name. - /// - public string PhpbbUsername { get; set; } - } -} diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index 458c311de2..ca41829846 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -1,4 +1,4 @@ - + @@ -30,14 +30,14 @@ - - + + - + @@ -68,7 +68,7 @@ - +