From 916c1ff1b3d057615c7cbe4619b15b12e442af1d Mon Sep 17 00:00:00 2001 From: Maksym Sadovnychyy Date: Fri, 28 Oct 2022 22:22:24 +0200 Subject: [PATCH] (feat): custom authorization --- db/DML/users.json | 67 +++++++- webapi/Core/DomainObjects/Documents/User.cs | 3 + .../Core/DomainObjects/UserAuthorizations.cs | 14 ++ webapi/Core/Enumerations/CrudActions.cs | 18 ++ webapi/Core/Enumerations/WebapiControllers.cs | 33 ++++ webapi/DataProviders/Mappings.cs | 17 +- webapi/Extensions/HttpRequestExtensions.cs | 12 -- .../Controllers/AccountController.cs | 25 +++ .../Controllers/AuthenticationController.cs | 34 ++-- .../Controllers/BlogItemController.cs | 54 ++++-- .../Controllers/BlogItemsController.cs | 23 ++- .../Controllers/CategoryItemController.cs | 57 +++++-- .../Controllers/CategoryItemsController.cs | 23 ++- .../Controllers/ContentController.cs | 9 +- .../Controllers/FileController.cs | 53 ++++-- .../Controllers/FilesController.cs | 34 ++-- .../Controllers/ImageController.cs | 4 +- .../Controllers/PasswordController.cs | 20 ++- .../Controllers/ShopCartItemController.cs | 48 ++++-- .../Controllers/ShopCartItemsController.cs | 30 +++- .../Controllers/ShopItemController.cs | 50 ++++-- .../Controllers/ShopItemsController.cs | 18 +- .../Controllers/UserController.cs | 18 -- .../Controllers/WeatherForecastController.cs | 1 - .../Policies/CrudAuthorizationHandler.cs | 83 +++++++++ .../Policies/WhitelistToken.cs | 55 ------ .../Services/AutheticationService.cs | 157 ----------------- .../WeatherForecast/Services/UserService.cs | 160 +++++++++++++++++- webapi/WeatherForecast/Startup.cs | 27 ++- 29 files changed, 750 insertions(+), 397 deletions(-) create mode 100644 webapi/Core/DomainObjects/UserAuthorizations.cs create mode 100644 webapi/Core/Enumerations/CrudActions.cs create mode 100644 webapi/Core/Enumerations/WebapiControllers.cs create mode 100644 webapi/WeatherForecast/Controllers/AccountController.cs delete mode 100644 webapi/WeatherForecast/Controllers/UserController.cs create mode 100644 webapi/WeatherForecast/Policies/CrudAuthorizationHandler.cs delete mode 100644 webapi/WeatherForecast/Policies/WhitelistToken.cs delete mode 100644 webapi/WeatherForecast/Services/AutheticationService.cs diff --git a/db/DML/users.json b/db/DML/users.json index 9defac9..95f120b 100644 --- a/db/DML/users.json +++ b/db/DML/users.json @@ -16,7 +16,7 @@ "$date": "2022-01-01T00:00:00.000Z" } }, - + "expiration": { "enabled": false, "days": "180" @@ -45,7 +45,7 @@ "confirmed": false } ], - "tokens": [], + "billingAddress": { "street": "", "city": "", @@ -57,6 +57,67 @@ "city": "", "postCode": "", "country": "" - } + }, + + "tokens": [], + + "authorizations": [ + { + "controller": 0, + "actions": [ 0, 1, 2, 3 ] + }, + { + "controller": 1, + "actions": [ 0, 1, 2, 3 ] + }, + { + "controller": 2, + "actions": [ 0, 1, 2, 3 ] + }, + { + "controller": 3, + "actions": [ 0, 1, 2, 3 ] + }, + { + "controller": 4, + "actions": [ 0, 1, 2, 3 ] + }, + { + "controller": 5, + "actions": [ 0, 1, 2, 3 ] + }, + { + "controller": 6, + "actions": [ 0, 1, 2, 3 ] + }, + { + "controller": 7, + "actions": [ 0, 1, 2, 3 ] + }, + { + "controller": 8, + "actions": [ 0, 1, 2, 3 ] + }, + { + "controller": 9, + "actions": [ 0, 1, 2, 3 ] + }, + { + "controller": 10, + "actions": [ 0, 1, 2, 3 ] + }, + { + "controller": 11, + "actions": [ 0, 1, 2, 3 ] + }, + { + "controller": 12, + "actions": [ 0, 1, 2, 3 ] + }, + { + "controller": 13, + "actions": [ 0, 1, 2, 3 ] + } + ] } ] diff --git a/webapi/Core/DomainObjects/Documents/User.cs b/webapi/Core/DomainObjects/Documents/User.cs index c3732bc..b281bdb 100644 --- a/webapi/Core/DomainObjects/Documents/User.cs +++ b/webapi/Core/DomainObjects/Documents/User.cs @@ -13,6 +13,7 @@ namespace Core.DomainObjects { public Passwords Passwords { get; set; } public string Name { get; set; } + public string LastName { get; set; } public List Contacts { get; set; } @@ -23,6 +24,8 @@ namespace Core.DomainObjects { public List Tokens { get; set; } + public List Authorizations { get; set; } + public override int GetHashCode() { throw new NotImplementedException(); } diff --git a/webapi/Core/DomainObjects/UserAuthorizations.cs b/webapi/Core/DomainObjects/UserAuthorizations.cs new file mode 100644 index 0000000..177bb8c --- /dev/null +++ b/webapi/Core/DomainObjects/UserAuthorizations.cs @@ -0,0 +1,14 @@ +using Core.Enumerations; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.DomainObjects { + + public class UserAuthorizations { + public WebapiControllers Controller { get; set; } + public List Actions { get; set; } + } +} diff --git a/webapi/Core/Enumerations/CrudActions.cs b/webapi/Core/Enumerations/CrudActions.cs new file mode 100644 index 0000000..1b07d9a --- /dev/null +++ b/webapi/Core/Enumerations/CrudActions.cs @@ -0,0 +1,18 @@ +using Core.Abstractions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.Enumerations { + public class CrudActions : Enumeration { + + public static CrudActions Create = new (0, "Create"); + public static CrudActions Read = new (1, "Read"); + public static CrudActions Update = new (2, "Update"); + public static CrudActions Delete = new (3, "Delete"); + + private CrudActions(int id, string displayName) : base(id, displayName) { } + } +} diff --git a/webapi/Core/Enumerations/WebapiControllers.cs b/webapi/Core/Enumerations/WebapiControllers.cs new file mode 100644 index 0000000..2cb0d48 --- /dev/null +++ b/webapi/Core/Enumerations/WebapiControllers.cs @@ -0,0 +1,33 @@ +using Core.Abstractions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.Enumerations { + public class WebapiControllers : Enumeration { + + public static WebapiControllers Authentication = new (0, "Authentication"); + public static WebapiControllers BlogItem = new(1, "BlogItem"); + public static WebapiControllers BlogItems = new(2, "BlogItems"); + public static WebapiControllers CategoryItem = new(3, "CategoryItem"); + public static WebapiControllers CategoryItems = new(4, "CategoryItems"); + public static WebapiControllers File = new(5, "File"); + public static WebapiControllers Files = new(6, "Files"); + public static WebapiControllers Image = new(7, "Image"); + public static WebapiControllers Password = new(8, "Password"); + public static WebapiControllers ShopCartItem = new(9, "ShopCartItem"); + public static WebapiControllers ShopCartItems = new(10, "ShopCartItems"); + public static WebapiControllers ShopItem = new(11, "ShopItem"); + public static WebapiControllers ShopItems = new(12, "ShopItems"); + public static WebapiControllers Account = new(13, "Account"); + public static WebapiControllers Content = new(14, "Content"); + + private WebapiControllers(int id, string displayName) : base(id, displayName) { } + + + + + } +} diff --git a/webapi/DataProviders/Mappings.cs b/webapi/DataProviders/Mappings.cs index 4eb7da9..d033e73 100644 --- a/webapi/DataProviders/Mappings.cs +++ b/webapi/DataProviders/Mappings.cs @@ -161,6 +161,18 @@ namespace DataProviders { cm.AutoMap(); }); } + + if (!BsonClassMap.IsClassMapRegistered(typeof(UserAuthorizations))) { + BsonClassMap.RegisterClassMap(cm => { + cm.AutoMap(); + + cm.GetMemberMap(c => c.Controller) + .SetSerializer(new EnumerationSerializer()); + + cm.GetMemberMap(c => c.Actions) + .SetSerializer(new EnumerationListSerializer()); + }); + } #endregion @@ -347,11 +359,6 @@ namespace DataProviders { } #endregion - - - - - #region User if (!BsonClassMap.IsClassMapRegistered(typeof(User))) { BsonClassMap.RegisterClassMap(cm => { diff --git a/webapi/Extensions/HttpRequestExtensions.cs b/webapi/Extensions/HttpRequestExtensions.cs index 43d8d26..0feb33b 100644 --- a/webapi/Extensions/HttpRequestExtensions.cs +++ b/webapi/Extensions/HttpRequestExtensions.cs @@ -20,8 +20,6 @@ namespace Extensions { return headers != null ? headers : new List(); } - - /// /// Return clean JWT Bearer token from Authorisation Header /// @@ -31,15 +29,5 @@ namespace Extensions { ? header.Replace("Bearer ", "") : default; } - - /// - /// Returns JWT Bearer token Vault path from custom AuthorizationPath Header - /// - /// - /// - public static string? GetBearerPath(this HttpRequest request) { - var header = request.GetHeader("AuthorizationPath").FirstOrDefault(); - return header; - } } } diff --git a/webapi/WeatherForecast/Controllers/AccountController.cs b/webapi/WeatherForecast/Controllers/AccountController.cs new file mode 100644 index 0000000..441b4f0 --- /dev/null +++ b/webapi/WeatherForecast/Controllers/AccountController.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace WeatherForecast.Controllers; + +/// +/// +/// +[ApiController] +[Route("api/[controller]")] +public class AccountController : ControllerBase { + + private readonly IAuthorizationService _authorizationService; + + /// + /// + /// + public AccountController( + IAuthorizationService authorizationService + + + ) { + _authorizationService = authorizationService; + } +} diff --git a/webapi/WeatherForecast/Controllers/AuthenticationController.cs b/webapi/WeatherForecast/Controllers/AuthenticationController.cs index 83e1cf3..fed22e8 100644 --- a/webapi/WeatherForecast/Controllers/AuthenticationController.cs +++ b/webapi/WeatherForecast/Controllers/AuthenticationController.cs @@ -1,9 +1,10 @@ - +using Core.Enumerations; using DomainResults.Mvc; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using WeatherForecast.Models.Requests; +using WeatherForecast.Policies; using WeatherForecast.Services; namespace WeatherForecast.Controllers; @@ -11,21 +12,26 @@ namespace WeatherForecast.Controllers; /// /// /// - [ApiController] [Route("api/[controller]")] public class AuthenticationController : ControllerBase { - private readonly IAuthenticationService _authenticationService; + private readonly IAuthorizationService _authorizationService; + private readonly IUserService _userService; + + private readonly WebapiControllers _webapiController = WebapiControllers.Authentication; /// /// /// /// public AuthenticationController( - IAuthenticationService authenticationService + IAuthorizationService authorizationService, + IUserService authenticationService + ) { - _authenticationService = authenticationService; + _authorizationService = authorizationService; + _userService = authenticationService; } /// @@ -33,10 +39,9 @@ public class AuthenticationController : ControllerBase { /// /// /// - [AllowAnonymous] - [HttpPost()] + [HttpPost] public IActionResult Post([FromBody] AuthenticationRequestModel requestData) { - var result = _authenticationService.Post(requestData); + var result = _userService.CreateToken(requestData); return result.ToActionResult(); } @@ -44,11 +49,16 @@ public class AuthenticationController : ControllerBase { /// /// /// - [Authorize(Policy = "WhitelistToken")] - [HttpGet()] + [HttpGet] + public async Task Get() { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Read))).Succeeded) { + + return Ok(); + } + else { + return Unauthorized(); + } - public IActionResult Get() { - return Ok(); } } diff --git a/webapi/WeatherForecast/Controllers/BlogItemController.cs b/webapi/WeatherForecast/Controllers/BlogItemController.cs index 8d22e11..39e00a9 100644 --- a/webapi/WeatherForecast/Controllers/BlogItemController.cs +++ b/webapi/WeatherForecast/Controllers/BlogItemController.cs @@ -5,26 +5,33 @@ using DomainResults.Mvc; using WeatherForecast.Services; using WeatherForecast.Models.Requests; +using WeatherForecast.Policies; +using Core.Enumerations; namespace WeatherForecast.Controllers; /// /// /// -[Authorize(Policy = "WhitelistToken")] [ApiController] [Route("api/[controller]")] public class BlogItemController : ControllerBase { + private readonly IAuthorizationService _authorizationService; private readonly IBlogItemService _blogItemService; + private readonly WebapiControllers _webapiController = WebapiControllers.BlogItem; + /// /// /// + /// /// public BlogItemController( + IAuthorizationService authorizationService, IBlogItemService blogItemService ) { + _authorizationService = authorizationService; _blogItemService = blogItemService; } @@ -35,9 +42,14 @@ public class BlogItemController : ControllerBase { /// /// [HttpPost("{siteId}")] - public IActionResult Post([FromRoute] Guid siteId, [FromBody] BlogItemRequestModel requestData) { - var result = _blogItemService.Post(siteId, requestData); - return result.ToActionResult(); + public async Task Post([FromRoute] Guid siteId, [FromBody] BlogItemRequestModel requestData) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Create))).Succeeded) { + var result = _blogItemService.Post(siteId, requestData); + return result.ToActionResult(); + } + else { + return Unauthorized(); + } } /// @@ -45,9 +57,14 @@ public class BlogItemController : ControllerBase { /// /// [HttpGet("{siteId}/{blogId}")] - public IActionResult Get([FromRoute] Guid siteId, [FromRoute] Guid blogId) { - var result = _blogItemService.Get(siteId, blogId); - return result.ToActionResult(); + public async Task Get([FromRoute] Guid siteId, [FromRoute] Guid blogId) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Read))).Succeeded) { + var result = _blogItemService.Get(siteId, blogId); + return result.ToActionResult(); + } + else { + return Unauthorized(); + } } /// @@ -56,7 +73,6 @@ public class BlogItemController : ControllerBase { /// /// /// - [AllowAnonymous] [HttpGet("{siteId}")] public IActionResult GetSlug([FromRoute] Guid siteId, [FromQuery] string slug) { var result = _blogItemService.GetSlug(siteId, slug); @@ -71,9 +87,14 @@ public class BlogItemController : ControllerBase { /// /// [HttpPut("{siteId}/{blogId}")] - public IActionResult Update([FromRoute] Guid siteId, [FromRoute] Guid blogId, [FromBody] BlogItemRequestModel requestData) { - var result = _blogItemService.Update(siteId, blogId, requestData); - return result.ToActionResult(); + public async Task Update([FromRoute] Guid siteId, [FromRoute] Guid blogId, [FromBody] BlogItemRequestModel requestData) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Update))).Succeeded) { + var result = _blogItemService.Update(siteId, blogId, requestData); + return result.ToActionResult(); + } + else { + return Unauthorized(); + } } /// @@ -83,9 +104,14 @@ public class BlogItemController : ControllerBase { /// /// [HttpDelete("{siteId}/{blogId}")] - public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] Guid blogId) { - var result = _blogItemService.Delete(siteId, blogId); - return result.ToActionResult(); + public async Task Delete([FromRoute] Guid siteId, [FromRoute] Guid blogId) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Update))).Succeeded) { + var result = _blogItemService.Delete(siteId, blogId); + return result.ToActionResult(); + } + else { + return Unauthorized(); + } } } diff --git a/webapi/WeatherForecast/Controllers/BlogItemsController.cs b/webapi/WeatherForecast/Controllers/BlogItemsController.cs index c883d1c..0393e90 100644 --- a/webapi/WeatherForecast/Controllers/BlogItemsController.cs +++ b/webapi/WeatherForecast/Controllers/BlogItemsController.cs @@ -4,26 +4,33 @@ using Microsoft.AspNetCore.Authorization; using DomainResults.Mvc; using WeatherForecast.Services; +using WeatherForecast.Policies; +using Core.Enumerations; namespace WeatherForecast.Controllers; /// /// /// -[Authorize(Policy = "WhitelistToken")] [ApiController] [Route("api/[controller]")] public class BlogItemsController : ControllerBase { + private readonly IAuthorizationService _authorizationService; private readonly IBlogItemsService _blogItemsService; + private readonly WebapiControllers _webapiController = WebapiControllers.BlogItems; + /// /// /// + /// /// public BlogItemsController( + IAuthorizationService authorizationService, IBlogItemsService blogItemsService ) { + _authorizationService = authorizationService; _blogItemsService = blogItemsService; } @@ -37,7 +44,6 @@ public class BlogItemsController : ControllerBase { /// /// /// - [AllowAnonymous] [HttpGet("{siteId}")] public IActionResult Get([FromRoute] Guid siteId, [FromQuery] Guid? category, [FromQuery] string? searchText, [FromQuery] int? currentPage, [FromQuery] int? itemsPerPage, [FromQuery] string? locale) { var result = _blogItemsService.Get(siteId, category, currentPage ?? 1, itemsPerPage ?? 8, locale, searchText); @@ -49,11 +55,16 @@ public class BlogItemsController : ControllerBase { /// /// /// - [Authorize] [HttpDelete("{siteId}")] - public IActionResult Delete([FromRoute] Guid siteId) { - var result = _blogItemsService.Delete(siteId); - return result.ToActionResult(); + public async Task Delete([FromRoute] Guid siteId) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Delete))).Succeeded) { + + var result = _blogItemsService.Delete(siteId); + return result.ToActionResult(); + } + else { + return Unauthorized(); + } } } diff --git a/webapi/WeatherForecast/Controllers/CategoryItemController.cs b/webapi/WeatherForecast/Controllers/CategoryItemController.cs index d8af455..56c7c7a 100644 --- a/webapi/WeatherForecast/Controllers/CategoryItemController.cs +++ b/webapi/WeatherForecast/Controllers/CategoryItemController.cs @@ -5,25 +5,33 @@ using DomainResults.Mvc; using WeatherForecast.Services; using WeatherForecast.Models.Requests; +using WeatherForecast.Policies; +using Core.Enumerations; namespace WeatherForecast.Controllers; /// /// /// -[Authorize(Policy = "WhitelistToken")] [ApiController] [Route("api/[controller]")] public class CategoryItemController : ControllerBase { + private readonly IAuthorizationService _authorizationService; private readonly ICategoryItemService _categoryItemService; + private readonly WebapiControllers _webapiController = WebapiControllers.CategoryItem; + /// /// /// + /// /// public CategoryItemController( - ICategoryItemService categoryItemService) { + IAuthorizationService authorizationService, + ICategoryItemService categoryItemService + ) { + _authorizationService = authorizationService; _categoryItemService = categoryItemService; } @@ -34,9 +42,14 @@ public class CategoryItemController : ControllerBase { /// /// [HttpPost("{siteId}")] - public IActionResult Post([FromRoute] Guid siteId, [FromBody] CategoryItemRequestModel requestData) { - var result = _categoryItemService.Post(siteId, requestData); - return result.ToActionResult(); + public async Task Post([FromRoute] Guid siteId, [FromBody] CategoryItemRequestModel requestData) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Create))).Succeeded) { + var result = _categoryItemService.Post(siteId, requestData); + return result.ToActionResult(); + } + else { + return Unauthorized(); + } } /// @@ -46,9 +59,14 @@ public class CategoryItemController : ControllerBase { /// /// [HttpGet("{siteId}/{categoryId}")] - public IActionResult Get([FromRoute] Guid siteId, [FromRoute] Guid categoryId) { - var result = _categoryItemService.Get(siteId, categoryId); - return result.ToActionResult(); + public async Task Get([FromRoute] Guid siteId, [FromRoute] Guid categoryId) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Read))).Succeeded) { + var result = _categoryItemService.Get(siteId, categoryId); + return result.ToActionResult(); + } + else { + return Unauthorized(); + } } /// @@ -56,7 +74,6 @@ public class CategoryItemController : ControllerBase { /// /// /// - [AllowAnonymous] [HttpGet("{siteId}")] public IActionResult GetSlug([FromRoute] Guid siteId, [FromQuery] string slug) { var result = _categoryItemService.GetSlug(siteId, slug); @@ -71,9 +88,14 @@ public class CategoryItemController : ControllerBase { /// /// [HttpPut("{siteId}/{categoryId}")] - public IActionResult Update([FromRoute] Guid siteId, [FromRoute] Guid categoryId, [FromBody] CategoryItemRequestModel requestData) { - var result = _categoryItemService.Update(siteId, categoryId, requestData); - return result.ToActionResult(); + public async Task Update([FromRoute] Guid siteId, [FromRoute] Guid categoryId, [FromBody] CategoryItemRequestModel requestData) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Update))).Succeeded) { + var result = _categoryItemService.Update(siteId, categoryId, requestData); + return result.ToActionResult(); + } + else { + return Unauthorized(); + } } /// @@ -83,9 +105,14 @@ public class CategoryItemController : ControllerBase { /// /// [HttpDelete("{siteId}/{categoryId}")] - public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] Guid categoryId) { - var result = _categoryItemService.Delete(siteId, categoryId); - return result.ToActionResult(); + public async Task Delete([FromRoute] Guid siteId, [FromRoute] Guid categoryId) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Delete))).Succeeded) { + var result = _categoryItemService.Delete(siteId, categoryId); + return result.ToActionResult(); + } + else { + return Unauthorized(); + } } } diff --git a/webapi/WeatherForecast/Controllers/CategoryItemsController.cs b/webapi/WeatherForecast/Controllers/CategoryItemsController.cs index 15c620e..c5bdfde 100644 --- a/webapi/WeatherForecast/Controllers/CategoryItemsController.cs +++ b/webapi/WeatherForecast/Controllers/CategoryItemsController.cs @@ -4,26 +4,33 @@ using Microsoft.AspNetCore.Authorization; using DomainResults.Mvc; using WeatherForecast.Services; +using WeatherForecast.Policies; +using Core.Enumerations; namespace WeatherForecast.Controllers; /// /// /// -[Authorize(Policy = "WhitelistToken")] [ApiController] [Route("api/[controller]")] public class CategoryItemsController : ControllerBase { + private readonly IAuthorizationService _authorizationService; private readonly ICategoryItemsService _categoryItemsService; + private readonly WebapiControllers _webapiController = WebapiControllers.CategoryItem; + /// /// /// + /// /// public CategoryItemsController( - ICategoryItemsService categoryItemsService + IAuthorizationService authorizationService, + ICategoryItemsService categoryItemsService ) { + _authorizationService = authorizationService; _categoryItemsService = categoryItemsService; } @@ -44,11 +51,15 @@ public class CategoryItemsController : ControllerBase { /// /// /// - [AllowAnonymous] [HttpDelete("{siteId}")] - public IActionResult Delete([FromRoute] Guid siteId) { - var result = _categoryItemsService.Delete(siteId); - return result.ToActionResult(); + public async Task Delete([FromRoute] Guid siteId) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Delete))).Succeeded) { + var result = _categoryItemsService.Delete(siteId); + return result.ToActionResult(); + } + else { + return Unauthorized(); + } } } diff --git a/webapi/WeatherForecast/Controllers/ContentController.cs b/webapi/WeatherForecast/Controllers/ContentController.cs index 5e55410..e03be86 100644 --- a/webapi/WeatherForecast/Controllers/ContentController.cs +++ b/webapi/WeatherForecast/Controllers/ContentController.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Authorization; using DomainResults.Mvc; using WeatherForecast.Services; +using Core.Enumerations; namespace WeatherForecast.Controllers; @@ -11,20 +12,24 @@ namespace WeatherForecast.Controllers; /// /// [ApiController] -[AllowAnonymous] [Route("api/[controller]")] public class ContentController : ControllerBase { + private readonly IAuthorizationService _authorizationService; private readonly IContentService _contentService; + private readonly WebapiControllers _webapiController = WebapiControllers.Content; + /// /// /// - /// + /// /// public ContentController( + IAuthorizationService authorizationService, IContentService contentService ) { + _authorizationService = authorizationService; _contentService = contentService; } diff --git a/webapi/WeatherForecast/Controllers/FileController.cs b/webapi/WeatherForecast/Controllers/FileController.cs index 5f1595f..2035f90 100644 --- a/webapi/WeatherForecast/Controllers/FileController.cs +++ b/webapi/WeatherForecast/Controllers/FileController.cs @@ -1,7 +1,9 @@ -using DomainResults.Mvc; +using Core.Enumerations; +using DomainResults.Mvc; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Net.Http.Headers; +using WeatherForecast.Policies; using WeatherForecast.Services; namespace WeatherForecast.Controllers; @@ -9,20 +11,24 @@ namespace WeatherForecast.Controllers; /// /// /// -[Authorize(Policy = "WhitelistToken")] [AllowAnonymous] [Route("api/[controller]")] -public class FileController : Controller { +public class FileController : ControllerBase { + private readonly IAuthorizationService _authorizationService; private readonly IFileService _fileService; + private readonly WebapiControllers _webapiController = WebapiControllers.File; + /// /// /// /// public FileController( + IAuthorizationService authorizationService, IFileService fileService ) { + _authorizationService = authorizationService; _fileService = fileService; } @@ -34,9 +40,13 @@ public class FileController : Controller { /// /// [HttpPost("{siteId}/{userId}")] - public IActionResult Post([FromRoute] Guid siteId, [FromRoute] Guid userId, IFormFile file) { - var result = _fileService.Post(siteId, userId, file); - return result.ToActionResult(); + public async Task Post([FromRoute] Guid siteId, [FromRoute] Guid userId, IFormFile file) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Create))).Succeeded) { + var result = _fileService.Post(siteId, userId, file); + return result.ToActionResult(); + } + + return Unauthorized(); } /// @@ -47,16 +57,21 @@ public class FileController : Controller { /// /// [HttpGet("{siteId}/{userId}/{fileId}")] - public IActionResult Get([FromRoute] Guid siteId, [FromRoute] Guid userid, [FromRoute] Guid fileId) { - var (file, result) = _fileService.Get(siteId, userid, fileId); + public async Task Get([FromRoute] Guid siteId, [FromRoute] Guid userid, [FromRoute] Guid fileId) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Read))).Succeeded) { - if (!result.IsSuccess || file == null) - return result.ToActionResult(); + var (file, result) = _fileService.Get(siteId, userid, fileId); - var stream = new MemoryStream(file.Bytes); - return new FileStreamResult(stream, file.ContentType) { - FileDownloadName = file.Name - }; + if (!result.IsSuccess || file == null) + return result.ToActionResult(); + + var stream = new MemoryStream(file.Bytes); + return new FileStreamResult(stream, file.ContentType) { + FileDownloadName = file.Name + }; + } + + return Unauthorized(); } /// @@ -67,9 +82,13 @@ public class FileController : Controller { /// /// [HttpDelete("{siteId}/{userId}/{fileId}")] - public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] Guid fileId) { - var result = _fileService.Delete(siteId, userId, fileId); - return result.ToActionResult(); + public async Task Delete([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] Guid fileId) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Delete))).Succeeded) { + var result = _fileService.Delete(siteId, userId, fileId); + return result.ToActionResult(); + } + + return Unauthorized(); } } diff --git a/webapi/WeatherForecast/Controllers/FilesController.cs b/webapi/WeatherForecast/Controllers/FilesController.cs index afcd5cd..84267fc 100644 --- a/webapi/WeatherForecast/Controllers/FilesController.cs +++ b/webapi/WeatherForecast/Controllers/FilesController.cs @@ -1,6 +1,8 @@ -using DomainResults.Mvc; +using Core.Enumerations; +using DomainResults.Mvc; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using WeatherForecast.Policies; using WeatherForecast.Services; namespace WeatherForecast.Controllers; @@ -8,20 +10,24 @@ namespace WeatherForecast.Controllers; /// /// /// -[Authorize(Policy = "WhitelistToken")] [AllowAnonymous] [Route("api/[controller]")] -public class FilesController : Controller { +public class FilesController : ControllerBase { + private readonly IAuthorizationService _authorizationService; private readonly IFilesService _filesService; + private readonly WebapiControllers _webapiController = WebapiControllers.Files; + /// /// /// /// public FilesController( + IAuthorizationService authorizationService, IFilesService filesService ) { + _authorizationService = authorizationService; _filesService = filesService; } @@ -33,9 +39,14 @@ public class FilesController : Controller { /// /// [HttpPost("{siteId}/{userId}")] - public IActionResult Post([FromRoute] Guid siteId, [FromRoute] Guid userId, List file) { - var result = _filesService.Post(siteId, userId, file); - return result.ToActionResult(); + public async Task Post([FromRoute] Guid siteId, [FromRoute] Guid userId, List file) { + + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Create))).Succeeded) { + var result = _filesService.Post(siteId, userId, file); + return result.ToActionResult(); + } + + return Unauthorized(); } /// @@ -46,9 +57,12 @@ public class FilesController : Controller { /// [HttpDelete("{siteId}/{userId}")] - public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] Guid userId) { - var result = _filesService.Delete(siteId, userId); - return result.ToActionResult(); + public async Task Delete([FromRoute] Guid siteId, [FromRoute] Guid userId) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Create))).Succeeded) { + var result = _filesService.Delete(siteId, userId); + return result.ToActionResult(); + } + + return Unauthorized(); } } - diff --git a/webapi/WeatherForecast/Controllers/ImageController.cs b/webapi/WeatherForecast/Controllers/ImageController.cs index aecbcf3..5296095 100644 --- a/webapi/WeatherForecast/Controllers/ImageController.cs +++ b/webapi/WeatherForecast/Controllers/ImageController.cs @@ -9,11 +9,11 @@ namespace WeatherForecast.Controllers; /// /// /// -[AllowAnonymous] [Route("api/[controller]")] [ApiController] public class ImageController : ControllerBase { + private readonly IAuthorizationService _authorizationService; private readonly IImageService _imageService; /// @@ -21,8 +21,10 @@ public class ImageController : ControllerBase { /// /// public ImageController( + IAuthorizationService authorizationService, IImageService imgeService ) { + _authorizationService = authorizationService; _imageService = imgeService; } diff --git a/webapi/WeatherForecast/Controllers/PasswordController.cs b/webapi/WeatherForecast/Controllers/PasswordController.cs index a5a29dc..c48c0a0 100644 --- a/webapi/WeatherForecast/Controllers/PasswordController.cs +++ b/webapi/WeatherForecast/Controllers/PasswordController.cs @@ -1,8 +1,10 @@ -using DomainResults.Mvc; +using Core.Enumerations; +using DomainResults.Mvc; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using WeatherForecast.Models.Requests; +using WeatherForecast.Policies; using WeatherForecast.Services; namespace WeatherForecast.Controllers; @@ -10,20 +12,24 @@ namespace WeatherForecast.Controllers; /// /// /// -[Authorize(Policy = "WhitelistToken")] [Route("api/[controller]")] [ApiController] public class PasswordController : ControllerBase { + private readonly IAuthorizationService _authorizationService; private readonly IPasswordService _passwordService; + private readonly WebapiControllers _webapiController = WebapiControllers.Password; + /// /// /// /// public PasswordController( + IAuthorizationService authorizationService, IPasswordService passwordService ) { + _authorizationService = authorizationService; _passwordService = passwordService; } @@ -35,9 +41,13 @@ public class PasswordController : ControllerBase { /// /// [HttpPost("{siteId}/{userId}")] - public IActionResult Post([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromBody] PasswordRequestModel requestData) { - var result = _passwordService.Post(siteId, userId, requestData); - return result.ToActionResult(); + public async Task Post([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromBody] PasswordRequestModel requestData) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Create))).Succeeded) { + var result = _passwordService.Post(siteId, userId, requestData); + return result.ToActionResult(); + } + + return Unauthorized(); } } diff --git a/webapi/WeatherForecast/Controllers/ShopCartItemController.cs b/webapi/WeatherForecast/Controllers/ShopCartItemController.cs index f54013c..3670a54 100644 --- a/webapi/WeatherForecast/Controllers/ShopCartItemController.cs +++ b/webapi/WeatherForecast/Controllers/ShopCartItemController.cs @@ -5,6 +5,8 @@ using DomainResults.Mvc; using WeatherForecast.Services; using WeatherForecast.Models.Requests; +using WeatherForecast.Policies; +using Core.Enumerations; namespace WeatherForecast.Controllers; @@ -12,20 +14,24 @@ namespace WeatherForecast.Controllers; /// /// /// -[Authorize(Policy = "WhitelistToken")] [AllowAnonymous] [Route("api/[controller]")] public class ShopCartItemController : ControllerBase { + private readonly IAuthorizationService _authorizationService; private readonly IShopCartItemService _shopCartItemService; + private readonly WebapiControllers _webapiController = WebapiControllers.ShopCartItem; + /// /// /// /// public ShopCartItemController( + IAuthorizationService authorizationService, IShopCartItemService shopCartItemService ) { + _authorizationService = authorizationService; _shopCartItemService = shopCartItemService; } @@ -38,9 +44,13 @@ public class ShopCartItemController : ControllerBase { /// /// [HttpPost("{siteId}/{userId}/{sku}")] - public IActionResult Post([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] string sku, [FromBody] ShopCartItemRequestModel requestData) { - var result = _shopCartItemService.Post(siteId, userId, sku, requestData); - return result.ToActionResult(); + public async Task Post([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] string sku, [FromBody] ShopCartItemRequestModel requestData) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Create))).Succeeded) { + var result = _shopCartItemService.Post(siteId, userId, sku, requestData); + return result.ToActionResult(); + } + + return Unauthorized(); } /// @@ -48,9 +58,13 @@ public class ShopCartItemController : ControllerBase { /// /// [HttpGet("{siteId}/{userId}/{sku}")] - public IActionResult Get([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] string sku, [FromQuery] string? locale) { - var result = _shopCartItemService.Get(siteId, userId, sku, locale); - return result.ToActionResult(); + public async Task Get([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] string sku, [FromQuery] string? locale) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Read))).Succeeded) { + var result = _shopCartItemService.Get(siteId, userId, sku, locale); + return result.ToActionResult(); + } + + return Unauthorized(); } /// @@ -62,9 +76,13 @@ public class ShopCartItemController : ControllerBase { /// /// [HttpPut("{siteId}/{userId}/{sku}")] - public IActionResult Update([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] string sku, [FromBody] ShopCartItemRequestModel requestData) { - var result = _shopCartItemService.Update(siteId, userId, sku, requestData); - return result.ToActionResult(); + public async Task Update([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] string sku, [FromBody] ShopCartItemRequestModel requestData) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Update))).Succeeded) { + var result = _shopCartItemService.Update(siteId, userId, sku, requestData); + return result.ToActionResult(); + } + + return Unauthorized(); } /// @@ -75,9 +93,13 @@ public class ShopCartItemController : ControllerBase { /// /// [HttpDelete("{siteId}/{userId}/{sku}")] - public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] string sku) { - var result = _shopCartItemService.Delete(siteId, userId, sku); - return result.ToActionResult(); + public async Task Delete([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] string sku) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Delete))).Succeeded) { + var result = _shopCartItemService.Delete(siteId, userId, sku); + return result.ToActionResult(); + } + + return Unauthorized(); } } diff --git a/webapi/WeatherForecast/Controllers/ShopCartItemsController.cs b/webapi/WeatherForecast/Controllers/ShopCartItemsController.cs index cf842c2..6e0d7ef 100644 --- a/webapi/WeatherForecast/Controllers/ShopCartItemsController.cs +++ b/webapi/WeatherForecast/Controllers/ShopCartItemsController.cs @@ -4,6 +4,8 @@ using Microsoft.AspNetCore.Authorization; using DomainResults.Mvc; using WeatherForecast.Services; +using WeatherForecast.Policies; +using Core.Enumerations; namespace WeatherForecast.Controllers; @@ -11,20 +13,24 @@ namespace WeatherForecast.Controllers; /// /// /// -[Authorize(Policy = "WhitelistToken")] -[AllowAnonymous] +[ApiController] [Route("api/[controller]")] public class ShopCartItemsController : ControllerBase { + private readonly IAuthorizationService _authorizationService; private readonly IShopCartItemsService _shopCartItemsService; + private readonly WebapiControllers _webapiController = WebapiControllers.ShopCartItems; + /// /// /// /// public ShopCartItemsController( + IAuthorizationService authorizationService, IShopCartItemsService shopCartItemsService ) { + _authorizationService = authorizationService; _shopCartItemsService = shopCartItemsService; } @@ -33,9 +39,13 @@ public class ShopCartItemsController : ControllerBase { /// /// [HttpGet("{siteId}/{userId}")] - public IActionResult Get([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromQuery] string? locale) { - var result = _shopCartItemsService.Get(siteId, userId, locale); - return result.ToActionResult(); + public async Task Get([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromQuery] string? locale) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(WebapiControllers.ShopCartItems, CrudActions.Read))).Succeeded) { + var result = _shopCartItemsService.Get(siteId, userId, locale); + return result.ToActionResult(); + } + + return Unauthorized(); } /// @@ -45,9 +55,13 @@ public class ShopCartItemsController : ControllerBase { /// /// [HttpDelete("{siteId}/{userId}")] - public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] Guid userId) { - var result = _shopCartItemsService.Delete(siteId, userId); - return result.ToActionResult(); + public async Task Delete([FromRoute] Guid siteId, [FromRoute] Guid userId) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Delete))).Succeeded) { + var result = _shopCartItemsService.Delete(siteId, userId); + return result.ToActionResult(); + } + + return Unauthorized(); } } diff --git a/webapi/WeatherForecast/Controllers/ShopItemController.cs b/webapi/WeatherForecast/Controllers/ShopItemController.cs index eb794e5..b299fdc 100644 --- a/webapi/WeatherForecast/Controllers/ShopItemController.cs +++ b/webapi/WeatherForecast/Controllers/ShopItemController.cs @@ -5,26 +5,32 @@ using DomainResults.Mvc; using WeatherForecast.Services; using WeatherForecast.Models.Requests; +using WeatherForecast.Policies; +using Core.Enumerations; namespace WeatherForecast.Controllers; /// /// /// -[Authorize(Policy = "WhitelistToken")] [ApiController] [Route("api/[controller]")] public class ShopItemController : ControllerBase { + private readonly IAuthorizationService _authorizationService; private readonly IShopItemService _shopItemService; + private readonly WebapiControllers _webapiController = WebapiControllers.ShopItem; + /// /// /// /// public ShopItemController( + IAuthorizationService authorizationService, IShopItemService shopItemService ) { + _authorizationService = authorizationService; _shopItemService = shopItemService; } @@ -36,9 +42,14 @@ public class ShopItemController : ControllerBase { /// /// [HttpPost("{siteId}/{sku}")] - public IActionResult Post([FromRoute] Guid siteId, [FromRoute] string sku, [FromBody] ShopItemRequestModel requestData) { - var result = _shopItemService.Post(siteId, sku, requestData); - return result.ToActionResult(); + public async Task Post([FromRoute] Guid siteId, [FromRoute] string sku, [FromBody] ShopItemRequestModel requestData) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Create))).Succeeded) { + + var result = _shopItemService.Post(siteId, sku, requestData); + return result.ToActionResult(); + } + + return Unauthorized(); } /// @@ -48,9 +59,13 @@ public class ShopItemController : ControllerBase { /// /// [HttpGet("{siteId}/{sku}")] - public IActionResult Get([FromRoute] Guid siteId, [FromRoute] string sku) { - var result = _shopItemService.Get(siteId, sku); - return result.ToActionResult(); + public async Task Get([FromRoute] Guid siteId, [FromRoute] string sku) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Read))).Succeeded) { + var result = _shopItemService.Get(siteId, sku); + return result.ToActionResult(); + } + + return Unauthorized(); } /// @@ -59,7 +74,6 @@ public class ShopItemController : ControllerBase { /// /// /// - [AllowAnonymous] [HttpGet("{siteId}")] public IActionResult GetSlug([FromRoute] Guid siteId, [FromQuery] string slug) { var result = _shopItemService.GetSlug(siteId, slug); @@ -74,9 +88,13 @@ public class ShopItemController : ControllerBase { /// /// [HttpPut("{siteId}/{sku}")] - public IActionResult Update([FromRoute] Guid siteId, [FromRoute] string sku, [FromBody] ShopItemRequestModel requestData) { - var result = _shopItemService.Update(siteId, sku, requestData); - return result.ToActionResult(); + public async Task Update([FromRoute] Guid siteId, [FromRoute] string sku, [FromBody] ShopItemRequestModel requestData) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Update))).Succeeded) { + var result = _shopItemService.Update(siteId, sku, requestData); + return result.ToActionResult(); + } + + return Unauthorized(); } /// @@ -86,9 +104,13 @@ public class ShopItemController : ControllerBase { /// /// [HttpDelete("{siteId}/{sku}")] - public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] string sku) { - var result = _shopItemService.Delete(siteId, sku); - return result.ToActionResult(); + public async Task Delete([FromRoute] Guid siteId, [FromRoute] string sku) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Delete))).Succeeded) { + var result = _shopItemService.Delete(siteId, sku); + return result.ToActionResult(); + } + + return Unauthorized(); } } diff --git a/webapi/WeatherForecast/Controllers/ShopItemsController.cs b/webapi/WeatherForecast/Controllers/ShopItemsController.cs index 92c4af7..6c26d50 100644 --- a/webapi/WeatherForecast/Controllers/ShopItemsController.cs +++ b/webapi/WeatherForecast/Controllers/ShopItemsController.cs @@ -4,26 +4,32 @@ using Microsoft.AspNetCore.Authorization; using DomainResults.Mvc; using WeatherForecast.Services; +using WeatherForecast.Policies; +using Core.Enumerations; namespace WeatherForecast.Controllers; /// /// /// -[Authorize(Policy = "WhitelistToken")] [ApiController] [Route("api/[controller]")] public class ShopItemsController : ControllerBase { + private readonly IAuthorizationService _authorizationService; private readonly IShopItemsService _shopItemsService; + private readonly WebapiControllers _webapiController = WebapiControllers.ShopItem; + /// /// /// /// public ShopItemsController( + IAuthorizationService authorizationService, IShopItemsService shopCatalogService ) { + _authorizationService = authorizationService; _shopItemsService = shopCatalogService; } @@ -49,8 +55,12 @@ public class ShopItemsController : ControllerBase { /// /// [HttpDelete("{siteId}")] - public IActionResult Delete([FromRoute] Guid siteId) { - var result = _shopItemsService.Delete(siteId); - return result.ToActionResult(); + public async Task Delete([FromRoute] Guid siteId) { + if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Create))).Succeeded) { + var result = _shopItemsService.Delete(siteId); + return result.ToActionResult(); + } + + return Unauthorized(); } } diff --git a/webapi/WeatherForecast/Controllers/UserController.cs b/webapi/WeatherForecast/Controllers/UserController.cs deleted file mode 100644 index 17d706b..0000000 --- a/webapi/WeatherForecast/Controllers/UserController.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace WeatherForecast.Controllers; - -/// -/// -/// -[Authorize(Policy = "WhitelistToken")] -[ApiController] -[Route("api/[controller]")] -public class UserController : ControllerBase { - - /// - /// - /// - public UserController() { } -} diff --git a/webapi/WeatherForecast/Controllers/WeatherForecastController.cs b/webapi/WeatherForecast/Controllers/WeatherForecastController.cs index 747b7b2..09405a2 100644 --- a/webapi/WeatherForecast/Controllers/WeatherForecastController.cs +++ b/webapi/WeatherForecast/Controllers/WeatherForecastController.cs @@ -8,7 +8,6 @@ namespace WeatherForecast.Controllers; /// /// /// -[AllowAnonymous] [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { diff --git a/webapi/WeatherForecast/Policies/CrudAuthorizationHandler.cs b/webapi/WeatherForecast/Policies/CrudAuthorizationHandler.cs new file mode 100644 index 0000000..3d2503c --- /dev/null +++ b/webapi/WeatherForecast/Policies/CrudAuthorizationHandler.cs @@ -0,0 +1,83 @@ +using Core.Enumerations; +using ExtensionMethods; +using Extensions; +using Microsoft.AspNetCore.Authorization; +using WeatherForecast.Services; + +namespace WeatherForecast.Policies { + + /// + /// + /// + public class CrudAuthorizationHandler : AuthorizationHandler { + + private readonly IHttpContextAccessor _contextAccessor; + private readonly IUserService _authenticationService; + + /// + /// + /// + /// + /// + public CrudAuthorizationHandler( + IHttpContextAccessor contextAccessor, + IUserService authenticationService + ) { + _contextAccessor = contextAccessor; + _authenticationService = authenticationService; + } + + /// + /// + /// + /// + /// + /// + protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CrudActionRequirement requirement) { + + var user = context.User; + var identity = user.Identity; + var name = identity?.Name; + var userId = name?.ToNullableGuid(); + + var request = _contextAccessor?.HttpContext?.Request; + var bearerToken = request?.GeBearerToken(); + + if (_authenticationService.VerifyToken(userId, bearerToken, requirement.Controller, requirement.Action).IsSuccess) + context.Succeed(requirement); + + return Task.CompletedTask; + } + } + + /// + /// + /// + public class CrudActionRequirement : IAuthorizationRequirement { + + /// + /// + /// + public WebapiControllers Controller { get; } + + /// + /// + /// + public CrudActions Action { get; } + + /// + /// + /// + public bool SameAuthor { get; } + + /// + /// + /// + /// + public CrudActionRequirement(WebapiControllers controller, CrudActions crudAction, bool sameAuthor = false) { + Controller = controller; + Action = crudAction; + SameAuthor = sameAuthor; + } + } +} diff --git a/webapi/WeatherForecast/Policies/WhitelistToken.cs b/webapi/WeatherForecast/Policies/WhitelistToken.cs deleted file mode 100644 index 5a0e1aa..0000000 --- a/webapi/WeatherForecast/Policies/WhitelistToken.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Extensions; -using Microsoft.AspNetCore.Authorization; -using WeatherForecast.Services; - -namespace WeatherForecast.Policies { - /// - /// - /// - public class WhitelistTokenRequirement : IAuthorizationRequirement { - // public string WhiteListToken { get; } - - /// - /// - /// - public WhitelistTokenRequirement() { - // WhiteListToken = whiteListToken; - } - } - - /// - /// - /// - public class WhitelistTokenHandler : AuthorizationHandler { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthenticationService _athenticationService; - - /// - /// - /// - /// - /// - public WhitelistTokenHandler( - IHttpContextAccessor httpContextAccessor, - IAuthenticationService athenticationService - ) { - _httpContextAccessor = httpContextAccessor; - _athenticationService = athenticationService; - } - - /// - /// - /// - /// - /// - /// - protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, WhitelistTokenRequirement requirement) { - var request = _httpContextAccessor?.HttpContext?.Request; - - if (request != null && _athenticationService.Get(request.GeBearerToken()).IsSuccess) - context.Succeed(requirement); - - return Task.CompletedTask; - } - } -} diff --git a/webapi/WeatherForecast/Services/AutheticationService.cs b/webapi/WeatherForecast/Services/AutheticationService.cs deleted file mode 100644 index db4c973..0000000 --- a/webapi/WeatherForecast/Services/AutheticationService.cs +++ /dev/null @@ -1,157 +0,0 @@ -using Core.Abstractions; -using Core.DomainObjects; -using DataProviders.Collections; -using DomainResults.Common; -using ExtensionMethods; -using CryptoProvider; -using JWTService; -using WeatherForecast.Models.Requests; -using Microsoft.Extensions.Options; - -namespace WeatherForecast.Services { - - /// - /// - /// - public interface IAuthenticationService { - - /// - /// - /// - /// - /// - (string?, IDomainResult) Post(AuthenticationRequestModel requestData); - - /// - /// - /// - /// - /// - IDomainResult Get(string? token); - } - - /// - /// - /// - public class AutheticationService : ServiceBase, IAuthenticationService { - - private readonly IAesKey? _aesKey; - private readonly IUserDataProvider _userDataProvider; - private readonly IJWTService _jwtService; - - /// - /// - /// - /// - /// - /// - /// - public AutheticationService ( - ILogger logger, - IOptions options, - IUserDataProvider userDataProvider, - IJWTService jwtService - ) : base(logger) { - _aesKey = options.Value.JwtTokenEncryption; - _userDataProvider = userDataProvider; - _jwtService = jwtService; - } - - /// - /// - /// - /// - /// - public (string?, IDomainResult) Post(AuthenticationRequestModel requestData) { - - if (_aesKey?.IV == null || _aesKey?.Key == null) - return IDomainResult.Failed("IV or Key are not set"); - - // Retrieve user from database by userName - var (user, getUserResult) = _userDataProvider.GetByUsername(requestData.Username); - if (!getUserResult.IsSuccess || user == null) - return (null, getUserResult); - - if (user.Passwords.Password == null) - return IDomainResult.Failed("Password is not set, create new password."); - - // Check provided password hash with the stored one - var (salt, hash) = HashService.CreateSaltedHash(requestData.Password); - if (!HashService.ValidateHash(requestData.Password, salt, hash)) - return IDomainResult.Unauthorized(); - - // Check password expiration if enabled - if (user.Passwords.Expiration.Enabled && DateTime.UtcNow > user.Passwords.Password.Created.AddDays(user.Passwords.Expiration.Days)) { - user.Passwords.Expired.Add(user.Passwords.Password.Prototype()); - - user.Passwords.Password = null; - user.Tokens = new List(); - - return IDomainResult.Failed("Password is expired, create new password."); - } - - // Creating JWT token - var claims = new List> { - new KeyValuePair("UserId", $"{user.Id}") - }; - - var created = DateTime.UtcNow; - var expires = created.AddDays(365); - - var token = _jwtService.CreateJwtToken(expires, claims); - - user.Tokens.Add(new Token { - Value = AesService.EncryptString(_aesKey.IV, _aesKey.Key, token), - Created = created, - Expires = expires, - }); - - var (_, usdateUserResult) = _userDataProvider.Update(user); - if (!usdateUserResult.IsSuccess) - return IDomainResult.Failed(); - - return IDomainResult.Success(token); - } - - /// - /// - /// - /// - /// - public IDomainResult Get(string? token) { - if (_aesKey?.IV == null || _aesKey?.Key == null) - return IDomainResult.Failed("IV or Key are not set"); - - if (token == null) - return IDomainResult.Failed(); - - #region Retrieve user id from token claim - var (claims, getClaimsResult) = _jwtService.JwtTokenClaims(token); - if (!getClaimsResult.IsSuccess || claims == null) - return IDomainResult.Failed(); - - var userId = claims.SingleOrDefault(x => x.Key == "UserId").Value.ToGuid(); - if (userId == Guid.Empty) - return IDomainResult.Failed(); - #endregion - - - var (user, getUserResult) = _userDataProvider.Get(userId); - if (!getUserResult.IsSuccess || user == null) - return IDomainResult.Failed(); - - #region Tokens cleanup - var userTokens = user.Tokens.Where(x => x.Expires > DateTime.UtcNow).ToList(); - - if (user.Tokens.Count != userTokens.Count) { - user.Tokens = userTokens; - _userDataProvider.Update(user); - } - #endregion - - return userTokens.Select(x => AesService.DecryptString(_aesKey.IV, _aesKey.Key, x.Value)).Any(x => string.Compare(x, token) == 0) - ? IDomainResult.Success() - : IDomainResult.Failed(); - } - } -} diff --git a/webapi/WeatherForecast/Services/UserService.cs b/webapi/WeatherForecast/Services/UserService.cs index d76769a..b1ef493 100644 --- a/webapi/WeatherForecast/Services/UserService.cs +++ b/webapi/WeatherForecast/Services/UserService.cs @@ -1,16 +1,168 @@ -namespace WeatherForecast.Services { +using Core.Abstractions; +using Core.DomainObjects; +using DataProviders.Collections; +using DomainResults.Common; +using CryptoProvider; +using JWTService; +using WeatherForecast.Models.Requests; +using Microsoft.Extensions.Options; +using Core.Enumerations; + +namespace WeatherForecast.Services { /// /// /// public interface IUserService { - + + /// + /// + /// + /// + /// + (string?, IDomainResult) CreateToken(AuthenticationRequestModel requestData); + + /// + /// + /// + /// + /// + /// + /// + /// + IDomainResult VerifyToken(Guid? userId, string? token, WebapiControllers? controller, CrudActions? actions); } /// /// /// - public class UserService : IUserService { - + public class UserService : ServiceBase, IUserService { + + private readonly IAesKey? _aesKey; + private readonly IUserDataProvider _userDataProvider; + private readonly IJWTService _jwtService; + + /// + /// + /// + /// + /// + /// + /// + public UserService ( + ILogger logger, + IOptions options, + IUserDataProvider userDataProvider, + IJWTService jwtService + ) : base(logger) { + _aesKey = options.Value.JwtTokenEncryption; + _userDataProvider = userDataProvider; + _jwtService = jwtService; + } + + /// + /// + /// + /// + /// + public (string?, IDomainResult) CreateToken(AuthenticationRequestModel requestData) { + + if (_aesKey?.IV == null || _aesKey?.Key == null) + return IDomainResult.Failed("IV or Key are not set"); + + // Retrieve user from database by userName + var (user, getUserResult) = _userDataProvider.GetByUsername(requestData.Username); + if (!getUserResult.IsSuccess || user == null) + return (null, getUserResult); + + if (user.Passwords.Password == null) + return IDomainResult.Failed("Password is not set, create new password."); + + // Check provided password hash with the stored one + var (salt, hash) = HashService.CreateSaltedHash(requestData.Password); + if (!HashService.ValidateHash(requestData.Password, salt, hash)) + return IDomainResult.Unauthorized(); + + // Check password expiration if enabled + if (user.Passwords.Expiration.Enabled && DateTime.UtcNow > user.Passwords.Password.Created.AddDays(user.Passwords.Expiration.Days)) { + user.Passwords.Expired.Add(user.Passwords.Password.Prototype()); + + user.Passwords.Password = null; + user.Tokens = new List(); + + return IDomainResult.Failed("Password is expired, create new password."); + } + + // Creating JWT token + var claims = new List> { + new KeyValuePair("unique_name", $"{user.Id}"), + + // (JWT ID): Unique identifier; can be used to prevent the JWT from being replayed (allows a token to be used only once) + new KeyValuePair("jti", $"{Guid.NewGuid()}") + }; + + var created = DateTime.UtcNow; + var expires = created.AddDays(365); + + var token = _jwtService.CreateJwtToken(expires, claims); + + user.Tokens.Add(new Token { + Value = AesService.EncryptString(_aesKey.IV, _aesKey.Key, token), + Created = created, + Expires = expires, + }); + + var (_, usdateUserResult) = _userDataProvider.Update(user); + if (!usdateUserResult.IsSuccess) + return IDomainResult.Failed(); + + return IDomainResult.Success(token); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public IDomainResult VerifyToken(Guid? userId, string? token, WebapiControllers? controller, CrudActions? action) { + if (_aesKey?.IV == null || _aesKey?.Key == null) + return IDomainResult.Failed("IV or Key are not set"); + + if (token == null || userId == null) + return IDomainResult.Failed(); + + + var (user, getUserResult) = _userDataProvider.Get(userId.Value); + if (!getUserResult.IsSuccess || user == null) + return IDomainResult.Failed(); + + #region Tokens cleanup + var userTokens = user.Tokens.Where(x => x.Expires > DateTime.UtcNow).OrderByDescending(x => x.Expires).Take(10).ToList(); + + if (user.Tokens.Count != userTokens.Count) { + user.Tokens = userTokens; + _userDataProvider.Update(user); + } + #endregion + + // Check if whitelisted + if(!userTokens.Select(x => AesService.DecryptString(_aesKey.IV, _aesKey.Key, x.Value)).Any(x => string.Compare(x, token) == 0)) + IDomainResult.Unauthorized(); + + // Check if authorized + if (controller != null) { + if (!user.Authorizations.Any(x => x.Controller == controller)) + return IDomainResult.Unauthorized(); + + if(action != null) + if (!user.Authorizations.SingleOrDefault(x => x.Controller == controller).Actions.Any(x => x == action)) + return IDomainResult.Unauthorized(); + } + + return IDomainResult.Success(); + } } } diff --git a/webapi/WeatherForecast/Startup.cs b/webapi/WeatherForecast/Startup.cs index abd3ff9..3c1b4d2 100644 --- a/webapi/WeatherForecast/Startup.cs +++ b/webapi/WeatherForecast/Startup.cs @@ -12,6 +12,7 @@ using JWTService.Extensions; using Core.Middlewares; using Microsoft.AspNetCore.Authorization; using WeatherForecast.Policies; +using Microsoft.Extensions.DependencyInjection; namespace WeatherForecast { @@ -44,12 +45,12 @@ namespace WeatherForecast { services.Configure(appSettingsSection); var appSettings = appSettingsSection.Get(); - + services.AddCors(options => { - options.AddPolicy(MyAllowSpecificOrigins, - builder => { - builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); - }); + options.AddPolicy(MyAllowSpecificOrigins, + builder => { + builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); + }); }); services.AddControllers().AddJsonOptions(options => @@ -62,7 +63,7 @@ namespace WeatherForecast { options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(options => { options.RequireHttpsMetadata = false; - options.SaveToken = true; + options.SaveToken = false; options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Convert.FromBase64String(appSettings.JwtConfig.Secret)), @@ -76,16 +77,13 @@ namespace WeatherForecast { // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-3.1#use-httpcontext-from-custom-components services.AddHttpContextAccessor(); - #region Policy Authorizations https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-6.0 - services.AddScoped(); - // services.AddScoped(); - services.AddAuthorization(options => { - options.AddPolicy("WhitelistToken", policy => policy.Requirements.Add(new WhitelistTokenRequirement())); - // options.AddPolicy("RecaptchaToken", policy => policy.Requirements.Add(new RecaptchaTokenRequirement("/swagger/index.html"))); - }); + #region Policy Handlers + services.AddScoped(); #endregion + + services.AddScoped(); services.AddScoped(); services.AddScoped(); @@ -95,13 +93,12 @@ namespace WeatherForecast { services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); services.RegisterDataproviders(appSettings); services.RegisterFileSecurityService();