Skip to content

Commit

Permalink
Merge pull request #1955 from tgstation/MoreShit [NugetDeploy][GQLDep…
Browse files Browse the repository at this point in the history
…loy]

More GraphQL stuff + Webpanel version bump
  • Loading branch information
Cyberboss authored Oct 7, 2024
2 parents 0c01d9f + bb8a983 commit d706658
Show file tree
Hide file tree
Showing 14 changed files with 42 additions and 36 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1908,7 +1908,7 @@ jobs:
name: Ensure TGS Release is Latest GitHub Release
needs: [deploy-dm, deploy-rest, deploy-gql]
runs-on: ubuntu-latest
if: (!(cancelled() || failure())) && (!contains(github.event.head_commit.message, '[TGSDeploy]')) && (needs.deploy-dm.result == 'success' || needs.deploy-http.result == 'success')
if: (!(cancelled() || failure())) && (!contains(github.event.head_commit.message, '[TGSDeploy]')) && (needs.deploy-dm.result == 'success' || needs.deploy-rest.result == 'success' || needs.deploy-gql.result == 'success')
steps:
- name: Setup dotnet
uses: actions/setup-dotnet@v4
Expand Down
6 changes: 3 additions & 3 deletions build/Version.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
<TgsCoreVersion>6.10.0</TgsCoreVersion>
<TgsConfigVersion>5.3.0</TgsConfigVersion>
<TgsRestVersion>10.10.0</TgsRestVersion>
<TgsGraphQLVersion>0.1.0</TgsGraphQLVersion>
<TgsGraphQLVersion>0.2.0</TgsGraphQLVersion>
<TgsCommonLibraryVersion>7.0.0</TgsCommonLibraryVersion>
<TgsApiLibraryVersion>16.0.0</TgsApiLibraryVersion>
<TgsClientVersion>19.0.0</TgsClientVersion>
<TgsApiLibraryVersion>16.1.0</TgsApiLibraryVersion>
<TgsClientVersion>19.1.0</TgsClientVersion>
<TgsDmapiVersion>7.3.0</TgsDmapiVersion>
<TgsInteropVersion>5.10.0</TgsInteropVersion>
<TgsHostWatchdogVersion>1.5.0</TgsHostWatchdogVersion>
Expand Down
2 changes: 1 addition & 1 deletion build/WebpanelVersion.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- This is in it's own file to help incremental building, changing it causes a complete rebuild of the web panel -->
<TgsWebpanelVersion>6.2.0</TgsWebpanelVersion>
<TgsWebpanelVersion>6.3.0</TgsWebpanelVersion>
</PropertyGroup>
</Project>
5 changes: 3 additions & 2 deletions src/Tgstation.Server.Api/ApiHeaders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ void AddError(HeaderErrorTypes headerType, string message)

var jsonAccept = new Microsoft.Net.Http.Headers.MediaTypeHeaderValue(ApplicationJsonMime);
var eventStreamAccept = new Microsoft.Net.Http.Headers.MediaTypeHeaderValue(TextEventStreamMime);
if (!requestHeaders.Accept.Any(jsonAccept.IsSubsetOf))
if (!requestHeaders.Accept.Any(accept => accept.IsSubsetOf(jsonAccept)))
if (!allowEventStreamAccept)
AddError(HeaderErrorTypes.Accept, $"Client does not accept {ApplicationJsonMime}!");
else if (!requestHeaders.Accept.Any(eventStreamAccept.IsSubsetOf))
Expand Down Expand Up @@ -358,8 +358,9 @@ void AddError(HeaderErrorTypes headerType, string message)
/// <summary>
/// Checks if the <see cref="ApiVersion"/> is compatible with <see cref="Version"/>.
/// </summary>
/// <param name="alternateApiVersion">The <see cref="System.Version"/> that can alternatively be used as the <see cref="ApiVersion"/>.</param>
/// <returns><see langword="true"/> if the API is compatible, <see langword="false"/> otherwise.</returns>
public bool Compatible() => CheckCompatibility(ApiVersion);
public bool Compatible(Version? alternateApiVersion = null) => CheckCompatibility(ApiVersion) || (alternateApiVersion != null && alternateApiVersion.Major == ApiVersion.Major);

/// <summary>
/// Set <see cref="HttpRequestHeaders"/> using the <see cref="ApiHeaders"/>. This initially clears <paramref name="headers"/>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public AuthenticatedGraphQLServerClient(
loginResult)
{
this.getRestClientForToken = getRestClientForToken ?? throw new ArgumentNullException(nameof(getRestClientForToken));
restClient = getRestClientForToken(loginResult.Data!.Login.Bearer!.EncodedToken);
restClient = getRestClientForToken(loginResult.Data!.Login.LoginResult!.Bearer.EncodedToken);
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
mutation Login {
login {
bearer
loginResult {
bearer
}
errors {
... on ErrorMessageError {
message
Expand Down
5 changes: 2 additions & 3 deletions src/Tgstation.Server.Client.GraphQL/LoginResultExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ public static JsonWebToken EnsureSuccess(this IOperationResult<ILoginResult> log
}
}

var bearer = data.Bearer;
if (bearer == null)
if (data.LoginResult == null)
{
if (errors != null)
{
Expand All @@ -68,7 +67,7 @@ public static JsonWebToken EnsureSuccess(this IOperationResult<ILoginResult> log
throw new AuthenticationException($"Null bearer and error fields!");
}

return bearer;
return data.LoginResult.Bearer;
}
}
}
4 changes: 2 additions & 2 deletions src/Tgstation.Server.Host/Authority/ILoginAuthority.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public interface ILoginAuthority : IAuthority
/// Attempt to login to the server with the current crentials.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in a <see cref="LoginPayload"/> and <see cref="Models.User"/> <see cref="AuthorityResponse{TResult}"/>.</returns>
ValueTask<AuthorityResponse<LoginPayload>> AttemptLogin(CancellationToken cancellationToken);
/// <returns>A <see cref="ValueTask{TResult}"/> resulting in a <see cref="LoginResult"/> and <see cref="Models.User"/> <see cref="AuthorityResponse{TResult}"/>.</returns>
ValueTask<AuthorityResponse<LoginResult>> AttemptLogin(CancellationToken cancellationToken);
}
}
30 changes: 15 additions & 15 deletions src/Tgstation.Server.Host/Authority/LoginAuthority.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ sealed class LoginAuthority : AuthorityBase, ILoginAuthority
/// Generate an <see cref="AuthorityResponse{TResult}"/> for a given <paramref name="headersException"/>.
/// </summary>
/// <param name="headersException">The <see cref="HeadersException"/> to generate a response for.</param>
/// <returns>A new, errored <see cref="LoginPayload"/> <see cref="AuthorityResponse{TResult}"/>.</returns>
static AuthorityResponse<LoginPayload> GenerateHeadersExceptionResponse(HeadersException headersException)
/// <returns>A new, errored <see cref="LoginResult"/> <see cref="AuthorityResponse{TResult}"/>.</returns>
static AuthorityResponse<LoginResult> GenerateHeadersExceptionResponse(HeadersException headersException)
=> new(
new ErrorMessageResponse(ErrorCode.BadHeaders)
{
Expand Down Expand Up @@ -131,14 +131,14 @@ public LoginAuthority(
}

/// <inheritdoc />
public async ValueTask<AuthorityResponse<LoginPayload>> AttemptLogin(CancellationToken cancellationToken)
public async ValueTask<AuthorityResponse<LoginResult>> AttemptLogin(CancellationToken cancellationToken)
{
var headers = apiHeadersProvider.ApiHeaders;
if (headers == null)
return GenerateHeadersExceptionResponse(apiHeadersProvider.HeadersException!);

if (headers.IsTokenAuthentication)
return BadRequest<LoginPayload>(ErrorCode.TokenWithToken);
return BadRequest<LoginResult>(ErrorCode.TokenWithToken);

var oAuthLogin = headers.OAuthProvider.HasValue;

Expand Down Expand Up @@ -168,7 +168,7 @@ public async ValueTask<AuthorityResponse<LoginPayload>> AttemptLogin(Cancellatio
.GetValidator(oAuthProvider);

if (validator == null)
return BadRequest<LoginPayload>(ErrorCode.OAuthProviderDisabled);
return BadRequest<LoginResult>(ErrorCode.OAuthProviderDisabled);

externalUserId = await validator
.ValidateResponseCode(headers.OAuthCode!, cancellationToken);
Expand All @@ -177,11 +177,11 @@ public async ValueTask<AuthorityResponse<LoginPayload>> AttemptLogin(Cancellatio
}
catch (Octokit.RateLimitExceededException ex)
{
return RateLimit<LoginPayload>(ex);
return RateLimit<LoginResult>(ex);
}

if (externalUserId == null)
return Unauthorized<LoginPayload>();
return Unauthorized<LoginResult>();

query = query.Where(
x => x.OAuthConnections!.Any(
Expand All @@ -192,7 +192,7 @@ public async ValueTask<AuthorityResponse<LoginPayload>> AttemptLogin(Cancellatio
{
var canonicalUserName = User.CanonicalizeName(headers.Username!);
if (canonicalUserName == User.CanonicalizeName(User.TgsSystemUserName))
return Unauthorized<LoginPayload>();
return Unauthorized<LoginResult>();

if (systemIdentity == null)
query = query.Where(x => x.CanonicalName == canonicalUserName);
Expand All @@ -204,7 +204,7 @@ public async ValueTask<AuthorityResponse<LoginPayload>> AttemptLogin(Cancellatio

// No user? You're not allowed
if (user == null)
return Unauthorized<LoginPayload>();
return Unauthorized<LoginResult>();

// A system user may have had their name AND password changed to one in our DB...
// Or a DB user was created that had the same user/pass as a system user
Expand All @@ -219,7 +219,7 @@ public async ValueTask<AuthorityResponse<LoginPayload>> AttemptLogin(Cancellatio
{
// DB User password check and update
if (!isLikelyDbUser || !cryptographySuite.CheckUserPassword(user, headers.Password!))
return Unauthorized<LoginPayload>();
return Unauthorized<LoginResult>();
if (user.PasswordHash != originalHash)
{
Logger.LogDebug("User ID {userId}'s password hash needs a refresh, updating database.", user.Id);
Expand Down Expand Up @@ -262,11 +262,11 @@ public async ValueTask<AuthorityResponse<LoginPayload>> AttemptLogin(Cancellatio
if (!user.Enabled!.Value)
{
Logger.LogTrace("Not logging in disabled user {userId}.", user.Id);
return Forbid<LoginPayload>();
return Forbid<LoginResult>();
}

var token = tokenFactory.CreateToken(user, oAuthLogin);
var payload = new LoginPayload
var payload = new LoginResult
{
Bearer = token,
User = ((IApiTransformable<User, GraphQL.Types.User, UserGraphQLTransformer>)user).ToApi(),
Expand All @@ -277,7 +277,7 @@ public async ValueTask<AuthorityResponse<LoginPayload>> AttemptLogin(Cancellatio

Logger.LogDebug("Successfully logged in user {userId}!", user.Id);

return new AuthorityResponse<LoginPayload>(payload);
return new AuthorityResponse<LoginResult>(payload);
}
}

Expand All @@ -286,9 +286,9 @@ public async ValueTask<AuthorityResponse<LoginPayload>> AttemptLogin(Cancellatio
/// </summary>
/// <param name="systemIdentity">The <see cref="ISystemIdentity"/> to cache.</param>
/// <param name="user">The <see cref="User"/> the <paramref name="systemIdentity"/> was generated for.</param>
/// <param name="loginPayload">The <see cref="LoginPayload"/> for the successful login.</param>
/// <param name="loginPayload">The <see cref="LoginResult"/> for the successful login.</param>
/// <returns>A <see cref="ValueTask"/> representing the running operation.</returns>
private async ValueTask CacheSystemIdentity(ISystemIdentity systemIdentity, User user, LoginPayload loginPayload)
private async ValueTask CacheSystemIdentity(ISystemIdentity systemIdentity, User user, LoginResult loginPayload)
{
// expire the identity slightly after the auth token in case of lag
var identExpiry = loginPayload.ToApi().ParseJwt().ValidTo;
Expand Down
2 changes: 1 addition & 1 deletion src/Tgstation.Server.Host/Controllers/ApiRootController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ public ValueTask<IActionResult> CreateToken(CancellationToken cancellationToken)
return ValueTask.FromResult(HeadersIssue(ApiHeadersProvider.HeadersException!));
}

return loginAuthority.InvokeTransformable<LoginPayload, TokenResponse>(this, authority => authority.AttemptLogin(cancellationToken));
return loginAuthority.InvokeTransformable<LoginResult, TokenResponse>(this, authority => authority.AttemptLogin(cancellationToken));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Tgstation.Server.Api.Models;
using Tgstation.Server.Api.Models.Response;
using Tgstation.Server.Host.Configuration;
using Tgstation.Server.Host.Properties;
using Tgstation.Server.Host.System;
using Tgstation.Server.Host.Utils;

Expand Down Expand Up @@ -130,7 +131,9 @@ public static void UseApiCompatibility(this IApplicationBuilder applicationBuild
applicationBuilder.Use(async (context, next) =>
{
var apiHeadersProvider = context.RequestServices.GetRequiredService<IApiHeadersProvider>();
if (apiHeadersProvider.ApiHeaders?.Compatible() == false)
if (apiHeadersProvider.ApiHeaders?.Compatible(
Version.Parse(
MasterVersionsAttribute.Instance.RawGraphQLVersion)) == false)
{
await new BadRequestObjectResult(
new ErrorMessageResponse(ErrorCode.ApiMismatch))
Expand Down
4 changes: 2 additions & 2 deletions src/Tgstation.Server.Host/GraphQL/Mutation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ public sealed class Mutation
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A Bearer token to be used with further communication with the server.</returns>
[Error(typeof(ErrorMessageException))]
public ValueTask<LoginPayload> Login(
public ValueTask<LoginResult> Login(
[Service] IGraphQLAuthorityInvoker<ILoginAuthority> loginAuthority,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(loginAuthority);

return loginAuthority.Invoke<LoginPayload, LoginPayload>(
return loginAuthority.Invoke<LoginResult, LoginResult>(
authority => authority.AttemptLogin(cancellationToken));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ namespace Tgstation.Server.Host.GraphQL.Mutations.Payloads
/// <summary>
/// Success response for a login attempt.
/// </summary>
public sealed class LoginPayload : ILegacyApiTransformable<TokenResponse>
public sealed class LoginResult : ILegacyApiTransformable<TokenResponse>
{
/// <summary>
/// The JSON Web Token (JWT) to use as a Bearer token for accessing the server. Contains an expiry time.
/// </summary>
[GraphQLType<JwtType>]
[GraphQLNonNullType]
public required string Bearer { get; init; }

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions tests/Tgstation.Server.Tests/Live/RawRequestTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using System;
using System.Net;
Expand Down Expand Up @@ -461,7 +461,7 @@ static async Task TestGraphQLLogin(IRestServerClientFactory clientFactory, IRest
var result = await gqlClient.RunOperation(client => client.Login.ExecuteAsync(cancellationToken), cancellationToken);

Assert.IsNotNull(result.Data);
Assert.IsNull(result.Data.Login.Bearer);
Assert.IsNull(result.Data.Login.LoginResult);
Assert.IsNotNull(result.Data.Login.Errors);
Assert.AreEqual(1, result.Data.Login.Errors.Count);
var castResult = result.Data.Login.Errors[0] is ILogin_Login_Errors_ErrorMessageError loginError;
Expand Down

0 comments on commit d706658

Please sign in to comment.