Skip to content
This repository has been archived by the owner on Dec 18, 2023. It is now read-only.

Auth tests #80

Merged
merged 4 commits into from
Oct 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/Abstractions/Database.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public interface IDatabase<TResponse>
/// Signs up to a specific authentication scope.
/// </summary>
/// <param name="auth"> Variables used in a signin query. </param>
public new Task<TResponse> Signup(object auth, CancellationToken ct = default);
public new Task<TResponse> Signup<TRequest>(TRequest auth, CancellationToken ct = default) where TRequest : IAuth;

/// <summary>
/// Signs in to a specific authentication scope.
Expand All @@ -36,7 +36,7 @@ public interface IDatabase<TResponse>
/// <remarks>
/// This updates the internal <see cref="Config" />.
/// </remarks>
public new Task<TResponse> Signin(object auth, CancellationToken ct = default);
public new Task<TResponse> Signin<TRequest>(TRequest auth, CancellationToken ct = default) where TRequest : IAuth;

/// <summary>
/// Invalidates the authentication for the current connection.
Expand Down Expand Up @@ -181,7 +181,7 @@ public interface IDatabase
/// Signs up to a specific authentication scope.
/// </summary>
/// <param name="auth"> Variables used in a signin query. </param>
public Task<IResponse> Signup(object auth, CancellationToken ct = default);
public Task<IResponse> Signup<TRequest>(TRequest auth, CancellationToken ct = default) where TRequest : IAuth;

/// <summary>
/// Signs in to a specific authentication scope.
Expand All @@ -190,7 +190,7 @@ public interface IDatabase
/// <remarks>
/// This updates the internal <see cref="Config" />.
/// </remarks>
public Task<IResponse> Signin(object auth, CancellationToken ct = default);
public Task<IResponse> Signin<TRequest>(TRequest auth, CancellationToken ct = default) where TRequest : IAuth;

/// <summary>
/// Invalidates the authentication for the current connection.
Expand Down
4 changes: 2 additions & 2 deletions src/Configuration/Auth.cs → src/Configuration/AuthMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ namespace SurrealDB.Configuration;
/// <summary>
/// Available authentication methods
/// </summary>
public enum Auth : byte {
public enum AuthMethod : byte {
None = 0,
Basic,
JsonWebToken,
}
}
13 changes: 9 additions & 4 deletions src/Configuration/Config.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Net;
using System.Net;

namespace SurrealDB.Configuration;

Expand All @@ -14,19 +14,24 @@ public struct Config {
public IPEndPoint? Endpoint { get; set; }

/// <summary>
/// Optional: The database to export the data from.
/// Optional: Sets the selected Database for queries.
/// </summary>
public string? Database { get; set; }

/// <summary>
/// Optional: The namespace to export the data from.
/// Optional: Sets the selected Namespace for queries.
/// </summary>
public string? Namespace { get; set; }

/// <summary>
/// Optional: Sets the desired authentication scope for for a user.
/// </summary>
public string? Scope { get; set; }

/// <summary>
/// The authentication method to use.
/// </summary>
public Auth Authentication { get; set; }
public AuthMethod Authentication { get; set; }

/// <summary>
/// Database authentication username to use when connecting.
Expand Down
4 changes: 2 additions & 2 deletions src/Configuration/ConfigBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ internal BasicAuth(IConfigBuilder? parent) {
/// <inheritdoc />
public void Configure(ref Config config) {
InvalidConfigException.ThrowIfNullOrWhitespace(Username, "Username cannot be null or whitespace");
config.Authentication = Auth.Basic;
config.Authentication = AuthMethod.Basic;
config.Username = Username;
config.Password = Password;
}
Expand Down Expand Up @@ -261,7 +261,7 @@ internal JwtAuth(IConfigBuilder? parent) {
/// <inheritdoc />
public void Configure(ref Config config) {
InvalidConfigException.ThrowIfNullOrWhitespace(Token, "Invalid Json Web Token");
config.Authentication = Auth.JsonWebToken;
config.Authentication = AuthMethod.JsonWebToken;
config.JsonWebToken = Token;
}

Expand Down
4 changes: 2 additions & 2 deletions src/Driver/Rest/DatabaseRest.IDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ async Task<IResponse> IDatabase.Use(string db, string ns, CancellationToken ct)
return await Use(db, ns, ct);
}

async Task<IResponse> IDatabase.Signup(object auth, CancellationToken ct) {
async Task<IResponse> IDatabase.Signup<TRequest>(TRequest auth, CancellationToken ct) {
return await Signup(auth, ct);
}

async Task<IResponse> IDatabase.Signin(object auth, CancellationToken ct) {
async Task<IResponse> IDatabase.Signin<TRequest>(TRequest auth, CancellationToken ct) {
return await Signin(auth, ct);
}

Expand Down
45 changes: 25 additions & 20 deletions src/Driver/Rest/DatabaseRest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,18 +97,18 @@ public Task<RestResponse> Use(
return CompletedOk;
}

public async Task<RestResponse> Signup(
object auth,
CancellationToken ct = default) {
public async Task<RestResponse> Signup<TRequest>(
TRequest auth,
CancellationToken ct = default) where TRequest : IAuth {
return await Signup(ToJsonContent(auth), ct);
}

public async Task<RestResponse> Signin(
object auth,
CancellationToken ct = default) {
// SetAuth(auth.Username, auth.Password);
public async Task<RestResponse> Signin<TRequest>(
TRequest auth,
CancellationToken ct = default) where TRequest : IAuth {

HttpResponseMessage rsp = await _client.PostAsync("signin", ToJsonContent(auth), ct);
return await rsp.ToSurrealFromSignin();
return await rsp.ToSurrealFromAuthResponse();
}

public Task<RestResponse> Invalidate(CancellationToken ct = default) {
Expand All @@ -121,7 +121,9 @@ public Task<RestResponse> Invalidate(CancellationToken ct = default) {
public Task<RestResponse> Authenticate(
string token,
CancellationToken ct = default) {
throw new NotSupportedException(); // TODO: Is it tho???
SetAuth(token);

return CompletedOk;
}

public Task<RestResponse> Let(
Expand Down Expand Up @@ -197,7 +199,8 @@ private void ConfigureClients() {
private void SetAuth(
string? user,
string? pass) {
// TODO: Support jwt auth
RemoveAuth();

_config.Username = user;
_config.Password = pass;
AuthenticationHeaderValue header = new(
Expand All @@ -208,7 +211,18 @@ private void SetAuth(
_client.DefaultRequestHeaders.Authorization = header;
}

private void SetAuth(
string? jwt) {
RemoveAuth();

_config.JsonWebToken = jwt;
AuthenticationHeaderValue header = new("Bearer", jwt);

_client.DefaultRequestHeaders.Authorization = header;
}

private void RemoveAuth() {
_config.JsonWebToken = null;
_config.Username = null;
_config.Password = null;
_client.DefaultRequestHeaders.Authorization = null;
Expand All @@ -235,7 +249,7 @@ public async Task<RestResponse> Signup(
HttpContent auth,
CancellationToken ct = default) {
HttpResponseMessage rsp = await _client.PostAsync("signup", auth, ct);
return await rsp.ToSurreal();
return await rsp.ToSurrealFromAuthResponse();
}

/// <inheritdoc cref="Query(string, IReadOnlyDictionary{string, object?}?, CancellationToken)" />
Expand Down Expand Up @@ -329,22 +343,13 @@ private HttpContent ToJsonContent<T>(T? v) {

private static HttpContent ToContent(string s = "") {
StringContent content = new(s, Encoding.UTF8, "application/json");

if (content.Headers.ContentType != null) {
// The server can only handle 'Content-Type' with 'application/json', remove any further information from this header
content.Headers.ContentType.CharSet = null;
}

return content;
}

private HttpRequestMessage ToRequestMessage(
HttpMethod method,
string requestUri,
string content = "") {
// SurrealDb must have a 'Content-Type' header defined,
// but HttpClient does not allow default request headers to be set.
// So we need to make PUT and DELETE requests with an empty request body, but with request headers
return new HttpRequestMessage { Method = method, RequestUri = new Uri(requestUri, UriKind.Relative), Content = ToContent(content), };
}
}
4 changes: 2 additions & 2 deletions src/Driver/Rest/RestClientExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public static Task<RestResponse> ToSurreal(this HttpResponseMessage msg) {
}

[DebuggerStepThrough]
public static Task<RestResponse> ToSurrealFromSignin(this HttpResponseMessage msg) {
return RestResponse.FromSignin(msg);
public static Task<RestResponse> ToSurrealFromAuthResponse(this HttpResponseMessage msg) {
return RestResponse.FromAuthResponse(msg);
}
}
18 changes: 12 additions & 6 deletions src/Driver/Rest/RestResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,14 @@ public bool TryGetResult(
/// <summary>
/// Parses a signing <see cref="HttpResponseMessage"/> containing JSON to a <see cref="RestResponse"/>.
/// </summary>
public static async Task<RestResponse> FromSignin(
public static async Task<RestResponse> FromAuthResponse(
HttpResponseMessage msg,
CancellationToken ct = default) {

// Signin returns a different object to the other response
// Signin and Signup returns a different object to the other response
// And for that reason needs it's on deserialization path
// The whole response is ultimately shoved into the RestResponse.Success.result field
// {"code":200,"details":"Authentication succeeded","token":""}
// {"code":200,"details":"Authentication succeeded","token":"a.jwt.token"}

#if NET6_0_OR_GREATER
Stream stream = await msg.Content.ReadAsStreamAsync(ct);
Expand All @@ -94,8 +94,8 @@ public static async Task<RestResponse> FromSignin(
return From(err);
}

JsonElement jsonElement = await JsonSerializer.DeserializeAsync<JsonElement>(stream, SerializerOptions.Shared, ct);
Success doc = new ("", "OK", jsonElement);
AuthResult jsonElement = await JsonSerializer.DeserializeAsync<AuthResult>(stream, SerializerOptions.Shared, ct);
Success doc = new ("", jsonElement.code.ToString(), jsonElement.token);

return From(doc);
}
Expand All @@ -122,6 +122,7 @@ public static async Task<RestResponse> From(
}

List<Success>? docs = await JsonSerializer.DeserializeAsync<List<Success>>(stream, SerializerOptions.Shared, ct);

Success doc = (docs?.FirstOrDefault(e => e.result.ValueKind != JsonValueKind.Null)).GetValueOrDefault(default);

return From(doc);
Expand Down Expand Up @@ -153,7 +154,7 @@ private static RestResponse From(Error? error) {

private static RestResponse From(Success success) {
if (success == default) {
return new(success.time, success.status, null, null, default);
return EmptyOk;
}
JsonElement e = success.result.IntoSingle();
return new(success.time, success.status, null, null, e);
Expand All @@ -174,4 +175,9 @@ private readonly record struct Success(
string time,
string status,
JsonElement result);

public readonly record struct AuthResult(
HttpStatusCode code,
string details,
JsonElement token);
}
4 changes: 2 additions & 2 deletions src/Driver/Rpc/DatabaseRpc.IDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ async Task<IResponse> IDatabase.Use(string db, string ns, CancellationToken ct)
return await Use(db, ns, ct);
}

async Task<IResponse> IDatabase.Signup(object auth, CancellationToken ct) {
async Task<IResponse> IDatabase.Signup<TRequest>(TRequest auth, CancellationToken ct) {
return await Signup(auth, ct);
}

async Task<IResponse> IDatabase.Signin(object auth, CancellationToken ct) {
async Task<IResponse> IDatabase.Signin<TRequest>(TRequest auth, CancellationToken ct) {
return await Signin(auth, ct);
}

Expand Down
21 changes: 10 additions & 11 deletions src/Driver/Rpc/DatabaseRpc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,18 @@ public async Task<RpcResponse> Use(
}

/// <inheritdoc />
public async Task<RpcResponse> Signup(
object auth,
CancellationToken ct = default) {
return await _client.Send(new() { method = "signup", parameters = new() { auth, }, }, ct).ToSurreal();
public async Task<RpcResponse> Signup<TRequest>(
TRequest auth,
CancellationToken ct = default) where TRequest : IAuth {
return await _client.Send(new() { method = "signup", parameters = new() { auth } }, ct).ToSurreal();
}

/// <inheritdoc />
public async Task<RpcResponse> Signin(
object auth,
CancellationToken ct = default) {
WsClient.Response rsp = await _client.Send(new() { method = "signin", parameters = new() { auth, }, }, ct);

// TODO: Update auth
public async Task<RpcResponse> Signin<TRequest>(
TRequest auth,
CancellationToken ct = default) where TRequest : IAuth {
WsClient.Response rsp = await _client.Send(new() { method = "signin", parameters = new() { auth } }, ct);

return rsp.ToSurreal();
}

Expand Down Expand Up @@ -181,7 +180,7 @@ private async Task SetAuth(
// TODO: Support jwt auth
_config.Username = user;
_config.Password = pass;
await Signin(new{ user = user, pass = pass, }, ct);
await Signin(new RootAuth(user, pass), ct);
}

public void Dispose() {
Expand Down
40 changes: 40 additions & 0 deletions src/Models/AuthRecords.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using SurrealDB.Models;

using System.Net;
// ReSharper disable InconsistentNaming

namespace SurrealDB.Models;

public interface IAuth {}

public readonly record struct RootAuth(
string user,
string pass) : IAuth;

public readonly record struct NamespaceAuth(
string user,
string pass,
string NS) : IAuth;

public readonly record struct DatabaseAuth(
string user,
string pass,
string NS,
string DB) : IAuth;

public readonly record struct ScopeAuth(
string user,
string pass,
string NS,
string DB,
string SC) : IAuth;

public readonly record struct Token(
string ID,
string NS,
string DB,
string SC,
DateTime exp,
DateTime iat,
DateTime nbf,
string iss) : IAuth;
2 changes: 1 addition & 1 deletion tests/Driver.Tests/DatabaseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ protected override async Task Run(T db) {
IResponse infoResp = await db.Info();
AssertOk(infoResp);

IResponse signInStatus = await db.Signin(new{ user = TestHelper.User, pass = TestHelper.Pass, });
IResponse signInStatus = await db.Signin(new RootAuth(TestHelper.User, TestHelper.Pass));

AssertOk(signInStatus);
//AssertOk(await db.Invalidate());
Expand Down
Loading