Skip to content

Commit 21f7210

Browse files
committed
fix: Act on CryptographyException in LocalStorage (fixes #412)
1 parent 12a108b commit 21f7210

File tree

9 files changed

+57
-18
lines changed

9 files changed

+57
-18
lines changed

Directory.Packages.props

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,6 @@
5252
</ItemGroup>
5353
<ItemGroup Label="Tools">
5454
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
55-
<PackageVersion Include="Microsoft.Playwright" Version="1.50.0" />
55+
<PackageVersion Include="Microsoft.Playwright" Version="1.51.0" />
5656
</ItemGroup>
57-
</Project>
57+
</Project>

src/LinkDotNet.Blog.Web/Features/Bookmarks/BookmarkService.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public async Task SetBookmark(string postId, bool isBookmarked)
5555

5656
private async Task InitializeIfNotExists()
5757
{
58-
if (!(await localStorageService.ContainKeyAsync("bookmarks")))
58+
if (!(await localStorageService.ContainsKeyAsync("bookmarks")))
5959
{
6060
await localStorageService.SetItemAsync("bookmarks", new List<string>());
6161
}

src/LinkDotNet.Blog.Web/Features/Home/Components/ThemeToggler.razor

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
{
5252
await using var _ = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./Features/Home/Components/ThemeToggler.razor.js");
5353

54-
currentTheme = await LocalStorageService.ContainKeyAsync(StorageKey)
54+
currentTheme = await LocalStorageService.ContainsKeyAsync(StorageKey)
5555
? await LocalStorageService.GetItemAsync<string>(StorageKey)
5656
: await JSRuntime.InvokeAsync<string>("getCurrentSystemPreference");
5757

src/LinkDotNet.Blog.Web/Features/Services/ILocalStorageService.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace LinkDotNet.Blog.Web.Features.Services;
44

55
public interface ILocalStorageService
66
{
7-
ValueTask<bool> ContainKeyAsync(string key);
7+
ValueTask<bool> ContainsKeyAsync(string key);
88

99
ValueTask<T> GetItemAsync<T>(string key);
1010

src/LinkDotNet.Blog.Web/Features/Services/LocalStorageService.cs

+44-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Security.Cryptography;
24
using System.Threading.Tasks;
35
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
46

@@ -13,10 +15,47 @@ public LocalStorageService(ProtectedLocalStorage localStorage)
1315
this.localStorage = localStorage;
1416
}
1517

16-
public async ValueTask<bool> ContainKeyAsync(string key) => (await localStorage.GetAsync<object>(key)).Success;
18+
public async ValueTask<bool> ContainsKeyAsync(string key)
19+
{
20+
try
21+
{
22+
return (await localStorage.GetAsync<object>(key)).Success;
23+
}
24+
catch (CryptographicException)
25+
{
26+
await localStorage.DeleteAsync(key);
27+
return false;
28+
}
29+
}
1730

18-
public async ValueTask<T> GetItemAsync<T>(string key) => (await localStorage.GetAsync<T>(key)).Value
19-
?? throw new KeyNotFoundException($"Key {key} not found");
31+
public async ValueTask<T> GetItemAsync<T>(string key)
32+
{
33+
try
34+
{
35+
var result = await localStorage.GetAsync<T>(key);
36+
if (!result.Success)
37+
{
38+
throw new KeyNotFoundException($"Key {key} not found");
39+
}
40+
return result.Value!;
41+
}
42+
catch (CryptographicException)
43+
{
44+
await localStorage.DeleteAsync(key);
45+
throw new KeyNotFoundException($"Key {key} was invalid and has been removed");
46+
}
47+
}
2048

21-
public async ValueTask SetItemAsync<T>(string key, T value) => await localStorage.SetAsync(key, value!);
49+
public async ValueTask SetItemAsync<T>(string key, T value)
50+
{
51+
try
52+
{
53+
await localStorage.SetAsync(key, value!);
54+
}
55+
catch (CryptographicException)
56+
{
57+
await localStorage.DeleteAsync(key);
58+
throw new InvalidOperationException($"Could not set value for key {key}. The key has been removed.");
59+
}
60+
}
2261
}

src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/Like.razor

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
@inject ILocalStorageService LocalStorage
44

55
<button class="btn @ButtonClass d-inline-flex align-items-center gap-2" @onclick="LikeBlogPost">
6-
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor"
6+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor"
77
class="bi bi-hand-thumbs-up@(HasLiked ? "-fill" : "")" viewBox="0 0 16 16">
88
@if (HasLiked)
99
{
@@ -29,8 +29,8 @@
2929

3030
private bool HasLiked { get; set; }
3131

32-
private string ButtonClass => HasLiked
33-
? "btn-primary"
32+
private string ButtonClass => HasLiked
33+
? "btn-primary"
3434
: "btn-outline-secondary";
3535

3636
protected override void OnParametersSet()
@@ -68,7 +68,7 @@
6868

6969
private async Task<bool> GetHasLiked()
7070
{
71-
if (await LocalStorage.ContainKeyAsync($"hasLiked/{BlogPost.Id}"))
71+
if (await LocalStorage.ContainsKeyAsync($"hasLiked/{BlogPost.Id}"))
7272
{
7373
return await LocalStorage.GetItemAsync<bool>($"hasLiked/{BlogPost.Id}");
7474
}

tests/LinkDotNet.Blog.IntegrationTests/Web/Features/ShowBlogPost/ShowBlogPostPageTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public async Task ShouldSubtractLikeOnEvent()
4848
using var ctx = new BunitContext();
4949
var localStorage = Substitute.For<ILocalStorageService>();
5050
var hasLikedStorage = $"hasLiked/{publishedPost.Id}";
51-
localStorage.ContainKeyAsync(hasLikedStorage).Returns(true);
51+
localStorage.ContainsKeyAsync(hasLikedStorage).Returns(true);
5252
localStorage.GetItemAsync<bool>(hasLikedStorage).Returns(true);
5353
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
5454
RegisterComponents(ctx, localStorage);

tests/LinkDotNet.Blog.UnitTests/Web/Features/Home/Components/ThemeTogglerTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public void ShouldSetSystemDefault()
2424
public void ShouldSetFromLocalStorage()
2525
{
2626
var localStorage = Substitute.For<ILocalStorageService>();
27-
localStorage.ContainKeyAsync("preferred-theme").Returns(true);
27+
localStorage.ContainsKeyAsync("preferred-theme").Returns(true);
2828
localStorage.GetItemAsync<string>("preferred-theme").Returns("dark");
2929
Services.AddScoped(_ => localStorage);
3030
JSInterop.SetupModule("./Features/Home/Components/ThemeToggler.razor.js");

tests/LinkDotNet.Blog.UnitTests/Web/Features/ShowBlogPost/Components/LikeTests.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public async Task ShouldSetLocalStorageVariableOnClick()
6161
public void ShouldCheckLocalStorageOnInit()
6262
{
6363
var localStorage = Substitute.For<ILocalStorageService>();
64-
localStorage.ContainKeyAsync("hasLiked/id").Returns(true);
64+
localStorage.ContainsKeyAsync("hasLiked/id").Returns(true);
6565
localStorage.GetItemAsync<bool>("hasLiked/id").Returns(true);
6666
Services.AddScoped(_ => localStorage);
6767
var blogPost = new BlogPostBuilder().Build();
@@ -87,7 +87,7 @@ public void ShouldCheckStorageOnClickAgainAndDoNothingOnMismatch()
8787
var cut = Render<Like>(
8888
p => p.Add(l => l.BlogPost, blogPost)
8989
.Add(l => l.OnBlogPostLiked, _ => wasClicked = true));
90-
localStorage.ContainKeyAsync("hasLiked/id").Returns(true);
90+
localStorage.ContainsKeyAsync("hasLiked/id").Returns(true);
9191
localStorage.GetItemAsync<bool>("hasLiked/id").Returns(true);
9292

9393
cut.Find("span").Click();

0 commit comments

Comments
 (0)