diff --git a/build/Version.props b/build/Version.props
index d6c8e492e3..a06f8924e2 100644
--- a/build/Version.props
+++ b/build/Version.props
@@ -7,7 +7,7 @@
4.0.0
9.3.0
9.3.0
- 10.3.0
+ 10.4.0
6.0.4
5.3.0
1.1.1
diff --git a/src/Tgstation.Server.Client/ApiClient.cs b/src/Tgstation.Server.Client/ApiClient.cs
index c155355b7e..2915a42865 100644
--- a/src/Tgstation.Server.Client/ApiClient.cs
+++ b/src/Tgstation.Server.Client/ApiClient.cs
@@ -30,7 +30,7 @@ namespace Tgstation.Server.Client
///
public ApiHeaders Headers
{
- get => headers;
+ get => headers ?? throw new InvalidOperationException("ApiClient constructed without headers!");
set => headers = value ?? throw new InvalidOperationException("Cannot set null headers!");
}
@@ -64,7 +64,7 @@ namespace Tgstation.Server.Client
///
/// Backing field for .
///
- ApiHeaders headers;
+ ApiHeaders? headers;
///
/// Get the to use.
@@ -131,11 +131,11 @@ namespace Tgstation.Server.Client
/// The value of .
/// The value of .
/// The value of .
- public ApiClient(IHttpClient httpClient, Uri url, ApiHeaders apiHeaders, ApiHeaders? tokenRefreshHeaders)
+ public ApiClient(IHttpClient httpClient, Uri url, ApiHeaders? apiHeaders, ApiHeaders? tokenRefreshHeaders)
{
this.httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
Url = url ?? throw new ArgumentNullException(nameof(url));
- headers = apiHeaders ?? throw new ArgumentNullException(nameof(apiHeaders));
+ headers = apiHeaders;
this.tokenRefreshHeaders = tokenRefreshHeaders;
requestLoggers = new List();
@@ -257,6 +257,9 @@ namespace Tgstation.Server.Client
if (tokenRefreshHeaders == null)
return false;
+ if (headers == null)
+ throw new InvalidOperationException("Cannot refresh token with no headers!");
+
var startingToken = headers.Token;
await semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false);
try
@@ -351,8 +354,8 @@ namespace Tgstation.Server.Client
if (content != null)
request.Content = content;
- var headersToUse = tokenRefresh ? tokenRefreshHeaders! : headers;
- headersToUse.SetRequestHeaders(request.Headers, instanceId);
+ var headersToUse = tokenRefresh ? tokenRefreshHeaders : headers;
+ headersToUse?.SetRequestHeaders(request.Headers, instanceId);
if (fileDownload)
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Octet));
diff --git a/src/Tgstation.Server.Client/ApiClientFactory.cs b/src/Tgstation.Server.Client/ApiClientFactory.cs
index 26881d2d26..1e9e0f64ac 100644
--- a/src/Tgstation.Server.Client/ApiClientFactory.cs
+++ b/src/Tgstation.Server.Client/ApiClientFactory.cs
@@ -8,6 +8,6 @@ namespace Tgstation.Server.Client
sealed class ApiClientFactory : IApiClientFactory
{
///
- public IApiClient CreateApiClient(Uri url, ApiHeaders apiHeaders, ApiHeaders? tokenRefreshHeaders) => new ApiClient(new HttpClientImplementation(), url, apiHeaders, tokenRefreshHeaders);
+ public IApiClient CreateApiClient(Uri url, ApiHeaders? apiHeaders, ApiHeaders? tokenRefreshHeaders) => new ApiClient(new HttpClientImplementation(), url, apiHeaders, tokenRefreshHeaders);
}
}
diff --git a/src/Tgstation.Server.Client/IApiClientFactory.cs b/src/Tgstation.Server.Client/IApiClientFactory.cs
index 8e5b7d704b..077802e254 100644
--- a/src/Tgstation.Server.Client/IApiClientFactory.cs
+++ b/src/Tgstation.Server.Client/IApiClientFactory.cs
@@ -16,6 +16,6 @@ namespace Tgstation.Server.Client
/// The for the .
/// The to use to generate a new .
/// A new .
- IApiClient CreateApiClient(Uri url, ApiHeaders apiHeaders, ApiHeaders? tokenRefreshHeaders);
+ IApiClient CreateApiClient(Uri url, ApiHeaders? apiHeaders, ApiHeaders? tokenRefreshHeaders);
}
}
diff --git a/src/Tgstation.Server.Client/IServerClientFactory.cs b/src/Tgstation.Server.Client/IServerClientFactory.cs
index 0216fa0e4e..69784ec765 100644
--- a/src/Tgstation.Server.Client/IServerClientFactory.cs
+++ b/src/Tgstation.Server.Client/IServerClientFactory.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Response;
namespace Tgstation.Server.Client
@@ -13,7 +14,21 @@ namespace Tgstation.Server.Client
public interface IServerClientFactory
{
///
- /// Create a .
+ /// Gets the for a given .
+ ///
+ /// The URL to access TGS.
+ /// Optional s.
+ /// Optional representing timeout for the HTTP request.
+ /// Optional for the operation.
+ /// A resulting in the .
+ Task GetServerInformation(
+ Uri host,
+ IEnumerable? requestLoggers = null,
+ TimeSpan? timeout = null,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// Create a using a password login.
///
/// The URL to access TGS.
/// The username to for the .
@@ -32,6 +47,24 @@ namespace Tgstation.Server.Client
bool attemptLoginRefresh = true,
CancellationToken cancellationToken = default);
+ ///
+ /// Create a using am OAuth login.
+ ///
+ /// The URL to access TGS.
+ /// The OAuth code used to complete the flow.
+ /// The .
+ /// Optional initial s to add to the .
+ /// Optional representing timeout for the connection.
+ /// Optional for the operation.
+ /// A resulting in a new .
+ Task CreateFromOAuth(
+ Uri host,
+ string oAuthCode,
+ OAuthProvider oAuthProvider,
+ IEnumerable? requestLoggers = null,
+ TimeSpan? timeout = null,
+ CancellationToken cancellationToken = default);
+
///
/// Create a .
///
diff --git a/src/Tgstation.Server.Client/ServerClientFactory.cs b/src/Tgstation.Server.Client/ServerClientFactory.cs
index 4acd77d8ee..c3ed8ed17d 100644
--- a/src/Tgstation.Server.Client/ServerClientFactory.cs
+++ b/src/Tgstation.Server.Client/ServerClientFactory.cs
@@ -6,6 +6,7 @@ using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api;
+using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Response;
namespace Tgstation.Server.Client
@@ -33,7 +34,7 @@ namespace Tgstation.Server.Client
}
///
- public async Task CreateFromLogin(
+ public Task CreateFromLogin(
Uri host,
string username,
string password,
@@ -49,33 +50,38 @@ namespace Tgstation.Server.Client
if (password == null)
throw new ArgumentNullException(nameof(password));
- requestLoggers ??= Enumerable.Empty();
-
- TokenResponse token;
var loginHeaders = new ApiHeaders(productHeaderValue, username, password);
- using (var api = ApiClientFactory.CreateApiClient(host, loginHeaders, null))
- {
- foreach (var requestLogger in requestLoggers)
- api.AddRequestLogger(requestLogger);
+ return CreateWithNewToken(
+ host,
+ loginHeaders,
+ requestLoggers,
+ timeout,
+ false,
+ cancellationToken);
+ }
- if (timeout.HasValue)
- api.Timeout = timeout.Value;
- token = await api.Update(Routes.Root, cancellationToken).ConfigureAwait(false);
- }
+ ///
+ public Task CreateFromOAuth(
+ Uri host,
+ string oAuthCode,
+ OAuthProvider oAuthProvider,
+ IEnumerable? requestLoggers = null,
+ TimeSpan? timeout = null,
+ CancellationToken cancellationToken = default)
+ {
+ if (host == null)
+ throw new ArgumentNullException(nameof(host));
+ if (oAuthCode == null)
+ throw new ArgumentNullException(nameof(oAuthCode));
- if (!attemptLoginRefresh)
- loginHeaders = null;
-
- var apiHeaders = new ApiHeaders(productHeaderValue, token.Bearer!);
-
- var client = new ServerClient(ApiClientFactory.CreateApiClient(host, apiHeaders, loginHeaders), token);
- if (timeout.HasValue)
- client.Timeout = timeout.Value;
-
- foreach (var requestLogger in requestLoggers)
- client.AddRequestLogger(requestLogger);
-
- return client;
+ var loginHeaders = new ApiHeaders(productHeaderValue, oAuthCode, oAuthProvider);
+ return CreateWithNewToken(
+ host,
+ loginHeaders,
+ requestLoggers,
+ timeout,
+ false,
+ cancellationToken);
}
///
@@ -90,5 +96,71 @@ namespace Tgstation.Server.Client
return new ServerClient(ApiClientFactory.CreateApiClient(host, new ApiHeaders(productHeaderValue, token.Bearer), null), token);
}
+
+ ///
+ public async Task GetServerInformation(
+ Uri host,
+ IEnumerable? requestLoggers = null,
+ TimeSpan? timeout = null,
+ CancellationToken cancellationToken = default)
+ {
+ using var api = ApiClientFactory.CreateApiClient(host, null, null);
+
+ if (requestLoggers != null)
+ foreach (var requestLogger in requestLoggers)
+ api.AddRequestLogger(requestLogger);
+
+ if (timeout.HasValue)
+ api.Timeout = timeout.Value;
+
+ return await api.Read(Routes.Root, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Creates a from a login operation.
+ ///
+ /// The URL to access TGS.
+ /// The to use for the login operation.
+ /// Optional initial s to add to the .
+ /// Optional representing timeout for the connection.
+ /// If may be used to re-login in the future.
+ /// Optional for the operation.
+ /// A resulting in a new .
+ async Task CreateWithNewToken(
+ Uri host,
+ ApiHeaders loginHeaders,
+ IEnumerable? requestLoggers,
+ TimeSpan? timeout,
+ bool attemptLoginRefresh,
+ CancellationToken cancellationToken)
+ {
+ requestLoggers ??= Enumerable.Empty();
+
+ TokenResponse token;
+ using (var api = ApiClientFactory.CreateApiClient(host, loginHeaders, null))
+ {
+ foreach (var requestLogger in requestLoggers)
+ api.AddRequestLogger(requestLogger);
+
+ if (timeout.HasValue)
+ api.Timeout = timeout.Value;
+ token = await api.Update(Routes.Root, cancellationToken).ConfigureAwait(false);
+ }
+
+ var apiHeaders = new ApiHeaders(productHeaderValue, token.Bearer!);
+ var client = new ServerClient(
+ ApiClientFactory.CreateApiClient(
+ host,
+ apiHeaders,
+ attemptLoginRefresh ? loginHeaders : null),
+ token);
+ if (timeout.HasValue)
+ client.Timeout = timeout.Value;
+
+ foreach (var requestLogger in requestLoggers)
+ client.AddRequestLogger(requestLogger);
+
+ return client;
+ }
}
}
diff --git a/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj b/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj
index cdaaf000b2..72003fcec5 100644
--- a/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj
+++ b/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj
@@ -16,7 +16,7 @@
https://github.com/tgstation/tgstation-server
2018
json web api tgstation-server tgstation ss13 byond client
- Fixed automatic refresh erroring internally. Fixed request loggers given to ServerClientFactory not being passed to the IServerClient.
+ Added OAuth login methods to IServerClientFactory.
true
snupkg
../../build/analyzers.ruleset