Skip to content

Commit

Permalink
[2.x] History Encryption (#25)
Browse files Browse the repository at this point in the history
* added header constants

* refactor resolve props

* added always prop

* added always prop test

* added async task wrapper

* added clear history & history encryption

* Delete InertiaCore/Utils/Header.cs

* fix test sdks?

* Minor changes

---------

Co-authored-by: Kacper Ziubryniewicz <[email protected]>
  • Loading branch information
adrum and kapi2289 authored Feb 23, 2025
1 parent 5a1c858 commit 81aa149
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 5 deletions.
1 change: 1 addition & 0 deletions InertiaCore/Models/InertiaOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ public class InertiaOptions

public bool SsrEnabled { get; set; } = false;
public string SsrUrl { get; set; } = "http://127.0.0.1:13714/render";
public bool EncryptHistory { get; set; } = false;
}
2 changes: 2 additions & 0 deletions InertiaCore/Models/Page.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ internal class Page
public string Component { get; set; } = default!;
public string? Version { get; set; }
public string Url { get; set; } = default!;
public bool EncryptHistory { get; set; } = false;
public bool ClearHistory { get; set; } = false;
}
12 changes: 8 additions & 4 deletions InertiaCore/Response.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ public class Response : IActionResult
private readonly Dictionary<string, object?> _props;
private readonly string _rootView;
private readonly string? _version;
private readonly bool _encryptHistory;
private readonly bool _clearHistory;

private ActionContext? _context;
private Page? _page;
private IDictionary<string, object>? _viewData;

internal Response(string component, Dictionary<string, object?> props, string rootView, string? version)
=> (_component, _props, _rootView, _version) = (component, props, rootView, version);
internal Response(string component, Dictionary<string, object?> props, string rootView, string? version, bool encryptHistory, bool clearHistory)
=> (_component, _props, _rootView, _version, _encryptHistory, _clearHistory) = (component, props, rootView, version, encryptHistory, clearHistory);

public async Task ExecuteResultAsync(ActionContext context)
{
Expand All @@ -40,7 +42,9 @@ protected internal async Task ProcessResponse()
Component = _component,
Version = _version,
Url = _context!.RequestedUri(),
Props = props
Props = props,
EncryptHistory = _encryptHistory,
ClearHistory = _clearHistory,
};

page.Props["errors"] = GetErrors();
Expand Down Expand Up @@ -169,7 +173,7 @@ protected internal async Task ProcessResponse()
protected internal JsonResult GetJson()
{
_context!.HttpContext.Response.Headers.Override(InertiaHeader.Inertia, "true");
_context!.HttpContext.Response.Headers.Override("Vary", "Accept");
_context!.HttpContext.Response.Headers.Override("Vary", InertiaHeader.Inertia);
_context!.HttpContext.Response.StatusCode = 200;

return new JsonResult(_page, new JsonSerializerOptions
Expand Down
10 changes: 9 additions & 1 deletion InertiaCore/ResponseFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ internal interface IResponseFactory
public LocationResult Location(string url);
public void Share(string key, object? value);
public void Share(IDictionary<string, object?> data);
public void ClearHistory(bool clear = true);
public void EncryptHistory(bool encrypt = true);
public AlwaysProp Always(object? value);
public AlwaysProp Always(Func<object?> callback);
public AlwaysProp Always(Func<Task<object?>> callback);
Expand All @@ -36,6 +38,8 @@ internal class ResponseFactory : IResponseFactory
private readonly IOptions<InertiaOptions> _options;

private object? _version;
private bool _clearHistory;
private bool? _encryptHistory;

public ResponseFactory(IHttpContextAccessor contextAccessor, IGateway gateway, IOptions<InertiaOptions> options) =>
(_contextAccessor, _gateway, _options) = (contextAccessor, gateway, options);
Expand All @@ -50,7 +54,7 @@ public Response Render(string component, object? props = null)
.ToDictionary(o => o.Name, o => o.GetValue(props))
};

return new Response(component, dictProps, _options.Value.RootView, GetVersion());
return new Response(component, dictProps, _options.Value.RootView, GetVersion(), _encryptHistory ?? _options.Value.EncryptHistory, _clearHistory);
}

public async Task<IHtmlContent> Head(dynamic model)
Expand Down Expand Up @@ -131,6 +135,10 @@ public void Share(IDictionary<string, object?> data)
context.Features.Set(sharedData);
}

public void ClearHistory(bool clear = true) => _clearHistory = clear;

public void EncryptHistory(bool encrypt = true) => _encryptHistory = encrypt;

public LazyProp Lazy(Func<object?> callback) => new(callback);
public LazyProp Lazy(Func<Task<object?>> callback) => new(callback);
public AlwaysProp Always(object? value) => new(value);
Expand Down
90 changes: 90 additions & 0 deletions InertiaCoreTests/UnitTestHistory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using InertiaCore.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace InertiaCoreTests;

public partial class Tests
{
[Test]
[Description("Test if history encryption is sent correctly.")]
public async Task TestHistoryEncryptionResult()
{
_factory.EncryptHistory();

var response = _factory.Render("Test/Page", new
{
Test = "Test"
});

var headers = new HeaderDictionary
{
{ "X-Inertia", "true" }
};

var context = PrepareContext(headers);

response.SetContext(context);
await response.ProcessResponse();

var result = response.GetResult();

Assert.Multiple(() =>
{
Assert.That(result, Is.InstanceOf<JsonResult>());

var json = (result as JsonResult)?.Value;
Assert.That(json, Is.InstanceOf<Page>());

Assert.That((json as Page)?.ClearHistory, Is.EqualTo(false));
Assert.That((json as Page)?.EncryptHistory, Is.EqualTo(true));
Assert.That((json as Page)?.Component, Is.EqualTo("Test/Page"));
Assert.That((json as Page)?.Props, Is.EqualTo(new Dictionary<string, object?>
{
{ "test", "Test" },
{ "errors", new Dictionary<string, string>(0) }
}));
});
}

[Test]
[Description("Test if clear history is sent correctly.")]
public async Task TestClearHistoryResult()
{
_factory.ClearHistory();

var response = _factory.Render("Test/Page", new
{
Test = "Test"
});

var headers = new HeaderDictionary
{
{ "X-Inertia", "true" }
};

var context = PrepareContext(headers);

response.SetContext(context);
await response.ProcessResponse();

var result = response.GetResult();

Assert.Multiple(() =>
{
Assert.That(result, Is.InstanceOf<JsonResult>());

var json = (result as JsonResult)?.Value;
Assert.That(json, Is.InstanceOf<Page>());

Assert.That((json as Page)?.ClearHistory, Is.EqualTo(true));
Assert.That((json as Page)?.EncryptHistory, Is.EqualTo(false));
Assert.That((json as Page)?.Component, Is.EqualTo("Test/Page"));
Assert.That((json as Page)?.Props, Is.EqualTo(new Dictionary<string, object?>
{
{ "test", "Test" },
{ "errors", new Dictionary<string, string>(0) }
}));
});
}
}

0 comments on commit 81aa149

Please sign in to comment.