Skip to content

Commit

Permalink
feat: Add account authorizer
Browse files Browse the repository at this point in the history
  • Loading branch information
data-miner00 committed Jan 17, 2024
1 parent 364c1de commit 3694ba1
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 3 deletions.
49 changes: 46 additions & 3 deletions src/Linker.WebApi/Controllers/ArticleController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
using Linker.Core.Controllers;
using Linker.Core.Models;
using Linker.Core.Repositories;
using Linker.WebApi.Filters;
using Linker.WebApi.Swagger;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using Swashbuckle.AspNetCore.Filters;
Expand Down Expand Up @@ -102,6 +104,7 @@ public async Task<IActionResult> GetAllAsync()
}

/// <inheritdoc/>
[AccountAuthorize]
[HttpGet("byuser/{userId:guid}", Name = "GetAllArticlesByUser")]
[SwaggerResponse((int)HttpStatusCode.OK, "Retrieved all articles by user.")]
[SwaggerResponseExample((int)HttpStatusCode.OK, typeof(ArticleResponseCollectionExample))]
Expand All @@ -116,7 +119,7 @@ public async Task<IActionResult> GetAllByUserAsync(Guid userId)
}

/// <inheritdoc/>
[HttpGet("{id:guid}", Name = "GetArticleByUser")]
[HttpGet("{id:guid}", Name = "GetArticle")]
[SwaggerResponse((int)HttpStatusCode.NotFound, "Article not found.")]
[SwaggerResponse((int)HttpStatusCode.OK, "Retrieved article by user.")]
[SwaggerResponseExample((int)HttpStatusCode.OK, typeof(ArticleResponseExample))]
Expand All @@ -138,7 +141,8 @@ public async Task<IActionResult> GetByIdAsync(Guid id)
}

/// <inheritdoc/>
[HttpGet("byuser/{userId:guid}/{linkId:guid}", Name = "GetArticle")]
[AccountAuthorize]
[HttpGet("byuser/{userId:guid}/{linkId:guid}", Name = "GetArticleByUser")]
[SwaggerResponse((int)HttpStatusCode.NotFound, "Article not found.")]
[SwaggerResponse((int)HttpStatusCode.OK, "Retrieved article.")]
[SwaggerResponseExample((int)HttpStatusCode.OK, typeof(ArticleResponseExample))]
Expand All @@ -160,6 +164,7 @@ public async Task<IActionResult> GetByUserAsync(Guid userId, Guid linkId)
}

/// <inheritdoc/>
[Authorize]
[HttpPut("{id:guid}", Name = "UpdateArticle")]
[SwaggerResponse((int)HttpStatusCode.NoContent, "Article updated.")]
[SwaggerResponse((int)HttpStatusCode.NotFound, "Article not found.")]
Expand All @@ -178,7 +183,7 @@ public async Task<IActionResult> UpdateAsync([FromRoute] Guid id, [FromBody] Upd

if (userId != existing.CreatedBy)
{
return this.Forbid();
return this.Unauthorized();
}

var article = this.mapper.Map<Article>(request);
Expand All @@ -195,5 +200,43 @@ await this.repository
return this.NotFound();
}
}

[AccountAuthorize]
[HttpPut("/byuser/{userId:guid}/{linkId:guid}", Name = "UpdateArticleByUser")]
[SwaggerResponse((int)HttpStatusCode.NoContent, "Article updated.")]
[SwaggerResponse((int)HttpStatusCode.NotFound, "Article not found.")]
public async Task<IActionResult> UpdateByUserAsync(
[FromRoute] Guid userId,
[FromRoute] Guid linkId,
[FromBody] UpdateArticleRequest request)
{
EnsureArg.IsNotNull(request, nameof(request));

try
{
var strUserId = userId.ToString();
var existing = await this.repository
.GetByIdAsync(strUserId, CancellationToken.None)
.ConfigureAwait(false);

if (strUserId != existing.CreatedBy)
{
return this.Unauthorized();
}

var article = this.mapper.Map<Article>(request);
article.Id = linkId.ToString();

await this.repository
.UpdateAsync(article, CancellationToken.None)
.ConfigureAwait(false);

return this.NoContent();
}
catch (InvalidOperationException)
{
return this.NotFound();
}
}
}
}
32 changes: 32 additions & 0 deletions src/Linker.WebApi/Filters/AccountAuthorizeAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace Linker.WebApi.Filters;

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Security.Claims;

public sealed class AccountAuthorizeAttribute : Attribute, IAuthorizationFilter
{
/// <inheritdoc/>
public void OnAuthorization(AuthorizationFilterContext context)
{
var userId = context.HttpContext.User.Claims
.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value;

var routeParameterDictionary = context.HttpContext.GetRouteData();
var routeUserId = routeParameterDictionary.Values["userId"]
?? throw new InvalidOperationException("The route has no accountId.");

if (userId is null)
{
goto ending;
}

if (routeUserId is string targetUserId && userId.Equals(targetUserId, StringComparison.OrdinalIgnoreCase))
{
return;
}

ending:
context.Result = new UnauthorizedResult();
}
}

0 comments on commit 3694ba1

Please sign in to comment.