- add: TFA think
This commit is contained in:
@@ -21,19 +21,6 @@ public static class CurrentConVar
|
||||
"https://hub.spacestation14.com/api/servers"
|
||||
]);
|
||||
|
||||
public static readonly ConVar<string[]> AuthServers = ConVarBuilder.Build<string[]>("launcher.authServers", [
|
||||
"https://auth.spacestation14.com/"
|
||||
]);
|
||||
|
||||
public static readonly ConVar<AuthLoginPassword[]> AuthProfiles =
|
||||
ConVarBuilder.Build<AuthLoginPassword[]>("auth.profiles", []);
|
||||
|
||||
public static readonly ConVar<AuthLoginPassword> AuthCurrent =
|
||||
ConVarBuilder.Build<AuthLoginPassword>("auth.current");
|
||||
|
||||
public static readonly ConVar<string[]> Favorites =
|
||||
ConVarBuilder.Build<string[]>("server.favorites", []);
|
||||
|
||||
public static readonly ConVar<Dictionary<string, EngineVersionInfo>> EngineManifestBackup =
|
||||
ConVarBuilder.Build<Dictionary<string, EngineVersionInfo>>("engine.manifest.backup");
|
||||
public static readonly ConVar<ModulesInfo> ModuleManifestBackup =
|
||||
|
||||
@@ -1,13 +1,3 @@
|
||||
namespace Nebula.Shared.Models.Auth;
|
||||
|
||||
public readonly struct LoginToken
|
||||
{
|
||||
public readonly string Token;
|
||||
public readonly DateTimeOffset ExpireTime;
|
||||
|
||||
public LoginToken(string token, DateTimeOffset expireTime)
|
||||
{
|
||||
Token = token;
|
||||
ExpireTime = expireTime;
|
||||
}
|
||||
}
|
||||
public sealed record LoginToken(string Token, DateTimeOffset ExpireTime);
|
||||
@@ -1,5 +1,9 @@
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Nebula.Shared.Models.Auth;
|
||||
using Nebula.Shared.Utils;
|
||||
|
||||
namespace Nebula.Shared.Services;
|
||||
|
||||
@@ -10,11 +14,9 @@ public class AuthService(
|
||||
CancellationService cancellationService)
|
||||
{
|
||||
private readonly HttpClient _httpClient = new();
|
||||
public CurrentAuthInfo? SelectedAuth { get; private set; }
|
||||
|
||||
public string Reason = "";
|
||||
public CurrentAuthInfo? SelectedAuth { get; internal set; }
|
||||
|
||||
public async Task<bool> Auth(AuthLoginPassword authLoginPassword)
|
||||
public async Task Auth(AuthLoginPassword authLoginPassword, string? code = null)
|
||||
{
|
||||
var authServer = authLoginPassword.AuthServer;
|
||||
var login = authLoginPassword.Login;
|
||||
@@ -24,20 +26,24 @@ public class AuthService(
|
||||
|
||||
var authUrl = new Uri($"{authServer}api/auth/authenticate");
|
||||
|
||||
var result =
|
||||
await restService.PostAsync<AuthenticateResponse, AuthenticateRequest>(
|
||||
new AuthenticateRequest(login, password), authUrl, cancellationService.Token);
|
||||
|
||||
if (result.Value is null)
|
||||
try
|
||||
{
|
||||
Reason = result.Message;
|
||||
return false;
|
||||
var result =
|
||||
await restService.PostAsync<AuthenticateResponse, AuthenticateRequest>(
|
||||
new AuthenticateRequest(login, null, password, code), authUrl, cancellationService.Token);
|
||||
|
||||
SelectedAuth = new CurrentAuthInfo(result.UserId,
|
||||
new LoginToken(result.Token, result.ExpireTime), authLoginPassword.Login, authLoginPassword.AuthServer);
|
||||
}
|
||||
catch (RestRequestException e)
|
||||
{
|
||||
Console.WriteLine(e.Content);
|
||||
if (e.StatusCode != HttpStatusCode.Unauthorized) throw;
|
||||
var err = await e.Content.AsJson<AuthDenyError>();
|
||||
|
||||
if (err is null) throw;
|
||||
throw new AuthException(err);
|
||||
}
|
||||
|
||||
SelectedAuth = new CurrentAuthInfo(result.Value.UserId,
|
||||
new LoginToken(result.Value.Token, result.Value.ExpireTime), authLoginPassword);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ClearAuth()
|
||||
@@ -45,17 +51,17 @@ public class AuthService(
|
||||
SelectedAuth = null;
|
||||
}
|
||||
|
||||
public void SetAuth(Guid guid, string token, string login, string authServer)
|
||||
public async Task<bool> SetAuth(CurrentAuthInfo info)
|
||||
{
|
||||
SelectedAuth = new CurrentAuthInfo(guid, new LoginToken(token, DateTimeOffset.Now),
|
||||
new AuthLoginPassword(login, "", authServer));
|
||||
SelectedAuth = info;
|
||||
return await EnsureToken();
|
||||
}
|
||||
|
||||
public async Task<bool> EnsureToken()
|
||||
{
|
||||
if (SelectedAuth is null) return false;
|
||||
|
||||
var authUrl = new Uri($"{SelectedAuth.AuthLoginPassword.AuthServer}api/auth/ping");
|
||||
var authUrl = new Uri($"{SelectedAuth.AuthServer}api/auth/ping");
|
||||
|
||||
using var requestMessage = new HttpRequestMessage(HttpMethod.Get, authUrl);
|
||||
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("SS14Auth", SelectedAuth.Token.Token);
|
||||
@@ -67,6 +73,24 @@ public class AuthService(
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record CurrentAuthInfo(Guid UserId, LoginToken Token, AuthLoginPassword AuthLoginPassword);
|
||||
public sealed record CurrentAuthInfo(Guid UserId, LoginToken Token, string Login, string AuthServer);
|
||||
|
||||
public record AuthLoginPassword(string Login, string Password, string AuthServer);
|
||||
public record AuthLoginPassword(string Login, string Password, string AuthServer);
|
||||
|
||||
public sealed record AuthDenyError(string[] Errors, AuthenticateDenyCode Code);
|
||||
|
||||
public sealed class AuthException(AuthDenyError error) : Exception
|
||||
{
|
||||
public AuthDenyError Error { get; } = error;
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public enum AuthenticateDenyCode
|
||||
{
|
||||
None = 0,
|
||||
InvalidCredentials = 1,
|
||||
AccountUnconfirmed = 2,
|
||||
TfaRequired = 3,
|
||||
TfaInvalid = 4,
|
||||
AccountLocked = 5,
|
||||
}
|
||||
|
||||
@@ -17,13 +17,12 @@ public partial class ContentService(
|
||||
var info = new RobustBuildInfo();
|
||||
info.Url = url;
|
||||
var bi = await restService.GetAsync<ServerInfo>(url.InfoUri, cancellationToken);
|
||||
if (bi.Value is null) throw new NoNullAllowedException();
|
||||
info.BuildInfo = bi.Value;
|
||||
info.BuildInfo = bi;
|
||||
info.RobustManifestInfo = info.BuildInfo.Build.Acz
|
||||
? new RobustManifestInfo(new RobustPath(info.Url, "manifest.txt"), new RobustPath(info.Url, "download"),
|
||||
bi.Value.Build.ManifestHash)
|
||||
bi.Build.ManifestHash)
|
||||
: new RobustManifestInfo(new Uri(info.BuildInfo.Build.ManifestUrl),
|
||||
new Uri(info.BuildInfo.Build.ManifestDownloadUrl), bi.Value.Build.ManifestHash);
|
||||
new Uri(info.BuildInfo.Build.ManifestDownloadUrl), bi.Build.ManifestHash);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -40,12 +40,10 @@ public sealed class EngineService
|
||||
_debugService.Log("Fetching engine manifest from: " + CurrentConVar.EngineManifestUrl);
|
||||
var info = await _restService.GetAsync<Dictionary<string, EngineVersionInfo>>(
|
||||
new Uri(_varService.GetConfigValue(CurrentConVar.EngineManifestUrl)!), cancellationToken);
|
||||
if (info.Value is null)
|
||||
throw new Exception("Engine version info is null");
|
||||
|
||||
VersionInfos = info.Value;
|
||||
|
||||
_varService.SetConfigValue(CurrentConVar.EngineManifestBackup, info.Value);
|
||||
VersionInfos = info;
|
||||
|
||||
_varService.SetConfigValue(CurrentConVar.EngineManifestBackup, info);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -64,11 +62,11 @@ public sealed class EngineService
|
||||
var moduleInfo = await _restService.GetAsync<ModulesInfo>(
|
||||
new Uri(_varService.GetConfigValue(CurrentConVar.EngineModuleManifestUrl)!), cancellationToken);
|
||||
|
||||
if (moduleInfo.Value is null)
|
||||
if (moduleInfo is null)
|
||||
throw new Exception("Module version info is null");
|
||||
|
||||
ModuleInfos = moduleInfo.Value.Modules;
|
||||
_varService.SetConfigValue(CurrentConVar.ModuleManifestBackup, moduleInfo.Value);
|
||||
ModuleInfos = moduleInfo.Modules;
|
||||
_varService.SetConfigValue(CurrentConVar.ModuleManifestBackup, moduleInfo);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -177,7 +175,7 @@ public sealed class EngineService
|
||||
|
||||
var engineVersionObj = Version.Parse(engineVersion);
|
||||
var module = ModuleInfos[moduleName];
|
||||
var selectedVersion = module.Versions.Select(kv => new { Version = Version.Parse(kv.Key), kv.Key, kv.Value })
|
||||
var selectedVersion = module.Versions.Select(kv => new { Version = Version.Parse(kv.Key), kv.Key, kv })
|
||||
.Where(kv => engineVersionObj >= kv.Version)
|
||||
.MaxBy(kv => kv.Version);
|
||||
|
||||
|
||||
@@ -23,19 +23,26 @@ public class RestService
|
||||
_debug = debug;
|
||||
}
|
||||
|
||||
public async Task<RestResult<T>> GetAsync<T>(Uri uri, CancellationToken cancellationToken) where T : notnull
|
||||
public async Task<T> GetAsync<T>(Uri uri, CancellationToken cancellationToken) where T : notnull
|
||||
{
|
||||
var response = await _client.GetAsync(uri, cancellationToken);
|
||||
return await ReadResult<T>(response, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<T> GetAsyncDefault<T>(Uri uri, T defaultValue, CancellationToken cancellationToken)
|
||||
public async Task<T> GetAsyncDefault<T>(Uri uri, T defaultValue, CancellationToken cancellationToken) where T : notnull
|
||||
{
|
||||
var result = await GetAsync<T>(uri, cancellationToken);
|
||||
return result.Value ?? defaultValue;
|
||||
try
|
||||
{
|
||||
return await GetAsync<T>(uri, cancellationToken);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_debug.Error(e);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<RestResult<K>> PostAsync<K, T>(T information, Uri uri, CancellationToken cancellationToken) where K : notnull
|
||||
public async Task<K> PostAsync<K, T>(T information, Uri uri, CancellationToken cancellationToken) where K : notnull
|
||||
{
|
||||
var json = JsonSerializer.Serialize(information, _serializerOptions);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
@@ -43,7 +50,7 @@ public class RestService
|
||||
return await ReadResult<K>(response, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<RestResult<T>> PostAsync<T>(Stream stream, Uri uri, CancellationToken cancellationToken) where T : notnull
|
||||
public async Task<T> PostAsync<T>(Stream stream, Uri uri, CancellationToken cancellationToken) where T : notnull
|
||||
{
|
||||
using var multipartFormContent =
|
||||
new MultipartFormDataContent("Upload----" + DateTime.Now.ToString(CultureInfo.InvariantCulture));
|
||||
@@ -52,61 +59,32 @@ public class RestService
|
||||
return await ReadResult<T>(response, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<RestResult<T>> DeleteAsync<T>(Uri uri, CancellationToken cancellationToken) where T : notnull
|
||||
public async Task<T> DeleteAsync<T>(Uri uri, CancellationToken cancellationToken) where T : notnull
|
||||
{
|
||||
var response = await _client.DeleteAsync(uri, cancellationToken);
|
||||
return await ReadResult<T>(response, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<RestResult<T>> ReadResult<T>(HttpResponseMessage response, CancellationToken cancellationToken) where T : notnull
|
||||
private async Task<T> ReadResult<T>(HttpResponseMessage response, CancellationToken cancellationToken) where T : notnull
|
||||
{
|
||||
var content = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
|
||||
if (typeof(T) == typeof(RawResult))
|
||||
return (new RestResult<RawResult>(new RawResult(content), null, response.StatusCode) as RestResult<T>)!;
|
||||
|
||||
if (typeof(T) == typeof(string) && content is T t)
|
||||
return t;
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
_debug.Debug($"SUCCESSFUL GET CONTENT {typeof(T)}");
|
||||
|
||||
return new RestResult<T>(await response.Content.AsJson<T>(), null,
|
||||
response.StatusCode);
|
||||
return await response.Content.AsJson<T>();
|
||||
}
|
||||
|
||||
throw new HttpRequestException();
|
||||
throw new RestRequestException(response.Content, response.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
public class RestResult<T>
|
||||
public sealed class RestRequestException(HttpContent content, HttpStatusCode statusCode) : Exception
|
||||
{
|
||||
public string Message = "Ok";
|
||||
public HttpStatusCode StatusCode;
|
||||
public T Value;
|
||||
|
||||
public RestResult(T value, string? message, HttpStatusCode statusCode)
|
||||
{
|
||||
Value = value;
|
||||
if (message != null) Message = message;
|
||||
StatusCode = statusCode;
|
||||
}
|
||||
|
||||
public static implicit operator T(RestResult<T> result)
|
||||
{
|
||||
return result.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public class RawResult
|
||||
{
|
||||
public string Result;
|
||||
|
||||
public RawResult(string result)
|
||||
{
|
||||
Result = result;
|
||||
}
|
||||
|
||||
public static implicit operator string(RawResult result)
|
||||
{
|
||||
return result.Result;
|
||||
}
|
||||
public HttpStatusCode StatusCode { get; } = statusCode;
|
||||
public HttpContent Content { get; } = content;
|
||||
}
|
||||
Reference in New Issue
Block a user