diff --git a/src/LinkDotNet.Blog.Domain/BlogPost.cs b/src/LinkDotNet.Blog.Domain/BlogPost.cs index 725efb27..0233874e 100644 --- a/src/LinkDotNet.Blog.Domain/BlogPost.cs +++ b/src/LinkDotNet.Blog.Domain/BlogPost.cs @@ -36,6 +36,8 @@ public sealed partial class BlogPost : Entity public int ReadingTimeInMinutes { get; private set; } + public bool IsMembersOnly { get; private set; } + public string Slug => GenerateSlug(); private string GenerateSlug() @@ -89,6 +91,7 @@ public static BlogPost Create( string content, string previewImageUrl, bool isPublished, + bool isMembersOnly, DateTime? updatedDate = null, DateTime? scheduledPublishDate = null, IEnumerable? tags = null, @@ -113,6 +116,7 @@ public static BlogPost Create( IsPublished = isPublished, Tags = tags?.Select(t => t.Trim()).ToImmutableArray() ?? [], ReadingTimeInMinutes = ReadingTimeCalculator.CalculateReadingTime(content), + IsMembersOnly = isMembersOnly, }; return blogPost; @@ -141,6 +145,7 @@ public void Update(BlogPost from) PreviewImageUrl = from.PreviewImageUrl; PreviewImageUrlFallback = from.PreviewImageUrlFallback; IsPublished = from.IsPublished; + IsMembersOnly = from.IsMembersOnly; Tags = from.Tags; ReadingTimeInMinutes = from.ReadingTimeInMinutes; } diff --git a/src/LinkDotNet.Blog.Web/Authentication/OpenIdConnect/AuthExtensions.cs b/src/LinkDotNet.Blog.Web/Authentication/OpenIdConnect/AuthExtensions.cs index 551e8e6a..e05ed96b 100644 --- a/src/LinkDotNet.Blog.Web/Authentication/OpenIdConnect/AuthExtensions.cs +++ b/src/LinkDotNet.Blog.Web/Authentication/OpenIdConnect/AuthExtensions.cs @@ -53,6 +53,12 @@ public static void UseAuthentication(this IServiceCollection services) }; }); + services.AddAuthorization(options => + { + options.AddPolicy("Admin", policy => policy.RequireRole("Admin")); + options.AddPolicy("Member", policy => policy.RequireRole("Member")); + }); + services.AddHttpContextAccessor(); services.AddScoped(); } diff --git a/src/LinkDotNet.Blog.Web/Features/AboutMe/AboutMePage.razor b/src/LinkDotNet.Blog.Web/Features/AboutMe/AboutMePage.razor index a8421f44..0ddaa1b1 100644 --- a/src/LinkDotNet.Blog.Web/Features/AboutMe/AboutMePage.razor +++ b/src/LinkDotNet.Blog.Web/Features/AboutMe/AboutMePage.razor @@ -30,7 +30,8 @@ protected override async Task OnInitializedAsync() { - var userIdentity = (await AuthenticationStateProvider.GetAuthenticationStateAsync()).User.Identity; - isAuthenticated = userIdentity?.IsAuthenticated ?? false; + var principal = (await AuthenticationStateProvider.GetAuthenticationStateAsync()).User; + var userIdentity = principal.Identity; + isAuthenticated = (userIdentity?.IsAuthenticated ?? false) && principal.IsInRole("Admin"); } } diff --git a/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/Components/CreateNewBlogPost.razor b/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/Components/CreateNewBlogPost.razor index 411eb1ef..559b1e01 100644 --- a/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/Components/CreateNewBlogPost.razor +++ b/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/Components/CreateNewBlogPost.razor @@ -31,85 +31,84 @@ @bind-Value="@model.Content"> -
- -
-
- - - The primary image which will be used. - -
-
- - - - Optional: Used as a fallback if the preview image can't be used by the browser. -
For example using a jpg or png as fallback for avif which is not supported in Safari or Edge. -
- -
-
- - - - If set the blog post will be published at the given date. - A blog post with a schedule date can't be set to published. - - -
-
- -
- If this blog post is only draft or it will be scheduled, uncheck the box. - -
-
- - -
- @if (BlogPost is not null && !IsScheduled) - { -
- -
- - If set the publish date is set to now, - otherwise its original date. - -
- } -
- -
- The first page of the blog is cached. Therefore, the blog post is not immediately visible. - Head over to settings to invalidate the cache or enable the checkmark. -
- The option should be enabled if you want to publish the blog post immediately and it should be visible on the first page. -
-
- -
-
-
- + + + +
+ + + The primary image which will be used. + +
+
+ + + Optional: Used as a fallback if the preview image can't be used by the browser. +
For example using a jpg or png as fallback for avif which is not supported in Safari or Edge.
+ +
+
+ + + If set the blog post will be published at the given date. + A blog post with a schedule date can't be set to published. + +
+
+ +
+ If this blog post is only draft or it will be scheduled, uncheck the box. + +
+
+ + +
+
+ +
+ The blog post can only be read by members. +
+ @if (BlogPost is not null && !IsScheduled) + { +
+ +
+ If set the publish date is set to now, + otherwise its original date. +
+ } +
+ +
+ The first page of the blog is cached. Therefore, the blog post is not immediately visible. + Head over to settings to invalidate the cache or enable the checkmark. +
+ The option should be enabled if you want to publish the blog post immediately and it should be visible on the first page. +
+
+ +
+
+
+ diff --git a/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/Components/CreateNewModel.cs b/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/Components/CreateNewModel.cs index 6c7c5153..6e92ca9c 100644 --- a/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/Components/CreateNewModel.cs +++ b/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/Components/CreateNewModel.cs @@ -18,6 +18,7 @@ public sealed class CreateNewModel private string tags = string.Empty; private string previewImageUrlFallback = string.Empty; private DateTime? scheduledPublishDate; + private bool isMembersOnly; [Required] [MaxLength(256)] @@ -64,6 +65,12 @@ public bool ShouldUpdateDate set => SetProperty(out shouldUpdateDate, value); } + public bool IsMembersOnly + { + get => isMembersOnly; + set => SetProperty(out isMembersOnly, value); + } + [FutureDateValidation] public DateTime? ScheduledPublishDate { @@ -128,6 +135,7 @@ public BlogPost ToBlogPost() Content, PreviewImageUrl, IsPublished, + IsMembersOnly, updatedDate, scheduledPublishDate, tagList, diff --git a/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/CreateBlogPost.razor b/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/CreateBlogPost.razor index 26e190b4..6728c76a 100644 --- a/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/CreateBlogPost.razor +++ b/src/LinkDotNet.Blog.Web/Features/Admin/BlogPostEditor/CreateBlogPost.razor @@ -1,5 +1,5 @@ @page "/create" -@attribute [Authorize] +@attribute [Authorize(Roles = "Admin")] @using LinkDotNet.Blog.Domain @using LinkDotNet.Blog.Infrastructure.Persistence @using LinkDotNet.Blog.Web.Features.Admin.BlogPostEditor.Components diff --git a/src/LinkDotNet.Blog.Web/Features/Admin/Sitemap/SitemapPage.razor b/src/LinkDotNet.Blog.Web/Features/Admin/Sitemap/SitemapPage.razor index 29f842ca..9bcf5168 100644 --- a/src/LinkDotNet.Blog.Web/Features/Admin/Sitemap/SitemapPage.razor +++ b/src/LinkDotNet.Blog.Web/Features/Admin/Sitemap/SitemapPage.razor @@ -1,7 +1,7 @@ @page "/Sitemap" @using LinkDotNet.Blog.Web.Features.Admin.Sitemap.Services @inject ISitemapService SitemapService -@attribute [Authorize] +@attribute [Authorize(Roles = "Admin")]

Sitemap

@@ -11,12 +11,12 @@ If you get a 404 there is currently no sitemap.xml

- @if (isGenerating) - { - - } - @if (sitemapUrlSet is not null) - { + @if (isGenerating) + { + + } + @if (sitemapUrlSet is not null) + { diff --git a/src/LinkDotNet.Blog.Web/Features/Components/ShortBlogPost.razor b/src/LinkDotNet.Blog.Web/Features/Components/ShortBlogPost.razor index ed5e67a6..63669467 100644 --- a/src/LinkDotNet.Blog.Web/Features/Components/ShortBlogPost.razor +++ b/src/LinkDotNet.Blog.Web/Features/Components/ShortBlogPost.razor @@ -1,7 +1,7 @@ @using LinkDotNet.Blog.Domain
-
+

@MarkdownConverter.ToMarkupString(BlogPost.ShortDescription)

- Read the whole article + Read the whole article

diff --git a/src/LinkDotNet.Blog.Web/Features/Home/Components/AccessControl.razor b/src/LinkDotNet.Blog.Web/Features/Home/Components/AccessControl.razor index 87760dd2..f6870fdf 100644 --- a/src/LinkDotNet.Blog.Web/Features/Home/Components/AccessControl.razor +++ b/src/LinkDotNet.Blog.Web/Features/Home/Components/AccessControl.razor @@ -1,31 +1,33 @@ - - - - - - - - + + + + + + + + + + @code { diff --git a/src/LinkDotNet.Blog.Web/Features/Home/Index.razor b/src/LinkDotNet.Blog.Web/Features/Home/Index.razor index 7fdcbec2..16bab172 100644 --- a/src/LinkDotNet.Blog.Web/Features/Home/Index.razor +++ b/src/LinkDotNet.Blog.Web/Features/Home/Index.razor @@ -19,7 +19,7 @@ AbsolutePreviewImageUrl="@ImageUrl" Description="@(Markdown.ToPlainText(Introduction.Value.Description))">
- +
diff --git a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/BlogPostAdminActions.razor b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/BlogPostAdminActions.razor index 5b166f21..003f58fc 100644 --- a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/BlogPostAdminActions.razor +++ b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/Components/BlogPostAdminActions.razor @@ -6,7 +6,7 @@ @inject IRepository BlogPostRepository @inject IInstantJobRegistry InstantJobRegistry - +
diff --git a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor index cfddd3eb..149319ac 100644 --- a/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor +++ b/src/LinkDotNet.Blog.Web/Features/ShowBlogPost/ShowBlogPostPage.razor @@ -14,6 +14,7 @@ @inject IOptions AppConfiguration @inject IOptions ProfileInformation @inject IOptions SupportConfiguration +@inject AuthenticationStateProvider AuthenticationStateProvider @if (isLoading) { @@ -40,6 +41,8 @@ else if (BlogPost is not null) @@ -110,6 +169,7 @@ else if (BlogPost is not null) private string OgDataImage => BlogPost!.PreviewImageUrlFallback ?? BlogPost.PreviewImageUrl; private string BlogPostCanoncialUrl => $"blogPost/{BlogPost?.Id}"; private IReadOnlyCollection shortCodes = []; + private bool hasPermission; private BlogPost? BlogPost { get; set; } @@ -122,6 +182,15 @@ else if (BlogPost is not null) { isLoading = true; BlogPost = await BlogPostRepository.GetByIdAsync(BlogPostId); + if (BlogPost?.IsMembersOnly ?? false) + { + var state = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + hasPermission = state.User.IsInRole("Admin") || state.User.IsInRole("Member"); + } + else + { + hasPermission = true; + } isLoading = false; } diff --git a/src/LinkDotNet.Blog.Web/Program.cs b/src/LinkDotNet.Blog.Web/Program.cs index 2c08dc54..ecdf2da5 100644 --- a/src/LinkDotNet.Blog.Web/Program.cs +++ b/src/LinkDotNet.Blog.Web/Program.cs @@ -37,7 +37,7 @@ private static void RegisterServices(WebApplicationBuilder builder) .AddResponseCompression() .AddHealthCheckSetup(); - if (builder.Environment.IsDevelopment()) + if (!builder.Environment.IsDevelopment()) { builder.Services.UseDummyAuthentication(); } diff --git a/tests/LinkDotNet.Blog.IntegrationTests/Infrastructure/Persistence/RavenDb/BlogPostRepositoryTests.cs b/tests/LinkDotNet.Blog.IntegrationTests/Infrastructure/Persistence/RavenDb/BlogPostRepositoryTests.cs index 7e0ddfe4..eaa7a686 100644 --- a/tests/LinkDotNet.Blog.IntegrationTests/Infrastructure/Persistence/RavenDb/BlogPostRepositoryTests.cs +++ b/tests/LinkDotNet.Blog.IntegrationTests/Infrastructure/Persistence/RavenDb/BlogPostRepositoryTests.cs @@ -27,7 +27,7 @@ public BlogPostRepositoryTests() [Fact] public async Task ShouldLoadBlogPost() { - var blogPost = BlogPost.Create("Title", "Subtitle", "Content", "url", true, tags: new[] { "Tag 1", "Tag 2" }); + var blogPost = BlogPost.Create("Title", "Subtitle", "Content", "url", true, true, tags: new[] { "Tag 1", "Tag 2" }); await SaveBlogPostAsync(blogPost); var blogPostFromRepo = await sut.GetByIdAsync(blogPost.Id); @@ -38,6 +38,7 @@ public async Task ShouldLoadBlogPost() blogPostFromRepo.Content.ShouldBe("Content"); blogPostFromRepo.PreviewImageUrl.ShouldBe("url"); blogPostFromRepo.IsPublished.ShouldBeTrue(); + blogPostFromRepo.IsMembersOnly.ShouldBeTrue(); blogPostFromRepo.Tags.Count.ShouldBe(2); var tagContent = blogPostFromRepo.Tags; tagContent.ShouldContain("Tag 1"); @@ -87,7 +88,7 @@ public async Task ShouldSort() [Fact] public async Task ShouldSaveBlogPost() { - var blogPost = BlogPost.Create("Title", "Subtitle", "Content", "url", true, tags: new[] { "Tag 1", "Tag 2" }); + var blogPost = BlogPost.Create("Title", "Subtitle", "Content", "url", true, true, tags: new[] { "Tag 1", "Tag 2" }); await sut.StoreAsync(blogPost); @@ -97,6 +98,7 @@ public async Task ShouldSaveBlogPost() blogPostFromContext.ShortDescription.ShouldBe("Subtitle"); blogPostFromContext.Content.ShouldBe("Content"); blogPostFromContext.IsPublished.ShouldBeTrue(); + blogPostFromContext.IsMembersOnly.ShouldBeTrue(); blogPostFromContext.PreviewImageUrl.ShouldBe("url"); blogPostFromContext.Tags.Count.ShouldBe(2); var tagContent = blogPostFromContext.Tags; @@ -107,7 +109,7 @@ public async Task ShouldSaveBlogPost() [Fact] public async Task ShouldGetAllBlogPosts() { - var blogPost = BlogPost.Create("Title", "Subtitle", "Content", "url", true, tags: new[] { "Tag 1", "Tag 2" }); + var blogPost = BlogPost.Create("Title", "Subtitle", "Content", "url", true, true, tags: new[] { "Tag 1", "Tag 2" }); await SaveBlogPostAsync(blogPost); var blogPostsFromRepo = await sut.GetAllAsync(); @@ -120,6 +122,7 @@ public async Task ShouldGetAllBlogPosts() blogPostFromRepo.Content.ShouldBe("Content"); blogPostFromRepo.PreviewImageUrl.ShouldBe("url"); blogPostFromRepo.IsPublished.ShouldBeTrue(); + blogPostFromRepo.IsMembersOnly.ShouldBeTrue(); blogPostFromRepo.Tags.Count.ShouldBe(2); var tagContent = blogPostFromRepo.Tags; tagContent.ShouldContain("Tag 1"); diff --git a/tests/LinkDotNet.Blog.IntegrationTests/Infrastructure/Persistence/Sql/BlogPostRepositoryTests.cs b/tests/LinkDotNet.Blog.IntegrationTests/Infrastructure/Persistence/Sql/BlogPostRepositoryTests.cs index 9fe8fd06..a3166b0e 100644 --- a/tests/LinkDotNet.Blog.IntegrationTests/Infrastructure/Persistence/Sql/BlogPostRepositoryTests.cs +++ b/tests/LinkDotNet.Blog.IntegrationTests/Infrastructure/Persistence/Sql/BlogPostRepositoryTests.cs @@ -14,7 +14,7 @@ public sealed class BlogPostRepositoryTests : SqlDatabaseTestBase [Fact] public async Task ShouldLoadBlogPost() { - var blogPost = BlogPost.Create("Title", "Subtitle", "Content", "url", true, tags: new[] { "Tag 1", "Tag 2" }); + var blogPost = BlogPost.Create("Title", "Subtitle", "Content", "url", true, true, tags: new[] { "Tag 1", "Tag 2" }); await DbContext.BlogPosts.AddAsync(blogPost, TestContext.Current.CancellationToken); await DbContext.SaveChangesAsync(TestContext.Current.CancellationToken); @@ -26,6 +26,7 @@ public async Task ShouldLoadBlogPost() blogPostFromRepo.Content.ShouldBe("Content"); blogPostFromRepo.PreviewImageUrl.ShouldBe("url"); blogPostFromRepo.IsPublished.ShouldBeTrue(); + blogPostFromRepo.IsMembersOnly.ShouldBeTrue(); blogPostFromRepo.Tags.Count.ShouldBe(2); var tagContent = blogPostFromRepo.Tags; tagContent.ShouldContain("Tag 1"); @@ -35,7 +36,7 @@ public async Task ShouldLoadBlogPost() [Fact] public async Task ShouldSaveBlogPost() { - var blogPost = BlogPost.Create("Title", "Subtitle", "Content", "url", true, tags: new[] { "Tag 1", "Tag 2" }); + var blogPost = BlogPost.Create("Title", "Subtitle", "Content", "url", true, true, tags: new[] { "Tag 1", "Tag 2" }); await Repository.StoreAsync(blogPost); @@ -48,6 +49,7 @@ public async Task ShouldSaveBlogPost() blogPostFromContext.ShortDescription.ShouldBe("Subtitle"); blogPostFromContext.Content.ShouldBe("Content"); blogPostFromContext.IsPublished.ShouldBeTrue(); + blogPostFromContext.IsMembersOnly.ShouldBeTrue(); blogPostFromContext.PreviewImageUrl.ShouldBe("url"); blogPostFromContext.Tags.Count.ShouldBe(2); var tagContent = blogPostFromContext.Tags; @@ -58,7 +60,7 @@ public async Task ShouldSaveBlogPost() [Fact] public async Task ShouldGetAllBlogPosts() { - var blogPost = BlogPost.Create("Title", "Subtitle", "Content", "url", true, tags: new[] { "Tag 1", "Tag 2" }); + var blogPost = BlogPost.Create("Title", "Subtitle", "Content", "url", true, true, tags: new[] { "Tag 1", "Tag 2" }); await DbContext.BlogPosts.AddAsync(blogPost, TestContext.Current.CancellationToken); await DbContext.SaveChangesAsync(TestContext.Current.CancellationToken); @@ -72,6 +74,7 @@ public async Task ShouldGetAllBlogPosts() blogPostFromRepo.Content.ShouldBe("Content"); blogPostFromRepo.PreviewImageUrl.ShouldBe("url"); blogPostFromRepo.IsPublished.ShouldBeTrue(); + blogPostFromRepo.IsMembersOnly.ShouldBeTrue(); blogPostFromRepo.Tags.Count.ShouldBe(2); var tagContent = blogPostFromRepo.Tags; tagContent.ShouldContain("Tag 1"); diff --git a/tests/LinkDotNet.Blog.IntegrationTests/SmokeTests.cs b/tests/LinkDotNet.Blog.IntegrationTests/SmokeTests.cs index dacacd38..d028afd8 100644 --- a/tests/LinkDotNet.Blog.IntegrationTests/SmokeTests.cs +++ b/tests/LinkDotNet.Blog.IntegrationTests/SmokeTests.cs @@ -17,6 +17,7 @@ public SmokeTests(WebApplicationFactory factory) { builder.UseSetting("PersistenceProvider", PersistenceProvider.Sqlite.Key); builder.UseSetting("ConnectionString", "DataSource=file::memory:?cache=shared"); + builder.UseSetting("Authentication:ClientId", "ClientId"); }); } diff --git a/tests/LinkDotNet.Blog.IntegrationTests/Web/Features/Home/IndexTests.cs b/tests/LinkDotNet.Blog.IntegrationTests/Web/Features/Home/IndexTests.cs index b52f3ff3..ff51620e 100644 --- a/tests/LinkDotNet.Blog.IntegrationTests/Web/Features/Home/IndexTests.cs +++ b/tests/LinkDotNet.Blog.IntegrationTests/Web/Features/Home/IndexTests.cs @@ -72,6 +72,7 @@ public async Task ShouldLoadOnlyItemsInPage() { await CreatePublishedBlogPosts(11); using var ctx = new BunitContext(); + ctx.JSInterop.Mode = JSRuntimeMode.Loose; RegisterComponents(ctx); diff --git a/tests/LinkDotNet.Blog.IntegrationTests/Web/Features/ShowBlogPost/ShowBlogPostPageTests.cs b/tests/LinkDotNet.Blog.IntegrationTests/Web/Features/ShowBlogPost/ShowBlogPostPageTests.cs index 245e7009..a7790b6c 100644 --- a/tests/LinkDotNet.Blog.IntegrationTests/Web/Features/ShowBlogPost/ShowBlogPostPageTests.cs +++ b/tests/LinkDotNet.Blog.IntegrationTests/Web/Features/ShowBlogPost/ShowBlogPostPageTests.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; +using AngleSharp.Dom; using Blazored.Toast.Services; using LinkDotNet.Blog.Domain; using LinkDotNet.Blog.Infrastructure; @@ -139,6 +140,38 @@ public async Task ShortCodesShouldBeReplacedByTheirContent() cut.Find(".blogpost-content > p").TextContent.ShouldBe("This is a Content shortcode"); } + [Fact] + public async Task MembersOnlyShouldOnlyBeVisibleForMembers() + { + using var ctx = new BunitContext(); + ctx.JSInterop.Mode = JSRuntimeMode.Loose; + ctx.AddAuthorization().SetAuthorized("s").SetRoles("Member"); + RegisterComponents(ctx); + var blogPost = new BlogPostBuilder().WithContent("Member Content").IsPublished().WithIsMembersOnly().Build(); + await Repository.StoreAsync(blogPost); + + var cut = ctx.Render( + p => p.Add(b => b.BlogPostId, blogPost.Id)); + + cut.Find(".blogpost-content").TextContent.ShouldContain("Member Content"); + } + + [Fact] + public async Task MembersOnlyShouldNotOnlyBeVisibleForNonMembers() + { + using var ctx = new BunitContext(); + ctx.JSInterop.Mode = JSRuntimeMode.Loose; + ctx.AddAuthorization(); + RegisterComponents(ctx); + var blogPost = new BlogPostBuilder().WithContent("Member Content").IsPublished().WithIsMembersOnly().Build(); + await Repository.StoreAsync(blogPost); + + var cut = ctx.Render( + p => p.Add(b => b.BlogPostId, blogPost.Id)); + + cut.FindAll(".blogpost-content").Count.ShouldBe(0); + } + private void RegisterComponents(BunitContext ctx, ILocalStorageService? localStorageService = null) { ctx.Services.AddScoped(_ => Repository); diff --git a/tests/LinkDotNet.Blog.IntegrationTests/Web/Shared/Admin/BlogPostAdminActionsTests.cs b/tests/LinkDotNet.Blog.IntegrationTests/Web/Shared/Admin/BlogPostAdminActionsTests.cs index dc7ed18d..3a26c59f 100644 --- a/tests/LinkDotNet.Blog.IntegrationTests/Web/Shared/Admin/BlogPostAdminActionsTests.cs +++ b/tests/LinkDotNet.Blog.IntegrationTests/Web/Shared/Admin/BlogPostAdminActionsTests.cs @@ -17,7 +17,7 @@ public BlogPostAdminActionsTests() Services.AddSingleton(Substitute.For>()); Services.AddSingleton(Substitute.For()); Services.AddSingleton(Substitute.For()); - AddAuthorization().SetAuthorized("s"); + AddAuthorization().SetAuthorized("s").SetRoles("Admin"); } [Fact] diff --git a/tests/LinkDotNet.Blog.TestUtilities/BlogPostBuilder.cs b/tests/LinkDotNet.Blog.TestUtilities/BlogPostBuilder.cs index 82c4e544..99abb154 100644 --- a/tests/LinkDotNet.Blog.TestUtilities/BlogPostBuilder.cs +++ b/tests/LinkDotNet.Blog.TestUtilities/BlogPostBuilder.cs @@ -15,6 +15,7 @@ public class BlogPostBuilder private int likes; private DateTime? updateDate; private DateTime? scheduledPublishDate; + private bool isMembersOnly; public BlogPostBuilder WithTitle(string title) { @@ -75,6 +76,12 @@ public BlogPostBuilder WithScheduledPublishDate(DateTime scheduledPublishDate) this.scheduledPublishDate = scheduledPublishDate; return this; } + + public BlogPostBuilder WithIsMembersOnly(bool isMembersOnly = true) + { + this.isMembersOnly = isMembersOnly; + return this; + } public BlogPost Build() { @@ -84,6 +91,7 @@ public BlogPost Build() content, previewImageUrl, isPublished, + isMembersOnly, updateDate, scheduledPublishDate, tags, diff --git a/tests/LinkDotNet.Blog.UnitTests/Domain/BlogPostTests.cs b/tests/LinkDotNet.Blog.UnitTests/Domain/BlogPostTests.cs index 0edc6e73..1f7a4fd9 100644 --- a/tests/LinkDotNet.Blog.UnitTests/Domain/BlogPostTests.cs +++ b/tests/LinkDotNet.Blog.UnitTests/Domain/BlogPostTests.cs @@ -12,7 +12,7 @@ public void ShouldUpdateBlogPost() { var blogPostToUpdate = new BlogPostBuilder().Build(); blogPostToUpdate.Id = "random-id"; - var blogPost = BlogPost.Create("Title", "Desc", "Other Content", "Url", true, previewImageUrlFallback: "Url2"); + var blogPost = BlogPost.Create("Title", "Desc", "Other Content", "Url", true, true, previewImageUrlFallback: "Url2"); blogPost.Id = "something else"; blogPostToUpdate.Update(blogPost); @@ -23,6 +23,7 @@ public void ShouldUpdateBlogPost() blogPostToUpdate.PreviewImageUrl.ShouldBe("Url"); blogPostToUpdate.PreviewImageUrlFallback.ShouldBe("Url2"); blogPostToUpdate.IsPublished.ShouldBeTrue(); + blogPostToUpdate.IsMembersOnly.ShouldBeTrue(); blogPostToUpdate.Tags.ShouldBeEmpty(); blogPostToUpdate.Slug.ShouldNotBeNull(); blogPostToUpdate.ReadingTimeInMinutes.ShouldBe(1); @@ -65,7 +66,7 @@ public void ShouldUpdateTagsWhenExisting() [Fact] public void ShouldTrimWhitespacesFromTags() { - var blogPost = BlogPost.Create("Title", "Sub", "Content", "Preview", false, tags: new[] { " Tag 1", " Tag 2 ", }); + var blogPost = BlogPost.Create("Title", "Sub", "Content", "Preview", false, false, tags: new[] { " Tag 1", " Tag 2 ", }); blogPost.Tags.ShouldContain("Tag 1"); blogPost.Tags.ShouldContain("Tag 2"); @@ -76,7 +77,7 @@ public void ShouldSetDateWhenGiven() { var somewhen = new DateTime(1991, 5, 17); - var blog = BlogPost.Create("1", "2", "3", "4", false, somewhen); + var blog = BlogPost.Create("1", "2", "3", "4", false, false, somewhen); blog.UpdatedDate.ShouldBe(somewhen); } @@ -108,7 +109,7 @@ public void ShouldPublishBlogPost() [Fact] public void ShouldThrowErrorWhenCreatingBlogPostThatIsPublishedAndHasScheduledPublishDate() { - Action action = () => BlogPost.Create("1", "2", "3", "4", true, scheduledPublishDate: new DateTime(2023, 3, 24)); + Action action = () => BlogPost.Create("1", "2", "3", "4", true, true, scheduledPublishDate: new DateTime(2023, 3, 24)); action.ShouldThrow(); } @@ -129,7 +130,7 @@ public void GivenScheduledPublishDate_WhenCreating_ThenUpdateDateIsScheduledPubl { var date = new DateTime(2023, 3, 24); - var bp = BlogPost.Create("1", "2", "3", "4", false, scheduledPublishDate: date); + var bp = BlogPost.Create("1", "2", "3", "4", false, false, scheduledPublishDate: date); bp.UpdatedDate.ShouldBe(date); } @@ -139,7 +140,7 @@ public void GivenScheduledPublishDate_WhenCreating_ThenIsScheduledPublishDateIsT { var date = new DateTime(2023, 3, 24); - var bp = BlogPost.Create("1", "2", "3", "4", false, scheduledPublishDate: date); + var bp = BlogPost.Create("1", "2", "3", "4", false, false, scheduledPublishDate: date); bp.IsScheduled.ShouldBeTrue(); } diff --git a/tests/LinkDotNet.Blog.UnitTests/Web/Features/Home/Components/AccessControlTests.cs b/tests/LinkDotNet.Blog.UnitTests/Web/Features/Home/Components/AccessControlTests.cs index 05637ee3..cbb332b9 100644 --- a/tests/LinkDotNet.Blog.UnitTests/Web/Features/Home/Components/AccessControlTests.cs +++ b/tests/LinkDotNet.Blog.UnitTests/Web/Features/Home/Components/AccessControlTests.cs @@ -19,7 +19,7 @@ public void ShouldShowLoginAndHideAdminWhenNotLoggedIn() [Fact] public void ShouldShowLogoutAndAdminWhenLoggedIn() { - AddAuthorization().SetAuthorized("steven"); + AddAuthorization().SetAuthorized("steven").SetRoles("Admin"); var cut = Render(); @@ -43,11 +43,23 @@ public void LoginShouldHaveCurrentUriAsRedirectUri() public void LogoutShouldHaveCurrentUriAsRedirectUri() { const string currentUri = "http://localhost/test"; - AddAuthorization().SetAuthorized("steven"); + AddAuthorization().SetAuthorized("steven").SetRoles("Admin"); var cut = Render( p => p.Add(s => s.CurrentUri, currentUri)); ((IHtmlAnchorElement)cut.Find("a:contains('Log out')")).Href.ShouldContain(currentUri); } + + [Fact] + public void MembersDontHaveAdminUi() + { + const string currentUri = "http://localhost/test"; + AddAuthorization().SetAuthorized("steven").SetRoles("Member"); + + var cut = Render( + p => p.Add(s => s.CurrentUri, currentUri)); + + cut.FindAll("#admin-actions").ShouldBeEmpty(); + } } \ No newline at end of file