Implemented Permissions on ArticleEditor
This commit is contained in:
		
							parent
							
								
									424cb19b54
								
							
						
					
					
						commit
						4a6599d165
					
				|  | @ -8,7 +8,7 @@ | ||||||
| @using Microsoft.AspNetCore.Identity | @using Microsoft.AspNetCore.Identity | ||||||
| 
 | 
 | ||||||
| @attribute [Authorize(Policy = "ArticleEditPermissions")] | @attribute [Authorize(Policy = "ArticleEditPermissions")] | ||||||
| @inject IDbContextFactory<ApplicationDbContext> ContextFactory; | @inject IDbContextFactory<ApplicationDbContext> ContextFactory | ||||||
| @inject NavigationManager Navigation | @inject NavigationManager Navigation | ||||||
| @inject UserManager<ApplicationUser> UserManager | @inject UserManager<ApplicationUser> UserManager | ||||||
| @inject IStringLocalizer<ArticleEditor> Localizer | @inject IStringLocalizer<ArticleEditor> Localizer | ||||||
|  | @ -29,7 +29,7 @@ | ||||||
|         <div class="label"> |         <div class="label"> | ||||||
|             <span class="label-text">@Localizer["Title_Label"]</span> |             <span class="label-text">@Localizer["Title_Label"]</span> | ||||||
|         </div> |         </div> | ||||||
|         <InputText class="input input-bordered w-full" maxlength="256" aria-required |         <InputText class="input input-bordered w-full" maxlength="256" aria-required disabled="@CannotEdit" | ||||||
|                    @bind-Value="@Model.Title" placeholder="@Localizer["Title_Placeholder"]" /> |                    @bind-Value="@Model.Title" placeholder="@Localizer["Title_Placeholder"]" /> | ||||||
|         <div class="label"> |         <div class="label"> | ||||||
|             <span class="label-text-alt text-error"> |             <span class="label-text-alt text-error"> | ||||||
|  | @ -41,7 +41,7 @@ | ||||||
|         <div class="label"> |         <div class="label"> | ||||||
|             <span class="label-text">@Localizer["PublishDate_Label"]</span> |             <span class="label-text">@Localizer["PublishDate_Label"]</span> | ||||||
|         </div> |         </div> | ||||||
|         <InputDate class="input input-bordered w-full" aria-required |         <InputDate class="input input-bordered w-full" disabled="@CannotEdit" | ||||||
|                    @bind-Value="@Model.PublishDate" placeholder="@Localizer["PublishDate_Placeholder"]" /> |                    @bind-Value="@Model.PublishDate" placeholder="@Localizer["PublishDate_Placeholder"]" /> | ||||||
|         <div class="label"> |         <div class="label"> | ||||||
|             <span class="label-text-alt text-error"> |             <span class="label-text-alt text-error"> | ||||||
|  | @ -53,7 +53,7 @@ | ||||||
|         <div class="label"> |         <div class="label"> | ||||||
|             <span class="label-text">@Localizer["Body_Label"]</span> |             <span class="label-text">@Localizer["Body_Label"]</span> | ||||||
|         </div> |         </div> | ||||||
|         <InputTextArea class="textarea textarea-bordered w-full" rows="10" aria-required |         <InputTextArea class="textarea textarea-bordered w-full" rows="10" aria-required disabled="@CannotEdit" | ||||||
|                        @bind-Value="@Model.Body" placeholder="@Localizer["Body_Placeholder"]" /> |                        @bind-Value="@Model.Body" placeholder="@Localizer["Body_Placeholder"]" /> | ||||||
|         <div class="label"> |         <div class="label"> | ||||||
|             <span class="label-text-alt text-error"> |             <span class="label-text-alt text-error"> | ||||||
|  | @ -62,7 +62,9 @@ | ||||||
|         </div> |         </div> | ||||||
|     </label> |     </label> | ||||||
|      |      | ||||||
|     <button type="submit" class="btn btn-primary btn-wide">@Localizer["EditorSubmit"]</button> |     <button type="submit" class="btn btn-primary btn-wide" disabled="@CannotEdit"> | ||||||
|  | 	    @Localizer["EditorSubmit"] | ||||||
|  |     </button> | ||||||
| </EditForm> | </EditForm> | ||||||
| 
 | 
 | ||||||
| @if (Article is not null) { | @if (Article is not null) { | ||||||
|  | @ -88,8 +90,11 @@ | ||||||
|     [CascadingParameter] |     [CascadingParameter] | ||||||
|     private Task<AuthenticationState>? AuthenticationState { get; set; } |     private Task<AuthenticationState>? AuthenticationState { get; set; } | ||||||
| 
 | 
 | ||||||
|  |     private ApplicationUser User { get; set; } = null!; | ||||||
|  |     private bool IsAdmin { get; set; } | ||||||
|     private Article? Article { get; set; }  |     private Article? Article { get; set; }  | ||||||
|     private MarkupString? Content => Article is null ? null : new MarkupString(Article.BodyHtml); |     private MarkupString? Content => Article is null ? null : new MarkupString(Article.BodyHtml); | ||||||
|  |     private bool CannotEdit => User is null || !IsAdmin && Article is not null && Article.Author.Id != User.Id; | ||||||
| 
 | 
 | ||||||
|     protected override async Task OnInitializedAsync() { |     protected override async Task OnInitializedAsync() { | ||||||
|         if (Id is not null) { |         if (Id is not null) { | ||||||
|  | @ -99,7 +104,6 @@ | ||||||
|             // ReSharper disable once MethodHasAsyncOverload |             // ReSharper disable once MethodHasAsyncOverload | ||||||
|             await using var context = ContextFactory.CreateDbContext(); |             await using var context = ContextFactory.CreateDbContext(); | ||||||
|             // ReSharper disable once MethodHasAsyncOverload |             // ReSharper disable once MethodHasAsyncOverload | ||||||
|             var now = DateTimeOffset.UtcNow; |  | ||||||
|             Article = context.Set<Article>() |             Article = context.Set<Article>() | ||||||
|                 .Include(a => a.Author) |                 .Include(a => a.Author) | ||||||
|                 .Include(a => a.Reviewer) |                 .Include(a => a.Reviewer) | ||||||
|  | @ -114,16 +118,17 @@ | ||||||
|         Model.Title ??= Article?.Title; |         Model.Title ??= Article?.Title; | ||||||
|         Model.Body ??= Article?.Body; |         Model.Body ??= Article?.Body; | ||||||
|         Model.PublishDate ??= Article?.PublishDate; |         Model.PublishDate ??= Article?.PublishDate; | ||||||
|  |          | ||||||
|  |         if (AuthenticationState is null) throw new ApplicationException("???"); | ||||||
|  |         var state = await AuthenticationState; | ||||||
|  |         var user = await UserManager.GetUserAsync(state.User); | ||||||
|  |         User = user ?? throw new ApplicationException("???2"); | ||||||
|  |         IsAdmin = await UserManager.IsInRoleAsync(User, "Admin"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private async Task OnValidSubmit() { |     private async Task OnValidSubmit() { | ||||||
|         await using var context = await ContextFactory.CreateDbContextAsync(); |         await using var context = await ContextFactory.CreateDbContextAsync(); | ||||||
|         if (AuthenticationState is null) throw new ApplicationException("???"); |         context.Entry(User).State = EntityState.Unchanged; | ||||||
| 
 |  | ||||||
|         var state = await AuthenticationState; |  | ||||||
|         var user = await UserManager.GetUserAsync(state.User); |  | ||||||
|         if (user is null) throw new ApplicationException("???2"); |  | ||||||
|         context.Entry(user).State = EntityState.Unchanged; |  | ||||||
| 
 | 
 | ||||||
|         Article article; |         Article article; | ||||||
|         if (Model.Id is not null) { |         if (Model.Id is not null) { | ||||||
|  | @ -137,16 +142,18 @@ | ||||||
|             article = new Article { |             article = new Article { | ||||||
|                 Title = Model.Title!, |                 Title = Model.Title!, | ||||||
|                 Body = Model.Body!, |                 Body = Model.Body!, | ||||||
|                 Author = user, |                 Author = User, | ||||||
|                 Status = ArticleStatus.Published // TODO remove |                 Status = ArticleStatus.Published // TODO remove | ||||||
|             }; |             }; | ||||||
|             await context.AddAsync(article); |             await context.AddAsync(article); | ||||||
|         } |         } | ||||||
|         if (Model.PublishDate is not null) article.PublishDate = Model.PublishDate.Value; |         if (Model.PublishDate is not null) article.PublishDate = Model.PublishDate.Value; | ||||||
| 
 | 
 | ||||||
|         if (user.Id != article.Author.Id)  |         if (User.Id != article.Author.Id && !IsAdmin)  | ||||||
|             throw new ApplicationException("You do not have permissions to edit this article"); |             throw new ApplicationException("You do not have permissions to edit this article"); | ||||||
| 
 |         if (User.Id != article.Author.Id) { | ||||||
|  | 	        article.Reviewer = User; // If an admin edits this article, add them as reviewer | ||||||
|  |         } | ||||||
|         article.LastModified = DateTimeOffset.UtcNow; |         article.LastModified = DateTimeOffset.UtcNow; | ||||||
| 
 | 
 | ||||||
|         var pipeline = new MarkdownPipelineBuilder() |         var pipeline = new MarkdownPipelineBuilder() | ||||||
|  |  | ||||||
|  | @ -25,6 +25,23 @@ Welcome to your new app. | ||||||
| 	} | 	} | ||||||
| </div> | </div> | ||||||
| 
 | 
 | ||||||
|  | <AuthorizeView> | ||||||
|  | 	<Authorized> | ||||||
|  | 		<h3 class="text-2xl my-3">Claims</h3> | ||||||
|  | 		<dl> | ||||||
|  | 			<dt>Author?</dt> | ||||||
|  | 			<dd>@context.User.IsInRole("Author")</dd> | ||||||
|  | 			<dt>Reviewer?</dt> | ||||||
|  | 			<dd>@context.User.IsInRole("Reviewer")</dd> | ||||||
|  | 			<dt>Moderator?</dt> | ||||||
|  | 			<dd>@context.User.IsInRole("Moderator")</dd> | ||||||
|  | 			<dt>Admin?</dt> | ||||||
|  | 			<dd>@context.User.IsInRole("Admin")</dd> | ||||||
|  | 		</dl> | ||||||
|  | 		 | ||||||
|  | 	</Authorized> | ||||||
|  | </AuthorizeView> | ||||||
|  | 
 | ||||||
| @code { | @code { | ||||||
|     private List<Article> Articles { get; } = []; |     private List<Article> Articles { get; } = []; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,5 +1,4 @@ | ||||||
| using Microsoft.AspNetCore.Components.Authorization; | using Microsoft.AspNetCore.Components.Authorization; | ||||||
| using Microsoft.AspNetCore.Components.Forms; |  | ||||||
| using Microsoft.AspNetCore.Components.Server; | using Microsoft.AspNetCore.Components.Server; | ||||||
| using Microsoft.AspNetCore.Identity; | using Microsoft.AspNetCore.Identity; | ||||||
| using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||||
|  | @ -46,6 +45,7 @@ | ||||||
| builder.Services.AddDatabaseDeveloperPageExceptionFilter(); | builder.Services.AddDatabaseDeveloperPageExceptionFilter(); | ||||||
| 
 | 
 | ||||||
| builder.Services.AddIdentityCore<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true) | builder.Services.AddIdentityCore<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true) | ||||||
|  |     .AddRoles<IdentityRole>() | ||||||
|     .AddEntityFrameworkStores<ApplicationDbContext>() |     .AddEntityFrameworkStores<ApplicationDbContext>() | ||||||
|     .AddSignInManager() |     .AddSignInManager() | ||||||
|     .AddDefaultTokenProviders(); |     .AddDefaultTokenProviders(); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue